From bdf8fe00f13e8d07c7bf627b64e27d383b8567ad Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Fri, 12 Apr 2019 11:12:31 -0700 Subject: [PATCH 1/9] fix --- client/Router.js | 2 + .../core/components/sidebar/SidebarMenu.js | 1 + .../components/inventory/FoodAddEditForm.js | 2 +- .../food/components/inventory/NewCategory.js | 16 +- .../volunteer/components/VolunteerList.js | 4 +- client/modules/volunteer/reducer.js | 52 + .../VolunteerSchedulingRouter.js | 16 + .../components/AddShiftModal.js | 177 + .../components/EditModal.js | 49 + .../components/VolunteerScheduling.js | 339 + .../reducers/volunteerScheduling.js | 15 + client/store/reducer.js | 4 +- common/placeholders.js | 1 + package-lock.json | 8801 +++++++++-------- package.json | 12 +- server/controllers/volunteer.js | 109 +- server/lib/mail/mail-generator.js | 11 + server/lib/mail/mail-helpers.js | 30 + server/models/volunteer.js | 7 + server/routes/volunteer.js | 22 + server1/config/env/all.js | 19 + server1/config/env/development.js | 4 + server1/config/env/production.js | 6 + server1/config/env/secrets-template.js | 10 + server1/config/env/secrets.js | 10 + server1/config/env/test.js | 6 + server1/config/express.js | 121 + server1/config/index.js | 16 + server1/config/mailer.js | 54 + server1/config/passport.js | 36 + server1/config/strategies/google.js | 57 + server1/config/strategies/local.js | 22 + server1/controllers/customer.js | 152 + server1/controllers/delivery.js | 73 + server1/controllers/donation.js | 69 + server1/controllers/donor.js | 89 + server1/controllers/food.js | 196 + server1/controllers/media.js | 27 + server1/controllers/packing.js | 219 + server1/controllers/page.js | 111 + server1/controllers/questionnaire.js | 45 + server1/controllers/settings.js | 55 + server1/controllers/users.js | 13 + server1/controllers/users/authentication.js | 74 + server1/controllers/users/authorization.js | 40 + server1/controllers/users/password.js | 88 + server1/controllers/users/profile.js | 134 + server1/controllers/volunteer.js | 194 + server1/entry.test.js | 30 + server1/index.js | 2 + server1/lib/enforce-ssl-middleware.js | 12 + server1/lib/errors.js | 92 + server1/lib/geolocate.js | 39 + server1/lib/mail/email-template.html | 87 + server1/lib/mail/html-transform.js | 116 + server1/lib/mail/html-transform.spec.js | 86 + server1/lib/mail/mail-generator.js | 146 + server1/lib/mail/mail-generator.spec.js | 101 + server1/lib/mail/mail-helpers.js | 123 + server1/lib/mapquest-client.js | 34 + server1/lib/media-helpers.js | 34 + server1/lib/media-helpers.spec.js | 54 + server1/lib/notification-sender.js | 46 + server1/lib/notification-sender.spec.js | 155 + server1/lib/questionnaire-helpers.js | 48 + server1/lib/questionnaire-helpers.spec.js | 98 + server1/lib/seed/address-generator.js | 34 + server1/lib/seed/address-generator.spec.js | 46 + server1/lib/seed/addresses.csv | 64 + server1/lib/seed/index.js | 164 + server1/lib/seed/seed-clients.js | 234 + server1/lib/seed/seed-data.js | 279 + server1/lib/update-linked-fields.js | 38 + server1/lib/websocket-middleware.js | 51 + server1/models/customer.js | 131 + server1/models/donation.js | 48 + server1/models/donor.js | 59 + server1/models/food.js | 44 + server1/models/index.js | 14 + server1/models/location-schema.js | 16 + server1/models/media.js | 29 + server1/models/notification.js | 25 + server1/models/package.js | 41 + server1/models/page.js | 29 + server1/models/questionnaire.js | 65 + server1/models/settings.js | 73 + server1/models/user.js | 121 + server1/models/volunteer.js | 120 + server1/routes/api.js | 58 + server1/routes/api.spec.js | 50 + server1/routes/customer.js | 35 + server1/routes/delivery.js | 26 + server1/routes/donation.js | 18 + server1/routes/donor.js | 31 + server1/routes/food.js | 41 + server1/routes/media.js | 20 + server1/routes/packing.js | 27 + server1/routes/page.js | 20 + server1/routes/questionnaire.js | 25 + server1/routes/settings.js | 16 + server1/routes/users.js | 76 + server1/routes/volunteer.js | 46 + server1/server.js | 33 + server1/tests/helpers.js | 69 + server1/tests/integration/customer.spec.js | 430 + server1/tests/integration/donation.spec.js | 149 + server1/tests/integration/donor.spec.js | 127 + server1/tests/integration/food.spec.js | 297 + server1/tests/integration/packing.spec.js | 643 ++ .../tests/integration/questionnaire.spec.js | 123 + server1/tests/integration/user.spec.js | 636 ++ server1/tests/integration/volunteer.spec.js | 169 + 112 files changed, 13455 insertions(+), 4348 deletions(-) create mode 100644 client/modules/volunteerScheduling/VolunteerSchedulingRouter.js create mode 100644 client/modules/volunteerScheduling/components/AddShiftModal.js create mode 100644 client/modules/volunteerScheduling/components/EditModal.js create mode 100644 client/modules/volunteerScheduling/components/VolunteerScheduling.js create mode 100644 client/modules/volunteerScheduling/reducers/volunteerScheduling.js create mode 100644 server1/config/env/all.js create mode 100644 server1/config/env/development.js create mode 100644 server1/config/env/production.js create mode 100644 server1/config/env/secrets-template.js create mode 100644 server1/config/env/secrets.js create mode 100644 server1/config/env/test.js create mode 100644 server1/config/express.js create mode 100644 server1/config/index.js create mode 100644 server1/config/mailer.js create mode 100644 server1/config/passport.js create mode 100644 server1/config/strategies/google.js create mode 100644 server1/config/strategies/local.js create mode 100644 server1/controllers/customer.js create mode 100644 server1/controllers/delivery.js create mode 100644 server1/controllers/donation.js create mode 100644 server1/controllers/donor.js create mode 100644 server1/controllers/food.js create mode 100644 server1/controllers/media.js create mode 100644 server1/controllers/packing.js create mode 100644 server1/controllers/page.js create mode 100644 server1/controllers/questionnaire.js create mode 100644 server1/controllers/settings.js create mode 100644 server1/controllers/users.js create mode 100644 server1/controllers/users/authentication.js create mode 100644 server1/controllers/users/authorization.js create mode 100644 server1/controllers/users/password.js create mode 100644 server1/controllers/users/profile.js create mode 100644 server1/controllers/volunteer.js create mode 100644 server1/entry.test.js create mode 100644 server1/index.js create mode 100644 server1/lib/enforce-ssl-middleware.js create mode 100644 server1/lib/errors.js create mode 100644 server1/lib/geolocate.js create mode 100644 server1/lib/mail/email-template.html create mode 100644 server1/lib/mail/html-transform.js create mode 100644 server1/lib/mail/html-transform.spec.js create mode 100644 server1/lib/mail/mail-generator.js create mode 100644 server1/lib/mail/mail-generator.spec.js create mode 100644 server1/lib/mail/mail-helpers.js create mode 100644 server1/lib/mapquest-client.js create mode 100644 server1/lib/media-helpers.js create mode 100644 server1/lib/media-helpers.spec.js create mode 100644 server1/lib/notification-sender.js create mode 100644 server1/lib/notification-sender.spec.js create mode 100644 server1/lib/questionnaire-helpers.js create mode 100644 server1/lib/questionnaire-helpers.spec.js create mode 100644 server1/lib/seed/address-generator.js create mode 100644 server1/lib/seed/address-generator.spec.js create mode 100644 server1/lib/seed/addresses.csv create mode 100644 server1/lib/seed/index.js create mode 100644 server1/lib/seed/seed-clients.js create mode 100644 server1/lib/seed/seed-data.js create mode 100644 server1/lib/update-linked-fields.js create mode 100644 server1/lib/websocket-middleware.js create mode 100644 server1/models/customer.js create mode 100644 server1/models/donation.js create mode 100644 server1/models/donor.js create mode 100644 server1/models/food.js create mode 100644 server1/models/index.js create mode 100644 server1/models/location-schema.js create mode 100644 server1/models/media.js create mode 100644 server1/models/notification.js create mode 100644 server1/models/package.js create mode 100644 server1/models/page.js create mode 100644 server1/models/questionnaire.js create mode 100644 server1/models/settings.js create mode 100644 server1/models/user.js create mode 100644 server1/models/volunteer.js create mode 100644 server1/routes/api.js create mode 100644 server1/routes/api.spec.js create mode 100644 server1/routes/customer.js create mode 100644 server1/routes/delivery.js create mode 100644 server1/routes/donation.js create mode 100644 server1/routes/donor.js create mode 100644 server1/routes/food.js create mode 100644 server1/routes/media.js create mode 100644 server1/routes/packing.js create mode 100644 server1/routes/page.js create mode 100644 server1/routes/questionnaire.js create mode 100644 server1/routes/settings.js create mode 100644 server1/routes/users.js create mode 100644 server1/routes/volunteer.js create mode 100644 server1/server.js create mode 100644 server1/tests/helpers.js create mode 100644 server1/tests/integration/customer.spec.js create mode 100644 server1/tests/integration/donation.spec.js create mode 100644 server1/tests/integration/donor.spec.js create mode 100644 server1/tests/integration/food.spec.js create mode 100644 server1/tests/integration/packing.spec.js create mode 100644 server1/tests/integration/questionnaire.spec.js create mode 100644 server1/tests/integration/user.spec.js create mode 100644 server1/tests/integration/volunteer.spec.js diff --git a/client/Router.js b/client/Router.js index 72d00d66..c61357b9 100644 --- a/client/Router.js +++ b/client/Router.js @@ -20,6 +20,7 @@ import Packing from './modules/food/components/Packing' import Settings from './modules/settings/SettingsRouter' import Users from './modules/users/UserRouter' import Volunteers from './modules/volunteer/VolunteerRouter' +import VolunteerScheduling from './modules/volunteerScheduling/volunteerSchedulingRouter' import requireRole from './components/router/requireRole' import SwitchWithNotFound from './components/router/SwitchWithNotFound' @@ -42,6 +43,7 @@ const Router = ({history}) => + diff --git a/client/modules/core/components/sidebar/SidebarMenu.js b/client/modules/core/components/sidebar/SidebarMenu.js index 7d97fc3a..c7d72528 100644 --- a/client/modules/core/components/sidebar/SidebarMenu.js +++ b/client/modules/core/components/sidebar/SidebarMenu.js @@ -8,6 +8,7 @@ const SidebarMenu = () => + diff --git a/client/modules/food/components/inventory/FoodAddEditForm.js b/client/modules/food/components/inventory/FoodAddEditForm.js index 29c265c8..1cbb1460 100644 --- a/client/modules/food/components/inventory/FoodAddEditForm.js +++ b/client/modules/food/components/inventory/FoodAddEditForm.js @@ -18,7 +18,7 @@ export default class FoodAddEditForm extends React.Component { touched: { foodName: false, foodCategory: false, foodQuantity: false }, } } - + componentWillReceiveProps = nextProps => { // If the modal is open and a save is complete, close the modal if (this.props.saving && !nextProps.saving && !nextProps.saveError) { diff --git a/client/modules/food/components/inventory/NewCategory.js b/client/modules/food/components/inventory/NewCategory.js index d683ec75..52fd5307 100644 --- a/client/modules/food/components/inventory/NewCategory.js +++ b/client/modules/food/components/inventory/NewCategory.js @@ -1,19 +1,33 @@ import React from 'react' class NewCategory extends React.Component { + // constructor(props) { + // super(props) + // this.state = { + // inputFieldValue: "", + // categoryExists: false, + // validInput: false + // } + // } + constructor(props) { super(props) this.state = { inputFieldValue: "", + // inputFieldValue2: "", categoryExists: false, validInput: false } - } + } onChange = e => { this.setState({inputFieldValue: e.target.value}, this.validate) } + // onChange2 = e => { + // this.setState({inputFieldValue2: e.target.value}, this.validate) + // } + onClick = () => { this.props.createCategory(this.state.inputFieldValue) this.setState({inputFieldValue: "", validInput: false, categoryExists: false}) diff --git a/client/modules/volunteer/components/VolunteerList.js b/client/modules/volunteer/components/VolunteerList.js index 16b017cb..01b08985 100644 --- a/client/modules/volunteer/components/VolunteerList.js +++ b/client/modules/volunteer/components/VolunteerList.js @@ -49,9 +49,7 @@ class VolunteerList extends Component { formatData = () => this.props.volunteers ? - this.props.volunteers.map(v => ({ - ...v, - address: fieldsByType(v, fieldTypes.ADDRESS).map(f => f.value).join(', ') + this.props.volunteers.map(v => ({...v, address: fieldsByType(v, fieldTypes.ADDRESS).map(f => f.value).join(', ') })) : [] diff --git a/client/modules/volunteer/reducer.js b/client/modules/volunteer/reducer.js index 8e0126e0..46888509 100644 --- a/client/modules/volunteer/reducer.js +++ b/client/modules/volunteer/reducer.js @@ -9,6 +9,10 @@ import {crudActions, crudReducer} from '../../store/utils' export const actions = crudActions('volunteer') +export const EMAIL_SHIFT_SUCCESS = 'auth/FORGOT_PASSWORD_SUCCESS' +export const EMAIL_SHIFT_REQUEST = 'auth/FORGOT_PASSWORD_REQUEST' +export const EMAIL_SHIFT_FAILURE = 'auth/FORGOT_PASSWORD_FAILURE' + export const loadVolunteers = () => ({ [CALL_API]: { endpoint: 'admin/volunteers', @@ -17,6 +21,54 @@ export const loadVolunteers = () => ({ } }) +export const makeShift = volunteer => ({ + [CALL_API]: { + endpoint: 'volunteers/addShift', + method: 'PUT', + body: volunteer, + schema: volunteerSchema, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + +export const deleteShift = volunteer => ({ + [CALL_API]: { + endpoint: 'volunteers/deleteShift', + method: 'PUT', + body: volunteer, + schema: volunteerSchema, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + +export const emailShift = credentials => ({ + [CALL_API]: { + endpoint: 'volunteers/emailShiftReminder', + method: 'POST', + body: credentials, + types: [EMAIL_SHIFT_REQUEST, EMAIL_SHIFT_SUCCESS, EMAIL_SHIFT_FAILURE] + } +}) + +export const updateShift = shift => ({ + [CALL_API]: { + endpoint: 'volunteers/updateShift', + method: 'PUT', + body: shift, + schema: volunteerSchema, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + +export const getAllVolunteers = () => ({ + [CALL_API]: { + endpoint: 'volunteers/getAllVolunteers', + method: 'GET', + schema: arrayOfVolunteers, + types: [actions.LOAD_ALL_REQUEST, actions.LOAD_ALL_SUCCESS, actions.LOAD_ALL_FAILURE] + } +}) + export const loadVolunteer = (id, admin) => ({ [CALL_API]: { endpoint: admin ? `admin/volunteers/${id}` : `volunteer/${id}`, diff --git a/client/modules/volunteerScheduling/VolunteerSchedulingRouter.js b/client/modules/volunteerScheduling/VolunteerSchedulingRouter.js new file mode 100644 index 00000000..f8a427ee --- /dev/null +++ b/client/modules/volunteerScheduling/VolunteerSchedulingRouter.js @@ -0,0 +1,16 @@ +import React from 'react' +import {Route} from 'react-router-dom' + +import {ADMIN_ROLE} from '../../../common/constants' +import VolunteerScheduling from './components/VolunteerScheduling' +import requireRole from '../../components/router/requireRole' +import SwitchWithNotFound from '../../components/router/SwitchWithNotFound' + +const IsAdmin = requireRole([ADMIN_ROLE]) + +const VolunteerSchedulingRouter = ({match}) => + + + + +export default VolunteerSchedulingRouter diff --git a/client/modules/volunteerScheduling/components/AddShiftModal.js b/client/modules/volunteerScheduling/components/AddShiftModal.js new file mode 100644 index 00000000..28088d09 --- /dev/null +++ b/client/modules/volunteerScheduling/components/AddShiftModal.js @@ -0,0 +1,177 @@ +import React from 'react' +import { Button, FormGroup, FormControl, ControlLabel } from 'react-bootstrap' +import Autosuggest from 'react-bootstrap-autosuggest' + +import { Box, BoxHeader, BoxBody } from '../../../components/box' + +// import SendGrid = require('@sendgrid/mail'); + +export default class AddShiftModal extends React.Component { + + constructor(props) { + super(props) + + var now = new Date() + now.setHours(0,0,0,0) + + this.state = { + formInputFields: { + volunteerName: "", + id: "", + dateTime: "", + startTime: "", + duration: "", + notes: "" + }, + validInput: false, + nowDate: now, + touched: { volunteerName: false, dateTime: false} + } + } + + componentWillReceiveProps = nextProps => { + // If the modal is open and a save is complete, close the modal + if (this.props.saving && !nextProps.saving && !nextProps.saveError) { + this.props.closeAddModal() + } + } + + validate = () => { + this.setState({ validInput: this.getValidationState.all()}) + } + + formatData = () => { + return this.props.volunteers.map(volunteer => ({ ...volunteer, fullName: (volunteer.firstName + " " + volunteer.lastName)})) + } + + handleChange = { + volunteerName: value => { + if (typeof value === 'object' && value !== null) { + this.setState({formInputFields: {volunteerName: value.fullName, id: value._id}, touched: {...this.state.touched, volunteerName: true}}) + } else { + this.setState({ + formInputFields: { + ...this.state.formInputFields, + volunteerName: "" + }, + touched: { ...this.state.touched, volunteerName: true} + }, this.validate) + } + }, + dateTime: e => { + // document.getElementById("test").innerHTML = e.target.value + this.setState({formInputFields: {...this.state.formInputFields, dateTime: e.target.value}, touched: {...this.state.touched, dateTime: true}}, this.validate) + }, + notes: e => { + this.setState({formInputFields: {...this.state.formInputFields, notes: e.target.value}}, this.validate) + } + } + + getValidationState = { + // The following 3 functions validate the individual input fields and return the validation state used for react-bootstrap-table + volunteerName: () => + !this.state.touched.volunteerName || this.state.formInputFields.volunteerName.trim().length ? null : 'error', + dateTime: () => + // No dates in the past + !this.state.touched.dateTime || this.state.nowDate.getTime() < new Date(this.state.formInputFields.dateTime).getTime() ? null : 'error', + all: () => + this.state.formInputFields.volunteerName.trim().length > 0 && + this.state.formInputFields.dateTime !== "" && + this.state.nowDate.getTime() < new Date(this.state.formInputFields.dateTime).getTime() + } + + saveFood = () => { + const volunteer = this.props.getVolunteer(this.state.formInputFields.id) + var dateStr = this.state.formInputFields.dateTime + + this.props.makeShift({ + ...volunteer, + shift: { + role: volunteer.firstName, + date: new Date(dateStr), + duration: 2, + notes: this.state.formInputFields.notes + } + }) + + var event = { + role: volunteer.firstName, + date: new Date(dateStr), + duration: 2, + notes: this.state.formInputFields.notes + } + + var temp_date = new Date(dateStr) + var email_date = temp_date.toLocaleString('en-US') + + var temp_event = { + date: email_date + } + + // Used to send email + var credentials = { + email: volunteer.email, + shift: temp_event + } + + this.props.emailShift(credentials) + + this.props.updateCalendar(volunteer, event) + } + + render = () => { + return ( + + + Volunteer Scheduling + +
+
+ + +
+ + Volunteer Name + + + + Date & Time + + + + Notes (Optional) + + +
+
+ + +
+
+
+ ) + } +} diff --git a/client/modules/volunteerScheduling/components/EditModal.js b/client/modules/volunteerScheduling/components/EditModal.js new file mode 100644 index 00000000..fa331a5d --- /dev/null +++ b/client/modules/volunteerScheduling/components/EditModal.js @@ -0,0 +1,49 @@ +import React from 'react' +import { Button } from 'react-bootstrap' + +import { Box, BoxHeader, BoxBody } from '../../../components/box' + +export default class EditModal extends React.Component { + constructor(props) { + super(props) + this.state = { + delete: false + } + } + + deleteEvent = () => { + const id = this.props.shiftToDelete.extendedProps.volunteerId + const volunteer = this.props.getVolunteer(id) + this.props.deleteShift({ + ...volunteer, + del_shift: { + start: this.props.shiftToDelete.start + } + }) + this.props.deleteCalendarEvent() + } + + render = () => { + return ( + + +

Volunteer Name: {this.props.shiftToDelete.title}

+

Start Time: {this.props.shiftToDelete.start.toLocaleString('en-US')}

+

Notes: {this.props.shiftToDelete.extendedProps.notes}

+
+ + +
+ + +
+
+
+ ) + } +} diff --git a/client/modules/volunteerScheduling/components/VolunteerScheduling.js b/client/modules/volunteerScheduling/components/VolunteerScheduling.js new file mode 100644 index 00000000..cf4fc803 --- /dev/null +++ b/client/modules/volunteerScheduling/components/VolunteerScheduling.js @@ -0,0 +1,339 @@ +import React from 'react' +import {connect} from 'react-redux' +import { Modal } from 'react-bootstrap' +import selectors from '../../../store/selectors' +import {Box, BoxBody} from '../../../components/box' + +import { makeShift, updateShift, deleteShift, getAllVolunteers, emailShift } from '../../volunteer/reducer' + +import { Calendar } from '@fullcalendar/core' +import dayGridPlugin from '@fullcalendar/daygrid' +import bootstrapPlugin from '@fullcalendar/bootstrap' +import interactionPlugin from '@fullcalendar/interaction' + +import EditModal from './EditModal' +import AddShiftModal from './AddShiftModal' + +import '@fullcalendar/core/main.css' +import '@fullcalendar/daygrid/main.css' +import '@fullcalendar/bootstrap/main.css' + +import {loadVolunteers} from '../../volunteer/reducer' +import {loadQuestionnaires} from '../../questionnaire/reducers/api' + +import { clearFlags } from '../../food/reducers/item' + + +const mapStateToProps = state => ({ + volunteers: selectors.volunteer.getAll(state), + getVolunteer: selectors.volunteer.getOne(state), + loading: selectors.volunteer.loading(state), + loadError: selectors.volunteer.loadError(state), + saving: selectors.volunteer.saving(state), + saveError: selectors.volunteer.saveError(state) +}) + +const mapDispatchToProps = dispatch => ({ + makeShift: volunteerId => dispatch(makeShift(volunteerId)), + emailShift: credentials => dispatch(emailShift(credentials)), + updateShift: shift => dispatch(updateShift(shift)), + deleteShift: volunteer => dispatch(deleteShift(volunteer)), + getAllVolunteers: () => dispatch(getAllVolunteers()), + clearFlags: () => dispatch(clearFlags()), + loadVolunteers: () => dispatch(loadVolunteers()), + loadQuestionnaires: () => dispatch(loadQuestionnaires()) +}) + +export class VolunteerScheduling extends React.Component { + constructor(props) { + super(props) + // console.log("In Constructor") + this.props.loadVolunteers() + this.state = { + c: "", + showAddModal: false, + showEditModal: false, + shiftToDelete: "", + id: "", + events: [], + added: false, + removed: false, + moved: false, + load: true + } + } + + componentDidMount() { + // console.log("In componentDidMount") + var calendarEl = document.getElementById('calendar') + var calendar = new Calendar(calendarEl, { + plugins: [ dayGridPlugin, bootstrapPlugin, interactionPlugin ], + themeSystem: 'bootstrap', + defaultView: 'dayGridMonth', + editable: true, + header: { + center: 'addEventButton' + }, + + eventClick: info => { + this.setState({showEditModal: true, shiftToDelete: info.event}) + }, + + /* Triggered when dragging stops and the event has moved to a different day/time */ + eventDrop: info => { + const volunteer = this.props.getVolunteer(info.event.extendedProps.volunteerId) + this.setState({moved: true}) + this.props.updateShift({ + ...volunteer, + times: { + oldTime: new Date(info.oldEvent.start), + newTime: new Date(info.event.start) + } + }) + }, + + customButtons: { + addEventButton: { + text: 'Add Shift', + click: () => { + this.setState({showAddModal: true}) + } + } + } + }) + calendar.render() + this.setState({c: calendar}) + } + + // dragEvent = (event) => { + + // } + + updateCalendar = (volunteer, event) => { + //console.log("In Update Calendar") + const calendar = this.state.c + + var new_id = calendar.getEvents().length != 0 ? (calendar.getEvents()[calendar.getEvents().length-1].id + 1) : 0 + + var temp = { + id: new_id, + title: volunteer.firstName + ' ' + volunteer.lastName, + extendedProps: {volunteerId: volunteer._id, notes: event.notes}, + start: event.date + } + var temp_events = this.state.events + temp_events.push(temp) + this.setState({events: temp_events, added: true}) + } + + deleteCalendarEvent = () => { + //console.log("In Delete Calendar Event") + + var calendar = this.state.c + + /* Checking for case in which user adds an event and deletes it in same state */ + var sameState = false + var events = this.state.events + var delEvent = this.state.shiftToDelete + + for(var i = 0; i < events.length; i++) { + if(events[i].id == delEvent.id) { + var temp_event = calendar.getEventById(events[i].id) + temp_event.remove() + sameState = true + } + } + + if(sameState == false) { + var event = calendar.getEventById(this.state.shiftToDelete.id) + event.remove() + } + this.setState({showEditModal: false, removed: true}) + } + + /* If parameter "event" is provided, open the "Edit Modal", else open the "Add Modal" */ + openModal = event => () => { + if(event) { + this.setState({showEditModal: true, shiftToDelete: event}) + } + else { + this.setState({showAddModal: true}) + } + this.props.clearFlags() + } + + /* Closes the Add Modal */ + closeAddModal = () => { + this.setState({showAddModal: false}) + this.props.clearFlags() + } + + /* Closes the Edit Modal */ + closeEditModal = () => { + this.setState({showEditModal: false}) + this.props.clearFlags() + } + + // Returns true if all prev and new volunteers have sam eshift length + countShifts = (v1, v2) => { + for(var i = 0; i < v1.length; i++) { + var shift1 = v1[i].shift + var shift2 = v2[i].shift + + if(shift1.length != shift2.length) { + return false + } + } + return true + } + + /* Used to determine if there is a difference b/w the previous volunteer shifts and the volunteer shifts + ** True if theres a difference + */ + difference = (v1, v2) => { + if(this.countShifts(v1, v2) == false) { + return true + } + + for(var i = 0; i < v1.length; i++) { + for(var j = 0; j < v1[i].shift.length; j++) { + var date1 = new Date(v1[i].shift[j].date) + var date2 = new Date(v2[i].shift[j].date) + + if(date1.getTime() !== date2.getTime()) { + return true + } + } + } + return false + } + + componentDidUpdate(prevProps) { + // console.log("In componentDidUpdate") + + const calendar = this.state.c + + if(prevProps.volunteers.length != this.props.volunteers.length) { + //console.log("prevProps.volunteers.length != this.props.volunteers.length") + /* Removes all events from calendar */ + const events = calendar.getEvents() + for(var x = 0; x < events.length; x++) { + calendar.getEventById(events[x].id).remove() + } + + /* Re-renders all events on calendar */ + var volunteers = this.props.volunteers + var id_num = 0 + for(var i = 0; i < volunteers.length; i++) { + var shift = volunteers[i].shift + + for(var j = 0; j < shift.length; j++) { + calendar.addEvent({ + id: id_num, + title: volunteers[i].firstName + ' ' + volunteers[i].lastName, + extendedProps: {volunteerId: volunteers[i]._id, notes: shift[j].notes}, + start: new Date(shift[j].date) + }) + id_num++ + } + } + } + else if(this.state.added == true) { + var num = this.state.events.length - 1 + calendar.addEvent({ + id: this.state.events[num].id, + title: this.state.events[num].title, + extendedProps: this.state.events[num].extendedProps, + start: this.state.events[num].start + }) + this.setState({added: false}) + } + //else if(this.countShifts(this.props.volunteers) != this.countShifts(prevProps.volunteers) && this.state.removed == false) { + else if(this.difference(this.props.volunteers, prevProps.volunteers) == true && this.state.removed == false && this.state.moved == false) { + //console.log("this.countShifts(this.props.volunteers) != this.countShifts(prevProps.volunteers)") + /* Removes all events from calendar */ + const events = calendar.getEvents() + for(var q = 0; q < events.length; q++) { + calendar.getEventById(events[q].id).remove() + } + + var id_num1 = 0 + /* Re-renders all events on calendar */ + var volunteers1 = this.props.volunteers + for(var z = 0; z < volunteers1.length; z++) { + var shift1 = volunteers1[z].shift + + for(var w = 0; w < shift1.length; w++) { + calendar.addEvent({ + id: id_num1, + title: volunteers1[z].firstName + ' ' + volunteers1[z].lastName, + extendedProps: {volunteerId: volunteers1[z]._id, notes: shift1[w].notes}, + start: new Date(shift1[w].date) + }) + id_num1++ + } + } + } + else if(this.state.load == true) { + //console.log("this.state.load == true") + var volunteers2 = this.props.volunteers + var id_num2 = 0 + for(var i1 = 0; i1 < volunteers2.length; i1++) { + var shift2 = volunteers2[i1].shift + + for(var j1 = 0; j1 < shift2.length; j1++) { + calendar.addEvent({ + id: id_num2, + title: volunteers2[i1].firstName + ' ' + volunteers2[i1].lastName, + extendedProps: {volunteerId: volunteers2[i1]._id, notes: shift2[j1].notes}, + start: new Date(shift2[j1].date) + }) + id_num2++ + } + } + this.setState({load: false}) + } + } + + + render = () => { + const {loading, loadError} = this.props + return ( + + +
+
+ + + + + + +
+ ) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(VolunteerScheduling) \ No newline at end of file diff --git a/client/modules/volunteerScheduling/reducers/volunteerScheduling.js b/client/modules/volunteerScheduling/reducers/volunteerScheduling.js new file mode 100644 index 00000000..bc94041f --- /dev/null +++ b/client/modules/volunteerScheduling/reducers/volunteerScheduling.js @@ -0,0 +1,15 @@ +import {CALL_API} from '../../../store/middleware/api' +import {crudActions, crudReducer} from '../../../store/utils' + +export const actions = crudActions('volunteer') + +export const makeShift = volunteerId => ({ + [CALL_API]: { + endpoint: 'volunteers/addShift', + method: 'PUT', + body: volunteerId, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + +export default crudReducer('volunteer') \ No newline at end of file diff --git a/client/store/reducer.js b/client/store/reducer.js index 318329c4..7b70bc37 100644 --- a/client/store/reducer.js +++ b/client/store/reducer.js @@ -17,6 +17,7 @@ import questionnaireEditor from '../modules/questionnaire/reducers/editor' import settings from '../modules/settings/reducers/settings' import user from '../modules/users/userReducer' import volunteer from '../modules/volunteer/reducer' +import volunteerScheduling from '../modules/volunteerScheduling/reducers/volunteerScheduling' export default combineReducers({ entities, @@ -35,5 +36,6 @@ export default combineReducers({ router, settings, user, - volunteer + volunteer, + volunteerScheduling }) diff --git a/common/placeholders.js b/common/placeholders.js index 2dcbcd1c..30e7d983 100644 --- a/common/placeholders.js +++ b/common/placeholders.js @@ -5,6 +5,7 @@ const placeholderTypes = { } const placeholders = [ + {id: 'date', label: 'Shift Date'}, {id: 'organization', label: 'Foodbank Name'}, {id: 'address', label: 'Foodbank Address'}, {id: 'url', label: 'Foodbank Website'}, diff --git a/package-lock.json b/package-lock.json index ec39239d..e1dc4b7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,12 @@ "resolved": "https://registry.npmjs.org/@allenfang/react-toastr/-/react-toastr-2.8.2.tgz", "integrity": "sha1-C+9lhRieBXHda9/E75i8n5xH2gw=", "requires": { - "classnames": "^2.2.5", - "element-class": "^0.2.2", - "lodash": "^4.16.1", - "react": "^0.14.0 || <15.4.0", - "react-addons-update": "^0.14.0 || <15.4.0", - "react-dom": "^0.14.0 || <15.4.0" + "classnames": "2.2.5", + "element-class": "0.2.2", + "lodash": "4.17.10", + "react": "15.3.2", + "react-addons-update": "15.3.2", + "react-dom": "15.3.2" }, "dependencies": { "react": { @@ -22,9 +22,9 @@ "resolved": "https://registry.npmjs.org/react/-/react-15.3.2.tgz", "integrity": "sha1-p7zNL+6K8SawMX4iLCjR1UUo0J4=", "requires": { - "fbjs": "^0.8.4", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.0" + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "react-dom": { @@ -50,10 +50,10 @@ "dev": true, "requires": { "@babel/types": "7.0.0-beta.44", - "jsesc": "^2.5.1", - "lodash": "^4.2.0", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "jsesc": "2.5.1", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" }, "dependencies": { "jsesc": { @@ -105,9 +105,9 @@ "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "@babel/parser": { @@ -125,7 +125,7 @@ "@babel/code-frame": "7.0.0-beta.44", "@babel/types": "7.0.0-beta.44", "babylon": "7.0.0-beta.44", - "lodash": "^4.2.0" + "lodash": "4.17.10" }, "dependencies": { "babylon": { @@ -148,10 +148,10 @@ "@babel/helper-split-export-declaration": "7.0.0-beta.44", "@babel/types": "7.0.0-beta.44", "babylon": "7.0.0-beta.44", - "debug": "^3.1.0", - "globals": "^11.1.0", - "invariant": "^2.2.0", - "lodash": "^4.2.0" + "debug": "3.1.0", + "globals": "11.5.0", + "invariant": "2.2.4", + "lodash": "4.17.10" }, "dependencies": { "babylon": { @@ -183,9 +183,9 @@ "integrity": "sha512-5eTV4WRmqbaFM3v9gHAIljEQJU4Ssc6fxL61JN+Oe2ga/BwyjzjamwkCVVAQjHGuAX8i0BWo42dshL8eO5KfLQ==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.2.0", - "to-fast-properties": "^2.0.0" + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "2.0.0" }, "dependencies": { "to-fast-properties": { @@ -196,11 +196,64 @@ } } }, + "@fullcalendar/bootstrap": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/bootstrap/-/bootstrap-4.0.1.tgz", + "integrity": "sha512-aKyb4LQjoCLtWfmOT/GPkPOgvxF/IQznJeKBmnw6co/F1drgwKtCrBKkrXarD7ayN+MXVzLQ8vAIqcMnO1IVjw==" + }, + "@fullcalendar/core": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-4.0.1.tgz", + "integrity": "sha512-dUhBtZ2hIuPFJP4Rr1QOFs5/nG5EAnzqMDvy1IZ0JEBw/+0Dwj4X55Ugb3buHBh5GDrQmPbZVW21mbcUqeTprQ==" + }, + "@fullcalendar/daygrid": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-4.0.1.tgz", + "integrity": "sha512-nKFFKgTm5eijOukD6pjsQSLlsBoMrKuHXc00zaHaMWSeJndGHxtseR8oeHOnclNNScMKLXLNZthzEduSqqyuRw==" + }, + "@fullcalendar/interaction": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-4.0.1.tgz", + "integrity": "sha512-JvbcK6PLqPDxUsiDHc+BD2qXt7Se9GmriezKQc45Tt2I1+z7NSmMmzuA5QiKYGCnz/Sii/QfQ6JiI3t59iOLLg==" + }, + "@fullcalendar/resource-common": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-common/-/resource-common-4.0.1.tgz", + "integrity": "sha512-KWPTMO+UNsbaU7J+2cXVucgbiEOeNAIN1+9W+mJ96MPx2aeljAPdrxY2co0frk3usbzQ0P47DkSx05k5r6FkgA==" + }, "@mapbox/polyline": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@mapbox/polyline/-/polyline-0.2.0.tgz", "integrity": "sha1-biWYB0SqIjMflLZFpULALT/P7pc=" }, + "@sendgrid/client": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-6.3.0.tgz", + "integrity": "sha512-fTy8vRpA9Whtf8ULQr/0vkSZaQvGQ97rY5N5PrevKRtugJMsJqFMKO0pwzEWeqITSg71aMMTj57QTgw3SjZvnQ==", + "requires": { + "@sendgrid/helpers": "6.3.0", + "@types/request": "2.48.1", + "request": "2.85.0" + } + }, + "@sendgrid/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-uTFcmhCDFg/2Uhz+z/cLwyLHH0UsblG49hKwdR7nKbWsGKWv4js7W32FlPdXqy2C/plTJ20vcPLgKM1m3F/MjQ==", + "requires": { + "chalk": "2.4.1", + "deepmerge": "2.2.1" + } + }, + "@sendgrid/mail": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-6.3.1.tgz", + "integrity": "sha512-5zIeAV9iU+0hQkrOQ/D4RB2MfpK+lNbOortIfQdCh95aMDF/TRc9WB8FGNhmQrx9YMuJTms5eiBklF0Fi/dbVg==", + "requires": { + "@sendgrid/client": "6.3.0", + "@sendgrid/helpers": "6.3.0" + } + }, "@sinonjs/formatio": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", @@ -210,6 +263,19 @@ "samsam": "1.3.0" } }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" + }, + "@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "requires": { + "@types/node": "6.0.107" + } + }, "@types/node": { "version": "6.0.107", "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.107.tgz", @@ -220,7 +286,7 @@ "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.6.tgz", "integrity": "sha512-cORm6a2Z18M3DRMR1oE29hMFycyUkA0I0K0Bbmo0gUs9/7HzNShLTV/e9btn6asoanYxQR3Mys+QSXPO/I57CQ==", "requires": { - "parchment": "^1.1.2" + "parchment": "1.1.4" } }, "@types/react": { @@ -228,16 +294,32 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-16.3.13.tgz", "integrity": "sha512-YMFH/E9ryjUm2AoOy8KdTuG1SufaMuYmO/5xACROl0pm9dRmE2RN3d2zjv/eHALF6LGRZPVb7G9kqP0n5dWttQ==", "requires": { - "csstype": "^2.2.0" + "csstype": "2.4.2" } }, + "@types/request": { + "version": "2.48.1", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz", + "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", + "requires": { + "@types/caseless": "0.12.2", + "@types/form-data": "2.2.1", + "@types/node": "6.0.107", + "@types/tough-cookie": "2.3.5" + } + }, + "@types/tough-cookie": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", + "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==" + }, "JSONStream": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "jsonparse": "1.3.1", + "through": "2.3.8" } }, "abab": { @@ -256,7 +338,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "requires": { - "mime-types": "~2.1.18", + "mime-types": "2.1.18", "negotiator": "0.6.1" } }, @@ -270,7 +352,7 @@ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", "requires": { - "acorn": "^4.0.3" + "acorn": "4.0.13" }, "dependencies": { "acorn": { @@ -286,8 +368,8 @@ "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", "dev": true, "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" + "acorn": "6.0.2", + "acorn-walk": "6.1.0" }, "dependencies": { "acorn": { @@ -304,7 +386,7 @@ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { - "acorn": "^3.0.4" + "acorn": "3.3.0" }, "dependencies": { "acorn": { @@ -320,8 +402,8 @@ "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", "requires": { - "acorn": "^5.4.1", - "xtend": "^4.0.1" + "acorn": "5.5.3", + "xtend": "4.0.1" } }, "acorn-walk": { @@ -340,34 +422,34 @@ "resolved": "https://registry.npmjs.org/admin-lte/-/admin-lte-2.4.3.tgz", "integrity": "sha512-9ZjrBxjmlFl5yErftAPfelNaqm5w4sXr2JJoOikasPSrufqROb6MyUzSGcb60ZBdtT9gPCBQbXo+ebVrzOAt3Q==", "requires": { - "bootstrap": "^3.3.7", - "bootstrap-colorpicker": "^2.5.1", - "bootstrap-datepicker": "^1.7.0", - "bootstrap-daterangepicker": "^2.1.25", - "bootstrap-slider": "^9.8.0", - "bootstrap-timepicker": "^0.5.2", - "chart.js": "1.0.*", - "ckeditor": "^4.7.0", - "datatables.net": "^1.10.15", - "datatables.net-bs": "^1.10.15", - "fastclick": "^1.0.6", - "flot": "^0.8.0-alpha", - "font-awesome": "^4.7.0", - "fullcalendar": "^3.4.0", - "inputmask": "^3.3.7", - "ion-rangeslider": "^2.2.0", - "ionicons": "^3.0.0", - "jquery": "^3.2.1", - "jquery-knob": "^1.2.11", - "jquery-sparkline": "^2.4.0", - "jquery-ui": "^1.12.1", - "jvectormap": "^1.2.2", - "moment": "^2.18.1", - "morris.js": "^0.5.0", + "bootstrap": "3.3.7", + "bootstrap-colorpicker": "2.5.2", + "bootstrap-datepicker": "1.8.0", + "bootstrap-daterangepicker": "2.1.30", + "bootstrap-slider": "9.10.0", + "bootstrap-timepicker": "0.5.2", + "chart.js": "1.0.2", + "ckeditor": "4.9.2", + "datatables.net": "1.10.16", + "datatables.net-bs": "1.10.16", + "fastclick": "1.0.6", + "flot": "0.8.0-alpha", + "font-awesome": "4.7.0", + "fullcalendar": "3.9.0", + "inputmask": "3.3.11", + "ion-rangeslider": "2.2.0", + "ionicons": "3.0.0", + "jquery": "3.3.1", + "jquery-knob": "1.2.11", + "jquery-sparkline": "2.4.0", + "jquery-ui": "1.12.1", + "jvectormap": "1.2.2", + "moment": "2.22.1", + "morris.js": "0.5.0", "pace": "0.0.4", - "raphael": "^2.2.7", - "select2": "^4.0.3", - "slimscroll": "^0.9.1" + "raphael": "2.2.7", + "select2": "4.0.5", + "slimscroll": "0.9.1" } }, "after": { @@ -380,10 +462,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "ajv-keywords": { @@ -396,9 +478,9 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" }, "dependencies": { "kind-of": { @@ -406,7 +488,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -426,7 +508,7 @@ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "requires": { - "string-width": "^2.0.0" + "string-width": "2.1.1" } }, "ansi-colors": { @@ -434,13 +516,13 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "requires": { - "ansi-wrap": "^0.1.0" + "ansi-wrap": "0.1.0" } }, "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, "ansi-gray": { @@ -466,7 +548,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.1" } }, "ansi-wrap": { @@ -484,8 +566,8 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" } }, "append-field": { @@ -508,7 +590,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "arity-n": { @@ -567,8 +649,8 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "define-properties": "1.1.2", + "es-abstract": "1.11.0" } }, "array-map": { @@ -591,7 +673,7 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -629,9 +711,9 @@ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "assert": { @@ -663,7 +745,7 @@ "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", "requires": { - "acorn": "^4.0.3" + "acorn": "4.0.13" }, "dependencies": { "acorn": { @@ -678,7 +760,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", "requires": { - "lodash": "^4.14.0" + "lodash": "4.17.10" } }, "async-each": { @@ -779,12 +861,12 @@ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz", "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==", "requires": { - "browserslist": "^2.11.3", - "caniuse-lite": "^1.0.30000805", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^6.0.17", - "postcss-value-parser": "^3.2.3" + "browserslist": "2.11.3", + "caniuse-lite": "1.0.30000832", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "6.0.22", + "postcss-value-parser": "3.3.0" } }, "aws-sign2": { @@ -802,9 +884,9 @@ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" }, "dependencies": { "ansi-styles": { @@ -817,11 +899,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { @@ -836,25 +918,25 @@ "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" + "babel-code-frame": "6.26.0", + "babel-generator": "6.26.1", + "babel-helpers": "6.24.1", + "babel-messages": "6.23.0", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "convert-source-map": "1.5.1", + "debug": "2.6.9", + "json5": "0.5.1", + "lodash": "4.17.10", + "minimatch": "3.0.4", + "path-is-absolute": "1.0.1", + "private": "0.1.8", + "slash": "1.0.0", + "source-map": "0.5.7" }, "dependencies": { "source-map": { @@ -874,8 +956,8 @@ "@babel/traverse": "7.0.0-beta.44", "@babel/types": "7.0.0-beta.44", "babylon": "7.0.0-beta.44", - "eslint-scope": "~3.7.1", - "eslint-visitor-keys": "^1.0.0" + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0" }, "dependencies": { "babylon": { @@ -891,14 +973,14 @@ "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" }, "dependencies": { "source-map": { @@ -913,9 +995,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-builder-binary-assignment-operator-visitor": { @@ -923,9 +1005,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-explode-assignable-expression": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-builder-react-jsx": { @@ -933,9 +1015,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "esutils": "^2.0.2" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "esutils": "2.0.2" } }, "babel-helper-call-delegate": { @@ -943,10 +1025,10 @@ "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-define-map": { @@ -954,10 +1036,10 @@ "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.10" } }, "babel-helper-explode-assignable-expression": { @@ -965,9 +1047,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-explode-class": { @@ -975,10 +1057,10 @@ "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", "requires": { - "babel-helper-bindify-decorators": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-bindify-decorators": "6.24.1", + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-function-name": { @@ -986,11 +1068,11 @@ "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-get-function-arity": { @@ -998,8 +1080,8 @@ "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-hoist-variables": { @@ -1007,8 +1089,8 @@ "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-optimise-call-expression": { @@ -1016,8 +1098,8 @@ "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-regex": { @@ -1025,9 +1107,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.10" } }, "babel-helper-remap-async-to-generator": { @@ -1035,11 +1117,11 @@ "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-replace-supers": { @@ -1047,12 +1129,12 @@ "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-optimise-call-expression": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helpers": { @@ -1060,8 +1142,8 @@ "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-loader": { @@ -1069,9 +1151,9 @@ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.4.tgz", "integrity": "sha512-/hbyEvPzBJuGpk9o80R0ZyTej6heEOr59GoEUtn8qFKbnx4cJm9FWES6J/iv644sYgrtVw9JJQkjaLW/bqb5gw==", "requires": { - "find-cache-dir": "^1.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1" + "find-cache-dir": "1.0.0", + "loader-utils": "1.1.0", + "mkdirp": "0.5.1" } }, "babel-messages": { @@ -1079,7 +1161,7 @@ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-check-es2015-constants": { @@ -1087,7 +1169,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-istanbul": { @@ -1096,10 +1178,10 @@ "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", "dev": true, "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.13.0", - "find-up": "^2.1.0", - "istanbul-lib-instrument": "^1.10.1", - "test-exclude": "^4.2.1" + "babel-plugin-syntax-object-rest-spread": "6.13.0", + "find-up": "2.1.0", + "istanbul-lib-instrument": "1.10.1", + "test-exclude": "4.2.1" } }, "babel-plugin-rewire": { @@ -1183,9 +1265,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-generators": "^6.5.0", - "babel-runtime": "^6.22.0" + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-generators": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-async-to-generator": { @@ -1193,9 +1275,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-class-constructor-call": { @@ -1203,9 +1285,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", "requires": { - "babel-plugin-syntax-class-constructor-call": "^6.18.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-syntax-class-constructor-call": "6.18.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-class-properties": { @@ -1213,10 +1295,10 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-plugin-syntax-class-properties": "6.13.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-decorators": { @@ -1224,11 +1306,11 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", "requires": { - "babel-helper-explode-class": "^6.24.1", - "babel-plugin-syntax-decorators": "^6.13.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-explode-class": "6.24.1", + "babel-plugin-syntax-decorators": "6.13.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-do-expressions": { @@ -1236,8 +1318,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz", "integrity": "sha1-KMyvkoEtlJws0SgfaQyP3EaK6bs=", "requires": { - "babel-plugin-syntax-do-expressions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-do-expressions": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-arrow-functions": { @@ -1245,7 +1327,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-block-scoped-functions": { @@ -1253,7 +1335,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-block-scoping": { @@ -1261,11 +1343,11 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.10" } }, "babel-plugin-transform-es2015-classes": { @@ -1273,15 +1355,15 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-define-map": "6.26.0", + "babel-helper-function-name": "6.24.1", + "babel-helper-optimise-call-expression": "6.24.1", + "babel-helper-replace-supers": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-computed-properties": { @@ -1289,8 +1371,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-destructuring": { @@ -1298,7 +1380,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-duplicate-keys": { @@ -1306,8 +1388,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-for-of": { @@ -1315,7 +1397,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-function-name": { @@ -1323,9 +1405,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-literals": { @@ -1333,7 +1415,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-modules-amd": { @@ -1341,9 +1423,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-modules-commonjs": { @@ -1351,10 +1433,10 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" + "babel-plugin-transform-strict-mode": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-modules-systemjs": { @@ -1362,9 +1444,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-modules-umd": { @@ -1372,9 +1454,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-object-super": { @@ -1382,8 +1464,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" + "babel-helper-replace-supers": "6.24.1", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-parameters": { @@ -1391,12 +1473,12 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-call-delegate": "6.24.1", + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-shorthand-properties": { @@ -1404,8 +1486,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-spread": { @@ -1413,7 +1495,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-sticky-regex": { @@ -1421,9 +1503,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-template-literals": { @@ -1431,7 +1513,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-typeof-symbol": { @@ -1439,7 +1521,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-unicode-regex": { @@ -1447,9 +1529,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "regexpu-core": "2.0.0" } }, "babel-plugin-transform-exponentiation-operator": { @@ -1457,9 +1539,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", + "babel-plugin-syntax-exponentiation-operator": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-export-extensions": { @@ -1467,8 +1549,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", "requires": { - "babel-plugin-syntax-export-extensions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-export-extensions": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-flow-strip-types": { @@ -1476,8 +1558,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", "requires": { - "babel-plugin-syntax-flow": "^6.18.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-flow": "6.18.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-function-bind": { @@ -1485,8 +1567,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz", "integrity": "sha1-xvuOlqwpajELjPjqQBRiQH3fapc=", "requires": { - "babel-plugin-syntax-function-bind": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-function-bind": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-object-rest-spread": { @@ -1494,8 +1576,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.26.0" + "babel-plugin-syntax-object-rest-spread": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-react-display-name": { @@ -1503,7 +1585,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-react-jsx": { @@ -1511,9 +1593,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", "requires": { - "babel-helper-builder-react-jsx": "^6.24.1", - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-builder-react-jsx": "6.26.0", + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-react-jsx-self": { @@ -1521,8 +1603,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", "requires": { - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-react-jsx-source": { @@ -1530,8 +1612,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", "requires": { - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-regenerator": { @@ -1539,7 +1621,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "requires": { - "regenerator-transform": "^0.10.0" + "regenerator-transform": "0.10.1" } }, "babel-plugin-transform-strict-mode": { @@ -1547,8 +1629,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-polyfill": { @@ -1556,9 +1638,9 @@ "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" + "babel-runtime": "6.26.0", + "core-js": "2.5.5", + "regenerator-runtime": "0.10.5" }, "dependencies": { "regenerator-runtime": { @@ -1573,30 +1655,30 @@ "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.24.1", - "babel-plugin-transform-es2015-classes": "^6.24.1", - "babel-plugin-transform-es2015-computed-properties": "^6.24.1", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.24.1", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-umd": "^6.24.1", - "babel-plugin-transform-es2015-object-super": "^6.24.1", - "babel-plugin-transform-es2015-parameters": "^6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", - "babel-plugin-transform-regenerator": "^6.24.1" + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.26.0", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", + "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", + "babel-plugin-transform-es2015-modules-umd": "6.24.1", + "babel-plugin-transform-es2015-object-super": "6.24.1", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.24.1", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-regenerator": "6.26.0" } }, "babel-preset-flow": { @@ -1604,7 +1686,7 @@ "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", "requires": { - "babel-plugin-transform-flow-strip-types": "^6.22.0" + "babel-plugin-transform-flow-strip-types": "6.22.0" } }, "babel-preset-react": { @@ -1612,12 +1694,12 @@ "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", "requires": { - "babel-plugin-syntax-jsx": "^6.3.13", - "babel-plugin-transform-react-display-name": "^6.23.0", - "babel-plugin-transform-react-jsx": "^6.24.1", - "babel-plugin-transform-react-jsx-self": "^6.22.0", - "babel-plugin-transform-react-jsx-source": "^6.22.0", - "babel-preset-flow": "^6.23.0" + "babel-plugin-syntax-jsx": "6.18.0", + "babel-plugin-transform-react-display-name": "6.25.0", + "babel-plugin-transform-react-jsx": "6.24.1", + "babel-plugin-transform-react-jsx-self": "6.22.0", + "babel-plugin-transform-react-jsx-source": "6.22.0", + "babel-preset-flow": "6.23.0" } }, "babel-preset-stage-0": { @@ -1625,9 +1707,9 @@ "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz", "integrity": "sha1-VkLRUEL5E4TX5a+LyIsduVsDnmo=", "requires": { - "babel-plugin-transform-do-expressions": "^6.22.0", - "babel-plugin-transform-function-bind": "^6.22.0", - "babel-preset-stage-1": "^6.24.1" + "babel-plugin-transform-do-expressions": "6.22.0", + "babel-plugin-transform-function-bind": "6.22.0", + "babel-preset-stage-1": "6.24.1" } }, "babel-preset-stage-1": { @@ -1635,9 +1717,9 @@ "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", "requires": { - "babel-plugin-transform-class-constructor-call": "^6.24.1", - "babel-plugin-transform-export-extensions": "^6.22.0", - "babel-preset-stage-2": "^6.24.1" + "babel-plugin-transform-class-constructor-call": "6.24.1", + "babel-plugin-transform-export-extensions": "6.22.0", + "babel-preset-stage-2": "6.24.1" } }, "babel-preset-stage-2": { @@ -1645,10 +1727,10 @@ "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators": "^6.24.1", - "babel-preset-stage-3": "^6.24.1" + "babel-plugin-syntax-dynamic-import": "6.18.0", + "babel-plugin-transform-class-properties": "6.24.1", + "babel-plugin-transform-decorators": "6.24.1", + "babel-preset-stage-3": "6.24.1" } }, "babel-preset-stage-3": { @@ -1656,11 +1738,11 @@ "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", "requires": { - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-generator-functions": "^6.24.1", - "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-plugin-transform-exponentiation-operator": "^6.24.1", - "babel-plugin-transform-object-rest-spread": "^6.22.0" + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-generator-functions": "6.24.1", + "babel-plugin-transform-async-to-generator": "6.24.1", + "babel-plugin-transform-exponentiation-operator": "6.24.1", + "babel-plugin-transform-object-rest-spread": "6.26.0" } }, "babel-register": { @@ -1668,13 +1750,13 @@ "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" + "babel-core": "6.26.3", + "babel-runtime": "6.26.0", + "core-js": "2.5.5", + "home-or-tmp": "2.0.0", + "lodash": "4.17.10", + "mkdirp": "0.5.1", + "source-map-support": "0.4.18" } }, "babel-runtime": { @@ -1682,8 +1764,8 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.5", + "regenerator-runtime": "0.11.1" } }, "babel-template": { @@ -1691,11 +1773,11 @@ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.10" } }, "babel-traverse": { @@ -1703,15 +1785,15 @@ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.4", + "lodash": "4.17.10" } }, "babel-types": { @@ -1719,10 +1801,10 @@ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "1.0.3" } }, "babylon": { @@ -1745,13 +1827,13 @@ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -1759,7 +1841,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -1767,7 +1849,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -1775,7 +1857,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -1783,9 +1865,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -1816,7 +1898,7 @@ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "beeper": { @@ -1863,15 +1945,15 @@ "integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=", "requires": { "bytes": "2.4.0", - "content-type": "~1.0.2", + "content-type": "1.0.4", "debug": "2.6.7", - "depd": "~1.1.0", - "http-errors": "~1.6.1", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.15", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.4.0", - "raw-body": "~2.2.0", - "type-is": "~1.6.15" + "raw-body": "2.2.0", + "type-is": "1.6.16" }, "dependencies": { "debug": { @@ -1889,12 +1971,12 @@ "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "array-flatten": "2.1.1", + "deep-equal": "1.0.1", + "dns-equal": "1.0.0", + "dns-txt": "2.0.2", + "multicast-dns": "6.2.3", + "multicast-dns-service-types": "1.1.0" }, "dependencies": { "array-flatten": { @@ -1914,7 +1996,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "requires": { - "hoek": "4.x.x" + "hoek": "4.2.1" } }, "bootstrap": { @@ -1927,7 +2009,7 @@ "resolved": "https://registry.npmjs.org/bootstrap-colorpicker/-/bootstrap-colorpicker-2.5.2.tgz", "integrity": "sha512-krzBno9AMUwI2+IDwMvjnpqpa2f8womW0CCKmEcxGzVkolCFrt22jjMjzx1NZqB8C1DUdNgZP4LfyCsgpHRiYA==", "requires": { - "jquery": ">=1.10" + "jquery": "3.3.1" } }, "bootstrap-datepicker": { @@ -1935,7 +2017,7 @@ "resolved": "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.8.0.tgz", "integrity": "sha512-213St/G8KT3mjs4qu4qwww74KWysMaIeqgq5OhrboZjIjemIpyuxlSo9FNNI5+KzpkkxkRRba+oewiRGV42B1A==", "requires": { - "jquery": ">=1.7.1 <4.0.0" + "jquery": "3.3.1" } }, "bootstrap-daterangepicker": { @@ -1943,8 +2025,8 @@ "resolved": "https://registry.npmjs.org/bootstrap-daterangepicker/-/bootstrap-daterangepicker-2.1.30.tgz", "integrity": "sha1-+JPb//Wk19+qt1Rg6OppabuJaJo=", "requires": { - "jquery": ">=1.10", - "moment": "^2.9.0" + "jquery": "3.3.1", + "moment": "2.22.1" } }, "bootstrap-slider": { @@ -1967,13 +2049,13 @@ "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.4.1", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "2.0.0" } }, "brace-expansion": { @@ -1981,7 +2063,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -1990,16 +2072,16 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -2007,7 +2089,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -2022,12 +2104,12 @@ "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", "requires": { - "JSONStream": "^1.0.3", - "combine-source-map": "~0.8.0", - "defined": "^1.0.0", - "safe-buffer": "^5.1.1", - "through2": "^2.0.0", - "umd": "^3.0.0" + "JSONStream": "1.3.2", + "combine-source-map": "0.8.0", + "defined": "1.0.0", + "safe-buffer": "5.1.2", + "through2": "2.0.3", + "umd": "3.0.3" } }, "browser-process-hrtime": { @@ -2056,54 +2138,54 @@ "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.2.tgz", "integrity": "sha512-fMES05wq1Oukts6ksGUU2TMVHHp06LyQt0SIwbXIHm7waSrQmNBZePsU0iM/4f94zbvb/wHma+D1YrdzWYnF/A==", "requires": { - "JSONStream": "^1.0.3", - "assert": "^1.4.0", - "browser-pack": "^6.0.1", - "browser-resolve": "^1.11.0", - "browserify-zlib": "~0.2.0", - "buffer": "^5.0.2", - "cached-path-relative": "^1.0.0", - "concat-stream": "^1.6.0", - "console-browserify": "^1.1.0", - "constants-browserify": "~1.0.0", - "crypto-browserify": "^3.0.0", - "defined": "^1.0.0", - "deps-sort": "^2.0.0", - "domain-browser": "^1.2.0", - "duplexer2": "~0.1.2", - "events": "^2.0.0", - "glob": "^7.1.0", - "has": "^1.0.0", - "htmlescape": "^1.1.0", - "https-browserify": "^1.0.0", - "inherits": "~2.0.1", - "insert-module-globals": "^7.0.0", - "labeled-stream-splicer": "^2.0.0", - "mkdirp": "^0.5.0", - "module-deps": "^6.0.0", - "os-browserify": "~0.3.0", - "parents": "^1.0.1", - "path-browserify": "~0.0.0", - "process": "~0.11.0", - "punycode": "^1.3.2", - "querystring-es3": "~0.2.0", - "read-only-stream": "^2.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.1.4", - "shasum": "^1.0.0", - "shell-quote": "^1.6.1", - "stream-browserify": "^2.0.0", - "stream-http": "^2.0.0", - "string_decoder": "^1.1.1", - "subarg": "^1.0.0", - "syntax-error": "^1.1.1", - "through2": "^2.0.0", - "timers-browserify": "^1.0.1", + "JSONStream": "1.3.2", + "assert": "1.4.1", + "browser-pack": "6.1.0", + "browser-resolve": "1.11.2", + "browserify-zlib": "0.2.0", + "buffer": "5.1.0", + "cached-path-relative": "1.0.1", + "concat-stream": "1.6.2", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "defined": "1.0.0", + "deps-sort": "2.0.0", + "domain-browser": "1.2.0", + "duplexer2": "0.1.4", + "events": "2.0.0", + "glob": "7.1.2", + "has": "1.0.1", + "htmlescape": "1.1.1", + "https-browserify": "1.0.0", + "inherits": "2.0.3", + "insert-module-globals": "7.0.6", + "labeled-stream-splicer": "2.0.1", + "mkdirp": "0.5.1", + "module-deps": "6.0.2", + "os-browserify": "0.3.0", + "parents": "1.0.1", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "read-only-stream": "2.0.0", + "readable-stream": "2.2.7", + "resolve": "1.7.1", + "shasum": "1.0.2", + "shell-quote": "1.6.1", + "stream-browserify": "2.0.1", + "stream-http": "2.8.1", + "string_decoder": "1.1.1", + "subarg": "1.0.0", + "syntax-error": "1.4.0", + "through2": "2.0.3", + "timers-browserify": "1.4.2", "tty-browserify": "0.0.1", - "url": "~0.11.0", - "util": "~0.10.1", - "vm-browserify": "^1.0.0", - "xtend": "^4.0.0" + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "1.0.1", + "xtend": "4.0.1" }, "dependencies": { "base64-js": { @@ -2116,8 +2198,8 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz", "integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "base64-js": "1.3.0", + "ieee754": "1.1.11" } }, "duplexer2": { @@ -2125,7 +2207,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "2.2.7" } }, "events": { @@ -2143,7 +2225,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "timers-browserify": { @@ -2151,7 +2233,7 @@ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", "requires": { - "process": "~0.11.0" + "process": "0.11.10" } }, "tty-browserify": { @@ -2171,12 +2253,12 @@ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "browserify-cipher": { @@ -2184,9 +2266,9 @@ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "browserify-aes": "1.2.0", + "browserify-des": "1.0.1", + "evp_bytestokey": "1.0.3" } }, "browserify-des": { @@ -2194,9 +2276,9 @@ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz", "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1" + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "2.0.3" } }, "browserify-rsa": { @@ -2204,8 +2286,8 @@ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "bn.js": "4.11.8", + "randombytes": "2.0.6" } }, "browserify-sign": { @@ -2213,13 +2295,13 @@ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "elliptic": "6.4.0", + "inherits": "2.0.3", + "parse-asn1": "5.1.1" } }, "browserify-zlib": { @@ -2227,7 +2309,7 @@ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "requires": { - "pako": "~1.0.5" + "pako": "1.0.6" } }, "browserslist": { @@ -2235,8 +2317,8 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", "requires": { - "caniuse-lite": "^1.0.30000792", - "electron-to-chromium": "^1.3.30" + "caniuse-lite": "1.0.30000832", + "electron-to-chromium": "1.3.44" } }, "bson": { @@ -2249,9 +2331,9 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "1.3.0", + "ieee754": "1.1.11", + "isarray": "1.0.0" }, "dependencies": { "base64-js": { @@ -2297,7 +2379,7 @@ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", "requires": { "dicer": "0.2.5", - "readable-stream": "1.1.x" + "readable-stream": "1.1.14" }, "dependencies": { "isarray": { @@ -2310,10 +2392,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -2333,19 +2415,19 @@ "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" + "bluebird": "3.5.1", + "chownr": "1.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.2", + "mississippi": "2.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "5.3.0", + "unique-filename": "1.1.0", + "y18n": "4.0.0" } }, "cache-base": { @@ -2353,15 +2435,15 @@ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" } }, "cached-path-relative": { @@ -2375,7 +2457,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "^0.2.0" + "callsites": "0.2.0" } }, "callsite": { @@ -2394,8 +2476,8 @@ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" + "no-case": "2.3.2", + "upper-case": "1.1.3" } }, "camelcase": { @@ -2408,8 +2490,8 @@ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "camelcase": "2.1.1", + "map-obj": "1.0.1" }, "dependencies": { "camelcase": { @@ -2434,10 +2516,10 @@ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", "requires": { - "browserslist": "^1.3.6", - "caniuse-db": "^1.0.30000529", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000832", + "lodash.memoize": "4.1.2", + "lodash.uniq": "4.5.0" }, "dependencies": { "browserslist": { @@ -2445,8 +2527,8 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" + "caniuse-db": "1.0.30000832", + "electron-to-chromium": "1.3.44" } } } @@ -2476,8 +2558,8 @@ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chai": { @@ -2486,12 +2568,12 @@ "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", "dev": true, "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", - "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" + "assertion-error": "1.1.0", + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.0", + "type-detect": "4.0.8" } }, "chai-as-promised": { @@ -2500,7 +2582,7 @@ "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", "dev": true, "requires": { - "check-error": "^1.0.2" + "check-error": "1.0.2" } }, "chai-enzyme": { @@ -2509,7 +2591,7 @@ "integrity": "sha512-vWT101M7qjq6kM/29G4vHrgLM4Mj1gCnKuvOSF03s8pFVsqol4B6USoGM/aYRKqaaIHs8/AxmHjWGFplQWhIQw==", "dev": true, "requires": { - "html": "^1.0.0" + "html": "1.0.0" } }, "chalk": { @@ -2517,9 +2599,9 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "change-emitter": { @@ -2554,22 +2636,22 @@ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" + "css-select": "1.2.0", + "dom-serializer": "0.1.0", + "entities": "1.1.1", + "htmlparser2": "3.9.2", + "lodash.assignin": "4.2.0", + "lodash.bind": "4.2.1", + "lodash.defaults": "4.2.0", + "lodash.filter": "4.6.0", + "lodash.flatten": "4.4.0", + "lodash.foreach": "4.5.0", + "lodash.map": "4.6.0", + "lodash.merge": "4.6.1", + "lodash.pick": "4.4.0", + "lodash.reduce": "4.6.0", + "lodash.reject": "4.6.0", + "lodash.some": "4.6.0" } }, "chickencurry": { @@ -2582,18 +2664,18 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.1.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.0" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.4", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.0.5" } }, "chownr": { @@ -2611,8 +2693,8 @@ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "circular-json": { @@ -2631,7 +2713,7 @@ "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", "requires": { - "chalk": "^1.1.3" + "chalk": "1.1.3" }, "dependencies": { "ansi-styles": { @@ -2644,11 +2726,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { @@ -2663,10 +2745,10 @@ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -2674,7 +2756,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -2694,7 +2776,7 @@ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz", "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", "requires": { - "source-map": "0.5.x" + "source-map": "0.5.7" }, "dependencies": { "source-map": { @@ -2715,7 +2797,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "2.0.0" } }, "cli-width": { @@ -2729,8 +2811,8 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -2761,7 +2843,7 @@ "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", "requires": { - "q": "^1.1.2" + "q": "1.5.1" } }, "code-point-at": { @@ -2774,8 +2856,8 @@ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color": { @@ -2783,9 +2865,9 @@ "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" + "clone": "1.0.4", + "color-convert": "1.9.1", + "color-string": "0.3.0" } }, "color-convert": { @@ -2793,7 +2875,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -2806,7 +2888,7 @@ "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", "requires": { - "color-name": "^1.0.0" + "color-name": "1.1.3" } }, "color-support": { @@ -2819,9 +2901,9 @@ "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", "requires": { - "color": "^0.11.0", + "color": "0.11.4", "css-color-names": "0.0.4", - "has": "^1.0.1" + "has": "1.0.1" } }, "colors": { @@ -2834,10 +2916,10 @@ "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", "requires": { - "convert-source-map": "~1.1.0", - "inline-source-map": "~0.6.0", - "lodash.memoize": "~3.0.3", - "source-map": "~0.5.3" + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" }, "dependencies": { "convert-source-map": { @@ -2862,7 +2944,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { @@ -2895,7 +2977,7 @@ "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-2.0.0.tgz", "integrity": "sha1-5kL6fh2iFSlyADFHZ3b8JGkawLA=", "requires": { - "arity-n": "^1.0.4" + "arity-n": "1.0.4" } }, "compressible": { @@ -2903,7 +2985,7 @@ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", "requires": { - "mime-db": ">= 1.33.0 < 2" + "mime-db": "1.33.0" } }, "compression": { @@ -2911,12 +2993,12 @@ "resolved": "http://registry.npmjs.org/compression/-/compression-1.6.2.tgz", "integrity": "sha1-zOsSHsydCcUtetDDNQ6pPd1AK8M=", "requires": { - "accepts": "~1.3.3", + "accepts": "1.3.5", "bytes": "2.3.0", - "compressible": "~2.0.8", - "debug": "~2.2.0", - "on-headers": "~1.0.1", - "vary": "~1.1.0" + "compressible": "2.0.13", + "debug": "2.2.0", + "on-headers": "1.0.1", + "vary": "1.1.2" }, "dependencies": { "bytes": { @@ -2949,10 +3031,10 @@ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "buffer-from": "1.0.0", + "inherits": "2.0.3", + "readable-stream": "2.2.7", + "typedarray": "0.0.6" } }, "configstore": { @@ -2960,12 +3042,12 @@ "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.2.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.0", + "xdg-basedir": "3.0.0" } }, "connect-history-api-fallback": { @@ -2978,8 +3060,8 @@ "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-1.3.2.tgz", "integrity": "sha1-fL9Y3/8mdg5eAOAX0KhbS8kLnTc=", "requires": { - "bluebird": "^3.0", - "mongodb": ">= 1.2.0 <3.0.0" + "bluebird": "3.5.1", + "mongodb": "2.2.35" } }, "console-browserify": { @@ -2987,7 +3069,7 @@ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "requires": { - "date-now": "^0.1.4" + "date-now": "0.1.4" } }, "constants-browserify": { @@ -3045,12 +3127,12 @@ "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" } }, "copy-descriptor": { @@ -3063,14 +3145,14 @@ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.1.tgz", "integrity": "sha512-OlTo6DYg0XfTKOF8eLf79wcHm4Ut10xU2cRBRPMW/NA5F9VMjZGTfRHWDIYC3s+1kObGYrBLshXWU1K0hILkNQ==", "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" + "cacache": "10.0.4", + "find-cache-dir": "1.0.0", + "globby": "7.1.1", + "is-glob": "4.0.0", + "loader-utils": "1.1.0", + "minimatch": "3.0.4", + "p-limit": "1.2.0", + "serialize-javascript": "1.5.0" } }, "core-js": { @@ -3088,13 +3170,13 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" + "is-directory": "0.3.1", + "js-yaml": "3.7.0", + "minimist": "1.2.0", + "object-assign": "4.1.1", + "os-homedir": "1.0.2", + "parse-json": "2.2.0", + "require-from-string": "1.2.1" }, "dependencies": { "minimist": { @@ -3114,8 +3196,8 @@ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.1.tgz", "integrity": "sha512-iZvCCg8XqHQZ1ioNBTzXS/cQSkqkqcPs8xSX4upNB+DAk9Ht3uzQf2J32uAHNCne8LDmKr29AgZrEs4oIrwLuQ==", "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "bn.js": "4.11.8", + "elliptic": "6.4.0" } }, "create-error-class": { @@ -3123,7 +3205,7 @@ "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.0" } }, "create-hash": { @@ -3131,11 +3213,11 @@ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "md5.js": "1.3.4", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" } }, "create-hmac": { @@ -3143,12 +3225,12 @@ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "inherits": "2.0.3", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" } }, "create-react-class": { @@ -3156,9 +3238,9 @@ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", "requires": { - "fbjs": "^0.8.9", - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "cross-env": { @@ -3167,8 +3249,8 @@ "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", "dev": true, "requires": { - "cross-spawn": "^6.0.5", - "is-windows": "^1.0.0" + "cross-spawn": "6.0.5", + "is-windows": "1.0.2" }, "dependencies": { "cross-spawn": { @@ -3177,11 +3259,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.4", + "path-key": "2.0.1", + "semver": "5.5.0", + "shebang-command": "1.2.0", + "which": "1.3.0" } } } @@ -3191,9 +3273,9 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.2", + "shebang-command": "1.2.0", + "which": "1.3.0" } }, "cryptiles": { @@ -3201,7 +3283,7 @@ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "requires": { - "boom": "5.x.x" + "boom": "5.2.0" }, "dependencies": { "boom": { @@ -3209,7 +3291,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", "requires": { - "hoek": "4.x.x" + "hoek": "4.2.1" } } } @@ -3219,17 +3301,17 @@ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "browserify-cipher": "1.0.1", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.1", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "diffie-hellman": "5.0.3", + "inherits": "2.0.3", + "pbkdf2": "3.0.16", + "public-encrypt": "4.0.2", + "randombytes": "2.0.6", + "randomfill": "1.0.4" } }, "crypto-random-string": { @@ -3247,20 +3329,20 @@ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", "requires": { - "babel-code-frame": "^6.26.0", - "css-selector-tokenizer": "^0.7.0", - "cssnano": "^3.10.0", - "icss-utils": "^2.1.0", - "loader-utils": "^1.0.2", - "lodash.camelcase": "^4.3.0", - "object-assign": "^4.1.1", - "postcss": "^5.0.6", - "postcss-modules-extract-imports": "^1.2.0", - "postcss-modules-local-by-default": "^1.2.0", - "postcss-modules-scope": "^1.1.0", - "postcss-modules-values": "^1.3.0", - "postcss-value-parser": "^3.3.0", - "source-list-map": "^2.0.0" + "babel-code-frame": "6.26.0", + "css-selector-tokenizer": "0.7.0", + "cssnano": "3.10.0", + "icss-utils": "2.1.0", + "loader-utils": "1.1.0", + "lodash.camelcase": "4.3.0", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-modules-extract-imports": "1.2.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0", + "postcss-value-parser": "3.3.0", + "source-list-map": "2.0.0" }, "dependencies": { "ansi-styles": { @@ -3273,11 +3355,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -3297,10 +3379,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -3313,7 +3395,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -3323,10 +3405,10 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", + "boolbase": "1.0.0", + "css-what": "2.1.0", "domutils": "1.5.1", - "nth-check": "~1.0.1" + "nth-check": "1.0.1" }, "dependencies": { "domutils": { @@ -3334,8 +3416,8 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" } } } @@ -3345,9 +3427,9 @@ "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" + "cssesc": "0.1.0", + "fastparse": "1.1.1", + "regexpu-core": "1.0.0" }, "dependencies": { "regexpu-core": { @@ -3355,9 +3437,9 @@ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.3.3", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } } } @@ -3377,38 +3459,38 @@ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "requires": { - "autoprefixer": "^6.3.1", - "decamelize": "^1.1.2", - "defined": "^1.0.0", - "has": "^1.0.1", - "object-assign": "^4.0.1", - "postcss": "^5.0.14", - "postcss-calc": "^5.2.0", - "postcss-colormin": "^2.1.8", - "postcss-convert-values": "^2.3.4", - "postcss-discard-comments": "^2.0.4", - "postcss-discard-duplicates": "^2.0.1", - "postcss-discard-empty": "^2.0.1", - "postcss-discard-overridden": "^0.1.1", - "postcss-discard-unused": "^2.2.1", - "postcss-filter-plugins": "^2.0.0", - "postcss-merge-idents": "^2.1.5", - "postcss-merge-longhand": "^2.0.1", - "postcss-merge-rules": "^2.0.3", - "postcss-minify-font-values": "^1.0.2", - "postcss-minify-gradients": "^1.0.1", - "postcss-minify-params": "^1.0.4", - "postcss-minify-selectors": "^2.0.4", - "postcss-normalize-charset": "^1.1.0", - "postcss-normalize-url": "^3.0.7", - "postcss-ordered-values": "^2.1.0", - "postcss-reduce-idents": "^2.2.2", - "postcss-reduce-initial": "^1.0.0", - "postcss-reduce-transforms": "^1.0.3", - "postcss-svgo": "^2.1.1", - "postcss-unique-selectors": "^2.0.2", - "postcss-value-parser": "^3.2.3", - "postcss-zindex": "^2.0.1" + "autoprefixer": "6.7.7", + "decamelize": "1.2.0", + "defined": "1.0.0", + "has": "1.0.1", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-calc": "5.3.1", + "postcss-colormin": "2.2.2", + "postcss-convert-values": "2.6.1", + "postcss-discard-comments": "2.0.4", + "postcss-discard-duplicates": "2.1.0", + "postcss-discard-empty": "2.1.0", + "postcss-discard-overridden": "0.1.1", + "postcss-discard-unused": "2.2.3", + "postcss-filter-plugins": "2.0.2", + "postcss-merge-idents": "2.1.7", + "postcss-merge-longhand": "2.0.2", + "postcss-merge-rules": "2.1.2", + "postcss-minify-font-values": "1.0.5", + "postcss-minify-gradients": "1.0.5", + "postcss-minify-params": "1.2.2", + "postcss-minify-selectors": "2.1.1", + "postcss-normalize-charset": "1.1.1", + "postcss-normalize-url": "3.0.8", + "postcss-ordered-values": "2.2.3", + "postcss-reduce-idents": "2.4.0", + "postcss-reduce-initial": "1.0.1", + "postcss-reduce-transforms": "1.0.4", + "postcss-svgo": "2.1.6", + "postcss-unique-selectors": "2.0.2", + "postcss-value-parser": "3.3.0", + "postcss-zindex": "2.2.0" }, "dependencies": { "ansi-styles": { @@ -3421,12 +3503,12 @@ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", "requires": { - "browserslist": "^1.7.6", - "caniuse-db": "^1.0.30000634", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^5.2.16", - "postcss-value-parser": "^3.2.3" + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000832", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" } }, "browserslist": { @@ -3434,8 +3516,8 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" + "caniuse-db": "1.0.30000832", + "electron-to-chromium": "1.3.44" } }, "chalk": { @@ -3443,11 +3525,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -3467,10 +3549,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -3483,7 +3565,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -3493,8 +3575,8 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", "requires": { - "clap": "^1.0.9", - "source-map": "^0.5.3" + "clap": "1.2.3", + "source-map": "0.5.7" }, "dependencies": { "source-map": { @@ -3516,7 +3598,7 @@ "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", "dev": true, "requires": { - "cssom": "0.3.x" + "cssom": "0.3.4" } }, "csstype": { @@ -3529,7 +3611,7 @@ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "requires": { - "array-find-index": "^1.0.1" + "array-find-index": "1.0.2" } }, "cyclist": { @@ -3542,7 +3624,7 @@ "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "0.10.42" } }, "dashdash": { @@ -3550,7 +3632,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "dasherize": { @@ -3564,9 +3646,9 @@ "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", "dev": true, "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" + "abab": "2.0.0", + "whatwg-mimetype": "2.2.0", + "whatwg-url": "7.0.0" } }, "datatables.net": { @@ -3574,7 +3656,7 @@ "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.16.tgz", "integrity": "sha1-SwUtEIKCQmG2ju2dInQbcR09JGk=", "requires": { - "jquery": ">=1.7" + "jquery": "3.3.1" } }, "datatables.net-bs": { @@ -3583,7 +3665,7 @@ "integrity": "sha1-sIVPWzdPcTrj20FWx86op2DD3nY=", "requires": { "datatables.net": "1.10.16", - "jquery": ">=1.7" + "jquery": "3.3.1" } }, "datauri": { @@ -3591,9 +3673,9 @@ "resolved": "https://registry.npmjs.org/datauri/-/datauri-1.1.0.tgz", "integrity": "sha512-0q+cTTKx7q8eDteZRIQLTFJuiIsVing17UbWTPssY4JLSMaYsk/VKpNulBDo9NSgQWcvlPrkEHW8kUO67T/7mQ==", "requires": { - "image-size": "^0.6.2", - "mimer": "^0.3.2", - "semver": "^5.5.0" + "image-size": "0.6.2", + "mimer": "0.3.2", + "semver": "5.5.0" } }, "date-now": { @@ -3630,7 +3712,7 @@ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { - "type-detect": "^4.0.0" + "type-detect": "4.0.8" } }, "deep-equal": { @@ -3649,12 +3731,17 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" + }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "requires": { - "clone": "^1.0.2" + "clone": "1.0.4" } }, "define-properties": { @@ -3662,8 +3749,8 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "foreach": "2.0.5", + "object-keys": "1.0.11" } }, "define-property": { @@ -3671,8 +3758,8 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -3680,7 +3767,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -3688,7 +3775,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -3696,9 +3783,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -3713,12 +3800,12 @@ "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" + "globby": "6.1.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "p-map": "1.2.0", + "pify": "3.0.0", + "rimraf": "2.6.2" }, "dependencies": { "globby": { @@ -3726,11 +3813,11 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" }, "dependencies": { "pify": { @@ -3762,10 +3849,10 @@ "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", "requires": { - "JSONStream": "^1.0.3", - "shasum": "^1.0.0", - "subarg": "^1.0.0", - "through2": "^2.0.0" + "JSONStream": "1.3.2", + "shasum": "1.0.2", + "subarg": "1.0.0", + "through2": "2.0.3" } }, "des.js": { @@ -3773,8 +3860,8 @@ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "destroy": { @@ -3792,7 +3879,7 @@ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "detect-node": { @@ -3805,9 +3892,9 @@ "resolved": "https://registry.npmjs.org/detective/-/detective-5.1.0.tgz", "integrity": "sha512-TFHMqfOvxlgrfVzTEkNBSh9SvSNX/HfF4OFI2QFGCyPm02EsyILqnUeb5P6q7JZ3SFNTBL5t2sePRgrN4epUWQ==", "requires": { - "acorn-node": "^1.3.0", - "defined": "^1.0.0", - "minimist": "^1.1.1" + "acorn-node": "1.3.0", + "defined": "1.0.0", + "minimist": "1.2.0" }, "dependencies": { "minimist": { @@ -3822,7 +3909,7 @@ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", "requires": { - "readable-stream": "1.1.x", + "readable-stream": "1.1.14", "streamsearch": "0.1.2" }, "dependencies": { @@ -3836,10 +3923,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -3854,9 +3941,9 @@ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "bn.js": "4.11.8", + "miller-rabin": "4.0.1", + "randombytes": "2.0.6" } }, "dir-glob": { @@ -3864,8 +3951,8 @@ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" + "arrify": "1.0.1", + "path-type": "3.0.0" } }, "discontinuous-range": { @@ -3884,10 +3971,10 @@ "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", "integrity": "sha1-ErrWbVh0LG5ffPKUP7aFlED4CcQ=", "requires": { - "asap": "^2.0.6", - "invariant": "^2.0.0", - "lodash": "^4.2.0", - "redux": "^3.7.1" + "asap": "2.0.6", + "invariant": "2.2.4", + "lodash": "4.17.10", + "redux": "3.7.2" } }, "dns-equal": { @@ -3900,8 +3987,8 @@ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" + "ip": "1.1.5", + "safe-buffer": "5.1.2" } }, "dns-prefetch-control": { @@ -3914,7 +4001,7 @@ "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", "requires": { - "buffer-indexof": "^1.0.0" + "buffer-indexof": "1.1.1" } }, "doctrine": { @@ -3923,7 +4010,7 @@ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "esutils": "^2.0.2" + "esutils": "2.0.2" } }, "dom-converter": { @@ -3931,7 +4018,7 @@ "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", "requires": { - "utila": "~0.3" + "utila": "0.3.3" }, "dependencies": { "utila": { @@ -3951,8 +4038,8 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" + "domelementtype": "1.1.3", + "entities": "1.1.1" }, "dependencies": { "domelementtype": { @@ -3983,7 +4070,7 @@ "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", "dev": true, "requires": { - "webidl-conversions": "^4.0.2" + "webidl-conversions": "4.0.2" } }, "domhandler": { @@ -3991,7 +4078,7 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", "requires": { - "domelementtype": "1" + "domelementtype": "1.3.0" } }, "domhelper": { @@ -3999,9 +4086,9 @@ "resolved": "https://registry.npmjs.org/domhelper/-/domhelper-0.9.1.tgz", "integrity": "sha1-JlVOW6wsnpWF3KUAl431Bn1kvQA=", "requires": { - "browserify": ">=3.46.0", - "classie": ">=0.0.1", - "util-extend": "^1.0.1" + "browserify": "16.2.2", + "classie": "1.0.0", + "util-extend": "1.0.3" } }, "domutils": { @@ -4009,8 +4096,8 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" } }, "dont-sniff-mimetype": { @@ -4023,7 +4110,7 @@ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "duplexer": { @@ -4036,7 +4123,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", "requires": { - "readable-stream": "~1.1.9" + "readable-stream": "1.1.14" }, "dependencies": { "isarray": { @@ -4049,10 +4136,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -4072,10 +4159,10 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz", "integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==", "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.2.7", + "stream-shift": "1.0.0" } }, "ecc-jsbn": { @@ -4084,7 +4171,7 @@ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "ee-first": { @@ -4107,13 +4194,13 @@ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" } }, "emojis-list": { @@ -4131,7 +4218,7 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "requires": { - "iconv-lite": "~0.4.13" + "iconv-lite": "0.4.15" } }, "end-of-stream": { @@ -4139,7 +4226,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "engine.io": { @@ -4147,12 +4234,12 @@ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.0.tgz", "integrity": "sha512-mRbgmAtQ4GAlKwuPnnAvXXwdPhEx+jkc0OBCLrXuD/CRvwNK3AxRSnqK4FSqmAMRRHryVJP8TopOvmEaA64fKw==", "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.5", "base64id": "1.0.0", "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" + "debug": "3.1.0", + "engine.io-parser": "2.1.2", + "ws": "3.3.3" }, "dependencies": { "debug": { @@ -4172,14 +4259,14 @@ "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", + "debug": "3.1.0", + "engine.io-parser": "2.1.2", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", + "ws": "3.3.3", + "xmlhttprequest-ssl": "1.5.5", "yeast": "0.1.2" }, "dependencies": { @@ -4199,10 +4286,10 @@ "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==", "requires": { "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", + "arraybuffer.slice": "0.0.7", "base64-arraybuffer": "0.1.5", "blob": "0.0.4", - "has-binary2": "~1.0.2" + "has-binary2": "1.0.2" } }, "enhanced-resolve": { @@ -4210,10 +4297,10 @@ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" + "graceful-fs": "4.1.11", + "memory-fs": "0.4.1", + "object-assign": "4.1.1", + "tapable": "0.2.8" } }, "entities": { @@ -4227,22 +4314,22 @@ "integrity": "sha512-l8csyPyLmtxskTz6pX9W8eDOyH1ckEtDttXk/vlFWCjv00SkjTjtoUrogqp4yEvMyneU9dUJoOLnqFoiHb8IHA==", "dev": true, "requires": { - "cheerio": "^1.0.0-rc.2", - "function.prototype.name": "^1.0.3", - "has": "^1.0.1", - "is-boolean-object": "^1.0.0", - "is-callable": "^1.1.3", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-subset": "^0.1.1", - "lodash": "^4.17.4", - "object-inspect": "^1.5.0", - "object-is": "^1.0.1", - "object.assign": "^4.1.0", - "object.entries": "^1.0.4", - "object.values": "^1.0.4", - "raf": "^3.4.0", - "rst-selector-parser": "^2.2.3" + "cheerio": "1.0.0-rc.2", + "function.prototype.name": "1.1.0", + "has": "1.0.1", + "is-boolean-object": "1.0.0", + "is-callable": "1.1.3", + "is-number-object": "1.0.3", + "is-string": "1.0.4", + "is-subset": "0.1.1", + "lodash": "4.17.10", + "object-inspect": "1.6.0", + "object-is": "1.0.1", + "object.assign": "4.1.0", + "object.entries": "1.0.4", + "object.values": "1.0.4", + "raf": "3.4.0", + "rst-selector-parser": "2.2.3" }, "dependencies": { "cheerio": { @@ -4251,12 +4338,12 @@ "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", "dev": true, "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash": "^4.15.0", - "parse5": "^3.0.1" + "css-select": "1.2.0", + "dom-serializer": "0.1.0", + "entities": "1.1.1", + "htmlparser2": "3.9.2", + "lodash": "4.17.10", + "parse5": "3.0.3" } } } @@ -4267,11 +4354,11 @@ "integrity": "sha512-GxQ+ZYbo6YFwwpaLc9LLyAwsx+F1au628/+hwTx3XV2OiuvHGyWgC/r1AAK1HlDRjujzfwwMNZTc/JxkjIuYVg==", "dev": true, "requires": { - "enzyme-adapter-utils": "^1.1.0", - "lodash": "^4.17.4", - "object.assign": "^4.0.4", - "object.values": "^1.0.4", - "prop-types": "^15.5.10" + "enzyme-adapter-utils": "1.3.0", + "lodash": "4.17.10", + "object.assign": "4.1.0", + "object.values": "1.0.4", + "prop-types": "15.6.1" } }, "enzyme-adapter-utils": { @@ -4280,9 +4367,9 @@ "integrity": "sha512-vVXSt6uDv230DIv+ebCG66T1Pm36Kv+m74L1TrF4kaE7e1V7Q/LcxO0QRkajk5cA6R3uu9wJf5h13wOTezTbjA==", "dev": true, "requires": { - "lodash": "^4.17.4", - "object.assign": "^4.0.4", - "prop-types": "^15.6.0" + "lodash": "4.17.10", + "object.assign": "4.1.0", + "prop-types": "15.6.1" } }, "errno": { @@ -4290,7 +4377,7 @@ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" } }, "error-ex": { @@ -4298,7 +4385,7 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "error-stack-parser": { @@ -4306,7 +4393,7 @@ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-1.3.6.tgz", "integrity": "sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI=", "requires": { - "stackframe": "^0.3.1" + "stackframe": "0.3.1" } }, "es-abstract": { @@ -4314,11 +4401,11 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" } }, "es-to-primitive": { @@ -4326,9 +4413,9 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" } }, "es5-ext": { @@ -4336,9 +4423,9 @@ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "next-tick": "1.0.0" } }, "es6-error": { @@ -4351,9 +4438,9 @@ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-symbol": "3.1.1" } }, "es6-map": { @@ -4361,12 +4448,12 @@ "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" } }, "es6-object-assign": { @@ -4384,11 +4471,11 @@ "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" + "event-emitter": "0.3.5" } }, "es6-symbol": { @@ -4396,8 +4483,8 @@ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.42" } }, "es6-weak-map": { @@ -4405,10 +4492,10 @@ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" } }, "escape-html": { @@ -4427,11 +4514,11 @@ "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.6.1" }, "dependencies": { "esprima": { @@ -4447,10 +4534,10 @@ "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "eslint": { @@ -4459,44 +4546,44 @@ "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", + "ajv": "5.5.2", + "babel-code-frame": "6.26.0", + "chalk": "2.4.1", + "concat-stream": "1.6.2", + "cross-spawn": "5.1.0", + "debug": "3.2.6", + "doctrine": "2.1.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0", + "espree": "3.5.4", + "esquery": "1.0.1", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "11.11.0", + "ignore": "3.3.8", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.13.1", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.10", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.3", + "regexpp": "1.1.0", + "require-uncached": "1.0.3", + "semver": "5.5.0", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", "table": "4.0.2", - "text-table": "~0.2.0" + "text-table": "0.2.0" }, "dependencies": { "ansi-regex": { @@ -4506,57 +4593,66 @@ "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "globals": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz", - "integrity": "sha512-hYyf+kI8dm3nORsiiXUQigOU62hDLfJ9G01uyGMxhc6BKsircrUhC4uJPQPUSuq2GrTmiiEt7ewxlMdBewfmKQ==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", "dev": true }, "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } }, "eslint-plugin-react": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.9.1.tgz", - "integrity": "sha512-uvq+2ZkiqzjwF+pMZ8xqIC3pChV4KviPvvPIyQOvKWnjtvyW3iGfHIRqVumw05L3itby0QGmA4VdBA9m1OdMmg==", + "version": "7.12.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz", + "integrity": "sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==", "dev": true, "requires": { - "doctrine": "^2.1.0", - "has": "^1.0.2", - "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.1" + "array-includes": "3.0.3", + "doctrine": "2.1.0", + "has": "1.0.3", + "jsx-ast-utils": "2.0.1", + "object.fromentries": "2.0.0", + "prop-types": "15.7.2", + "resolve": "1.10.0" }, "dependencies": { "has": { @@ -4565,7 +4661,42 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "function-bind": "1.1.1" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "1.4.0", + "object-assign": "4.1.1", + "react-is": "16.8.6" + } + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "1.0.6" } } } @@ -4576,8 +4707,8 @@ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "eslint-visitor-keys": { @@ -4592,8 +4723,8 @@ "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "acorn": "5.5.3", + "acorn-jsx": "3.0.1" } }, "esprima": { @@ -4607,7 +4738,7 @@ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "4.2.0" } }, "esrecurse": { @@ -4615,7 +4746,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "requires": { - "estraverse": "^4.1.0" + "estraverse": "4.2.0" } }, "estraverse": { @@ -4643,8 +4774,8 @@ "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.42" } }, "event-stream": { @@ -4652,13 +4783,13 @@ "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" } }, "eventemitter3": { @@ -4676,7 +4807,7 @@ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", "requires": { - "original": ">=0.0.5" + "original": "1.0.0" } }, "evp_bytestokey": { @@ -4684,8 +4815,8 @@ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "md5.js": "1.3.4", + "safe-buffer": "5.1.2" } }, "execa": { @@ -4693,13 +4824,13 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "exenv": { @@ -4712,13 +4843,13 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -4726,7 +4857,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -4734,7 +4865,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -4744,7 +4875,7 @@ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.3" }, "dependencies": { "fill-range": { @@ -4752,11 +4883,11 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" } }, "is-number": { @@ -4764,7 +4895,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "isobject": { @@ -4780,7 +4911,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -4790,7 +4921,7 @@ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.1" } }, "expect-ct": { @@ -4803,36 +4934,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "requires": { - "accepts": "~1.3.5", + "accepts": "1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "~1.0.4", + "content-type": "1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", + "proxy-addr": "2.0.3", "qs": "6.5.1", - "range-parser": "~1.2.0", + "range-parser": "1.2.0", "safe-buffer": "5.1.1", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "statuses": "1.4.0", + "type-is": "1.6.16", "utils-merge": "1.0.1", - "vary": "~1.1.2" + "vary": "1.1.2" }, "dependencies": { "body-parser": { @@ -4841,15 +4972,15 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "~1.6.15" + "type-is": "1.6.16" } }, "bytes": { @@ -4891,7 +5022,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" + "statuses": "1.4.0" } }, "setprototypeof": { @@ -4918,9 +5049,9 @@ "resolved": "https://registry.npmjs.org/express-promise-router/-/express-promise-router-1.1.1.tgz", "integrity": "sha1-Vqw5SMJ8gJvzK9EpSOFHOnYQ32k=", "requires": { - "bluebird": "^3.4.1", - "lodash": "^4.13.1", - "methods": "^1.0.0" + "bluebird": "3.5.1", + "lodash": "4.17.10", + "methods": "1.1.2" } }, "express-session": { @@ -4932,10 +5063,10 @@ "cookie-signature": "1.0.6", "crc": "3.4.4", "debug": "2.6.9", - "depd": "~1.1.1", - "on-headers": "~1.0.1", - "parseurl": "~1.3.2", - "uid-safe": "~2.1.5", + "depd": "1.1.2", + "on-headers": "1.0.1", + "parseurl": "1.3.2", + "uid-safe": "2.1.5", "utils-merge": "1.0.1" } }, @@ -4944,9 +5075,9 @@ "resolved": "https://registry.npmjs.org/express-socket.io-session/-/express-socket.io-session-1.3.3.tgz", "integrity": "sha512-cGLpEM2LNQTnxOtahaB+20QeY5MpIFGHSpVjoJbCMZMQh2pYJJut3clg2mI39JWd7YxVmhUfHozpw5P4+j5ypw==", "requires": { - "cookie-parser": "~1.3.3", - "crc": "^3.3.0", - "debug": "~2.6.0" + "cookie-parser": "1.3.5", + "crc": "3.4.4", + "debug": "2.6.9" }, "dependencies": { "cookie": { @@ -4975,8 +5106,8 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -4984,7 +5115,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -4995,18 +5126,18 @@ "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" + "chardet": "0.4.2", + "iconv-lite": "0.4.24", + "tmp": "0.0.33" }, "dependencies": { "iconv-lite": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", - "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } } } @@ -5016,14 +5147,14 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -5031,7 +5162,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -5039,7 +5170,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -5047,7 +5178,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -5055,7 +5186,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -5063,9 +5194,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -5075,10 +5206,10 @@ "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", "requires": { - "async": "^2.4.1", - "loader-utils": "^1.1.0", - "schema-utils": "^0.3.0", - "webpack-sources": "^1.0.1" + "async": "2.6.0", + "loader-utils": "1.1.0", + "schema-utils": "0.3.0", + "webpack-sources": "1.1.0" } }, "extsprintf": { @@ -5096,9 +5227,9 @@ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "time-stamp": "^1.0.0" + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "time-stamp": "1.1.0" } }, "fast-deep-equal": { @@ -5137,7 +5268,7 @@ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.0" } }, "fbjs": { @@ -5145,13 +5276,13 @@ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.9" + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.17" }, "dependencies": { "core-js": { @@ -5167,7 +5298,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "1.0.5" } }, "file-entry-cache": { @@ -5176,8 +5307,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.3.4", + "object-assign": "4.1.1" } }, "file-loader": { @@ -5185,7 +5316,7 @@ "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.2.tgz", "integrity": "sha512-N+uhF3mswIFeziHQjGScJ/yHXYt3DiLBeC+9vWW+WjUBiClMSOlV1YrXQi+7KM2aA3Rn4Bybgv+uXFQbfkzpvg==", "requires": { - "loader-utils": "^1.0.2" + "loader-utils": "1.1.0" } }, "filename-regex": { @@ -5198,10 +5329,10 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -5209,7 +5340,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5220,12 +5351,12 @@ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.4.0", + "unpipe": "1.0.0" }, "dependencies": { "statuses": { @@ -5240,9 +5371,9 @@ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" + "commondir": "1.0.1", + "make-dir": "1.2.0", + "pkg-dir": "2.0.0" } }, "find-index": { @@ -5255,7 +5386,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "findup": { @@ -5264,8 +5395,8 @@ "integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=", "dev": true, "requires": { - "colors": "~0.6.0-1", - "commander": "~2.1.0" + "colors": "0.6.2", + "commander": "2.1.0" }, "dependencies": { "colors": { @@ -5287,10 +5418,10 @@ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" }, "dependencies": { "is-glob": { @@ -5298,7 +5429,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -5308,11 +5439,11 @@ "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" } }, "first-chunk-stream": { @@ -5326,52 +5457,15 @@ "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=" }, "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" - }, - "dependencies": { - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } + "circular-json": "0.3.3", + "graceful-fs": "4.1.11", + "rimraf": "2.6.2", + "write": "0.2.1" } }, "flatten": { @@ -5395,8 +5489,8 @@ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" + "inherits": "2.0.3", + "readable-stream": "2.2.7" } }, "follow-redirects": { @@ -5404,7 +5498,7 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", "requires": { - "debug": "^3.1.0" + "debug": "3.1.0" }, "dependencies": { "debug": { @@ -5432,7 +5526,7 @@ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "foreach": { @@ -5450,9 +5544,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.18" } }, "formidable": { @@ -5471,7 +5565,7 @@ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "frameguard": { @@ -5494,8 +5588,8 @@ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "inherits": "2.0.3", + "readable-stream": "2.2.7" } }, "fs-write-stream-atomic": { @@ -5503,10 +5597,10 @@ "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.2.7" } }, "fs.realpath": { @@ -5520,8 +5614,8 @@ "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.10.0", + "node-pre-gyp": "0.10.0" }, "dependencies": { "abbrev": { @@ -5555,7 +5649,7 @@ "version": "1.1.11", "bundled": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -5655,7 +5749,7 @@ "bundled": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -5663,7 +5757,7 @@ "bundled": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -5688,7 +5782,7 @@ "version": "1.0.0", "bundled": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -5700,7 +5794,7 @@ "version": "3.0.4", "bundled": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -5740,9 +5834,9 @@ "bundled": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" } }, "node-pre-gyp": { @@ -5767,8 +5861,8 @@ "bundled": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { @@ -5781,8 +5875,8 @@ "bundled": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" } }, "npmlog": { @@ -5827,8 +5921,8 @@ "bundled": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -5846,10 +5940,10 @@ "bundled": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -5914,9 +6008,9 @@ "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -5924,14 +6018,14 @@ "bundled": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -5981,8 +6075,8 @@ "resolved": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-3.9.0.tgz", "integrity": "sha512-bbALDK8+SBqluv8SKPDeVNtaSr87NYblte/pRgV5NnDJWCEARpRlQ1qQ/XEakcAXbMZov6rWYIvLOrtKwQo2Bg==", "requires": { - "jquery": "2 - 3", - "moment": "^2.20.1" + "jquery": "3.3.1", + "moment": "2.22.1" } }, "function-bind": { @@ -5996,9 +6090,9 @@ "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "is-callable": "^1.1.3" + "define-properties": "1.1.2", + "function-bind": "1.1.1", + "is-callable": "1.1.3" } }, "functional-red-black-tree": { @@ -6012,7 +6106,7 @@ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", "requires": { - "globule": "~0.1.0" + "globule": "0.1.0" } }, "geoposition-to-object": { @@ -6051,7 +6145,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "ghooks": { @@ -6073,12 +6167,12 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -6086,8 +6180,8 @@ "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" }, "dependencies": { "glob-parent": { @@ -6095,7 +6189,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "is-extglob": { @@ -6108,7 +6202,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -6118,8 +6212,8 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -6127,7 +6221,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -6137,12 +6231,12 @@ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", "requires": { - "glob": "^4.3.1", - "glob2base": "^0.0.12", - "minimatch": "^2.0.1", - "ordered-read-streams": "^0.1.0", - "through2": "^0.6.1", - "unique-stream": "^1.0.0" + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" }, "dependencies": { "glob": { @@ -6150,10 +6244,10 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.4.0" } }, "isarray": { @@ -6166,7 +6260,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", "requires": { - "brace-expansion": "^1.0.0" + "brace-expansion": "1.1.11" } }, "readable-stream": { @@ -6174,10 +6268,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -6190,8 +6284,8 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } } } @@ -6201,7 +6295,7 @@ "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", "requires": { - "gaze": "^0.5.1" + "gaze": "0.5.2" } }, "glob2base": { @@ -6209,7 +6303,7 @@ "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", "requires": { - "find-index": "^0.1.1" + "find-index": "0.1.1" } }, "global": { @@ -6217,8 +6311,8 @@ "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", "requires": { - "min-document": "^2.19.0", - "process": "~0.5.1" + "min-document": "2.19.0", + "process": "0.5.2" } }, "global-dirs": { @@ -6226,7 +6320,7 @@ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "requires": { - "ini": "^1.3.4" + "ini": "1.3.5" } }, "global-modules": { @@ -6234,9 +6328,9 @@ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" } }, "global-prefix": { @@ -6244,11 +6338,11 @@ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.1", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.0" } }, "globals": { @@ -6261,12 +6355,12 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" + "array-union": "1.0.2", + "dir-glob": "2.0.0", + "glob": "7.1.2", + "ignore": "3.3.8", + "pify": "3.0.0", + "slash": "1.0.0" } }, "globule": { @@ -6274,9 +6368,9 @@ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", "requires": { - "glob": "~3.1.21", - "lodash": "~1.0.1", - "minimatch": "~0.2.11" + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" }, "dependencies": { "glob": { @@ -6284,9 +6378,9 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", "requires": { - "graceful-fs": "~1.2.0", - "inherits": "1", - "minimatch": "~0.2.11" + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" } }, "graceful-fs": { @@ -6314,8 +6408,8 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" + "lru-cache": "2.7.3", + "sigmund": "1.0.1" } } } @@ -6325,7 +6419,7 @@ "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.0" } }, "google-maps-infobox": { @@ -6338,17 +6432,17 @@ "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "requires": { - "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" + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" } }, "graceful-fs": { @@ -6366,19 +6460,19 @@ "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "requires": { - "archy": "^1.0.0", - "chalk": "^1.0.0", - "deprecated": "^0.0.1", - "gulp-util": "^3.0.0", - "interpret": "^1.0.0", - "liftoff": "^2.1.0", - "minimist": "^1.1.0", - "orchestrator": "^0.3.0", - "pretty-hrtime": "^1.0.0", - "semver": "^4.1.0", - "tildify": "^1.0.0", - "v8flags": "^2.0.2", - "vinyl-fs": "^0.3.0" + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.1.0", + "liftoff": "2.5.0", + "minimist": "1.2.0", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" }, "dependencies": { "ansi-styles": { @@ -6391,11 +6485,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "minimist": { @@ -6420,12 +6514,12 @@ "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-6.1.3.tgz", "integrity": "sha512-tm15R3rt4gO59WXCuqrwf4QXJM9VIJC+0J2NPYSC6xZn+cZRD5y5RPGAiHaDxCJq7Rz5BDljlrk3cEjWADF+wQ==", "requires": { - "babel-core": "^6.23.1", - "object-assign": "^4.0.1", - "plugin-error": "^1.0.1", + "babel-core": "6.26.3", + "object-assign": "4.1.1", + "plugin-error": "1.0.1", "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl-sourcemaps-apply": "^0.2.0" + "through2": "2.0.3", + "vinyl-sourcemaps-apply": "0.2.1" } }, "gulp-nodemon": { @@ -6433,10 +6527,10 @@ "resolved": "https://registry.npmjs.org/gulp-nodemon/-/gulp-nodemon-2.2.1.tgz", "integrity": "sha1-2b8Zn1WFRYFZ09KZFT5gtGhotvQ=", "requires": { - "colors": "^1.0.3", - "event-stream": "^3.2.1", - "gulp": "^3.9.1", - "nodemon": "^1.10.2" + "colors": "1.1.2", + "event-stream": "3.3.4", + "gulp": "3.9.1", + "nodemon": "1.17.3" } }, "gulp-uglify": { @@ -6444,13 +6538,13 @@ "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-3.0.0.tgz", "integrity": "sha1-DfAzHXKg0wLj434QlIXd3zPG0co=", "requires": { - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash": "^4.13.1", - "make-error-cause": "^1.1.1", - "through2": "^2.0.0", - "uglify-js": "^3.0.5", - "vinyl-sourcemaps-apply": "^0.2.0" + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash": "4.17.10", + "make-error-cause": "1.2.2", + "through2": "2.0.3", + "uglify-js": "3.3.23", + "vinyl-sourcemaps-apply": "0.2.1" } }, "gulp-util": { @@ -6458,24 +6552,24 @@ "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.2", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" + "through2": "2.0.3", + "vinyl": "0.5.3" }, "dependencies": { "ansi-styles": { @@ -6488,11 +6582,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "minimist": { @@ -6517,7 +6611,7 @@ "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "requires": { - "glogg": "^1.0.0" + "glogg": "1.0.1" } }, "handle-thing": { @@ -6535,8 +6629,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, "has": { @@ -6544,7 +6638,7 @@ "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", "requires": { - "function-bind": "^1.0.2" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -6552,7 +6646,7 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-binary2": { @@ -6585,7 +6679,7 @@ "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.0" } }, "has-symbols": { @@ -6599,9 +6693,9 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, "has-values": { @@ -6609,8 +6703,8 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "kind-of": { @@ -6618,7 +6712,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6628,8 +6722,8 @@ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "hash.js": { @@ -6637,8 +6731,8 @@ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "hawk": { @@ -6646,10 +6740,10 @@ "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" } }, "he": { @@ -6698,11 +6792,11 @@ "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", "requires": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "value-equal": "^0.4.0", - "warning": "^3.0.0" + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "resolve-pathname": "2.2.0", + "value-equal": "0.4.0", + "warning": "3.0.0" } }, "hmac-drbg": { @@ -6710,9 +6804,9 @@ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "hash.js": "1.1.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" } }, "hoek": { @@ -6730,8 +6824,8 @@ "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "homedir-polyfill": { @@ -6739,7 +6833,7 @@ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "requires": { - "parse-passwd": "^1.0.0" + "parse-passwd": "1.0.0" } }, "hosted-git-info": { @@ -6752,10 +6846,10 @@ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "inherits": "2.0.3", + "obuf": "1.1.2", + "readable-stream": "2.2.7", + "wbuf": "1.7.3" } }, "hpkp": { @@ -6774,7 +6868,7 @@ "integrity": "sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E=", "dev": true, "requires": { - "concat-stream": "^1.4.7" + "concat-stream": "1.6.2" } }, "html-comment-regex": { @@ -6788,7 +6882,7 @@ "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", "dev": true, "requires": { - "whatwg-encoding": "^1.0.1" + "whatwg-encoding": "1.0.5" } }, "html-entities": { @@ -6801,13 +6895,13 @@ "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.15.tgz", "integrity": "sha512-OZa4rfb6tZOZ3Z8Xf0jKxXkiDcFWldQePGYFDcgKqES2sXeWaEv9y6QQvWUtX3ySI3feApQi5uCsHLINQ6NoAw==", "requires": { - "camel-case": "3.0.x", - "clean-css": "4.1.x", - "commander": "2.15.x", - "he": "1.1.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.3.x" + "camel-case": "3.0.0", + "clean-css": "4.1.11", + "commander": "2.15.1", + "he": "1.1.1", + "param-case": "2.1.1", + "relateurl": "0.2.7", + "uglify-js": "3.3.23" } }, "html-to-react": { @@ -6815,11 +6909,11 @@ "resolved": "https://registry.npmjs.org/html-to-react/-/html-to-react-1.3.3.tgz", "integrity": "sha512-4Qi5/t8oBr6c1t1kBJKyxEeJu0lb7ctvq29oFZioiUHH0Wz88VWGwoXuH26HDt9v64bDHA4NMPNTH8bVrcaJWA==", "requires": { - "domhandler": "^2.3.0", - "escape-string-regexp": "^1.0.5", - "htmlparser2": "^3.8.3", - "ramda": "^0.25.0", - "underscore.string.fp": "^1.0.4" + "domhandler": "2.4.1", + "escape-string-regexp": "1.0.5", + "htmlparser2": "3.9.2", + "ramda": "0.25.0", + "underscore.string.fp": "1.0.4" } }, "html-webpack-plugin": { @@ -6827,12 +6921,12 @@ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz", "integrity": "sha1-f5xCG36pHsRg9WUn1430hO51N9U=", "requires": { - "bluebird": "^3.4.7", - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "toposort": "^1.0.0" + "bluebird": "3.5.1", + "html-minifier": "3.5.15", + "loader-utils": "0.2.17", + "lodash": "4.17.10", + "pretty-error": "2.1.1", + "toposort": "1.0.7" }, "dependencies": { "loader-utils": { @@ -6840,10 +6934,10 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1", + "object-assign": "4.1.1" } } } @@ -6858,12 +6952,12 @@ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", "requires": { - "domelementtype": "^1.3.0", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" + "domelementtype": "1.3.0", + "domhandler": "2.4.1", + "domutils": "1.7.0", + "entities": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.2.7" } }, "http-deceiver": { @@ -6876,10 +6970,10 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "statuses": "1.5.0" } }, "http-parser-js": { @@ -6892,9 +6986,9 @@ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "eventemitter3": "3.1.0", + "follow-redirects": "1.4.1", + "requires-port": "1.0.0" }, "dependencies": { "eventemitter3": { @@ -6909,10 +7003,10 @@ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", "requires": { - "http-proxy": "^1.16.2", - "is-glob": "^3.1.0", - "lodash": "^4.17.2", - "micromatch": "^2.3.11" + "http-proxy": "1.17.0", + "is-glob": "3.1.0", + "lodash": "4.17.10", + "micromatch": "2.3.11" }, "dependencies": { "arr-diff": { @@ -6920,7 +7014,7 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "array-unique": { @@ -6933,9 +7027,9 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" } }, "expand-brackets": { @@ -6943,7 +7037,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "extglob": { @@ -6951,7 +7045,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" }, "dependencies": { "is-extglob": { @@ -6966,7 +7060,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } }, "kind-of": { @@ -6974,7 +7068,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "micromatch": { @@ -6982,19 +7076,19 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" }, "dependencies": { "is-extglob": { @@ -7007,7 +7101,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -7019,9 +7113,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.1" } }, "https-browserify": { @@ -7044,7 +7138,7 @@ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", "requires": { - "postcss": "^6.0.1" + "postcss": "6.0.22" } }, "ieee754": { @@ -7093,8 +7187,8 @@ "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", "requires": { - "pkg-dir": "^2.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "2.0.0", + "resolve-cwd": "2.0.0" } }, "imurmurhash": { @@ -7107,7 +7201,7 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "indexes-of": { @@ -7125,8 +7219,8 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -7144,7 +7238,7 @@ "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", "requires": { - "source-map": "~0.5.3" + "source-map": "0.5.7" }, "dependencies": { "source-map": { @@ -7165,20 +7259,20 @@ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", + "ansi-escapes": "3.2.0", + "chalk": "2.4.1", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.2.0", + "figures": "2.0.0", + "lodash": "4.17.10", "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" }, "dependencies": { "ansi-regex": { @@ -7193,7 +7287,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -7203,15 +7297,15 @@ "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.6.tgz", "integrity": "sha512-R3sidKJr3SsggqQQ5cEwQb3pWG8RNx0UnpyeiOSR6jorRIeAOzH2gkTWnNdMnyRiVbjrG047K7UCtlMkQ1Mo9w==", "requires": { - "JSONStream": "^1.0.3", - "combine-source-map": "^0.8.0", - "concat-stream": "^1.6.1", - "is-buffer": "^1.1.0", - "lexical-scope": "^1.2.0", - "path-is-absolute": "^1.0.1", - "process": "~0.11.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" + "JSONStream": "1.3.2", + "combine-source-map": "0.8.0", + "concat-stream": "1.6.2", + "is-buffer": "1.1.6", + "lexical-scope": "1.2.0", + "path-is-absolute": "1.0.1", + "process": "0.11.10", + "through2": "2.0.3", + "xtend": "4.0.1" }, "dependencies": { "process": { @@ -7226,7 +7320,7 @@ "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", "requires": { - "meow": "^3.3.0" + "meow": "3.7.0" } }, "interpret": { @@ -7239,7 +7333,7 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -7252,7 +7346,7 @@ "resolved": "https://registry.npmjs.org/ion-rangeslider/-/ion-rangeslider-2.2.0.tgz", "integrity": "sha1-OI8SzXBZOmGzNo+tbE8wpdqLl8k=", "requires": { - "jquery": ">=1.8" + "jquery": "3.3.1" } }, "ionicons": { @@ -7275,8 +7369,8 @@ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "is-relative": "1.0.0", + "is-windows": "1.0.2" } }, "is-absolute-url": { @@ -7289,7 +7383,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7297,7 +7391,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7312,7 +7406,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.11.0" } }, "is-boolean-object": { @@ -7331,7 +7425,7 @@ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-callable": { @@ -7344,7 +7438,7 @@ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", "requires": { - "ci-info": "^1.0.0" + "ci-info": "1.1.3" } }, "is-data-descriptor": { @@ -7352,7 +7446,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7360,7 +7454,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7375,9 +7469,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -7402,7 +7496,7 @@ "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -7420,7 +7514,7 @@ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -7433,7 +7527,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-installed-globally": { @@ -7441,8 +7535,8 @@ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" } }, "is-npm": { @@ -7455,7 +7549,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7463,7 +7557,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7484,7 +7578,7 @@ "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "requires": { - "is-number": "^4.0.0" + "is-number": "4.0.0" }, "dependencies": { "is-number": { @@ -7504,7 +7598,7 @@ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "1.0.1" } }, "is-path-inside": { @@ -7512,7 +7606,7 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-plain-obj": { @@ -7525,7 +7619,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "is-posix-bracket": { @@ -7553,7 +7647,7 @@ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "requires": { - "has": "^1.0.1" + "has": "1.0.1" } }, "is-relative": { @@ -7561,7 +7655,7 @@ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "requires": { - "is-unc-path": "^1.0.0" + "is-unc-path": "1.0.0" } }, "is-resolvable": { @@ -7597,7 +7691,7 @@ "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", "requires": { - "html-comment-regex": "^1.1.0" + "html-comment-regex": "1.1.1" } }, "is-symbol": { @@ -7615,7 +7709,7 @@ "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "requires": { - "unc-path-regex": "^0.1.2" + "unc-path-regex": "0.1.2" } }, "is-utf8": { @@ -7653,8 +7747,8 @@ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.4" } }, "isstream": { @@ -7674,13 +7768,13 @@ "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.0", - "semver": "^5.3.0" + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" } }, "jquery": { @@ -7723,8 +7817,8 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", "requires": { - "argparse": "^1.0.7", - "esprima": "^2.6.0" + "argparse": "1.0.10", + "esprima": "2.7.3" } }, "jsbn": { @@ -7739,32 +7833,32 @@ "integrity": "sha512-Kmq4ASMNkgpY+YufE322EnIKoiz0UWY2DRkKlU7d5YrIW4xiVRhWFrZV1fr6w/ZNxQ50wGAH5gGRzydgnmkkvw==", "dev": true, "requires": { - "abab": "^2.0.0", - "acorn": "^6.0.2", - "acorn-globals": "^4.3.0", - "array-equal": "^1.0.0", - "cssom": "^0.3.4", - "cssstyle": "^1.1.1", - "data-urls": "^1.0.1", - "domexception": "^1.0.1", - "escodegen": "^1.11.0", - "html-encoding-sniffer": "^1.0.2", - "nwsapi": "^2.0.9", + "abab": "2.0.0", + "acorn": "6.0.2", + "acorn-globals": "4.3.0", + "array-equal": "1.0.0", + "cssom": "0.3.4", + "cssstyle": "1.1.1", + "data-urls": "1.1.0", + "domexception": "1.0.1", + "escodegen": "1.11.0", + "html-encoding-sniffer": "1.0.2", + "nwsapi": "2.0.9", "parse5": "5.1.0", - "pn": "^1.1.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.5", - "saxes": "^3.1.3", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.4.3", - "w3c-hr-time": "^1.0.1", - "w3c-xmlserializer": "^1.0.0", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0", - "ws": "^6.1.0", - "xml-name-validator": "^3.0.0" + "pn": "1.1.0", + "request": "2.88.0", + "request-promise-native": "1.0.5", + "saxes": "3.1.3", + "symbol-tree": "3.2.2", + "tough-cookie": "2.4.3", + "w3c-hr-time": "1.0.1", + "w3c-xmlserializer": "1.0.0", + "webidl-conversions": "4.0.2", + "whatwg-encoding": "1.0.5", + "whatwg-mimetype": "2.2.0", + "whatwg-url": "7.0.0", + "ws": "6.1.0", + "xml-name-validator": "3.0.0" }, "dependencies": { "acorn": { @@ -7791,8 +7885,8 @@ "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "dev": true, "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, "mime-db": { @@ -7807,7 +7901,7 @@ "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", "dev": true, "requires": { - "mime-db": "~1.37.0" + "mime-db": "1.37.0" } }, "oauth-sign": { @@ -7834,26 +7928,26 @@ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "aws-sign2": "0.7.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.1.0", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.21", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" } }, "tough-cookie": { @@ -7862,8 +7956,8 @@ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.1.29", + "punycode": "1.4.1" } }, "uuid": { @@ -7878,7 +7972,7 @@ "integrity": "sha512-H3dGVdGvW2H8bnYpIDc3u3LH8Wue3Qh+Zto6aXXFzvESkTVT6rAfKR6tR/+coaUvxs8yHtmNV0uioBF62ZGSTg==", "dev": true, "requires": { - "async-limiter": "~1.0.0" + "async-limiter": "1.0.0" } } } @@ -7908,7 +8002,7 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stable-stringify-without-jsonify": { @@ -7959,7 +8053,7 @@ "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", "dev": true, "requires": { - "array-includes": "^3.0.3" + "array-includes": "3.0.3" } }, "juice": { @@ -7967,13 +8061,13 @@ "resolved": "https://registry.npmjs.org/juice/-/juice-4.2.3.tgz", "integrity": "sha512-Jisv2A20k7y/aqLpEevSm0E+1JfWmd13jY2s5aST54cHJQKHkKES2RlDtkdOM/ecGnvUKAjG/j+KcDbkzoGjKg==", "requires": { - "cheerio": "^0.22.0", + "cheerio": "0.22.0", "commander": "2.9.0", - "cross-spawn": "^5.0.1", - "deep-extend": "^0.5.0", - "mensch": "^0.3.3", + "cross-spawn": "5.1.0", + "deep-extend": "0.5.1", + "mensch": "0.3.3", "slick": "1.12.2", - "web-resource-inliner": "^4.2.0" + "web-resource-inliner": "4.2.1" }, "dependencies": { "commander": { @@ -7981,7 +8075,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "requires": { - "graceful-readlink": ">= 1.0.0" + "graceful-readlink": "1.0.1" } } } @@ -7997,7 +8091,7 @@ "resolved": "https://registry.npmjs.org/jvectormap/-/jvectormap-1.2.2.tgz", "integrity": "sha1-LkQIskpgRz/xBsHnJD43WuXKhdo=", "requires": { - "jquery": ">=1.5" + "jquery": "3.3.1" } }, "kareem": { @@ -8025,9 +8119,9 @@ "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz", "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==", "requires": { - "inherits": "^2.0.1", - "isarray": "^2.0.4", - "stream-splicer": "^2.0.0" + "inherits": "2.0.3", + "isarray": "2.0.4", + "stream-splicer": "2.0.0" }, "dependencies": { "isarray": { @@ -8042,7 +8136,7 @@ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "requires": { - "package-json": "^4.0.0" + "package-json": "4.0.1" } }, "lazy-cache": { @@ -8055,7 +8149,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "levn": { @@ -8064,8 +8158,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "lexical-scope": { @@ -8073,7 +8167,7 @@ "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", "requires": { - "astw": "^2.0.0" + "astw": "2.2.0" } }, "liftoff": { @@ -8081,14 +8175,14 @@ "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", "requires": { - "extend": "^3.0.0", - "findup-sync": "^2.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" + "extend": "3.0.1", + "findup-sync": "2.0.0", + "fined": "1.1.0", + "flagged-respawn": "1.0.0", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.7.1" } }, "load-json-file": { @@ -8096,10 +8190,10 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" }, "dependencies": { "pify": { @@ -8124,9 +8218,9 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" } }, "locate-path": { @@ -8134,8 +8228,8 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "lodash": { @@ -8245,7 +8339,7 @@ "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "requires": { - "lodash._root": "^3.0.0" + "lodash._root": "3.0.1" } }, "lodash.escaperegexp": { @@ -8304,9 +8398,9 @@ "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" } }, "lodash.map": { @@ -8365,15 +8459,15 @@ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" } }, "lodash.templatesettings": { @@ -8381,8 +8475,8 @@ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" } }, "lodash.unescape": { @@ -8410,7 +8504,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "requires": { - "js-tokens": "^3.0.0" + "js-tokens": "3.0.2" } }, "loud-rejection": { @@ -8418,8 +8512,8 @@ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" } }, "lower-case": { @@ -8437,8 +8531,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "macaddress": { @@ -8451,10 +8545,10 @@ "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-0.6.2.tgz", "integrity": "sha1-A8SGA5vfTfbNO2rcqqxBB9/bwGg=", "requires": { - "encoding": "^0.1.12", - "mime": "^1.3.4", - "mimelib": "^0.3.0", - "uue": "^3.1.0" + "encoding": "0.1.12", + "mime": "1.4.1", + "mimelib": "0.3.1", + "uue": "3.1.2" } }, "make-dir": { @@ -8462,7 +8556,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "make-error": { @@ -8475,7 +8569,7 @@ "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", "requires": { - "make-error": "^1.2.0" + "make-error": "1.3.4" } }, "make-iterator": { @@ -8483,7 +8577,7 @@ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" } }, "manage-path": { @@ -8512,7 +8606,7 @@ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "marker-clusterer-plus": { @@ -8530,8 +8624,8 @@ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "hash-base": "3.0.4", + "inherits": "2.0.3" } }, "media-typer": { @@ -8544,7 +8638,7 @@ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "memory-fs": { @@ -8552,8 +8646,8 @@ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "errno": "0.1.7", + "readable-stream": "2.2.7" } }, "mensch": { @@ -8566,16 +8660,16 @@ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" }, "dependencies": { "find-up": { @@ -8583,8 +8677,8 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "load-json-file": { @@ -8592,11 +8686,11 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" } }, "minimist": { @@ -8609,7 +8703,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-type": { @@ -8617,9 +8711,9 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -8632,9 +8726,9 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -8642,8 +8736,8 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" } }, "strip-bom": { @@ -8651,7 +8745,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } @@ -8671,19 +8765,19 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "miller-rabin": { @@ -8691,8 +8785,8 @@ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "bn.js": "4.11.8", + "brorand": "1.1.0" } }, "mime": { @@ -8710,7 +8804,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "~1.33.0" + "mime-db": "1.33.0" } }, "mimelib": { @@ -8718,8 +8812,8 @@ "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.3.1.tgz", "integrity": "sha1-eHrdJBXYJ6yzr27EvKHqlZZBiFM=", "requires": { - "addressparser": "~1.0.1", - "encoding": "~0.1.12" + "addressparser": "1.0.1", + "encoding": "0.1.12" } }, "mimer": { @@ -8737,7 +8831,7 @@ "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", "requires": { - "dom-walk": "^0.1.0" + "dom-walk": "0.1.1" } }, "minimalistic-assert": { @@ -8755,7 +8849,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -8768,16 +8862,16 @@ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.5.4", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "2.0.1", + "pumpify": "1.4.0", + "stream-each": "1.2.2", + "through2": "2.0.3" } }, "mixin-deep": { @@ -8785,8 +8879,8 @@ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -8794,7 +8888,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -8860,21 +8954,21 @@ "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.0.2.tgz", "integrity": "sha512-KWBI3009iRnHjRlxRhe8nJ6kdeBTg4sMi5N6AZgg5f1/v5S7EBCRBOY854I4P5Anl4kx6AJH+4bBBC2Gi3nkvg==", "requires": { - "JSONStream": "^1.0.3", - "browser-resolve": "^1.7.0", - "cached-path-relative": "^1.0.0", - "concat-stream": "~1.6.0", - "defined": "^1.0.0", - "detective": "^5.0.2", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "parents": "^1.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.4.0", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" + "JSONStream": "1.3.2", + "browser-resolve": "1.11.2", + "cached-path-relative": "1.0.1", + "concat-stream": "1.6.2", + "defined": "1.0.0", + "detective": "5.1.0", + "duplexer2": "0.1.4", + "inherits": "2.0.3", + "parents": "1.0.1", + "readable-stream": "2.2.7", + "resolve": "1.7.1", + "stream-combiner2": "1.1.1", + "subarg": "1.0.0", + "through2": "2.0.3", + "xtend": "4.0.1" }, "dependencies": { "duplexer2": { @@ -8882,7 +8976,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "2.2.7" } } } @@ -8897,7 +8991,7 @@ "resolved": "https://registry.npmjs.org/moment-recur/-/moment-recur-1.0.7.tgz", "integrity": "sha1-TVCSr2SK7e1q/lwT7zjFKQHJlBk=", "requires": { - "moment": "<3.0.0" + "moment": "2.22.1" } }, "mongodb": { @@ -8915,8 +9009,8 @@ "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.19.tgz", "integrity": "sha512-Jt4AtWUkpuW03kRdYGxga4O65O1UHlFfvvInslEfLlGi+zDMxbBe3J2NVmN9qPJ957Mn6Iz0UpMtV80cmxCVxw==", "requires": { - "bson": "~1.0.4", - "require_optional": "~1.0.0" + "bson": "1.0.6", + "require_optional": "1.0.1" } }, "mongoose": { @@ -8925,7 +9019,7 @@ "integrity": "sha512-GZqoN85gpk7+MTxZkE+yEVTtvvGfG5//X3UMWfbPQJhNO3nmmF4GFdE1qro73Vsn0PCchxyst3z55JpqZuYAZA==", "requires": { "async": "2.1.4", - "bson": "~1.0.4", + "bson": "1.0.6", "kareem": "2.0.6", "lodash.get": "4.4.2", "mongodb": "3.0.7", @@ -8942,7 +9036,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", "requires": { - "lodash": "^4.14.0" + "lodash": "4.17.10" } }, "mongodb": { @@ -8958,8 +9052,8 @@ "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.7.tgz", "integrity": "sha512-z6YufO7s40wLiv2ssFshqoLS4+Kf+huhHq6KZ7gDArsKNzXYjAwTMnhEIJ9GQ8fIfBGs5tBLNPfbIDoCKGPmOw==", "requires": { - "bson": "~1.0.4", - "require_optional": "^1.0.1" + "bson": "1.0.6", + "require_optional": "1.0.1" } } } @@ -8979,11 +9073,11 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.8.2.tgz", "integrity": "sha1-eErHc05KRTqcbm6GgKkyknXItoc=", "requires": { - "basic-auth": "~1.1.0", + "basic-auth": "1.1.0", "debug": "2.6.8", - "depd": "~1.1.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "depd": "1.1.2", + "on-finished": "2.3.0", + "on-headers": "1.0.1" }, "dependencies": { "debug": { @@ -9006,12 +9100,12 @@ "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" } }, "mpath": { @@ -9052,14 +9146,14 @@ "resolved": "https://registry.npmjs.org/multer/-/multer-1.3.0.tgz", "integrity": "sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=", "requires": { - "append-field": "^0.1.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.0", - "mkdirp": "^0.5.1", - "object-assign": "^3.0.0", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "append-field": "0.1.0", + "busboy": "0.2.14", + "concat-stream": "1.6.2", + "mkdirp": "0.5.1", + "object-assign": "3.0.0", + "on-finished": "2.3.0", + "type-is": "1.6.16", + "xtend": "4.0.1" }, "dependencies": { "object-assign": { @@ -9074,8 +9168,8 @@ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" + "dns-packet": "1.3.1", + "thunky": "1.0.2" } }, "multicast-dns-service-types": { @@ -9108,18 +9202,18 @@ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "natives": { @@ -9139,10 +9233,10 @@ "integrity": "sha512-ioYYogSaZhFlCpRizQgY3UT3G1qFXmHGY/5ozoFE3dMfiCRAeJfh+IPE3/eh9gCZvqLhPCWb4bLt7Bqzo+1mLQ==", "dev": true, "requires": { - "nomnom": "~1.6.2", - "railroad-diagrams": "^1.0.0", + "nomnom": "1.6.2", + "railroad-diagrams": "1.0.0", "randexp": "0.4.6", - "semver": "^5.4.1" + "semver": "5.5.0" } }, "negotiator": { @@ -9172,11 +9266,11 @@ "integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==", "dev": true, "requires": { - "@sinonjs/formatio": "^2.0.0", - "just-extend": "^1.1.27", - "lolex": "^2.3.2", - "path-to-regexp": "^1.7.0", - "text-encoding": "^0.6.4" + "@sinonjs/formatio": "2.0.0", + "just-extend": "1.1.27", + "lolex": "2.7.0", + "path-to-regexp": "1.7.0", + "text-encoding": "0.6.4" }, "dependencies": { "isarray": { @@ -9207,7 +9301,7 @@ "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "requires": { - "lower-case": "^1.1.1" + "lower-case": "1.1.4" } }, "nocache": { @@ -9220,8 +9314,8 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" + "encoding": "0.1.12", + "is-stream": "1.1.0" } }, "node-forge": { @@ -9234,9 +9328,9 @@ "resolved": "https://registry.npmjs.org/node-geocoder/-/node-geocoder-3.22.0.tgz", "integrity": "sha512-w7ew1vH6IjkhexoxcJ2aFBMMHdfS/VY5xiJ29jd6ml3l5nitySLeJ2vc5IxEfhgq2sZvh7mBk9dJlMqKEKBqJg==", "requires": { - "bluebird": "^3.4.6", - "request": "^2.74.0", - "request-promise": "^4.1.1" + "bluebird": "3.5.1", + "request": "2.85.0", + "request-promise": "4.2.2" } }, "node-libs-browser": { @@ -9244,28 +9338,28 @@ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", + "assert": "1.4.1", + "browserify-zlib": "0.2.0", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "domain-browser": "1.2.0", + "events": "1.1.1", + "https-browserify": "1.0.0", + "os-browserify": "0.3.0", "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.6", + "stream-browserify": "2.0.1", + "stream-http": "2.8.1", + "string_decoder": "1.0.3", + "timers-browserify": "2.0.10", "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", + "url": "0.11.0", + "util": "0.10.3", "vm-browserify": "0.0.4" }, "dependencies": { @@ -9284,13 +9378,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" }, "dependencies": { "string_decoder": { @@ -9298,7 +9392,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -9310,16 +9404,16 @@ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.17.3.tgz", "integrity": "sha512-8AtS+wA5u6qoE12LONjqOzUzxAI5ObzSw6U5LgqpaO/0y6wwId4l5dN0ZulYyYdpLZD1MbkBp7GjG1hqaoRqYg==", "requires": { - "chokidar": "^2.0.2", - "debug": "^3.1.0", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.0", - "semver": "^5.5.0", - "supports-color": "^5.2.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.3.0" + "chokidar": "2.0.3", + "debug": "3.1.0", + "ignore-by-default": "1.0.1", + "minimatch": "3.0.4", + "pstree.remy": "1.1.0", + "semver": "5.5.0", + "supports-color": "5.4.0", + "touch": "3.1.0", + "undefsafe": "2.0.2", + "update-notifier": "2.5.0" }, "dependencies": { "debug": { @@ -9338,8 +9432,8 @@ "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", "dev": true, "requires": { - "colors": "0.5.x", - "underscore": "~1.4.4" + "colors": "0.5.1", + "underscore": "1.4.4" }, "dependencies": { "colors": { @@ -9355,7 +9449,7 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "requires": { - "abbrev": "1" + "abbrev": "1.1.1" } }, "normalize-package-data": { @@ -9363,10 +9457,10 @@ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" } }, "normalize-path": { @@ -9374,7 +9468,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "normalize-range": { @@ -9387,10 +9481,10 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" + "object-assign": "4.1.1", + "prepend-http": "1.0.4", + "query-string": "4.3.4", + "sort-keys": "1.1.2" } }, "normalizr": { @@ -9403,7 +9497,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "nth-check": { @@ -9411,7 +9505,7 @@ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "requires": { - "boolbase": "~1.0.0" + "boolbase": "1.0.0" } }, "num2fraction": { @@ -9436,33 +9530,33 @@ "integrity": "sha1-ikpO1pCWbBHsWH/4fuoMEsl0upk=", "dev": true, "requires": { - "archy": "^1.0.0", - "arrify": "^1.0.1", - "caching-transform": "^1.0.0", - "convert-source-map": "^1.5.1", - "debug-log": "^1.0.1", - "default-require-extensions": "^1.0.0", - "find-cache-dir": "^0.1.1", - "find-up": "^2.1.0", - "foreground-child": "^1.5.3", - "glob": "^7.0.6", - "istanbul-lib-coverage": "^1.2.0", - "istanbul-lib-hook": "^1.1.0", - "istanbul-lib-instrument": "^2.1.0", - "istanbul-lib-report": "^1.1.3", - "istanbul-lib-source-maps": "^1.2.5", - "istanbul-reports": "^1.4.1", - "md5-hex": "^1.2.0", - "merge-source-map": "^1.1.0", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.0", - "resolve-from": "^2.0.0", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.1", - "spawn-wrap": "^1.4.2", - "test-exclude": "^4.2.0", + "archy": "1.0.0", + "arrify": "1.0.1", + "caching-transform": "1.0.1", + "convert-source-map": "1.5.1", + "debug-log": "1.0.1", + "default-require-extensions": "1.0.0", + "find-cache-dir": "0.1.1", + "find-up": "2.1.0", + "foreground-child": "1.5.6", + "glob": "7.1.2", + "istanbul-lib-coverage": "1.2.0", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "2.2.0", + "istanbul-lib-report": "1.1.3", + "istanbul-lib-source-maps": "1.2.5", + "istanbul-reports": "1.4.1", + "md5-hex": "1.3.0", + "merge-source-map": "1.1.0", + "micromatch": "3.1.10", + "mkdirp": "0.5.1", + "resolve-from": "2.0.0", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "spawn-wrap": "1.4.2", + "test-exclude": "4.2.1", "yargs": "11.1.0", - "yargs-parser": "^8.0.0" + "yargs-parser": "8.1.0" }, "dependencies": { "@babel/code-frame": { @@ -9481,10 +9575,10 @@ "dev": true, "requires": { "@babel/types": "7.0.0-beta.49", - "jsesc": "^2.5.1", - "lodash": "^4.17.5", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "jsesc": "2.5.1", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" } }, "@babel/helper-function-name": { @@ -9522,9 +9616,9 @@ "integrity": "sha1-lr3GtD4TSCASumaRsQGEktOWIsw=", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "@babel/template": { @@ -9536,7 +9630,7 @@ "@babel/code-frame": "7.0.0-beta.49", "@babel/parser": "7.0.0-beta.49", "@babel/types": "7.0.0-beta.49", - "lodash": "^4.17.5" + "lodash": "4.17.10" } }, "@babel/traverse": { @@ -9551,10 +9645,10 @@ "@babel/helper-split-export-declaration": "7.0.0-beta.49", "@babel/parser": "7.0.0-beta.49", "@babel/types": "7.0.0-beta.49", - "debug": "^3.1.0", - "globals": "^11.1.0", - "invariant": "^2.2.0", - "lodash": "^4.17.5" + "debug": "3.1.0", + "globals": "11.5.0", + "invariant": "2.2.4", + "lodash": "4.17.10" } }, "@babel/types": { @@ -9563,9 +9657,9 @@ "integrity": "sha1-t+Oxw/TUz+Eb34yJ8e/V4WF7h6Y=", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.5", - "to-fast-properties": "^2.0.0" + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "2.0.0" } }, "align-text": { @@ -9574,9 +9668,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -9597,7 +9691,7 @@ "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { - "default-require-extensions": "^1.0.0" + "default-require-extensions": "1.0.0" } }, "archy": { @@ -9666,13 +9760,13 @@ "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -9681,7 +9775,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -9690,7 +9784,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -9699,7 +9793,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -9708,9 +9802,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "kind-of": { @@ -9727,7 +9821,7 @@ "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -9737,16 +9831,16 @@ "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -9755,7 +9849,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -9772,15 +9866,15 @@ "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" } }, "caching-transform": { @@ -9789,9 +9883,9 @@ "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", "dev": true, "requires": { - "md5-hex": "^1.2.0", - "mkdirp": "^0.5.1", - "write-file-atomic": "^1.1.4" + "md5-hex": "1.3.0", + "mkdirp": "0.5.1", + "write-file-atomic": "1.3.4" } }, "camelcase": { @@ -9808,8 +9902,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "class-utils": { @@ -9818,10 +9912,10 @@ "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -9830,7 +9924,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -9842,8 +9936,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -9868,8 +9962,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "commondir": { @@ -9908,8 +10002,8 @@ "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "which": "1.3.1" } }, "debug": { @@ -9945,7 +10039,7 @@ "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { - "strip-bom": "^2.0.0" + "strip-bom": "2.0.0" } }, "define-property": { @@ -9954,8 +10048,8 @@ "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -9964,7 +10058,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -9973,7 +10067,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -9982,9 +10076,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "kind-of": { @@ -10001,7 +10095,7 @@ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "execa": { @@ -10010,13 +10104,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" }, "dependencies": { "cross-spawn": { @@ -10025,9 +10119,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" } } } @@ -10038,13 +10132,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "debug": { @@ -10062,7 +10156,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -10071,7 +10165,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -10082,8 +10176,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -10092,7 +10186,7 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -10103,14 +10197,14 @@ "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -10119,7 +10213,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -10128,7 +10222,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -10137,7 +10231,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -10146,7 +10240,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -10155,9 +10249,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "kind-of": { @@ -10174,10 +10268,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -10186,7 +10280,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -10197,9 +10291,9 @@ "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true, "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" } }, "find-up": { @@ -10208,7 +10302,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "for-in": { @@ -10223,8 +10317,8 @@ "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", "dev": true, "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "cross-spawn": "4.0.2", + "signal-exit": "3.0.2" } }, "fragment-cache": { @@ -10233,7 +10327,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "fs.realpath": { @@ -10266,12 +10360,12 @@ "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "globals": { @@ -10292,10 +10386,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "source-map": { @@ -10304,7 +10398,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -10315,9 +10409,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, "has-values": { @@ -10326,8 +10420,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "kind-of": { @@ -10336,7 +10430,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -10359,8 +10453,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -10381,7 +10475,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-arrayish": { @@ -10402,7 +10496,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-data-descriptor": { @@ -10411,7 +10505,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-descriptor": { @@ -10420,9 +10514,9 @@ "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -10451,7 +10545,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-odd": { @@ -10460,7 +10554,7 @@ "integrity": "sha1-dkZiRnH9fqVYzNmieVGC8pWPGyQ=", "dev": true, "requires": { - "is-number": "^4.0.0" + "is-number": "4.0.0" }, "dependencies": { "is-number": { @@ -10477,7 +10571,7 @@ "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "is-stream": { @@ -10528,7 +10622,7 @@ "integrity": "sha1-hTjZcDcss3FtU+VVI91UtVeo2Js=", "dev": true, "requires": { - "append-transform": "^0.4.0" + "append-transform": "0.4.0" } }, "istanbul-lib-instrument": { @@ -10542,8 +10636,8 @@ "@babel/template": "7.0.0-beta.49", "@babel/traverse": "7.0.0-beta.49", "@babel/types": "7.0.0-beta.49", - "istanbul-lib-coverage": "^2.0.0", - "semver": "^5.5.0" + "istanbul-lib-coverage": "2.0.0", + "semver": "5.5.0" }, "dependencies": { "istanbul-lib-coverage": { @@ -10560,10 +10654,10 @@ "integrity": "sha1-LfEhiMD6d5kMDSF20tC6M5QYglk=", "dev": true, "requires": { - "istanbul-lib-coverage": "^1.1.2", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" }, "dependencies": { "has-flag": { @@ -10578,7 +10672,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -10589,11 +10683,11 @@ "integrity": "sha1-/+a+Tnq4bTYD5CkNVJkLFFBvybE=", "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.0", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "debug": "3.1.0", + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" } }, "istanbul-reports": { @@ -10602,7 +10696,7 @@ "integrity": "sha1-Ty6OkoqnoF0dpsQn1AmLJlXsczQ=", "dev": true, "requires": { - "handlebars": "^4.0.3" + "handlebars": "4.0.11" } }, "jsesc": { @@ -10617,7 +10711,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "lazy-cache": { @@ -10633,7 +10727,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "load-json-file": { @@ -10642,11 +10736,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" } }, "locate-path": { @@ -10655,8 +10749,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" }, "dependencies": { "path-exists": { @@ -10679,8 +10773,8 @@ "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "map-cache": { @@ -10695,7 +10789,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "md5-hex": { @@ -10704,7 +10798,7 @@ "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", "dev": true, "requires": { - "md5-o-matic": "^0.1.1" + "md5-o-matic": "0.1.1" } }, "md5-o-matic": { @@ -10719,7 +10813,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "merge-source-map": { @@ -10728,7 +10822,7 @@ "integrity": "sha1-L93n5gIJOfcJBqaPLXrmheTIxkY=", "dev": true, "requires": { - "source-map": "^0.6.1" + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -10745,19 +10839,19 @@ "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "kind-of": { @@ -10780,7 +10874,7 @@ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -10795,8 +10889,8 @@ "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -10805,7 +10899,7 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -10831,18 +10925,18 @@ "integrity": "sha1-h59xUMstq3pHElkGbBBO7m4Pp8I=", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "kind-of": { @@ -10859,10 +10953,10 @@ "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" } }, "npm-run-path": { @@ -10871,7 +10965,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -10892,9 +10986,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -10903,7 +10997,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -10914,7 +11008,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" } }, "object.pick": { @@ -10923,7 +11017,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "once": { @@ -10932,7 +11026,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "optimist": { @@ -10941,8 +11035,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" } }, "os-homedir": { @@ -10957,9 +11051,9 @@ "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "p-finally": { @@ -10974,7 +11068,7 @@ "integrity": "sha1-DpK2vty1nwIsE9DxlJ3ILRWQnxw=", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -10983,7 +11077,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.2.0" } }, "p-try": { @@ -10998,7 +11092,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.1" } }, "pascalcase": { @@ -11013,7 +11107,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -11040,9 +11134,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -11063,7 +11157,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -11072,7 +11166,7 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" }, "dependencies": { "find-up": { @@ -11081,8 +11175,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } } } @@ -11105,9 +11199,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -11116,8 +11210,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" }, "dependencies": { "find-up": { @@ -11126,8 +11220,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } } } @@ -11138,8 +11232,8 @@ "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "repeat-element": { @@ -11191,7 +11285,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -11200,7 +11294,7 @@ "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "safe-regex": { @@ -11209,7 +11303,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "semver": { @@ -11230,10 +11324,10 @@ "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -11242,7 +11336,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -11253,7 +11347,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -11280,14 +11374,14 @@ "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.2", + "use": "3.1.0" }, "dependencies": { "debug": { @@ -11305,7 +11399,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -11314,7 +11408,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -11325,9 +11419,9 @@ "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -11336,7 +11430,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -11345,7 +11439,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -11354,7 +11448,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -11363,9 +11457,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "kind-of": { @@ -11382,7 +11476,7 @@ "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" } }, "source-map": { @@ -11397,11 +11491,11 @@ "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", "dev": true, "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.1", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-url": { @@ -11416,12 +11510,12 @@ "integrity": "sha1-z/WOc6giRhe2Vhq9wyWG6gyCJIw=", "dev": true, "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" + "foreground-child": "1.5.6", + "mkdirp": "0.5.1", + "os-homedir": "1.0.2", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "which": "1.3.1" } }, "spdx-correct": { @@ -11430,8 +11524,8 @@ "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, "spdx-exceptions": { @@ -11446,8 +11540,8 @@ "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" } }, "spdx-license-ids": { @@ -11462,7 +11556,7 @@ "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "static-extend": { @@ -11471,8 +11565,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -11481,7 +11575,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -11492,8 +11586,8 @@ "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "strip-ansi": { @@ -11502,7 +11596,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "strip-bom": { @@ -11511,7 +11605,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "strip-eof": { @@ -11526,11 +11620,11 @@ "integrity": "sha1-36Ii8DSAvKaSB8pyizfXS0X3JPo=", "dev": true, "requires": { - "arrify": "^1.0.1", - "micromatch": "^3.1.8", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", - "require-main-filename": "^1.0.1" + "arrify": "1.0.1", + "micromatch": "3.1.10", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" } }, "to-fast-properties": { @@ -11545,7 +11639,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "to-regex": { @@ -11554,10 +11648,10 @@ "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -11566,8 +11660,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } }, "uglify-js": { @@ -11577,9 +11671,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" }, "dependencies": { "yargs": { @@ -11589,9 +11683,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } @@ -11610,10 +11704,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -11622,7 +11716,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -11631,10 +11725,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -11645,8 +11739,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -11655,9 +11749,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -11691,7 +11785,7 @@ "integrity": "sha1-FHFr8D/f79AwQK71jYtLhfOnxUQ=", "dev": true, "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" }, "dependencies": { "kind-of": { @@ -11708,8 +11802,8 @@ "integrity": "sha1-gWQ7y+8b3+zUYjeT3EZIlIupgzg=", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "which": { @@ -11718,7 +11812,7 @@ "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -11746,8 +11840,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "ansi-regex": { @@ -11762,7 +11856,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -11771,9 +11865,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "strip-ansi": { @@ -11782,7 +11876,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } } } @@ -11799,9 +11893,9 @@ "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "slide": "^1.1.5" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" } }, "y18n": { @@ -11822,18 +11916,18 @@ "integrity": "sha1-kLhpk07W6HERXqL/WLA/RyTtLXc=", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" }, "dependencies": { "camelcase": { @@ -11848,9 +11942,9 @@ "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" } }, "yargs-parser": { @@ -11859,7 +11953,7 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -11870,7 +11964,7 @@ "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" }, "dependencies": { "camelcase": { @@ -11908,9 +12002,9 @@ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -11918,7 +12012,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "kind-of": { @@ -11926,7 +12020,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -11953,7 +12047,7 @@ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" } }, "object.assign": { @@ -11962,10 +12056,10 @@ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "define-properties": "1.1.2", + "function-bind": "1.1.1", + "has-symbols": "1.0.0", + "object-keys": "1.0.11" } }, "object.defaults": { @@ -11973,10 +12067,10 @@ "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" } }, "object.entries": { @@ -11985,10 +12079,22 @@ "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", - "has": "^1.0.1" + "define-properties": "1.1.2", + "es-abstract": "1.11.0", + "function-bind": "1.1.1", + "has": "1.0.1" + } + }, + "object.fromentries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", + "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.11.0", + "function-bind": "1.1.1", + "has": "1.0.1" } }, "object.map": { @@ -11996,8 +12102,8 @@ "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "object.omit": { @@ -12005,8 +12111,8 @@ "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" }, "dependencies": { "for-own": { @@ -12014,7 +12120,7 @@ "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } } } @@ -12024,7 +12130,7 @@ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "object.values": { @@ -12033,10 +12139,10 @@ "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", - "has": "^1.0.1" + "define-properties": "1.1.2", + "es-abstract": "1.11.0", + "function-bind": "1.1.1", + "has": "1.0.1" } }, "obuf": { @@ -12062,7 +12168,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -12071,7 +12177,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "opn": { @@ -12079,7 +12185,7 @@ "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", "requires": { - "is-wsl": "^1.1.0" + "is-wsl": "1.1.0" } }, "opt-cli": { @@ -12100,7 +12206,7 @@ "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "dev": true, "requires": { - "graceful-readlink": ">= 1.0.0" + "graceful-readlink": "1.0.1" } }, "lodash.clone": { @@ -12109,7 +12215,7 @@ "integrity": "sha1-5WsXa2gjp93jj38r9Y3n1ZcSAOk=", "dev": true, "requires": { - "lodash._baseclone": "~4.5.0" + "lodash._baseclone": "4.5.7" } }, "spawn-command": { @@ -12126,12 +12232,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" } }, "orchestrator": { @@ -12139,9 +12245,9 @@ "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", "requires": { - "end-of-stream": "~0.1.5", - "sequencify": "~0.0.7", - "stream-consume": "~0.1.0" + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.1" }, "dependencies": { "end-of-stream": { @@ -12149,7 +12255,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", "requires": { - "once": "~1.3.0" + "once": "1.3.3" } }, "once": { @@ -12157,7 +12263,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } } } @@ -12172,7 +12278,7 @@ "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", "requires": { - "url-parse": "1.0.x" + "url-parse": "1.0.5" }, "dependencies": { "url-parse": { @@ -12180,8 +12286,8 @@ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", "requires": { - "querystringify": "0.0.x", - "requires-port": "1.0.x" + "querystringify": "0.0.4", + "requires-port": "1.0.0" } } } @@ -12201,9 +12307,9 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "os-tmpdir": { @@ -12221,7 +12327,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -12229,7 +12335,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.2.0" } }, "p-map": { @@ -12247,7 +12353,7 @@ "resolved": "https://registry.npmjs.org/pace/-/pace-0.0.4.tgz", "integrity": "sha1-1mQF1fW8EtJUQabibIeNvGnnenc=", "requires": { - "charm": "~0.1.0" + "charm": "0.1.2" } }, "package-json": { @@ -12255,10 +12361,10 @@ "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "6.7.1", + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0", + "semver": "5.5.0" } }, "pako": { @@ -12271,9 +12377,9 @@ "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.2.7" } }, "param-case": { @@ -12281,7 +12387,7 @@ "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", "requires": { - "no-case": "^2.2.0" + "no-case": "2.3.2" } }, "parchment": { @@ -12294,7 +12400,7 @@ "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", "requires": { - "path-platform": "~0.11.15" + "path-platform": "0.11.15" } }, "parse-asn1": { @@ -12302,11 +12408,11 @@ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" + "asn1.js": "4.10.1", + "browserify-aes": "1.2.0", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.16" } }, "parse-filepath": { @@ -12314,9 +12420,9 @@ "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" } }, "parse-glob": { @@ -12324,10 +12430,10 @@ "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" }, "dependencies": { "is-extglob": { @@ -12340,7 +12446,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -12350,7 +12456,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.1" } }, "parse-passwd": { @@ -12363,7 +12469,7 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", "requires": { - "@types/node": "*" + "@types/node": "6.0.107" } }, "parseqs": { @@ -12371,7 +12477,7 @@ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseuri": { @@ -12379,7 +12485,7 @@ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseurl": { @@ -12397,7 +12503,7 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.3.2.tgz", "integrity": "sha1-ndAJ+RXo/glbASSgG4+C2gdRAQI=", "requires": { - "passport-strategy": "1.x.x", + "passport-strategy": "1.0.0", "pause": "0.0.1" } }, @@ -12406,7 +12512,7 @@ "resolved": "https://registry.npmjs.org/passport-google-oauth2/-/passport-google-oauth2-0.1.6.tgz", "integrity": "sha1-39cBasdEn+J8/rJSrpdK/CMleg0=", "requires": { - "passport-oauth2": "^1.1.2" + "passport-oauth2": "1.4.0" } }, "passport-local": { @@ -12414,7 +12520,7 @@ "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", "requires": { - "passport-strategy": "1.x.x" + "passport-strategy": "1.0.0" } }, "passport-oauth2": { @@ -12422,10 +12528,10 @@ "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz", "integrity": "sha1-9i+BWDy+EmCb585vFguTlaJ7hq0=", "requires": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" + "oauth": "0.9.15", + "passport-strategy": "1.0.0", + "uid2": "0.0.3", + "utils-merge": "1.0.1" } }, "passport-strategy": { @@ -12478,7 +12584,7 @@ "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "requires": { - "path-root-regex": "^0.1.0" + "path-root-regex": "0.1.2" } }, "path-root-regex": { @@ -12496,7 +12602,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "pathval": { @@ -12515,7 +12621,7 @@ "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "requires": { - "through": "~2.3" + "through": "2.3.8" } }, "pbkdf2": { @@ -12523,11 +12629,11 @@ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" } }, "performance-now": { @@ -12550,7 +12656,7 @@ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -12558,7 +12664,7 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "requires": { - "find-up": "^2.1.0" + "find-up": "2.1.0" } }, "platform": { @@ -12571,10 +12677,10 @@ "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" + "ansi-colors": "1.1.0", + "arr-diff": "4.0.0", + "arr-union": "3.1.0", + "extend-shallow": "3.0.2" } }, "pluralize": { @@ -12589,14 +12695,19 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "popper.js": { + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.7.tgz", + "integrity": "sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ==" + }, "portfinder": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" + "async": "1.5.2", + "debug": "2.6.9", + "mkdirp": "0.5.1" }, "dependencies": { "async": { @@ -12616,9 +12727,9 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" } }, "postcss-calc": { @@ -12626,9 +12737,9 @@ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", "requires": { - "postcss": "^5.0.2", - "postcss-message-helpers": "^2.0.0", - "reduce-css-calc": "^1.2.6" + "postcss": "5.2.18", + "postcss-message-helpers": "2.0.0", + "reduce-css-calc": "1.3.0" }, "dependencies": { "ansi-styles": { @@ -12641,11 +12752,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -12665,10 +12776,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -12681,7 +12792,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -12691,9 +12802,9 @@ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", "requires": { - "colormin": "^1.0.5", - "postcss": "^5.0.13", - "postcss-value-parser": "^3.2.3" + "colormin": "1.1.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -12706,11 +12817,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -12730,10 +12841,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -12746,7 +12857,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -12756,8 +12867,8 @@ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", "requires": { - "postcss": "^5.0.11", - "postcss-value-parser": "^3.1.2" + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -12770,11 +12881,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -12794,10 +12905,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -12810,7 +12921,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -12820,7 +12931,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", "requires": { - "postcss": "^5.0.14" + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { @@ -12833,11 +12944,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -12857,10 +12968,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -12873,7 +12984,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -12883,7 +12994,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", "requires": { - "postcss": "^5.0.4" + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { @@ -12896,11 +13007,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -12920,10 +13031,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -12936,7 +13047,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -12946,7 +13057,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", "requires": { - "postcss": "^5.0.14" + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { @@ -12959,11 +13070,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -12983,10 +13094,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -12999,7 +13110,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13009,7 +13120,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", "requires": { - "postcss": "^5.0.16" + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { @@ -13022,11 +13133,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13046,10 +13157,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13062,7 +13173,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13072,8 +13183,8 @@ "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", "requires": { - "postcss": "^5.0.14", - "uniqs": "^2.0.0" + "postcss": "5.2.18", + "uniqs": "2.0.0" }, "dependencies": { "ansi-styles": { @@ -13086,11 +13197,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13110,10 +13221,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13126,7 +13237,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13136,8 +13247,8 @@ "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", "requires": { - "postcss": "^5.0.4", - "uniqid": "^4.0.0" + "postcss": "5.2.18", + "uniqid": "4.1.1" }, "dependencies": { "ansi-styles": { @@ -13150,11 +13261,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13174,10 +13285,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13190,7 +13301,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13200,10 +13311,10 @@ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0", - "postcss-load-options": "^1.2.0", - "postcss-load-plugins": "^2.3.0" + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1", + "postcss-load-options": "1.2.0", + "postcss-load-plugins": "2.3.0" } }, "postcss-load-options": { @@ -13211,8 +13322,8 @@ "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0" + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1" } }, "postcss-load-plugins": { @@ -13220,8 +13331,8 @@ "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", "requires": { - "cosmiconfig": "^2.1.1", - "object-assign": "^4.1.0" + "cosmiconfig": "2.2.2", + "object-assign": "4.1.1" } }, "postcss-loader": { @@ -13229,10 +13340,10 @@ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.4.tgz", "integrity": "sha512-L2p654oK945B/gDFUGgOhh7uzj19RWoY1SVMeJVoKno1H2MdbQ0RppR/28JGju4pMb22iRC7BJ9aDzbxXSLf4A==", "requires": { - "loader-utils": "^1.1.0", - "postcss": "^6.0.0", - "postcss-load-config": "^1.2.0", - "schema-utils": "^0.4.0" + "loader-utils": "1.1.0", + "postcss": "6.0.22", + "postcss-load-config": "1.2.0", + "schema-utils": "0.4.5" }, "dependencies": { "ajv": { @@ -13240,10 +13351,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", "requires": { - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1", + "uri-js": "3.0.2" } }, "schema-utils": { @@ -13251,8 +13362,8 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "ajv": "6.4.0", + "ajv-keywords": "3.2.0" } } } @@ -13262,9 +13373,9 @@ "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "requires": { - "has": "^1.0.1", - "postcss": "^5.0.10", - "postcss-value-parser": "^3.1.1" + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -13277,11 +13388,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13301,10 +13412,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13317,7 +13428,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13327,7 +13438,7 @@ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", "requires": { - "postcss": "^5.0.4" + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { @@ -13340,11 +13451,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13364,10 +13475,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13380,7 +13491,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13390,11 +13501,11 @@ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", "requires": { - "browserslist": "^1.5.2", - "caniuse-api": "^1.5.2", - "postcss": "^5.0.4", - "postcss-selector-parser": "^2.2.2", - "vendors": "^1.0.0" + "browserslist": "1.7.7", + "caniuse-api": "1.6.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3", + "vendors": "1.0.2" }, "dependencies": { "ansi-styles": { @@ -13407,8 +13518,8 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" + "caniuse-db": "1.0.30000832", + "electron-to-chromium": "1.3.44" } }, "chalk": { @@ -13416,11 +13527,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13440,10 +13551,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13456,7 +13567,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13471,9 +13582,9 @@ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", "requires": { - "object-assign": "^4.0.1", - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -13486,11 +13597,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13510,10 +13621,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13526,7 +13637,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13536,8 +13647,8 @@ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", "requires": { - "postcss": "^5.0.12", - "postcss-value-parser": "^3.3.0" + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -13550,11 +13661,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13574,10 +13685,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13590,7 +13701,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13600,10 +13711,10 @@ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.2", - "postcss-value-parser": "^3.0.2", - "uniqs": "^2.0.0" + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "uniqs": "2.0.0" }, "dependencies": { "ansi-styles": { @@ -13616,11 +13727,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13640,10 +13751,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13656,7 +13767,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13666,10 +13777,10 @@ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", "requires": { - "alphanum-sort": "^1.0.2", - "has": "^1.0.1", - "postcss": "^5.0.14", - "postcss-selector-parser": "^2.0.0" + "alphanum-sort": "1.0.2", + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3" }, "dependencies": { "ansi-styles": { @@ -13682,11 +13793,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13706,10 +13817,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13722,7 +13833,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13732,7 +13843,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", "requires": { - "postcss": "^6.0.1" + "postcss": "6.0.22" } }, "postcss-modules-local-by-default": { @@ -13740,8 +13851,8 @@ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.22" } }, "postcss-modules-scope": { @@ -13749,8 +13860,8 @@ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.22" } }, "postcss-modules-values": { @@ -13758,8 +13869,8 @@ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.22" } }, "postcss-normalize-charset": { @@ -13767,7 +13878,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", "requires": { - "postcss": "^5.0.5" + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { @@ -13780,11 +13891,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13804,10 +13915,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13820,7 +13931,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13830,10 +13941,10 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^1.4.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3" + "is-absolute-url": "2.1.0", + "normalize-url": "1.9.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -13846,11 +13957,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13870,10 +13981,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13886,7 +13997,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13896,8 +14007,8 @@ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.1" + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -13910,11 +14021,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13934,10 +14045,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -13950,7 +14061,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -13960,8 +14071,8 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -13974,11 +14085,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -13998,10 +14109,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -14014,7 +14125,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -14024,7 +14135,7 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", "requires": { - "postcss": "^5.0.4" + "postcss": "5.2.18" }, "dependencies": { "ansi-styles": { @@ -14037,11 +14148,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -14061,10 +14172,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -14077,7 +14188,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -14087,9 +14198,9 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", "requires": { - "has": "^1.0.1", - "postcss": "^5.0.8", - "postcss-value-parser": "^3.0.1" + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" }, "dependencies": { "ansi-styles": { @@ -14102,11 +14213,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -14126,10 +14237,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -14142,7 +14253,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -14152,9 +14263,9 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", "requires": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "flatten": "1.0.2", + "indexes-of": "1.0.1", + "uniq": "1.0.1" } }, "postcss-svgo": { @@ -14162,10 +14273,10 @@ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "requires": { - "is-svg": "^2.0.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3", - "svgo": "^0.7.0" + "is-svg": "2.1.0", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "svgo": "0.7.2" }, "dependencies": { "ansi-styles": { @@ -14178,11 +14289,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -14202,10 +14313,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -14218,7 +14329,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -14228,9 +14339,9 @@ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "uniqs": "2.0.0" }, "dependencies": { "ansi-styles": { @@ -14243,11 +14354,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -14267,10 +14378,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -14283,7 +14394,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -14298,9 +14409,9 @@ "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "requires": { - "has": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" + "has": "1.0.1", + "postcss": "5.2.18", + "uniqs": "2.0.0" }, "dependencies": { "ansi-styles": { @@ -14313,11 +14424,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "supports-color": { @@ -14337,10 +14448,10 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "1.1.3", + "js-base64": "2.4.3", + "source-map": "0.5.7", + "supports-color": "3.2.3" } }, "source-map": { @@ -14353,7 +14464,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -14379,8 +14490,8 @@ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" + "renderkid": "2.0.1", + "utila": "0.4.0" } }, "pretty-hrtime": { @@ -14404,9 +14515,9 @@ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, "promise": { @@ -14414,7 +14525,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "promise-inflight": { @@ -14427,9 +14538,9 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "prop-types-extra": { @@ -14437,7 +14548,7 @@ "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.0.1.tgz", "integrity": "sha1-pXvUgQ6C0no/9DF+zBtK0AX3moI=", "requires": { - "warning": "^3.0.0" + "warning": "3.0.0" } }, "proxy-addr": { @@ -14445,7 +14556,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.1.2", "ipaddr.js": "1.6.0" } }, @@ -14459,7 +14570,7 @@ "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz", "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", "requires": { - "event-stream": "~3.3.0" + "event-stream": "3.3.4" } }, "pseudomap": { @@ -14478,7 +14589,7 @@ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.0.tgz", "integrity": "sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q==", "requires": { - "ps-tree": "^1.1.0" + "ps-tree": "1.1.0" } }, "public-encrypt": { @@ -14486,11 +14597,11 @@ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "parse-asn1": "5.1.1", + "randombytes": "2.0.6" } }, "pump": { @@ -14498,8 +14609,8 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "pumpify": { @@ -14507,9 +14618,9 @@ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", "requires": { - "duplexify": "^3.5.3", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "duplexify": "3.5.4", + "inherits": "2.0.3", + "pump": "2.0.1" } }, "punycode": { @@ -14532,8 +14643,8 @@ "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" } }, "querystring": { @@ -14556,12 +14667,12 @@ "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz", "integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==", "requires": { - "clone": "^2.1.1", - "deep-equal": "^1.0.1", - "eventemitter3": "^2.0.3", - "extend": "^3.0.1", - "parchment": "^1.1.4", - "quill-delta": "^3.6.2" + "clone": "2.1.1", + "deep-equal": "1.0.1", + "eventemitter3": "2.0.3", + "extend": "3.0.1", + "parchment": "1.1.4", + "quill-delta": "3.6.2" }, "dependencies": { "clone": { @@ -14576,8 +14687,8 @@ "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.2.tgz", "integrity": "sha512-grWEQq9woEidPDogtDNxQKmy2LFf9zBC0EU/YTSw6TwKmMjtihTxdnPtPRfrqazB2MSJ7YdCWxmsJ7aQKRSEgg==", "requires": { - "deep-equal": "^1.0.1", - "extend": "^3.0.1", + "deep-equal": "1.0.1", + "extend": "3.0.1", "fast-diff": "1.1.2" } }, @@ -14586,7 +14697,7 @@ "resolved": "https://registry.npmjs.org/quill-image-drop-module/-/quill-image-drop-module-1.0.3.tgz", "integrity": "sha1-Dl7IMp3WehISbxZrGRv2TSBXp9M=", "requires": { - "quill": "^1.2.2" + "quill": "1.3.6" } }, "quill-image-resize-module": { @@ -14594,9 +14705,9 @@ "resolved": "https://registry.npmjs.org/quill-image-resize-module/-/quill-image-resize-module-3.0.0.tgz", "integrity": "sha1-D9k3Rqg3M22VsvU2FAQWpiPHF3E=", "requires": { - "lodash": "^4.17.4", - "quill": "^1.2.2", - "raw-loader": "^0.5.1" + "lodash": "4.17.4", + "quill": "1.2.3", + "raw-loader": "0.5.1" }, "dependencies": { "acorn": { @@ -14609,7 +14720,7 @@ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", "requires": { - "acorn": "^4.0.3" + "acorn": "4.0.11" }, "dependencies": { "acorn": { @@ -14624,7 +14735,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "requires": { - "acorn": "^3.0.4" + "acorn": "3.3.0" }, "dependencies": { "acorn": { @@ -14639,8 +14750,8 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", "integrity": "sha1-tu50ZXuZOgHc5Et5RNVvSFgo1b0=", "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "co": "4.6.0", + "json-stable-stringify": "1.0.1" } }, "ajv-keywords": { @@ -14653,9 +14764,9 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.1.0", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "ansi-escapes": { @@ -14678,8 +14789,8 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=", "requires": { - "arrify": "^1.0.0", - "micromatch": "^2.1.5" + "arrify": "1.0.1", + "micromatch": "2.3.11" } }, "argparse": { @@ -14687,7 +14798,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "aria-query": { @@ -14703,7 +14814,7 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.0.1" } }, "arr-flatten": { @@ -14716,7 +14827,7 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -14734,8 +14845,8 @@ "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "define-properties": "1.1.2", + "es-abstract": "1.7.0" } }, "arrify": { @@ -14748,9 +14859,9 @@ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "bn.js": "4.11.6", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" } }, "assert": { @@ -14771,7 +14882,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.3.0.tgz", "integrity": "sha1-EBPRBRBH3TIP4k5JTVxm7K9hR9k=", "requires": { - "lodash": "^4.14.0" + "lodash": "4.17.4" } }, "async-each": { @@ -14784,21 +14895,21 @@ "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.24.0.tgz", "integrity": "sha1-oF/9IQ3KDCiKJtUxnFrIZpomWtA=", "requires": { - "babel-core": "^6.24.0", - "babel-polyfill": "^6.23.0", - "babel-register": "^6.24.0", - "babel-runtime": "^6.22.0", - "chokidar": "^1.6.1", - "commander": "^2.8.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.0.0", - "glob": "^7.0.0", - "lodash": "^4.2.0", - "output-file-sync": "^1.1.0", - "path-is-absolute": "^1.0.0", - "slash": "^1.0.0", - "source-map": "^0.5.0", - "v8flags": "^2.0.10" + "babel-core": "6.24.0", + "babel-polyfill": "6.23.0", + "babel-register": "6.24.0", + "babel-runtime": "6.23.0", + "chokidar": "1.6.1", + "commander": "2.9.0", + "convert-source-map": "1.5.0", + "fs-readdir-recursive": "1.0.0", + "glob": "7.1.1", + "lodash": "4.17.4", + "output-file-sync": "1.1.2", + "path-is-absolute": "1.0.1", + "slash": "1.0.0", + "source-map": "0.5.6", + "v8flags": "2.0.12" } }, "babel-code-frame": { @@ -14806,9 +14917,9 @@ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", "requires": { - "chalk": "^1.1.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.1" } }, "babel-core": { @@ -14816,25 +14927,25 @@ "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.0.tgz", "integrity": "sha1-jzagp39cFVrtb5ILhE0julZ0KgI=", "requires": { - "babel-code-frame": "^6.22.0", - "babel-generator": "^6.24.0", - "babel-helpers": "^6.23.0", - "babel-messages": "^6.23.0", - "babel-register": "^6.24.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0", - "babel-traverse": "^6.23.1", - "babel-types": "^6.23.0", - "babylon": "^6.11.0", - "convert-source-map": "^1.1.0", - "debug": "^2.1.1", - "json5": "^0.5.0", - "lodash": "^4.2.0", - "minimatch": "^3.0.2", - "path-is-absolute": "^1.0.0", - "private": "^0.1.6", - "slash": "^1.0.0", - "source-map": "^0.5.0" + "babel-code-frame": "6.22.0", + "babel-generator": "6.24.0", + "babel-helpers": "6.23.0", + "babel-messages": "6.23.0", + "babel-register": "6.24.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0", + "babylon": "6.16.1", + "convert-source-map": "1.5.0", + "debug": "2.6.3", + "json5": "0.5.1", + "lodash": "4.17.4", + "minimatch": "3.0.3", + "path-is-absolute": "1.0.1", + "private": "0.1.7", + "slash": "1.0.0", + "source-map": "0.5.6" } }, "babel-eslint": { @@ -14842,10 +14953,10 @@ "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.1.tgz", "integrity": "sha1-B5Qi63O6gR48oIZc6Hrykyf4xS8=", "requires": { - "babel-code-frame": "^6.22.0", - "babel-traverse": "^6.23.1", - "babel-types": "^6.23.0", - "babylon": "^6.16.1" + "babel-code-frame": "6.22.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0", + "babylon": "6.16.1" } }, "babel-generator": { @@ -14853,14 +14964,14 @@ "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.0.tgz", "integrity": "sha1-66JwqMxM5uCaYb5DRl18YsH4fFY=", "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-types": "^6.23.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.2.0", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.23.0", + "babel-types": "6.23.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.6", + "trim-right": "1.0.1" } }, "babel-helper-builder-binary-assignment-operator-visitor": { @@ -14868,9 +14979,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.22.0.tgz", "integrity": "sha1-Kd9WvhRNgb3qwIJiv6QdLF6Rzc0=", "requires": { - "babel-helper-explode-assignable-expression": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-helper-explode-assignable-expression": "6.22.0", + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-helper-call-delegate": { @@ -14878,10 +14989,10 @@ "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz", "integrity": "sha1-EZkhtWEg8X6drj90tPXMe8wbN+8=", "requires": { - "babel-helper-hoist-variables": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.22.0", - "babel-types": "^6.22.0" + "babel-helper-hoist-variables": "6.22.0", + "babel-runtime": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0" } }, "babel-helper-define-map": { @@ -14889,10 +15000,10 @@ "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.23.0.tgz", "integrity": "sha1-FET5YMlpHWmiztaiBTFfj9AIBOc=", "requires": { - "babel-helper-function-name": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-types": "^6.23.0", - "lodash": "^4.2.0" + "babel-helper-function-name": "6.23.0", + "babel-runtime": "6.23.0", + "babel-types": "6.23.0", + "lodash": "4.17.4" } }, "babel-helper-explode-assignable-expression": { @@ -14900,9 +15011,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.22.0.tgz", "integrity": "sha1-yXv3bu0+C65ASBIfK52uGk59BHg=", "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.22.0", - "babel-types": "^6.22.0" + "babel-runtime": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0" } }, "babel-helper-function-name": { @@ -14910,11 +15021,11 @@ "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.23.0.tgz", "integrity": "sha1-JXQtZxdciQPb5LbLnZ4fy43PI6Y=", "requires": { - "babel-helper-get-function-arity": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0", - "babel-traverse": "^6.23.0", - "babel-types": "^6.23.0" + "babel-helper-get-function-arity": "6.22.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0" } }, "babel-helper-get-function-arity": { @@ -14922,8 +15033,8 @@ "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz", "integrity": "sha1-C+tGStadxzR0EKxq3p8DpQY09c4=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-helper-hoist-variables": { @@ -14931,8 +15042,8 @@ "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz", "integrity": "sha1-Pqy/cx2AcFhF3S6XGPYAz7m0unI=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-helper-optimise-call-expression": { @@ -14940,8 +15051,8 @@ "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.23.0.tgz", "integrity": "sha1-8+5+7TVbQoITizPQK3g2nkcGIvU=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.23.0" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-helper-regex": { @@ -14949,9 +15060,9 @@ "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz", "integrity": "sha1-efUyvhZHsfDuNHS19cPaWAAdJH0=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0", - "lodash": "^4.2.0" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0", + "lodash": "4.17.4" } }, "babel-helper-remap-async-to-generator": { @@ -14959,11 +15070,11 @@ "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.22.0.tgz", "integrity": "sha1-IYaucyeO0DuLFc7QiWCdqYEFM4M=", "requires": { - "babel-helper-function-name": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.22.0", - "babel-traverse": "^6.22.0", - "babel-types": "^6.22.0" + "babel-helper-function-name": "6.23.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0" } }, "babel-helper-replace-supers": { @@ -14971,12 +15082,12 @@ "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.23.0.tgz", "integrity": "sha1-7q+K2bWOxDN8qUIjus3KH42bS/0=", "requires": { - "babel-helper-optimise-call-expression": "^6.23.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0", - "babel-traverse": "^6.23.0", - "babel-types": "^6.23.0" + "babel-helper-optimise-call-expression": "6.23.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0" } }, "babel-helpers": { @@ -14984,8 +15095,8 @@ "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.23.0.tgz", "integrity": "sha1-T48uCS0LaogIpL3nnCfx4uzw2ZI=", "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0" + "babel-runtime": "6.23.0", + "babel-template": "6.23.0" } }, "babel-loader": { @@ -14993,10 +15104,10 @@ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-6.4.1.tgz", "integrity": "sha1-CzQRLVsHSKjc2/Uaz2+b1C1QuMo=", "requires": { - "find-cache-dir": "^0.1.1", - "loader-utils": "^0.2.16", - "mkdirp": "^0.5.1", - "object-assign": "^4.0.1" + "find-cache-dir": "0.1.1", + "loader-utils": "0.2.17", + "mkdirp": "0.5.1", + "object-assign": "4.1.1" } }, "babel-messages": { @@ -15004,7 +15115,7 @@ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-check-es2015-constants": { @@ -15012,7 +15123,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-syntax-async-functions": { @@ -15040,9 +15151,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.22.0.tgz", "integrity": "sha1-GUtpOOwZWtNu/EwzqXGs8A2M014=", "requires": { - "babel-helper-remap-async-to-generator": "^6.22.0", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-remap-async-to-generator": "6.22.0", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-class-properties": { @@ -15050,10 +15161,10 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.23.0.tgz", "integrity": "sha1-GHt0fuQEOZATVjyZPbA480dUrDs=", "requires": { - "babel-helper-function-name": "^6.23.0", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0" + "babel-helper-function-name": "6.23.0", + "babel-plugin-syntax-class-properties": "6.13.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0" } }, "babel-plugin-transform-es2015-arrow-functions": { @@ -15061,7 +15172,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-block-scoped-functions": { @@ -15069,7 +15180,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-block-scoping": { @@ -15077,11 +15188,11 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.23.0.tgz", "integrity": "sha1-5IiVzws3W+FIzXyIebQicHoFO1E=", "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0", - "babel-traverse": "^6.23.0", - "babel-types": "^6.23.0", - "lodash": "^4.2.0" + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0", + "lodash": "4.17.4" } }, "babel-plugin-transform-es2015-classes": { @@ -15089,15 +15200,15 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.23.0.tgz", "integrity": "sha1-SbU/MmICov0bO7ql4u3YpPeGQ8E=", "requires": { - "babel-helper-define-map": "^6.23.0", - "babel-helper-function-name": "^6.23.0", - "babel-helper-optimise-call-expression": "^6.23.0", - "babel-helper-replace-supers": "^6.23.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0", - "babel-traverse": "^6.23.0", - "babel-types": "^6.23.0" + "babel-helper-define-map": "6.23.0", + "babel-helper-function-name": "6.23.0", + "babel-helper-optimise-call-expression": "6.23.0", + "babel-helper-replace-supers": "6.23.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0" } }, "babel-plugin-transform-es2015-computed-properties": { @@ -15105,8 +15216,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz", "integrity": "sha1-fDg+lim7pIIMEbBCW91ikPfwV+c=", "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.22.0" + "babel-runtime": "6.23.0", + "babel-template": "6.23.0" } }, "babel-plugin-transform-es2015-destructuring": { @@ -15114,7 +15225,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-duplicate-keys": { @@ -15122,8 +15233,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.22.0.tgz", "integrity": "sha1-ZyOXAxwhYQ1y3Su7C6n7Ynfhw2s=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-plugin-transform-es2015-for-of": { @@ -15131,7 +15242,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-function-name": { @@ -15139,9 +15250,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz", "integrity": "sha1-9fzIsJCT+aI8dqw9njksPsS3cQQ=", "requires": { - "babel-helper-function-name": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-helper-function-name": "6.23.0", + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-plugin-transform-es2015-literals": { @@ -15149,7 +15260,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-modules-amd": { @@ -15157,9 +15268,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.0.tgz", "integrity": "sha1-oZEfubfsfgWkOmPFmVAHVXvPai4=", "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.22.0" + "babel-plugin-transform-es2015-modules-commonjs": "6.24.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0" } }, "babel-plugin-transform-es2015-modules-commonjs": { @@ -15167,10 +15278,10 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.0.tgz", "integrity": "sha1-6SGu+3LCzCbLA9EHYmFWQTIiE08=", "requires": { - "babel-plugin-transform-strict-mode": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0", - "babel-types": "^6.23.0" + "babel-plugin-transform-strict-mode": "6.22.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-types": "6.23.0" } }, "babel-plugin-transform-es2015-modules-systemjs": { @@ -15178,9 +15289,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.23.0.tgz", "integrity": "sha1-rjRpIn/6w5sDENkP7HO/3E9jF7A=", "requires": { - "babel-helper-hoist-variables": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0" + "babel-helper-hoist-variables": "6.22.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0" } }, "babel-plugin-transform-es2015-modules-umd": { @@ -15188,9 +15299,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.0.tgz", "integrity": "sha1-/V+mNSHK6NJzknw5WK/XwGdzNFA=", "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0" + "babel-plugin-transform-es2015-modules-amd": "6.24.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0" } }, "babel-plugin-transform-es2015-object-super": { @@ -15198,8 +15309,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz", "integrity": "sha1-2qYOEUoELqdp3VP+Uo/IIxHrmPw=", "requires": { - "babel-helper-replace-supers": "^6.22.0", - "babel-runtime": "^6.22.0" + "babel-helper-replace-supers": "6.23.0", + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-parameters": { @@ -15207,12 +15318,12 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.23.0.tgz", "integrity": "sha1-OiqrtwyK+UXVzjhvGkJQYlqDrjs=", "requires": { - "babel-helper-call-delegate": "^6.22.0", - "babel-helper-get-function-arity": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.23.0", - "babel-traverse": "^6.23.0", - "babel-types": "^6.23.0" + "babel-helper-call-delegate": "6.22.0", + "babel-helper-get-function-arity": "6.22.0", + "babel-runtime": "6.23.0", + "babel-template": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0" } }, "babel-plugin-transform-es2015-shorthand-properties": { @@ -15220,8 +15331,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz", "integrity": "sha1-i6d24K/6pgv/IekhQDuKZSov9yM=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-plugin-transform-es2015-spread": { @@ -15229,7 +15340,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-sticky-regex": { @@ -15237,9 +15348,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz", "integrity": "sha1-qzFoKehm7j9LnrlpOXV9GaW8RZM=", "requires": { - "babel-helper-regex": "^6.22.0", - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-helper-regex": "6.22.0", + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-plugin-transform-es2015-template-literals": { @@ -15247,7 +15358,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-typeof-symbol": { @@ -15255,7 +15366,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-es2015-unicode-regex": { @@ -15263,9 +15374,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz", "integrity": "sha1-jZzCfn7h3s/mVFT7mGRSoEphPSA=", "requires": { - "babel-helper-regex": "^6.22.0", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" + "babel-helper-regex": "6.22.0", + "babel-runtime": "6.23.0", + "regexpu-core": "2.0.0" } }, "babel-plugin-transform-exponentiation-operator": { @@ -15273,9 +15384,9 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.22.0.tgz", "integrity": "sha1-1XyDNSgZGOVO8FMRjObrEIRoCE0=", "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.22.0", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-builder-binary-assignment-operator-visitor": "6.22.0", + "babel-plugin-syntax-exponentiation-operator": "6.13.0", + "babel-runtime": "6.23.0" } }, "babel-plugin-transform-regenerator": { @@ -15291,8 +15402,8 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz", "integrity": "sha1-4AjfATQP3IfpWdplmRt+BZcMjHw=", "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.22.0" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0" } }, "babel-polyfill": { @@ -15300,9 +15411,9 @@ "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=", "requires": { - "babel-runtime": "^6.22.0", - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" + "babel-runtime": "6.23.0", + "core-js": "2.4.1", + "regenerator-runtime": "0.10.3" } }, "babel-preset-env": { @@ -15310,35 +15421,35 @@ "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.3.2.tgz", "integrity": "sha1-COq9K/gQw2eAaffgUjI0GfFEh0k=", "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^1.4.0", - "invariant": "^2.2.2" + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-to-generator": "6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.23.0", + "babel-plugin-transform-es2015-classes": "6.23.0", + "babel-plugin-transform-es2015-computed-properties": "6.22.0", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.22.0", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.22.0", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.24.0", + "babel-plugin-transform-es2015-modules-systemjs": "6.23.0", + "babel-plugin-transform-es2015-modules-umd": "6.24.0", + "babel-plugin-transform-es2015-object-super": "6.22.0", + "babel-plugin-transform-es2015-parameters": "6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "6.22.0", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.22.0", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.22.0", + "babel-plugin-transform-exponentiation-operator": "6.22.0", + "babel-plugin-transform-regenerator": "6.22.0", + "browserslist": "1.7.7", + "invariant": "2.2.2" } }, "babel-preset-es2015": { @@ -15346,30 +15457,30 @@ "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.0.tgz", "integrity": "sha1-wWLWixkyaW4DbNMRDcHM0wPSZzo=", "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.22.0", - "babel-plugin-transform-es2015-classes": "^6.22.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.22.0", - "babel-plugin-transform-es2015-modules-umd": "^6.24.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.22.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0" + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.23.0", + "babel-plugin-transform-es2015-classes": "6.23.0", + "babel-plugin-transform-es2015-computed-properties": "6.22.0", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.22.0", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.22.0", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.24.0", + "babel-plugin-transform-es2015-modules-systemjs": "6.23.0", + "babel-plugin-transform-es2015-modules-umd": "6.24.0", + "babel-plugin-transform-es2015-object-super": "6.22.0", + "babel-plugin-transform-es2015-parameters": "6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "6.22.0", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.22.0", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.22.0", + "babel-plugin-transform-regenerator": "6.22.0" } }, "babel-register": { @@ -15377,13 +15488,13 @@ "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.0.tgz", "integrity": "sha1-Xon4RjuplwNW0C6wfavjMIsIDP0=", "requires": { - "babel-core": "^6.24.0", - "babel-runtime": "^6.22.0", - "core-js": "^2.4.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.2" + "babel-core": "6.24.0", + "babel-runtime": "6.23.0", + "core-js": "2.4.1", + "home-or-tmp": "2.0.0", + "lodash": "4.17.4", + "mkdirp": "0.5.1", + "source-map-support": "0.4.14" } }, "babel-runtime": { @@ -15391,8 +15502,8 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" + "core-js": "2.4.1", + "regenerator-runtime": "0.10.3" } }, "babel-template": { @@ -15400,11 +15511,11 @@ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.23.0.tgz", "integrity": "sha1-BNTycK27OqcEqBQ64m+qUpI45jg=", "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.23.0", - "babel-types": "^6.23.0", - "babylon": "^6.11.0", - "lodash": "^4.2.0" + "babel-runtime": "6.23.0", + "babel-traverse": "6.23.1", + "babel-types": "6.23.0", + "babylon": "6.16.1", + "lodash": "4.17.4" } }, "babel-traverse": { @@ -15412,15 +15523,15 @@ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.23.1.tgz", "integrity": "sha1-08tZAQ7NBql9gTEAZflmtpnhT0g=", "requires": { - "babel-code-frame": "^6.22.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-types": "^6.23.0", - "babylon": "^6.15.0", - "debug": "^2.2.0", - "globals": "^9.0.0", - "invariant": "^2.2.0", - "lodash": "^4.2.0" + "babel-code-frame": "6.22.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.23.0", + "babel-types": "6.23.0", + "babylon": "6.16.1", + "debug": "2.6.3", + "globals": "9.17.0", + "invariant": "2.2.2", + "lodash": "4.17.4" } }, "babel-types": { @@ -15428,10 +15539,10 @@ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.23.0.tgz", "integrity": "sha1-uxcXnXU4utOM0MnhFdNA935+ms8=", "requires": { - "babel-runtime": "^6.22.0", - "esutils": "^2.0.2", - "lodash": "^4.2.0", - "to-fast-properties": "^1.0.1" + "babel-runtime": "6.23.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.2" } }, "babylon": { @@ -15469,7 +15580,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=", "requires": { - "balanced-match": "^0.4.1", + "balanced-match": "0.4.2", "concat-map": "0.0.1" } }, @@ -15478,9 +15589,9 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" } }, "brorand": { @@ -15493,11 +15604,11 @@ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=", "requires": { - "buffer-xor": "^1.0.2", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "inherits": "^2.0.1" + "buffer-xor": "1.0.3", + "cipher-base": "1.0.3", + "create-hash": "1.1.2", + "evp_bytestokey": "1.0.0", + "inherits": "2.0.3" } }, "browserify-cipher": { @@ -15505,9 +15616,9 @@ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "browserify-aes": "1.0.6", + "browserify-des": "1.0.0", + "evp_bytestokey": "1.0.0" } }, "browserify-des": { @@ -15515,9 +15626,9 @@ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1" + "cipher-base": "1.0.3", + "des.js": "1.0.0", + "inherits": "2.0.3" } }, "browserify-rsa": { @@ -15525,8 +15636,8 @@ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "bn.js": "4.11.6", + "randombytes": "2.0.3" } }, "browserify-sign": { @@ -15534,13 +15645,13 @@ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "bn.js": "4.11.6", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.2", + "create-hmac": "1.1.4", + "elliptic": "6.4.0", + "inherits": "2.0.3", + "parse-asn1": "5.1.0" } }, "browserify-zlib": { @@ -15548,7 +15659,7 @@ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", "requires": { - "pako": "~0.2.0" + "pako": "0.2.9" } }, "browserslist": { @@ -15556,8 +15667,8 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" + "caniuse-db": "1.0.30000646", + "electron-to-chromium": "1.3.2" } }, "buffer": { @@ -15565,9 +15676,9 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "1.2.0", + "ieee754": "1.1.8", + "isarray": "1.0.0" } }, "buffer-shims": { @@ -15595,7 +15706,7 @@ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "requires": { - "callsites": "^0.2.0" + "callsites": "0.2.0" } }, "callsites": { @@ -15618,8 +15729,8 @@ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chalk": { @@ -15627,11 +15738,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "chokidar": { @@ -15639,15 +15750,15 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz", "integrity": "sha1-L0RHq16W5Q+z14n9kNTHLg5McMI=", "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "anymatch": "1.3.0", + "async-each": "1.0.1", + "fsevents": "1.1.1", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" } }, "cipher-base": { @@ -15655,7 +15766,7 @@ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz", "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=", "requires": { - "inherits": "^2.0.1" + "inherits": "2.0.3" } }, "circular-json": { @@ -15668,7 +15779,7 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "requires": { - "restore-cursor": "^1.0.1" + "restore-cursor": "1.0.1" } }, "cli-width": { @@ -15681,8 +15792,8 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" } }, @@ -15706,7 +15817,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "requires": { - "graceful-readlink": ">= 1.0.0" + "graceful-readlink": "1.0.1" } }, "commondir": { @@ -15724,9 +15835,9 @@ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "inherits": "2.0.3", + "readable-stream": "2.2.6", + "typedarray": "0.0.6" } }, "console-browserify": { @@ -15734,7 +15845,7 @@ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "requires": { - "date-now": "^0.1.4" + "date-now": "0.1.4" } }, "constants-browserify": { @@ -15767,8 +15878,8 @@ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "bn.js": "4.11.6", + "elliptic": "6.4.0" } }, "create-hash": { @@ -15776,10 +15887,10 @@ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz", "integrity": "sha1-USEAYte7dHn2xlu0GpIgix1hq60=", "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "ripemd160": "^1.0.0", - "sha.js": "^2.3.6" + "cipher-base": "1.0.3", + "inherits": "2.0.3", + "ripemd160": "1.0.1", + "sha.js": "2.4.8" } }, "create-hmac": { @@ -15787,8 +15898,8 @@ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.4.tgz", "integrity": "sha1-0/tLolPriz9W456i+8uK90e9MXA=", "requires": { - "create-hash": "^1.1.0", - "inherits": "^2.0.1" + "create-hash": "1.1.2", + "inherits": "2.0.3" } }, "crypto-browserify": { @@ -15796,16 +15907,16 @@ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz", "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=", "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0" + "browserify-cipher": "1.0.0", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.0", + "create-hash": "1.1.2", + "create-hmac": "1.1.4", + "diffie-hellman": "5.0.2", + "inherits": "2.0.3", + "pbkdf2": "3.0.9", + "public-encrypt": "4.0.0", + "randombytes": "2.0.3" } }, "d": { @@ -15813,7 +15924,7 @@ "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "0.10.15" } }, "damerau-levenshtein": { @@ -15854,8 +15965,8 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "foreach": "2.0.5", + "object-keys": "1.0.11" } }, "del": { @@ -15863,13 +15974,13 @@ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.1" } }, "des.js": { @@ -15877,8 +15988,8 @@ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" } }, "detect-indent": { @@ -15886,7 +15997,7 @@ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "diffie-hellman": { @@ -15894,9 +16005,9 @@ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "bn.js": "4.11.6", + "miller-rabin": "4.0.0", + "randombytes": "2.0.3" } }, "doctrine": { @@ -15904,8 +16015,8 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } }, "domain-browser": { @@ -15923,13 +16034,13 @@ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "bn.js": "4.11.6", + "brorand": "1.1.0", + "hash.js": "1.0.3", + "hmac-drbg": "1.0.0", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" } }, "emoji-regex": { @@ -15947,10 +16058,10 @@ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz", "integrity": "sha1-n0tib1dyRe3PSyrYPYbhf09CHew=", "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.5" + "graceful-fs": "4.1.11", + "memory-fs": "0.4.1", + "object-assign": "4.1.1", + "tapable": "0.2.6" } }, "errno": { @@ -15958,7 +16069,7 @@ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", "requires": { - "prr": "~0.0.0" + "prr": "0.0.0" } }, "error-ex": { @@ -15966,7 +16077,7 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "es-abstract": { @@ -15974,10 +16085,10 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.7.0.tgz", "integrity": "sha1-363ndOAb/Nl/lhgCmMRJyGI/uUw=", "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.0", - "is-callable": "^1.1.3", - "is-regex": "^1.0.3" + "es-to-primitive": "1.1.1", + "function-bind": "1.1.0", + "is-callable": "1.1.3", + "is-regex": "1.0.4" } }, "es-to-primitive": { @@ -15985,9 +16096,9 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" } }, "es5-ext": { @@ -15995,8 +16106,8 @@ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.15.tgz", "integrity": "sha1-wzClk0we4hKEp8CBqG5f2TfJHqY=", "requires": { - "es6-iterator": "2", - "es6-symbol": "~3.1" + "es6-iterator": "2.0.1", + "es6-symbol": "3.1.1" } }, "es6-iterator": { @@ -16004,9 +16115,9 @@ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-symbol": "^3.1" + "d": "1.0.0", + "es5-ext": "0.10.15", + "es6-symbol": "3.1.1" } }, "es6-map": { @@ -16014,12 +16125,12 @@ "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "d": "1.0.0", + "es5-ext": "0.10.15", + "es6-iterator": "2.0.1", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" } }, "es6-set": { @@ -16027,11 +16138,11 @@ "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", + "d": "1.0.0", + "es5-ext": "0.10.15", + "es6-iterator": "2.0.1", "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" + "event-emitter": "0.3.5" } }, "es6-symbol": { @@ -16039,8 +16150,8 @@ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.15" } }, "es6-weak-map": { @@ -16048,10 +16159,10 @@ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.15", + "es6-iterator": "2.0.1", + "es6-symbol": "3.1.1" } }, "escape-string-regexp": { @@ -16064,10 +16175,10 @@ "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.1.0", + "estraverse": "4.2.0" } }, "eslint": { @@ -16075,41 +16186,41 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", "requires": { - "babel-code-frame": "^6.16.0", - "chalk": "^1.1.3", - "concat-stream": "^1.5.2", - "debug": "^2.1.1", - "doctrine": "^2.0.0", - "escope": "^3.6.0", - "espree": "^3.4.0", - "esquery": "^1.0.0", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "glob": "^7.0.3", - "globals": "^9.14.0", - "ignore": "^3.2.0", - "imurmurhash": "^0.1.4", - "inquirer": "^0.12.0", - "is-my-json-valid": "^2.10.0", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.5.1", - "json-stable-stringify": "^1.0.0", - "levn": "^0.3.0", - "lodash": "^4.0.0", - "mkdirp": "^0.5.0", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.1", - "pluralize": "^1.2.1", - "progress": "^1.1.8", - "require-uncached": "^1.0.2", - "shelljs": "^0.7.5", - "strip-bom": "^3.0.0", - "strip-json-comments": "~2.0.1", - "table": "^3.7.8", - "text-table": "~0.2.0", - "user-home": "^2.0.0" + "babel-code-frame": "6.22.0", + "chalk": "1.1.3", + "concat-stream": "1.6.0", + "debug": "2.6.3", + "doctrine": "2.0.0", + "escope": "3.6.0", + "espree": "3.4.1", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "glob": "7.1.1", + "globals": "9.17.0", + "ignore": "3.2.6", + "imurmurhash": "0.1.4", + "inquirer": "0.12.0", + "is-my-json-valid": "2.16.0", + "is-resolvable": "1.0.0", + "js-yaml": "3.8.2", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "1.2.1", + "progress": "1.1.8", + "require-uncached": "1.0.3", + "shelljs": "0.7.7", + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1", + "table": "3.8.3", + "text-table": "0.2.0", + "user-home": "2.0.0" }, "dependencies": { "user-home": { @@ -16117,7 +16228,7 @@ "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } } } @@ -16127,7 +16238,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-14.1.0.tgz", "integrity": "sha1-NV0pAEC7+OAL+LSxn0twy+fCMX8=", "requires": { - "eslint-config-airbnb-base": "^11.1.0" + "eslint-config-airbnb-base": "11.1.2" } }, "eslint-config-airbnb-base": { @@ -16140,9 +16251,9 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", "requires": { - "debug": "^2.2.0", - "object-assign": "^4.0.1", - "resolve": "^1.1.6" + "debug": "2.6.3", + "object-assign": "4.1.1", + "resolve": "1.3.2" } }, "eslint-module-utils": { @@ -16151,7 +16262,7 @@ "integrity": "sha1-pvjCHZATWHWc3DXbrBmCrh7li84=", "requires": { "debug": "2.2.0", - "pkg-dir": "^1.0.0" + "pkg-dir": "1.0.0" }, "dependencies": { "debug": { @@ -16174,16 +16285,16 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", "requires": { - "builtin-modules": "^1.1.1", - "contains-path": "^0.1.0", - "debug": "^2.2.0", + "builtin-modules": "1.1.1", + "contains-path": "0.1.0", + "debug": "2.6.3", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.2.0", - "eslint-module-utils": "^2.0.0", - "has": "^1.0.1", - "lodash.cond": "^4.3.0", - "minimatch": "^3.0.3", - "pkg-up": "^1.0.0" + "eslint-import-resolver-node": "0.2.3", + "eslint-module-utils": "2.0.0", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "minimatch": "3.0.3", + "pkg-up": "1.0.0" }, "dependencies": { "doctrine": { @@ -16191,8 +16302,8 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } } } @@ -16202,12 +16313,12 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-4.0.0.tgz", "integrity": "sha1-d5uw/nsI2lZKQiYkkR3hAGHgSO4=", "requires": { - "aria-query": "^0.3.0", + "aria-query": "0.3.0", "ast-types-flow": "0.0.7", - "damerau-levenshtein": "^1.0.0", - "emoji-regex": "^6.1.0", - "jsx-ast-utils": "^1.0.0", - "object-assign": "^4.0.1" + "damerau-levenshtein": "1.0.4", + "emoji-regex": "6.4.1", + "jsx-ast-utils": "1.4.0", + "object-assign": "4.1.1" } }, "eslint-plugin-react": { @@ -16215,11 +16326,11 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz", "integrity": "sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=", "requires": { - "array.prototype.find": "^2.0.1", - "doctrine": "^1.2.2", - "has": "^1.0.1", - "jsx-ast-utils": "^1.3.4", - "object.assign": "^4.0.4" + "array.prototype.find": "2.0.4", + "doctrine": "1.5.0", + "has": "1.0.1", + "jsx-ast-utils": "1.4.0", + "object.assign": "4.0.4" }, "dependencies": { "doctrine": { @@ -16227,8 +16338,8 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "2.0.2", + "isarray": "1.0.0" } } } @@ -16238,8 +16349,8 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.1.tgz", "integrity": "sha1-KKg6tKrtce2P4PXv5ht2oFwTxNI=", "requires": { - "acorn": "^5.0.1", - "acorn-jsx": "^3.0.0" + "acorn": "5.0.2", + "acorn-jsx": "3.0.1" } }, "esprima": { @@ -16252,7 +16363,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", "requires": { - "estraverse": "^4.0.0" + "estraverse": "4.2.0" } }, "esrecurse": { @@ -16260,8 +16371,8 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", "integrity": "sha1-RxO2U2rffyrE8yfVWed1a/9kgiA=", "requires": { - "estraverse": "~4.1.0", - "object-assign": "^4.0.1" + "estraverse": "4.1.1", + "object-assign": "4.1.1" }, "dependencies": { "estraverse": { @@ -16286,8 +16397,8 @@ "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.15" } }, "eventemitter3": { @@ -16305,7 +16416,7 @@ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz", "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=", "requires": { - "create-hash": "^1.1.1" + "create-hash": "1.1.2" } }, "exit-hook": { @@ -16318,7 +16429,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "expand-range": { @@ -16326,7 +16437,7 @@ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.3" } }, "extend": { @@ -16339,7 +16450,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "fast-diff": { @@ -16357,8 +16468,8 @@ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" } }, "file-entry-cache": { @@ -16366,8 +16477,8 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.2.2", + "object-assign": "4.1.1" } }, "filename-regex": { @@ -16380,11 +16491,11 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.6", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" } }, "find-cache-dir": { @@ -16392,9 +16503,9 @@ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" } }, "find-up": { @@ -16402,8 +16513,8 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "flat-cache": { @@ -16411,10 +16522,10 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" + "circular-json": "0.3.1", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" } }, "for-in": { @@ -16427,7 +16538,7 @@ "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "foreach": { @@ -16451,8 +16562,8 @@ "integrity": "sha1-8Z/Sj0Pur3YWgOUZogPE0LPTGv8=", "optional": true, "requires": { - "nan": "^2.3.0", - "node-pre-gyp": "^0.6.29" + "nan": "2.5.1", + "node-pre-gyp": "0.6.33" }, "dependencies": { "abbrev": { @@ -16484,8 +16595,8 @@ "integrity": "sha1-gORw6VoIR5T+GJkmLFZnxuiN4bM=", "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.0 || ^1.1.13" + "delegates": "1.0.0", + "readable-stream": "2.2.2" } }, "asn1": { @@ -16529,7 +16640,7 @@ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "block-stream": { @@ -16575,11 +16686,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "optional": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "code-point-at": { @@ -16724,10 +16835,10 @@ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz", "integrity": "sha1-YE6Kkv4m/9n2+uMDmdSYThqyKCI=", "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.5.4" } }, "fstream-ignore": { @@ -16736,9 +16847,9 @@ "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", "optional": true, "requires": { - "fstream": "^1.0.0", - "inherits": "2", - "minimatch": "^3.0.0" + "fstream": "1.0.10", + "inherits": "2.0.3", + "minimatch": "3.0.3" } }, "gauge": { @@ -16747,14 +16858,14 @@ "integrity": "sha1-HCOFX5YvF7OtPQ3HRD8wRULt/gk=", "optional": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.0" } }, "generate-function": { @@ -16794,12 +16905,12 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.3", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "graceful-fs": { @@ -16819,10 +16930,10 @@ "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", "optional": true, "requires": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "is-my-json-valid": "^2.12.4", - "pinkie-promise": "^2.0.0" + "chalk": "1.1.3", + "commander": "2.9.0", + "is-my-json-valid": "2.15.0", + "pinkie-promise": "2.0.1" } }, "has-ansi": { @@ -16863,9 +16974,9 @@ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", "optional": true, "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "0.2.0", + "jsprim": "1.3.1", + "sshpk": "1.10.2" } }, "inflight": { @@ -16873,8 +16984,8 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -16902,10 +17013,10 @@ "integrity": "sha1-k27do8o8IR/ZjzstPgjaQ/eykVs=", "optional": true, "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" } }, "is-property": { @@ -17021,15 +17132,15 @@ "integrity": "sha1-ZArFUZj2qSWXLgwWxKwmoDTV7Mk=", "optional": true, "requires": { - "mkdirp": "~0.5.1", - "nopt": "~3.0.6", - "npmlog": "^4.0.1", - "rc": "~1.1.6", - "request": "^2.79.0", - "rimraf": "~2.5.4", - "semver": "~5.3.0", - "tar": "~2.2.1", - "tar-pack": "~3.3.0" + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.0.2", + "rc": "1.1.7", + "request": "2.79.0", + "rimraf": "2.5.4", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.3.0" } }, "nopt": { @@ -17047,10 +17158,10 @@ "integrity": "sha1-0DlQ4OeM4VJ7om0qdZLpNIrD518=", "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.1", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.2", + "console-control-strings": "1.1.0", + "gauge": "2.7.3", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -17075,7 +17186,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "path-is-absolute": { @@ -17121,10 +17232,10 @@ "integrity": "sha1-xepWS7B6/5/TpbMukGwdOmWUD+o=", "optional": true, "requires": { - "deep-extend": "~0.4.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.4.1", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -17141,13 +17252,13 @@ "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=", "optional": true, "requires": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" } }, "request": { @@ -17156,26 +17267,26 @@ "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", "optional": true, "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.11.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~2.0.6", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "qs": "~6.3.0", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "~0.4.1", - "uuid": "^3.0.0" + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.0", + "forever-agent": "0.6.1", + "form-data": "2.1.2", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.14", + "oauth-sign": "0.8.2", + "qs": "6.3.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3", + "uuid": "3.0.1" } }, "rimraf": { @@ -17183,7 +17294,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", "requires": { - "glob": "^7.0.5" + "glob": "7.1.1" } }, "semver": { @@ -17219,15 +17330,15 @@ "integrity": "sha1-1agEziJpVRVjjnmNviMnPeBwpfo=", "optional": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jodid25519": "^1.0.0", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.6", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" }, "dependencies": { "assert-plus": { @@ -17243,9 +17354,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -17264,7 +17375,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -17284,9 +17395,9 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" + "block-stream": "0.0.9", + "fstream": "1.0.10", + "inherits": "2.0.3" } }, "tar-pack": { @@ -17295,14 +17406,14 @@ "integrity": "sha1-MJMYFkGPVa/E0hd1r91nIM7kXa4=", "optional": true, "requires": { - "debug": "~2.2.0", - "fstream": "~1.0.10", - "fstream-ignore": "~1.0.5", - "once": "~1.3.3", - "readable-stream": "~2.1.4", - "rimraf": "~2.5.1", - "tar": "~2.2.1", - "uid-number": "~0.0.6" + "debug": "2.2.0", + "fstream": "1.0.10", + "fstream-ignore": "1.0.5", + "once": "1.3.3", + "readable-stream": "2.1.5", + "rimraf": "2.5.4", + "tar": "2.2.1", + "uid-number": "0.0.6" }, "dependencies": { "once": { @@ -17311,7 +17422,7 @@ "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", "optional": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "readable-stream": { @@ -17320,13 +17431,13 @@ "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", "optional": true, "requires": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" } } } @@ -17337,7 +17448,7 @@ "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", "optional": true, "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" } }, "tunnel-agent": { @@ -17384,7 +17495,7 @@ "integrity": "sha1-QO3egCpx/qHwcNo+YtzaLnrdlq0=", "optional": true, "requires": { - "string-width": "^1.0.1" + "string-width": "1.0.2" } }, "wrappy": { @@ -17415,7 +17526,7 @@ "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "requires": { - "is-property": "^1.0.0" + "is-property": "1.0.2" } }, "get-caller-file": { @@ -17428,12 +17539,12 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.3", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -17441,8 +17552,8 @@ "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" } }, "glob-parent": { @@ -17450,7 +17561,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "globals": { @@ -17463,12 +17574,12 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.1", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "graceful-fs": { @@ -17486,7 +17597,7 @@ "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", "requires": { - "function-bind": "^1.0.2" + "function-bind": "1.1.0" } }, "has-ansi": { @@ -17494,7 +17605,7 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-flag": { @@ -17507,7 +17618,7 @@ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz", "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=", "requires": { - "inherits": "^2.0.1" + "inherits": "2.0.3" } }, "hmac-drbg": { @@ -17515,9 +17626,9 @@ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.0.tgz", "integrity": "sha1-PbRx9FquSplKBogyIXH1G4uRvuU=", "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "hash.js": "1.0.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" } }, "home-or-tmp": { @@ -17525,8 +17636,8 @@ "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "hosted-git-info": { @@ -17564,8 +17675,8 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -17578,19 +17689,19 @@ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", "requires": { - "ansi-escapes": "^1.1.0", - "ansi-regex": "^2.0.0", - "chalk": "^1.0.0", - "cli-cursor": "^1.0.1", - "cli-width": "^2.0.0", - "figures": "^1.3.5", - "lodash": "^4.3.0", - "readline2": "^1.0.1", - "run-async": "^0.1.0", - "rx-lite": "^3.1.2", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "2.1.0", + "figures": "1.7.0", + "lodash": "4.17.4", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" } }, "interpret": { @@ -17603,7 +17714,7 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -17621,7 +17732,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.8.0" } }, "is-buffer": { @@ -17634,7 +17745,7 @@ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-callable": { @@ -17657,7 +17768,7 @@ "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -17675,7 +17786,7 @@ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -17683,7 +17794,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-glob": { @@ -17691,7 +17802,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-my-json-valid": { @@ -17699,10 +17810,10 @@ "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" } }, "is-number": { @@ -17710,7 +17821,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.1.0" } }, "is-path-cwd": { @@ -17723,7 +17834,7 @@ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "1.0.0" } }, "is-path-inside": { @@ -17731,7 +17842,7 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-posix-bracket": { @@ -17754,7 +17865,7 @@ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "requires": { - "has": "^1.0.1" + "has": "1.0.1" } }, "is-resolvable": { @@ -17762,7 +17873,7 @@ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", "requires": { - "tryit": "^1.0.1" + "tryit": "1.0.3" } }, "is-symbol": { @@ -17798,8 +17909,8 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.2.tgz", "integrity": "sha1-AtPiwPa+qyAkjUEsNSIDgn14ZyE=", "requires": { - "argparse": "^1.0.7", - "esprima": "^3.1.1" + "argparse": "1.0.9", + "esprima": "3.1.3" } }, "jsesc": { @@ -17817,7 +17928,7 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json5": { @@ -17840,7 +17951,7 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.0.tgz", "integrity": "sha1-Wv44ho9WvIzHrq7wEAuox1vRJZE=", "requires": { - "object-assign": "^4.1.0" + "object-assign": "4.1.1" } }, "kind-of": { @@ -17848,7 +17959,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz", "integrity": "sha1-R11pil5J/15T0U4+cyQp3Iv0z0c=", "requires": { - "is-buffer": "^1.0.2" + "is-buffer": "1.1.5" } }, "lazy-cache": { @@ -17861,7 +17972,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "levn": { @@ -17869,8 +17980,8 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "load-json-file": { @@ -17878,11 +17989,11 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" }, "dependencies": { "strip-bom": { @@ -17890,7 +18001,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } @@ -17905,10 +18016,10 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1", + "object-assign": "4.1.1" } }, "lodash": { @@ -17931,7 +18042,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "requires": { - "js-tokens": "^3.0.0" + "js-tokens": "3.0.1" } }, "memory-fs": { @@ -17939,8 +18050,8 @@ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "errno": "0.1.4", + "readable-stream": "2.2.6" } }, "micromatch": { @@ -17948,19 +18059,19 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.0", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.1.0", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.3" } }, "miller-rabin": { @@ -17968,8 +18079,8 @@ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "bn.js": "4.11.6", + "brorand": "1.1.0" } }, "minimalistic-assert": { @@ -17987,7 +18098,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", "requires": { - "brace-expansion": "^1.0.0" + "brace-expansion": "1.1.6" } }, "minimist": { @@ -18029,28 +18140,28 @@ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.1.4", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", + "assert": "1.4.1", + "browserify-zlib": "0.1.4", + "buffer": "4.9.1", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.11.0", + "domain-browser": "1.1.7", + "events": "1.1.1", "https-browserify": "0.0.1", - "os-browserify": "^0.2.0", + "os-browserify": "0.2.1", "path-browserify": "0.0.0", - "process": "^0.11.0", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.0.5", - "stream-browserify": "^2.0.1", - "stream-http": "^2.3.1", - "string_decoder": "^0.10.25", - "timers-browserify": "^2.0.2", + "process": "0.11.9", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.2.6", + "stream-browserify": "2.0.1", + "stream-http": "2.7.0", + "string_decoder": "0.10.31", + "timers-browserify": "2.0.2", "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", + "url": "0.11.0", + "util": "0.10.3", "vm-browserify": "0.0.4" } }, @@ -18059,10 +18170,10 @@ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.6.tgz", "integrity": "sha1-SY+kIMlkAfeHQCuiHmAN75+YH/8=", "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.4.1", + "is-builtin-module": "1.0.0", + "semver": "5.3.0", + "validate-npm-package-license": "3.0.1" } }, "normalize-path": { @@ -18070,7 +18181,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.0.1" } }, "number-is-nan": { @@ -18093,9 +18204,9 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.0", - "object-keys": "^1.0.10" + "define-properties": "1.1.2", + "function-bind": "1.1.0", + "object-keys": "1.0.11" } }, "object.omit": { @@ -18103,8 +18214,8 @@ "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" } }, "once": { @@ -18112,7 +18223,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -18125,12 +18236,12 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" }, "dependencies": { "wordwrap": { @@ -18155,7 +18266,7 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { - "lcid": "^1.0.0" + "lcid": "1.0.0" } }, "os-tmpdir": { @@ -18168,9 +18279,9 @@ "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", "requires": { - "graceful-fs": "^4.1.4", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.0" + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "object-assign": "4.1.1" } }, "pako": { @@ -18188,11 +18299,11 @@ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" + "asn1.js": "4.9.1", + "browserify-aes": "1.0.6", + "create-hash": "1.1.2", + "evp_bytestokey": "1.0.0", + "pbkdf2": "3.0.9" } }, "parse-glob": { @@ -18200,10 +18311,10 @@ "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.2", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" } }, "parse-json": { @@ -18211,7 +18322,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.1" } }, "path-browserify": { @@ -18224,7 +18335,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -18247,9 +18358,9 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pbkdf2": { @@ -18257,7 +18368,7 @@ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.9.tgz", "integrity": "sha1-8sSyWmAAWLPDdzwIbDfbvuH/5pM=", "requires": { - "create-hmac": "^1.1.2" + "create-hmac": "1.1.4" } }, "pify": { @@ -18275,7 +18386,7 @@ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -18283,7 +18394,7 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" } }, "pkg-up": { @@ -18291,7 +18402,7 @@ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" } }, "pluralize": { @@ -18339,11 +18450,11 @@ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" + "bn.js": "4.11.6", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.2", + "parse-asn1": "5.1.0", + "randombytes": "2.0.3" } }, "punycode": { @@ -18366,10 +18477,10 @@ "resolved": "https://registry.npmjs.org/quill/-/quill-1.2.3.tgz", "integrity": "sha1-j8Yu1m2Bc+35vB2ft3fddOsGMu4=", "requires": { - "clone": "~2.1.1", - "deep-equal": "~1.0.1", - "eventemitter3": "~2.0.2", - "extend": "~3.0.0", + "clone": "2.1.1", + "deep-equal": "1.0.1", + "eventemitter3": "2.0.3", + "extend": "3.0.0", "parchment": "1.0.8", "quill-delta": "3.5.0" } @@ -18379,8 +18490,8 @@ "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.5.0.tgz", "integrity": "sha1-W2fmhdpgw06r7URJxBbHSquJFXs=", "requires": { - "deep-equal": "^1.0.1", - "extend": "^3.0.0", + "deep-equal": "1.0.1", + "extend": "3.0.0", "fast-diff": "1.1.1" } }, @@ -18389,8 +18500,8 @@ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz", "integrity": "sha1-EQ3Kv/OX6dz/fAeJzMCkmt8exbs=", "requires": { - "is-number": "^2.0.2", - "kind-of": "^3.0.2" + "is-number": "2.1.0", + "kind-of": "3.1.0" } }, "randombytes": { @@ -18408,9 +18519,9 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.3.6", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -18418,8 +18529,8 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" } }, "readable-stream": { @@ -18427,13 +18538,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", "integrity": "sha1-i0Ou125xSDk40SqNRsbPGgCx+BY=", "requires": { - "buffer-shims": "^1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" } }, "readdirp": { @@ -18441,10 +18552,10 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "minimatch": "3.0.3", + "readable-stream": "2.2.6", + "set-immediate-shim": "1.0.1" } }, "readline2": { @@ -18452,8 +18563,8 @@ "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", "mute-stream": "0.0.5" } }, @@ -18462,7 +18573,7 @@ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "requires": { - "resolve": "^1.1.6" + "resolve": "1.3.2" } }, "regenerate": { @@ -18480,9 +18591,9 @@ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.8.tgz", "integrity": "sha1-D4i7K8A5Mt23trcxLmgHjwECbWw=", "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" + "babel-runtime": "6.23.0", + "babel-types": "6.23.0", + "private": "0.1.7" } }, "regex-cache": { @@ -18490,8 +18601,8 @@ "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", "requires": { - "is-equal-shallow": "^0.1.3", - "is-primitive": "^2.0.0" + "is-equal-shallow": "0.1.3", + "is-primitive": "2.0.0" } }, "regexpu-core": { @@ -18499,9 +18610,9 @@ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.3.2", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } }, "regjsgen": { @@ -18514,7 +18625,7 @@ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { @@ -18544,7 +18655,7 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "require-directory": { @@ -18562,8 +18673,8 @@ "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" } }, "resolve": { @@ -18571,7 +18682,7 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.2.tgz", "integrity": "sha1-HwRCyeDLuBNuh7kwX5MvRsfygjU=", "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.5" } }, "resolve-from": { @@ -18584,8 +18695,8 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" + "exit-hook": "1.1.1", + "onetime": "1.1.0" } }, "right-align": { @@ -18593,7 +18704,7 @@ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -18601,7 +18712,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", "requires": { - "glob": "^7.0.5" + "glob": "7.1.1" } }, "ripemd160": { @@ -18614,7 +18725,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", "requires": { - "once": "^1.3.0" + "once": "1.4.0" } }, "rx-lite": { @@ -18647,7 +18758,7 @@ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", "requires": { - "inherits": "^2.0.1" + "inherits": "2.0.3" } }, "shelljs": { @@ -18655,9 +18766,9 @@ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", "integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E=", "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "glob": "7.1.1", + "interpret": "1.0.2", + "rechoir": "0.6.2" } }, "slash": { @@ -18685,7 +18796,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.14.tgz", "integrity": "sha1-nURjdyWYuGJxtPUj9sH04Cp9au8=", "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.6" } }, "spdx-correct": { @@ -18693,7 +18804,7 @@ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", "requires": { - "spdx-license-ids": "^1.0.2" + "spdx-license-ids": "1.2.2" } }, "spdx-expression-parse": { @@ -18716,8 +18827,8 @@ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "inherits": "2.0.3", + "readable-stream": "2.2.6" } }, "stream-http": { @@ -18725,11 +18836,11 @@ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.0.tgz", "integrity": "sha1-zsH047SUvEqBtFGAiXD4sgtO1fY=", "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.2.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.2.6", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" } }, "string-width": { @@ -18737,9 +18848,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -18752,7 +18863,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -18775,12 +18886,12 @@ "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "requires": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", + "ajv": "4.11.5", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.4", "slice-ansi": "0.0.4", - "string-width": "^2.0.0" + "string-width": "2.0.0" }, "dependencies": { "is-fullwidth-code-point": { @@ -18793,8 +18904,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^3.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "3.0.1" } } } @@ -18819,7 +18930,7 @@ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz", "integrity": "sha1-q0iDz1l9zVCvIRNJoA+8pWrIa4Y=", "requires": { - "setimmediate": "^1.0.4" + "setimmediate": "1.0.5" } }, "to-arraybuffer": { @@ -18852,7 +18963,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "typedarray": { @@ -18865,9 +18976,9 @@ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.20.tgz", "integrity": "sha1-vocQD7wY3jh27WBunSS0VoMRzs8=", "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.6", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "uglify-to-browserify": { @@ -18922,7 +19033,7 @@ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.0.12.tgz", "integrity": "sha1-cyNdn3F2+OiDP7KGeVRF95ONhOU=", "requires": { - "user-home": "^1.1.1" + "user-home": "1.1.1" } }, "validate-npm-package-license": { @@ -18930,8 +19041,8 @@ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", "requires": { - "spdx-correct": "~1.0.0", - "spdx-expression-parse": "~1.0.0" + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" } }, "vm-browserify": { @@ -18947,9 +19058,9 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.3.1.tgz", "integrity": "sha1-fYaTkHsozmAT5/NhCqKhrPB9rYc=", "requires": { - "async": "^2.1.2", - "chokidar": "^1.4.3", - "graceful-fs": "^4.1.2" + "async": "2.3.0", + "chokidar": "1.6.1", + "graceful-fs": "4.1.11" } }, "webpack": { @@ -18957,26 +19068,26 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.3.3.tgz", "integrity": "sha1-7swIPBj7e/lY6k9AtXpmQMWgzHg=", "requires": { - "acorn": "^4.0.4", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^4.7.0", - "ajv-keywords": "^1.1.1", - "async": "^2.1.2", - "enhanced-resolve": "^3.0.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "loader-runner": "^2.3.0", - "loader-utils": "^0.2.16", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^3.1.0", - "tapable": "~0.2.5", - "uglify-js": "^2.8.5", - "watchpack": "^1.3.1", - "webpack-sources": "^0.2.3", - "yargs": "^6.0.0" + "acorn": "4.0.11", + "acorn-dynamic-import": "2.0.2", + "ajv": "4.11.5", + "ajv-keywords": "1.5.1", + "async": "2.3.0", + "enhanced-resolve": "3.1.0", + "interpret": "1.0.2", + "json-loader": "0.5.4", + "loader-runner": "2.3.0", + "loader-utils": "0.2.17", + "memory-fs": "0.4.1", + "mkdirp": "0.5.1", + "node-libs-browser": "2.0.0", + "source-map": "0.5.6", + "supports-color": "3.2.3", + "tapable": "0.2.6", + "uglify-js": "2.8.20", + "watchpack": "1.3.1", + "webpack-sources": "0.2.3", + "yargs": "6.6.0" }, "dependencies": { "acorn": { @@ -18994,9 +19105,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" } }, "supports-color": { @@ -19004,7 +19115,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } }, "yargs": { @@ -19012,19 +19123,19 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^4.2.0" + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "4.2.1" } } } @@ -19034,8 +19145,8 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=", "requires": { - "source-list-map": "^1.1.1", - "source-map": "~0.5.3" + "source-list-map": "1.1.1", + "source-map": "0.5.6" } }, "which-module": { @@ -19058,8 +19169,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" } }, "wrappy": { @@ -19072,7 +19183,7 @@ "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.1" } }, "xtend": { @@ -19090,9 +19201,9 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } }, @@ -19101,7 +19212,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "requires": { - "camelcase": "^3.0.0" + "camelcase": "3.0.0" }, "dependencies": { "camelcase": { @@ -19119,7 +19230,7 @@ "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==", "dev": true, "requires": { - "performance-now": "^2.1.0" + "performance-now": "2.1.0" } }, "railroad-diagrams": { @@ -19140,7 +19251,7 @@ "dev": true, "requires": { "discontinuous-range": "1.0.0", - "ret": "~0.1.10" + "ret": "0.1.15" } }, "random-bytes": { @@ -19153,8 +19264,8 @@ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "kind-of": { @@ -19162,7 +19273,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -19172,7 +19283,7 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", "requires": { - "safe-buffer": "^5.1.0" + "safe-buffer": "5.1.2" } }, "randomfill": { @@ -19180,8 +19291,8 @@ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "randombytes": "2.0.6", + "safe-buffer": "5.1.2" } }, "range-parser": { @@ -19212,10 +19323,10 @@ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -19230,11 +19341,11 @@ "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz", "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=", "requires": { - "create-react-class": "^15.6.0", - "fbjs": "^0.8.9", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.0", - "prop-types": "^15.5.10" + "create-react-class": "15.6.3", + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "prop-types": "15.6.1" } }, "react-addons-update": { @@ -19247,16 +19358,16 @@ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.31.5.tgz", "integrity": "sha512-xgDihgX4QvYHmHzL87faDBMDnGfYyqcrqV0TEbWY+JizePOG1vfb8M3xJN+6MJ3kUYqDtQSZ7v/Q6Y5YDrkMdA==", "requires": { - "babel-runtime": "^6.11.6", - "classnames": "^2.2.5", - "dom-helpers": "^3.2.0", - "invariant": "^2.2.1", - "keycode": "^2.1.2", - "prop-types": "^15.5.10", - "prop-types-extra": "^1.0.1", - "react-overlays": "^0.7.4", - "uncontrollable": "^4.1.0", - "warning": "^3.0.0" + "babel-runtime": "6.26.0", + "classnames": "2.2.5", + "dom-helpers": "3.3.1", + "invariant": "2.2.4", + "keycode": "2.2.0", + "prop-types": "15.6.1", + "prop-types-extra": "1.0.1", + "react-overlays": "0.7.4", + "uncontrollable": "4.1.0", + "warning": "3.0.0" } }, "react-bootstrap-autosuggest": { @@ -19264,11 +19375,11 @@ "resolved": "https://registry.npmjs.org/react-bootstrap-autosuggest/-/react-bootstrap-autosuggest-0.5.0.tgz", "integrity": "sha1-DIxiyiHY0UzV9O61mXFSSEdzxDk=", "requires": { - "classnames": "^2.2.5", - "fbjs": "^0.8.12", - "keycode": "^2.1.8", - "prop-types": "^15.5.8", - "warning": "^3.0.0" + "classnames": "2.2.5", + "fbjs": "0.8.16", + "keycode": "2.2.0", + "prop-types": "15.6.1", + "warning": "3.0.0" } }, "react-bootstrap-table": { @@ -19277,8 +19388,8 @@ "integrity": "sha1-wkKKX+zpgj8La97ffxmGMKxVoWQ=", "requires": { "@allenfang/react-toastr": "2.8.2", - "classnames": "^2.1.2", - "react-modal": "^1.4.0" + "classnames": "2.2.5", + "react-modal": "1.9.7" } }, "react-day-picker": { @@ -19286,8 +19397,8 @@ "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-6.2.1.tgz", "integrity": "sha1-/CK4Cdt5I4bRRo0/RtclrZHgTbk=", "requires": { - "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "object-assign": "4.1.1", + "prop-types": "15.6.1" } }, "react-deep-force-update": { @@ -19305,12 +19416,12 @@ "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", "integrity": "sha1-f6JWds+CfViokSk+PBq1naACVFo=", "requires": { - "disposables": "^1.0.1", - "dnd-core": "^2.6.0", - "hoist-non-react-statics": "^2.1.0", - "invariant": "^2.1.0", - "lodash": "^4.2.0", - "prop-types": "^15.5.10" + "disposables": "1.0.2", + "dnd-core": "2.6.0", + "hoist-non-react-statics": "2.5.0", + "invariant": "2.2.4", + "lodash": "4.17.10", + "prop-types": "15.6.1" } }, "react-dnd-html5-backend": { @@ -19318,7 +19429,7 @@ "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", "integrity": "sha1-WQzRzKeEQbsnTt1XH+9MCxbdz44=", "requires": { - "lodash": "^4.2.0" + "lodash": "4.17.10" } }, "react-dom": { @@ -19326,10 +19437,10 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz", "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=", "requires": { - "fbjs": "^0.8.9", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.0", - "prop-types": "^15.5.10" + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "prop-types": "15.6.1" } }, "react-dom-factories": { @@ -19361,8 +19472,8 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.11.6.tgz", "integrity": "sha1-bbcH/vLUnEm/o8tk79tDa1GLgiI=", "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.9.5" + "core-js": "2.5.5", + "regenerator-runtime": "0.9.6" } }, "create-react-class": { @@ -19370,9 +19481,9 @@ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.5.3.tgz", "integrity": "sha1-+w98rnkznpoXnhlO9Gbvo5I4IP4=", "requires": { - "fbjs": "^0.8.9", - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "invariant": { @@ -19380,7 +19491,7 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.1.tgz", "integrity": "sha1-sJcBBUdmjH4zcCjr6Bbr42yKjVQ=", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "lodash": { @@ -19393,7 +19504,7 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.8.tgz", "integrity": "sha1-a3suFBCDvjjIWVqlH8VXdccZk5Q=", "requires": { - "fbjs": "^0.8.9" + "fbjs": "0.8.16" } }, "regenerator-runtime": { @@ -19408,24 +19519,30 @@ "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-3.1.3.tgz", "integrity": "sha512-d7nZf78irxoGN5PY4zd6CSgZiroOhvIWzRast3qwTn4sSnBwlt08kV8WMQ9mitmxEdlCTwZt+5ClrRSjxWguMQ==", "requires": { - "global": "^4.3.0", - "react-deep-force-update": "^2.1.1", - "react-proxy": "^3.0.0-alpha.0", - "redbox-react": "^1.3.6", - "source-map": "^0.6.1" + "global": "4.3.2", + "react-deep-force-update": "2.1.1", + "react-proxy": "3.0.0-alpha.1", + "redbox-react": "1.6.0", + "source-map": "0.6.1" } }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "dev": true + }, "react-modal": { "version": "1.9.7", "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-1.9.7.tgz", "integrity": "sha512-oZNqI0ZnPD7NnfObrCMz2hxHTAw5oEuhZJ+gnyFNIQB2rR8h1YbLQTfhms1mtSJigb0J23OOWElHjXYYaKO+wg==", "requires": { - "create-react-class": "^15.5.2", - "element-class": "^0.2.0", + "create-react-class": "15.6.3", + "element-class": "0.2.2", "exenv": "1.2.0", - "lodash.assign": "^4.2.0", - "prop-types": "^15.5.7", - "react-dom-factories": "^1.0.0" + "lodash.assign": "4.2.0", + "prop-types": "15.6.1", + "react-dom-factories": "1.0.2" } }, "react-overlays": { @@ -19433,11 +19550,11 @@ "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.7.4.tgz", "integrity": "sha512-7vsooMx3siLAuEfTs8FYeP/lAORWWFXTO8PON3KgX0Htq1Oa+po6ioSjGyO0/GO5CVSMNhpWt6V2opeexHgBuQ==", "requires": { - "classnames": "^2.2.5", - "dom-helpers": "^3.2.1", - "prop-types": "^15.5.10", - "prop-types-extra": "^1.0.1", - "warning": "^3.0.0" + "classnames": "2.2.5", + "dom-helpers": "3.3.1", + "prop-types": "15.6.1", + "prop-types-extra": "1.0.1", + "warning": "3.0.0" } }, "react-prop-types-element-of-type": { @@ -19450,7 +19567,7 @@ "resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-3.0.0-alpha.1.tgz", "integrity": "sha1-RABCa8+oDKpnJMd1VpUxUgn6Swc=", "requires": { - "lodash": "^4.6.1" + "lodash": "4.17.10" } }, "react-quill": { @@ -19458,13 +19575,13 @@ "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-1.2.7.tgz", "integrity": "sha1-PnT627MNzpFXM60jD5JBWJRDfBY=", "requires": { - "@types/quill": "*", - "@types/react": "*", - "create-react-class": "^15.6.0", - "lodash": "^4.17.4", - "prop-types": "^15.5.10", - "quill": "^1.2.6", - "react-dom-factories": "^1.0.0" + "@types/quill": "1.3.6", + "@types/react": "16.3.13", + "create-react-class": "15.6.3", + "lodash": "4.17.10", + "prop-types": "15.6.1", + "quill": "1.3.6", + "react-dom-factories": "1.0.2" } }, "react-redux": { @@ -19472,12 +19589,12 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz", "integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==", "requires": { - "hoist-non-react-statics": "^2.5.0", - "invariant": "^2.0.0", - "lodash": "^4.17.5", - "lodash-es": "^4.17.5", - "loose-envify": "^1.1.0", - "prop-types": "^15.6.0" + "hoist-non-react-statics": "2.5.0", + "invariant": "2.2.4", + "lodash": "4.17.10", + "lodash-es": "4.17.10", + "loose-envify": "1.3.1", + "prop-types": "15.6.1" } }, "react-router": { @@ -19485,13 +19602,13 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz", "integrity": "sha512-DY6pjwRhdARE4TDw7XjxjZsbx9lKmIcyZoZ+SDO7SBJ1KUeWNxT22Kara2AC7u6/c2SYEHlEDLnzBCcNhLE8Vg==", "requires": { - "history": "^4.7.2", - "hoist-non-react-statics": "^2.3.0", - "invariant": "^2.2.2", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.5.4", - "warning": "^3.0.0" + "history": "4.7.2", + "hoist-non-react-statics": "2.5.0", + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "path-to-regexp": "1.7.0", + "prop-types": "15.6.1", + "warning": "3.0.0" }, "dependencies": { "isarray": { @@ -19514,12 +19631,12 @@ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.2.2.tgz", "integrity": "sha512-cHMFC1ZoLDfEaMFoKTjN7fry/oczMgRt5BKfMAkTu5zEuJvUiPp1J8d0eXSVTnBh6pxlbdqDhozunOOLtmKfPA==", "requires": { - "history": "^4.7.2", - "invariant": "^2.2.2", - "loose-envify": "^1.3.1", - "prop-types": "^15.5.4", - "react-router": "^4.2.0", - "warning": "^3.0.0" + "history": "4.7.2", + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "prop-types": "15.6.1", + "react-router": "4.2.0", + "warning": "3.0.0" } }, "react-router-redux": { @@ -19527,9 +19644,9 @@ "resolved": "https://registry.npmjs.org/react-router-redux/-/react-router-redux-5.0.0-alpha.9.tgz", "integrity": "sha512-euSgNIANnRXr4GydIuwA7RZCefrLQzIw5WdXspS8NPYbV+FxrKSS9MKG7U9vb6vsKHONnA4VxrVNWfnMUnUQAw==", "requires": { - "history": "^4.7.2", - "prop-types": "^15.6.0", - "react-router": "^4.2.0" + "history": "4.7.2", + "prop-types": "15.6.1", + "react-router": "4.2.0" } }, "react-s-alert": { @@ -19537,7 +19654,7 @@ "resolved": "https://registry.npmjs.org/react-s-alert/-/react-s-alert-1.3.2.tgz", "integrity": "sha512-6Gz/s5Jw3LbuFR6liD3A4f7w0cDASo1Zas+HaUtUCEm6cVQhgHsUPOBMR9bP8mfiH6KWfRGhiX3t38FbFYrVOg==", "requires": { - "babel-runtime": "^6.23.0" + "babel-runtime": "6.26.0" } }, "react-test-renderer": { @@ -19546,8 +19663,8 @@ "integrity": "sha1-0DM0NPwsQ4CSaWyncNpe1IA376g=", "dev": true, "requires": { - "fbjs": "^0.8.9", - "object-assign": "^4.1.0" + "fbjs": "0.8.16", + "object-assign": "4.1.1" } }, "read-only-stream": { @@ -19555,7 +19672,7 @@ "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "2.2.7" } }, "read-pkg": { @@ -19563,9 +19680,9 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" }, "dependencies": { "path-type": { @@ -19573,7 +19690,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "requires": { - "pify": "^2.0.0" + "pify": "2.3.0" } }, "pify": { @@ -19588,8 +19705,8 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "find-up": "2.1.0", + "read-pkg": "2.0.0" } }, "readable-stream": { @@ -19597,13 +19714,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", "requires": { - "buffer-shims": "~1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~1.0.0", - "util-deprecate": "~1.0.1" + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "readdirp": { @@ -19611,10 +19728,10 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.2.7", + "set-immediate-shim": "1.0.1" } }, "rechoir": { @@ -19622,7 +19739,7 @@ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "requires": { - "resolve": "^1.1.6" + "resolve": "1.7.1" } }, "recompose": { @@ -19630,10 +19747,10 @@ "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.23.5.tgz", "integrity": "sha1-cqyCYSRr7DeCNdGHRn0CpyHosd4=", "requires": { - "change-emitter": "^0.1.2", - "fbjs": "^0.8.1", - "hoist-non-react-statics": "^1.0.0", - "symbol-observable": "^1.0.4" + "change-emitter": "0.1.6", + "fbjs": "0.8.16", + "hoist-non-react-statics": "1.2.0", + "symbol-observable": "1.2.0" }, "dependencies": { "hoist-non-react-statics": { @@ -19648,10 +19765,10 @@ "resolved": "https://registry.npmjs.org/redbox-react/-/redbox-react-1.6.0.tgz", "integrity": "sha512-mLjM5eYR41yOp5YKHpd3syFeGq6B4Wj5vZr64nbLvTZW5ZLff4LYk7VE4ITpVxkZpCY6OZuqh0HiP3A3uEaCpg==", "requires": { - "error-stack-parser": "^1.3.6", - "object-assign": "^4.0.1", - "prop-types": "^15.5.4", - "sourcemapped-stacktrace": "^1.1.6" + "error-stack-parser": "1.3.6", + "object-assign": "4.1.1", + "prop-types": "15.6.1", + "sourcemapped-stacktrace": "1.1.8" } }, "redent": { @@ -19659,8 +19776,8 @@ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, "reduce-css-calc": { @@ -19668,9 +19785,9 @@ "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "requires": { - "balanced-match": "^0.4.2", - "math-expression-evaluator": "^1.2.14", - "reduce-function-call": "^1.0.1" + "balanced-match": "0.4.2", + "math-expression-evaluator": "1.2.17", + "reduce-function-call": "1.0.2" }, "dependencies": { "balanced-match": { @@ -19685,7 +19802,7 @@ "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", "requires": { - "balanced-match": "^0.4.2" + "balanced-match": "0.4.2" }, "dependencies": { "balanced-match": { @@ -19700,10 +19817,10 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", "requires": { - "lodash": "^4.2.1", - "lodash-es": "^4.2.1", - "loose-envify": "^1.1.0", - "symbol-observable": "^1.0.3" + "lodash": "4.17.10", + "lodash-es": "4.17.10", + "loose-envify": "1.3.1", + "symbol-observable": "1.2.0" } }, "redux-form": { @@ -19711,14 +19828,14 @@ "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-6.8.0.tgz", "integrity": "sha512-rISN+EERGB8nAS/LDnOSQaTf0f+QreXEq+7pRVvBFzmH5vIsYRwVpBtYA8UsibGzO+0BL1bl5L5bxdrNwxI+sA==", "requires": { - "deep-equal": "^1.0.1", - "es6-error": "^4.0.0", - "hoist-non-react-statics": "^1.2.0", - "invariant": "^2.2.2", - "is-promise": "^2.1.0", - "lodash": "^4.17.3", - "lodash-es": "^4.17.3", - "prop-types": "^15.5.9" + "deep-equal": "1.0.1", + "es6-error": "4.1.1", + "hoist-non-react-statics": "1.2.0", + "invariant": "2.2.4", + "is-promise": "2.1.0", + "lodash": "4.17.10", + "lodash-es": "4.17.10", + "prop-types": "15.6.1" }, "dependencies": { "hoist-non-react-statics": { @@ -19753,9 +19870,9 @@ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "private": "0.1.8" } }, "regex-cache": { @@ -19763,7 +19880,7 @@ "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "regex-not": { @@ -19771,8 +19888,8 @@ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "regexp-clone": { @@ -19791,9 +19908,9 @@ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.3.3", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } }, "registry-auth-token": { @@ -19801,8 +19918,8 @@ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "1.2.7", + "safe-buffer": "5.1.2" } }, "registry-url": { @@ -19810,7 +19927,7 @@ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "requires": { - "rc": "^1.0.1" + "rc": "1.2.7" } }, "regjsgen": { @@ -19823,7 +19940,7 @@ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { @@ -19848,11 +19965,11 @@ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", "requires": { - "css-select": "^1.1.0", - "dom-converter": "~0.1", - "htmlparser2": "~3.3.0", - "strip-ansi": "^3.0.0", - "utila": "~0.3" + "css-select": "1.2.0", + "dom-converter": "0.1.4", + "htmlparser2": "3.3.0", + "strip-ansi": "3.0.1", + "utila": "0.3.3" }, "dependencies": { "domhandler": { @@ -19860,7 +19977,7 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", "requires": { - "domelementtype": "1" + "domelementtype": "1.3.0" } }, "domutils": { @@ -19868,7 +19985,7 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", "requires": { - "domelementtype": "1" + "domelementtype": "1.3.0" } }, "htmlparser2": { @@ -19876,10 +19993,10 @@ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "requires": { - "domelementtype": "1", - "domhandler": "2.1", - "domutils": "1.1", - "readable-stream": "1.0" + "domelementtype": "1.3.0", + "domhandler": "2.1.0", + "domutils": "1.1.6", + "readable-stream": "1.0.34" } }, "isarray": { @@ -19892,10 +20009,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -19925,7 +20042,7 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "replace-ext": { @@ -19938,28 +20055,28 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "stringstream": "~0.0.5", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" }, "dependencies": { "qs": { @@ -19974,10 +20091,10 @@ "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", "requires": { - "bluebird": "^3.5.0", + "bluebird": "3.5.1", "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.4" } }, "request-promise-core": { @@ -19985,7 +20102,7 @@ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "requires": { - "lodash": "^4.13.1" + "lodash": "4.17.10" } }, "request-promise-native": { @@ -19995,8 +20112,8 @@ "dev": true, "requires": { "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.4" } }, "require-directory": { @@ -20020,8 +20137,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" }, "dependencies": { "resolve-from": { @@ -20037,8 +20154,8 @@ "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", "requires": { - "resolve-from": "^2.0.0", - "semver": "^5.1.0" + "resolve-from": "2.0.0", + "semver": "5.5.0" } }, "requires-port": { @@ -20056,7 +20173,7 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.5" } }, "resolve-cwd": { @@ -20064,7 +20181,7 @@ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "3.0.0" }, "dependencies": { "resolve-from": { @@ -20079,8 +20196,8 @@ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" } }, "resolve-from": { @@ -20104,8 +20221,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "onetime": "2.0.1", + "signal-exit": "3.0.2" } }, "ret": { @@ -20123,7 +20240,7 @@ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -20131,7 +20248,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "ripemd160": { @@ -20139,8 +20256,8 @@ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "hash-base": "3.0.4", + "inherits": "2.0.3" } }, "rst-selector-parser": { @@ -20149,8 +20266,8 @@ "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", "dev": true, "requires": { - "lodash.flattendeep": "^4.4.0", - "nearley": "^2.7.10" + "lodash.flattendeep": "4.4.0", + "nearley": "2.13.0" } }, "run-async": { @@ -20159,7 +20276,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "^2.1.0" + "is-promise": "2.1.0" } }, "run-queue": { @@ -20167,7 +20284,7 @@ "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "requires": { - "aproba": "^1.1.1" + "aproba": "1.2.0" } }, "rx-lite": { @@ -20182,7 +20299,7 @@ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", "dev": true, "requires": { - "rx-lite": "*" + "rx-lite": "4.0.8" } }, "safe-buffer": { @@ -20195,7 +20312,7 @@ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { @@ -20215,16 +20332,16 @@ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.18.2.tgz", "integrity": "sha512-52ThA+Z7h6BnvpSVbURwChl10XZrps5q7ytjTwWcIe9bmJwnVP6cpEVK2NvDOUhGupoqAvNbUz3cpnJDp4+/pg==", "requires": { - "chalk": "^2.3.0", - "htmlparser2": "^3.9.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.0", - "postcss": "^6.0.14", - "srcset": "^1.0.0", - "xtend": "^4.0.0" + "chalk": "2.4.1", + "htmlparser2": "3.9.2", + "lodash.clonedeep": "4.5.0", + "lodash.escaperegexp": "4.1.2", + "lodash.isplainobject": "4.0.6", + "lodash.isstring": "4.0.1", + "lodash.mergewith": "4.6.1", + "postcss": "6.0.22", + "srcset": "1.0.0", + "xtend": "4.0.1" } }, "sax": { @@ -20238,7 +20355,7 @@ "integrity": "sha512-Nc5DXc5A+m3rUDtkS+vHlBWKT7mCKjJPyia7f8YMW773hsXVv2wEHQZGE0zs4+5PLwz9U5Sbl/94Cnd9vHV7Bg==", "dev": true, "requires": { - "xmlchars": "^1.3.1" + "xmlchars": "1.3.1" } }, "schema-utils": { @@ -20246,7 +20363,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", "requires": { - "ajv": "^5.0.0" + "ajv": "5.5.2" } }, "scriptjs": { @@ -20264,8 +20381,8 @@ "resolved": "https://registry.npmjs.org/select2/-/select2-4.0.5.tgz", "integrity": "sha1-eqxQaSVhmFs007guxV4ib4lg1Ao=", "requires": { - "almond": "~0.3.1", - "jquery-mousewheel": "~3.1.13" + "almond": "0.3.3", + "jquery-mousewheel": "3.1.13" } }, "selfsigned": { @@ -20286,7 +20403,7 @@ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "requires": { - "semver": "^5.0.3" + "semver": "5.5.0" } }, "send": { @@ -20295,18 +20412,18 @@ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", + "http-errors": "1.6.3", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" }, "dependencies": { "statuses": { @@ -20321,13 +20438,13 @@ "resolved": "https://registry.npmjs.org/sendgrid/-/sendgrid-5.2.3.tgz", "integrity": "sha512-FD7oR9TbJFUew1p0Vw9JX0wBetDyq634LzylSXz4n9+hwaf+6a9dNloZl8CcjpsX4NuEc3HJanTN4GjDwNyi4A==", "requires": { - "async.ensureasync": "^0.5.2", - "async.queue": "^0.5.2", - "bottleneck": "^1.12.0", - "debug": "^2.2.0", - "lodash.chunk": "^4.2.0", - "mailparser": "^0.6.1", - "sendgrid-rest": "^2.3.0" + "async.ensureasync": "0.5.2", + "async.queue": "0.5.2", + "bottleneck": "1.16.0", + "debug": "2.6.9", + "lodash.chunk": "4.2.0", + "mailparser": "0.6.2", + "sendgrid-rest": "2.4.0" } }, "sendgrid-rest": { @@ -20350,13 +20467,13 @@ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.5", "batch": "0.6.1", "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "escape-html": "1.0.3", + "http-errors": "1.6.3", + "mime-types": "2.1.18", + "parseurl": "1.3.2" } }, "serve-static": { @@ -20364,9 +20481,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.16.2" } }, @@ -20385,10 +20502,10 @@ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -20396,7 +20513,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -20416,8 +20533,8 @@ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.2" } }, "shasum": { @@ -20425,8 +20542,8 @@ "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", "requires": { - "json-stable-stringify": "~0.0.0", - "sha.js": "~2.4.4" + "json-stable-stringify": "0.0.1", + "sha.js": "2.4.11" } }, "shebang-command": { @@ -20434,7 +20551,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -20447,10 +20564,10 @@ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", "requires": { - "array-filter": "~0.0.0", - "array-map": "~0.0.0", - "array-reduce": "~0.0.0", - "jsonify": "~0.0.0" + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "0.0.0" } }, "shelljs": { @@ -20458,9 +20575,9 @@ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "glob": "7.1.2", + "interpret": "1.1.0", + "rechoir": "0.6.2" } }, "shx": { @@ -20468,9 +20585,9 @@ "resolved": "https://registry.npmjs.org/shx/-/shx-0.2.2.tgz", "integrity": "sha1-CjBNAgsO3xMGrYFXDoDwNG31ijk=", "requires": { - "es6-object-assign": "^1.0.3", - "minimist": "^1.2.0", - "shelljs": "^0.7.3" + "es6-object-assign": "1.1.0", + "minimist": "1.2.0", + "shelljs": "0.7.8" }, "dependencies": { "minimist": { @@ -20496,13 +20613,13 @@ "integrity": "sha512-MatciKXyM5pXMSoqd593MqTsItJNCkSSl53HJYeKR5wfsDdp2yljjUQJLfVwAWLoBNfx1HThteqygGQ0ZEpXpQ==", "dev": true, "requires": { - "@sinonjs/formatio": "^2.0.0", - "diff": "^3.5.0", - "lodash.get": "^4.4.2", - "lolex": "^2.4.2", - "nise": "^1.3.3", - "supports-color": "^5.4.0", - "type-detect": "^4.0.8" + "@sinonjs/formatio": "2.0.0", + "diff": "3.5.0", + "lodash.get": "4.4.2", + "lolex": "2.7.0", + "nise": "1.4.2", + "supports-color": "5.4.0", + "type-detect": "4.0.8" }, "dependencies": { "diff": { @@ -20536,7 +20653,7 @@ "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0" + "is-fullwidth-code-point": "2.0.0" } }, "sliced": { @@ -20554,10 +20671,10 @@ "resolved": "https://registry.npmjs.org/slimscroll/-/slimscroll-0.9.1.tgz", "integrity": "sha1-9nXNxgHYCtog8WAE0ifRVv0Rh7I=", "requires": { - "browserify": ">=3.46.0", - "classie": ">=0.0.1", - "domhelper": "~0.9.0", - "util-extend": "^1.0.1" + "browserify": "16.2.2", + "classie": "1.0.0", + "domhelper": "0.9.1", + "util-extend": "1.0.3" } }, "snapdragon": { @@ -20565,14 +20682,14 @@ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "3.1.0" }, "dependencies": { "define-property": { @@ -20580,7 +20697,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -20588,7 +20705,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "source-map": { @@ -20603,9 +20720,9 @@ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -20613,7 +20730,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -20621,7 +20738,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -20629,7 +20746,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -20637,9 +20754,9 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -20649,7 +20766,7 @@ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -20657,7 +20774,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -20667,7 +20784,7 @@ "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "requires": { - "hoek": "4.x.x" + "hoek": "4.2.1" } }, "socket.io": { @@ -20675,12 +20792,12 @@ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.0.tgz", "integrity": "sha512-KS+3CNWWNtLbVN5j0/B+1hjxRzey+oTK6ejpAOoxMZis6aXeB8cUtfuvjHl97tuZx+t/qD/VyqFMjuzu2Js6uQ==", "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", + "debug": "3.1.0", + "engine.io": "3.2.0", + "has-binary2": "1.0.2", + "socket.io-adapter": "1.1.1", "socket.io-client": "2.1.0", - "socket.io-parser": "~3.2.0" + "socket.io-parser": "3.2.0" }, "dependencies": { "debug": { @@ -20707,15 +20824,15 @@ "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", + "debug": "3.1.0", + "engine.io-client": "3.2.1", + "has-binary2": "1.0.2", "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", + "socket.io-parser": "3.2.0", "to-array": "0.1.4" }, "dependencies": { @@ -20735,7 +20852,7 @@ "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "requires": { "component-emitter": "1.2.1", - "debug": "~3.1.0", + "debug": "3.1.0", "isarray": "2.0.1" }, "dependencies": { @@ -20759,8 +20876,8 @@ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" + "faye-websocket": "0.10.0", + "uuid": "3.2.1" } }, "sockjs-client": { @@ -20768,12 +20885,12 @@ "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", "requires": { - "debug": "^2.6.6", + "debug": "2.6.9", "eventsource": "0.1.6", - "faye-websocket": "~0.11.0", - "inherits": "^2.0.1", - "json3": "^3.3.2", - "url-parse": "^1.1.8" + "faye-websocket": "0.11.1", + "inherits": "2.0.3", + "json3": "3.3.2", + "url-parse": "1.4.0" }, "dependencies": { "faye-websocket": { @@ -20781,7 +20898,7 @@ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.0" } } } @@ -20791,7 +20908,7 @@ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "requires": { - "is-plain-obj": "^1.0.0" + "is-plain-obj": "1.1.0" } }, "source-list-map": { @@ -20809,11 +20926,11 @@ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", "requires": { - "atob": "^2.0.0", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.1", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-support": { @@ -20821,7 +20938,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.7" }, "dependencies": { "source-map": { @@ -20867,8 +20984,8 @@ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, "spdx-exceptions": { @@ -20881,8 +20998,8 @@ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" } }, "spdx-license-ids": { @@ -20895,12 +21012,12 @@ "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", "requires": { - "debug": "^2.6.8", - "handle-thing": "^1.2.5", - "http-deceiver": "^1.2.7", - "safe-buffer": "^5.0.1", - "select-hose": "^2.0.0", - "spdy-transport": "^2.0.18" + "debug": "2.6.9", + "handle-thing": "1.2.5", + "http-deceiver": "1.2.7", + "safe-buffer": "5.1.2", + "select-hose": "2.0.0", + "spdy-transport": "2.1.0" } }, "spdy-transport": { @@ -20908,13 +21025,13 @@ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", "requires": { - "debug": "^2.6.8", - "detect-node": "^2.0.3", - "hpack.js": "^2.1.6", - "obuf": "^1.1.1", - "readable-stream": "^2.2.9", - "safe-buffer": "^5.0.1", - "wbuf": "^1.7.2" + "debug": "2.6.9", + "detect-node": "2.0.3", + "hpack.js": "2.1.6", + "obuf": "1.1.2", + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2", + "wbuf": "1.7.3" }, "dependencies": { "process-nextick-args": { @@ -20927,13 +21044,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -20941,7 +21058,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -20951,7 +21068,7 @@ "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "requires": { - "through": "2" + "through": "2.3.8" } }, "split-string": { @@ -20959,7 +21076,7 @@ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "sprintf-js": { @@ -20972,8 +21089,8 @@ "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" + "array-uniq": "1.0.3", + "number-is-nan": "1.0.1" } }, "sshpk": { @@ -20981,14 +21098,14 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" } }, "ssri": { @@ -20996,7 +21113,7 @@ "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", "requires": { - "safe-buffer": "^5.1.1" + "safe-buffer": "5.1.2" } }, "stackframe": { @@ -21009,8 +21126,8 @@ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -21018,7 +21135,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -21038,8 +21155,8 @@ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "inherits": "2.0.3", + "readable-stream": "2.2.7" } }, "stream-combiner": { @@ -21047,7 +21164,7 @@ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "requires": { - "duplexer": "~0.1.1" + "duplexer": "0.1.1" } }, "stream-combiner2": { @@ -21055,8 +21172,8 @@ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", "requires": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" + "duplexer2": "0.1.4", + "readable-stream": "2.2.7" }, "dependencies": { "duplexer2": { @@ -21064,7 +21181,7 @@ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "2.2.7" } } } @@ -21079,8 +21196,8 @@ "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" } }, "stream-http": { @@ -21088,11 +21205,11 @@ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.1.tgz", "integrity": "sha512-cQ0jo17BLca2r0GfRdZKYAGLU6JRoIWxqSOakUMuKOT6MOK7AAlE856L33QuDmAy/eeOrhLee3dZKX0Uadu93A==", "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.3", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" }, "dependencies": { "process-nextick-args": { @@ -21105,13 +21222,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -21119,7 +21236,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -21134,8 +21251,8 @@ "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" + "inherits": "2.0.3", + "readable-stream": "2.2.7" } }, "streamsearch": { @@ -21153,8 +21270,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -21167,7 +21284,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -21177,7 +21294,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "stringstream": { @@ -21190,7 +21307,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -21198,8 +21315,8 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", "requires": { - "first-chunk-stream": "^1.0.0", - "is-utf8": "^0.2.0" + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" } }, "strip-eof": { @@ -21212,7 +21329,7 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "requires": { - "get-stdin": "^4.0.1" + "get-stdin": "4.0.1" } }, "strip-json-comments": { @@ -21230,8 +21347,8 @@ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.18.2.tgz", "integrity": "sha512-WPpJPZGUxWYHWIUMNNOYqql7zh85zGmr84FdTVWq52WTIkqlW9xSxD3QYWi/T31cqn9UNSsietVEgGn2aaSCzw==", "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^0.3.0" + "loader-utils": "1.1.0", + "schema-utils": "0.3.0" } }, "subarg": { @@ -21239,7 +21356,7 @@ "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", "requires": { - "minimist": "^1.1.0" + "minimist": "1.2.0" }, "dependencies": { "minimist": { @@ -21255,7 +21372,7 @@ "integrity": "sha512-O44AMnmJqx294uJQjfUmEyYOg7d9mylNFsMw/Wkz4evKd1njyPrtCN+U6ZIC7sKtfEVQhfTqFFijlXx8KP/Czw==", "dev": true, "requires": { - "methods": "~1.1.2", + "methods": "1.1.2", "superagent": "3.8.2" }, "dependencies": { @@ -21280,16 +21397,16 @@ "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", "dev": true, "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.1.1", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.0.5" + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.2", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "1.4.1", + "qs": "6.5.2", + "readable-stream": "2.2.7" } } } @@ -21299,7 +21416,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "svgo": { @@ -21307,13 +21424,13 @@ "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", "requires": { - "coa": "~1.0.1", - "colors": "~1.1.2", - "csso": "~2.3.1", - "js-yaml": "~3.7.0", - "mkdirp": "~0.5.1", - "sax": "~1.2.1", - "whet.extend": "~0.9.9" + "coa": "1.0.4", + "colors": "1.1.2", + "csso": "2.3.2", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "sax": "1.2.4", + "whet.extend": "0.9.9" } }, "symbol-observable": { @@ -21332,7 +21449,7 @@ "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", "requires": { - "acorn-node": "^1.2.0" + "acorn-node": "1.3.0" } }, "table": { @@ -21341,12 +21458,12 @@ "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "chalk": "2.4.1", + "lodash": "4.17.10", "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "string-width": "2.1.1" }, "dependencies": { "ajv-keywords": { @@ -21367,7 +21484,7 @@ "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "requires": { - "execa": "^0.7.0" + "execa": "0.7.0" } }, "test-exclude": { @@ -21376,11 +21493,11 @@ "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==", "dev": true, "requires": { - "arrify": "^1.0.1", - "micromatch": "^3.1.8", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", - "require-main-filename": "^1.0.1" + "arrify": "1.0.1", + "micromatch": "3.1.10", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" }, "dependencies": { "find-up": { @@ -21389,8 +21506,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "load-json-file": { @@ -21399,11 +21516,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" } }, "path-exists": { @@ -21412,7 +21529,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-type": { @@ -21421,9 +21538,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -21438,9 +21555,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -21449,8 +21566,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" } }, "strip-bom": { @@ -21459,7 +21576,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } @@ -21481,7 +21598,7 @@ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", "requires": { - "any-promise": "^1.0.0" + "any-promise": "1.3.0" } }, "through": { @@ -21494,8 +21611,8 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.2.7", + "xtend": "4.0.1" } }, "thunky": { @@ -21508,7 +21625,7 @@ "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } }, "time-stamp": { @@ -21526,7 +21643,7 @@ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", "requires": { - "setimmediate": "^1.0.4" + "setimmediate": "1.0.5" } }, "tmp": { @@ -21535,7 +21652,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "to-array": { @@ -21558,7 +21675,7 @@ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -21566,7 +21683,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -21576,10 +21693,10 @@ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -21587,8 +21704,16 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" + } + }, + "tooltip.js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tooltip.js/-/tooltip.js-1.3.1.tgz", + "integrity": "sha512-nZQGUM3YfjUlCVw3+RqYcCubIDj5NmtPsQ6Xj1NZvii1KSnPxuQ9d6xvNgsNmsmilwhNcBOmHLhwuSDVYINm0g==", + "requires": { + "popper.js": "1.14.7" } }, "toposort": { @@ -21601,7 +21726,7 @@ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "requires": { - "nopt": "~1.0.10" + "nopt": "1.0.10" } }, "tough-cookie": { @@ -21609,7 +21734,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" } }, "tr46": { @@ -21618,7 +21743,7 @@ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" }, "dependencies": { "punycode": { @@ -21649,7 +21774,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -21664,7 +21789,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-detect": { @@ -21679,7 +21804,7 @@ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "2.1.18" } }, "typedarray": { @@ -21697,8 +21822,8 @@ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.23.tgz", "integrity": "sha512-Ks+KqLGDsYn4z+pU7JsKCzC0T3mPYl+rU+VcPZiQOazjE4Uqi4UCRY3qPMDbJi7ze37n1lDXj3biz1ik93vqvw==", "requires": { - "commander": "~2.15.0", - "source-map": "~0.6.1" + "commander": "2.15.1", + "source-map": "0.6.1" } }, "uglify-to-browserify": { @@ -21712,9 +21837,9 @@ "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", "requires": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" + "source-map": "0.5.7", + "uglify-js": "2.8.29", + "webpack-sources": "1.1.0" }, "dependencies": { "camelcase": { @@ -21732,9 +21857,9 @@ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "yargs": { @@ -21742,9 +21867,9 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } } @@ -21755,7 +21880,7 @@ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "requires": { - "random-bytes": "~1.0.0" + "random-bytes": "1.0.0" } }, "uid2": { @@ -21783,7 +21908,7 @@ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-4.1.0.tgz", "integrity": "sha1-4DWCkSUuGGUiLZCTmxny9J+Bwak=", "requires": { - "invariant": "^2.1.0" + "invariant": "2.2.4" } }, "undefsafe": { @@ -21791,7 +21916,7 @@ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", "requires": { - "debug": "^2.2.0" + "debug": "2.6.9" } }, "underscore": { @@ -21811,7 +21936,7 @@ "integrity": "sha1-BUs/GEO8rlYShsh95eiHm0/Jg2Q=", "requires": { "chickencurry": "1.1.1", - "compose-function": "^2.0.0", + "compose-function": "2.0.0", "reverse-arguments": "1.0.0", "underscore.string": "3.0.3" } @@ -21821,10 +21946,10 @@ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -21832,7 +21957,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -21840,10 +21965,10 @@ "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -21858,7 +21983,7 @@ "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", "requires": { - "macaddress": "^0.2.8" + "macaddress": "0.2.8" } }, "uniqs": { @@ -21871,7 +21996,7 @@ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "2.0.0" } }, "unique-slug": { @@ -21879,7 +22004,7 @@ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", "requires": { - "imurmurhash": "^0.1.4" + "imurmurhash": "0.1.4" } }, "unique-stream": { @@ -21892,7 +22017,7 @@ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "1.0.0" } }, "unpipe": { @@ -21905,8 +22030,8 @@ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -21914,9 +22039,9 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -21951,16 +22076,16 @@ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "boxen": "1.3.0", + "chalk": "2.4.1", + "configstore": "3.1.2", + "import-lazy": "2.1.0", + "is-ci": "1.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" } }, "upper-case": { @@ -21973,7 +22098,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.0" }, "dependencies": { "punycode": { @@ -22009,8 +22134,8 @@ "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.9.tgz", "integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==", "requires": { - "loader-utils": "^1.0.2", - "mime": "1.3.x" + "loader-utils": "1.1.0", + "mime": "1.3.6" }, "dependencies": { "mime": { @@ -22025,8 +22150,8 @@ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.0.tgz", "integrity": "sha512-ERuGxDiQ6Xw/agN4tuoCRbmwRuZP0cJ1lJxJubXr5Q/5cDa78+Dc4wfvtxzhzhkm5VvmW6Mf8EVj9SPGN4l8Lg==", "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" + "querystringify": "2.0.0", + "requires-port": "1.0.0" }, "dependencies": { "querystringify": { @@ -22041,7 +22166,7 @@ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "use": { @@ -22049,7 +22174,7 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" } }, "user-home": { @@ -22097,8 +22222,8 @@ "resolved": "https://registry.npmjs.org/uue/-/uue-3.1.2.tgz", "integrity": "sha512-axKLXVqwtdI/czrjG0X8hyV1KLgeWx8F4KvSbvVCnS+RUvsQMGRjx0kfuZDXXqj0LYvVJmx3B9kWlKtEdRrJLg==", "requires": { - "escape-string-regexp": "~1.0.5", - "extend": "~3.0.0" + "escape-string-regexp": "1.0.5", + "extend": "3.0.1" } }, "uuid": { @@ -22111,7 +22236,7 @@ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "requires": { - "user-home": "^1.1.1" + "user-home": "1.1.1" } }, "valid-data-url": { @@ -22124,8 +22249,8 @@ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "value-equal": { @@ -22148,9 +22273,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "vinyl": { @@ -22158,8 +22283,8 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } }, @@ -22168,14 +22293,14 @@ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", "requires": { - "defaults": "^1.0.0", - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" }, "dependencies": { "clone": { @@ -22188,7 +22313,7 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "requires": { - "natives": "^1.1.0" + "natives": "1.1.3" } }, "isarray": { @@ -22201,10 +22326,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -22217,8 +22342,8 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } }, "vinyl": { @@ -22226,8 +22351,8 @@ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -22237,7 +22362,7 @@ "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "requires": { - "source-map": "^0.5.1" + "source-map": "0.5.7" }, "dependencies": { "source-map": { @@ -22261,7 +22386,7 @@ "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", "dev": true, "requires": { - "browser-process-hrtime": "^0.1.2" + "browser-process-hrtime": "0.1.3" } }, "w3c-xmlserializer": { @@ -22270,9 +22395,9 @@ "integrity": "sha512-0et1+9uXYiIRAecx1D5Z1nk60+vimniGdIKl4XjeqkWi6acoHNlXMv1VR5jV+jF4ooeO08oWbYxeAJOcon1oMA==", "dev": true, "requires": { - "domexception": "^1.0.1", - "webidl-conversions": "^4.0.2", - "xml-name-validator": "^3.0.0" + "domexception": "1.0.1", + "webidl-conversions": "4.0.2", + "xml-name-validator": "3.0.0" } }, "warning": { @@ -22280,7 +22405,7 @@ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "watchpack": { @@ -22288,9 +22413,9 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "chokidar": "2.0.3", + "graceful-fs": "4.1.11", + "neo-async": "2.5.1" } }, "wbuf": { @@ -22298,7 +22423,7 @@ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "requires": { - "minimalistic-assert": "^1.0.0" + "minimalistic-assert": "1.0.1" } }, "web-resource-inliner": { @@ -22306,14 +22431,14 @@ "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-4.2.1.tgz", "integrity": "sha512-fOWnBQHVX8zHvEbECDTxtYL0FXIIZZ5H3LWoez8mGopYJK7inEru1kVMDzM1lVdeJBNEqUnNP5FBGxvzuMcwwQ==", "requires": { - "async": "^2.1.2", - "chalk": "^1.1.3", - "datauri": "^1.0.4", - "htmlparser2": "^3.9.2", - "lodash.unescape": "^4.0.1", - "request": "^2.78.0", - "valid-data-url": "^0.1.4", - "xtend": "^4.0.0" + "async": "2.6.0", + "chalk": "1.1.3", + "datauri": "1.1.0", + "htmlparser2": "3.9.2", + "lodash.unescape": "4.0.1", + "request": "2.85.0", + "valid-data-url": "0.1.6", + "xtend": "4.0.1" }, "dependencies": { "ansi-styles": { @@ -22326,11 +22451,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { @@ -22351,28 +22476,28 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.11.0.tgz", "integrity": "sha512-3kOFejWqj5ISpJk4Qj/V7w98h9Vl52wak3CLiw/cDOfbVTq7FeoZ0SdoHHY9PYlHr50ZS42OfvzE2vB4nncKQg==", "requires": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "async": "^2.1.2", - "enhanced-resolve": "^3.4.0", - "escope": "^3.6.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "json5": "^0.5.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^4.2.1", - "tapable": "^0.2.7", - "uglifyjs-webpack-plugin": "^0.4.6", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1", - "yargs": "^8.0.2" + "acorn": "5.5.3", + "acorn-dynamic-import": "2.0.2", + "ajv": "6.4.0", + "ajv-keywords": "3.2.0", + "async": "2.6.0", + "enhanced-resolve": "3.4.1", + "escope": "3.6.0", + "interpret": "1.1.0", + "json-loader": "0.5.7", + "json5": "0.5.1", + "loader-runner": "2.3.0", + "loader-utils": "1.1.0", + "memory-fs": "0.4.1", + "mkdirp": "0.5.1", + "node-libs-browser": "2.1.0", + "source-map": "0.5.7", + "supports-color": "4.5.0", + "tapable": "0.2.8", + "uglifyjs-webpack-plugin": "0.4.6", + "watchpack": "1.6.0", + "webpack-sources": "1.1.0", + "yargs": "8.0.2" }, "dependencies": { "ajv": { @@ -22380,10 +22505,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", "requires": { - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1", + "uri-js": "3.0.2" } }, "has-flag": { @@ -22401,7 +22526,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } } } @@ -22411,11 +22536,11 @@ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", "requires": { - "memory-fs": "~0.4.1", - "mime": "^1.5.0", - "path-is-absolute": "^1.0.0", - "range-parser": "^1.0.3", - "time-stamp": "^2.0.0" + "memory-fs": "0.4.1", + "mime": "1.6.0", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", + "time-stamp": "2.0.0" }, "dependencies": { "mime": { @@ -22436,30 +22561,30 @@ "integrity": "sha512-zrPoX97bx47vZiAXfDrkw8pe9QjJ+lunQl3dypojyWwWr1M5I2h0VSrMPfTjopHQPRNn+NqfjcMmhoLcUJe2gA==", "requires": { "ansi-html": "0.0.7", - "array-includes": "^3.0.3", - "bonjour": "^3.5.0", - "chokidar": "^2.0.0", - "compression": "^1.5.2", - "connect-history-api-fallback": "^1.3.0", - "debug": "^3.1.0", - "del": "^3.0.0", - "express": "^4.16.2", - "html-entities": "^1.2.0", - "http-proxy-middleware": "~0.17.4", - "import-local": "^1.0.0", + "array-includes": "3.0.3", + "bonjour": "3.5.0", + "chokidar": "2.0.3", + "compression": "1.6.2", + "connect-history-api-fallback": "1.5.0", + "debug": "3.1.0", + "del": "3.0.0", + "express": "4.16.3", + "html-entities": "1.2.1", + "http-proxy-middleware": "0.17.4", + "import-local": "1.0.0", "internal-ip": "1.2.0", - "ip": "^1.1.5", - "killable": "^1.0.0", - "loglevel": "^1.4.1", - "opn": "^5.1.0", - "portfinder": "^1.0.9", - "selfsigned": "^1.9.1", - "serve-index": "^1.7.2", + "ip": "1.1.5", + "killable": "1.0.0", + "loglevel": "1.6.1", + "opn": "5.3.0", + "portfinder": "1.0.13", + "selfsigned": "1.10.2", + "serve-index": "1.9.1", "sockjs": "0.3.19", "sockjs-client": "1.1.4", - "spdy": "^3.4.1", - "strip-ansi": "^3.0.0", - "supports-color": "^5.1.0", + "spdy": "3.4.7", + "strip-ansi": "3.0.1", + "supports-color": "5.4.0", "webpack-dev-middleware": "1.12.2", "yargs": "6.6.0" }, @@ -22474,9 +22599,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" } }, "debug": { @@ -22492,8 +22617,8 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "is-fullwidth-code-point": { @@ -22501,7 +22626,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "load-json-file": { @@ -22509,11 +22634,11 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" } }, "os-locale": { @@ -22521,7 +22646,7 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { - "lcid": "^1.0.0" + "lcid": "1.0.0" } }, "path-exists": { @@ -22529,7 +22654,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-type": { @@ -22537,9 +22662,9 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -22552,9 +22677,9 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -22562,8 +22687,8 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" } }, "string-width": { @@ -22571,9 +22696,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "strip-bom": { @@ -22581,7 +22706,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "which-module": { @@ -22599,19 +22724,19 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^4.2.0" + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "4.2.1" } }, "yargs-parser": { @@ -22619,7 +22744,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "requires": { - "camelcase": "^3.0.0" + "camelcase": "3.0.0" } } } @@ -22629,7 +22754,7 @@ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.2.tgz", "integrity": "sha512-/0QYwW/H1N/CdXYA2PNPVbsxO3u2Fpz34vs72xm03SRfg6bMNGfMJIQEpQjKRvkG2JvT6oRJFpDtSrwbX8Jzvw==", "requires": { - "lodash": "^4.17.5" + "lodash": "4.17.10" } }, "webpack-sources": { @@ -22637,8 +22762,8 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" + "source-list-map": "2.0.0", + "source-map": "0.6.1" } }, "websocket-driver": { @@ -22646,8 +22771,8 @@ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" + "http-parser-js": "0.4.12", + "websocket-extensions": "0.1.3" } }, "websocket-extensions": { @@ -22670,7 +22795,7 @@ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } } } @@ -22692,9 +22817,9 @@ "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", "dev": true, "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "lodash.sortby": "4.7.0", + "tr46": "1.0.1", + "webidl-conversions": "4.0.2" } }, "whet.extend": { @@ -22707,7 +22832,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -22720,7 +22845,7 @@ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", "requires": { - "string-width": "^2.1.1" + "string-width": "2.1.1" } }, "window-size": { @@ -22739,8 +22864,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "is-fullwidth-code-point": { @@ -22748,7 +22873,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -22756,9 +22881,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -22774,7 +22899,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.1" } }, "write-file-atomic": { @@ -22782,9 +22907,9 @@ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "ws": { @@ -22792,9 +22917,9 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" + "async-limiter": "1.0.0", + "safe-buffer": "5.1.2", + "ultron": "1.1.1" } }, "x-xss-protection": { @@ -22844,19 +22969,19 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" }, "dependencies": { "cliui": { @@ -22864,9 +22989,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" }, "dependencies": { "string-width": { @@ -22874,9 +22999,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -22886,7 +23011,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "y18n": { @@ -22901,7 +23026,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } }, "yeast": { diff --git a/package.json b/package.json index 4644b329..dbb9876f 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,17 @@ }, "config": { "ghooks": { - "pre-commit": "npm run lint && npm run test" + "pre-commit": "npm run lint" } }, "dependencies": { + "@fullcalendar/bootstrap": "^4.0.1", + "@fullcalendar/core": "^4.0.1", + "@fullcalendar/daygrid": "^4.0.1", + "@fullcalendar/interaction": "^4.0.1", + "@fullcalendar/resource-common": "^4.0.1", "@mapbox/polyline": "^0.2.0", + "@sendgrid/mail": "^6.3.1", "admin-lte": "^2.4.3", "autoprefixer": "^7.1.1", "babel-core": "^6.22.1", @@ -87,6 +93,7 @@ "passport": "~0.3.2", "passport-google-oauth2": "^0.1.6", "passport-local": "~1.0.0", + "popper.js": "^1.14.7", "postcss-loader": "^2.0.5", "prop-types": "^15.5.10", "quill-image-drop-module": "^1.0.3", @@ -119,6 +126,7 @@ "striptags": "^3.0.1", "style-loader": "^0.18.2", "thenify": "^3.2.1", + "tooltip.js": "^1.3.1", "url-loader": "^0.5.7", "uuid": "^3.0.1", "webpack": "^3.1.0", @@ -139,7 +147,7 @@ "enzyme": "^3.3.0", "enzyme-adapter-react-15": "^1.0.5", "eslint": "^4.19.1", - "eslint-plugin-react": "^7.9.0", + "eslint-plugin-react": "^7.12.4", "flow-bin": "^0.74.0", "ghooks": "^2.0.4", "ignore-styles": "^5.0.1", diff --git a/server/controllers/volunteer.js b/server/controllers/volunteer.js index 128f9149..ec2848e7 100644 --- a/server/controllers/volunteer.js +++ b/server/controllers/volunteer.js @@ -1,11 +1,15 @@ import {difference, extend, omit} from 'lodash' -import {ForbiddenError, NotFoundError} from '../lib/errors' +import {ForbiddenError, NotFoundError, BadRequestError} from '../lib/errors' import {ADMIN_ROLE, clientRoles} from '../../common/constants' import User from '../models/user' import Volunteer from '../models/volunteer' import {updateFields} from '../lib/update-linked-fields' +import config from '../config' + +import mailer from '../lib/mail/mail-helpers' + export default { /** * Create a volunteer @@ -25,6 +29,105 @@ export default { res.json(savedVolunteer) }, + /* + * Adds a Volunteer shift + */ + async addShift(req, res) { + + //const volunteer = extend(req.volunteer, req.body) + const new_volunteer = req.body + const shift = new_volunteer.shift + + const old_volunteer = await Volunteer.findById(new_volunteer._id) + const shifts = old_volunteer.shift + shifts.push(shift) + + const updatedVolunteer = await Volunteer.findOneAndUpdate( + { _id: new_volunteer._id}, + { $set: { shift: shifts}}) + + res.json(updatedVolunteer) + }, + + /* + * Deletes a volunteer shift + */ + async deleteShift(req, res) { + const new_volunteer = req.body + const del_shift = new_volunteer.del_shift + + const old_volunteer = await Volunteer.findById(new_volunteer._id) + const shifts = old_volunteer.shift + + var del_time = new Date(del_shift.start) + var new_shifts = [] + + for(var i = 0; i < shifts.length; i++) { + var new_time = new Date(shifts[i].date) + if(del_time.getTime() === new_time.getTime()) { + continue + } + else { + new_shifts.push(shifts[i]) + } + } + + const updatedVolunteer = await Volunteer.findOneAndUpdate( + { _id: new_volunteer._id}, + { $set: {shift: new_shifts}}) + + res.json(updatedVolunteer) + }, + + async emailShiftReminder(req, res) { + if (!config.sendgrid.API_KEY) { + return res.status(500).json({message: "This site does not have email capability at this time. Please contact the site owner for assistance."}) + } + + // const token = (await randomBytes(20)).toString('hex') + + // // Lookup user by email + if (!req.body.email) throw new BadRequestError('Email is required') + + const volunteer = await Volunteer.findOne({email: req.body.email}) + + await mailer.sendShiftReminder(volunteer, req.body.shift) + res.send({message: 'Shift reminder email sent'}) + }, + + /* + * Updates a Volunteer shift + */ + async updateShift(req, res) { + const new_volunteer = req.body + const times = new_volunteer.times + + const old_volunteer = await Volunteer.findById(new_volunteer._id) + const shifts = old_volunteer.shift + + var old_date = new Date(times.oldTime) + + for(var i = 0; i < shifts.length; i++) { + var new_time = new Date(shifts[i].date) + if(old_date.getTime() === new_time.getTime()) { + shifts[i].date = times.newTime + } + } + + const updatedVolunteer = await Volunteer.findOneAndUpdate( + { _id: new_volunteer._id}, + { $set: {shift: shifts}}) + + res.json(updatedVolunteer) + }, + + async getAllVolunteers(req, res) { + const volunteers = await Volunteer.find({}) + .sort('firstName') + + res.json(volunteers) + }, + /** * Show the current volunteer */ @@ -48,6 +151,7 @@ export default { const user = await User.findById(volunteer._id).lean() const newVolunteer = await volunteer.save() + //Volunteer.findByIdAndUpdate({ _id: 10017}, { $set : { lastName: "hi"}}) if (!volunteer.roles || !req.user.roles.find(r => r === ADMIN_ROLE)) { updateFields(clientRoles.VOLUNTEER,req.body.fields,volunteer._id) @@ -113,4 +217,5 @@ export default { } next() } -} + +} \ No newline at end of file diff --git a/server/lib/mail/mail-generator.js b/server/lib/mail/mail-generator.js index 548e1a66..a8319866 100644 --- a/server/lib/mail/mail-generator.js +++ b/server/lib/mail/mail-generator.js @@ -25,7 +25,15 @@ export default async function generate(identifier, bindings) { if (!page || page.disabled) return let attachments = [] + + + // page.body = The HTML of the email + // bindings = To replace placeholders in email const body = transformBody(page.body, bindings, attachments) + + + + const subject = transformSubject(page.subject, bindings) const text = transformText(body) const html = template.replace(/\{content\}/, body) @@ -51,6 +59,8 @@ function transformText(body) { function transformBody(body, bindings, attachments) { const replaceTags = { + + // If it's a placeholder, then replace the span node with the bindings and attachments span: node => isPlaceholder(node) ? bindPlaceholder(node, bindings, attachments) : node, @@ -76,6 +86,7 @@ function transformSubject(subject, bindings) { function bindPlaceholder(node, bindings, attachments) { const id = getAttr(node, 'data-id') + const placeholder = placeholders.find(pl => pl.id === id) //Receipt Placeholder only diff --git a/server/lib/mail/mail-helpers.js b/server/lib/mail/mail-helpers.js index 83192184..dbe48534 100644 --- a/server/lib/mail/mail-helpers.js +++ b/server/lib/mail/mail-helpers.js @@ -12,6 +12,9 @@ export default { * @param {string} identifier * @param {object} bindings */ + + // Identifier - Determines which "Page" modal is going to be used + // Bindings - async send(toEmail, toName, identifier, bindings = null) { const settings = await Settings.findOne().lean() const mail = await mailGenerator(identifier, {...settings, ...bindings}) @@ -24,6 +27,33 @@ export default { } }, + + + /** + * Sends an email to volunteer after shift created + */ + async sendShiftReminder(volunteer, shift) { + const {firstName, lastName, email} = volunteer + const {date} = shift + var fullName = firstName + ' ' + lastName + await this.send( + email, + fullName, + 'volunteer-shift', + {firstName, lastName, fullName, date} + ) + }, + + + + + + + + + + + /** * Send an email for account status update * diff --git a/server/models/volunteer.js b/server/models/volunteer.js index 11b9ac77..58a38c07 100644 --- a/server/models/volunteer.js +++ b/server/models/volunteer.js @@ -53,6 +53,13 @@ var VolunteerSchema = new Schema({ type: String, trim: true }, + shift: [{ + role: String, + date: Date, + duration: Number, + notes: String + }], + test: String, disclaimerGuardianEmail: { type: String, trim: true diff --git a/server/routes/volunteer.js b/server/routes/volunteer.js index 9352e418..c70eb4c3 100644 --- a/server/routes/volunteer.js +++ b/server/routes/volunteer.js @@ -16,6 +16,26 @@ export default () => { .get(requiresLogin, hasAuthorization, volunteerController.read) .put(requiresLogin, hasAuthorization, volunteerController.update) + // Updates a shift + volunteerRouter.route('/volunteers/updateShift') + .put(volunteerController.updateShift) + + // Volunteer routes for admin for volunteer scheduling + volunteerRouter.route('/volunteers/addShift') + .put(volunteerController.addShift) + + volunteerRouter.route('/volunteers/deleteShift') + .put(volunteerController.deleteShift) + + volunteerRouter.route('/volunteers/getAllVolunteers') + .get(volunteerController.getAllVolunteers) + + + // FOR SHIFT EMAIL NOTIFICATIONS + volunteerRouter.route('/volunteers/emailShiftReminder') + .post(volunteerController.emailShiftReminder) + + // Volunteer routes for admin volunteerRouter.route('/admin/volunteers') .get(volunteerController.list) @@ -27,5 +47,7 @@ export default () => { // Finish by binding the volunteer middleware volunteerRouter.param('volunteerId', volunteerController.volunteerById) + + return volunteerRouter } diff --git a/server1/config/env/all.js b/server1/config/env/all.js new file mode 100644 index 00000000..1eb74c90 --- /dev/null +++ b/server1/config/env/all.js @@ -0,0 +1,19 @@ +const production = process.env.NODE_ENV === 'production' + +const protocol = production ? 'https' : 'http' +const host = process.env.HOST_NAME || 'localhost' +const port = process.env.PORT || 3000 + +const url = `${protocol}://${host}` + production ? '' : `:${port}` + +export default { + protocol: process.env.PROTOCOL || protocol, + host, + port, + sessionCollection: 'sessions', + sessionIdleTimeout: 3600000, + mailFrom: `no-reply@${host}`, + oauth: { + googleCallbackURL: `${url}/api/auth/google/callback` + } +} diff --git a/server1/config/env/development.js b/server1/config/env/development.js new file mode 100644 index 00000000..48cc6620 --- /dev/null +++ b/server1/config/env/development.js @@ -0,0 +1,4 @@ +export default { + db: process.env.MONGODB_URI || 'mongodb://localhost/fb-dev', + sessionSecret: 'foodbank-app' +} diff --git a/server1/config/env/production.js b/server1/config/env/production.js new file mode 100644 index 00000000..2eb803ae --- /dev/null +++ b/server1/config/env/production.js @@ -0,0 +1,6 @@ +if (!process.env.SECRET) throw new Error('environment variable SECRET must be set') + +export default { + db: process.env.MONGODB_URI || 'mongodb://localhost:27017/fb-prod', + sessionSecret: process.env.SECRET +} diff --git a/server1/config/env/secrets-template.js b/server1/config/env/secrets-template.js new file mode 100644 index 00000000..4424c372 --- /dev/null +++ b/server1/config/env/secrets-template.js @@ -0,0 +1,10 @@ +export default { + gmapsApiKey: process.env.GMAPS_API_KEY || '', + oauth: { + googleClientID: process.env.GOOGLE_CLIENT_ID || '', + googleClientSecret: process.env.GOOGLE_CLIENT_SECRET || '', + }, + sendgrid: { + API_KEY: process.env.SENDGRID_API_KEY || '' + } +} diff --git a/server1/config/env/secrets.js b/server1/config/env/secrets.js new file mode 100644 index 00000000..4424c372 --- /dev/null +++ b/server1/config/env/secrets.js @@ -0,0 +1,10 @@ +export default { + gmapsApiKey: process.env.GMAPS_API_KEY || '', + oauth: { + googleClientID: process.env.GOOGLE_CLIENT_ID || '', + googleClientSecret: process.env.GOOGLE_CLIENT_SECRET || '', + }, + sendgrid: { + API_KEY: process.env.SENDGRID_API_KEY || '' + } +} diff --git a/server1/config/env/test.js b/server1/config/env/test.js new file mode 100644 index 00000000..342e00ca --- /dev/null +++ b/server1/config/env/test.js @@ -0,0 +1,6 @@ +export default { + db: process.env.MONGODB_URI || 'mongodb://localhost:27017/fb-test', + port: 3001, + sessionSecret: 'foodbank-app' +} + diff --git a/server1/config/express.js b/server1/config/express.js new file mode 100644 index 00000000..fb5ab2b6 --- /dev/null +++ b/server1/config/express.js @@ -0,0 +1,121 @@ +import bodyParser from 'body-parser' +import compress from 'compression' +import cookieParser from 'cookie-parser' +import express from 'express' +import helmet from 'helmet' +import morgan from 'morgan' +import mongoose from 'mongoose' +import connectMongo from 'connect-mongo' +import passport from 'passport' +import path from 'path' +import session from 'express-session' +import {get} from 'lodash' + +import {addUser} from '../lib/websocket-middleware' +import apiRoutes from '../routes/api' +import config from './index' +import enforceSSLMiddleware from '../lib/enforce-ssl-middleware' +import getErrorMessage, {HttpError} from '../lib/errors' +import seed from '../lib/seed' +import '../models' + +// set api delay and failure probablility for testing +const API_DELAY = 0 +const API_FAILURE_RATE = 0 + +const mongoStore = connectMongo({session}) + +export default function(io) { + const app = express() + + const sharedSession = session({ + saveUninitialized: false, + cookie: { maxAge: config.sessionIdleTimeout }, + resave: true, + rolling: true, + secret: config.sessionSecret, + store: new mongoStore({ + mongooseConnection: mongoose.connection, + collection: config.sessionCollection + }) + }) + + app.set('sharedSession', sharedSession) + + // call with true or delete db to seed + seed(process.env.NODE_ENV, false) + + // force https + if (process.env.NODE_ENV === 'production') { + app.use(enforceSSLMiddleware) + } + + app.use(compress({ + filter: function(req, res) { + return (/json|text|javascript|css/).test(res.getHeader('Content-Type')) + }, + level: 9 + })) + + if (process.env.NODE_ENV === 'development') { + app.use(morgan('dev')) + } + + app.use(bodyParser.urlencoded({ + extended: true + })) + app.use('/api/admin/pages', bodyParser.json({limit: '5mb'})) + app.use(bodyParser.json({})) + + // CookieParser should be above session + app.use(cookieParser()) + + // Express MongoDB session storage + app.use(sharedSession) + + // use passport session + app.use(passport.initialize()) + app.use(passport.session()) + + // Use helmet to secure Express headers + app.use(helmet()) + app.disable('x-powered-by') + + app.use('/api', apiRoutes(API_DELAY, API_FAILURE_RATE)) + + // Setting the static folder + if (process.env.NODE_ENV === 'production') + app.use(express.static(path.resolve('./dist/client'))) + + // Error handler + app.use(function(err, req, res, next) { + if (!err) return next() + + // Dont log client errors or during testing + if (process.env.NODE_ENV !== 'test' && !(err instanceof HttpError)) { + console.error(err) + } + + const error = getErrorMessage(err) + res.status(error.status).json({ + message: error.message, + ...(error.paths ? {paths: err.paths} : {}) + }) + }) + + if (process.env.NODE_ENV === 'production') { + app.use(function(req, res) { + res.sendFile(path.resolve('./dist/client/index.html')) + }) + } + + if (io) { + io.on('connection', socket => { + const user = get(socket.handshake.session, 'passport.user') + if (user) addUser(user, socket) + }) + } + + // Return Express server instance + return app +} diff --git a/server1/config/index.js b/server1/config/index.js new file mode 100644 index 00000000..9ba79316 --- /dev/null +++ b/server1/config/index.js @@ -0,0 +1,16 @@ +import {merge} from 'lodash' + +const env = process.env.NODE_ENV || 'development' + +let secrets +try { + secrets = require('./env/secrets').default +} catch (err) { + secrets = require('./env/secrets-template').default +} // eslint-disable-line no-empty + +export default merge( + require('./env/all').default, + require(`./env/${env}`).default, + secrets +) diff --git a/server1/config/mailer.js b/server1/config/mailer.js new file mode 100644 index 00000000..c7a1504c --- /dev/null +++ b/server1/config/mailer.js @@ -0,0 +1,54 @@ +/** + * This module adds functionality for the app to use email + */ + +import {readFileSync} from 'fs' +import mime from 'mime-types' +import sendgrid, {mail as helper} from 'sendgrid' + +import config from './index.js' +import Media from '../models/media' + +const sg = sendgrid(config.sendgrid.API_KEY) +const path = process.env.NODE_ENV === 'production' ? + 'dist/client/' : + 'assets/' + +export default async function sendEmail( + toEmail, + toName, + {html, text, subject, attachments} +) { + if (!config.sendgrid.API_KEY || toEmail.endsWith('@example.com')) return + + const from = new helper.Email(config.mailFrom) + const to = new helper.Email(toEmail, toName) + const content = new helper.Content('text/plain', text) + const mail = new helper.Mail(from, subject, to, content) + mail.addContent(new helper.Content('text/html', html)) + + if (attachments.length) { + const media = await Media.findOne().lean() + + attachments.forEach(attachmentId => { + const file = `${path}${media.path}${media[attachmentId]}` + const content = readFileSync(file).toString('base64') + + const attachment = new helper.Attachment() + attachment.setContent(content) + attachment.setType(mime.lookup(file)) + attachment.setFilename(file) + attachment.setDisposition('inline') + attachment.setContentId(attachmentId) + mail.addAttachment(attachment) + }) + } + + const request = sg.emptyRequest({ + method: 'POST', + path: '/v3/mail/send', + body: mail.toJSON() + }) + + return sg.API(request) +} diff --git a/server1/config/passport.js b/server1/config/passport.js new file mode 100644 index 00000000..45b64795 --- /dev/null +++ b/server1/config/passport.js @@ -0,0 +1,36 @@ +import passport from 'passport' +import localStrategy from './strategies/local' +import googleStrategy from './strategies/google' +import config from './index.js' +import User from '../models/user' + +export default function() { + // Serialize sessions + passport.serializeUser(function(user, done) { + done(null, {_id: user._id, roles: user.roles}) + }) + + // Deserialize sessions + passport.deserializeUser(async function({_id}, done) { + try { + const user = await User.findById(_id) + done(null, user) + } catch (err) { + done(err) + } + + }) + + // Initialize strategies + localStrategy() + + if (config.oauth.googleClientID && config.oauth.googleClientSecret) { + googleStrategy() + } else if (process.env.NODE_ENV !== 'test') { + console.warn() + console.warn('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') + console.warn('!!! Google oauth API keys not set. Google login is disabled !!!') + console.warn('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') + console.warn() + } +} diff --git a/server1/config/strategies/google.js b/server1/config/strategies/google.js new file mode 100644 index 00000000..86597245 --- /dev/null +++ b/server1/config/strategies/google.js @@ -0,0 +1,57 @@ +import passport from 'passport' +import {Strategy as GoogleStrategy} from 'passport-google-oauth2' + +import User from '../../models/user' +import config from '../index' + +export default function() { + passport.use(new GoogleStrategy( + { + clientID: config.oauth.googleClientID, + clientSecret: config.oauth.googleClientSecret, + callbackURL: config.oauth.googleCallbackURL, + passReqToCallback: true + }, + async function(req, accessToken, refreshToken, profile, cb) { + const query = JSON.parse(req.query.state) + const action = query.action + + try { + const user = await User.findOne({'google.id': profile.id}) + if (action === 'login') { + cb(null, user, profile) + } else if (action === 'signup') { + signup(user, profile, cb) + } else { + throw new Error('An action of type "login" or "signup" must be specified') + } + } catch (err) { + cb(err) + } + } + )) +} + +const signup = (user, profile, cb) => { + if (user) { + throw new Error("An account already exists with that google ID") + } else { + User.create( + { + firstName: profile.name.givenName, + lastName: profile.name.familyName, + email: profile.email, + provider: 'google', + roles: [], + google: {id: profile.id} + }, + (err, newUser) => { + if (err) { + throw err + } else { + cb(null, newUser) + } + } + ) + } +} diff --git a/server1/config/strategies/local.js b/server1/config/strategies/local.js new file mode 100644 index 00000000..35b1127a --- /dev/null +++ b/server1/config/strategies/local.js @@ -0,0 +1,22 @@ +import passport from 'passport' +import {Strategy as LocalStrategy} from 'passport-local' +import User from '../../models/user' + +export default function() { + passport.use(new LocalStrategy({ + usernameField: 'email', + passwordField: 'password' + }, async function(email, password, done) { + try { + const user = await User.findOne({email}).select('+salt +password') + if (!user || !user.authenticate(password)) { + return done(null, false, { + message: 'Unknown user or invalid password' + }) + } + return done(null, user) + } catch (err) { + done(err) + } + })) +} diff --git a/server1/controllers/customer.js b/server1/controllers/customer.js new file mode 100644 index 00000000..aca2a12f --- /dev/null +++ b/server1/controllers/customer.js @@ -0,0 +1,152 @@ +import {extend, intersection, includes, omit} from 'lodash' + +import {ForbiddenError, NotFoundError} from '../lib/errors' +import {ADMIN_ROLE, clientRoles, volunteerRoles, customerStatus} from '../../common/constants' +import Customer from '../models/customer' +import Volunteer from '../models/volunteer' +import Package from '../models/package' +import mailer from '../lib/mail/mail-helpers' +import User from '../models/user' + +import {updateFields} from '../lib/update-linked-fields' +import {searchUserAndSetNotification, searchVolunteerAndSetNotification} from '../lib/notification-sender' + +export default { + /** + * Create a customer + */ + async create(req, res) { + let customer = new Customer(omit(req.body, ['status'])) + customer._id = req.user.id + + await User.findOneAndUpdate( + {_id: customer._id}, + {$push: {roles: clientRoles.CUSTOMER}} + ) + + // Sent Notification + searchUserAndSetNotification('roles/admin', {message:`Customer ${customer.fullName} was created!`, url: `/customers/${customer._id}`}, req.user._id) + + const savedCustomer = await customer.save() + updateFields(clientRoles.CUSTOMER, req.body.fields, customer._id) + res.json(savedCustomer) + + // mailer.send(config.mailer.to, 'A new client has applied.', 'create-customer-email') + }, + + /** + * Show the current customer + */ + read(req, res) { + res.json(req.customer) + }, + + /** + * Update a customer + */ + async update(req, res) { + const customer = extend(req.customer, req.body) + + const oldCustomer = await Customer.findById(customer._id) + const newCustomer = await customer.save() + + // Check if status is updated by admin + if (oldCustomer.status === 'Pending' && (newCustomer.status === 'Accepted' || newCustomer.status === 'Rejected')) { + mailer.sendStatus(newCustomer) + // Check if status was updated by someone else than the user + } else if(!(newCustomer.status === 'Away' || newCustomer.status === 'Available' )){ + mailer.sendUpdate(newCustomer) + } + + // Sent Notification + searchUserAndSetNotification('roles/admin', {message:`Customer ${customer.fullName} was updated!`, url: `/customers/${customer._id}`}, req.user._id) + searchVolunteerAndSetNotification({message:`Customer ${customer.fullName} was updated!`, url: `/customers/${customer._id}`}, customer._id) + + updateFields(clientRoles.CUSTOMER, req.body.fields, customer._id) + res.json(newCustomer) + }, + + /** + * List customers + */ + async list(req, res) { + const customers = await Customer.find() + .sort('-dateReceived') + .populate('user', 'displayName') + .populate('assignedTo', 'firstName lastName') + res.json(customers) + }, + + /** + * Delete customer + */ + async delete(req, res) { + const id = req.customer._id + + // Check to see if the customer is associated with any packages + const packageCount = await Package.find({customer: id}).count() + + if (packageCount > 0) { + // Don't delete the customer since a package references its data + res.status(409).json({message: "This customer has packages and can't be deleted"}) + } else { + // Remove the customer if it occurs in any Volunteers.customers + await Volunteer.update({}, { $pull: { "customers": id } }, {multi: true}) + + await Customer.findByIdAndRemove(id) + res.json(req.customer) + } + }, + /** + * Customer middleware + */ + async customerById(req, res, next, id) { + const customer = await Customer.findById(id) + + if (!customer) throw new NotFoundError + + req.customer = customer + next() + }, + + /** + * Customer authorization middleware + */ + hasAuthorization(req, res, next) { + const authorizedRoles = [ + ADMIN_ROLE, + volunteerRoles.DRIVER, + volunteerRoles.PACKING + ] + + if (req.user && intersection(req.user.roles, authorizedRoles).length) { + return next() + } + + if (!req.customer || req.customer._id !== +req.user.id) + throw new ForbiddenError + + next() + }, + + /** + * Customer Middleware + */ + async canChangeStatus(req, res, next) { + const unrestrictedRoles = [ + ADMIN_ROLE, + ] + const restrictedStatus = [ + customerStatus.REJECTED, + customerStatus.PENDING, + ] + + if (req.user && !intersection(req.user.roles, unrestrictedRoles).length) { + const statusChanged = req.customer.status !== req.body.status + if (statusChanged && includes(restrictedStatus, req.customer.status)) + throw new ForbiddenError + } + + next() + } +} diff --git a/server1/controllers/delivery.js b/server1/controllers/delivery.js new file mode 100644 index 00000000..146049a7 --- /dev/null +++ b/server1/controllers/delivery.js @@ -0,0 +1,73 @@ +import {difference, intersection, values} from 'lodash' + +import {ForbiddenError} from '../lib/errors' +import {getDirections} from '../lib/mapquest-client' +import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' +import Customer from '../models/customer' +import Volunteer from '../models/volunteer' + + +export default { + async directions(req, res) { + const directions = await getDirections(req.query.waypoints, { + optimize: req.query.optimize + }) + + res.json(directions) + }, + async assign(req, res) { + const customerIds = req.body.customerIds.map(id => Number(id)) + const driverId = Number(req.body.driverId) + + let updatedDrivers = {} + const previousAssigned = (await Volunteer.findById(driverId)).customers + + // unassign customers that were deselected + const unassignedCustomers = await Promise.all( + difference(previousAssigned, customerIds).map(id => + Customer.findByIdAndUpdate(id, {$set: {assignedTo: null}}, {new: true})) + ) + + // assign selected customers + const assignedCustomers = await Promise.all( + customerIds.map(async id => { + const assignedTo = (await Customer.findById(id)).assignedTo + + if (assignedTo && assignedTo !== driverId) { + // customer was assigned to another driver, remove from old driver + const updatedDriver = await Volunteer.findByIdAndUpdate(assignedTo, { + $pull: {customers: id} + }, {new: true}).populate('user', 'roles') + + updatedDrivers[assignedTo] = updatedDriver + } + return Customer.findByIdAndUpdate(id, { + $set: {assignedTo: driverId}}, {new: true}) + }) + ) + + const driver = await Volunteer.findByIdAndUpdate(driverId, { + $set: { + customers: customerIds, + optimized: false + } + }, {new: true}).populate('user', 'roles') + + res.json({ + customers: [...unassignedCustomers, ...assignedCustomers], + volunteers: [driver, ...values(updatedDrivers)] + }) + }, + async hasAuthorization(req, res, next) { + const authorizedRoles = [ + ADMIN_ROLE, + volunteerRoles.DRIVER + ] + + if (req.user && intersection(req.user.roles, authorizedRoles).length) { + return next() + } else { + throw new ForbiddenError + } + } +} diff --git a/server1/controllers/donation.js b/server1/controllers/donation.js new file mode 100644 index 00000000..b98cf313 --- /dev/null +++ b/server1/controllers/donation.js @@ -0,0 +1,69 @@ +import {pick} from 'lodash' +import {ADMIN_ROLE} from '../../common/constants' +import Donation from '../models/donation' +import Donor from '../models/donor' +import {UnauthorizedError} from '../lib/errors' +import {BadRequestError} from '../lib/errors' +import mailer from '../lib/mail/mail-helpers' + +export default { + async create(req, res) { + let newDonation = { + ...(pick(req.body, ['donor', 'description', 'items'])), + total: req.body.items.reduce((acc, item) => { + if (isNaN(Number(item.value))) { + throw new BadRequestError + } else { + return acc + Number(item.value) + } + },0 ) + } + + if (!req.user.roles.find(r => r === ADMIN_ROLE) && + newDonation.donor !== req.user._id) { + throw new UnauthorizedError + } + + const donation = await Donation.create(newDonation) + const donor = await Donor.findByIdAndUpdate(donation.donor, + {$push: {donations: donation}}, + {new: true} + ) + + mailer.sendThanks({...donation, donor}) + + res.json({ + donation, + donor + }) + }, + + async approve(req, res) { + const donation = await Donation.findByIdAndUpdate( + req.params.donationId, + {$set: {approved: true, dateIssued: Date.now()}}, + {new: true} + ).populate('donor') + + mailer.sendReceipt(donation) + + res.json(donation) + }, + + async sendEmail(req, res) { + const donation = await Donation.findById(req.params.donationId) + .populate('donor') + + mailer.sendReceipt(donation) + res.json(donation) + }, + + hasAuthorization(req, res, next) { + if (req.user.roles.find(r => r === ADMIN_ROLE) || + req.params.donationId === req.user._id) { + return next() + } + + throw new UnauthorizedError + } +} diff --git a/server1/controllers/donor.js b/server1/controllers/donor.js new file mode 100644 index 00000000..0fcf5a92 --- /dev/null +++ b/server1/controllers/donor.js @@ -0,0 +1,89 @@ +import extend from 'lodash/extend' +import {omit} from 'lodash' + +import {ForbiddenError, NotFoundError} from '../lib/errors' +import {ADMIN_ROLE, clientRoles} from '../../common/constants' +import Donor from '../models/donor' +import User from '../models/user' +import {updateFields} from '../lib/update-linked-fields' + +export default { + /** + * Create a donor + */ + async create(req, res) { + const donor = new Donor({ + ...(omit(req.body, ['status', 'donations'])), + _id: req.user.id + }) + + const savedDonor = await donor.save() + + await User.findOneAndUpdate({_id: donor._id}, {$push: {roles: clientRoles.DONOR}}) + updateFields(clientRoles.DONOR,req.body.fields,donor._id) + res.json(savedDonor) + }, + + /** + * Show the current donor + */ + read(req, res) { + res.json(req.donor) + }, + + /** + * Update a donor + */ + async update(req, res) { + const donor = extend(req.donor, req.body) + const savedDonor = await donor.save() + updateFields(clientRoles.DONOR,req.body.fields,donor._id) + res.json(savedDonor) + }, + + /** + * List of donors + */ + async list(req, res) { + const donors = await Donor.find() + .sort('-dateReceived') + .populate('donations') + + res.json(donors) + }, + + /** + * Delete donor + */ + async delete(req, res) { + const id = req.donor._id + + await User.findByIdAndRemove(id) + await Donor.findByIdAndRemove(id) + + res.json(req.donor) + }, + + /** + * Donor middleware + */ + async donorById(req, res, next, id) { + const donor = await Donor.findById(id) + .populate('donations') + + if (!donor) throw new NotFoundError + + req.donor = donor + next() + }, + + /** + * Donor authorization middleware + */ + hasAuthorization(req, res, next) { + if (req.user.roles.find(r => r === ADMIN_ROLE) || req.donor._id === +req.user.id) + return next() + + throw new ForbiddenError + } +} diff --git a/server1/controllers/food.js b/server1/controllers/food.js new file mode 100644 index 00000000..a6900250 --- /dev/null +++ b/server1/controllers/food.js @@ -0,0 +1,196 @@ +import {extend, intersection} from 'lodash' + +import { + BadRequestError, + ForbiddenError, + NotFoundError, + ValidationError +} from '../lib/errors' +import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' +import Customer from '../models/customer' +import Food from '../models/food' + +const {INVENTORY, SCHEDULE} = volunteerRoles + +export default { + /** + * Create a Food category + */ + async create(req, res) { + authorizeByRole(req.user.roles, [INVENTORY]) + + const food = new Food(req.body) + + const existingFoodCategory = await Food.find({'category': food.category, 'deleted': false}).lean() + + if (existingFoodCategory.length) { + throw new ValidationError(['category'], 'That category already exists' ) + } + + const savedFood = await food.save() + res.json(savedFood) + }, + + /** + * Update a Food category + */ + async update(req, res) { + authorizeByRole(req.user.roles, [INVENTORY]) + + const food = extend(req.food, req.body) + + const savedFood = await food.save() + res.json(savedFood) + }, + + /** + * Delete a Food category + */ + async delete(req, res) { + authorizeByRole(req.user.roles, [INVENTORY]) + + const food = req.food + + // Prevent deleting if food category contains food items not marked as deleted + if (food.items.filter(item => !item.deleted).length) { + throw new BadRequestError('Food category must be empty before deleting') + } + + // If the category has items then mark it as deleted instead of deleting from the database + if (food.items.length) { + await Food.findByIdAndUpdate(food._id, {deleted: true}) + } else { + await food.remove() + } + res.json(food) + }, + + /** + * List of Food categories + */ + async list(req, res) { + const foods = await Food.find() + .sort('category') + + res.json(foods) + }, + + /** + * Create a food item + */ + async createItem(req, res) { + authorizeByRole(req.user.roles, [INVENTORY]) + + const item = req.body + item.name = item.name.trim() + + //Check to see if an item with the same name already exists in a category + let categoryWithExistingItem = await Food.findOne( + { items: {$elemMatch:{name: {$regex: `^${item.name}$`, $options: "i"}, deleted: false }} }, + { 'items.$': 1 } + ).lean() + + if (categoryWithExistingItem) { + const existingFoodItem = categoryWithExistingItem.items[0] + existingFoodItem.categoryId = item.categoryId + existingFoodItem.quantity += Number(item.quantity) + + const updatedCategory = await updateItemHelper(categoryWithExistingItem._id, existingFoodItem) + res.json(updatedCategory) + } else { + const savedFood = await Food.findByIdAndUpdate(req.food._id, { $addToSet: { items: item } }, { new: true }) + res.json(savedFood) + + // Add item to every customer's food preferences + // TODO: do we want this? + await Customer.update({}, { $addToSet: { foodPreferences: item._id } }, { multi: true }) + } + }, + + /** + * Update a food item + */ + async updateItem(req, res) { + authorizeByRole(req.user.roles, [INVENTORY, SCHEDULE]) + + const originalCategoryId = req.params.foodId + const updatedItem = req.body + + const involvesCategoryChange = !updatedItem.categoryId || updatedItem.categoryId === originalCategoryId + if (involvesCategoryChange) authorizeByRole(req.user.roles, [INVENTORY]) + + const updatedCategory = await updateItemHelper(originalCategoryId, updatedItem) + res.json(updatedCategory) + }, + + /** + * Delete a food item + */ + async deleteItem(req, res) { + authorizeByRole(req.user.roles, [INVENTORY]) + + const categoryId = req.food._id + const foodItemId = req.itemId + await Food.update({"_id": categoryId, "items._id": foodItemId}, { $set: {"items.$.deleted": true} }) + + // Delete the food item from customer preferences + await Customer.update({}, { $pull: { "foodPreferences": foodItemId } }, {multi: true}) + + res.json({deletedItemId: req.itemId}) + }, + + /** + * Food middleware + */ + async foodById(req, res, next, id) { + const food = await Food.findById(id) + + if (!food) throw new NotFoundError + + req.food = food + next() + }, + + itemById(req, res, next, id) { + req.itemId = id + next() + } +} + +function authorizeByRole(userRoles, roles = []) { + if (!intersection(userRoles, [...roles, ADMIN_ROLE]).length) { + throw new ForbiddenError + } +} + +/** + * Common functionality used by createItem and UpdateItem to update a food item + */ +function updateItemHelper(originalCategoryId, updatedItem) { + if (!updatedItem.categoryId || updatedItem.categoryId === originalCategoryId) { + return updateFoodItemWithoutCategoryChange(originalCategoryId, updatedItem) + } else { + return updateFoodItemWithCategoryChange(originalCategoryId, updatedItem) + } +} + +function updateFoodItemWithoutCategoryChange(categoryId, updatedItem) { + // same food category so just update the item in that category + return Food.findOneAndUpdate( + { _id: categoryId, 'items._id': updatedItem._id }, + { $set: { 'items.$': updatedItem } }, + { new: true } + ) +} + +async function updateFoodItemWithCategoryChange(originalCategoryId, updatedItem) { + // delete from old category + await Food.findByIdAndUpdate(originalCategoryId, { + $pull: { items: { _id: updatedItem._id } } }) + + // add to new + return Food.findByIdAndUpdate(updatedItem.categoryId, + { $addToSet: { items: updatedItem } }, + { new: true } + ) +} diff --git a/server1/controllers/media.js b/server1/controllers/media.js new file mode 100644 index 00000000..b7d52105 --- /dev/null +++ b/server1/controllers/media.js @@ -0,0 +1,27 @@ +import Media from '../models/media' +import { deleteFile } from '../lib/media-helpers' + +export default { + async read(req, res) { + const media = await Media.findOne() + res.json(media || new Media) + }, + + async upload(req, res) { + const media = (await Media.findOne()) || new Media + + Object.keys(req.files).forEach(type => { + const {filename} = req.files[type][0] + + if (media[type] && media[type] !== filename) { + deleteFile(media[type]) + } + + media[type] = filename + }) + + await media.save() + res.json(media) + } +} + diff --git a/server1/controllers/packing.js b/server1/controllers/packing.js new file mode 100644 index 00000000..f9c56553 --- /dev/null +++ b/server1/controllers/packing.js @@ -0,0 +1,219 @@ +import {intersection, uniq} from 'lodash' +import moment from 'moment' +import mongoose from 'mongoose' + +import {BadRequestError, ForbiddenError} from '../lib/errors' +import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' +import Customer from '../models/customer' +import Food from '../models/food' +import Package from '../models/package' + +const beginWeek = moment.utc().startOf('isoWeek') + +export default { + list: function(req, res) { + Package.find().then(data => res.json(data)) + }, + complete: async function(req, res) { + const {packageId} = req.body + const deliveredPackage = await Package.findByIdAndUpdate(packageId, {status: 'Delivered'}, {new: true}) + if (!deliveredPackage) { + throw new BadRequestError(`package with _id ${req.body.singlePackage} not found`) + } + res.json(deliveredPackage) + }, + /** + * Creates new food packages + * The req.body post data should be an array of food package objects with the following shape + * {customer: "10000", contents: ["594d6a4f9431ac26453cef08", "594d6a4f9431ac26453cef0b"]} + */ + pack: async function(req, res) { + const packages = req.body + if (!Array.isArray(packages)) { + throw new BadRequestError('Request body must be an array') + } + const now = new Date().toISOString() + const newPackages = [] + const customerIdsToUpdate = [] + const packedItemCounts = {} + + // Iterate through the package data to populate newPackages, customerIdsToUpdate and packedItemCounts + packages.forEach(customerPackage => { + const newPackage = new Package({ + customer: customerPackage.customer, + contents: customerPackage.contents, + datePacked: now, + packedBy: req.user._id, + status: 'Packed' + }) + + newPackages.push(newPackage) + customerIdsToUpdate.push(newPackage.customer) + addPackageContentsToItemCounts(newPackage.contents, packedItemCounts) + }) + + // Validate the data for the new packages + try { + await Promise.all( + newPackages.map(customerPackage => customerPackage.validate()) + ) + } catch (err) { + if (err instanceof mongoose.Error.ValidationError) { + throw new BadRequestError(err.message) + } else { + throw err + } + } + + // Verify the customerIds and foodItemIds in the data are valid id's + const foodItemIdsToUpdate = Object.keys(packedItemCounts) + await Promise.all([ + verifyCustomerIds(customerIdsToUpdate), + verifyFoodItemIds(foodItemIdsToUpdate) + ]) + + // Save the new packages to the database + await Promise.all( + newPackages.map(customerPackage => customerPackage.save()) + ) + + //update customers lastPacked field in database + await Customer.update({ _id: { $in: customerIdsToUpdate } }, { "$set": { lastPacked: now } }, { "multi": true }) + + + // Update the food item inventory quantities + const foodItemUpdates = [] + await Promise.all( + foodItemIdsToUpdate.map(itemId => + Food.findOneAndUpdate( + { 'items._id': itemId }, + { $inc: { 'items.$.quantity': -packedItemCounts[itemId] } }, + { new: true } + ).then(food => { + const quantity = food.items.find(item => item._id.equals(itemId)).quantity + foodItemUpdates.push({ _id: itemId, quantity }) + }) + ) + ) + + // Create a list of customers lastPacked fields that the client needs to update + const customerUpdates = customerIdsToUpdate.map(_id => ({ _id, lastPacked: now })) + + res.json({ + packages: newPackages, + foodItems: foodItemUpdates, + customers: customerUpdates + }) + }, + + /** + * Unpacks a packed food package for a customer. + * In essence it undoes the pack() function by deleting the package, adding the package contents + * back to the foodItem counts and updating the customer.lastPacked field. + * req.body.id must be set to the _id of the package to delete + */ + unpack: async function (req, res) { + if (!req.body._id) { + throw new BadRequestError('package _id must be included in the request') + } + if (!mongoose.Types.ObjectId.isValid(req.body._id)) { + throw new BadRequestError(`${req.body._id} is not a valid package _id`) + } + const deletedPackage = await Package.findByIdAndRemove(req.body._id) + + if (!deletedPackage) { + throw new BadRequestError(`package with _id ${req.body._id} not found`) + } + + // Have to update the lastPacked date for the customer by finding the latest datePacked package for that customer + const customersLastPackedPackage = + await Package.findOne({ customer: deletedPackage.customer }, {}, { sort: { 'datePacked': -1 } }).lean() + + const updatedLastPacked = customersLastPackedPackage ? customersLastPackedPackage.datePacked : null + + await Customer.findByIdAndUpdate(deletedPackage.customer, {lastPacked: updatedLastPacked}) + + const updatedItemCounts = [] + + // Add the items from the unpacked package back to the inventory counts + await Promise.all( + deletedPackage.contents.map(itemId => Food.findOneAndUpdate( + {'items._id': itemId}, + {$inc: {'items.$.quantity': 1 }}, + {new: true} + ).then(foodCategory => { + // store the updated quantity for this foodItem in updatedItemCounts + const foodItem = foodCategory.items.find(item => item._id.equals(itemId)) + updatedItemCounts.push({ + _id: foodItem._id, + quantity: foodItem.quantity + }) + })) + ) + + res.json({ + packages: deletedPackage, + foodItems: updatedItemCounts, + customers: { + _id: deletedPackage.customer, + lastPacked: updatedLastPacked + } + }) + + }, + deliver: async function(req, res) { + const {customerIds} = req.body + const customers = await Promise.all( + customerIds.map(async id => + Customer.findByIdAndUpdate(id, + {lastDelivered: beginWeek}, {new: true})) + ) + res.json({customers}) + }, + hasAuthorization(req, res, next) { + const authorizedRoles = [ + ADMIN_ROLE, + volunteerRoles.PACKING + ] + if (req.user && intersection(req.user.roles, authorizedRoles).length) { + return next() + } + + throw new ForbiddenError + } +} + +// Verify a list of customer Ids exist in the database +async function verifyCustomerIds(customerIds) { + const count = await Customer.count({ _id: { $in: customerIds } }) + + if (count !== uniq(customerIds).length) { + throw new BadRequestError('One or more customerIds are not valid') + } + +} + +// Verify a list of foodItem Ids exist in the database +async function verifyFoodItemIds(foodItemIds) { + await Promise.all( + foodItemIds.map(_id => { + if (!mongoose.Types.ObjectId.isValid(_id)) { + throw new BadRequestError(`${_id} is not a valid foodItem _id`) + } + return Food.count({ 'items._id': _id }).then(count => { + if (!count) throw new BadRequestError(`foodItem ${_id} was not found in the database`) + }) + }) + ) +} + +// Takes an array of foodItem ids and increments packedItemCounts[foodItemId] for each item in contents +function addPackageContentsToItemCounts(contents, packedItemCounts) { + contents.forEach(foodItemId => { + if (packedItemCounts[foodItemId]) { + packedItemCounts[foodItemId] = packedItemCounts[foodItemId] + 1 + } else { + packedItemCounts[foodItemId] = 1 + } + }) +} diff --git a/server1/controllers/page.js b/server1/controllers/page.js new file mode 100644 index 00000000..8ea91072 --- /dev/null +++ b/server1/controllers/page.js @@ -0,0 +1,111 @@ +import sanitizeHtml from 'sanitize-html' +import {extend} from 'lodash' +import {writeFileSync, mkdirSync} from 'fs' +import del from 'del' + +import {NotFoundError} from '../lib/errors' +import Page from '../models/page' + +const clientPath = '/media/pages' +const serverPath = process.env.NODE_ENV === 'production' ? + 'dist/client/media/pages' : + 'assets/media/pages' + +try { + mkdirSync(serverPath) +} catch (err) { + if (err.code !== 'EEXIST') throw err +} + +const sanitizeHtmlConfig = { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'span', 'h1', 'h2', 's', 'u']), + allowedAttributes: { + img: ['src', 'width', 'style'], + span: ['style', 'data-id', 'data-label', 'class'], + a: ['href'], + p: ['class'] + } +} + +export default { + async list(req, res) { + const {type} = req.query + const pages = await Page.find(type ? {type} : {}) + res.json(pages) + }, + + async update(req, res) { + const {body, identifier} = req.body + let keepImages = [] + + const sanitizedBody = sanitizeHtml(body, { + ...sanitizeHtmlConfig, + transformTags: { + img: processImageTag(identifier, keepImages) + } + }) + + const page = extend(req.page, {body: sanitizedBody}) + const updatedPage = await page.save() + + // delete stale images + await del([ + `${serverPath}/${identifier}-*`, + ...keepImages.map(img => `!${img}`) + ]) + + res.json(updatedPage) + }, + + async read(req, res) { + res.json(req.page) + }, + + async pageByIdentifier(req, res, next, identifier) { + const page = await Page.findOne({identifier}) + if (!page) throw new NotFoundError + + req.page = page + next() + } +} + +/** + * create a sanitizeHtml transform function to save image data and update 'src' + * attribute to the path of the saved image + * + * @param {string} identifier page identifier + * @param {array} keepImages + * @returns {object} + */ +function processImageTag(identifier, keepImages) { + return (tagName, attribs) => { + const src = attribs.src.startsWith(clientPath) ? + attribs.src : + saveImage(identifier, attribs.src) + + keepImages.push(src.replace(clientPath, serverPath)) + + return { + tagName, + attribs: {...attribs, src} + } + } +} + +/** + * save a base64 image for a given page identifier + * + * @param {string} identifier + * @param {string} src + * @returns {string} updated src attribute + */ +function saveImage(identifier, src) { + // eslint-disable-next-line no-unused-vars + const [_, ext, data] = src.match(/^data:image\/(.+);base64(.+)/) + const filename = `${identifier}-${new Date().getTime()}.${ext}` + + writeFileSync(`${serverPath}/${filename}`, data, 'base64') + + return `${clientPath}/${filename}` +} diff --git a/server1/controllers/questionnaire.js b/server1/controllers/questionnaire.js new file mode 100644 index 00000000..bda6042f --- /dev/null +++ b/server1/controllers/questionnaire.js @@ -0,0 +1,45 @@ +import {extend} from 'lodash' + +import {NotFoundError} from '../lib/errors' +import Questionnaire from '../models/questionnaire' + +export default { +// Create questionnaire + async create(req, res) { + const questionnaire = new Questionnaire(req.body) + const savedQuestionnaire = await questionnaire.save() + + res.json(savedQuestionnaire) + }, + + // Update a questionnaire + async update(req, res) { + const questionnaire = extend(req.questionnaire, req.body) + + const savedQuestionnaire = await questionnaire.save() + + res.json(savedQuestionnaire) + }, + + // Query questionnaires + async query(req, res) { + const questionnaires = await Questionnaire.find() + + res.json(questionnaires) + }, + + // Get specified questionnaire + async get(req, res) { + res.json(req.questionnaire) + }, + + // Questionnaire middleware + async questionnaireById(req, res, next, id) { + const questionnaire = await Questionnaire.findById(id) + + if (!questionnaire) throw new NotFoundError + + req.questionnaire = questionnaire + next() + } +} diff --git a/server1/controllers/settings.js b/server1/controllers/settings.js new file mode 100644 index 00000000..bd11585f --- /dev/null +++ b/server1/controllers/settings.js @@ -0,0 +1,55 @@ +import {includes, intersection} from 'lodash' + +import {ForbiddenError, ValidationError} from '../lib/errors' +import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' +import config from '../config' +import Settings from '../models/settings' + +import {locateAddress} from '../lib/geolocate' + +export default { + async read (req, res) { + const {user} = req + const mapRoles = [ADMIN_ROLE, volunteerRoles.DRIVER] + const projection = user && intersection(user.roles, mapRoles).length ? + '+gmapsApiKey +gmapsClientId ' : '' + + let settings = await Settings.findOne().select(projection).lean() + + // Add property to indicate if google authentication is available + settings.googleAuthentication = !!(config.oauth.googleClientID && config.oauth.googleClientSecret) + + // Remove unnecessary info before sending off the object to the client + delete settings.__v + + res.json(settings) + }, + + async save (req, res) { + const {user} = req + + if (!includes(user.roles, ADMIN_ROLE)) { + throw new ForbiddenError + } + + const location = await locateAddress(req.body.address) + + if (!location) { + throw new ValidationError({address: 'Address not found'}) + } + + const settings = { + ...req.body, + location, + } + + const count = await Settings.count() + + const query = count ? + Settings.findByIdAndUpdate(settings._id, settings, {new: true}) : + Settings.create(settings) + + const savedSettings = await query.select('+gmapsApiKey +gmapsClientId') + res.json(savedSettings) + } +} diff --git a/server1/controllers/users.js b/server1/controllers/users.js new file mode 100644 index 00000000..4a366c8b --- /dev/null +++ b/server1/controllers/users.js @@ -0,0 +1,13 @@ +import {extend} from 'lodash' + +import * as authentication from './users/authentication' +import * as authorization from './users/authorization' +import * as password from './users/password' +import * as profile from './users/profile' + +export default extend( + authentication, + authorization, + password, + profile +) diff --git a/server1/controllers/users/authentication.js b/server1/controllers/users/authentication.js new file mode 100644 index 00000000..1c5fba29 --- /dev/null +++ b/server1/controllers/users/authentication.js @@ -0,0 +1,74 @@ +import {map, pick} from 'lodash' +import passport from 'passport' + +import {UnauthorizedError, ValidationError} from '../../lib/errors' +import User from '../../models/user' + +/** + * Signup + */ +export const signup = async function(req, res) { + // Init Variables + let user = new User({ + ...(pick(req.body, ['firstName', 'lastName', 'email', 'password'])), + roles: [], + provider: 'local', + displayName: `${req.body.firstName} ${req.body.lastName}` + }) + + try { + await user.save() + } catch (error) { + // Check for unique key violation from email + if (error.code === 11000 && error.errmsg.match('email')) { + throw new ValidationError({email: 'Email address already has an account'}) + } else if (error.name === 'ValidationError') { + const errors = Object.assign( + {}, + ...map(error.errors, (v, k) => ({[k]: v.message})) + ) + + throw new ValidationError(errors) + } else { + throw error + } + } + + user.password = undefined + user.salt = undefined + + req.login(user, err => { + if (err) throw new UnauthorizedError + }) + return res.json(user) +} + +/** + * Signin after passport authentication + */ +export const signin = function(req, res, next) { + passport.authenticate('local', function(err, user, info) { + if (err || !user) { + res.status(400).send(info) + } else { + // Remove sensitive data before login + user.password = undefined + user.salt = undefined + + req.login(user, function(err) { + if (err) throw new UnauthorizedError + return res.json(user) + }) + } + })(req, res, next) +} + +/** + * Signout + */ +export const signout = function(req, res) { + req.logout() + req.session.destroy(function() { + res.redirect('/') + }) +} diff --git a/server1/controllers/users/authorization.js b/server1/controllers/users/authorization.js new file mode 100644 index 00000000..c2c16ac6 --- /dev/null +++ b/server1/controllers/users/authorization.js @@ -0,0 +1,40 @@ +import intersection from 'lodash/intersection' + +import {ForbiddenError, NotFoundError, UnauthorizedError} from '../../lib/errors' +import User from '../../models/user' + +/** + * User middleware + */ +export const userByID = async function(req, res, next, id) { + const user = User.findById(id) + if (!user) throw new NotFoundError + + req.profile = user + next() +} + +/** + * Require login routing middleware + */ +export const requiresLogin = function(req, res, next) { + if (!req.isAuthenticated()) { + throw new UnauthorizedError + } + next() +} + +/** + * User authorizations routing middleware + */ +export const hasAuthorization = function(roles) { + return (req, res, next) => { + this.requiresLogin(req, res, function() { + if (!intersection(req.user.roles, roles).length) { + throw new ForbiddenError + } + + next() + }) + } +} diff --git a/server1/controllers/users/password.js b/server1/controllers/users/password.js new file mode 100644 index 00000000..63ed0457 --- /dev/null +++ b/server1/controllers/users/password.js @@ -0,0 +1,88 @@ +import crypto from 'crypto' +import thenify from 'thenify' + +import config from '../../config' +import { + BadRequestError, + NotFoundError, + UnauthorizedError + // ValidationError +} from '../../lib/errors' +import mailer from '../../lib/mail/mail-helpers' +import User from '../../models/user' + +const randomBytes = thenify(crypto.randomBytes) + +/** + * Forgot password + */ +export const forgot = async function(req, res) { + if (!config.sendgrid.API_KEY) { + return res.status(500).json({message: "This site does not have email capability at this time. Please contact the site owner for assistance."}) + } + + const token = (await randomBytes(20)).toString('hex') + + // Lookup user by email + if (!req.body.email) throw new BadRequestError('Email is required') + const user = await User.findOne({email: req.body.email}).select('-salt -password') + if (user && user.provider !== 'local') { + if (user.provider === 'google') await mailer.sendPasswordGoogle(user) + } else if (user && user.provider === 'local') { + user.resetPasswordToken = token + user.resetPasswordExpires = Date.now() + 3600000 // 1 hour + + const port = process.env.NODE_ENV === 'production' ? '' : `:${config.port}` + const url = `${config.protocol}://${config.host}${port}/users/reset-password/${token}` + + await mailer.sendPasswordReset(user, url) + await user.save() + } + + res.send({message: 'Password reset email sent'}) +} + +/** + * Reset password POST from email token + */ +export const reset = async function (req, res) { + const user = await User.findOne({ + resetPasswordToken: req.params.token, + resetPasswordExpires: {$gt: Date.now()} + }) + + if (!user) { + throw new BadRequestError('Password reset token is invalid or has expired.') + } + + user.password = req.body.password + user.resetPasswordToken = undefined + user.resetPasswordExpires = undefined + + await user.save() + res.json({message: "Password successfully reset"}) +} + +/** + * Change Password + */ +export const changePassword = async function(req, res) { + const {newPassword, currentPassword, verifyPassword} = req.body.passwordDetails + + if (!req.user || !newPassword || !currentPassword || newPassword !== verifyPassword) { + throw new BadRequestError + } + + const user = await User.findById(req.user.id).select('+salt +password') + if (!user) throw new NotFoundError + if (!user.authenticate(currentPassword)) + throw new BadRequestError('Password is incorrect') + + user.password = newPassword + await user.save() + + req.login(user, function(err) { + if (err) throw new UnauthorizedError + return res.send({message: 'Password changed successfully'}) + }) +} diff --git a/server1/controllers/users/profile.js b/server1/controllers/users/profile.js new file mode 100644 index 00000000..33428618 --- /dev/null +++ b/server1/controllers/users/profile.js @@ -0,0 +1,134 @@ +import {extend, includes, omit, pick} from 'lodash' + +import {ADMIN_ROLE} from '../../../common/constants' +import {BadRequestError} from '../../lib/errors' +import User from '../../models/user' + +/** + * List users + */ +export const list = async function(req, res) { + const users = await User.find({}).lean() + res.json(users) +} + +/** + * Get a user by userId + */ +export const getById = async function(req, res) { + if (!req.params.userId.match(/^\d+$/)){ + throw new BadRequestError(`UserId ${req.params.userId} is not valid`) + } + const user = await User.findById(req.params.userId).lean() + if (user) { + res.json(user) + } else { + throw new + BadRequestError(`UserId ${req.params.userId} not found`) + } +} + +/** + * Updating own profile + */ +export const updateProfile = async function(req, res) { + const user = req.user + extend(user, pick(req.body, ['firstName', 'lastName', 'email'])) + + if (!user.firstName || !user.lastName) throw new BadRequestError('First Name and Last Name are required') + + user.updated = Date.now() + user.displayName = user.firstName + ' ' + user.lastName + + const sameEmail = await User.findOne({email: user.email}).lean() + if (sameEmail && sameEmail._id !== user._id) + throw new BadRequestError('Email address is taken') + + await user.save() + res.json(user) +} + +/** + * Updating own notifications profile + */ +export const updateNotifications = async function(req, res) { + const user = req.user + extend(user, pick(req.body, ['notifications'])) + + user.updated = Date.now() + + const sameEmail = await User.findOne({email: user.email}).lean() + if (sameEmail && sameEmail._id !== user._id) + throw new BadRequestError('Email address is taken') + + await user.save() + res.json(user) +} + +/** + * Update user details + */ +export const update = async function(req, res) { + // TODO: why doesn't `req.profile` have a save method? + const user = await User.findById(req.params.userId) + + extend(user, omit(req.body, ['_id', 'isAdmin'])) + + if (!user.firstName || !user.lastName) throw new BadRequestError('First Name and Last Name are required') + + user.updated = Date.now() + user.displayName = user.firstName + ' ' + user.lastName + + const sameEmail = await User.findOne({email: user.email}).lean() + if (sameEmail && sameEmail._id !== user._id) + throw new BadRequestError('Email address is taken') + + // Update admin status + const previouslyAdmin = includes(user.roles, ADMIN_ROLE) + const currentlyAdmin = req.body.isAdmin + + if (currentlyAdmin && !previouslyAdmin) { + user.roles.push(ADMIN_ROLE) + } else if (!currentlyAdmin && previouslyAdmin) { + if (user._id === req.user._id) + throw new BadRequestError('You are not allowed to demote yourself') + + user.roles.splice(user.roles.indexOf(ADMIN_ROLE), 1) + } + + await user.save() + res.json(user) +} + +/** + * Send User + */ +export const me = async function(req, res) { + if (!req.session.passport) return res.json(null) + + const user = await User.findById(req.session.passport.user) + return res.json(user) +} + +/** + * List own notifications profile + */ +export const listNotifications = async function(req, res) { + const user = req.user + res.json(user.notifications) +} + +/** + * Remove own notification profile + */ +export const removeNotification = async function(req, res) { + //console.log('removeNotification') + const id = req.originalUrl.split('?id=')[1] + const user = req.user + if (id !== null && id !== undefined && id !== "") user.notifications.splice(id,1) + else if (id === null || id === undefined || id === "") user.notifications = [] + User.findOneAndUpdate({ '_id': user._id }, + { 'notifications': user.notifications }) + .exec() + res.json(user.notifications) +} diff --git a/server1/controllers/volunteer.js b/server1/controllers/volunteer.js new file mode 100644 index 00000000..d4e8fe41 --- /dev/null +++ b/server1/controllers/volunteer.js @@ -0,0 +1,194 @@ +import {difference, extend, omit} from 'lodash' + +import {ForbiddenError, NotFoundError} from '../lib/errors' +import {ADMIN_ROLE, clientRoles} from '../../common/constants' +import User from '../models/user' +import Volunteer from '../models/volunteer' +import {updateFields} from '../lib/update-linked-fields' + +export default { + /** + * Create a volunteer + */ + async create(req, res) { + let volunteer = new Volunteer(omit(req.body, ['status', 'customers'])) + volunteer._id = req.user.id + volunteer.user = req.user.id + + await User.findOneAndUpdate( + {_id: volunteer._id}, + {$push: {roles: clientRoles.VOLUNTEER}} + ) + + const savedVolunteer = await volunteer.save() + updateFields(clientRoles.VOLUNTEER,req.body.fields,volunteer._id) + res.json(savedVolunteer) + }, + + /* + * Adds a Volunteer shift + */ + async addShift(req, res) { + + //const volunteer = extend(req.volunteer, req.body) + const new_volunteer = req.body + const shift = new_volunteer.shift + + const old_volunteer = await Volunteer.findById(new_volunteer._id) + const shifts = old_volunteer.shift + shifts.push(shift) + + const updatedVolunteer = await Volunteer.findOneAndUpdate( + { _id: new_volunteer._id}, + { $set: { shift: shifts}}) + + res.json(updatedVolunteer) + }, + + /* + * Deletes a volunteer shift + */ + async deleteShift(req, res) { + const new_volunteer = req.body + const del_shift = new_volunteer.del_shift + + const old_volunteer = await Volunteer.findById(new_volunteer._id) + const shifts = old_volunteer.shift + + var del_time = new Date(del_shift.start) + var new_shifts = [] + + for(var i = 0; i < shifts.length; i++) { + var new_time = new Date(shifts[i].date) + if(del_time.getTime() === new_time.getTime()) { + continue + } + else { + new_shifts.push(shifts[i]) + } + } + + const updatedVolunteer = await Volunteer.findOneAndUpdate( + { _id: new_volunteer._id}, + { $set: {shift: new_shifts}}) + + res.json(updatedVolunteer) + }, + + /* + * Updates a Volunteer shift + */ + async updateShift(req, res) { + const new_volunteer = req.body + const times = new_volunteer.times + + const old_volunteer = await Volunteer.findById(new_volunteer._id) + const shifts = old_volunteer.shift + + var old_date = new Date(times.oldTime) + + for(var i = 0; i < shifts.length; i++) { + var new_time = new Date(shifts[i].date) + if(old_date.getTime() === new_time.getTime()) { + shifts[i].date = times.newTime + } + } + + const updatedVolunteer = await Volunteer.findOneAndUpdate( + { _id: new_volunteer._id}, + { $set: {shift: shifts}}) + + res.json(updatedVolunteer) + }, + + /** + * Show the current volunteer + */ + async read(req, res) { + if (!req.user.roles.find(r => r === ADMIN_ROLE)) + return res.json(req.volunteer) + + const {roles} = await User.findById(req.volunteer._id).lean() + + res.json({ + ...req.volunteer.toObject(), + roles + }) + }, + + /** + * Update a volunteer + */ + async update(req, res) { + const volunteer = extend(req.volunteer, req.body) + + const user = await User.findById(volunteer._id).lean() + const newVolunteer = await volunteer.save() + //Volunteer.findByIdAndUpdate({ _id: 10017}, { $set : { lastName: "hi"}}) + + if (!volunteer.roles || !req.user.roles.find(r => r === ADMIN_ROLE)) { + updateFields(clientRoles.VOLUNTEER,req.body.fields,volunteer._id) + return res.json(newVolunteer) + } + const oldRoles = difference(user.roles, volunteer.roles) + const newRoles = difference(volunteer.roles, user.roles) + const roles = difference(user.roles.concat(newRoles), oldRoles) + + if (newRoles.length || oldRoles.length){ + await User.findByIdAndUpdate(volunteer._id, {$set: {roles}}) + } + updateFields(clientRoles.VOLUNTEER,req.body.fields,volunteer._id) + res.json({ + ...newVolunteer.toObject(), + roles + }) + }, + + /** + * List of volunteers + */ + async list(req, res) { + const volunteers = await Volunteer.find() + .sort('-dateReceived') + .populate('user', 'roles') + + res.json(volunteers) + }, + + /** + * Delete volunteer + */ + async delete(req, res) { + const id = req.volunteer._id + + const volunteer = await Volunteer.findByIdAndRemove(id) + await User.findByIdAndRemove(id) + + res.json(volunteer) + }, + + /** + * Volunteer middleware + */ + async volunteerById(req, res, next, id) { + const volunteer = await Volunteer.findById(id) + .populate('customers') + + if (!volunteer) throw new NotFoundError + + req.volunteer = volunteer + next() + }, + + /** + * Volunteer authorization middleware + */ + async hasAuthorization(req, res, next) { + if (!req.user.roles.find(r => r === ADMIN_ROLE) && + req.volunteer._id !== +req.user.id) { + throw new ForbiddenError + } + next() + } + +} diff --git a/server1/entry.test.js b/server1/entry.test.js new file mode 100644 index 00000000..765dd1e2 --- /dev/null +++ b/server1/entry.test.js @@ -0,0 +1,30 @@ +import mongoose from 'mongoose' +import chai from 'chai' +import sinon from 'sinon' +import sinonChai from 'sinon-chai' +import chaiAsPromised from 'chai-as-promised' +import supertest from 'supertest' + +import config from './config' +global.expect = chai.expect +global.sinon = sinon +global.supertest = supertest + +chai.use(sinonChai) +chai.use(chaiAsPromised) + +mongoose.Promise = global.Promise + +global.initDb = async function() { + if (!mongoose.connection.readyState) { + await mongoose.connect(config.db) + } +} + +global.resetDb = async function() { + mongoose.models = {} + mongoose.modelSchemas = {} + if (mongoose.connection.readyState) { + await mongoose.disconnect() + } +} diff --git a/server1/index.js b/server1/index.js new file mode 100644 index 00000000..04171d26 --- /dev/null +++ b/server1/index.js @@ -0,0 +1,2 @@ +require('babel-register') +require('./server') diff --git a/server1/lib/enforce-ssl-middleware.js b/server1/lib/enforce-ssl-middleware.js new file mode 100644 index 00000000..06abda51 --- /dev/null +++ b/server1/lib/enforce-ssl-middleware.js @@ -0,0 +1,12 @@ +import {BadRequestError} from './errors' + +export default function(req, res, next) { + if (req.headers['x-forwarded-proto'] !== 'https') { + if (req.method === 'GET') { + return res.redirect(301, `https://${req.hostname}${req.url}`) + } else { + throw new BadRequestError + } + } + next() +} diff --git a/server1/lib/errors.js b/server1/lib/errors.js new file mode 100644 index 00000000..089b702d --- /dev/null +++ b/server1/lib/errors.js @@ -0,0 +1,92 @@ +export class HttpError extends Error { + constructor() { + super() + } +} + +export class BadRequestError extends HttpError { + constructor(message) { + super() + this.name = this.constructor.name + this.message = message || 'Invalid request' + this.status = 400 + } +} + +export class UnauthorizedError extends HttpError { + constructor(message) { + super() + this.name = this.constructor.name + this.message = message || 'Unauthorized' + this.status = 401 + } +} + +export class ForbiddenError extends HttpError { + constructor(message) { + super() + this.name = this.constructor.name + this.message = message || 'User is not authorized' + this.status = 403 + } +} + +export class NotFoundError extends HttpError { + constructor(message) { + super() + this.name = this.constructor.name + this.message = message || 'Not found' + this.status = 404 + } +} + +export class ValidationError extends BadRequestError { + constructor(paths, message) { + super(message || 'Validation error') + this.name = this.constructor.name + this.paths = paths + } +} + +export class SimulatedError extends Error { + constructor(message) { + super() + Error.captureStackTrace(this, this.constructor) + this.name = this.constructor.name + this.message = message || 'Simulated error' + this.status = 500 + } +} + +const defaultResponse = { + message: 'Something went wrong on the server', + status: 500 +} + +export default function getErrorMessage(err) { + if (err.code && err.code === 11000 || err.code === 11001) { + return { + message: 'Unique field already exists', + status: 400 + } + } + + if (err instanceof HttpError) { + return { + message: err.message, + status: err.status, + paths: err.paths + } + } + + //This catches when body-parser encounters bad JSON user data in a request + if(err.stack.match(/^SyntaxError:.+in JSON(.|\n)*node_modules\/body-parser/)) { + return { + message: (process.env.NODE_ENV === 'production') ? 'The data received by the server is not properly formatted. Try refreshing your browser.' + : `Bad JSON in HTTP request. ${err.message}: ${err.body}`, + status: 400 + } + } + + return defaultResponse +} diff --git a/server1/lib/geolocate.js b/server1/lib/geolocate.js new file mode 100644 index 00000000..135e1d1f --- /dev/null +++ b/server1/lib/geolocate.js @@ -0,0 +1,39 @@ +import keys from '../config/index' +import nodeGeocoder from 'node-geocoder' +import {fieldTypes} from '../../common/constants' +import {getFieldsByType} from '../lib/questionnaire-helpers' +import {ValidationError} from '../lib/errors' + +const geocoder = nodeGeocoder({ + provider: 'google', + apiKey: keys.gmapsApiKey, + formatter: null +}) + +export async function locateAddress(address) { + const [result] = await geocoder.geocode(address) + if (!result) return + + const {latitude, longitude} = result + return {lat: latitude, lng: longitude} +} + +export async function locateQuestionnaire(fields, identifier) { + if (process.env.NODE_ENV === 'test') return + + const addressFields = await getFieldsByType( + identifier, fields, fieldTypes.ADDRESS) + + const address = addressFields.map(field => field.value).join(', ') + + const location = await locateAddress(address) + + // Mark all address fields as invalid, add error text to first field + if (!location) return new ValidationError({ + fields: Object.assign({}, ...addressFields.map((field, i) => ({ + [field.meta]: i === 0 ? 'Address not found' : ' '} + ))) + }) + + return location +} diff --git a/server1/lib/mail/email-template.html b/server1/lib/mail/email-template.html new file mode 100644 index 00000000..8c0f2ee8 --- /dev/null +++ b/server1/lib/mail/email-template.html @@ -0,0 +1,87 @@ + + + + + {subject} + + + + + + +
+ + {content} +
+
+ + diff --git a/server1/lib/mail/html-transform.js b/server1/lib/mail/html-transform.js new file mode 100644 index 00000000..1942eb69 --- /dev/null +++ b/server1/lib/mail/html-transform.js @@ -0,0 +1,116 @@ +import {parseFragment, parse, serialize} from 'parse5' +import {trim} from 'lodash' + +/** + * @export + * @param {string} html + * @param {Object} options + * @returns string + */ +export default function transformHtml(html, { + replaceTags = {}, + omitTags = [], + fragment = true +} = {}) { + const node = fragment ? parseFragment(html) : parse(html) + const transformed = transform(node) + + return serialize(transformed) + + function transform(node) { + if (!node.childNodes) return node + + return { + ...node, + childNodes: node.childNodes.reduce(reduceChildren, []) + } + } + + function reduceChildren(acc, node) { + const shouldOmit = omitTags.find(tag => tag === node.nodeName) + const empty = node.value && !trim(node.value) + if (shouldOmit || empty) return acc + return acc.concat(replace(node)) + } + + function replace(node) { + const replacement = replaceTags[node.nodeName] + const replaceAll = replaceTags['*'] + + let childNodes = node.childNodes + if (childNodes) { + childNodes = replaceAll ? childNodes.reduce(replaceAll, []) : childNodes + childNodes = childNodes.reduce(reduceChildren, []) + } + + if (typeof replacement === 'string') { + return [h('#text', replacement), ...childNodes] + } + + if (Array.isArray(replacement)) { + const leading = replacement[0] + const trailing = replacement[1] || '' + return [h('#text', leading), ...childNodes, h('#text', trailing)] + } + + if (typeof replacement === 'function') { + return replacement({...node, childNodes}) + } + + return {...node, childNodes} + } +} + +// utils + +export function getAttr(node, name) { + const attr = node.attrs.find(attr => attr.name === name) + return attr && attr.value +} + +export function hasClass(node, className) { + const classes = getAttr(node, 'class') + return classes && classes.split(' ').find(name => name === className) +} + +/** + * Node factory, usage: + * + * h(htmlString) + * + * h(node) + * + * h('#text', textString) + * + * h(tagName, attrs, children) + * + * @param {[string]|[object]|[string, string]|[string, [object], [object]]} args + * @returns {object} + */ +export function h(...args) { + if (args.length === 1) { + const [node] = typeof args[0] === 'string' ? + parseFragment(args[0]).childNodes : + args + + return h(node.tagName, node.attrs, node.childNodes) + } + + if (args.length === 2) { + const [type, value] = args + + return { + nodeName: type, + value: value || '' + } + } + + const [type, attrs, childNodes] = args + + return { + nodeName: type, + tagName: type, + attrs, + childNodes + } +} diff --git a/server1/lib/mail/html-transform.spec.js b/server1/lib/mail/html-transform.spec.js new file mode 100644 index 00000000..f29ef5e7 --- /dev/null +++ b/server1/lib/mail/html-transform.spec.js @@ -0,0 +1,86 @@ +import transformHtml, {h, getAttr, hasClass} from './html-transform' + +describe('Html transform', function() { + it('does nothing', function() { + const html = '

Something

' + + const res = transformHtml(html) + expect(res).to.equal(html) + }) + + it('omits tags and their contents', function() { + const input = '

Title

Text

' + const output = '

Text

' + + const res = transformHtml(input, {omitTags: ['h1']}) + expect(res).to.equal(output) + }) + + it('replaces tags with a string', function() { + const input = '

A paragraphNested span

' + const output = '
\nA paragraphNested span
' + const replaceTags = {p: '\n'} + + const res = transformHtml(input, {replaceTags}) + expect(res).to.equal(output) + }) + + it('replaces tags with an array', function() { + const input = '

A paragraphNested span

' + const output = '
\nA paragraphNested span\n
' + const replaceTags = {p: ['\n', '\n']} + + const res = transformHtml(input, {replaceTags}) + expect(res).to.equal(output) + }) + + it('replaces tags with a function', function() { + const input = '

A paragraphNested span

' + const output = '
A paragraphNested span
' + const replaceTags = { + p: node => h('div', [], node.childNodes) + } + + const res = transformHtml(input, {replaceTags}) + expect(res).to.equal(output) + }) + + it('runs a custom reducer on all tags', function() { + const input = '' + const output = '
[foo.com] Link
' + const replaceTags = {'*': stringifyLinks} + + const res = transformHtml(input, {replaceTags}) + expect(res).to.equal(output) + }) + + it('handles complex cases', function() { + const input = '

Some text with a Link

' + const output = '
Some text with a [foo.com] Link
' + + function replaceParagraph(node) { + const tdAttrs = hasClass(node, 'text-center') ? + [{name: 'align', value: 'center'}] : [] + + return h('tr', [], [ + h('td', tdAttrs, node.childNodes) + ]) + } + + const replaceTags = { + p: replaceParagraph, + '*': stringifyLinks + } + + const res = transformHtml(input, {replaceTags}) + expect(res).to.equal(output) + }) +}) + +function stringifyLinks(acc, node) { + if (node.tagName !== 'a') return acc.concat(node) + return acc.concat([ + h('#text', `[${getAttr(node, 'href')}] `), + ...node.childNodes + ]) +} diff --git a/server1/lib/mail/mail-generator.js b/server1/lib/mail/mail-generator.js new file mode 100644 index 00000000..548e1a66 --- /dev/null +++ b/server1/lib/mail/mail-generator.js @@ -0,0 +1,146 @@ +import juice from 'juice' +import {readFileSync} from 'fs' +import {resolve} from 'path' +import striptags from 'striptags' +import transformHtml, {h, getAttr, hasClass} from './html-transform' +import {trim, uniq, isUndefined} from 'lodash' + +import config from '../../config' +import Page from '../../models/page' +import placeholders, {placeholderTypes} from '../../../common/placeholders' + +const templatePath = resolve(__dirname, 'email-template.html') +const template = readFileSync(templatePath).toString() + +/** + * bind placeholders, insert mail into template and inline styles + * + * @export + * @param {string} identifier + * @param {object} bindings + * @returns + */ +export default async function generate(identifier, bindings) { + const page = await Page.findOne({identifier}).lean() + if (!page || page.disabled) return + + let attachments = [] + const body = transformBody(page.body, bindings, attachments) + const subject = transformSubject(page.subject, bindings) + const text = transformText(body) + const html = template.replace(/\{content\}/, body) + .replace(/\{subject\}/g, subject) + + return { + html: juice(html), + text, + subject, + attachments: uniq(attachments) + } +} + +function transformText(body) { + const replaceTags = { + td: '\n\n', + img: replaceWithAltText, + '*': stringifyLinks + } + + return trim(striptags(transformHtml(body, {replaceTags}))) +} + +function transformBody(body, bindings, attachments) { + const replaceTags = { + span: node => isPlaceholder(node) ? + bindPlaceholder(node, bindings, attachments) : + node, + img: setFullUrl, + p: replaceWithTrTd, + blockquote: nestInTrTd, + h1: nestInTrTd, + h2: nestInTrTd, + h3: nestInTrTd + } + + return transformHtml(body, {replaceTags}) +} + +function transformSubject(subject, bindings) { + const replaceTags = { + span: node => isPlaceholder(node) ? bindPlaceholder(node, bindings) : node + } + + return striptags(transformHtml(subject, {replaceTags})) +} + +function bindPlaceholder(node, bindings, attachments) { + + const id = getAttr(node, 'data-id') + const placeholder = placeholders.find(pl => pl.id === id) + + //Receipt Placeholder only + if (id === 'receipt') { + return h(placeholder.format(bindings)) + } + + if (!placeholder) throw new Error('Invalid placeholder', id) + + if (isUndefined(bindings[id]) && placeholder.type !== placeholderTypes.ATTACHMENT) + throw new Error('Missing binding for placeholder', id) + + if (Array.isArray(attachments) && placeholder.type === placeholderTypes.ATTACHMENT) + attachments.push(placeholder.id) + + return typeof placeholder.format === 'function' ? + h(placeholder.format(bindings[id])) : + h('#text', bindings[id]) +} + +function isPlaceholder(node) { + return node.nodeName === 'span' && hasClass(node, 'ql-placeholder-content') +} + +function stringifyLinks(acc, node) { + if (node.tagName !== 'a') return acc.concat(node) + return acc.concat([ + ...node.childNodes, + h('#text', `\n[${getAttr(node, 'href')}]\n`), + ]) +} + +function replaceWithAltText(node) { + const text = getAttr(node, 'alt') + return h('#text', text ? `\n[${text}]\n` : '') +} + +function replaceWithTrTd(node) { + return h('tr', [], [ + h('td', node.attrs, node.childNodes) + ]) +} + +function nestInTrTd(node) { + return h('tr', [], [ + h('td', [], [ + h(node) + ]) + ]) +} + +function setFullUrl(node) { + const port = process.env.NODE_ENV === 'production' ? '' : ':8080' + const baseUrl = `${config.protocol}://${config.host}${port}` + + const src = getAttr(node, 'src') + if (!src.startsWith('/')) return node + + const attrs = [ + ...node.attrs.filter(attr => attr.name !== 'src'), + {name: 'src', value: `${baseUrl}${src}`} + ] + + return { + ...node, + attrs + } +} diff --git a/server1/lib/mail/mail-generator.spec.js b/server1/lib/mail/mail-generator.spec.js new file mode 100644 index 00000000..fd13b5c1 --- /dev/null +++ b/server1/lib/mail/mail-generator.spec.js @@ -0,0 +1,101 @@ +import generate from '../../lib/mail/mail-generator' + +describe('Mail generator', function() { + let pageMock = { + findOne: sinon.stub().returnsThis() + } + + before(function() { + generate.__Rewire__('template', '{content}') + }) + + afterEach(function() { + generate.__ResetDependency__('Page') + }) + + after(function() { + generate.__ResetDependency__('template') + }) + + it('binds placeholders', async function() { + const page = { + body: 'From ', + subject: '' + } + + pageMock.lean = sinon.stub().returns(page) + generate.__Rewire__('Page', pageMock) + + const bindings = {organization: 'foodbank template'} + const result = await generate('', bindings) + + expect(result.html).to.equal(`From ${bindings.organization}`) + }) + + it('binds html placeholders', async function() { + const page = { + body: '', + subject: '' + } + + pageMock.lean = sinon.stub().returns(page) + generate.__Rewire__('Page', pageMock) + + const bindings = {passwordResetLink: 'example.com/reset'} + const result = await generate('', bindings) + + expect(result.html).to.equal(`Reset Password`) + }) + + it('binds subject placeholders', async function() { + const page = { + body: '

', + subject: '

email

' + } + + pageMock.lean = sinon.stub().returns(page) + generate.__Rewire__('Page', pageMock) + + const bindings = {organization: 'foodbank template'} + const result = await generate('', bindings) + + expect(result.subject).to.equal(`${bindings.organization} email`) + }) + + it('replaces tags with email suitable tags', async function() { + const page = { + body: '

Centered

', + subject: '' + } + + pageMock.lean = sinon.stub().returns(page) + generate.__Rewire__('Page', pageMock) + + const result = await generate('', {}) + expect(result.html).to.equal('
Centered
') + }) + + it('generates plain text version', async function() { + const page = { + body: ` + + + + +
+

+

Another paragraph

+

Linklogo

+
+ `, + subject: '' + } + + pageMock.lean = sinon.stub().returns(page) + generate.__Rewire__('Page', pageMock) + + const bindings = {organization: 'foodbank template'} + const result = await generate('', bindings) + expect(result.text).to.equal(`${bindings.organization}\n\nAnother paragraph\n\nLink\n[foo.com]\n\n[logo]`) + }) +}) diff --git a/server1/lib/mail/mail-helpers.js b/server1/lib/mail/mail-helpers.js new file mode 100644 index 00000000..83192184 --- /dev/null +++ b/server1/lib/mail/mail-helpers.js @@ -0,0 +1,123 @@ +import {pageIdentifiers} from '../../../common/constants' +import mailGenerator from './mail-generator' +import sendEmail from '../../config/mailer' +import Settings from '../../models/settings' + +export default { + /** + * Send an email + * + * @param {string} toEmail + * @param {string} toName + * @param {string} identifier + * @param {object} bindings + */ + async send(toEmail, toName, identifier, bindings = null) { + const settings = await Settings.findOne().lean() + const mail = await mailGenerator(identifier, {...settings, ...bindings}) + if (!mail) return + + try { + await sendEmail(toEmail, toName, mail) + } catch (err) { + handleError(err) + } + }, + + /** + * Send an email for account status update + * + * @param {object} customer + */ + async sendStatus(customer) { + const {firstName, lastName, fullName, email} = customer + const date = customer.dateReceived.toDateString() + + if (customer.status === 'Accepted') { + await this.send( + email, + fullName, + pageIdentifiers.CUSTOMER_ACCEPTED, + {fullName, date} + ) + } else if (customer.status === 'Rejected'){ + await this.send( + email, + fullName, + pageIdentifiers.CUSTOMER_REJECTED, + {firstName, lastName, fullName, date} + ) + } + }, + + /** + * Send an email for account update + * + * @param {object} customer + */ + async sendUpdate(customer) { + const {firstName, lastName, fullName, email, id} = customer + await this.send( + email, + fullName, + pageIdentifiers.CUSTOMER_UPDATED, + {firstName, lastName, fullName, id} + ) + }, + + async sendPasswordReset(user, passwordResetLink) { + const {firstName, lastName, email} = user + const fullName = `${firstName} ${lastName}` + await this.send( + email, + fullName, + pageIdentifiers.PASSWORD_RESET, + {firstName, lastName, fullName, passwordResetLink} + ) + }, + + async sendPasswordGoogle(user) { + const {firstName, lastName, email} = user + const fullName = `${firstName} ${lastName}` + await this.send( + email, + fullName, + pageIdentifiers.PASSWORD_RESET_GOOGLE, + {firstName, lastName, fullName} + ) + }, + + async sendThanks(donation) { + const {donor} = donation + const {firstName, lastName, email} = donor + const fullName = `${firstName} ${lastName}` + await this.send( + email, + fullName, + pageIdentifiers.DONATION_RECEIVED, + {firstName, lastName, fullName} + ) + }, + + async sendReceipt(donation) { + const {donor, items} = donation + const {firstName, lastName, email} = donor + const fullName = `${firstName} ${lastName}` + await this.send( + email, + fullName, + pageIdentifiers.DONATION_RECEIPT, + {firstName, lastName, fullName, items} + ) + } +} + +function handleError(err) { + // suppress errors during testing + if (process.env.NODE_ENV === 'test') return + + console.error('email error', err) + console.error(err.response.body.errors) + + // throw err +} diff --git a/server1/lib/mapquest-client.js b/server1/lib/mapquest-client.js new file mode 100644 index 00000000..7a64c037 --- /dev/null +++ b/server1/lib/mapquest-client.js @@ -0,0 +1,34 @@ +import fetch from 'isomorphic-fetch' +import polyline from '@mapbox/polyline' + +import config from '../config' + +const baseUrl = 'http://open.mapquestapi.com/directions/v2' +const directionsUrl = `${baseUrl}/route?key=${config.mapquestKey}` +const optimizeUrl = `${baseUrl}/optimizedroute?key=${config.mapquestKey}` + +const defaultOptions = { + shapeFormat: 'cmp', // return polyline + generalize: 0 // max detail +} + +export const getDirections = (waypoints, options) => { + const body = JSON.stringify({ + locations: polylineToLatLngStrings(waypoints), + options: defaultOptions + }) + + const url = options.optimize ? optimizeUrl : directionsUrl + return fetch(url, { + method: 'POST', + body + }).then(res => res.json().then(json => ({ + geometry: json.route.shape.shapePoints, + waypoints: json.route.locationSequence, + legs: json.route.legs + }))) +} + +function polylineToLatLngStrings(line) { + return polyline.decode(line).map(points => points.join(',')) +} diff --git a/server1/lib/media-helpers.js b/server1/lib/media-helpers.js new file mode 100644 index 00000000..316d23af --- /dev/null +++ b/server1/lib/media-helpers.js @@ -0,0 +1,34 @@ +import fs from 'fs' +import path from 'path' +import crypto from 'crypto' +import multer from 'multer' +import {last} from 'lodash' + +const mediaRoot = process.env.NODE_ENV === 'production' ? 'dist/client/media' : 'assets/media' + +const getPath = filename => { + return path.resolve(`${mediaRoot}/${filename}`) +} + +const generateUniqueFilename = filename => { + const ext = last(filename.split('.')) + const raw = crypto.pseudoRandomBytes(16) + return `${raw.toString('hex')}.${ext}` +} + +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, `${mediaRoot}`) + }, + filename: (req, file, cb) => { + const filename = generateUniqueFilename(file.originalname) + cb(null, filename) + } +}) + +export const upload = multer({storage}) + +export const deleteFile = filename => { + return fs.unlink(getPath(filename)) +} + diff --git a/server1/lib/media-helpers.spec.js b/server1/lib/media-helpers.spec.js new file mode 100644 index 00000000..e26cd216 --- /dev/null +++ b/server1/lib/media-helpers.spec.js @@ -0,0 +1,54 @@ +import { + default as mediaHelpers +} from './media-helpers.js' + + +describe('Media helpers', function() { + context('node env = production', function() { + const mediaRoot = 'dist/client/media' + + beforeEach(function() { + mediaHelpers.__Rewire__('mediaRoot', mediaRoot) + }) + + afterEach(function() { + mediaHelpers.__ResetDependency__('mediaRoot') + }) + + describe('getPath', function() { + const getPath = mediaHelpers.__GetDependency__('getPath') + + it('should return pathname of production environment of type string given a valid filename', function() { + const filename = 'testfile' + const expected = mediaRoot + '/' + filename + const actual = getPath(filename) + + expect(actual).to.contain(expected) + }) + }) + }) + + context('node env = dev', function() { + const mediaRoot = 'assets/media' + + beforeEach(function() { + mediaHelpers.__Rewire__('mediaRoot', mediaRoot) + }) + + afterEach(function() { + mediaHelpers.__ResetDependency__('mediaRoot') + }) + + describe('getPath', function() { + const getPath = mediaHelpers.__GetDependency__('getPath') + + it('should return pathname of dev environment', function() { + const filename = 'testfile' + const expected = mediaRoot + '/' + filename + const actual = getPath(filename) + + expect(actual).to.contain(expected) + }) + }) + }) +}) \ No newline at end of file diff --git a/server1/lib/notification-sender.js b/server1/lib/notification-sender.js new file mode 100644 index 00000000..b75282f6 --- /dev/null +++ b/server1/lib/notification-sender.js @@ -0,0 +1,46 @@ + +import User from '../models/user' +import Volunteer from '../models/volunteer' + +/** + * Set Notification + */ +// let notification = {message:'', url: '', date: date} +async function setNotification(id, notification, model){ + await model.findOneAndUpdate( + {_id: id}, + {$push: {notifications: notification}} + ) +} + +/** + * Search User and Set Notification to admin role + */ +export async function searchUserAndSetNotification(roleU, notification) { + const users = await User.find({}) + + users.forEach(function(user) { + user.roles.map(role => { + if (role === roleU){ + setNotification(user._id, notification, User) + } + }) + }) +} + +/** + * Search volunteer and Set Notification to admin role + */ +export async function searchVolunteerAndSetNotification(notification, customerId) { + const volunteers = await Volunteer.find({}) + + volunteers.forEach(function(volunteer) { + if(volunteer.customers.length > 0){ + volunteer.customers.map(customer => { + if (customer === customerId){ + setNotification(volunteer._id, notification, User) + } + }) + } + }) +} \ No newline at end of file diff --git a/server1/lib/notification-sender.spec.js b/server1/lib/notification-sender.spec.js new file mode 100644 index 00000000..a172a679 --- /dev/null +++ b/server1/lib/notification-sender.spec.js @@ -0,0 +1,155 @@ +import { + searchUserAndSetNotification, + searchVolunteerAndSetNotification, + default as notification_sender +} from './notification-sender' +import User from '../models/user' +import Volunteer from '../models/volunteer' +import {clientRoles} from '../../common/constants' + +const sandbox = require('sinon').createSandbox() + +describe('Notification sender', function() { + describe('searchUserAndSetNotification', function() { + let dummy_users = null + let roleU = null + let notification = null + let setNotification = null + + beforeEach(function() { + dummy_users = [ + { + "_id" : 10003, + "firstName" : "Una", + "lastName" : "Deckow", + "roles" : [ + clientRoles.CUSTOMER + ], + "email" : "customer7@example.com" + }, + { + "_id" : 10022, + "firstName" : "dummy", + "lastName" : "dummy", + "roles" : [ + clientRoles.CUSTOMER + ], + "email" : "volunteer5@example.com" + }, + { + "_id" : 10015, + "firstName" : "Fernando", + "lastName" : "Cummerata", + "roles" : [ + clientRoles.CUSTOMER + ], + "email" : "customer25@example.com" + }] + roleU = clientRoles.CUSTOMER + notification = {} + + // stub setNotification function before every test to avoid running db update + setNotification = sandbox.stub() + notification_sender.__Rewire__('setNotification', setNotification) + }) + + afterEach(function() { + notification_sender.__ResetDependency__('setNotification') + sandbox.restore() + + }) + + it('should call setNotification 3 times with 3 matching users and 3 matching roles', async function() { + sandbox.stub(User, 'find').resolves(dummy_users) // stub User.find db query + await searchUserAndSetNotification(roleU, notification) + + sandbox.assert.callCount(setNotification, dummy_users.length) + }) + + it('should call setNotification 2 times with 3 matching users and 2 matching roles', async function() { + dummy_users[1].roles[0] = clientRoles.VOLUNTEER + sandbox.stub(User, 'find').resolves(dummy_users) // stub User.find db query + await searchUserAndSetNotification(roleU, notification) + + sandbox.assert.callCount(setNotification, 2) + }) + + it('should call setNotification 0 times with 0 matching users', async function() { + dummy_users = [] + sandbox.stub(User, 'find').resolves(dummy_users) // stub User.find db query + await searchUserAndSetNotification(roleU, notification) + + sandbox.assert.callCount(setNotification, 0) + }) + }) + + describe('searchVolunteerAndSetNotification', function() { + let dummy_volunteers = null + let setNotification = null + let notification = null + const customerId = 10051 + + beforeEach(function() { + dummy_volunteers = [{ + "_id": 10001, + "customers" : [ 10051, 10052, 10053 ], + "firstName" : "Annamae", + "lastName" : "Kuphal" + }, { + "_id": 10002, + "customers" : [ 10051, 10055, 10056 ], + "firstName" : "tester1", + "lastName" : "tester1" + }, { + "_id": 10003, + "customers" : [ 10051, 10058, 10059 ], + "firstName" : "tester2", + "lastName" : "tester2" + }] + notification = {} + + // stub setNotification function before every test to avoid running db update + setNotification = sandbox.stub() + notification_sender.__Rewire__('setNotification', setNotification) + }) + + afterEach(function() { + notification_sender.__ResetDependency__('setNotification') + sandbox.restore() + }) + + it('should call setNotification 3 times with 3 matching volunteers each with 1 matching customer id', async function() { + sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) // stub Volunteer find db query + await searchVolunteerAndSetNotification(notification, customerId) + + sandbox.assert.callCount(setNotification, 3) + }) + + it('should call setNotification 2 times with 3 matching volunteers with 2 volunteers having 1 matching customer id and 1 volunteer with 0 matching id', async function() { + dummy_volunteers[1].customers = [ 10052, 10055, 10056 ] + sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) + await searchVolunteerAndSetNotification(notification, customerId) + + sandbox.assert.callCount(setNotification, 2) + }) + + it('should call setNotification 0 times with 0 matching volunteers', async function() { + dummy_volunteers = [] + sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) + await searchVolunteerAndSetNotification(notification, customerId) + + sandbox.assert.callCount(setNotification, 0) + }) + + it('should call setNotification 0 times with 3 matching volunteers each with 0 customers', async function() { + dummy_volunteers[0].customers = [] + dummy_volunteers[1].customers = [] + dummy_volunteers[2].customers = [] + + sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) + await searchVolunteerAndSetNotification(notification, customerId) + + sandbox.assert.callCount(setNotification, 0) + }) + }) +}) \ No newline at end of file diff --git a/server1/lib/questionnaire-helpers.js b/server1/lib/questionnaire-helpers.js new file mode 100644 index 00000000..41d644f2 --- /dev/null +++ b/server1/lib/questionnaire-helpers.js @@ -0,0 +1,48 @@ +import {flatMap} from 'lodash' + +import Questionnaire from '../models/questionnaire' +import validate from '../../common/validators' + +async function getQuestionnaireFields (identifier, type) { + const questionnaire = await Questionnaire.findOne({identifier}) + if (!questionnaire) throw new Error('Invalid questionnaire') + + const allFields = flatMap(questionnaire.sections, section => section.fields) + return type ? allFields.filter(field => field.type === type) : allFields +} + +/** + * @export + * @param {string} identifier questionnaire identifier + * @param {[object]} modelFields the fields to select from + * @param {string=} type field type + * @returns {Promise<[object]>} + */ +export async function getFieldsByType(identifier, modelFields, type) { + const qFields = await getQuestionnaireFields(identifier, type) + + return qFields.reduce((acc, qField) => { + const modelField = modelFields.find(modelField => modelField.meta === qField._id) + if (modelField) return acc.concat(modelField) + return acc + }, []) +} + +export function getValidator(identifier) { + return async function validator(fields) { + const qFields = await getQuestionnaireFields(identifier) + + if (!fields.every(field => qFields.find(qField => qField._id === field.meta))) { + return false + } + + return qFields.reduce((valid, qField) => { + if (!valid) return false + + const field = fields.find(f => String(qField._id) === String(f.meta)) || {} + const error = validate(field.value, qField) + + return !Object.keys(error).length + }, true) + } +} diff --git a/server1/lib/questionnaire-helpers.spec.js b/server1/lib/questionnaire-helpers.spec.js new file mode 100644 index 00000000..c2934e8c --- /dev/null +++ b/server1/lib/questionnaire-helpers.spec.js @@ -0,0 +1,98 @@ +import { + getFieldsByType, + getValidator, + default as qHelpers +} from './questionnaire-helpers' + +describe('Questionnaire helpers', function() { + const qFields = [ + {_id: 1, type: 'address'}, + {_id: 2, type: 'text'} + ] + + const clientFields = [ + {meta: 2, value: 'Some text'}, + {meta: 1, value: 'An address'} + ] + + describe('getFieldsByType', function() { + afterEach(function() { + qHelpers.__ResetDependency__('getQuestionnaireFields') + }) + + it('maps questionnaire fields to model fields', async function() { + const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) + qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) + + const fields = await getFieldsByType('qClient', clientFields) + + expect(fields).to.eql([clientFields[1], clientFields[0]]) + expect(getQuestionnaireFields).to.have.been.calledWith('qClient') + }) + + it('returns fields by type', async function() { + const getQuestionnaireFields = sinon.stub().returns(Promise.resolve([qFields[0]])) + qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) + + const fields = await getFieldsByType('qClient', clientFields, 'address') + + expect(fields).to.eql([clientFields[1]]) + expect(getQuestionnaireFields).to.have.been.calledWith('qClient', 'address') + }) + }) + + describe('getValidator', function() { + afterEach(function() { + qHelpers.__ResetDependency__('getQuestionnaireFields') + qHelpers.__ResetDependency__('validate') + }) + + it('validator passes valid fields', async function() { + const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) + const validate = sinon.stub().returns({}) + + qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) + qHelpers.__Rewire__('validate', validate) + + const validator = getValidator('qClient') + const valid = await validator(clientFields) + + expect(valid).to.be.true + expect(getQuestionnaireFields).to.have.been.calledWith('qClient') + expect(validate).to.have.been.calledWith('Some text', qFields[1]) + expect(validate).to.have.been.calledWith('An address', qFields[0]) + }) + + it('validator fails with undeclared fields', async function() { + const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) + qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) + + const validator = getValidator('qClient') + const valid = await validator([ + ...clientFields, + {meta: 3, value: 'Undeclared field'} + ]) + + expect(valid).to.be.false + expect(getQuestionnaireFields).to.have.been.calledWith('qClient') + }) + + it('validator fails if validation function returns errors', async function() { + const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) + const validate = sinon.stub() + .onFirstCall().returns({}) + .onSecondCall().returns({'2': 'Required'}) + + qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) + qHelpers.__Rewire__('validate', validate) + + const validator = getValidator('qClient') + const valid = await validator(clientFields) + + expect(valid).to.be.false + expect(getQuestionnaireFields).to.have.been.calledWith('qClient') + expect(validate).to.have.been.calledWith('Some text', qFields[1]) + expect(validate).to.have.been.calledWith('An address', qFields[0]) + }) + }) +}) diff --git a/server1/lib/seed/address-generator.js b/server1/lib/seed/address-generator.js new file mode 100644 index 00000000..801be4c9 --- /dev/null +++ b/server1/lib/seed/address-generator.js @@ -0,0 +1,34 @@ +import {readFileSync} from 'fs' +import {range} from 'lodash' + +export default class AddressGenerator { + constructor() { + try { + const file = readFileSync(`${__dirname}/addresses.csv`).toString() + this.lines = file.split('\n').filter(x => x) + } catch (err) { + this.lines = [] + } + + this.indices = range(this.lines.length) + } + + getOne() { + if (!this.indices.length) throw new Error('No seed addresses remaining') + + const i = Math.floor(Math.random() * this.indices.length) + const index = this.indices[i] + this.indices.splice(i, 1) + + const [lat, lng, street, city, state, zip] = this.lines[index].split(',') + + return { + lat, + lng, + street, + city, + state, + zip + } + } +} diff --git a/server1/lib/seed/address-generator.spec.js b/server1/lib/seed/address-generator.spec.js new file mode 100644 index 00000000..3133287f --- /dev/null +++ b/server1/lib/seed/address-generator.spec.js @@ -0,0 +1,46 @@ +import AddressGenerator from './address-generator' + +describe('Address generator', function() { + afterEach(function() { + AddressGenerator.__ResetDependency__('readFileSync') + }) + + it('pulls addresses from a file', function() { + const addresses = '1,1,street,city,state,zip\n' + const readFileSync = sinon.stub().returns(addresses) + AddressGenerator.__Rewire__('readFileSync', readFileSync) + + const generator = new AddressGenerator + const address1 = generator.getOne() + expect(address1).to.have.property('lat', '1') + expect(address1).to.have.property('lng', '1') + expect(address1).to.have.property('street', 'street') + expect(address1).to.have.property('city', 'city') + expect(address1).to.have.property('state', 'state') + expect(address1).to.have.property('zip', 'zip') + }) + + it('gets unique addresses', function() { + const addresses = '1,1,str,c,s,z\n2,2,str,c,s,z\n3,3,str,c,s,z\n' + const readFileSync = sinon.stub().returns(addresses) + AddressGenerator.__Rewire__('readFileSync', readFileSync) + + const generator = new AddressGenerator + const address1 = generator.getOne() + const address2 = generator.getOne() + const address3 = generator.getOne() + expect(address1.lat).to.not.equal(address2.lat) + expect(address2.lat).to.not.equal(address3.lat) + expect(address1.lat).to.not.equal(address3.lat) + }) + + it('throws if more addresses are requested', function() { + const addresses = '1,1,street,city,state,zip' + const readFileSync = sinon.stub().returns(addresses) + AddressGenerator.__Rewire__('readFileSync', readFileSync) + + const generator = new AddressGenerator + generator.getOne() + expect(generator.getOne).to.throw() + }) +}) diff --git a/server1/lib/seed/addresses.csv b/server1/lib/seed/addresses.csv new file mode 100644 index 00000000..a3ec4679 --- /dev/null +++ b/server1/lib/seed/addresses.csv @@ -0,0 +1,64 @@ +40.747495,-73.995125,227 W 27th St,New York,NY,10001,USA +40.747618,-73.997635,307 W 26th St,New York,NY,10001,USA +40.745608,-73.998517,309 W 23rd St,New York,NY,10011,USA +40.749241,-73.998775,296 9th Ave,New York,NY,10001,USA +40.750022,-74.002787,287 10th Ave,New York,NY,10001,USA +40.748038,-74.008516,62 Chelsea Piers,New York,NY,10011,USA +40.745494,-74.006811,512 W 19th St,New York,NY,10011,USA +40.741243,-74.006714,420 W 14th St,New York,NY,10014,USA +40.737700,-74.008072,775 Washington St,New York,NY,10014,USA +40.732529,-74.008158,154 Christopher St #1E,New York,NY,10014,USA +40.733448,-74.006130,510 Hudson St,New York,NY,10014,USA +40.731277,-74.005347,38 Commerce St,New York,NY,10014,USA +40.723496,-74.006505,75 Varick St,New York,NY,10013,USA +40.723984,-74.009971,135 Watts St,New York,NY,10013,USA +40.720783,-74.002166,19 Mercer St,New York,NY,10013,USA +40.724173,-74.000240,91 Greene St,New York,NY,10012,USA +40.720401,-73.995266,151 Elizabeth St,New York,NY,10012,USA +40.719861,-73.977384,442 E Houston St,New York,NY,10002,USA +40.724983,-73.974702,454 E 10th St,New York,NY,10009,USA +40.729453,-73.978680,538 E 14th St,New York,NY,10009,USA +40.735168,-73.979420,348 1st Avenue,New York,NY,10010,USA +40.737940,-73.978240,345 E 24th St,New York,NY,10010,USA +40.738908,-73.980826,241 E 24th St,New York,NY,10010,USA +40.743818,-73.979914,446 3rd Ave,New York,NY,10016,USA +40.746858,-73.980161,115 E 34th St,New York,NY,10016,USA +40.745890,-73.981534,1 Park Ave,New York,NY,10016,USA +40.751767,-73.981706,10 East 40th Street,New York,NY,10016,USA +40.751214,-73.986351,980 6th Ave,New York,NY,10018,USA +40.746004,-73.991694,115 W 27th St,New York,NY,10001,USA +40.756392,-73.988867,234 W 42nd St,New York,NY,10036,USA +40.762545,-73.985101,222 W 51st St,New York,NY,10019,USA +40.766320,-73.979045,180 Central Park S,New York,NY,10019,USA +40.780536,-73.980984,2124 Broadway,New York,NY,10023,USA +40.795024,-73.966351,808 Columbus Ave,New York,NY,10025,USA +40.802486,-73.967385,2790 Broadway,New York,NY,10025,USA +40.807056,-73.960296,435 W 116th St,New York,NY,10027,USA +40.792512,-73.951958,1220 5th Ave,New York,NY,10029,USA +40.810013,-73.950062,253 W 125th St,New York,NY,10027,USA +40.815211,-73.944339,200 W 135th St,New York,NY,10030,USA +40.817894,-73.954161,530 W 133rd St,New York,NY,10027,USA +40.833688,-73.946467,3755 Broadway,New York,NY,10032,USA +40.846498,-73.937907,4140 Broadway,New York,NY,10033,USA +40.745654,-73.948559,4602 21st St,Long Island City,NY,11101,USA +40.747257,-73.944427,1 Ct Square W,Long Island City,NY,11120,USA +40.748555,-73.948790,11-11 44th Dr,Queens,NY,11101,USA +40.747770,-73.943793,2 Ct Square W,Long Island City,NY,11101,USA +40.752057,-73.947465,11-01 43rd Ave,Long Island City,NY,11101,USA +40.752679,-73.940352,23-10 41st Ave,Long Island City,NY,11101,USA +40.744017,-73.935038,45-35 Van Dam St,Long Island City,NY,11101,USA +40.728092,-73.957760,52 Noble St,Brooklyn,NY,11222,USA +40.722289,-73.957854,74 Wythe Ave,Brooklyn,NY,11249,USA +40.719113,-73.961770,66 N 6th St,Brooklyn,NY,11211,USA +40.714137,-73.959841,217 Grand St,Brooklyn,NY,11211,USA +40.710256,-73.941124,87 Ten Eyck St,Brooklyn,NY,11206,USA +40.704675,-73.945514,29 Leonard St,Brooklyn,NY,11206,USA +40.752712,-73.927583,37-6 36th St,Long Island City,NY,11101,USA +40.755819,-73.941062,39-34 21st St,Long Island City,NY,11101,USA +40.694789,-73.992446,128 Pierrepont St,Brooklyn,NY,11201,USA +40.690825,-73.992005,106 Court St,Brooklyn,NY,11201,USA +40.679235,-74.012139,159 Pioneer St,Brooklyn,NY,11231,USA +40.671777,-73.995093,118 2nd Ave,Brooklyn,NY,11215,USA +40.679101,-73.983546,622 Degraw St,Brooklyn,NY,11217,USA +40.668588,-73.984398,357 9th St,Brooklyn,NY,11215,USA +40.670865,-73.974593,274 Garfield Pl,Brooklyn,NY,11215,USA diff --git a/server1/lib/seed/index.js b/server1/lib/seed/index.js new file mode 100644 index 00000000..89525107 --- /dev/null +++ b/server1/lib/seed/index.js @@ -0,0 +1,164 @@ +import mongoose from 'mongoose' +import {range, values} from 'lodash' +import faker from 'faker' + +import { + foodFields, + getSettingsFields, + pages, + customerQuestionnaire, + donorQuestionnaire, + volunteerQuestionnaire +} from './seed-data' +import seedClients from './seed-clients' +import AddressGenerator from './address-generator' +import { + ADMIN_ROLE, + clientRoles, + volunteerRoles, + modelTypes +} from '../../../common/constants' + +const addressGenerator = new AddressGenerator + +const User = mongoose.model(modelTypes.USER) +const Customer = mongoose.model(modelTypes.CUSTOMER) +const Donor = mongoose.model(modelTypes.DONOR) +const Donation = mongoose.model(modelTypes.DONATION) +const Volunteer = mongoose.model(modelTypes.VOLUNTEER) +const Questionnaire = mongoose.model(modelTypes.QUESTIONNAIRE) +const Food = mongoose.model(modelTypes.FOOD) +const Settings = mongoose.model(modelTypes.SETTINGS) +const Page = mongoose.model(modelTypes.PAGE) +const Media = mongoose.model(modelTypes.MEDIA) + +/** + * populate empty collections with seed data + * + * @param {string} env the environment + * @param {boolean} replace replace all except admin user with fresh data + * @param {boolean} replaceAdmin also replace admin user + * @return {Promise} + */ +export default async function seed(env, replace, replaceAdmin = false) { + if (env === 'test') return + if (replace) await clearDb(replaceAdmin) + seedDb(env) +} + +async function clearDb(replaceAdmin) { + if (replaceAdmin) await User.find().remove() + else await User.find({roles: {$in: values(clientRoles)}}).remove() + + await Promise.all([ + Customer.find().remove(), + Donation.find().remove(), + Donor.find().remove(), + Volunteer.find().remove(), + Food.find().remove(), + Page.find().remove(), + Questionnaire.find().remove(), + Settings.find().remove(), + Media.find().remove() + ]) +} + +async function seedDb(env) { + await seedAdmin() + await seedQuestionnaires() + await seedSettings() + await seedPages() + await Media.create({}) + + // for development also seed clients and foods + if (env !== 'production') { + await seedUsers() + await seedFoods() + await seedClients(addressGenerator) + } +} + +async function seedAdmin() { + const adminCount = await User.count({roles: ADMIN_ROLE}) + if (!adminCount) { + const adminUser = { + firstName: 'admin', + lastName: 'user', + displayName: 'admin', + roles: [ADMIN_ROLE], + email: `admin@example.com`, + password: 'password', + provider: 'local' + } + await User.create(adminUser) + } +} + +async function seedUsers() { + const clientCount = await User.count({roles: {$in: values(clientRoles)}}) + if (clientCount) return + + const customers = range(45).map(i => + createTestUser(`customer ${i + 1}`, clientRoles.CUSTOMER)) + const volunteers = range(8).map(i => + createTestUser(`volunteer ${i + 1}`, clientRoles.VOLUNTEER)) + const donors = range(5).map(i => + createTestUser(`donor ${i + 1}`, clientRoles.DONOR)) + const drivers = range(5).map(i => + createTestUser(`driver ${i + 1}`, clientRoles.VOLUNTEER)) + + await User.create([ + ...customers, + ...volunteers, + ...donors, + ...drivers + ]) +} + +async function seedFoods() { + const count = await Food.count() + if (count) return + + await Food.insertMany(foodFields) +} + +async function seedQuestionnaires() { + const count = await Questionnaire.count() + if (count) return + + await Questionnaire.insertMany([ + customerQuestionnaire, + donorQuestionnaire, + volunteerQuestionnaire + ]) +} + +async function seedSettings() { + const count = await Settings.count() + if (count) return + + await Settings.create(getSettingsFields(addressGenerator)) +} + +async function seedPages() { + const count = await Page.count() + if (count) return + + await Page.insertMany(pages) +} + +// Helpers +function createTestUser(name, role) { + const names = name.split(' ') + if (names.length !== 2) throw new Error('name should be "firstname lastname"') + return { + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + roles: names[0] === 'driver' ? + [volunteerRoles.DRIVER, role] : + [role], + email: `${names.join('')}@example.com`, + password: 'password', + provider: 'local' + } +} diff --git a/server1/lib/seed/seed-clients.js b/server1/lib/seed/seed-clients.js new file mode 100644 index 00000000..44f926e2 --- /dev/null +++ b/server1/lib/seed/seed-clients.js @@ -0,0 +1,234 @@ +import mongoose from 'mongoose' +import faker from 'faker' +import { + extend, + intersection, + map, + random, + range +} from 'lodash' + +import { + clientRoles, + volunteerRoles, + fieldTypes, + modelTypes, + questionnaireIdentifiers +} from '../../../common/constants' + +const User = mongoose.model(modelTypes.USER) +const Donation = mongoose.model(modelTypes.DONATION) +const Food = mongoose.model(modelTypes.FOOD) +const Questionnaire = mongoose.model(modelTypes.QUESTIONNAIRE) + +export default async function seedClients(addressGenerator) { + await Promise.all(map(clientRoles, async (role, key) => { + const Model = mongoose.model(modelTypes[key]) + const count = await Model.count() + if (count) return + + const users = await User.find({roles: role}) + .select('-__v') + + const clients = await Promise.all(users.map(user => { + const client = new Model(user.toObject()) + return seedClientFields(client, user, addressGenerator) + })) + + await Model.insertMany(clients) + })) +} + +/** + * Seed client fields + * + * @param {mongoose.Document} client + * @returns {Promise} + */ +async function seedClientFields(client, user, addressGenerator) { + const address = addressGenerator.getOne() + const dynamicFields = await seedDynamicFields(client, user, address) + const staticFields = await seedStaticFields(client, user, dynamicFields[0].value, address) + + return extend(client, staticFields, {fields: dynamicFields}) +} + +/** + * Seed client dynamic fields + * + * @param {object} client + * @returns {array} + */ +async function seedDynamicFields(client, user, address) { + const roleKey = Object.keys(clientRoles).find(role => + intersection(user.roles, [clientRoles[role]]).length) + + const identifier = questionnaireIdentifiers[roleKey] + const questionnaire = await Questionnaire + .findOne({identifier}) + .lean() + + const generalInfoFields = questionnaire.sections[0].fields + + const addressFields = generalInfoFields.filter(field => + field.type === fieldTypes.ADDRESS) + const dateOfBirthField = generalInfoFields.find(field => + field.label === 'Date of Birth') + + let fields = [{ + meta: addressFields[0], + value: address.street + }, { + meta: addressFields[1], + value: address.city + }, { + meta: addressFields[2], + value: address.state + }, { + meta: addressFields[3], + value: address.zip + }] + + if (dateOfBirthField) { + fields.unshift({ + meta: dateOfBirthField, + value: faker.date.between('1950-01-01', '2000-12-31').toISOString() + }) + } + + return fields +} + +/** + * Seed client static fields + * + * @param {object} client + * @param {string} dateOfBirth + * @returns {object} + */ +async function seedStaticFields(client, user, dateOfBirth, address) { + const {lat, lng} = address + let properties = {} + + if (user.roles.find(role => role === clientRoles.VOLUNTEER)) { + properties.user = user._id + + properties.status = randomIn({ + 'Active': 0.8, + 'Inactive': 0.2 + }) + + if (user.roles.find(r => r === volunteerRoles.DRIVER)) { + properties.status = 'Active' + properties.location = {lat, lng} + } + } + + if (user.roles.find(role => role === clientRoles.DONOR)) { + properties = { + ...properties, + ...await populateDonorFields(client) + } + } + + if (user.roles.find(role => role === clientRoles.CUSTOMER)) { + properties = { + ...properties, + ...await populateCustomerFields(client, dateOfBirth, address) + } + } + + return properties +} + +/** + * return extra fields specific to donors + * + * @param {object} client + * @returns {object} + */ +async function populateDonorFields(client) { + const donations = range(random(3, 30)).map(() => { + const items = range(random(1, 4)).map(() => ({ + name: randomIn({ + 'Cash': 0.75, + 'Food': 0.25, + }), + value: random(1, 50) + })) + + const total = items.reduce((acc, item) => acc + item.value, 0) + const dateReceived = faker.date.past(3).toISOString() + const approved = Boolean(Math.round(Math.random())) + + return { + donor: client._id, + dateReceived, + items, + total, + approved, + ...(approved ? {dateIssued: Date.now()} : {}) + } + }) + + return {donations: await Donation.create(donations)} +} + +/** + * return extra fields specific to customers + * + * @param {object} client + * @param {string} dateOfBirth + * @returns {object} + */ +async function populateCustomerFields(client, dateOfBirth, address) { + // get a random sample of foods for foodPreferences + const foodPreferences = (await Food.aggregate([ + {$unwind: '$items'}, + {$sample: {size: random(4, 10)}} + ])).map(res => res.items._id) + + const household = [{ + name: `${client.firstName} ${client.lastName}`, + relationship: 'Applicant', + dateOfBirth + }] + + const {lat, lng} = address + const location = {lat, lng} + + const status = randomIn({ + 'Accepted': 0.8, + 'Rejected': 0.1, + 'Pending': 0.05, + 'Inactive': 0.05 + }) + + return { + foodPreferences, + household, + location, + status + } +} + +/** + * return a random key with given probability + * + * @param {object} choices object of keys with probability of returning each key + * @returns {string} the key + */ +function randomIn(choices) { + const keys = Object.keys(choices) + const rand = Math.random() + + return keys.reduce((a, key) => { + // already have a key just return it + if (a.key) return a + // add probability for this key to accumulator + a.acc += choices[key] + + if (rand < a.acc) return {key} + else return a + }, {acc: 0}).key +} diff --git a/server1/lib/seed/seed-data.js b/server1/lib/seed/seed-data.js new file mode 100644 index 00000000..ab412514 --- /dev/null +++ b/server1/lib/seed/seed-data.js @@ -0,0 +1,279 @@ +import {utc} from 'moment' +import faker from 'faker' +import {v4} from 'uuid' + +import config from '../../config' +import { + fieldTypes, + widgetTypes, + pageTypes, + pageIdentifiers, + questionnaireIdentifiers +} from '../../../common/constants' + +const addRandomDate = item => + ({...item, startDate: utc(faker.date.past(2)).startOf('isoWeek')}) +export const foodFields = [{ + category: 'Cheese', + items: [ + {name: 'Cheddar', quantity: 20, frequency: 1}, + {name: 'Mozarella', quantity: 35, frequency: 2}, + {name: 'Brie', quantity: 30, frequency: 4}, + {name: 'Emmental', quantity: 20, frequency: 1}, + {name: 'Monterey Jack', quantity: 20, frequency: 1}, + {name: 'Stilton', quantity: 20, frequency: 1}, + {name: 'Camembert', quantity: 20, frequency: 1}, + {name: 'Port Salut', quantity: 20, frequency: 1}, + {name: 'Gorgonzola', quantity: 20, frequency: 1}, + ].map(addRandomDate) +}, { + category: 'Pasta, rice etc', + items: [ + {name: 'Penne pasta', quantity: 50, frequency: 1}, + {name: 'Basmati rice', quantity: 40, frequency: 1}, + {name: 'Fusilli pasta', quantity: 45, frequency: 2}, + {name: 'Couscous', quantity: 40, frequency: 1}, + {name: 'Spagetti', quantity: 50, frequency: 1}, + {name: 'Jasmine rice', quantity: 50, frequency: 1}, + ].map(addRandomDate) +}, { + category: 'Meat', + items: [ + {name: 'Beef mince', quantity: 25, frequency: 2}, + {name: 'Chicken breast', quantity: 15, frequency: 2}, + {name: 'Pork chop', quantity: 20, frequency: 2}, + {name: 'T-Bone steak', quantity: 25, frequency: 2}, + {name: 'Chicken thighs', quantity: 20, frequency: 2}, + {name: 'Bacon', quantity: 20, frequency: 2}, + {name: 'Salami', quantity: 15, frequency: 2}, + {name: 'Pepperoni', quantity: 15, frequency: 2}, + ].map(addRandomDate) +}, { + category: 'Vegetables', + items: [ + {name: 'Carrots', quantity: 30, frequency: 1}, + {name: 'Onions', quantity: 25, frequency: 2}, + {name: 'Squash', quantity: 25, frequency: 4}, + {name: 'Leek', quantity: 30, frequency: 1}, + {name: 'Potatoes', quantity: 25, frequency: 2}, + {name: 'Turnips', quantity: 25, frequency: 4}, + {name: 'Cauliflower', quantity: 30, frequency: 1}, + {name: 'Broccoli', quantity: 25, frequency: 2}, + {name: 'Peas', quantity: 25, frequency: 4} + ].map(addRandomDate) +}, { + category: 'Milk', + items: [ + {name: 'Whole milk', quantity: 15, frequency: 1}, + {name: 'Semi-skimmed milk', quantity: 15, frequency: 1} + ].map(addRandomDate) +}, { + category: 'Snacks', + items: [ + {name: 'Snickers', quantity: 20, frequency: 3}, + {name: 'Mars bar', quantity: 20, frequency: 3}, + {name: 'Potato chips', quantity: 20, frequency: 3}, + ].map(addRandomDate) +}] + +export const getSettingsFields = () => { + const [lat, lng, street, city, zip] = + [40.720230, -73.984029, '176 Stanton St', 'New York', 'NY 10002'] + + return { + organization: 'Pantry for Good', + url: 'https://github.com/FreeCodeCamp/pantry-for-good', + address: `${street} ${city} ${zip}`, + clientIntakeNumber: faker.phone.phoneNumber(), + supportNumber: faker.phone.phoneNumber(), + location: {lat, lng}, + gmapsApiKey: config.gmapsApiKey, + } +} + +export const pages = [{ + identifier: pageIdentifiers.HOME, + title: 'Home', + body: '

is an application for managing food bank operations created as part of the Open Source for Good project at freeCodeCamp.

The content of this page and the site branding can be changed by an administrator on the settings page.

' +}, { + identifier: pageIdentifiers.CUSTOMER, + title: 'Customers', + body: '

Please carefully read the following information:

  • Boxes will be delivered by our volunteers every Wednesday evening between 8:00PM and 11:00PM.
  • Boxes will be left at your doorstep. Volunteers may knock on your door and quickly leave to keep anonymity. During holidays, or other special times, including severe weather conditions, time and day of delivery may change without notice.
  • We ask that you leave the empty box outside your door the following week, so it can be picked up by the volunteers and reused.
  • Volunteers do their best to check food items for expiry dates, however mistakes do occur. Please use your own judgment and discretion before consuming any food or contact the manufacturer directly if you have any concerns. If you find an item that is expired, you may throw it away. If there are items that you don\'t use, please leave them in the box, and we will make a note so that you will not receive them again.
  • If there is a change in your financial situation and you no longer need our assistance, if you will be away for a certain period, if you have a change in contact information, or any other change, please inform us promptly.
' +}, { + identifier: pageIdentifiers.DONOR, + title: 'Donors', + body: '' +}, { + identifier: pageIdentifiers.VOLUNTEER, + title: 'Volunteers', + body: '

In the past year, over (amount) pounds of food was distributed on a weekly basis by our team of dedicated volunteers. Your assistance helps us respond to the ever-growing demand of our community. Thank you for taking the opportunity to help turn lives around.

With your help, we are able to support the less fortunate families in our community, by providing them with nutritious food and energy to grow, learn, work, and give them hope for a better and brighter future.

' +}, { + identifier: pageIdentifiers.CUSTOMER_ACCEPTED, + title: 'Customer Accepted', + type: pageTypes.EMAIL, + subject: '

Your application was accepted

', + body: `

Dear ,

+

We are pleased to inform you that based on the information provided in your application, you are eligible to receive our assistance.

+

Sincerely,

+

The Team

+

+ is committed to protecting your personal information by following responsible information handling practices and in keeping with privacy laws. All information remains strictly confidential as outlined by 's Privacy Policy that can be accessed at . +

+

If you have any questions or concerns, feel free to contact us.

` +}, { + identifier: pageIdentifiers.CUSTOMER_REJECTED, + title: 'Customer Rejected', + type: pageTypes.EMAIL, + subject: '

Your application was rejected

', + body: `

Dear ,

+

We regret to inform you that based on the information provided in your application, you are not eligible to receive our assistance.

+

If your circumstances change please edit your application and we will reevaluate your request

+

Sincerely,

+

The Team

+

+ is committed to protecting your personal information by following responsible information handling practices and in keeping with privacy laws. All information remains strictly confidential as outlined by 's Privacy Policy that can be accessed at . +

+

If you have any questions or concerns, feel free to contact us.

` +}, { + identifier: pageIdentifiers.CUSTOMER_APPLIED, + title: 'Customer Application', + type: pageTypes.EMAIL, + subject: '

A new customer has applied

', + body: `

Dear Admin,

+

A new client application has been created.

+

Please visit and login with your admin credentials.

` +}, { + identifier: pageIdentifiers.CUSTOMER_UPDATED, + title: 'Customer Updated', + type: pageTypes.EMAIL, + subject: '

A customers details have changed

', + body: `

Dear Admin,

+

This is a notification email to notify you that either with application ID +{id}} or an admin has changed his/her personal information.

+

Please visit and login with your admin credentials.

` +}, { + identifier: pageIdentifiers.PASSWORD_RESET, + title: 'Password Reset', + type: pageTypes.EMAIL, + subject: '

Password Reset

', + body: `

Dear ,

+

You have requested to have your password reset for your account at

+

Please visit this url to reset your password:

+

+If you didn't make this request, you can ignore this email. +

The support team

` +}, { + identifier: pageIdentifiers.DONATION_RECEIVED, + title: 'Donation Received', + type: pageTypes.EMAIL, + subject: '

Thank You for Your Donation to

', + body: `

Dear ,

+

Thank you for your generous donation.

+

Sincerely,

+

The Team

+

+ is committed to protecting your personal information by following responsible information handling practices and in keeping with privacy laws. All information remains strictly confidential as outlined by 's Privacy Policy that can be accessed at . +

+

If you have any questions or concerns, feel free to contact us.

+` +}, { + identifier: pageIdentifiers.DONATION_RECEIPT, + title: 'Donation Receipt', + type: pageTypes.EMAIL, + subject: '

Receipt for Your Donation to

', + body: `

Dear ,

+

Thanks for your donations to:

+

+` +}, { + identifier: pageIdentifiers.PASSWORD_RESET_GOOGLE, + title: 'Password Reset Google', + type: pageTypes.EMAIL, + subject: '

Password Reset

', + body: `

Dear ,

+

You have requested to have your password reset for your account at

+

you appear to have signed up with google, try logging in with google. If you have any problems give us a call to the

+If you didn't make this request, you can ignore this email. +

The support team

+` +}] + +const commonFields = [ + {type: fieldTypes.ADDRESS, label: 'Street', required: true}, + {type: fieldTypes.ADDRESS, label: 'Town/City', required: true}, + {type: fieldTypes.ADDRESS, label: 'State/Province', required: true}, + {type: fieldTypes.ADDRESS, label: 'Zip/Post Code', required: true}, + {type: fieldTypes.TEXT, label: 'Telephone Number'} +].map(field => ({ + ...field, + _id: v4() +})) + +export const customerQuestionnaire = { + name: 'Customer Application', + identifier: questionnaireIdentifiers.CUSTOMER, + sections: [{ + name: 'General Info', + fields: [ + ...commonFields, + {type: fieldTypes.DATE, label: 'Date of Birth', required: true}, + {type: fieldTypes.RADIO, choices: 'Male, Female', label: 'Gender'}, + {type: fieldTypes.RADIO, choices: 'Rental, Own, Subsidized, Other', label: 'Accommodation Type'}, + {type:fieldTypes.RADIO, choices: 'Phone, Email', label: 'Best way to contact'}, + {type: fieldTypes.TEXTAREA, label: 'Delivery instructions'}, + {type: fieldTypes.TEXTAREA, label: 'Other organizations you are currently receiving assistance from'} + ] + }, { + name: 'Employment', + fields: [ + {type: fieldTypes.RADIO, choices: 'Employed, Unemployed, Laid-off, Retired, Disabled, Student', label: 'Current work status'}, + {type: fieldTypes.TEXT, label: 'Hours per week'}, + {type: fieldTypes.TEXT, label: 'Job title'}, + ] + }, { + name: 'Food Preferences', + fields: [ + {type: widgetTypes.FOOD_PREFERENCES, label: 'Please select the foods you are interested in'}, + {type: fieldTypes.TEXTAREA, label: 'Please list any food allergies or sensitivities'}, + {type: fieldTypes.TEXTAREA, label: 'Other food preferences'}, + ] + // }, { + // name: 'Financial Assessment', + // fields: [ + // {label:'Monthly Gross Income', rows: 'Employment Income, Employment Insurance Benefits, Social Assistance, Spousal/Child Support, Self Employment, Pension Income (eg. Employer Plan), Disability Income, Pension Plan, Child Tax Benefits, Income from Rental Property, Severance/Termination Pay, Other income not listed above', columns: 'Self, Other', type: 'table'}, + // {label: 'Monthly Living Expenses', rows: 'Rent mortgage or room and board, Food, Utilities, Transportation, Dependant Care (eg. day care), Disability Needs, Spousal/Child support, Loans, Leases, Insurance, Credit card debt, Property taxes, Other costs not listed above', columns: 'Household', type: 'table'} + // ] + }, { + name: 'Household', + fields: [ + {type: widgetTypes.HOUSEHOLD, label: 'Household'} + ] + }] +} + +export const donorQuestionnaire = { + name: 'Donor Application', + identifier: questionnaireIdentifiers.DONOR, + sections: [{ + name: 'General Info', + fields: [ + ...commonFields, + {type: fieldTypes.TEXTAREA, label: 'How did you hear about us?'} + ] + }] +} + +export const volunteerQuestionnaire = { + name: 'Volunteer Application', + identifier: questionnaireIdentifiers.VOLUNTEER, + sections: [{ + name: 'General Info', + fields: [ + ...commonFields, + {type: fieldTypes.DATE, label: 'Date of Birth', required:true}, + {type: fieldTypes.RADIO, choices: 'Phone, Email', label: 'Best way to contact'}, + {type: fieldTypes.TEXTAREA, label: 'How did you hear about us?'}, + {type: fieldTypes.TEXTAREA, label: 'Why do you want to volunteer with us?'} + ] + }] +} diff --git a/server1/lib/update-linked-fields.js b/server1/lib/update-linked-fields.js new file mode 100644 index 00000000..29179ac9 --- /dev/null +++ b/server1/lib/update-linked-fields.js @@ -0,0 +1,38 @@ +import Customer from '../models/customer' +import Volunteer from '../models/volunteer' +import Donor from '../models/donor' +import {clientRoles} from '../../common/constants' + +/** +* Update fields of address +*/ +async function updatedModel(Model, reqBodyFields, userId) { + const model = await Model.findById(userId) + if (model != undefined && model != null && model != {}) { + let modelFields = model.fields + for (let j = 0; j < modelFields.length; j++) { + for (let k = 0; k < reqBodyFields.length; k++) { + if (modelFields[j].meta == reqBodyFields[k].meta) + modelFields[j].value = reqBodyFields[k].value + } + } + await Model.findByIdAndUpdate(userId, {fields: modelFields}) + } +} + +/** +* Update All fields of address +*/ +export function updateFields(clientRole, reqBodyFields, userId) { + //Updating address of the client type role if exists + if (clientRole == clientRoles.CUSTOMER) { + updatedModel(Volunteer, reqBodyFields, userId) + updatedModel(Donor, reqBodyFields, userId) + } else if (clientRole == clientRoles.DONOR) { + updatedModel(Volunteer, reqBodyFields, userId) + updatedModel(Customer, reqBodyFields, userId) + } else if (clientRole == clientRoles.VOLUNTEER) { + updatedModel(Donor, reqBodyFields, userId) + updatedModel(Customer, reqBodyFields, userId) + } +} diff --git a/server1/lib/websocket-middleware.js b/server1/lib/websocket-middleware.js new file mode 100644 index 00000000..7d755aab --- /dev/null +++ b/server1/lib/websocket-middleware.js @@ -0,0 +1,51 @@ +import {normalize, schema} from 'normalizr' +import {intersection} from 'lodash' + +let users = [] + +export default sync => { + if (!sync.syncTo) sync.syncTo = [] + if (typeof(sync.type) !== 'string') + throw new Error('action type must be a string') + + return (req, res, next) => { + const oldJson = res.json + + res.json = json => { + oldJson.call(res, json) + if (res.statusCode !== 200) return + + json = JSON.parse(JSON.stringify(json)) + let response + + if (!sync.schema) { + response = json + } else if (Object.keys(schema).some(type => sync.schema instanceof schema[type])) { + // schema is a normalizr schema + response = normalize(json, sync.schema) + } else { + // schema is a map of entity types to schemas + const entities = Object.assign({}, + ...Object.keys(sync.schema).map(k => ({ + [k]: normalize(json[k], sync.schema[k]).entities[k] + })) + ) + response = {entities} + } + + const action = {type: sync.type, response} + const syncTo = sync.syncTo.concat('admin') + + users.forEach(user => { + if (user.socket.id === req.headers['socket-id']) return + if (intersection(user.roles, syncTo).length) { + user.socket.emit('action', action) + } + }) + } + + next() + } +} + +export const addUser = (user, socket) => users.push({...user, socket}) diff --git a/server1/models/customer.js b/server1/models/customer.js new file mode 100644 index 00000000..4406e3f3 --- /dev/null +++ b/server1/models/customer.js @@ -0,0 +1,131 @@ +import mongoose from 'mongoose' +import moment from 'moment' + +import {modelTypes, questionnaireIdentifiers} from '../../common/constants' +import locationSchema from './location-schema' +import {getValidator} from '../lib/questionnaire-helpers' +import {locateQuestionnaire} from '../lib/geolocate' + +const {Schema} = mongoose + +const CustomerSchema = new Schema({ + _id: { + type: Number, + ref: modelTypes.USER + }, + firstName: { + type: String, + required: true + }, + lastName: { + type: String, + required: true + }, + email: { + type: String, + required: true + }, + location: locationSchema, + status: { + type: String, + enum: ['Accepted', 'Rejected', 'Pending', 'Inactive'], + default: 'Pending' + }, + household: [{ + name: { + type: String, + trim: true + }, + relationship: { + type: String, + trim: true + }, + dateOfBirth: { + type: Date + } + }], + disclaimerAgree: { + type: Boolean + }, + disclaimerSign: { + type: String, + trim: true + }, + lastPacked: { + type: Date, + default: null + }, + packingList: [{ + type: Schema.Types.ObjectId, + ref: 'FoodItem' + }], + lastDelivered: { + type: Date + }, + assignedTo: { + type: Number, + ref: modelTypes.VOLUNTEER + }, + foodPreferences: [Schema.Types.ObjectId], + fields: [{ + meta: { + type: String, + required: true + }, + value: String + }], + dateReceived: { + type: Date, + default: Date.now + } +}, { + id: false +}) + +CustomerSchema.path('fields') + .validate(getValidator(questionnaireIdentifiers.CUSTOMER), 'Invalid field') + +/** + * Hook a pre save method for geolocation + */ +CustomerSchema.pre('save', async function(next) { + const result = await locateQuestionnaire(this.fields, questionnaireIdentifiers.CUSTOMER) + if (result instanceof Error) + return next(result) + + if (result) + this.location = result + + return next() +}) + +/** + * Virtual getters & setters + */ +CustomerSchema.virtual('fullName').get(function() { + var fullName = this.firstName ? this.firstName + ' ' : '' + fullName += this.middleName ? this.middleName + ' ' : '' + fullName += this.lastName ? this.lastName : '' + return fullName +}) + +CustomerSchema.virtual('householdSummary').get(function() { + var householdSummary = 'None' + + if (this.household.length) { + householdSummary = '#' + this.household.length + ' -' + this.household.forEach(function(dependant) { + var ageInYears = moment().diff(dependant.dateOfBirth, 'years') + householdSummary += ' ' + householdSummary += ageInYears ? ageInYears + 'y' : moment().diff(dependant.dateOfBirth, 'months') + 'm' + }) + } + return householdSummary +}) + +/** + * Schema options + */ +CustomerSchema.set('toJSON', {virtuals: true}) + +export default mongoose.model(modelTypes.CUSTOMER, CustomerSchema) diff --git a/server1/models/donation.js b/server1/models/donation.js new file mode 100644 index 00000000..c41b5457 --- /dev/null +++ b/server1/models/donation.js @@ -0,0 +1,48 @@ +import mongoose from 'mongoose' +import autoIncrement from 'mongoose-plugin-autoinc' + +import {modelTypes} from '../../common/constants' + +const Schema = mongoose.Schema + +const DonatedItemSchema = new Schema({ + name: { + type: String, + trim: true + }, + value: { + type: Number, + required: true + } +}, { + _id: false +}) + +const DonationSchema = new Schema({ + donor: { + type: Number, + ref: modelTypes.USER + }, + total: Number, + approved: { + type: Boolean, + default: false + }, + description: { + type: String, + trim: true + }, + dateReceived: { + type: Date, + default: Date.now + }, + dateIssued: Date, + items: [DonatedItemSchema] +}) + +DonationSchema.plugin(autoIncrement, { + model: modelTypes.DONATION, + startAt: 1 +}) + +export default mongoose.model(modelTypes.DONATION, DonationSchema) diff --git a/server1/models/donor.js b/server1/models/donor.js new file mode 100644 index 00000000..c7708a42 --- /dev/null +++ b/server1/models/donor.js @@ -0,0 +1,59 @@ +import mongoose from 'mongoose' + +import {modelTypes, questionnaireIdentifiers} from '../../common/constants' +import {getValidator} from '../lib/questionnaire-helpers' + +const {Schema} = mongoose + +var DonorSchema = new Schema({ + _id: { + type: Number, + ref: modelTypes.USER + }, + lastName: { + type: String, + trim: true + }, + firstName: { + type: String, + trim: true + }, + email: { + type: String, + trim: true + }, + donations: [{ + type: Number, + ref: modelTypes.DONATION + }], + fields: [{ + meta: { + type: String, + required: true + }, + value: String + }], + dateReceived: { + type: Date, + default: Date.now + } +}, { + id: false +}) + +DonorSchema.path('fields') + .validate(getValidator(questionnaireIdentifiers.DONOR), 'Invalid field') + +/** + * Virtual getters & setters + */ +DonorSchema.virtual('fullName').get(function() { + var fullName = this.firstName ? this.firstName + ' ' : '' + fullName += this.middleName ? this.middleName + ' ' : '' + fullName += this.lastName ? this.lastName : '' + return fullName +}) + +DonorSchema.set('toJSON', {virtuals: true}) + +export default mongoose.model(modelTypes.DONOR, DonorSchema) diff --git a/server1/models/food.js b/server1/models/food.js new file mode 100644 index 00000000..a5fa8a59 --- /dev/null +++ b/server1/models/food.js @@ -0,0 +1,44 @@ +import mongoose from 'mongoose' + +import {modelTypes} from '../../common/constants' + +const {Schema} = mongoose + +const FoodItemSchema = new Schema({ + name: { + type: String, + trim: true + }, + quantity: { + type: Number, + default: 0 + }, + startDate: { + type: Date, + default: Date.now + }, + frequency: { + type: Number, + default: 1 + }, + deleted: { + type: Boolean, + default: false + } +}) + +const FoodSchema = new Schema({ + category: { + type: String, + required: 'Please fill in a category name', + trim: true + }, + items: [FoodItemSchema], + deleted: { + type: Boolean, + default: false + } +}) + +export default mongoose.model(modelTypes.FOOD, FoodSchema) +export const FoodItem = mongoose.model(modelTypes.FOOD_ITEM, FoodItemSchema) \ No newline at end of file diff --git a/server1/models/index.js b/server1/models/index.js new file mode 100644 index 00000000..a7621e35 --- /dev/null +++ b/server1/models/index.js @@ -0,0 +1,14 @@ +export {default as Customer} from './customer' +export {default as Donation} from './donation' +export {default as Donor} from './donor' +export {default as Food} from './food' +export {default as Media} from './media' +export {default as Page} from './page' +export { + Field as Field, + Section as Section, + Questionnaire as Questionnaire +} from './questionnaire' +export {default as Settings} from './settings' +export {default as User} from './user' +export {default as Volunteer} from './volunteer' diff --git a/server1/models/location-schema.js b/server1/models/location-schema.js new file mode 100644 index 00000000..c94eb800 --- /dev/null +++ b/server1/models/location-schema.js @@ -0,0 +1,16 @@ +import mongoose from 'mongoose' + +const {Schema} = mongoose + +export default Schema({ + lat: { + type: Number, + required: true + }, + lng: { + type: Number, + required: true + } +}, { + _id: false +}) diff --git a/server1/models/media.js b/server1/models/media.js new file mode 100644 index 00000000..189f00cc --- /dev/null +++ b/server1/models/media.js @@ -0,0 +1,29 @@ +import mongoose from 'mongoose' + +import {modelTypes} from '../../common/constants' + +const {Schema} = mongoose + +/** + * Settings Schema + */ +const MediaSchema = new Schema({ + path: { + type: String, + default: 'media/' + }, + logo: { + type: String, + default: 'logo.png' + }, + signature: { + type: String, + default: 'signature.png' + }, + favicon: { + type: String, + default: 'favicon.ico' + } +}) + +export default mongoose.model(modelTypes.MEDIA, MediaSchema) diff --git a/server1/models/notification.js b/server1/models/notification.js new file mode 100644 index 00000000..0f64564b --- /dev/null +++ b/server1/models/notification.js @@ -0,0 +1,25 @@ + +import mongoose from 'mongoose' + +const Schema = mongoose.Schema + +/** + * Notification Schema + */ +export const notificationSchema = new Schema({ + message: { + type: String, + required: true + }, + url: { + type: String, + required: true + }, + date: { + type: Date, + required: true, + default: Date.now() + } +}, { + _id: false +}) diff --git a/server1/models/package.js b/server1/models/package.js new file mode 100644 index 00000000..ae6a2b8b --- /dev/null +++ b/server1/models/package.js @@ -0,0 +1,41 @@ +import mongoose from 'mongoose' + +import {modelTypes} from '../../common/constants' + +const {Schema} = mongoose + +const PackageSchema = new Schema({ + customer: { + type: Number, + ref: modelTypes.CUSTOMER, + required: true + }, + status: { + type: String, + required: true, + enum: ['Packed', 'Delivered'], + }, + packedBy: { + type: Number, + ref: modelTypes.USER, + required: true + }, + datePacked: { + type: Date, + default: Date.now + }, + dateReceived: { + type: Date + }, + contents: { + type: [{type: Schema.Types.ObjectId, ref: modelTypes.FOOD_ITEM}], + required: true, + validate: [arrayMinElements, 'Path `{PATH}` is required to have at least one element.'] + } +}) + +function arrayMinElements(val) { + return val.length > 0 +} + +export default mongoose.model(modelTypes.PACKAGE, PackageSchema) diff --git a/server1/models/page.js b/server1/models/page.js new file mode 100644 index 00000000..dafddac8 --- /dev/null +++ b/server1/models/page.js @@ -0,0 +1,29 @@ +import mongoose from 'mongoose' + +import {modelTypes, pageTypes} from '../../common/constants' + +const {Schema} = mongoose + +const PageSchema = new Schema({ + identifier: { + type: String, + required: true + }, + title: { + type: String, + required: true + }, + type: { + type: String, + enum: [pageTypes.PAGE, pageTypes.EMAIL], + default: pageTypes.PAGE + }, + disabled: { + type: Boolean, + default: false + }, + subject: String, + body: String +}) + +export default mongoose.model(modelTypes.PAGE, PageSchema) diff --git a/server1/models/questionnaire.js b/server1/models/questionnaire.js new file mode 100644 index 00000000..32b30d81 --- /dev/null +++ b/server1/models/questionnaire.js @@ -0,0 +1,65 @@ +import mongoose from 'mongoose' +import uuid from 'uuid' +import {values} from 'lodash' + +import {fieldTypes, widgetTypes, modelTypes} from '../../common/constants' + +const {Schema} = mongoose + +const FieldSchema = new Schema({ + _id: { + type: String, + default: uuid.v4 + }, + label: { + type: String, + required: true, + trim: true + }, + type: { + type: String, + required: true, + enum: [...values(fieldTypes), ...values(widgetTypes)] + }, + choices: { + type: String, + trim: true + }, + rows: String, + columns: String, + required: { + type: Boolean, + default: false + } +}) + +const SectionSchema = new Schema({ + _id: { + type: String, + default: uuid.v4 + }, + name: { + type: String, + required: true, + trim: true + }, + fields: [FieldSchema] +}) + +const QuestionnaireSchema = new Schema({ + name: { + type: String, + unique: true, + required: true, + trim: true + }, + identifier: { + type: String, + unique: true, + required: true, + trim: true + }, + sections: [SectionSchema] +}) + +export default mongoose.model(modelTypes.QUESTIONNAIRE, QuestionnaireSchema) diff --git a/server1/models/settings.js b/server1/models/settings.js new file mode 100644 index 00000000..cbdc58ad --- /dev/null +++ b/server1/models/settings.js @@ -0,0 +1,73 @@ +import mongoose from 'mongoose' + +import {modelTypes} from '../../common/constants' +import locationSchema from './location-schema' + +const {Schema} = mongoose + +const receiptFieldSchema = new Schema({ + name: { + type: String, + required: true + }, + value: { + type: String, + required: true + } +}, { + _id: false +}) + +const SettingsSchema = new Schema({ + organization: { + type: String, + trim: true + }, + url: { + type: String, + trim: true + }, + address:{ + type: String, + trim: true + }, + clientIntakeNumber: { + type: String, + trim: true + }, + supportNumber: { + type: String, + trim: true + }, + location: locationSchema, + receiptFields: [receiptFieldSchema], + gmapsApiKey: { + type: String, + trim: true, + select: false + }, + gmapsClientId: { + type: String, + trim: true, + select: false + }, + distanceUnit: { + type: String, + trim: true, + enum: ['km','mi'], + default: 'mi' + }, + moneyUnit: { + type: String, + trim: true, + default: '$' + }, + ein: { + type: String, + trim: true, + default: '' + }, +}) + +export default mongoose.model(modelTypes.SETTINGS, SettingsSchema) +// TODO: set location on save diff --git a/server1/models/user.js b/server1/models/user.js new file mode 100644 index 00000000..e8ecf0fa --- /dev/null +++ b/server1/models/user.js @@ -0,0 +1,121 @@ +import crypto from 'crypto' +import {values} from 'lodash' +import mongoose from 'mongoose' +import autoIncrement from 'mongoose-plugin-autoinc' + +import {ADMIN_ROLE, clientRoles, volunteerRoles, modelTypes} from '../../common/constants' +import {notificationSchema} from './notification' + +const Schema = mongoose.Schema + +/** + * A Validation function for local strategy properties + */ +var validateLocalStrategyProperty = function(property) { + return ((this.provider !== 'local' && !this.updated) || property.length) +} + +/** + * A Validation function for local strategy password + */ +var validateLocalStrategyPassword = function(password) { + return (this.provider !== 'local' || (password && password.length > 6)) +} + +/** + * User Schema + */ +const UserSchema = new Schema({ + firstName: { + type: String, + trim: true, + default: '', + validate: [validateLocalStrategyProperty, 'Please fill in your first name'] + }, + lastName: { + type: String, + trim: true, + default: '', + validate: [validateLocalStrategyProperty, 'Please fill in your last name'] + }, + displayName: { + type: String, + trim: true + }, + email: { + type: String, + trim: true, + unique: true, + validate: [validateLocalStrategyProperty, 'Please fill in your email'], + match: [/.+@.+\..+/, 'Please fill a valid email address'] + }, + password: { + type: String, + default: '', + select: false, + validate: [validateLocalStrategyPassword, 'Password should be longer'] + }, + salt: { + type: String, + select: false + }, + provider: { + type: String, + required: 'Provider is required' + }, + google: {}, + roles: [{ + type: String, + enum: [ADMIN_ROLE, ...values(clientRoles), ...values(volunteerRoles)] + }], + updated: Date, + created: { + type: Date, + default: Date.now + }, + /* For reset password */ + resetPasswordToken: String, + resetPasswordExpires: Date, + /* For notifications */ + notifications: [notificationSchema] +}) + +/** + * Hook a pre save method to hash the password + */ +UserSchema.pre('save', function(next) { + if (this.password && this.password.length > 6) { + this.salt = new Buffer(crypto.randomBytes(16).toString('base64'), 'base64') + this.password = this.hashPassword(this.password) + } + + next() +}) + +/** + * Create instance method for hashing a password + */ +UserSchema.methods.hashPassword = function(password) { + if (this.salt && password) { + return crypto.pbkdf2Sync(password, this.salt, 10000, 64, 'sha512').toString('base64') + } else { + return password + } +} + +/** + * Create instance method for authenticating user + */ +UserSchema.methods.authenticate = function(password) { + return this.password === this.hashPassword(password) +} + +/** + * Schema plugins + */ +UserSchema.plugin(autoIncrement, { + model: modelTypes.USER, + startAt: 10000 +}) + +export default mongoose.model(modelTypes.USER, UserSchema) diff --git a/server1/models/volunteer.js b/server1/models/volunteer.js new file mode 100644 index 00000000..58a38c07 --- /dev/null +++ b/server1/models/volunteer.js @@ -0,0 +1,120 @@ +import mongoose from 'mongoose' + +import {modelTypes, questionnaireIdentifiers} from '../../common/constants' +import locationSchema from './location-schema' +import {getValidator} from '../lib/questionnaire-helpers' +import {locateQuestionnaire} from '../lib/geolocate' + +const {Schema} = mongoose + +var VolunteerSchema = new Schema({ + _id: { + type: Number, + ref: modelTypes.USER + }, + lastName: { + type: String, + trim: true + }, + firstName: { + type: String, + trim: true + }, + email: { + type: String, + trim: true + }, + location: locationSchema, + status: { + type: String, + enum: ['Active', 'Inactive', 'Pending'], + default: 'Pending' + }, + disclaimerAgree: { + type: Boolean + }, + customers: [{ + type: Number, + ref: modelTypes.CUSTOMER + }], + optimized: { + type: Boolean, + default: false + }, + generalNotes: { + type: String, + trim: true + }, + disclaimerSign: { + type: String, + trim: true + }, + disclaimerSignGuardian: { + type: String, + trim: true + }, + shift: [{ + role: String, + date: Date, + duration: Number, + notes: String + }], + test: String, + disclaimerGuardianEmail: { + type: String, + trim: true + }, + fields: [{ + meta: { + type: String, + required: true + }, + value: String + }], + // to populate with user roles, nicer way? + user: { + type: Number, + ref: modelTypes.USER + }, + dateReceived: { + type: Date, + default: Date.now + }, + +}, { + id: false +}) + +VolunteerSchema.path('fields') + .validate(getValidator(questionnaireIdentifiers.VOLUNTEER), 'Invalid field') + +/** + * Hook a pre save method for geolocation + */ +VolunteerSchema.pre('save', async function(next) { + const result = await locateQuestionnaire(this.fields, questionnaireIdentifiers.VOLUNTEER) + if (result instanceof Error) + return next(result) + + if (result) + this.location = result + + return next() +}) + +/** + * Virtual getters & setters + */ +VolunteerSchema.virtual('fullName').get(function() { + var fullName = this.firstName ? this.firstName + ' ' : '' + fullName += this.middleName ? this.middleName + ' ' : '' + fullName += this.lastName ? this.lastName : '' + return fullName +}) + +/** + * Schema options + */ +VolunteerSchema.set('toJSON', {virtuals: true}) + +export default mongoose.model(modelTypes.VOLUNTEER, VolunteerSchema) diff --git a/server1/routes/api.js b/server1/routes/api.js new file mode 100644 index 00000000..1435e19d --- /dev/null +++ b/server1/routes/api.js @@ -0,0 +1,58 @@ +import express from 'express' + +import {NotFoundError, SimulatedError} from '../lib/errors' +import {ADMIN_ROLE} from '../../common/constants' +import users from '../controllers/users' +import customerRoutes from './customer' +import donationRoutes from './donation' +import donorRoutes from './donor' +import foodRoutes from './food' +import deliveryRoutes from './delivery' +import mediaRoutes from './media' +import packingRoutes from './packing' +import pageRoutes from './page' +import questionnaireRoutes from './questionnaire' +import settingsRoutes from './settings' +import usersRoutes from './users' +import volunteerRoutes from './volunteer' + +var apiRouter = express.Router() + +export default (delay, errorProb) => apiRouter + // delay api requests + .use(loadingSimulator(delay)) + .all('/admin/*', users.hasAuthorization([ADMIN_ROLE])) + // uncomment to also test core components + // .use(errorSimulator(errorProb)) + .use(usersRoutes()) + .use(deliveryRoutes()) + .use(pageRoutes()) + .use(mediaRoutes()) + .use(settingsRoutes()) + // for testing non-core components + .use(errorSimulator(errorProb)) + .use(customerRoutes()) + .use(donationRoutes) + .use(donorRoutes()) + .use(foodRoutes()) + .use(packingRoutes) + .use(questionnaireRoutes()) + .use(volunteerRoutes()) + .all('*', () => { + throw new NotFoundError + }) + +function loadingSimulator(time = 0) { + return (req, res, next) => { + setTimeout(next, time) + } +} + +function errorSimulator(chance = 0) { + return (req, res, next) => { + if (Math.random() < chance) { + throw new SimulatedError + } + next() + } +} diff --git a/server1/routes/api.spec.js b/server1/routes/api.spec.js new file mode 100644 index 00000000..7a492bdc --- /dev/null +++ b/server1/routes/api.spec.js @@ -0,0 +1,50 @@ +import apiRouterFactory from './api' + +import {ADMIN_ROLE} from '../../common/constants' + +describe('Api router', function() { + let userControllerMock + let apiRouterMock + + before(async function() { + await initDb() + }) + + beforeEach(function() { + userControllerMock = { + hasAuthorization: sinon.spy() + } + + apiRouterMock = { + use: sinon.stub().returnsThis(), + all: sinon.stub().returnsThis() + } + + apiRouterFactory.__Rewire__('users', userControllerMock) + apiRouterFactory.__Rewire__('apiRouter', apiRouterMock) + apiRouterFactory.__Rewire__('customerRoutes', () => sinon.spy()) + + apiRouterFactory() + }) + + afterEach(function() { + apiRouterFactory.__ResetDependency__('users') + apiRouterFactory.__ResetDependency__('apiRouter') + apiRouterFactory.__ResetDependency__('customerRoutes') + }) + + after(async function() { + await resetDb() + }) + + it('uses subrouters', function() { + expect(apiRouterMock.use).to.have.been.called + }) + + it('checks admin/ routes for admin role', function () { + expect(userControllerMock.hasAuthorization) + .to.have.been.calledWith([ADMIN_ROLE]) + expect(apiRouterMock.all).to.have.been + .calledWith('/admin/*') + }) +}) diff --git a/server1/routes/customer.js b/server1/routes/customer.js new file mode 100644 index 00000000..34636a9e --- /dev/null +++ b/server1/routes/customer.js @@ -0,0 +1,35 @@ +import Router from 'express-promise-router' + +import customerController from '../controllers/customer' +import userController from '../controllers/users' +import websocketMiddleware from '../lib/websocket-middleware' +import {customer} from '../../common/schemas' + +const sync = {schema: customer} +const saveSchema = {...sync, type: 'customer/SAVE_SUCCESS'} +const deleteSchema = {...sync, type: 'customer/DELETE_SUCCESS'} + +export default () => { + const {requiresLogin} = userController + const {hasAuthorization, canChangeStatus} = customerController + + const customerRouter = Router({mergeParams: true}) + + customerRouter.route('/customer') + .post(requiresLogin, customerController.create) + + customerRouter.route('/customer/:customerId') + .get(requiresLogin, hasAuthorization, customerController.read) + .put(requiresLogin, hasAuthorization, canChangeStatus, customerController.update) + + customerRouter.get('/admin/customer', customerController.list) + + customerRouter.route('/admin/customers/:customerId') + .put(websocketMiddleware(saveSchema), customerController.update) + .delete(websocketMiddleware(deleteSchema), customerController.delete) + + // Finish by binding the customer middleware + customerRouter.param('customerId', customerController.customerById) + + return customerRouter +} diff --git a/server1/routes/delivery.js b/server1/routes/delivery.js new file mode 100644 index 00000000..da46e9e8 --- /dev/null +++ b/server1/routes/delivery.js @@ -0,0 +1,26 @@ +import Router from 'express-promise-router' + +import deliveryController from '../controllers/delivery' +import websocketMiddleware from '../lib/websocket-middleware' +import {arrayOfCustomers, arrayOfVolunteers} from '../../common/schemas' + +const sync = { + syncTo: ['volunteer'], + type: 'delivery/assignment/ASSIGN_SUCCESS', + schema: { + customers: arrayOfCustomers, + volunteers: arrayOfVolunteers + } +} + +export default () => { + const deliveryRouter = Router({mergeParams: true}) + + deliveryRouter.route('/delivery/directions') + .get(/*deliveryController.hasAuthorization,*/ deliveryController.directions) + + deliveryRouter.route('/admin/delivery/assign') + .post(websocketMiddleware(sync), deliveryController.assign) + + return deliveryRouter +} diff --git a/server1/routes/donation.js b/server1/routes/donation.js new file mode 100644 index 00000000..df2131fc --- /dev/null +++ b/server1/routes/donation.js @@ -0,0 +1,18 @@ +import Router from 'express-promise-router' +import donationController from '../controllers/donation' +import usersController from '../controllers/users' + +const {requiresLogin} = usersController + +const donationRouter = Router({mergeParams: true}) + +donationRouter.route('/donations') + .post(donationController.create) + +donationRouter.route('/admin/donations/:donationId/approve') + .put(donationController.approve) + +donationRouter.route('/donations/:donationId') + .put(requiresLogin, donationController.hasAuthorization, donationController.sendEmail) + +export default donationRouter diff --git a/server1/routes/donor.js b/server1/routes/donor.js new file mode 100644 index 00000000..b8944ff2 --- /dev/null +++ b/server1/routes/donor.js @@ -0,0 +1,31 @@ +import Router from 'express-promise-router' + +import donorController from '../controllers/donor' +import userController from '../controllers/users' + +export default () => { + const {requiresLogin} = userController + const {hasAuthorization} = donorController + + const donorRouter = Router({mergeParams: true}) + + // Donor routes for users + donorRouter.route('/donor') + .post(requiresLogin, donorController.create) + donorRouter.route('/donor/:donorId') + .get(requiresLogin, hasAuthorization, donorController.read) + .put(requiresLogin, hasAuthorization, donorController.update) + + // Donor routes for admin + donorRouter.route('/admin/donors') + .get(donorController.list) + donorRouter.route('/admin/donors/:donorId') + .get(donorController.read) + .put(donorController.update) + .delete(donorController.delete) + + // Finish by binding the donor middleware + donorRouter.param('donorId', donorController.donorById) + + return donorRouter +} diff --git a/server1/routes/food.js b/server1/routes/food.js new file mode 100644 index 00000000..61f4df41 --- /dev/null +++ b/server1/routes/food.js @@ -0,0 +1,41 @@ +import Router from 'express-promise-router' + +import foodController from '../controllers/food' +import userController from '../controllers/users' +import websocketMiddleware from '../lib/websocket-middleware' +import {foodCategory} from '../../common/schemas' + +const sync = { + syncTo: ['volunteer'], + schema: foodCategory +} + +const saveCatSchema = {...sync, type: 'foodCategory/SAVE_SUCCESS'} +const deleteCatSchema = {...sync, type: 'foodCategory/DELETE_SUCCESS'} +const saveItemSchema = {...sync, type: 'foodItem/SAVE_SUCCESS'} +const deleteItemSchema = {syncTo: ['volunteer'], type: 'foodItem/DELETE_SUCCESS'} + +export default () => { + const {requiresLogin} = userController + + const foodRouter = Router({mergeParams: true}) + + foodRouter.use('/foods', requiresLogin) + foodRouter.route('/foods') + .get(foodController.list) + .post(websocketMiddleware(saveCatSchema), foodController.create) + foodRouter.route('/foods/:foodId') + .put(websocketMiddleware(saveCatSchema), foodController.update) + .delete(websocketMiddleware(deleteCatSchema), foodController.delete) + foodRouter.route('/foods/:foodId/items') + .post(websocketMiddleware(saveItemSchema), foodController.createItem) + foodRouter.route('/foods/:foodId/items/:itemId') + .put(websocketMiddleware(saveItemSchema), foodController.updateItem) + .delete(websocketMiddleware(deleteItemSchema), foodController.deleteItem) + + // Finish by binding the food middleware + foodRouter.param('foodId', foodController.foodById) + foodRouter.param('itemId', foodController.itemById) + + return foodRouter +} diff --git a/server1/routes/media.js b/server1/routes/media.js new file mode 100644 index 00000000..6facbb32 --- /dev/null +++ b/server1/routes/media.js @@ -0,0 +1,20 @@ +import Router from 'express-promise-router' +import {upload} from '../lib/media-helpers' + +import mediaController from '../controllers/media' + +export default () => { + const mediaRouter = Router({mergeParams: true}) + + mediaRouter.route('/admin/media/upload') + .post(upload.fields([ + {name: 'logo', maxCount: 1}, + {name: 'signature', maxCount: 1}, + {name: 'favicon', maxCount: 1} + ]), mediaController.upload) + + mediaRouter.route('/media') + .get(mediaController.read) + + return mediaRouter +} diff --git a/server1/routes/packing.js b/server1/routes/packing.js new file mode 100644 index 00000000..70d576e9 --- /dev/null +++ b/server1/routes/packing.js @@ -0,0 +1,27 @@ +import Router from 'express-promise-router' + +import packingController from '../controllers/packing' +import websocketMiddleware from '../lib/websocket-middleware' +import {arrayOfPackages} from '../../common/schemas' +import usersController from '../controllers/users' + +const {requiresLogin} = usersController + +const packingRouter = Router({mergeParams: true}) + +const sync = { + syncTo: ['volunteer'], + type: 'packing/PACK_SUCCESS', + schema: { + packages: arrayOfPackages, + } +} + +packingRouter.route('/packing') + .all(requiresLogin) + .get(packingController.hasAuthorization, packingController.list) + .post(packingController.hasAuthorization, websocketMiddleware(sync), packingController.pack) + .delete(packingController.hasAuthorization, packingController.unpack) + .put(packingController.hasAuthorization, packingController.complete) + +export default packingRouter diff --git a/server1/routes/page.js b/server1/routes/page.js new file mode 100644 index 00000000..52333e7a --- /dev/null +++ b/server1/routes/page.js @@ -0,0 +1,20 @@ +import Router from 'express-promise-router' + +import pageController from '../controllers/page' + +export default () => { + const pageRouter = Router({mergeParams: true}) + + pageRouter.route('/admin/pages') + .get(pageController.list) + + pageRouter.route('/admin/pages/:identifier') + .put(pageController.update) + + pageRouter.route('/pages/:identifier') + .get(pageController.read) + + pageRouter.param('identifier', pageController.pageByIdentifier) + + return pageRouter +} diff --git a/server1/routes/questionnaire.js b/server1/routes/questionnaire.js new file mode 100644 index 00000000..78b4d4b7 --- /dev/null +++ b/server1/routes/questionnaire.js @@ -0,0 +1,25 @@ +import Router from 'express-promise-router' + +import questionnaireController from '../controllers/questionnaire' +import usersController from '../controllers/users' + +const {requiresLogin} = usersController + +export default () => { + const questionnaireRouter = Router({mergeParams: true}) + + questionnaireRouter.use('/questionnaires', requiresLogin) + + questionnaireRouter.route('/questionnaires') + .get(questionnaireController.query) + // .post(questionnaireController.create) + + questionnaireRouter.route('/questionnaires/:questionnaireId') + .get(questionnaireController.get) + .put(questionnaireController.update) + // .delete(questionnaireController.delete) + + questionnaireRouter.param('questionnaireId', questionnaireController.questionnaireById) + + return questionnaireRouter +} diff --git a/server1/routes/settings.js b/server1/routes/settings.js new file mode 100644 index 00000000..26382c25 --- /dev/null +++ b/server1/routes/settings.js @@ -0,0 +1,16 @@ +import Router from 'express-promise-router' + +import usersController from '../controllers/users' +import settingsController from '../controllers/settings' + +const {requiresLogin} = usersController + +export default () => { + const settingsRouter = Router({mergeParams: true}) + + settingsRouter.route('/settings') + .post(requiresLogin, settingsController.save) + .get(settingsController.read) + + return settingsRouter +} diff --git a/server1/routes/users.js b/server1/routes/users.js new file mode 100644 index 00000000..e2fdcde9 --- /dev/null +++ b/server1/routes/users.js @@ -0,0 +1,76 @@ +import Router from 'express-promise-router' +import passport from 'passport' +import users from '../controllers/users' + +const {requiresLogin} = users +const userRouter = Router({mergeParams: true}) + +export default () => { + userRouter.route('/users').get(users.list) + + // Current logged in user + userRouter.route('/users/me') + .get(users.me) + .put(requiresLogin, users.updateProfile) + + userRouter.route('/users/me/notifications') + .get(requiresLogin, users.listNotifications) + .delete(requiresLogin, users.removeNotification) + + userRouter.route('/admin/users/:userId') + .get(users.getById) + .put(users.update) + + // Setting up the users password api + userRouter.route('/users/password').post(users.changePassword) + userRouter.route('/auth/forgot').post(users.forgot) + userRouter.route('/auth/reset/:token').post(users.reset) + + // Setting up the users authentication api + userRouter.route('/auth/signup').post(users.signup) + userRouter.route('/auth/signin').post(users.signin) + userRouter.route('/auth/signout').get(users.signout) + + // Setting the google oauth routes + userRouter.route('/auth/google').get( + (req, res, next) => { + const action = req.query.action || 'login' + passport.authenticate('google', + { + scope: [ + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/userinfo.email'], + state: JSON.stringify({action}) + } + )(req, res, next) + } + ) + + userRouter.route('/auth/google/callback').get( + (req, res, next) => passport.authenticate('google', { + scope: [ + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/userinfo.email' + ] + }, (err, user, googleProfile) => { + if (err) { + return res.status(500).send(err.message) + } else if (user) { + req.login(user, err => { + if (err) return res.status(500).send(err.message) + return res.redirect('/') + }) + } else if (googleProfile) { + //google login but no account exists + return res.redirect('/users/confirm-new-google-account') + } else { + return res.status(500).send("Server error. Could not login the user") + } + })(req, res, next) + ) + + // Finish by binding the user middleware + userRouter.param('userId', users.userByID) + + return userRouter +} diff --git a/server1/routes/volunteer.js b/server1/routes/volunteer.js new file mode 100644 index 00000000..bfdfe7d4 --- /dev/null +++ b/server1/routes/volunteer.js @@ -0,0 +1,46 @@ +import Router from 'express-promise-router' + +import volunteerController from '../controllers/volunteer' +import userController from '../controllers/users' +//import websocketMiddleware from '../lib/websocket-middleware' +//import {volunteer} from '../../common/schemas' + +export default () => { + const {requiresLogin} = userController + const {hasAuthorization} = volunteerController + + const volunteerRouter = Router({mergeParams: true}) + + // Volunteer routes for user + volunteerRouter.route('/volunteer') + .post(requiresLogin, volunteerController.create) + volunteerRouter.route('/volunteer/:volunteerId') + .get(requiresLogin, hasAuthorization, volunteerController.read) + .put(requiresLogin, hasAuthorization, volunteerController.update) + + // Updates a shift + volunteerRouter.route('/volunteers/updateShift') + .put(volunteerController.updateShift) + + // Volunteer routes for admin for volunteer scheduling + volunteerRouter.route('/volunteers/addShift') + .put(volunteerController.addShift) + + volunteerRouter.route('/volunteers/deleteShift') + .put(volunteerController.deleteShift) + + // Volunteer routes for admin + volunteerRouter.route('/admin/volunteers') + .get(volunteerController.list) + volunteerRouter.route('/admin/volunteers/:volunteerId') + .get(volunteerController.read) + .put(volunteerController.update) + .delete(volunteerController.delete) + + // Finish by binding the volunteer middleware + volunteerRouter.param('volunteerId', volunteerController.volunteerById) + + + + return volunteerRouter +} diff --git a/server1/server.js b/server1/server.js new file mode 100644 index 00000000..d7729bc2 --- /dev/null +++ b/server1/server.js @@ -0,0 +1,33 @@ +import mongoose from 'mongoose' +import http from 'http' +import socketIO from 'socket.io' +import socketIOSession from 'express-socket.io-session' + +import config from './config' +import setupPassport from './config/passport' +import setupExpress from './config/express' + +process.on('unhandledRejection', err => console.error(err)) + +mongoose.Promise = global.Promise +mongoose.connect(config.db) +const db = mongoose.connection + +db.on('error', function(err) { + console.error('Mongoose error', err) +}) + +db.once('open', function() { + console.info('Connected to', config.db) + + const io = socketIO() + const app = setupExpress(io) + const server = http.createServer(app).listen(config.port) + + setupPassport() + + io.listen(server) + io.use(socketIOSession(app.get('sharedSession'))) + + console.info('Application started on port', config.port) +}) diff --git a/server1/tests/helpers.js b/server1/tests/helpers.js new file mode 100644 index 00000000..c3a79a4b --- /dev/null +++ b/server1/tests/helpers.js @@ -0,0 +1,69 @@ +import express from 'express' + +import setupPassport from '../config/passport' +import app from '../config/express' +import User from '../models/user' + +/** + * returns a user and express app where the user is authenticated + * + * @param {object} userModel new or existing user to create mock session for + * @return {promise} + */ +export const createUserSession = async function(userModel) { + setupPassport() + + const user = userModel._id ? + userModel : + await new User(userModel).save() + + const mockApp = express() + mockApp.all('*', mockSessionMiddleware(user)) + mockApp.use(app()) + + return { + user, + app: mockApp + } +} + +/** + * create an express app without an authenticated user + * + * @return {object} + */ +export const createGuestSession = function() { + setupPassport() + const mockApp = express() + mockApp.use(app()) + + return mockApp +} + +/** + * create a user model + * + * @param {string} username + * @param {string} role + * @param {object} props additional properties to set + * @return {object} + */ +export const createTestUser = (username, role, props = null) => ({ + firstName: username, + lastName: 'test', + roles: [role], + email: `${username}@test.com`, + password: 'password', + provider: 'local', + ...props +}) + +function mockSessionMiddleware({_id, roles}) { + return (req, res, next) => { + req.session = { + passport: {user: {_id, roles}} + } + + next() + } +} \ No newline at end of file diff --git a/server1/tests/integration/customer.spec.js b/server1/tests/integration/customer.spec.js new file mode 100644 index 00000000..065f04f4 --- /dev/null +++ b/server1/tests/integration/customer.spec.js @@ -0,0 +1,430 @@ +import { + ADMIN_ROLE, + clientRoles, + volunteerRoles, + questionnaireIdentifiers +} from '../../../common/constants' +import Customer from '../../models/customer' +import Volunteer from '../../models/volunteer' +import {createGuestSession, createUserSession, createTestUser} from '../helpers' +import User from '../../models/user' +import Questionnaire from '../../models/questionnaire' +import Food, {FoodItem} from '../../models/food' +import Package from '../../models/package' + +describe('Customer Api', function() { + before(async function() { + await initDb() + await Customer.find().remove() + await Volunteer.find().remove() + await User.find().remove() + await Package.find().remove() + await Questionnaire.find().remove() + await new Questionnaire({ + name: 'Customer Application', + identifier: questionnaireIdentifiers.CUSTOMER + }).save() + await new Questionnaire({ + name: 'Volunteer Application', + identifier: questionnaireIdentifiers.VOLUNTEER + }).save() + }) + + afterEach(async function() { + await Customer.find().remove() + await User.find().remove() + await Package.find().remove() + await Volunteer.find().remove() + }) + + after(async function() { + await Questionnaire.find().remove() + await resetDb() + }) + + describe('POST /api/customer', function() { + it('requires login', async function() { + const app = createGuestSession() + const request = supertest.agent(app) + + return request.post('/api/customer').expect(401) + }) + + it('creates customers', async function() { + const newCustomer = createTestUser('user', clientRoles.CUSTOMER) + const {user, app} = await createUserSession(newCustomer) + const request = supertest.agent(app) + + return request.post('/api/customer') + .send(user) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('status', 'Pending') + }) + .expect(200) + }) + + + it('doesn\'t create duplicate customers', async function() { + const newCustomer = createTestUser('user', clientRoles.CUSTOMER) + const {user, app} = await createUserSession(newCustomer) + const request = supertest.agent(app) + + await request.post('/api/customer') + .send(user) + + return request.post('/api/customer') + .send(user) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('message', 'Unique field already exists') + }) + .expect(400) + }) + }) + + describe('GET /api/customer/:customerId', function() { + it('requires login', async function() { + const customer = createTestUser('customer', clientRoles.CUSTOMER) + const customerSession = await createUserSession(customer) + await supertest.agent(customerSession.app).post('/api/customer').send(customerSession.user) + + const app = createGuestSession() + const request = supertest.agent(app) + + return request.get(`/api/customer/${customerSession.user._id}`).expect(401) + }) + + it('shows a customer', async function() { + const newCustomer = createTestUser('user', clientRoles.CUSTOMER) + const {user, app} = await createUserSession(newCustomer) + const request = supertest.agent(app) + + await request.post('/api/customer') + .send(user) + + return request.get(`/api/customer/${user._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'user') + }) + .expect(200) + }) + + it('doesn\'t show non-existing customer', async function() { + const newCustomer = createTestUser('user', clientRoles.CUSTOMER) + const {user, app} = await createUserSession(newCustomer) + const request = supertest.agent(app) + + return request.get(`/api/customer/${user._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('message', 'Not found') + }) + .expect(404) + }) + + it('doesn\'t show other customers', async function() { + const firstCustomer = createTestUser('user1', clientRoles.CUSTOMER) + const secondCustomer = createTestUser('user2', clientRoles.CUSTOMER) + + const first = await createUserSession(firstCustomer) + const second = await createUserSession(secondCustomer) + + const firstReq = supertest.agent(first.app) + const secondReq = supertest.agent(second.app) + + await secondReq.post('/api/customer') + .send(second.user) + + return firstReq.get(`/api/customer/${second.user._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('message', 'User is not authorized') + }) + .expect(403) + }) + + it('shows any customer to admins', async function() { + const customer = createTestUser('customer', clientRoles.CUSTOMER) + const admin = createTestUser('admin', ADMIN_ROLE) + + const customerSession = await createUserSession(customer) + await supertest.agent(customerSession.app).post('/api/customer').send(customerSession.user) + + const adminSession = await createUserSession(admin) + return supertest.agent(adminSession.app).get(`/api/customer/${customerSession.user._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'customer') + }) + .expect(200) + }) + }) + + describe('PUT /api/customer/:customerId', function() { + it('requires login', async function() { + const customer = createTestUser('customer', clientRoles.CUSTOMER) + const customerSession = await createUserSession(customer) + await supertest.agent(customerSession.app).post('/api/customer').send(customerSession.user) + + const app = createGuestSession() + const request = supertest.agent(app) + + return request.put(`/api/customer/${customerSession.user._id}`).expect(401) + }) + + it('updates customers', async function() { + const newCustomer = createTestUser('user', clientRoles.CUSTOMER) + const {user, app} = await createUserSession(newCustomer) + const request = supertest.agent(app) + + const savedCustomer = (await request.post('/api/customer') + .send(user)).body + + const updatedCustomer = { + ...savedCustomer, + firstName: 'updated' + } + + return request.put(`/api/customer/${user._id}`) + .send(updatedCustomer) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'updated') + }) + .expect(200) + }) + + it('cannot update other customers', async function() { + const {user: customer, app: customerApp} = await createUserSession( + createTestUser('customer', clientRoles.CUSTOMER) + ) + await supertest.agent(customerApp).post('/api/customer').send(customer) + + const {app} = await createUserSession( + createTestUser('user', clientRoles.CUSTOMER) + ) + return supertest.agent(app).put(`/api/customer/${customer._id}`).expect(403) + }) + + it('updates other customers if admin', async function() { + const {user, app: customerApp} = await createUserSession( + createTestUser('customer', clientRoles.CUSTOMER) + ) + const customer = (await supertest.agent(customerApp).post('/api/customer').send(user)).body + + const {app} = await createUserSession( + createTestUser('admin', ADMIN_ROLE) + ) + return supertest.agent(app).put(`/api/customer/${customer._id}`) + .send({...customer, firstName: 'updated'}) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'updated') + }) + .expect(200) + }) + + + + + + + + + + + + + + + + + + }) + + describe('GET /api/admin/customer', function() { + it('lists customers', async function() { + const newAdmin = createTestUser('admin', ADMIN_ROLE) + const newCustomer = createTestUser('user', clientRoles.CUSTOMER) + const admin = await createUserSession(newAdmin) + const customer = await createUserSession(newCustomer) + + const adminReq = supertest.agent(admin.app) + const customerReq = supertest.agent(customer.app) + + await customerReq.post('/api/customer') + .send(customer.user) + + return adminReq.get(`/api/admin/customer`) + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.lengthOf(1) + expect(res.body[0]).to.have.property('firstName', 'user') + }) + .expect(200) + }) + }) + + describe('DELETE /api/admin/customers/:customerId', function() { + it('deletes customers', async function() { + const newAdmin = createTestUser('admin', ADMIN_ROLE) + const newCustomer = createTestUser('user', clientRoles.CUSTOMER) + const admin = await createUserSession(newAdmin) + const customer = await createUserSession(newCustomer) + + const adminReq = supertest.agent(admin.app) + const customerReq = supertest.agent(customer.app) + + await customerReq.post('/api/customer') + .send(newCustomer) + + return adminReq.delete(`/api/admin/customers/${customer.user._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'user') + }) + .expect(200) + }) + + it('Won\'t delete a customer that has packages', async function () { + const foodItems = [new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 })] + const food = await Food.create({ category: 'test', items: foodItems }) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + await Package.create({ + customer: customer._id, + status: 'Packed', + packedBy: 0, + contents: food.items.map(item => item._id.toString()) + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + // delete the customer associated with the package + return request.delete(`/api/admin/customers/${customer._id}`) + .expect(409) + .expect(function(res) { + expect(res.body).to.have.property('message', 'This customer has packages and can\'t be deleted') + }) + }) + + it('When deleting a customer, it deletes its occurance in Volunteers.customers', async function() { + const customer1 = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const customer2 = await Customer.create({ + _id: 2, + firstName: 'Ben', + lastName: 'Franklin', + email: 'bf@example.com', + }) + + const volunteer = await Volunteer.create({ + _id: 3, + customers: [customer1._id, customer2._id] + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + await request.delete(`/api/admin/customers/${customer1._id}`).expect(200) + + const updatedVolunteer = await Volunteer.findById(volunteer._id).lean() + expect(updatedVolunteer.customers).to.eql([customer2._id]) + }) + }) + + describe('PUT /api/admin/customers/:customerId', function() { + it('assigns customers', async function() { + const newAdmin = createTestUser('admin', ADMIN_ROLE) + const newCustomer = createTestUser('customer', clientRoles.CUSTOMER) + const newVolunteer = createTestUser('driver', null, {roles: [ + clientRoles.VOLUNTEER, volunteerRoles.DRIVER]}) + + const admin = await createUserSession(newAdmin) + const customer = await createUserSession(newCustomer) + const volunteer = await createUserSession(newVolunteer) + + const adminReq = supertest.agent(admin.app) + const customerReq = supertest.agent(customer.app) + const volunteerReq = supertest.agent(volunteer.app) + + const savedCustomer = (await customerReq.post('/api/customer') + .send(newCustomer)).body + + const savedVolunteer = (await volunteerReq.post('/api/volunteer') + .send(newVolunteer)).body + + return adminReq.post('/api/admin/delivery/assign') + .send({ + customerIds: [savedCustomer._id], + driverId: savedVolunteer._id + }) + .expect(200) + .expect(res => { + expect(res.body.customers).to.be.an('array') + expect(res.body.customers).to.have.length(1) + expect(res.body.customers[0]).to.have.property('assignedTo', savedVolunteer._id) + }) + }) + }) + + describe('notifications', function(){ + it('admin and volunteer notifications for a customer creation and update', async function() { + const newCustomer = createTestUser('customer', clientRoles.CUSTOMER) + const {app} = await createUserSession(newCustomer) + const request = supertest.agent(app) + + await User.create({ + firstName: 'admin', + lastName: 'test', + email: 'admin@test.com', + roles: [ADMIN_ROLE], + provider: 'local', + password: 'password' + }) + + const customer = (await request.post('/api/customer').send(newCustomer)).body + + await User.create({ + firstName: 'volunteer', + lastName: 'test', + email: 'volunteer@test.com', + password: 'password', + provider: 'local', + status:'Active' + }) + const user = await User.findOne({email:'volunteer@test.com'}) + await Volunteer.create({ + _id: user._id, + firstName: user.firstname, + lastName: user.lastName, + email: user.email, + customers:[customer._id], + user: user._id, + status:'Active' + }) + + await request.put(`/api/customer/${customer._id}`).send({...customer, firstName: 'updated'}) + + const volunteer = (await request.post('/api/auth/signin').send({email: 'volunteer@test.com', password: 'password'})).body + const admin = (await request.post('/api/auth/signin').send({email: 'admin@test.com', password: 'password'})).body + return expect(volunteer.notifications[0].message).to.equal(`Customer updated test was updated!`) && expect(admin.notifications[0].message).to.equal(`Customer customer test was created!`) + }) + }) +}) diff --git a/server1/tests/integration/donation.spec.js b/server1/tests/integration/donation.spec.js new file mode 100644 index 00000000..98a85b9a --- /dev/null +++ b/server1/tests/integration/donation.spec.js @@ -0,0 +1,149 @@ +import { + questionnaireIdentifiers, + clientRoles, + ADMIN_ROLE, +} from '../../../common/constants' +import Donor from '../../models/donor' +import Donation from '../../models/donation' +import Questionnaire from '../../models/questionnaire' +import User from '../../models/user' +import {createUserSession, createTestUser} from '../helpers' + +describe('Donation Api', function() { + before(async function() { + await initDb() + await Donor.find().remove() + await User.find().remove() + await new Questionnaire({ + name: 'Donor Application', + identifier: questionnaireIdentifiers.DONOR + }).save() + }) + + afterEach(async function() { + await Donor.find().remove() + await User.find().remove() + await Donation.find().remove() + }) + + after(async function() { + await Questionnaire.find().remove() + await resetDb() + }) + + describe('User routes', function() { + it('creates donations', async function() { + const testDonor = await createTestUser('user', clientRoles.DONOR) + const {user, app} = await createUserSession(testDonor) + const request = supertest.agent(app) + const donor = await Donor.create({ + ...user, + _id: user.id, + }) + + const items = [ + {name: 'Potatoes', value: 10}, + {name: 'Carrots', value: 5,}, + {name: 'Spaghetti', value: 5,}, + ] + + const donation = { + donor: donor._id, + description: 'User Donation', + items: items, + } + + return request.post('/api/donations') + .send(donation) + .expect(res => { + expect(res.body.donor).to.be.an('object') + expect(res.body.donor).to.have.property('_id', donor._id) + expect(res.body.donation).to.be.an('object') + expect(res.body.donation).to.have.property('total', 20) + expect(res.body.donation).to.have.property('description', donation.description) + expect(res.body.donation.items).to.be.deep.eql(items) + }) + .expect(200) + }) + + it('does not allow non-admins to create donations for other donors', async function() { + const testDonor = await createTestUser('userdonor', clientRoles.DONOR) + const testDonor2 = await createTestUser('userdonor2', clientRoles.DONOR) + const firstSession = await createUserSession(testDonor) + const secondSession = await createUserSession(testDonor2) + const request = supertest.agent(firstSession.app) + + const donor2 = await Donor.create({ + ...secondSession.user, + _id: secondSession.user.id, + }) + + const donation = { + donor: donor2._id, + description: 'User Donation', + items: [ + {name: 'Potatoes', value: 10}, + ] + } + + return request.post('/api/donations') + .send(donation) + .expect(401) + }) + + it('allows admins to create donations for other donors', async function() { + const admin = await createTestUser('admin', ADMIN_ROLE) + const testDonor = await createTestUser('userdonor', clientRoles.DONOR) + const firstSession = await createUserSession(admin) + const secondSession = await createUserSession(testDonor) + const request = supertest.agent(firstSession.app) + + const donor = await Donor.create({ + ...secondSession.user, + _id: secondSession.user.id, + }) + + const donation = { + donor: donor._id, + description: 'User Donation', + items: [ + {name: 'Potatoes', value: 10}, + ] + } + + return request.post('/api/donations') + .send(donation) + .expect(200) + }) + + it('approves a donation', async function() { + const admin = await createTestUser('admin', ADMIN_ROLE) + const testDonor = await createTestUser('userdonor', clientRoles.DONOR) + const firstSession = await createUserSession(admin) + const secondSession = await createUserSession(testDonor) + const request = supertest.agent(firstSession.app) + + const donor = await Donor.create({ + ...secondSession.user, + _id: secondSession.user.id, + }) + + const response = await request.post('/api/donations') + .send({ + donor: donor._id, + description: 'User Donation', + items: [ + {name: 'Potatoes', value: 10}, + ] + }) + + return request.put('/api/admin/donations/' + response.body.donation._id + '/approve') + .expect(res => { + expect(res.body.donor).to.be.an('object') + expect(res.body).to.have.property('approved', true) + }) + .expect(200) + }) + + }) +}) diff --git a/server1/tests/integration/donor.spec.js b/server1/tests/integration/donor.spec.js new file mode 100644 index 00000000..2187890d --- /dev/null +++ b/server1/tests/integration/donor.spec.js @@ -0,0 +1,127 @@ +import {ADMIN_ROLE, clientRoles, questionnaireIdentifiers} from '../../../common/constants' +import Donor from '../../models/donor' +import {createUserSession, createTestUser} from '../helpers' +import User from '../../models/user' +import Questionnaire from '../../models/questionnaire' + +describe('Donor Api', function() { + before(async function() { + await initDb() + await Donor.find().remove() + await User.find().remove() + await Questionnaire.find().remove() + await new Questionnaire({ + name: 'Donor Application', + identifier: questionnaireIdentifiers.DONOR + }).save() + }) + + afterEach(async function() { + await Donor.find().remove() + await User.find().remove() + }) + + after(async function() { + await Questionnaire.find().remove() + await resetDb() + }) + + describe('User routes', function() { + it('creates donors', async function() { + const testDonor = createTestUser('user', clientRoles.DONOR) + const {user, app} = await createUserSession(testDonor) + const request = supertest.agent(app) + + return request.post('/api/donor') + .send(user) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'user') + }) + .expect(200) + }) + + it('shows a donor', async function() { + const testDonor = createTestUser('user', clientRoles.DONOR) + const {user, app} = await createUserSession(testDonor) + const request = supertest.agent(app) + + const newDonor = (await request.post('/api/donor') + .send(user)).body + + return request.get(`/api/donor/${newDonor._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'user') + }) + .expect(200) + }) + + it('updates a donor', async function() { + const testDonor = createTestUser('user', clientRoles.DONOR) + const {user, app} = await createUserSession(testDonor) + const request = supertest.agent(app) + + const newDonor = (await request.post('/api/donor') + .send(user)).body + + const updatedDonor = { + ...newDonor, + firstName: 'updated' + } + + return request.put(`/api/donor/${newDonor._id}`) + .send(updatedDonor) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'updated') + }) + .expect(200) + }) + }) + + describe('Admin routes', function() { + it('lists donors', async function() { + const testDonor = createTestUser('user', clientRoles.DONOR) + const testAdmin = createTestUser('admin', ADMIN_ROLE) + + const donor = await createUserSession(testDonor) + const admin = await createUserSession(testAdmin) + + const donorRequest = supertest.agent(donor.app) + const adminRequest = supertest.agent(admin.app) + + await donorRequest.post('/api/donor') + .send(donor.user) + + return adminRequest.get('/api/admin/donors') + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(1) + expect(res.body[0]).to.have.property('firstName', 'user') + }) + .expect(200) + }) + + it('deletes a donor', async function() { + const testDonor = createTestUser('user', clientRoles.DONOR) + const testAdmin = createTestUser('admin', ADMIN_ROLE) + + const donor = await createUserSession(testDonor) + const admin = await createUserSession(testAdmin) + + const donorRequest = supertest.agent(donor.app) + const adminRequest = supertest.agent(admin.app) + + const newDonor = (await donorRequest.post('/api/donor') + .send(donor.user)).body + + return adminRequest.delete(`/api/admin/donors/${newDonor._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'user') + }) + .expect(200) + }) + }) +}) diff --git a/server1/tests/integration/food.spec.js b/server1/tests/integration/food.spec.js new file mode 100644 index 00000000..dcea5ffd --- /dev/null +++ b/server1/tests/integration/food.spec.js @@ -0,0 +1,297 @@ +import {ADMIN_ROLE, clientRoles} from '../../../common/constants' +import Food, {FoodItem} from '../../models/food' +import {createUserSession, createTestUser} from '../helpers' +import User from '../../models/user' + +describe('Food Api', function() { + before(async function() { + await initDb() + await Food.find().remove() + await User.find().remove() + }) + + afterEach(async function() { + await Food.find().remove() + await User.find().remove() + }) + + after(async function() { + await resetDb() + }) + + describe('User routes', function() { + it('lists food categories', async function() { + const testUser = createTestUser('user', clientRoles.CUSTOMER) + const testAdmin = createTestUser('admin', ADMIN_ROLE) + + const user = await createUserSession(testUser) + const admin = await createUserSession(testAdmin) + + const userRequest = supertest.agent(user.app) + const adminRequest = supertest.agent(admin.app) + + const testFood = { + category: 'test food', + items: [ + {name: 'test item'} + ] + } + + await adminRequest.post('/api/foods') + .send(testFood) + + return userRequest.get('/api/foods') + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(1) + expect(res.body[0]).to.have.property('items') + expect(res.body[0].items[0]).to.have.property('name', 'test item') + }) + }) + }) + + describe('Admin routes', function () { + it('creates food categories', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testFood = {category: 'test food'} + + return request.post('/api/foods') + .send(testFood) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('category', 'test food') + }) + .expect(200) + }) + + it('lists food categories', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testFood = {category: 'test food'} + + await request.post('/api/foods') + .send(testFood) + + return request.get('/api/foods') + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(1) + expect(res.body[0]).to.have.property('category', 'test food') + }) + .expect(200) + }) + + it('updates a food categories', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testFood = {category: 'test food'} + + const savedFood = (await request.post('/api/foods') + .send(testFood)).body + + const updatedFood = {category: 'new category'} + + return request.put(`/api/foods/${savedFood._id}`) + .send(updatedFood) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('category', 'new category') + }) + .expect(200) + }) + + it('deletes a food categories', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testFood = {category: 'test food'} + + const savedFood = (await request.post('/api/foods') + .send(testFood)).body + + return request.delete(`/api/foods/${savedFood._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('category', 'test food') + }) + .expect(200) + }) + + it('adds food items', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testCategory = {category: 'test food'} + const testItem = {name: 'test item'} + + const savedCategory = (await request.post('/api/foods') + .send(testCategory)).body + + return request.post(`/api/foods/${savedCategory._id}/items`) + .send(testItem) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('items') + expect(res.body.items).to.be.an('array') + expect(res.body.items).to.have.length(1) + expect(res.body.items[0]).to.have.property('name', 'test item') + }) + .expect(200) + }) + + it('updates food items', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testCategory = {category: 'test category'} + const testItem = {name: 'test item'} + + const savedCategory = (await request.post('/api/foods') + .send(testCategory)).body + + const savedCategoryWithItem = (await request + .post(`/api/foods/${savedCategory._id}/items`) + .send(testItem) + ).body + + const updatedItem = { + ...savedCategoryWithItem.items[0], + name: 'updated item' + } + + const itemId = savedCategoryWithItem.items[0]._id + return request.put(`/api/foods/${savedCategory._id}/items/${itemId}`) + .send(updatedItem) + .expect(res => { + expect(res.body).to.have.property('items') + expect(res.body.items[0]).to.have.property('name', 'updated item') + }) + .expect(200) + }) + + it('changes food items category', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testCategory1 = {category: 'test category 1'} + const testCategory2 = {category: 'test category 2'} + const testItem = {name: 'test item'} + + const savedCategory1 = (await request.post('/api/foods') + .send(testCategory1)).body + + const savedCategory2 = (await request.post('/api/foods') + .send(testCategory2)).body + + const savedCategoryWithItem = (await request + .post(`/api/foods/${savedCategory1._id}/items`) + .send(testItem) + ).body + + const savedItem = savedCategoryWithItem.items[0] + + const updatedItem = { + ...savedItem, + name: 'test item', + categoryId: savedCategory2._id + } + + return request.put(`/api/foods/${savedCategory1._id}/items/${savedItem._id}`) + .send(updatedItem) + .expect(res => { + expect(res.body).to.have.property('_id', savedCategory2._id) + expect(res.body.items[0]).to.have.property('name', 'test item') + }) + .expect(200) + }) + + it('deletes food items', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const testCategory = {category: 'test category'} + const testItem = {name: 'test item'} + + const savedCategory = (await request.post('/api/foods') + .send(testCategory)).body + + const savedCategoryWithItem = (await request + .post(`/api/foods/${savedCategory._id}/items`) + .send(testItem) + ).body + + const itemId = savedCategoryWithItem.items[0]._id + + await request.delete(`/api/foods/${savedCategory._id}/items/${itemId}`) + .expect(200) + .expect(res => { + expect(res.body).to.have.property('deletedItemId') + expect(res.body.deletedItemId).to.equal(itemId) + }) + + // Fetch the foods to make sure it is not still there + return request.get('/api/foods') + .expect(200) + .expect(res => { + expect(res.body).to.have.length(1) + expect(res.body[0].items.filter(item => !item.deleted)).to.have.length(0) + }) + }) + + it('creates a new item when a deleted item with the same name exists', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const apple = await FoodItem.create({name: 'apple', quantity: 2, deleted: true}) + const category = await Food.create({category: 'fruits', items: [apple]}) + + return request.post(`/api/foods/${category._id}/items`) + .send({ name: 'apple', quantity: 10 }) + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('items') + expect(res.body.items).to.be.an('array') + expect(res.body.items).to.have.length(2) + const item = res.body.items.find(i => i.name === 'apple' && i.deleted === false && i.quantity === 10) + expect(item).to.not.be.undefined + }) + }) + + it('creates a new item when deleted item with the same name exists and another item is not deleted', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const {app} = await createUserSession(testAdmin) + const request = supertest.agent(app) + + const apple = await FoodItem.create({name: 'apple', quantity: 2, deleted: true}) + const banana = await FoodItem.create({name: 'banana', quantity: 5, deleted: false}) + const category = await Food.create({category: 'fruits', items: [apple, banana]}) + + return request.post(`/api/foods/${category._id}/items`) + .send({ name: 'apple', quantity: 10 }) + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('items') + expect(res.body.items).to.be.an('array') + expect(res.body.items).to.have.length(3) + const item = res.body.items.find(i => i.name === 'apple' && i.deleted === false && i.quantity === 10) + expect(item).to.not.be.undefined + }) + }) + + }) +}) diff --git a/server1/tests/integration/packing.spec.js b/server1/tests/integration/packing.spec.js new file mode 100644 index 00000000..2bda22a0 --- /dev/null +++ b/server1/tests/integration/packing.spec.js @@ -0,0 +1,643 @@ +import {find} from 'lodash' + +import {createUserSession, createTestUser} from '../helpers' +import {ADMIN_ROLE, questionnaireIdentifiers} from '../../../common/constants' +import Customer from '../../models/customer' +import Food, {FoodItem} from '../../models/food' +import Package from '../../models/package' +import User from '../../models/user' +import Questionnaire from '../../models/questionnaire' + +describe('Packing Api', function() { + before(async function() { + await initDb() + await Customer.find().remove() + await User.find().remove() + await Food.find().remove() + await Package.find().remove() + await Questionnaire.find().remove() + await Questionnaire.create({ + name: 'Customer Application', + identifier: questionnaireIdentifiers.CUSTOMER + }) + }) + + afterEach(async function() { + await Customer.find().remove() + await User.find().remove() + await Food.find().remove() + await Package.find().remove() + }) + + after(async function() { + await Questionnaire.find().remove() + await resetDb() + }) + + describe('packing GET route', function() { + it ('Returns empty array when no packages', async function() { + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.get('/api/packing') + .expect(200) + .expect(function (res) { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(0) + }) + }) + + it('Returns one item when there is 1 packed item', async function () { + const foodItems = [ + new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 }), + new FoodItem({ name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1 }), + ] + const food = await Food.create({ category: 'test', items: foodItems }) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const package1 = await Package.create({ + customer: customer._id, + status: 'Packed', + packedBy: 0, + contents: foodItemIds + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + const expected = [{ + __v: package1.__v, + _id: package1._id.toString(), + customer: package1.customer, + datePacked: package1.datePacked.toISOString(), + packedBy: package1.packedBy, + status: package1.status, + contents: package1.contents.map(_id => _id.toString()) + }] + + return request.get('/api/packing') + .expect(200) + .expect(function (res) { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(1) + expect(res.body).to.eql(expected) + }) + }) + + it('Returns 2 packages when there are 2 packages', async function () { + + const foodItems = [ + new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 }), + new FoodItem({ name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1 }), + ] + const food = await Food.create({ category: 'test', items: foodItems }) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const package1 = await Package.create({ + customer: customer._id, + status: 'Packed', + packedBy: 0, + contents: foodItemIds[0] + }) + + const package2 = await Package.create({ + customer: customer._id, + status: 'Packed', + packedBy: 0, + contents: foodItemIds[1] + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + const expected = [package1, package2].map(p => ({ + __v: p.__v, + _id: p._id.toString(), + customer: p.customer, + datePacked: p.datePacked.toISOString(), + packedBy: p.packedBy, + status: p.status, + contents: p.contents.map(_id => _id.toString()) + })) + + return request.get('/api/packing') + .expect(200) + .expect(function (res) { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(2) + expect(res.body).to.eql(expected) + }) + }) + }) + + describe('packing POST route', function() { + + it('Packs a package for one customer', async function() { + let foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + new FoodItem({name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1}), + new FoodItem({name: 'Cantaloupe', quantity: 100, startDate: '2017-06-25', frequency: 1}) + ] + const food = await Food.create({category: 'test', items: foodItems}) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.post('/api/packing').send([{ + customer: customer._id, + contents: foodItemIds + }]) + .expect(200) + .expect(function(res) { + expect(res.body.packages).to.be.an('array') + expect(res.body.packages).to.have.length(1) + expect(res.body.packages[0]).to.have.property('customer', 1) + expect(res.body.packages[0].contents).to.be.an('array') + expect(res.body.packages[0].contents).to.have.length(3) + expect(res.body.packages[0].contents).to.have.same.members(foodItemIds) + }) + }) + + it('Packs a package for two customers', async function() { + let foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + new FoodItem({name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1}), + new FoodItem({name: 'Cantaloupe', quantity: 100, startDate: '2017-06-25', frequency: 1}) + ] + const food = await Food.create({category: 'test', items: foodItems}) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer1 = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const customer2 = await Customer.create({ + _id: 2, + firstName: 'Thomas', + lastName: 'Jefferson', + email: 'tj@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + const package1Contents = [foodItemIds[0]] + const package2Contents = [foodItemIds[1], foodItemIds[2]] + return request.post('/api/packing').send([ + { "customer": customer1._id, "contents": package1Contents }, + { "customer": customer2._id, "contents": package2Contents } + ]) + .expect(200) + .expect(function(res) { + expect(res.body.packages).to.be.an('array') + expect(res.body.packages).to.have.length(2) + + const package1 = find(res.body.packages, {customer: 1}) + expect(package1, 'No package found for customer1').to.exist + expect(package1).to.have.property('customer', 1) + expect(package1.contents).to.be.an('array') + expect(package1.contents).to.have.length(1) + expect(package1.contents).to.have.same.members(package1Contents) + + const package2 = find(res.body.packages, {customer: 2}) + expect(package2, 'No package found for customer2').to.exist + expect(package2).to.have.property('customer', 2) + expect(package2.contents).to.be.an('array') + expect(package2.contents).to.have.length(2) + expect(package2.contents).to.have.same.members(package2Contents) + }) + }) + + it('Updates the customers lastPacked date', async function() { + let foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + ] + const food = await Food.create({category: 'test', items: foodItems}).then(food => food.toObject()) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + status: 'Accepted', + lastPacked: '2017-01-01', + foodPreferences: foodItemIds, + }).then(customer => customer.toObject()) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + const reply = await request.post('/api/packing').send( + [{customer: customer._id, contents: customer.foodPreferences }] + ) + + return request.get(`/api/customer/${customer._id}`) + .expect(200) + .expect(function(res) { + expect(res.body.lastPacked).to.equal(reply.body.packages[0].datePacked) + }) + }) + + it('Updates the food inventory', async function() { + let foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + new FoodItem({name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1}), + new FoodItem({name: 'Cantaloupe', quantity: 100, startDate: '2017-06-25', frequency: 1}) + ] + const food = await Food.create({category: 'test', items: foodItems}) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.post('/api/packing').send([{ + customer: customer._id, + contents: [foodItemIds[0], foodItemIds[1]] + }]) + .expect(200) + .then(function () { + return Food.find({}).lean() + }) + .then(function (food) { + const foodItemsFromDb = food[0].items + const updatedItems = foodItemIds.map(_id => + foodItemsFromDb.find(item => item._id.equals(_id)) + ) + expect(updatedItems[0].quantity).to.equal(99) + expect(updatedItems[1].quantity).to.equal(99) + expect(updatedItems[2].quantity).to.equal(100) + }) + }) + + it('Returns 400 if customer property is not set', async function() { + let foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + ] + const food = await Food.create({category: 'test', items: foodItems}) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.post('/api/packing').send([{ + contents: foodItemIds + }]) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal('Package validation failed: customer: Path `customer` is required.') + }) + }) + + it('Returns 400 if an invalid customerId is given', async function() { + let foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + ] + const food = await Food.create({category: 'test', items: foodItems}) + + const foodItemIds = food.items.map(item => item._id.toString()) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.post('/api/packing').send([{ + customer: 9999, contents: foodItemIds + }]) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal('One or more customerIds are not valid') + }) + }) + + it('Returns 400 if contents are not set', async function () { + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + status: 'Accepted', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.post('/api/packing').send( + [{ customer: customer._id}] + ) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal('Package validation failed: contents: Path `contents` is required to have at least one element.') + }) + }) + + it('Returns 400 if contents is not an array', async function () { + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.post('/api/packing').send( + [{ customer: customer._id, contents: '123456789'}] + ) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal('Package validation failed: contents: Cast to Array failed for value "123456789" at path "contents"') + }) + }) + + it('Returns 400 if an invalid foodItem is specified', async function () { + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.post('/api/packing').send( + [{ customer: customer._id, contents: ['xxxxxxxxxxxxxxxxxxxxxxxx']}] + ) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal('Package validation failed: contents: Cast to Array failed for value "[ \'xxxxxxxxxxxxxxxxxxxxxxxx\' ]" at path "contents"') + }) + }) + + it('Returns 400 if a foodItem is not found in the database', async function () { + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + const nonExistingFoodItemId = '594d6a4f9431ac26453cef07' + return request.post('/api/packing').send( + [{ customer: customer._id, contents: [nonExistingFoodItemId]}] + ) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal(`foodItem ${nonExistingFoodItemId} was not found in the database`) + }) + }) + }) + + describe('packing DELETE route', function() { + + it('Unpacks a package', async function() { + const foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + ] + const food = await Food.create({category: 'test', items: foodItems}) + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + + const testPackage = await Package.create({ + customer: customer._id, + contents: foodItemIds, + status: 'Packed', + packedBy: session.user._id + }) + + const request = supertest.agent(session.app) + + return request.delete('/api/packing').send({ _id: testPackage._id, }) + .expect(200) + .then(function() { + return Package.find({}) + }) + .then(function(packages){ + expect(packages).to.have.length(0) + }) + }) + + it('Does not unpack other packages', async function () { + const foodItems = [ + new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 }), + ] + const food = await Food.create({ category: 'test', items: foodItems }) + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com' + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + + const testPackage1 = await Package.create({ + customer: customer._id, + contents: foodItemIds, + status: 'Packed', + packedBy: session.user._id + }) + + const testPackage2 = await Package.create({ + customer: customer._id, + contents: foodItemIds, + status: 'Packed', + packedBy: session.user._id + }) + + const request = supertest.agent(session.app) + + return request.delete('/api/packing').send({ + _id: testPackage2._id, + }) + .expect(200) + .then(function () { + return (Package.find({})) + }) + .then(function (results) { + expect(results).to.be.an('array') + expect(results).to.have.length(1) + expect(results[0]._id.equals(testPackage1._id)).to.be.true + }) + }) + + it('Adds the foodItems from the package back to the inventory counts', async function() { + const foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + ] + const food = await Food.create({category: 'test', items: foodItems}) + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + + const testPackage = await Package.create({ + customer: customer._id, + contents: foodItemIds, + status: 'Packed', + packedBy: session.user._id + }) + + const request = supertest.agent(session.app) + + return request.delete('/api/packing').send({ _id: testPackage._id, }) + .expect(200) + .then(function() { + return Food.find({}) + }) + .then(function(foods){ + expect(foods[0].items[0].quantity).to.equal(101) + }) + }) + + it('Updates the lastPacked date for the customer', async function() { + const foodItems = [ + new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), + ] + const food = await Food.create({category: 'test', items: foodItems}) + const foodItemIds = food.items.map(item => item._id.toString()) + + const customer = await Customer.create({ + _id: 1, + firstName: 'George', + lastName: 'Washington', + email: 'gw@example.com', + }) + + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + + const testPackage = await Package.create({ + customer: customer._id, + contents: foodItemIds, + status: 'Packed', + packedBy: session.user._id + }) + + const request = supertest.agent(session.app) + + return request.delete('/api/packing').send({ _id: testPackage._id, }) + .expect(200) + .then(function() { + return Customer.findById(1) + }) + .then(function(customer){ + expect(customer.lastPacked).to.be.a('null') + }) + }) + + it('Returns 400 when a package _id is not specified', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.delete('/api/packing') + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal(`package _id must be included in the request`) + }) + }) + + it('Returns 400 when a package _id is not valid', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.delete('/api/packing').send({ _id: 'xxxxxxxx', }) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal('xxxxxxxx is not a valid package _id') + }) + }) + + it('Returns 400 when a package _id is not found in the database', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(testAdmin) + const request = supertest.agent(session.app) + + return request.delete('/api/packing').send({ _id: '594d6a4f9431ac26453cef08', }) + .expect(400) + .expect(function (res) { + expect(res.body.message).to.equal('package with _id 594d6a4f9431ac26453cef08 not found') + }) + }) + + }) + +}) diff --git a/server1/tests/integration/questionnaire.spec.js b/server1/tests/integration/questionnaire.spec.js new file mode 100644 index 00000000..ea7bf8d9 --- /dev/null +++ b/server1/tests/integration/questionnaire.spec.js @@ -0,0 +1,123 @@ +import Questionnaire from '../../models/questionnaire' +import User from '../../models/user' +import { + ADMIN_ROLE, + questionnaireIdentifiers, + fieldTypes, +} from '../../../common/constants' +import{createTestUser, createUserSession} from '../helpers' + +describe('Questionnaire Api', function() { + before(async function() { + await initDb() + await Questionnaire.find().remove() + await User.find().remove() + }) + + afterEach(async function() { + await Questionnaire.find().remove() + await User.find().remove() + }) + + after(async function() { + await Questionnaire.find().remove() + await User.find().remove() + await resetDb() + }) + + describe('User routes', function() { + it('lists questionnaires', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const admin = await createUserSession(testAdmin) + const request = supertest.agent(admin.app) + + const newQuestionnaire = await new Questionnaire({ + name: 'Customer Application', + identifier: questionnaireIdentifiers.CUSTOMER, + }).save() + + return request.get('/api/questionnaires') + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(1) + expect(res.body[0]).to.have.property('_id', newQuestionnaire.id) + expect(res.body[0]).to.have.property('name', 'Customer Application') + expect(res.body[0]).to.have.property('identifier', questionnaireIdentifiers.CUSTOMER) + }) + .expect(200) + }) + it('shows a questionnaire', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const admin = await createUserSession(testAdmin) + const request = supertest.agent(admin.app) + + const newQuestionnaire = await new Questionnaire({ + name: 'Customer Application', + identifier: questionnaireIdentifiers.CUSTOMER, + }).save() + + return request.get(`/api/questionnaires/${newQuestionnaire._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body.name).to.equal(newQuestionnaire.name) + expect(res.body.identifier).to.equal(newQuestionnaire.identifier) + }) + .expect(200) + }) + it('doesn\'t show non-existing questionnaires', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const admin = await createUserSession(testAdmin) + const request = supertest.agent(admin.app) + + const notSavedQuestionnaire = new Questionnaire({ + name: 'Customer Application', + identifier: questionnaireIdentifiers.CUSTOMER, + }) + + return request.get(`/api/questionnaires/${notSavedQuestionnaire._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('message', 'Not found') + }) + .expect(404) + }) + it('updates a questionnaire', async function() { + const testAdmin = createTestUser('admin', ADMIN_ROLE) + const admin = await createUserSession(testAdmin) + const request = supertest.agent(admin.app) + + const section = { + name: 'Section Info', + fields: [{ + type: fieldTypes.TEXT, label: 'Section Field' + }] + } + + const newSection = { + name: 'New Section Info', + fields: [{ + type: fieldTypes.TEXT, label: 'New Section Field' + }] + } + + const savedQuestionnaire = await new Questionnaire({ + name: 'Customer Application', + identifier: questionnaireIdentifiers.CUSTOMER, + sections: [section] + }).save() + + const updatedQuestionnaire = {sections: [newSection]} + + return request.put(`/api/questionnaires/${savedQuestionnaire._id}`) + .send(updatedQuestionnaire) + .expect(res => { + expect(res.body).to.be.an('object') + // Check section change + expect(res.body.sections[0]).to.have.property('name', 'New Section Info') + // Check field change + expect(res.body.sections[0].fields[0]).to.have.property('label', 'New Section Field') + }) + .expect(200) + }) + }) +}) diff --git a/server1/tests/integration/user.spec.js b/server1/tests/integration/user.spec.js new file mode 100644 index 00000000..0af795ce --- /dev/null +++ b/server1/tests/integration/user.spec.js @@ -0,0 +1,636 @@ +import Customer from '../../models/customer' +import Volunteer from '../../models/volunteer' +import Donor from '../../models/donor' +import app from '../../config/express' +import User from '../../models/user' +import { ADMIN_ROLE } from '../../../common/constants' +import { createUserSession, createTestUser } from '../helpers' + +import {searchUserAndSetNotification} from '../../lib/notification-sender' + +describe('User Api', function() { + before(async function() { + await initDb() + }) + + beforeEach(async function() { + await Customer.find().remove() + await Volunteer.find().remove() + await Donor.find().remove() + await User.find().remove() + }) + + after(async function() { + await Customer.find().remove() + await Volunteer.find().remove() + await Donor.find().remove() + await User.find().remove() + await resetDb() + }) + + describe('signup', function() { + it('signs up users', async function() { + const request = supertest.agent(app()) + const newUser = { + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678' + } + return await request.post('/api/auth/signup') + .send(newUser) + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('_id') + expect(res.body.firstName).to.equal('Frank') + expect(res.body.lastName).to.equal('Harper') + expect(res.body.email).to.equal('fharper@example.com') + }) + }) + + it('it requires a first name', async function() { + const request = supertest.agent(app()) + const newUser = { + lastName: 'Willis', + email: 'mwillis@example.com', + password: '12345678' + } + return await request.post('/api/auth/signup') + .send(newUser) + .expect(400) + .expect(res => { + expect(res.body.paths.firstName).to.equal('Please fill in your first name') + }) + }) + + it('it requires a last name', async function() { + const request = supertest.agent(app()) + const newUser = { + firstName: 'Willis', + email: 'mwillis@example.com', + password: '12345678' + } + return await request.post('/api/auth/signup') + .send(newUser) + .expect(400) + .expect(res => { + expect(res.body.paths.lastName).to.equal('Please fill in your last name') + }) + }) + + it('requires a password', async function() { + const request = supertest.agent(app()) + const newUser = { + firstName: 'Margaret', + lastName: 'Willis', + email: 'mwillis@example.com' + } + return await request.post('/api/auth/signup') + .send(newUser) + .expect(400) + .expect(res => { + expect(res.body.paths.password).to.equal('Password should be longer') + }) + }) + + it('requires a password to be at least 7 characters long', async function() { + const request = supertest.agent(app()) + const newUser = { + firstName: 'Margaret', + lastName: 'Willis', + email: 'mwillis@example.com', + password: '12345' + } + return await request.post('/api/auth/signup') + .send(newUser) + .expect(400) + .expect(res => { + expect(res.body.paths.password).to.equal('Password should be longer') + }) + }) + + it('does not allow signup if email address already has an account', async function() { + const request = supertest.agent(app()) + const newUser = { + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678' + } + + await request.post('/api/auth/signup') + .send(newUser) + + const newUser2 = { + firstName: 'Francine', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678' + } + return await request.post('/api/auth/signup') + .send(newUser2) + .expect(400) + .expect(res => { + expect(res.body.paths.email).to.equal('Email address already has an account') + }) + }) + }) + + describe('logging in', function() { + it('signs in', async function() { + const newUser = { + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678' + } + const request = supertest.agent(app()) + await request.post('/api/auth/signup') + .send(newUser) + + return await request.post('/api/auth/signin') + .send({email: 'fharper@example.com', password: '12345678'}) + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('_id') + }) + }) + + it('fails without an email', async function() { + const newUser = { + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678' + } + const request = supertest.agent(app()) + await request.post('/api/auth/signup') + .send(newUser) + + return await request.post('/api/auth/signin') + .send({password: '12345678'}) + .expect(400) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body.message).to.equal('Missing credentials') + }) + }) + + it('fails without a password', async function() { + const newUser = { + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678' + } + const request = supertest.agent(app()) + await request.post('/api/auth/signup') + .send(newUser) + + return await request.post('/api/auth/signin') + .send({email: 'fharper@example.com'}) + .expect(400) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body.message).to.equal('Missing credentials') + }) + }) + + it('fails with the wrong password', async function() { + const newUser = { + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678' + } + const request = supertest.agent(app()) + await request.post('/api/auth/signup') + .send(newUser) + + return await request.post('/api/auth/signin') + .send({email: 'fharper@example.com', password: 'xxxxxxxxx'}) + .expect(400) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body.message).to.equal('Unknown user or invalid password') + }) + }) + }) + + describe('listing user accounts', function () { + it('returns an empty array when there are no accounts', async function () { + const request = supertest.agent(app()) + return await request.get('/api/users') + .expect(200) + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(0) + }) + }) + + it('returns one account when there is one account', async function () { + await User.create({ + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678', + provider: 'local' + }) + + const request = supertest.agent(app()) + return await request.get('/api/users') + .expect(200) + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(1) + expect(res.body[0].firstName).to.equal('Frank') + expect(res.body[0].lastName).to.equal('Harper') + expect(res.body[0].email).to.equal('fharper@example.com') + }) + }) + + it('returns two account when there is two accounts', async function () { + await User.create({ + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678', + provider: 'local' + }) + await User.create({ + firstName: 'Bill', + lastName: 'Willis', + email: 'mwillis@example.com', + password: '12345678', + provider: 'local' + }) + + const request = supertest.agent(app()) + return await request.get('/api/users') + .expect(200) + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(2) + const frank = res.body.find(user => user.firstName === 'Frank') + expect(frank.firstName).to.equal('Frank') + expect(frank.lastName).to.equal('Harper') + expect(frank.email).to.equal('fharper@example.com') + const bill = res.body.find(user => user.firstName === 'Bill') + expect(bill.firstName).to.equal('Bill') + expect(bill.lastName).to.equal('Willis') + expect(bill.email).to.equal('mwillis@example.com') + }) + }) + }) + + describe('getting a user by userId', function () { + let request + beforeEach(async function() { + const user = createTestUser('admin', ADMIN_ROLE) + const session = await createUserSession(user) + request = supertest.agent(session.app) + }) + + it('Gets the user', async function () { + const user = await User.create({ + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678', + provider: 'local' + }) + await User.create({ + firstName: 'Bill', + lastName: 'Willis', + email: 'mwillis@example.com', + password: '12345678', + provider: 'local' + }) + + await request.get(`/api/admin/users/${user._id}`) + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body.firstName).to.equal('Frank') + expect(res.body.lastName).to.equal('Harper') + expect(res.body.email).to.equal('fharper@example.com') + }) + }) + + it('returns when the user does not exist', async function () { + const user = await User.create({ + firstName: 'Frank', + lastName: 'Harper', + email: 'fharper@example.com', + password: '12345678', + provider: 'local' + }) + await User.create({ + firstName: 'Bill', + lastName: 'Willis', + email: 'mwillis@example.com', + password: '12345678', + provider: 'local' + }) + + await User.remove({_id: user._id}) + + await request.get(`/api/admin/users/${user._id}`) + .expect(400) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body.message).to.equal(`UserId ${user._id} not found`) + }) + }) + + }) + + describe('updating a user', function() { + + + + + + + + + + + + + + + + + it('updates the database', async function(){ + const user = await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + + const adminUser = createTestUser('admin', ADMIN_ROLE) + const adminSession = await createUserSession(adminUser) + const adminReq = supertest.agent(adminSession.app) + + const requestBody = { + _id: user._id, + created: user.created, + displayName: user.displayName, + email: 'new.email@example.com', + firstName: 'newFirstName', + lastName: 'newLastName', + provider: user.provider, + roles: user.roles, + updated: user.updated + } + + await adminReq.put(`/api/admin/users/${user._id}`).send(requestBody).expect(200) + + const updatedUser = await User.findById(requestBody._id).lean() + expect(updatedUser).to.have.property('_id', requestBody._id) + expect(updatedUser).to.have.property('email', requestBody.email) + expect(updatedUser).to.have.property('firstName', requestBody.firstName) + expect(updatedUser).to.have.property('lastName', requestBody.lastName) + }) + + + + + + it('returns the updated user object', async function(){ + const user = await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + + const adminUser = createTestUser('admin', ADMIN_ROLE) + const adminSession = await createUserSession(adminUser) + const adminReq = supertest.agent(adminSession.app) + + const requestBody = { + _id: user._id, + created: user.created, + displayName: user.displayName, + email: 'new.email@example.com', + firstName: 'newFirstName', + lastName: 'newLastName', + provider: user.provider, + roles: user.roles, + updated: user.updated + } + + await adminReq.put(`/api/admin/users/${user._id}`).send(requestBody) + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('_id', requestBody._id) + expect(res.body).to.have.property('email', requestBody.email) + expect(res.body).to.have.property('firstName', requestBody.firstName) + expect(res.body).to.have.property('lastName', requestBody.lastName) + }) + }) + + it('admins can set other users as admins', async function(){ + const user = await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + + const adminUser = createTestUser('admin', ADMIN_ROLE) + const adminSession = await createUserSession(adminUser) + const adminReq = supertest.agent(adminSession.app) + + await adminReq.put(`/api/admin/users/${user._id}`) + .send({ isAdmin: true }) + .expect(200) + + const updatedUser = await User.findById(user._id).lean() + expect(updatedUser).to.have.property('roles') + expect(updatedUser.roles).to.include(ADMIN_ROLE) + }) + + // see https://github.com/freeCodeCamp/pantry-for-good/issues/347 + it('regular users cannot update another user profile', async function(){ + const user1 = await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + + const user2 = await User.create({ + firstName: 'first2', + lastName: 'last2', + email: '345@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + const user2Session = await createUserSession(user2) + const user2Req = supertest.agent(user2Session.app) + + const requestBody = { + _id: user1._id, + created: user1.created, + displayName: user1.displayName, + email: 'new.email@example.com', + firstName: 'newFirstName', + lastName: 'newLastName', + provider: user1.provider, + roles: user1.roles, + updated: user1.updated + } + + await user2Req.put(`/api/admin/users/${user1._id}`).send(requestBody).expect(403) + + const updatedUser = await User.findById(user1._id).lean() + expect(updatedUser).to.have.property('_id', user1._id) + expect(updatedUser).to.have.property('email', user1.email) + expect(updatedUser).to.have.property('firstName', user1.firstName) + expect(updatedUser).to.have.property('lastName', user1.lastName) + }) + }) + + describe('updating profile', function() { + it('regular users can update their own profile', async function(){ + const user = await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + + const userSession = await createUserSession(user) + const userReq = supertest.agent(userSession.app) + + const requestBody = { + _id: user._id, + created: user.created, + displayName: user.displayName, + email: 'new.email@example.com', + firstName: 'newFirstName', + lastName: 'newLastName', + provider: user.provider, + roles: user.roles, + updated: user.updated + } + + await userReq.put('/api/users/me').send(requestBody).expect(200) + + const updatedUser = await User.findById(user._id).lean() + expect(updatedUser).to.have.property('email', requestBody.email) + expect(updatedUser).to.have.property('firstName', requestBody.firstName) + expect(updatedUser).to.have.property('lastName', requestBody.lastName) + }) + + it('update ignores req.body properties: displayName, provider, salt, resetPasswordToken and roles', async function(){ + const user = await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + + const userBeforeUpdate = await User.findById(user._id) + + const userSession = await createUserSession(user) + const userReq = supertest.agent(userSession.app) + + const requestBody = { + _id: user._id, + email: 'new.email@example.com', + firstName: 'newFirstName', + lastName: 'newLastName', + displayName: 'xxxxxx', + provider: 'xxxxxx', + salt: 'xxxxxx', + resetPasswordToken: 'xxxxxx', + resetPasswordExpires: 'xxxxxx', + roles: 'xxxxxx', + } + + await userReq.put('/api/users/me').send(requestBody).expect(200) + + const userAfterUpdate = await User.findById(user._id) + + expect(userAfterUpdate).to.have.property('email', requestBody.email) + expect(userAfterUpdate).to.have.property('firstName', requestBody.firstName) + expect(userAfterUpdate).to.have.property('lastName', requestBody.lastName) + expect(userAfterUpdate).to.have.property('salt', userBeforeUpdate.salt) + expect(userAfterUpdate).to.have.property('provider', userBeforeUpdate.provider) + expect(userAfterUpdate).to.have.property('resetPasswordToken', userBeforeUpdate.resetPasswordToken) + expect(userAfterUpdate).to.have.property('resetPasswordExpires', userBeforeUpdate.resetPasswordExpires) + }) + + it('regular users cannot make themselves admins', async function(){ + const user = await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [], + provider: 'local', + password: '12345678' + }) + + const userSession = await createUserSession(user) + const userReq = supertest.agent(userSession.app) + + await userReq.put('/api/users/me') + .send({ isAdmin: true }) + .expect(200) + + const updatedUser = await User.findById(user._id).lean() + expect(updatedUser).to.have.property('roles') + expect(updatedUser.roles).to.not.include(ADMIN_ROLE) + }) + }) + + describe('users notifications', function() { + it('function for creating an admin notifications', async function(){ + await User.create({ + firstName: 'first', + lastName: 'last', + email: '123@example.com', + roles: [ADMIN_ROLE], + provider: 'local', + password: '12345678' + }) + + // Sent Notifications + await searchUserAndSetNotification('roles/admin', {message:`Customer customer test was created!`, url: `/customers/2018`}, 2018) + + const request = supertest.agent(app()) + return await request.post('/api/auth/signin') + .send({email: '123@example.com', password: '12345678'}) + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('notifications') + expect(res.body.notifications[0]).to.have.property('message') + expect(res.body.notifications[0]).to.have.property('url') + expect(res.body.notifications[0]).to.have.property('date') + expect(res.body.notifications[0].message).to.equal('Customer customer test was created!') + }) + }) + }) +}) diff --git a/server1/tests/integration/volunteer.spec.js b/server1/tests/integration/volunteer.spec.js new file mode 100644 index 00000000..43943020 --- /dev/null +++ b/server1/tests/integration/volunteer.spec.js @@ -0,0 +1,169 @@ +import { + ADMIN_ROLE, + clientRoles, + questionnaireIdentifiers +} from '../../../common/constants' +import {createUserSession, createTestUser} from '../helpers' +import User from '../../models/user' +import Questionnaire from '../../models/questionnaire' +import Volunteer from '../../models/volunteer' + +import {searchVolunteerAndSetNotification} from '../../lib/notification-sender' + +describe('Volunteer Api', function() { + before(async function() { + await initDb() + await Volunteer.find().remove() + await User.find().remove() + await Questionnaire.find().remove() + await new Questionnaire({ + name: 'Volunteer Application', + identifier: questionnaireIdentifiers.VOLUNTEER + }).save() + }) + + afterEach(async function() { + await Volunteer.find().remove() + await User.find().remove() + }) + + after(async function() { + await Questionnaire.find().remove() + await resetDb() + }) + + describe('User routes', function() { + it('creates volunteers', async function() { + const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) + const {user, app} = await createUserSession(testVolunteer) + const request = supertest.agent(app) + + return request.post('/api/volunteer') + .send(user) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('status', 'Pending') + }) + .expect(200) + }) + + it('shows a volunteer', async function() { + const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) + const {user, app} = await createUserSession(testVolunteer) + const request = supertest.agent(app) + + const newVolunteer = (await request.post('/api/volunteer') + .send(user)).body + + return request.get(`/api/volunteer/${newVolunteer._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('status', 'Pending') + }) + .expect(200) + }) + + it('updates a volunteer', async function() { + const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) + const {user, app} = await createUserSession(testVolunteer) + const request = supertest.agent(app) + + const newVolunteer = (await request.post('/api/volunteer') + .send(user)).body + + const updatedVolunteer = { + ...newVolunteer, + firstName: 'updated' + } + + return request.put(`/api/volunteer/${updatedVolunteer._id}`) + .send(updatedVolunteer) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'updated') + }) + .expect(200) + }) + }) + + describe('Admin routes', function() { + it('lists volunteers', async function() { + const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) + const testAdmin = createTestUser('admin', ADMIN_ROLE) + + const volunteer = await createUserSession(testVolunteer) + const admin = await createUserSession(testAdmin) + + const volunteerRequest = supertest.agent(volunteer.app) + const adminRequest = supertest.agent(admin.app) + + await volunteerRequest.post('/api/volunteer') + .send(volunteer.user) + + return adminRequest.get('/api/admin/volunteers') + .expect(res => { + expect(res.body).to.be.an('array') + expect(res.body).to.have.length(1) + expect(res.body[0]).to.have.property('firstName', 'user') + }) + .expect(200) + }) + + it('deletes volunteers', async function() { + const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) + const testAdmin = createTestUser('admin', ADMIN_ROLE) + + const volunteer = await createUserSession(testVolunteer) + const admin = await createUserSession(testAdmin) + + const volunteerRequest = supertest.agent(volunteer.app) + const adminRequest = supertest.agent(admin.app) + + const newVolunteer = (await volunteerRequest.post('/api/volunteer') + .send(volunteer.user)).body + + return adminRequest.delete(`/api/admin/volunteers/${newVolunteer._id}`) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('firstName', 'user') + }) + .expect(200) + }) + }) + + describe('Volunteer notifications', function() { + it('creates volunteer notifications', async function(){ + const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) + const testAdmin = createTestUser('admin', ADMIN_ROLE) + + const volunteer = await createUserSession(testVolunteer) + const admin = await createUserSession(testAdmin) + + const volunteerRequest = supertest.agent(volunteer.app) + const adminRequest = supertest.agent(admin.app) + + const newVolunteer = (await volunteerRequest.post('/api/volunteer') + .send(volunteer.user)).body + + await (adminRequest.put(`/api/admin/volunteers/${newVolunteer._id}`).send({ + status: 'Active', + customers: [10077] + })) + + await searchVolunteerAndSetNotification({message: 'Customer Pepe Gonzales was updated!', url: '/customers/10077'}, 10077) + + return volunteerRequest.get('/api/users/me') + .expect(200) + .expect(res => { + expect(res.body).to.be.an('object') + expect(res.body).to.have.property('notifications') + expect(res.body.notifications).to.have.length(1) + + const notification = res.body.notifications[0] + expect(notification).to.have.property('url', '/customers/10077') + expect(notification).to.have.property('message', 'Customer Pepe Gonzales was updated!') + expect(notification).to.have.property('date') + }) + }) + }) +}) From 4193f03217b7ef083bed8ed83ad3c2abe013b564 Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Tue, 16 Apr 2019 11:11:33 -0700 Subject: [PATCH 2/9] Added unit tests --- .../components/AddShiftModal.js | 28 ++- .../components/AddShiftModal.spec.js | 224 ++++++++++++++++++ .../components/EditModal.js | 2 +- .../components/EditModal.spec.js | 54 +++++ .../components/VolunteerScheduling.js | 9 - .../components/VolunteerScheduling.spec.js | 160 +++++++++++++ package.json | 2 + 7 files changed, 457 insertions(+), 22 deletions(-) create mode 100644 client/modules/volunteerScheduling/components/AddShiftModal.spec.js create mode 100644 client/modules/volunteerScheduling/components/EditModal.spec.js create mode 100644 client/modules/volunteerScheduling/components/VolunteerScheduling.spec.js diff --git a/client/modules/volunteerScheduling/components/AddShiftModal.js b/client/modules/volunteerScheduling/components/AddShiftModal.js index 28088d09..40abe817 100644 --- a/client/modules/volunteerScheduling/components/AddShiftModal.js +++ b/client/modules/volunteerScheduling/components/AddShiftModal.js @@ -4,8 +4,6 @@ import Autosuggest from 'react-bootstrap-autosuggest' import { Box, BoxHeader, BoxBody } from '../../../components/box' -// import SendGrid = require('@sendgrid/mail'); - export default class AddShiftModal extends React.Component { constructor(props) { @@ -59,7 +57,6 @@ export default class AddShiftModal extends React.Component { } }, dateTime: e => { - // document.getElementById("test").innerHTML = e.target.value this.setState({formInputFields: {...this.state.formInputFields, dateTime: e.target.value}, touched: {...this.state.touched, dateTime: true}}, this.validate) }, notes: e => { @@ -80,7 +77,7 @@ export default class AddShiftModal extends React.Component { this.state.nowDate.getTime() < new Date(this.state.formInputFields.dateTime).getTime() } - saveFood = () => { + saveShift = () => { const volunteer = this.props.getVolunteer(this.state.formInputFields.id) var dateStr = this.state.formInputFields.dateTime @@ -109,14 +106,19 @@ export default class AddShiftModal extends React.Component { } // Used to send email - var credentials = { - email: volunteer.email, - shift: temp_event + if(volunteer.email == "" || volunteer.email == null) { + alert("Volunteer has no email in database. Shift has been saved, but no email reminders will be sent.") + this.props.updateCalendar(volunteer, event) + this.props.closeAddModal() + } + else { + var credentials = { + email: volunteer.email, + shift: temp_event + } + this.props.emailShift(credentials) + this.props.updateCalendar(volunteer, event) } - - this.props.emailShift(credentials) - - this.props.updateCalendar(volunteer, event) } render = () => { @@ -139,6 +141,7 @@ export default class AddShiftModal extends React.Component { value={this.state.formInputFields.volunteerName} datalist={this.formatData()} placeholder="Volunteer Name" + maxLength='45' itemValuePropName='fullName' itemReactKeyPropName='fullName' itemSortKeyPropName='fullName' @@ -157,6 +160,7 @@ export default class AddShiftModal extends React.Component { Notes (Optional) @@ -165,7 +169,7 @@ export default class AddShiftModal extends React.Component {
diff --git a/client/modules/volunteerScheduling/components/AddShiftModal.spec.js b/client/modules/volunteerScheduling/components/AddShiftModal.spec.js new file mode 100644 index 00000000..aa917ed3 --- /dev/null +++ b/client/modules/volunteerScheduling/components/AddShiftModal.spec.js @@ -0,0 +1,224 @@ +import React from 'react' +import Enzyme, { shallow } from 'enzyme' +import Adapter from 'enzyme-adapter-react-15' + +import AddShiftModal from './AddShiftModal' + +Enzyme.configure({adapter: new Adapter()}) + +describe('AddShiftModal', () => { + let props + + beforeEach(() => { + props = { + AddShiftModal: [], + loading: false, + saving: false, + volunteers: [], + getVolunteer: sinon.spy(), + emailShift: sinon.spy(), + closeAddModal: sinon.spy(), + makeShift: sinon.spy(), + updateCalendar: sinon.spy() + } + }) + + it('sets state when constructed', () => { + const wrapper = shallow() + expect(wrapper.state().formInputFields).to.deep.equal({volunteerName: "", + id: "", + dateTime: "", + startTime: "", + duration: "", + notes: "" + }) + expect(wrapper.state().validInput).to.be.false + expect(wrapper.state().touched).to.deep.equal({volunteerName: false, dateTime: false}) + }) + + it('componentWillReceiveProps closes modal if a save is complete', () => { + const wrapper = shallow() + wrapper.setProps({ saving: true }) + const nextProps = { + saving: false, + saveError: undefined + } + expect(props.closeAddModal.called).to.be.false + wrapper.setProps(nextProps) + expect(props.closeAddModal.called).to.be.true + }) + + it('componentWillReceiveProps will not close a modal if in process of saving', () => { + const wrapper = shallow() + wrapper.setProps({ saving: false }) + const nextProps = { + saving: true + } + wrapper.setProps(nextProps) + expect(props.closeAddModal.called).to.be.false + }) + + it('componentWillReceiveProps will not close a modal if there is a save error', () => { + const wrapper = shallow() + wrapper.setProps({ saving: true }) + const nextProps = { + saving: false, + saveError: { message: 'error' } + } + wrapper.setProps(nextProps) + expect(props.closeAddModal.called).to.be.false + }) + + describe('getValidationState', () => { + it('volunteerName() returns null for a valid name', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.setState({ formInputFields: { volunteerName: 'abc' } }) + expect(instance.getValidationState.volunteerName()).to.be.null + }) + + it('volunteerName() returns error for an empty string', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.setState({ formInputFields: { volunteerName: '' }, touched: { volunteerName: true } }) + expect(instance.getValidationState.volunteerName()).to.equal('error') + }) + + it('volunteerName() returns error for a whitespace string', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.setState({ formInputFields: { volunteerName: ' ' }, touched: { volunteerName: true } }) + expect(instance.getValidationState.volunteerName()).to.equal('error') + }) + + it('getAll() returns true when VolunteerName and dateTime are all valid', () => { + const wrapper = shallow() + const instance = wrapper.instance() + const state = { + formInputFields: { volunteerName: 'abc', dateTime: "04/20/2019" }, + touched: { volunteerName: true, dateTime: true } + } + instance.setState(state) + expect(instance.getValidationState.all()).to.be.true + }) + + it('getAll() returns false when VolunteerName or dateTime are not valid', () => { + const wrapper = shallow() + const instance = wrapper.instance() + const state = { + formInputFields: { volunteerName: '', dateTime: "04/20/2019" }, + touched: { volunteerName: true, dateTime: true } + } + instance.setState(state) + expect(instance.getValidationState.all()).to.be.false + }) + }) + + describe('handleChange', () => { + it('volunteerName() when called with a string sets state.formInputFields.volunteerName to blank', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.handleChange.volunteerName('abcde') + expect(instance.state.formInputFields.volunteerName).to.equal('') + }) + + it('volunteerName() when called with a string sets state.touched.volunteerName to true', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.handleChange.volunteerName('abcde') + expect(instance.state.touched.volunteerName).to.equal(true) + }) + + it('volunteerName() when called with an object sets state.formInputFields.volunteerName to name', () => { + const wrapper = shallow() + const instance = wrapper.instance() + const vol = { + fullName: "Samuel Freiberg", + _id: 1 + } + instance.handleChange.volunteerName(vol) + expect(instance.state.formInputFields.volunteerName).to.equal("Samuel Freiberg") + }) + + it('volunteerName() when called with an object sets state.touched.volunteerName to true', () => { + const wrapper = shallow() + const instance = wrapper.instance() + const vol = { + fullName: "Samuel Freiberg", + _id: 1 + } + instance.handleChange.volunteerName(vol) + expect(instance.state.touched.volunteerName).to.equal(true) + }) + + it('volunteerName() when called with a string calls validate()', () => { + const wrapper = shallow() + const instance = wrapper.instance() + sinon.spy(instance, "validate") + instance.handleChange.volunteerName("") + expect(instance.validate.called).to.be.true + }) + + it('dateTime() when called with a string sets state.formInputFields.dateTime to the string', () => { + const wrapper = shallow() + const instance = wrapper.instance() + const date = { + target: { + value: "04/14/2019" + } + } + instance.handleChange.dateTime(date) + expect(instance.state.formInputFields.dateTime).to.equal('04/14/2019') + }) + + it('dateTime() when called sets state.touched.dateTime to true', () => { + const wrapper = shallow() + const instance = wrapper.instance() + const date = { + target: { + value: "04/14/2019" + } + } + instance.handleChange.dateTime(date) + expect(instance.state.touched.dateTime).to.equal(true) + }) + + it('dateTime() when called calls validate()', () => { + const wrapper = shallow() + const instance = wrapper.instance() + sinon.spy(instance, "validate") + const date = { + target: { + value: "04/14/2019" + } + } + instance.handleChange.dateTime(date) + expect(instance.validate.called).to.be.true + }) + + it('notes() when called with a string sets state.formInputFields.notes to the string', () => { + const wrapper = shallow() + const instance = wrapper.instance() + const note = { + target: { + value: "Hello" + } + } + instance.handleChange.notes(note) + expect(instance.state.formInputFields.notes).to.equal('Hello') + }) + + it('notes() when called calls validate()', () => { + const wrapper = shallow() + const instance = wrapper.instance() + sinon.spy(instance, "validate") + const note = { + target: { + value: "Hello" + } + } + instance.handleChange.notes(note) + expect(instance.validate.called).to.be.true + }) + }) +}) diff --git a/client/modules/volunteerScheduling/components/EditModal.js b/client/modules/volunteerScheduling/components/EditModal.js index fa331a5d..604bb58b 100644 --- a/client/modules/volunteerScheduling/components/EditModal.js +++ b/client/modules/volunteerScheduling/components/EditModal.js @@ -46,4 +46,4 @@ export default class EditModal extends React.Component { ) } -} +} \ No newline at end of file diff --git a/client/modules/volunteerScheduling/components/EditModal.spec.js b/client/modules/volunteerScheduling/components/EditModal.spec.js new file mode 100644 index 00000000..d6f3c408 --- /dev/null +++ b/client/modules/volunteerScheduling/components/EditModal.spec.js @@ -0,0 +1,54 @@ +import React from 'react' +import Enzyme, { shallow } from 'enzyme' +import Adapter from 'enzyme-adapter-react-15' + +import EditModal from './EditModal' + +Enzyme.configure({adapter: new Adapter()}) + +describe('EditModal', () => { + let props + + beforeEach(() => { + props = { + EditModal: [], + loading: false, + saving: false, + closeEditModal: sinon.spy(), + getVolunteer: sinon.spy(), + deleteShift: sinon.spy(), + deleteCalendarEvent: sinon.spy() + } + }) + + it('sets state when constructed', () => { + const wrapper = shallow() + expect(wrapper.state().delete).to.be.false + }) + + it('deleteCalendarEvent should call props.getVolunteer()', () => { + const wrapper = shallow() + wrapper.setProps({shiftToDelete: {extendedProps: {volunteerId: 1}}}) + const instance = wrapper.instance() + instance.deleteEvent() + expect(instance.props.getVolunteer.called).to.be.true + }) + + it('deleteCalendarEvent should call props.deleteShift()', () => { + const wrapper = shallow() + wrapper.setProps({shiftToDelete: {extendedProps: {volunteerId: 1}}}) + const instance = wrapper.instance() + instance.deleteEvent() + expect(instance.props.deleteShift.called).to.be.true + }) + + it('deleteCalendarEvent should call props.deleteCalendarEvent()', () => { + const wrapper = shallow() + wrapper.setProps({shiftToDelete: {extendedProps: {volunteerId: 1}}}) + const instance = wrapper.instance() + instance.deleteEvent() + expect(instance.props.deleteCalendarEvent.called).to.be.true + }) + + +}) diff --git a/client/modules/volunteerScheduling/components/VolunteerScheduling.js b/client/modules/volunteerScheduling/components/VolunteerScheduling.js index cf4fc803..c7597c28 100644 --- a/client/modules/volunteerScheduling/components/VolunteerScheduling.js +++ b/client/modules/volunteerScheduling/components/VolunteerScheduling.js @@ -47,14 +47,12 @@ const mapDispatchToProps = dispatch => ({ export class VolunteerScheduling extends React.Component { constructor(props) { super(props) - // console.log("In Constructor") this.props.loadVolunteers() this.state = { c: "", showAddModal: false, showEditModal: false, shiftToDelete: "", - id: "", events: [], added: false, removed: false, @@ -105,12 +103,7 @@ export class VolunteerScheduling extends React.Component { this.setState({c: calendar}) } - // dragEvent = (event) => { - - // } - updateCalendar = (volunteer, event) => { - //console.log("In Update Calendar") const calendar = this.state.c var new_id = calendar.getEvents().length != 0 ? (calendar.getEvents()[calendar.getEvents().length-1].id + 1) : 0 @@ -127,8 +120,6 @@ export class VolunteerScheduling extends React.Component { } deleteCalendarEvent = () => { - //console.log("In Delete Calendar Event") - var calendar = this.state.c /* Checking for case in which user adds an event and deletes it in same state */ diff --git a/client/modules/volunteerScheduling/components/VolunteerScheduling.spec.js b/client/modules/volunteerScheduling/components/VolunteerScheduling.spec.js new file mode 100644 index 00000000..17943b44 --- /dev/null +++ b/client/modules/volunteerScheduling/components/VolunteerScheduling.spec.js @@ -0,0 +1,160 @@ +import React from 'react' +import Enzyme, { shallow } from 'enzyme' +import Adapter from 'enzyme-adapter-react-15' + +import { VolunteerScheduling } from './VolunteerScheduling' + +Enzyme.configure({adapter: new Adapter()}) + +describe('VolunteerScheduling', () => { + let props + + beforeEach(() => { + props = { + volunteers: [], + clearFlags: sinon.spy(), + loadVolunteers: sinon.spy() + } + }) + + it('sets state when constructed', () => { + const wrapper = shallow() + expect(wrapper.state().showAddModal).to.be.false + expect(wrapper.state().showEditModal).to.be.false + expect(wrapper.state().shiftToDelete).to.equal("") + expect(wrapper.state().added).to.be.false + expect(wrapper.state().removed).to.be.false + expect(wrapper.state().moved).to.be.false + }) + + it('should set calendar to the calendar object', () => { + const wrapper = shallow() + expect(wrapper.state().c).to.not.equal("") + }) + + it('updateCalendar add event to state and sets added to true', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.setState({events: [] }) + + const volunteer = {firstName: "Samuel", lastName: "Freiberg", _id: 1} + const event = {date: "04/12/2019T00:00:00", notes: "Test"} + + instance.updateCalendar(volunteer, event) + expect(instance.state.events.length).to.equal(1) + }) + + it('openModal sets showAddModal to true', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.openModal()() + expect(instance.state.showAddModal).to.be.true + }) + + it('openModal sets showEditModal to true and shiftToDelete to event param', () => { + const wrapper = shallow() + const instance = wrapper.instance() + + var event = { + date: "04/12/2019T00:00:00", + notes: "Test" + } + + instance.openModal(event)() + expect(instance.state.showEditModal).to.be.true + expect(instance.state.shiftToDelete).to.deep.equal({ date: "04/12/2019T00:00:00", notes: "Test" }) + }) + + it('openModal calls props.clearFlags', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.openModal()() + expect(instance.props.clearFlags.called).to.be.true + }) + + it('closeAddModal sets showAddModal to false', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.closeAddModal() + expect(instance.state.showAddModal).to.be.false + }) + + it('closeAddModal calls props.clearFlags', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.closeAddModal() + expect(instance.props.clearFlags.called).to.be.true + }) + + it('closeEditModal sets showEditModal to false', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.closeEditModal() + expect(instance.state.showEditModal).to.be.false + }) + + it('closeEditModal calls props.clearFlags', () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.closeEditModal() + expect(instance.props.clearFlags.called).to.be.true + }) + + it('countShifts returns true if params have same length', () => { + const wrapper = shallow() + const instance = wrapper.instance() + + var shift1 = ["1", "2"] + var shift12 = ["1", "2", "3"] + var shift2 = ["1", "2"] + var shift22 = ["1", "2", "3"] + + var shifts1 = [] + shifts1.push(shift1) + shifts1.push(shift12) + + var shifts2 = [] + shifts2.push(shift2) + shifts2.push(shift22) + + var volunteer1 = { shift: shifts1} + var volunteer2 = { shift: shifts2} + + var v1 = [] + v1.push(volunteer1) + var v2 = [] + v2.push(volunteer2) + + expect(instance.countShifts(v1, v2)).to.equal(true) + }) + + it('countShifts returns false if params have diff length', () => { + const wrapper = shallow() + const instance = wrapper.instance() + + var shift1 = ["1", "2"] + var shift12 = ["1", "2", "3", "4"] + var shift13 = ["1", "2", "3", "4"] + var shift2 = ["1", "2"] + var shift22 = ["1", "2", "3"] + + var shifts1 = [] + shifts1.push(shift1) + shifts1.push(shift12) + shifts1.push(shift13) + + var shifts2 = [] + shifts2.push(shift2) + shifts2.push(shift22) + + var volunteer1 = { shift: shifts1} + var volunteer2 = { shift: shifts2} + + var v1 = [] + v1.push(volunteer1) + var v2 = [] + v2.push(volunteer2) + + expect(instance.countShifts(v1, v2)).to.be.false + }) +}) diff --git a/package.json b/package.json index dbb9876f..5c30ccca 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "test": "npm run test:server:once && npm run test:client:once", "test:client": "npm run test:client:once -- --watch", "test:server": "npm run test:server:once -- --watch", + "test:volunteerScheduling": "npm run test:volunteerScheduling:once -- --watch", + "test:volunteerScheduling:once": "cross-env NODE_ENV=test mocha --require babel-register --require ignore-styles --require client/entry.test.js \"client/modules/volunteerScheduling/components/VolunteerScheduling.spec.js\"", "test:client:once": "cross-env NODE_ENV=test mocha --require babel-register --require ignore-styles --require client/entry.test.js \"client/**/*.spec.js\"", "test:server:once": "cross-env NODE_ENV=test mocha --timeout 5000 --require babel-register --require server/entry.test.js \"server/**/*.spec.js\"", "flow": "flow" From 3c11c2ff965600fb3f2d19817c66b690e943e3b5 Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Wed, 17 Apr 2019 10:24:14 -0700 Subject: [PATCH 3/9] Added initial framework for client mass upload --- .../customer/components/CustomerList.js | 33 +++- .../customer/components/MassImportsModal.js | 143 ++++++++++++++++++ client/modules/customer/reducer.js | 9 ++ package-lock.json | 13 ++ package.json | 5 +- server/controllers/customer.js | 27 ++++ server/lib/questionnaire-helpers.js | 21 ++- server/routes/customer.js | 3 + 8 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 client/modules/customer/components/MassImportsModal.js diff --git a/client/modules/customer/components/CustomerList.js b/client/modules/customer/components/CustomerList.js index a9ce4d8c..0cacc4a2 100644 --- a/client/modules/customer/components/CustomerList.js +++ b/client/modules/customer/components/CustomerList.js @@ -2,18 +2,21 @@ import React, {Component} from 'react' import {connect} from 'react-redux' import {Link} from 'react-router-dom' import {BootstrapTable, TableHeaderColumn, SizePerPageDropDown} from 'react-bootstrap-table' +import { Button, Modal } from 'react-bootstrap' import 'react-bootstrap-table/dist/react-bootstrap-table.min.css' import {fieldTypes} from '../../../../common/constants' import selectors from '../../../store/selectors' import {fieldsByType} from '../../../lib/questionnaire-helpers' -import {loadCustomers} from '../reducer' +import {loadCustomers, massUpload} from '../reducer' import {loadQuestionnaires} from '../../questionnaire/reducers/api' import {Box, BoxBody, BoxHeader} from '../../../components/box' import ClientStatusLabel from '../../../components/ClientStatusLabel' import {Page, PageBody} from '../../../components/page' +import MassImportsModal from './MassImportsModal' + const mapStateToProps = state => ({ customers: selectors.customer.getAll(state), loading: selectors.customer.loading(state) || @@ -24,15 +27,31 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ loadCustomers: () => dispatch(loadCustomers()), - loadQuestionnaires: () => dispatch(loadQuestionnaires()) + loadQuestionnaires: () => dispatch(loadQuestionnaires()), + massUpload: (docs) => dispatch(massUpload(docs)) }) class CustomerList extends Component { + constructor(props) { + super(props) + this.state = { + showImportsModal: false + } + } + componentWillMount() { this.props.loadCustomers() this.props.loadQuestionnaires() } + openModal = () => { + this.setState({showImportsModal: true}) + } + + closeModal = () => { + this.setState({showImportsModal: false}) + } + getStatusLabel = (_, customer) => getActionButtons = (_, customer) => @@ -70,6 +89,16 @@ class CustomerList extends Component { loading={loading} error={loadError} > + + + + + + + { + /* Error Checking for Empty File */ + if(data.length == 0 || data.length == 1 || data.length == 2 || data[0][0] == "") { + this.printErrorMessage("Empty") + } + /* Error Checking for Header Format */ + else if (!this.validateHeaders(data[0])) { + this.printErrorMessage("Format") + } + else { + document.getElementById("error").innerHTML = "" + var info = [] + + for(var i = 1; i < data.length-1; i++) { + if(!this.validateRow(data[i])) { + this.printErrorMessage("Format") + } + else { + var firstName = data[i][0] + var lastName = data[i][1] + var email = data[i][2] + var address = "" + // var birthday = "" + var relationship = "" + + for(var j = 3; j < data[i].length; j++) { + if(j == 3) { + address = data[i][j] + } + // else if(j == 4) { + // birthday = new Date(data[i][j]) + // } + else if(j == 5) { + relationship = data[i][j] + } + } + // var household = [] + // household.push({name: "", relationship: relationship, dateOfBirth: birthday}) + var household = {name: "", relationship: relationship, dateOfBirth: new Date()} + + info.push({firstName: firstName, lastName: lastName, email: email, address: address, household: household}) + } + } + this.setState({validInput: true, documents: info}) + } + } + + printErrorMessage = error => { + document.getElementById("error").style.color = "red" + if(error == "Empty") { + document.getElementById("error").innerHTML = "Error: Empty CSV Input File." + } + else if(error == "Format") { + document.getElementById("error").innerHTML = "Error: Invalid Input File Format. Please Use CSV Template." + } + this.setState({validInput: false}) + } + + /* Validates header length and values */ + /* EXPECTING: [First Name, Last Name, Email, Address, Birthday, Relationship] (Only first 3 required) */ + validateHeaders = headers => { + if(headers.length > 6 || headers.length < 3) { + return false + } + else { + var expected = ["First Name", "Last Name", "Email", "Address", "Birthday", "Relationship"] + for(var i = 0; i < headers.length; i++) { + if(headers[i] != expected[i]) { + return false + } + } + } + return true + } + + /* Validates that a row has the correct informaiton and no blanks for required fields */ + validateRow = row => { + if(row.length < 3) { + return false + } + else if(row[0] == "" || row[1] == "" || row[2] == "") { + return false + } + return true + } + + + importData = () => { + var docs = this.state.documents + // console.log(docs) + //console.log(docs[0].household.dateOfBirth) + this.props.massUpload(docs) + this.props.closeModal() + } + + + handleError = () => { + document.getElementById("error").innerHTML = "Weird error" + } + + render = () => { + return ( + + + Customer Mass Imports + + +
+ +
+ + +
+
+
+ ) + } +} diff --git a/client/modules/customer/reducer.js b/client/modules/customer/reducer.js index 73a3ee99..03b15adb 100644 --- a/client/modules/customer/reducer.js +++ b/client/modules/customer/reducer.js @@ -25,6 +25,15 @@ export const loadCustomer = id => ({ } }) +export const massUpload = docs => ({ + [CALL_API]: { + endpoint: 'customer/massUpload', + method: 'POST', + body: docs, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + export const saveCustomer = customer => { const endpoint = customer._id ? `customer/${customer._id}` : `customer` return { diff --git a/package-lock.json b/package-lock.json index e1dc4b7b..9165f1ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12372,6 +12372,11 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, + "papaparse": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-4.6.3.tgz", + "integrity": "sha512-LRq7BrHC2kHPBYSD50aKuw/B/dGcg29omyJbKWY3KsYUZU69RKwaBHu13jGmCYBtOc4odsLCrFyk6imfyNubJQ==" + }, "parallel-transform": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", @@ -19392,6 +19397,14 @@ "react-modal": "1.9.7" } }, + "react-csv-reader": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/react-csv-reader/-/react-csv-reader-1.2.2.tgz", + "integrity": "sha512-k4kpdEgJm6KiZ87yQTrB4gvlwsXgRZaSR/BZ0dAp+ry/EdlmJn983MTdWnR4htgx91RdjSFqEb75WDcW8OD11g==", + "requires": { + "papaparse": "4.6.3" + } + }, "react-day-picker": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-6.2.1.tgz", diff --git a/package.json b/package.json index 5c30ccca..92436271 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "test": "npm run test:server:once && npm run test:client:once", "test:client": "npm run test:client:once -- --watch", "test:server": "npm run test:server:once -- --watch", - "test:volunteerScheduling": "npm run test:volunteerScheduling:once -- --watch", - "test:volunteerScheduling:once": "cross-env NODE_ENV=test mocha --require babel-register --require ignore-styles --require client/entry.test.js \"client/modules/volunteerScheduling/components/VolunteerScheduling.spec.js\"", + "test:volunteerScheduling": "npm run test:volunteerScheduling:once -- --watch", + "test:volunteerScheduling:once": "cross-env NODE_ENV=test mocha --require babel-register --require ignore-styles --require client/entry.test.js \"client/modules/volunteerScheduling/components/VolunteerScheduling.spec.js\"", "test:client:once": "cross-env NODE_ENV=test mocha --require babel-register --require ignore-styles --require client/entry.test.js \"client/**/*.spec.js\"", "test:server:once": "cross-env NODE_ENV=test mocha --timeout 5000 --require babel-register --require server/entry.test.js \"server/**/*.spec.js\"", "flow": "flow" @@ -104,6 +104,7 @@ "react-bootstrap": "^0.31.0", "react-bootstrap-autosuggest": "^0.5.0", "react-bootstrap-table": "^3.1.7", + "react-csv-reader": "^1.2.2", "react-day-picker": "^6.1.0", "react-dnd": "^2.3.0", "react-dnd-html5-backend": "^2.3.0", diff --git a/server/controllers/customer.js b/server/controllers/customer.js index aca2a12f..424fd408 100644 --- a/server/controllers/customer.js +++ b/server/controllers/customer.js @@ -41,6 +41,33 @@ export default { res.json(req.customer) }, + + + async massUpload(req, res) { + const docs = req.body + const fields = [] + fields.push({meta: "c523b236-cf40-47fc-94d4-1f5ae9ccd3c1", value: "test"}) + fields.push({meta: "d2cd8ff9-d70a-4316-a970-5c9a11dc0076", value: "test"}) + fields.push({meta: "d4b7113c-9943-4858-893c-1b2512533f46", value: "test"}) + fields.push({meta: "ce470f63-9c6f-401f-8c85-c9fef022be90", value: "test"}) + fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: docs[0].household.dateOfBirth}) + + await Customer.insertMany( [ + {firstName: "Sam", lastName: "Freiberg", email: "hi", fields: fields} + ]) + + res.status(200).json({message: "Successful mass import!"}) + + // await Customer.insertMany(docs, function(err, docs) { + // if(err) { + // res.status(400).json({message: "Unable to mass import in Database"}) + // } + // else { + // res.status(200).json({message: "Successful mass import!"}) + // } + // }) + }, + /** * Update a customer */ diff --git a/server/lib/questionnaire-helpers.js b/server/lib/questionnaire-helpers.js index 41d644f2..1e780f96 100644 --- a/server/lib/questionnaire-helpers.js +++ b/server/lib/questionnaire-helpers.js @@ -7,7 +7,12 @@ async function getQuestionnaireFields (identifier, type) { const questionnaire = await Questionnaire.findOne({identifier}) if (!questionnaire) throw new Error('Invalid questionnaire') + // questionnaire = Customer Application + const allFields = flatMap(questionnaire.sections, section => section.fields) + + // allFields = All different fields... ie address, birthday, etc + return type ? allFields.filter(field => field.type === type) : allFields } @@ -32,14 +37,28 @@ export function getValidator(identifier) { return async function validator(fields) { const qFields = await getQuestionnaireFields(identifier) + // qFields = all different fields... ie address, birthday, etc + // console.log(fields) + // console.log(qFields) + if (!fields.every(field => qFields.find(qField => qField._id === field.meta))) { + //console.log("In 1st") return false } return qFields.reduce((valid, qField) => { - if (!valid) return false + // console.log(valid + ", " + qField) + if (!valid) { + return false + } + // if(qField.label == "Date of Birth") { + // console.log(fields) + // } const field = fields.find(f => String(qField._id) === String(f.meta)) || {} + if(qField.label == "Date of Birth") { + // console.log(field) + } const error = validate(field.value, qField) return !Object.keys(error).length diff --git a/server/routes/customer.js b/server/routes/customer.js index 34636a9e..3bfbf770 100644 --- a/server/routes/customer.js +++ b/server/routes/customer.js @@ -18,6 +18,9 @@ export default () => { customerRouter.route('/customer') .post(requiresLogin, customerController.create) + customerRouter.route('/customer/massUpload') + .post(requiresLogin, customerController.massUpload) + customerRouter.route('/customer/:customerId') .get(requiresLogin, hasAuthorization, customerController.read) .put(requiresLogin, hasAuthorization, canChangeStatus, customerController.update) From 3cbe735e6c1f0e3ac6b5990cdb1785018285b3cf Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Wed, 17 Apr 2019 10:25:13 -0700 Subject: [PATCH 4/9] Added initial framework for client mass upload --- client/modules/customer/components/CustomerList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/modules/customer/components/CustomerList.js b/client/modules/customer/components/CustomerList.js index 0cacc4a2..bd1c2e3f 100644 --- a/client/modules/customer/components/CustomerList.js +++ b/client/modules/customer/components/CustomerList.js @@ -28,7 +28,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ loadCustomers: () => dispatch(loadCustomers()), loadQuestionnaires: () => dispatch(loadQuestionnaires()), - massUpload: (docs) => dispatch(massUpload(docs)) + massUpload: docs => dispatch(massUpload(docs)) }) class CustomerList extends Component { From f44fe03ae97150209f0de6cddb1cb3251a0de537 Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Wed, 17 Apr 2019 10:50:44 -0700 Subject: [PATCH 5/9] Client mass upload working. --- .../customer/components/MassImportsModal.js | 49 ++++++++++--------- server/controllers/customer.js | 29 +++-------- 2 files changed, 33 insertions(+), 45 deletions(-) diff --git a/client/modules/customer/components/MassImportsModal.js b/client/modules/customer/components/MassImportsModal.js index a13166d0..c3b90b11 100644 --- a/client/modules/customer/components/MassImportsModal.js +++ b/client/modules/customer/components/MassImportsModal.js @@ -35,26 +35,24 @@ export default class MassImportsModal extends React.Component { var firstName = data[i][0] var lastName = data[i][1] var email = data[i][2] - var address = "" - // var birthday = "" - var relationship = "" + var birthday = new Date(data[i][3]) + var street = data[i][4] + var city = data[i][5] + var state = data[i][6] + var zip = data[i][7] - for(var j = 3; j < data[i].length; j++) { - if(j == 3) { - address = data[i][j] - } - // else if(j == 4) { - // birthday = new Date(data[i][j]) - // } - else if(j == 5) { - relationship = data[i][j] - } - } - // var household = [] - // household.push({name: "", relationship: relationship, dateOfBirth: birthday}) - var household = {name: "", relationship: relationship, dateOfBirth: new Date()} + /* + ** The meta field was taken from the Questionnaire model. I'm not sure what it is, + ** but it's a required field. + */ + var fields = [] + fields.push({meta: "c523b236-cf40-47fc-94d4-1f5ae9ccd3c1", value: street}) + fields.push({meta: "d2cd8ff9-d70a-4316-a970-5c9a11dc0076", value: city}) + fields.push({meta: "d4b7113c-9943-4858-893c-1b2512533f46", value: state}) + fields.push({meta: "ce470f63-9c6f-401f-8c85-c9fef022be90", value: zip}) + fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: birthday}) - info.push({firstName: firstName, lastName: lastName, email: email, address: address, household: household}) + info.push({firstName: firstName, lastName: lastName, email: email, fields: fields}) } } this.setState({validInput: true, documents: info}) @@ -73,13 +71,12 @@ export default class MassImportsModal extends React.Component { } /* Validates header length and values */ - /* EXPECTING: [First Name, Last Name, Email, Address, Birthday, Relationship] (Only first 3 required) */ validateHeaders = headers => { - if(headers.length > 6 || headers.length < 3) { + if(headers.length != 8) { return false } else { - var expected = ["First Name", "Last Name", "Email", "Address", "Birthday", "Relationship"] + var expected = ["First Name", "Last Name", "Email", "Birthday", "Street", "City/Town", "State/Province", "ZIP"] for(var i = 0; i < headers.length; i++) { if(headers[i] != expected[i]) { return false @@ -91,11 +88,15 @@ export default class MassImportsModal extends React.Component { /* Validates that a row has the correct informaiton and no blanks for required fields */ validateRow = row => { - if(row.length < 3) { + if(row.length != 8) { return false } - else if(row[0] == "" || row[1] == "" || row[2] == "") { - return false + else { + for(var i = 0; i < row.length; i++) { + if(row[i] == "") { + return false + } + } } return true } diff --git a/server/controllers/customer.js b/server/controllers/customer.js index 424fd408..60b1aaa0 100644 --- a/server/controllers/customer.js +++ b/server/controllers/customer.js @@ -45,27 +45,14 @@ export default { async massUpload(req, res) { const docs = req.body - const fields = [] - fields.push({meta: "c523b236-cf40-47fc-94d4-1f5ae9ccd3c1", value: "test"}) - fields.push({meta: "d2cd8ff9-d70a-4316-a970-5c9a11dc0076", value: "test"}) - fields.push({meta: "d4b7113c-9943-4858-893c-1b2512533f46", value: "test"}) - fields.push({meta: "ce470f63-9c6f-401f-8c85-c9fef022be90", value: "test"}) - fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: docs[0].household.dateOfBirth}) - - await Customer.insertMany( [ - {firstName: "Sam", lastName: "Freiberg", email: "hi", fields: fields} - ]) - - res.status(200).json({message: "Successful mass import!"}) - - // await Customer.insertMany(docs, function(err, docs) { - // if(err) { - // res.status(400).json({message: "Unable to mass import in Database"}) - // } - // else { - // res.status(200).json({message: "Successful mass import!"}) - // } - // }) + + await Customer.insertMany(docs, function(err) { + if(err) { + res.status(400).json({message: "Unable to mass import in Database"}) + } else { + res.status(200).json({message: "Successful mass import!"}) + } + }) }, /** From 6cacb065add9a21b51031a1f44914f17d75a25ee Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Thu, 18 Apr 2019 13:24:54 -0700 Subject: [PATCH 6/9] Client mass upload working... Still need tests --- .../customer/components/CustomerList.js | 3 ++ .../customer/components/MassImportsModal.js | 31 +++++++++++++++++-- client/modules/customer/reducer.js | 11 +++++++ server/controllers/customer.js | 30 ++++++++++++++++-- server/models/customer.js | 4 +++ server/routes/customer.js | 12 +++++++ 6 files changed, 85 insertions(+), 6 deletions(-) diff --git a/client/modules/customer/components/CustomerList.js b/client/modules/customer/components/CustomerList.js index bd1c2e3f..79edc9e9 100644 --- a/client/modules/customer/components/CustomerList.js +++ b/client/modules/customer/components/CustomerList.js @@ -49,6 +49,7 @@ class CustomerList extends Component { } closeModal = () => { + location.reload() this.setState({showImportsModal: false}) } @@ -94,8 +95,10 @@ class CustomerList extends Component { diff --git a/client/modules/customer/components/MassImportsModal.js b/client/modules/customer/components/MassImportsModal.js index c3b90b11..e85ede0a 100644 --- a/client/modules/customer/components/MassImportsModal.js +++ b/client/modules/customer/components/MassImportsModal.js @@ -41,6 +41,8 @@ export default class MassImportsModal extends React.Component { var state = data[i][6] var zip = data[i][7] + //this.isDuplicate(firstName, lastName, email, customers) + /* ** The meta field was taken from the Questionnaire model. I'm not sure what it is, ** but it's a required field. @@ -53,12 +55,29 @@ export default class MassImportsModal extends React.Component { fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: birthday}) info.push({firstName: firstName, lastName: lastName, email: email, fields: fields}) + //id++ } } this.setState({validInput: true, documents: info}) } } + // Determines if customer is a duplicate + // I use email as well in the edge case that there are 2 people + // with the same name + isDuplicate = (firstName, lastName, email, customers) => { + for(var i = 0; i < customers.length; i++) { + if(customers[i].firstName.toLowerCase() == firstName.toLowerCase() && + customers[i].lastName.toLowerCase() == lastName.toLowerCase() && + customers[i].email.toLowerCase() == email.toLowerCase()) { + + this.props.duplicate(customers[i]) + // API call to change duplicate value + + } + } + } + printErrorMessage = error => { document.getElementById("error").style.color = "red" if(error == "Empty") { @@ -104,9 +123,15 @@ export default class MassImportsModal extends React.Component { importData = () => { var docs = this.state.documents - // console.log(docs) - //console.log(docs[0].household.dateOfBirth) - this.props.massUpload(docs) + const customer = { + firstName: 'test', + lastName: 'test', + email: 'test@test.com' + } + this.props.massUpload({ + ...customer, + docs: docs + }) this.props.closeModal() } diff --git a/client/modules/customer/reducer.js b/client/modules/customer/reducer.js index 03b15adb..25bf6531 100644 --- a/client/modules/customer/reducer.js +++ b/client/modules/customer/reducer.js @@ -30,10 +30,21 @@ export const massUpload = docs => ({ endpoint: 'customer/massUpload', method: 'POST', body: docs, + schema: customerSchema, types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] } }) +// export const duplicate = customer => ({ +// [CALL_API]: { +// endpoint: 'customer/duplicate', +// method: 'PUT', +// body: customer, +// schema: customerSchema, +// types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] +// } +// }) + export const saveCustomer = customer => { const endpoint = customer._id ? `customer/${customer._id}` : `customer` return { diff --git a/server/controllers/customer.js b/server/controllers/customer.js index 60b1aaa0..f7232447 100644 --- a/server/controllers/customer.js +++ b/server/controllers/customer.js @@ -44,15 +44,39 @@ export default { async massUpload(req, res) { - const docs = req.body + const customer = req.body + var docs = customer.docs + var max = 0 - await Customer.insertMany(docs, function(err) { + await Customer.find({}, function(err, customers) { + max = customers.sort( (a, b) => a._id > b._id ? 1 : -1)[customers.length-1]._id + 1 + }) + + + let newDocs = docs.map(cust => new Customer(cust)) + for(var i = 0; i < newDocs.length; i++) { + newDocs[i]._id = max + max = max + 1 + } + + + // let newCustomer = new Customer(docs[0]) + // newCustomer._id = req.user.id + // // res.status(200).json({message: "Successful mass import!"}) + // //let newDocs = docs.map(cust => new Customer(cust)) + // // var newDocs2 = newDocs.map(cust => ({ ...cust, _id: req.user.id})) + + // // for(var i = 0; i < newDocs.length; i++) { + // // newDocs[i]._id = req.user.id + // // } + + await Customer.insertMany(newDocs, function(err) { if(err) { res.status(400).json({message: "Unable to mass import in Database"}) } else { res.status(200).json({message: "Successful mass import!"}) } - }) + }) }, /** diff --git a/server/models/customer.js b/server/models/customer.js index 4406e3f3..ec1bac32 100644 --- a/server/models/customer.js +++ b/server/models/customer.js @@ -55,6 +55,10 @@ const CustomerSchema = new Schema({ type: Date, default: null }, + // duplicate: { + // type: Boolean, + // default: false + // }, packingList: [{ type: Schema.Types.ObjectId, ref: 'FoodItem' diff --git a/server/routes/customer.js b/server/routes/customer.js index 3bfbf770..55e80e54 100644 --- a/server/routes/customer.js +++ b/server/routes/customer.js @@ -18,9 +18,21 @@ export default () => { customerRouter.route('/customer') .post(requiresLogin, customerController.create) + + + customerRouter.route('/customer/massUpload') .post(requiresLogin, customerController.massUpload) + // customerRouter.route('/customer/duplicate') + // .put(requiresLogin, customerController.duplicate) + + + + + + + customerRouter.route('/customer/:customerId') .get(requiresLogin, hasAuthorization, customerController.read) .put(requiresLogin, hasAuthorization, canChangeStatus, customerController.update) From 446a3993be972314589aa459a11ecbcb3402232d Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Sat, 20 Apr 2019 13:52:48 -0700 Subject: [PATCH 7/9] Added mass imports for inventory --- .../customer/components/MassImportsModal.js | 16 +- client/modules/donor/components/DonorList.js | 37 +++- .../donor/components/MassImportsModal.js | 166 +++++++++++++++++ client/modules/donor/reducers/donor.js | 10 + .../food/components/inventory/FoodItems.js | 56 +++++- .../components/inventory/MassImportsModal.js | 135 ++++++++++++++ client/modules/food/reducers/item.js | 10 + .../volunteer/components/MassImportsModal.js | 175 ++++++++++++++++++ .../volunteer/components/VolunteerList.js | 35 +++- client/modules/volunteer/reducer.js | 10 + server/controllers/customer.js | 11 -- server/controllers/donor.js | 34 ++++ server/controllers/food.js | 59 ++++++ server/controllers/volunteer.js | 26 +++ server/routes/customer.js | 12 -- server/routes/donor.js | 8 + server/routes/food.js | 7 + server/routes/volunteer.js | 3 + 18 files changed, 774 insertions(+), 36 deletions(-) create mode 100644 client/modules/donor/components/MassImportsModal.js create mode 100644 client/modules/food/components/inventory/MassImportsModal.js create mode 100644 client/modules/volunteer/components/MassImportsModal.js diff --git a/client/modules/customer/components/MassImportsModal.js b/client/modules/customer/components/MassImportsModal.js index e85ede0a..3984f51b 100644 --- a/client/modules/customer/components/MassImportsModal.js +++ b/client/modules/customer/components/MassImportsModal.js @@ -27,6 +27,8 @@ export default class MassImportsModal extends React.Component { document.getElementById("error").innerHTML = "" var info = [] + const customers = this.props.customers + for(var i = 1; i < data.length-1; i++) { if(!this.validateRow(data[i])) { this.printErrorMessage("Format") @@ -41,7 +43,13 @@ export default class MassImportsModal extends React.Component { var state = data[i][6] var zip = data[i][7] - //this.isDuplicate(firstName, lastName, email, customers) + // Don't add this entry if it's a duplicate + if(this.isDuplicate(firstName, lastName, email, customers) == true) { + console.log("Duplicate!") + continue + } + + console.log("Not duplicate!") /* ** The meta field was taken from the Questionnaire model. I'm not sure what it is, @@ -55,7 +63,6 @@ export default class MassImportsModal extends React.Component { fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: birthday}) info.push({firstName: firstName, lastName: lastName, email: email, fields: fields}) - //id++ } } this.setState({validInput: true, documents: info}) @@ -71,11 +78,10 @@ export default class MassImportsModal extends React.Component { customers[i].lastName.toLowerCase() == lastName.toLowerCase() && customers[i].email.toLowerCase() == email.toLowerCase()) { - this.props.duplicate(customers[i]) - // API call to change duplicate value - + return true } } + return false } printErrorMessage = error => { diff --git a/client/modules/donor/components/DonorList.js b/client/modules/donor/components/DonorList.js index 00f13607..d8fb745e 100644 --- a/client/modules/donor/components/DonorList.js +++ b/client/modules/donor/components/DonorList.js @@ -8,13 +8,14 @@ import 'react-bootstrap-table/dist/react-bootstrap-table.min.css' import {fieldTypes} from '../../../../common/constants' import {fieldsByType} from '../../../lib/questionnaire-helpers' import selectors from '../../../store/selectors' -import {loadDonors, deleteDonor} from '../reducers/donor' +import {loadDonors, deleteDonor, massUpload} from '../reducers/donor' import {loadQuestionnaires} from '../../questionnaire/reducers/api' import {showConfirmDialog, hideDialog} from '../../core/reducers/dialog' import {Box, BoxBody, BoxHeader} from '../../../components/box' import {Page, PageBody} from '../../../components/page' + const mapStateToProps = state => ({ donors: selectors.donor.getAll(state), savingDonors: selectors.donor.saving(state), @@ -31,15 +32,32 @@ const mapDispatchToProps = dispatch => ({ loadQuestionnaires: () => dispatch(loadQuestionnaires()), showDialog: (cancel, confirm, message) => dispatch(showConfirmDialog(cancel, confirm, message, 'Delete')), - hideDialog: () => dispatch(hideDialog()) + hideDialog: () => dispatch(hideDialog()), + massUpload: docs => dispatch(massUpload(docs)) }) class DonorList extends Component { + constructor(props) { + super(props) + this.state = { + showImportsModal: false + } + } + componentWillMount() { this.props.loadDonors() this.props.loadQuestionnaires() } + openModal = () => { + this.setState({showImportsModal: true}) + } + + closeModal = () => { + location.reload() + this.setState({showImportsModal: false}) + } + totalDonations = (_, donor) => { if (!donor || !donor.donations.length) return 0 return donor.donations.reduce((acc, x) => acc + x.total || 0, 0) @@ -90,7 +108,8 @@ class DonorList extends Component { + > + Mass Imports + +// +// +// \ No newline at end of file diff --git a/client/modules/donor/components/MassImportsModal.js b/client/modules/donor/components/MassImportsModal.js new file mode 100644 index 00000000..e31af994 --- /dev/null +++ b/client/modules/donor/components/MassImportsModal.js @@ -0,0 +1,166 @@ +import React from 'react' +import { Button } from 'react-bootstrap' +import { Box, BoxHeader, BoxBody } from '../../../components/box' + +import CSVReader from 'react-csv-reader' + +export default class MassImportsModal extends React.Component { + + constructor(props) { + super(props) + this.state = { + validInput: false, + documents: [] + } + } + + handleInput = data => { + /* Error Checking for Empty File */ + if(data.length == 0 || data.length == 1 || data.length == 2 || data[0][0] == "") { + this.printErrorMessage("Empty") + } + /* Error Checking for Header Format */ + else if (!this.validateHeaders(data[0])) { + this.printErrorMessage("Format") + } + else { + document.getElementById("error").innerHTML = "" + var info = [] + + for(var i = 1; i < data.length-1; i++) { + if(!this.validateRow(data[i])) { + this.printErrorMessage("Format") + } + else { + var firstName = data[i][0] + var lastName = data[i][1] + var email = data[i][2] + var birthday = new Date(data[i][3]) + var street = data[i][4] + var city = data[i][5] + var state = data[i][6] + var zip = data[i][7] + + /* + ** The meta field was taken from the Questionnaire model. I'm not sure what it is, + ** but it's a required field. + */ + var fields = [] + fields.push({meta: "c523b236-cf40-47fc-94d4-1f5ae9ccd3c1", value: street}) + fields.push({meta: "d2cd8ff9-d70a-4316-a970-5c9a11dc0076", value: city}) + fields.push({meta: "d4b7113c-9943-4858-893c-1b2512533f46", value: state}) + fields.push({meta: "ce470f63-9c6f-401f-8c85-c9fef022be90", value: zip}) + fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: birthday}) + + info.push({firstName: firstName, lastName: lastName, email: email, fields: fields}) + } + } + this.setState({validInput: true, documents: info}) + } + } + + // Determines if customer is a duplicate + // I use email as well in the edge case that there are 2 people + // with the same name + isDuplicate = (firstName, lastName, email, customers) => { + for(var i = 0; i < customers.length; i++) { + if(customers[i].firstName.toLowerCase() == firstName.toLowerCase() && + customers[i].lastName.toLowerCase() == lastName.toLowerCase() && + customers[i].email.toLowerCase() == email.toLowerCase()) { + + this.props.duplicate(customers[i]) + // API call to change duplicate value + + } + } + } + + printErrorMessage = error => { + document.getElementById("error").style.color = "red" + if(error == "Empty") { + document.getElementById("error").innerHTML = "Error: Empty CSV Input File." + } + else if(error == "Format") { + document.getElementById("error").innerHTML = "Error: Invalid Input File Format. Please Use CSV Template." + } + this.setState({validInput: false}) + } + + /* Validates header length and values */ + validateHeaders = headers => { + if(headers.length != 8) { + return false + } + else { + var expected = ["First Name", "Last Name", "Email", "Birthday", "Street", "City/Town", "State/Province", "ZIP"] + for(var i = 0; i < headers.length; i++) { + if(headers[i] != expected[i]) { + return false + } + } + } + return true + } + + /* Validates that a row has the correct informaiton and no blanks for required fields */ + validateRow = row => { + if(row.length != 8) { + return false + } + else { + for(var i = 0; i < row.length; i++) { + if(row[i] == "") { + return false + } + } + } + return true + } + + + importData = () => { + var docs = this.state.documents + const donor = { + firstName: 'test', + lastName: 'test', + email: 'test@test.com' + } + this.props.massUpload({ + ...donor, + docs: docs + }) + this.props.closeModal() + } + + + handleError = () => { + document.getElementById("error").innerHTML = "Weird error" + } + + render = () => { + return ( + + + Donor Mass Imports + + +
+ +
+ + +
+
+
+ ) + } +} diff --git a/client/modules/donor/reducers/donor.js b/client/modules/donor/reducers/donor.js index 6cbbb97b..3a5ad088 100644 --- a/client/modules/donor/reducers/donor.js +++ b/client/modules/donor/reducers/donor.js @@ -24,6 +24,16 @@ export const loadDonor = (id, admin) => ({ } }) +export const massUpload = docs => ({ + [CALL_API]: { + endpoint: 'donor/massUpload', + method: 'POST', + body: docs, + schema: donorSchema, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + export const saveDonor = (donor, admin) => { let endpoint if (admin) endpoint = donor._id ? `admin/donors/${donor._id}` : `admin/donors` diff --git a/client/modules/food/components/inventory/FoodItems.js b/client/modules/food/components/inventory/FoodItems.js index fab23c94..650289dc 100644 --- a/client/modules/food/components/inventory/FoodItems.js +++ b/client/modules/food/components/inventory/FoodItems.js @@ -5,11 +5,13 @@ import { BootstrapTable, TableHeaderColumn, SizePerPageDropDown } from 'react-bo import _ from 'lodash' import selectors from '../../../../store/selectors' -import { saveFoodItem, deleteFoodItem, clearFlags } from '../../reducers/item' +import { saveFoodItem, deleteFoodItem, clearFlags, massUpload } from '../../reducers/item' import { Box, BoxBody } from '../../../../components/box' import { showConfirmDialog, hideDialog } from '../../../core/reducers/dialog' import FoodAddEditForm from './FoodAddEditForm' +import MassImportsModal from './MassImportsModal' + export class FoodItems extends React.Component { constructor(props) { super(props) @@ -18,7 +20,8 @@ export class FoodItems extends React.Component { modalType: undefined, // editModalFood is the food being edited when the modalType is edit editModalFood: undefined, - searchText: "" + searchText: "", + massImportModal: false } } @@ -60,6 +63,24 @@ export class FoodItems extends React.Component { return categoryObject.category } + + /** + * Opens the mass imports modal + */ + openMassImportModal = () => { + this.setState({massImportModal: true}) + this.props.clearFlags() + } + + // Closes the mass imports modal + + closeMassImportModal = () => { + this.setState({massImportModal: false}) + this.props.clearFlags() + } + + + /** * Used by react-bootstrap-table to get the Edit and Delete buttons for a row */ @@ -94,7 +115,7 @@ export class FoodItems extends React.Component {
-
+
{props.components.searchPanel}
@@ -130,6 +151,17 @@ export class FoodItems extends React.Component { loading={this.state.modalType ? undefined : (this.props.loading || this.props.saving)} error={this.state.modalType ? undefined : (this.props.loadError || this.props.saveError)} errorBottom={true}> + + + + + Name Category @@ -170,9 +202,27 @@ const mapDispatchToProps = dispatch => ({ saveFoodItem: (categoryId, foodItem) => dispatch(saveFoodItem(categoryId, foodItem)), deleteFoodItem: (categoryId, _id) => dispatch(deleteFoodItem(categoryId, _id)), clearFlags: () => dispatch(clearFlags()), + massUpload: docs => dispatch(massUpload(docs)), hideDialog: () => dispatch(hideDialog()), showConfirmDialog: (cancel, confirm, message, label) => dispatch(showConfirmDialog(cancel, confirm, message, label)), }) export default connect(mapStateToProps, mapDispatchToProps)(FoodItems) + + + + +// +// +// + +//
+// +//
\ No newline at end of file diff --git a/client/modules/food/components/inventory/MassImportsModal.js b/client/modules/food/components/inventory/MassImportsModal.js new file mode 100644 index 00000000..0e28fa85 --- /dev/null +++ b/client/modules/food/components/inventory/MassImportsModal.js @@ -0,0 +1,135 @@ +import React from 'react' +import { Button } from 'react-bootstrap' +import { Box, BoxHeader, BoxBody } from '../../../../components/box' + +import CSVReader from 'react-csv-reader' + +export default class MassImportsModal extends React.Component { + + constructor(props) { + super(props) + this.state = { + validInput: false, + documents: [] + } + } + + handleInput = data => { + /* Error Checking for Empty File */ + if(data.length == 0 || data.length == 1 || data.length == 2 || data[0][0] == "") { + //console.log("1") + this.printErrorMessage("Empty") + } + /* Error Checking for Header Format */ + else if (!this.validateHeaders(data[0])) { + //console.log(data[0]) + this.printErrorMessage("Format") + } + else { + document.getElementById("error").innerHTML = "" + var info = [] + + for(var i = 1; i < data.length-1; i++) { + if(!this.validateRow(data[i])) { + //console.log("3") + this.printErrorMessage("Format") + } + else { + var category = data[i][0] + var foodName = data[i][1] + var quantity = Number(data[i][4]) + + info.push({foodName: foodName, quantity: quantity, category: category}) + } + } + this.setState({validInput: true, documents: info}) + } + } + + printErrorMessage = error => { + document.getElementById("error").style.color = "red" + if(error == "Empty") { + document.getElementById("error").innerHTML = "Error: Empty CSV Input File." + } + else if(error == "Format") { + document.getElementById("error").innerHTML = "Error: Invalid Input File Format. Please Use CSV Template." + } + this.setState({validInput: false}) + } + + /* Validates header length and values */ + validateHeaders = headers => { + if(headers.length != 8) { + return false + } + else { + var expected = ["Category", "Product", "Item Number", "Location", "Current Quantity", "Weight", "Desired Quantity", "Desired Weight"] + for(var i = 0; i < headers.length; i++) { + if(headers[i] != expected[i]) { + return false + } + } + } + return true + } + + /* Validates that a row has the correct informaiton and no blanks for required fields */ + validateRow = row => { + if(row.length != 8) { + return false + } + else { + if(row[0] == "" || row[1] == "" || row[4] == "") + for(var i = 0; i < row.length; i++) { + if(row[i] == "") { + return false + } + } + } + return true + } + + importData = () => { + var docs = this.state.documents + const foodItem = { + name: 'test', + quantity: 0 + } + this.props.massUpload({ + ...foodItem, + docs: docs + }) + this.props.closeMassImportModal() + } + + handleError = () => { + document.getElementById("error").innerHTML = "Weird error" + } + + render = () => { + return ( + + + Customer Mass Imports + + +
+ +
+ + +
+
+
+ ) + } +} diff --git a/client/modules/food/reducers/item.js b/client/modules/food/reducers/item.js index f1a7524b..4a13577b 100644 --- a/client/modules/food/reducers/item.js +++ b/client/modules/food/reducers/item.js @@ -29,6 +29,16 @@ export const saveFoodItem = (categoryId, foodItem) => ({ } }) +export const massUpload = docs => ({ + [CALL_API]: { + endpoint: 'foods/massUpload', + method: 'POST', + body: docs, + schema: foodItemSchema, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + export const deleteFoodItem = (categoryId, foodItemId) => ({ [CALL_API]: { endpoint: `foods/${categoryId}/items/${foodItemId}`, diff --git a/client/modules/volunteer/components/MassImportsModal.js b/client/modules/volunteer/components/MassImportsModal.js new file mode 100644 index 00000000..8de0e7a9 --- /dev/null +++ b/client/modules/volunteer/components/MassImportsModal.js @@ -0,0 +1,175 @@ +import React from 'react' +import { Button } from 'react-bootstrap' +import { Box, BoxHeader, BoxBody } from '../../../components/box' + +import CSVReader from 'react-csv-reader' + +export default class MassImportsModal extends React.Component { + + constructor(props) { + super(props) + this.state = { + validInput: false, + documents: [] + } + } + + handleInput = data => { + /* Error Checking for Empty File */ + if(data.length == 0 || data.length == 1 || data.length == 2 || data[0][0] == "") { + this.printErrorMessage("Empty") + } + /* Error Checking for Header Format */ + else if (!this.validateHeaders(data[0])) { + this.printErrorMessage("Format") + } + else { + document.getElementById("error").innerHTML = "" + var info = [] + + const volunteers = this.props.volunteers + + for(var i = 1; i < data.length-1; i++) { + if(!this.validateRow(data[i])) { + this.printErrorMessage("Format") + } + else { + var firstName = data[i][0] + var lastName = data[i][1] + var email = data[i][2] + var birthday = new Date(data[i][3]) + var street = data[i][4] + var city = data[i][5] + var state = data[i][6] + var zip = data[i][7] + + // Don't add this entry if it's a duplicate + if(this.isDuplicate(firstName, lastName, email, volunteers) == true) { + console.log("Duplicate!") + continue + } + + console.log("Not duplicate!") + + /* + ** The meta field was taken from the Questionnaire model. I'm not sure what it is, + ** but it's a required field. + */ + var fields = [] + fields.push({meta: "c523b236-cf40-47fc-94d4-1f5ae9ccd3c1", value: street}) + fields.push({meta: "d2cd8ff9-d70a-4316-a970-5c9a11dc0076", value: city}) + fields.push({meta: "d4b7113c-9943-4858-893c-1b2512533f46", value: state}) + fields.push({meta: "ce470f63-9c6f-401f-8c85-c9fef022be90", value: zip}) + fields.push({meta: "8a537855-657a-476f-98cc-3f8c6313532d", value: birthday}) + + info.push({firstName: firstName, lastName: lastName, email: email, fields: fields}) + } + } + this.setState({validInput: true, documents: info}) + } + } + + // Determines if customer is a duplicate + // I use email as well in the edge case that there are 2 people + // with the same name + isDuplicate = (firstName, lastName, email, volunteers) => { + for(var i = 0; i < volunteers.length; i++) { + if(volunteers[i].firstName.toLowerCase() == firstName.toLowerCase() && + volunteers[i].lastName.toLowerCase() == lastName.toLowerCase() && + volunteers[i].email.toLowerCase() == email.toLowerCase()) { + + return true + } + } + return false + } + + printErrorMessage = error => { + document.getElementById("error").style.color = "red" + if(error == "Empty") { + document.getElementById("error").innerHTML = "Error: Empty CSV Input File." + } + else if(error == "Format") { + document.getElementById("error").innerHTML = "Error: Invalid Input File Format. Please Use CSV Template." + } + this.setState({validInput: false}) + } + + /* Validates header length and values */ + validateHeaders = headers => { + if(headers.length != 8) { + return false + } + else { + var expected = ["First Name", "Last Name", "Email", "Birthday", "Street", "City/Town", "State/Province", "ZIP"] + for(var i = 0; i < headers.length; i++) { + if(headers[i] != expected[i]) { + return false + } + } + } + return true + } + + /* Validates that a row has the correct informaiton and no blanks for required fields */ + validateRow = row => { + if(row.length != 8) { + return false + } + else { + for(var i = 0; i < row.length; i++) { + if(row[i] == "") { + return false + } + } + } + return true + } + + + importData = () => { + var docs = this.state.documents + const volunteer = { + firstName: 'test', + lastName: 'test', + email: 'test@test.com' + } + this.props.massUpload({ + ...volunteer, + docs: docs + }) + this.props.closeModal() + } + + + handleError = () => { + document.getElementById("error").innerHTML = "Weird error" + } + + render = () => { + return ( + + + Customer Mass Imports + + +
+ +
+ + +
+
+
+ ) + } +} diff --git a/client/modules/volunteer/components/VolunteerList.js b/client/modules/volunteer/components/VolunteerList.js index 01b08985..9830b2a9 100644 --- a/client/modules/volunteer/components/VolunteerList.js +++ b/client/modules/volunteer/components/VolunteerList.js @@ -1,19 +1,22 @@ import React, {Component} from 'react' import {connect} from 'react-redux' import {Link} from 'react-router-dom' +import { Button, Modal } from 'react-bootstrap' import {BootstrapTable, TableHeaderColumn, SizePerPageDropDown} from 'react-bootstrap-table' import 'react-bootstrap-table/dist/react-bootstrap-table.min.css' import {fieldTypes} from '../../../../common/constants' import {fieldsByType} from '../../../lib/questionnaire-helpers' import selectors from '../../../store/selectors' -import {loadVolunteers} from '../reducer' +import {loadVolunteers, massUpload} from '../reducer' import {loadQuestionnaires} from '../../questionnaire/reducers/api' import {Box, BoxBody, BoxHeader} from '../../../components/box' import ClientStatusLabel from '../../../components/ClientStatusLabel' import {Page, PageBody} from '../../../components/page' +import MassImportsModal from './MassImportsModal' + const mapStateToProps = state => ({ volunteers: selectors.volunteer.getAll(state), loading: selectors.volunteer.loading(state) || @@ -24,10 +27,28 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ loadVolunteers: () => dispatch(loadVolunteers()), - loadQuestionnaires: () => dispatch(loadQuestionnaires()) + loadQuestionnaires: () => dispatch(loadQuestionnaires()), + massUpload: docs => dispatch(massUpload(docs)) }) class VolunteerList extends Component { + constructor(props) { + super(props) + this.state = { + showImportsModal: false + } + } + + openModal = () => { + this.setState({showImportsModal: true}) + } + + closeModal = () => { + location.reload() + this.setState({showImportsModal: false}) + } + + componentWillMount() { this.props.loadVolunteers() this.props.loadQuestionnaires() @@ -67,6 +88,16 @@ class VolunteerList extends Component { loading={loading} error={loadError} > + + + + + + ({ } }) +export const massUpload = docs => ({ + [CALL_API]: { + endpoint: 'volunteers/massUpload', + method: 'POST', + body: docs, + schema: volunteerSchema, + types: [actions.SAVE_REQUEST, actions.SAVE_SUCCESS, actions.SAVE_FAILURE] + } +}) + export const getAllVolunteers = () => ({ [CALL_API]: { endpoint: 'volunteers/getAllVolunteers', diff --git a/server/controllers/customer.js b/server/controllers/customer.js index f7232447..5cadd27a 100644 --- a/server/controllers/customer.js +++ b/server/controllers/customer.js @@ -59,17 +59,6 @@ export default { max = max + 1 } - - // let newCustomer = new Customer(docs[0]) - // newCustomer._id = req.user.id - // // res.status(200).json({message: "Successful mass import!"}) - // //let newDocs = docs.map(cust => new Customer(cust)) - // // var newDocs2 = newDocs.map(cust => ({ ...cust, _id: req.user.id})) - - // // for(var i = 0; i < newDocs.length; i++) { - // // newDocs[i]._id = req.user.id - // // } - await Customer.insertMany(newDocs, function(err) { if(err) { res.status(400).json({message: "Unable to mass import in Database"}) diff --git a/server/controllers/donor.js b/server/controllers/donor.js index 0fcf5a92..e42217b1 100644 --- a/server/controllers/donor.js +++ b/server/controllers/donor.js @@ -31,6 +31,40 @@ export default { res.json(req.donor) }, + + + async massUpload(req, res) { + const donor = req.body + var docs = donor.docs + var max = 0 + + await Donor.find({}, function(err, donors) { + max = donors.sort( (a, b) => a._id > b._id ? 1 : -1)[donors.length-1]._id + 1 + }) + + + let newDocs = docs.map(don => new Donor(don)) + for(var i = 0; i < newDocs.length; i++) { + newDocs[i]._id = max + max = max + 1 + } + + // res.status(200).json({message: "Successful mass import!"}) + + await Donor.insertMany(newDocs, function(err) { + if(err) { + res.status(400).json({message: "Unable to mass import in Database"}) + } else { + res.status(200).json({message: "Successful mass import!"}) + } + }) + }, + + + + + + /** * Update a donor */ diff --git a/server/controllers/food.js b/server/controllers/food.js index a6900250..d5101ab4 100644 --- a/server/controllers/food.js +++ b/server/controllers/food.js @@ -31,6 +31,65 @@ export default { res.json(savedFood) }, + async massUpload(req, res) { + const food = req.body + var docs = food.docs + + for(var i = 0; i < docs.length; i++) { + var doc = docs[i] + var category = doc.category + var foodName = doc.foodName + var quantity = doc.quantity + + /* Checks if category already exists */ + const existingFoodCategory = await Food.find({'category': category, 'deleted': false}).lean() + + /* Checks if item already exists, if so, add current quantity with new quantity */ + if (existingFoodCategory.length) { + //console.log("Category Exists") + + //Check to see if an item with the same name already exists in a category + let categoryWithExistingItem = await Food.findOne( + { items: {$elemMatch:{name: {$regex: `^${foodName}$`, $options: "i"}, deleted: false }} }, + { 'items.$': 1 }).lean() + + if(categoryWithExistingItem) { + //console.log("Food exists in category...") + const existingFoodItem = categoryWithExistingItem.items[0] + var newQuantity = existingFoodItem.quantity + Number(quantity) + existingFoodItem.quantity = newQuantity + + //const updatedCategory = await updateItemHelper(categoryWithExistingItem._id, existingFoodItem) + await updateItemHelper(categoryWithExistingItem._id, existingFoodItem) + } + else { + //console.log("Food does not exists in category...") + var info = { + name: foodName.trim(), + categoryId: category, + quantity: quantity + } + //const savedFood = await Food.findByIdAndUpdate(existingFoodCategory[0]._id, { $addToSet: { items: info } }, { new: true }) + await Food.findByIdAndUpdate(existingFoodCategory[0]._id, { $addToSet: { items: info } }, { new: true }) + } + } + else { + //console.log("Category does not exist.") + const newCategory = new Food({category: category}) + const savedCategory = await newCategory.save() + var info1 = { + name: foodName.trim(), + categoryId: category, + quantity: quantity + } + //const savedFood = await Food.findByIdAndUpdate(savedCategory._id, { $addToSet: { items: info1 } }, { new: true }) + await Food.findByIdAndUpdate(savedCategory._id, { $addToSet: { items: info1 } }, { new: true }) + } + } + res.json(food) + }, + + /** * Update a Food category */ diff --git a/server/controllers/volunteer.js b/server/controllers/volunteer.js index ec2848e7..7b39da52 100644 --- a/server/controllers/volunteer.js +++ b/server/controllers/volunteer.js @@ -49,6 +49,32 @@ export default { res.json(updatedVolunteer) }, + + async massUpload(req, res) { + const volunteer = req.body + var docs = volunteer.docs + var max = 0 + + await Volunteer.find({}, function(err, volunteers) { + max = volunteers.sort( (a, b) => a._id > b._id ? 1 : -1)[volunteers.length-1]._id + 1 + }) + + + let newDocs = docs.map(vol => new Volunteer(vol)) + for(var i = 0; i < newDocs.length; i++) { + newDocs[i]._id = max + max = max + 1 + } + + await Volunteer.insertMany(newDocs, function(err) { + if(err) { + res.status(400).json({message: "Unable to mass import in Database"}) + } else { + res.status(200).json({message: "Successful mass import!"}) + } + }) + }, + /* * Deletes a volunteer shift */ diff --git a/server/routes/customer.js b/server/routes/customer.js index 55e80e54..3bfbf770 100644 --- a/server/routes/customer.js +++ b/server/routes/customer.js @@ -18,21 +18,9 @@ export default () => { customerRouter.route('/customer') .post(requiresLogin, customerController.create) - - - customerRouter.route('/customer/massUpload') .post(requiresLogin, customerController.massUpload) - // customerRouter.route('/customer/duplicate') - // .put(requiresLogin, customerController.duplicate) - - - - - - - customerRouter.route('/customer/:customerId') .get(requiresLogin, hasAuthorization, customerController.read) .put(requiresLogin, hasAuthorization, canChangeStatus, customerController.update) diff --git a/server/routes/donor.js b/server/routes/donor.js index b8944ff2..514f2e89 100644 --- a/server/routes/donor.js +++ b/server/routes/donor.js @@ -16,6 +16,14 @@ export default () => { .get(requiresLogin, hasAuthorization, donorController.read) .put(requiresLogin, hasAuthorization, donorController.update) + + + donorRouter.route('/donor/massUpload') + .post(requiresLogin, donorController.massUpload) + + + + // Donor routes for admin donorRouter.route('/admin/donors') .get(donorController.list) diff --git a/server/routes/food.js b/server/routes/food.js index 61f4df41..0245d6ad 100644 --- a/server/routes/food.js +++ b/server/routes/food.js @@ -24,6 +24,13 @@ export default () => { foodRouter.route('/foods') .get(foodController.list) .post(websocketMiddleware(saveCatSchema), foodController.create) + + + foodRouter.route('/foods/massUpload') + .post(requiresLogin, foodController.massUpload) + + + foodRouter.route('/foods/:foodId') .put(websocketMiddleware(saveCatSchema), foodController.update) .delete(websocketMiddleware(deleteCatSchema), foodController.delete) diff --git a/server/routes/volunteer.js b/server/routes/volunteer.js index c70eb4c3..d66c1794 100644 --- a/server/routes/volunteer.js +++ b/server/routes/volunteer.js @@ -20,6 +20,9 @@ export default () => { volunteerRouter.route('/volunteers/updateShift') .put(volunteerController.updateShift) + volunteerRouter.route('/volunteers/massUpload') + .post(requiresLogin, volunteerController.massUpload) + // Volunteer routes for admin for volunteer scheduling volunteerRouter.route('/volunteers/addShift') .put(volunteerController.addShift) From 82cd4fec12e012247575e41ae8afc1e3e8a15f82 Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Sat, 20 Apr 2019 16:06:02 -0700 Subject: [PATCH 8/9] Performed Black-Box tests on client/inventory mass uploads... Changed a couple things --- .../customer/components/CustomerList.js | 1 - .../customer/components/MassImportsModal.js | 30 +- .../components/inventory/MassImportsModal.js | 12 +- .../volunteer/components/MassImportsModal.js | 7 +- server/controllers/customer.js | 7 +- server1/config/env/all.js | 19 - server1/config/env/development.js | 4 - server1/config/env/production.js | 6 - server1/config/env/secrets-template.js | 10 - server1/config/env/secrets.js | 10 - server1/config/env/test.js | 6 - server1/config/express.js | 121 ---- server1/config/index.js | 16 - server1/config/mailer.js | 54 -- server1/config/passport.js | 36 - server1/config/strategies/google.js | 57 -- server1/config/strategies/local.js | 22 - server1/controllers/customer.js | 152 ----- server1/controllers/delivery.js | 73 -- server1/controllers/donation.js | 69 -- server1/controllers/donor.js | 89 --- server1/controllers/food.js | 196 ------ server1/controllers/media.js | 27 - server1/controllers/packing.js | 219 ------ server1/controllers/page.js | 111 --- server1/controllers/questionnaire.js | 45 -- server1/controllers/settings.js | 55 -- server1/controllers/users.js | 13 - server1/controllers/users/authentication.js | 74 -- server1/controllers/users/authorization.js | 40 -- server1/controllers/users/password.js | 88 --- server1/controllers/users/profile.js | 134 ---- server1/controllers/volunteer.js | 194 ------ server1/entry.test.js | 30 - server1/index.js | 2 - server1/lib/enforce-ssl-middleware.js | 12 - server1/lib/errors.js | 92 --- server1/lib/geolocate.js | 39 -- server1/lib/mail/email-template.html | 87 --- server1/lib/mail/html-transform.js | 116 ---- server1/lib/mail/html-transform.spec.js | 86 --- server1/lib/mail/mail-generator.js | 146 ---- server1/lib/mail/mail-generator.spec.js | 101 --- server1/lib/mail/mail-helpers.js | 123 ---- server1/lib/mapquest-client.js | 34 - server1/lib/media-helpers.js | 34 - server1/lib/media-helpers.spec.js | 54 -- server1/lib/notification-sender.js | 46 -- server1/lib/notification-sender.spec.js | 155 ----- server1/lib/questionnaire-helpers.js | 48 -- server1/lib/questionnaire-helpers.spec.js | 98 --- server1/lib/seed/address-generator.js | 34 - server1/lib/seed/address-generator.spec.js | 46 -- server1/lib/seed/addresses.csv | 64 -- server1/lib/seed/index.js | 164 ----- server1/lib/seed/seed-clients.js | 234 ------- server1/lib/seed/seed-data.js | 279 -------- server1/lib/update-linked-fields.js | 38 -- server1/lib/websocket-middleware.js | 51 -- server1/models/customer.js | 131 ---- server1/models/donation.js | 48 -- server1/models/donor.js | 59 -- server1/models/food.js | 44 -- server1/models/index.js | 14 - server1/models/location-schema.js | 16 - server1/models/media.js | 29 - server1/models/notification.js | 25 - server1/models/package.js | 41 -- server1/models/page.js | 29 - server1/models/questionnaire.js | 65 -- server1/models/settings.js | 73 -- server1/models/user.js | 121 ---- server1/models/volunteer.js | 120 ---- server1/routes/api.js | 58 -- server1/routes/api.spec.js | 50 -- server1/routes/customer.js | 35 - server1/routes/delivery.js | 26 - server1/routes/donation.js | 18 - server1/routes/donor.js | 31 - server1/routes/food.js | 41 -- server1/routes/media.js | 20 - server1/routes/packing.js | 27 - server1/routes/page.js | 20 - server1/routes/questionnaire.js | 25 - server1/routes/settings.js | 16 - server1/routes/users.js | 76 --- server1/routes/volunteer.js | 46 -- server1/server.js | 33 - server1/tests/helpers.js | 69 -- server1/tests/integration/customer.spec.js | 430 ------------ server1/tests/integration/donation.spec.js | 149 ---- server1/tests/integration/donor.spec.js | 127 ---- server1/tests/integration/food.spec.js | 297 -------- server1/tests/integration/packing.spec.js | 643 ------------------ .../tests/integration/questionnaire.spec.js | 123 ---- server1/tests/integration/user.spec.js | 636 ----------------- server1/tests/integration/volunteer.spec.js | 169 ----- 97 files changed, 40 insertions(+), 8150 deletions(-) delete mode 100644 server1/config/env/all.js delete mode 100644 server1/config/env/development.js delete mode 100644 server1/config/env/production.js delete mode 100644 server1/config/env/secrets-template.js delete mode 100644 server1/config/env/secrets.js delete mode 100644 server1/config/env/test.js delete mode 100644 server1/config/express.js delete mode 100644 server1/config/index.js delete mode 100644 server1/config/mailer.js delete mode 100644 server1/config/passport.js delete mode 100644 server1/config/strategies/google.js delete mode 100644 server1/config/strategies/local.js delete mode 100644 server1/controllers/customer.js delete mode 100644 server1/controllers/delivery.js delete mode 100644 server1/controllers/donation.js delete mode 100644 server1/controllers/donor.js delete mode 100644 server1/controllers/food.js delete mode 100644 server1/controllers/media.js delete mode 100644 server1/controllers/packing.js delete mode 100644 server1/controllers/page.js delete mode 100644 server1/controllers/questionnaire.js delete mode 100644 server1/controllers/settings.js delete mode 100644 server1/controllers/users.js delete mode 100644 server1/controllers/users/authentication.js delete mode 100644 server1/controllers/users/authorization.js delete mode 100644 server1/controllers/users/password.js delete mode 100644 server1/controllers/users/profile.js delete mode 100644 server1/controllers/volunteer.js delete mode 100644 server1/entry.test.js delete mode 100644 server1/index.js delete mode 100644 server1/lib/enforce-ssl-middleware.js delete mode 100644 server1/lib/errors.js delete mode 100644 server1/lib/geolocate.js delete mode 100644 server1/lib/mail/email-template.html delete mode 100644 server1/lib/mail/html-transform.js delete mode 100644 server1/lib/mail/html-transform.spec.js delete mode 100644 server1/lib/mail/mail-generator.js delete mode 100644 server1/lib/mail/mail-generator.spec.js delete mode 100644 server1/lib/mail/mail-helpers.js delete mode 100644 server1/lib/mapquest-client.js delete mode 100644 server1/lib/media-helpers.js delete mode 100644 server1/lib/media-helpers.spec.js delete mode 100644 server1/lib/notification-sender.js delete mode 100644 server1/lib/notification-sender.spec.js delete mode 100644 server1/lib/questionnaire-helpers.js delete mode 100644 server1/lib/questionnaire-helpers.spec.js delete mode 100644 server1/lib/seed/address-generator.js delete mode 100644 server1/lib/seed/address-generator.spec.js delete mode 100644 server1/lib/seed/addresses.csv delete mode 100644 server1/lib/seed/index.js delete mode 100644 server1/lib/seed/seed-clients.js delete mode 100644 server1/lib/seed/seed-data.js delete mode 100644 server1/lib/update-linked-fields.js delete mode 100644 server1/lib/websocket-middleware.js delete mode 100644 server1/models/customer.js delete mode 100644 server1/models/donation.js delete mode 100644 server1/models/donor.js delete mode 100644 server1/models/food.js delete mode 100644 server1/models/index.js delete mode 100644 server1/models/location-schema.js delete mode 100644 server1/models/media.js delete mode 100644 server1/models/notification.js delete mode 100644 server1/models/package.js delete mode 100644 server1/models/page.js delete mode 100644 server1/models/questionnaire.js delete mode 100644 server1/models/settings.js delete mode 100644 server1/models/user.js delete mode 100644 server1/models/volunteer.js delete mode 100644 server1/routes/api.js delete mode 100644 server1/routes/api.spec.js delete mode 100644 server1/routes/customer.js delete mode 100644 server1/routes/delivery.js delete mode 100644 server1/routes/donation.js delete mode 100644 server1/routes/donor.js delete mode 100644 server1/routes/food.js delete mode 100644 server1/routes/media.js delete mode 100644 server1/routes/packing.js delete mode 100644 server1/routes/page.js delete mode 100644 server1/routes/questionnaire.js delete mode 100644 server1/routes/settings.js delete mode 100644 server1/routes/users.js delete mode 100644 server1/routes/volunteer.js delete mode 100644 server1/server.js delete mode 100644 server1/tests/helpers.js delete mode 100644 server1/tests/integration/customer.spec.js delete mode 100644 server1/tests/integration/donation.spec.js delete mode 100644 server1/tests/integration/donor.spec.js delete mode 100644 server1/tests/integration/food.spec.js delete mode 100644 server1/tests/integration/packing.spec.js delete mode 100644 server1/tests/integration/questionnaire.spec.js delete mode 100644 server1/tests/integration/user.spec.js delete mode 100644 server1/tests/integration/volunteer.spec.js diff --git a/client/modules/customer/components/CustomerList.js b/client/modules/customer/components/CustomerList.js index 79edc9e9..358e053c 100644 --- a/client/modules/customer/components/CustomerList.js +++ b/client/modules/customer/components/CustomerList.js @@ -49,7 +49,6 @@ class CustomerList extends Component { } closeModal = () => { - location.reload() this.setState({showImportsModal: false}) } diff --git a/client/modules/customer/components/MassImportsModal.js b/client/modules/customer/components/MassImportsModal.js index 3984f51b..83bfe28a 100644 --- a/client/modules/customer/components/MassImportsModal.js +++ b/client/modules/customer/components/MassImportsModal.js @@ -31,25 +31,29 @@ export default class MassImportsModal extends React.Component { for(var i = 1; i < data.length-1; i++) { if(!this.validateRow(data[i])) { - this.printErrorMessage("Format") + this.printErrorMessage("Row") } else { var firstName = data[i][0] var lastName = data[i][1] var email = data[i][2] var birthday = new Date(data[i][3]) + + if(isNaN(birthday.getTime())) { + continue + } + + var street = data[i][4] var city = data[i][5] var state = data[i][6] var zip = data[i][7] // Don't add this entry if it's a duplicate - if(this.isDuplicate(firstName, lastName, email, customers) == true) { - console.log("Duplicate!") - continue + if(this.isDuplicate(firstName, lastName, email, customers) == true || this.isDuplicate(firstName, lastName, email, info) == true) { + continue } - console.log("Not duplicate!") /* ** The meta field was taken from the Questionnaire model. I'm not sure what it is, @@ -65,7 +69,12 @@ export default class MassImportsModal extends React.Component { info.push({firstName: firstName, lastName: lastName, email: email, fields: fields}) } } - this.setState({validInput: true, documents: info}) + if(info.length > 262) { + this.printErrorMessage("Size") + } + else { + this.setState({validInput: true, documents: info}) + } } } @@ -78,7 +87,7 @@ export default class MassImportsModal extends React.Component { customers[i].lastName.toLowerCase() == lastName.toLowerCase() && customers[i].email.toLowerCase() == email.toLowerCase()) { - return true + return true } } return false @@ -92,6 +101,12 @@ export default class MassImportsModal extends React.Component { else if(error == "Format") { document.getElementById("error").innerHTML = "Error: Invalid Input File Format. Please Use CSV Template." } + else if(error == "Size") { + document.getElementById("error").innerHTML = "Error: Too Many Entries. 262 is Maximum Number of Entries for One Upload." + } + else if(error == "Row") { + document.getElementById("error").innerHTML = "Warning: File Contains Some Invalid Rows. Rows will be Ignored." + } this.setState({validInput: false}) } @@ -139,6 +154,7 @@ export default class MassImportsModal extends React.Component { docs: docs }) this.props.closeModal() + location.reload() } diff --git a/client/modules/food/components/inventory/MassImportsModal.js b/client/modules/food/components/inventory/MassImportsModal.js index 0e28fa85..36e6bc86 100644 --- a/client/modules/food/components/inventory/MassImportsModal.js +++ b/client/modules/food/components/inventory/MassImportsModal.js @@ -17,12 +17,10 @@ export default class MassImportsModal extends React.Component { handleInput = data => { /* Error Checking for Empty File */ if(data.length == 0 || data.length == 1 || data.length == 2 || data[0][0] == "") { - //console.log("1") this.printErrorMessage("Empty") } /* Error Checking for Header Format */ else if (!this.validateHeaders(data[0])) { - //console.log(data[0]) this.printErrorMessage("Format") } else { @@ -31,8 +29,7 @@ export default class MassImportsModal extends React.Component { for(var i = 1; i < data.length-1; i++) { if(!this.validateRow(data[i])) { - //console.log("3") - this.printErrorMessage("Format") + this.printErrorMessage("Row") } else { var category = data[i][0] @@ -54,6 +51,9 @@ export default class MassImportsModal extends React.Component { else if(error == "Format") { document.getElementById("error").innerHTML = "Error: Invalid Input File Format. Please Use CSV Template." } + else if(error == "Row") { + document.getElementById("error").innerHTML = "Warning: File Contains Some Invalid Rows. Rows will be Ignored." + } this.setState({validInput: false}) } @@ -86,6 +86,9 @@ export default class MassImportsModal extends React.Component { } } } + if(row[4] < 0) { + return false + } return true } @@ -100,6 +103,7 @@ export default class MassImportsModal extends React.Component { docs: docs }) this.props.closeMassImportModal() + location.reload() } handleError = () => { diff --git a/client/modules/volunteer/components/MassImportsModal.js b/client/modules/volunteer/components/MassImportsModal.js index 8de0e7a9..2f8f7c48 100644 --- a/client/modules/volunteer/components/MassImportsModal.js +++ b/client/modules/volunteer/components/MassImportsModal.js @@ -45,11 +45,10 @@ export default class MassImportsModal extends React.Component { // Don't add this entry if it's a duplicate if(this.isDuplicate(firstName, lastName, email, volunteers) == true) { - console.log("Duplicate!") - continue + continue } - console.log("Not duplicate!") + //console.log("Not duplicate!") /* ** The meta field was taken from the Questionnaire model. I'm not sure what it is, @@ -78,7 +77,7 @@ export default class MassImportsModal extends React.Component { volunteers[i].lastName.toLowerCase() == lastName.toLowerCase() && volunteers[i].email.toLowerCase() == email.toLowerCase()) { - return true + return true } } return false diff --git a/server/controllers/customer.js b/server/controllers/customer.js index 5cadd27a..41319e9a 100644 --- a/server/controllers/customer.js +++ b/server/controllers/customer.js @@ -49,7 +49,12 @@ export default { var max = 0 await Customer.find({}, function(err, customers) { - max = customers.sort( (a, b) => a._id > b._id ? 1 : -1)[customers.length-1]._id + 1 + if(customers.length == 0) { + max = 1 + } + else { + max = customers.sort( (a, b) => a._id > b._id ? 1 : -1)[customers.length-1]._id + 1 + } }) diff --git a/server1/config/env/all.js b/server1/config/env/all.js deleted file mode 100644 index 1eb74c90..00000000 --- a/server1/config/env/all.js +++ /dev/null @@ -1,19 +0,0 @@ -const production = process.env.NODE_ENV === 'production' - -const protocol = production ? 'https' : 'http' -const host = process.env.HOST_NAME || 'localhost' -const port = process.env.PORT || 3000 - -const url = `${protocol}://${host}` + production ? '' : `:${port}` - -export default { - protocol: process.env.PROTOCOL || protocol, - host, - port, - sessionCollection: 'sessions', - sessionIdleTimeout: 3600000, - mailFrom: `no-reply@${host}`, - oauth: { - googleCallbackURL: `${url}/api/auth/google/callback` - } -} diff --git a/server1/config/env/development.js b/server1/config/env/development.js deleted file mode 100644 index 48cc6620..00000000 --- a/server1/config/env/development.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - db: process.env.MONGODB_URI || 'mongodb://localhost/fb-dev', - sessionSecret: 'foodbank-app' -} diff --git a/server1/config/env/production.js b/server1/config/env/production.js deleted file mode 100644 index 2eb803ae..00000000 --- a/server1/config/env/production.js +++ /dev/null @@ -1,6 +0,0 @@ -if (!process.env.SECRET) throw new Error('environment variable SECRET must be set') - -export default { - db: process.env.MONGODB_URI || 'mongodb://localhost:27017/fb-prod', - sessionSecret: process.env.SECRET -} diff --git a/server1/config/env/secrets-template.js b/server1/config/env/secrets-template.js deleted file mode 100644 index 4424c372..00000000 --- a/server1/config/env/secrets-template.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - gmapsApiKey: process.env.GMAPS_API_KEY || '', - oauth: { - googleClientID: process.env.GOOGLE_CLIENT_ID || '', - googleClientSecret: process.env.GOOGLE_CLIENT_SECRET || '', - }, - sendgrid: { - API_KEY: process.env.SENDGRID_API_KEY || '' - } -} diff --git a/server1/config/env/secrets.js b/server1/config/env/secrets.js deleted file mode 100644 index 4424c372..00000000 --- a/server1/config/env/secrets.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - gmapsApiKey: process.env.GMAPS_API_KEY || '', - oauth: { - googleClientID: process.env.GOOGLE_CLIENT_ID || '', - googleClientSecret: process.env.GOOGLE_CLIENT_SECRET || '', - }, - sendgrid: { - API_KEY: process.env.SENDGRID_API_KEY || '' - } -} diff --git a/server1/config/env/test.js b/server1/config/env/test.js deleted file mode 100644 index 342e00ca..00000000 --- a/server1/config/env/test.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - db: process.env.MONGODB_URI || 'mongodb://localhost:27017/fb-test', - port: 3001, - sessionSecret: 'foodbank-app' -} - diff --git a/server1/config/express.js b/server1/config/express.js deleted file mode 100644 index fb5ab2b6..00000000 --- a/server1/config/express.js +++ /dev/null @@ -1,121 +0,0 @@ -import bodyParser from 'body-parser' -import compress from 'compression' -import cookieParser from 'cookie-parser' -import express from 'express' -import helmet from 'helmet' -import morgan from 'morgan' -import mongoose from 'mongoose' -import connectMongo from 'connect-mongo' -import passport from 'passport' -import path from 'path' -import session from 'express-session' -import {get} from 'lodash' - -import {addUser} from '../lib/websocket-middleware' -import apiRoutes from '../routes/api' -import config from './index' -import enforceSSLMiddleware from '../lib/enforce-ssl-middleware' -import getErrorMessage, {HttpError} from '../lib/errors' -import seed from '../lib/seed' -import '../models' - -// set api delay and failure probablility for testing -const API_DELAY = 0 -const API_FAILURE_RATE = 0 - -const mongoStore = connectMongo({session}) - -export default function(io) { - const app = express() - - const sharedSession = session({ - saveUninitialized: false, - cookie: { maxAge: config.sessionIdleTimeout }, - resave: true, - rolling: true, - secret: config.sessionSecret, - store: new mongoStore({ - mongooseConnection: mongoose.connection, - collection: config.sessionCollection - }) - }) - - app.set('sharedSession', sharedSession) - - // call with true or delete db to seed - seed(process.env.NODE_ENV, false) - - // force https - if (process.env.NODE_ENV === 'production') { - app.use(enforceSSLMiddleware) - } - - app.use(compress({ - filter: function(req, res) { - return (/json|text|javascript|css/).test(res.getHeader('Content-Type')) - }, - level: 9 - })) - - if (process.env.NODE_ENV === 'development') { - app.use(morgan('dev')) - } - - app.use(bodyParser.urlencoded({ - extended: true - })) - app.use('/api/admin/pages', bodyParser.json({limit: '5mb'})) - app.use(bodyParser.json({})) - - // CookieParser should be above session - app.use(cookieParser()) - - // Express MongoDB session storage - app.use(sharedSession) - - // use passport session - app.use(passport.initialize()) - app.use(passport.session()) - - // Use helmet to secure Express headers - app.use(helmet()) - app.disable('x-powered-by') - - app.use('/api', apiRoutes(API_DELAY, API_FAILURE_RATE)) - - // Setting the static folder - if (process.env.NODE_ENV === 'production') - app.use(express.static(path.resolve('./dist/client'))) - - // Error handler - app.use(function(err, req, res, next) { - if (!err) return next() - - // Dont log client errors or during testing - if (process.env.NODE_ENV !== 'test' && !(err instanceof HttpError)) { - console.error(err) - } - - const error = getErrorMessage(err) - res.status(error.status).json({ - message: error.message, - ...(error.paths ? {paths: err.paths} : {}) - }) - }) - - if (process.env.NODE_ENV === 'production') { - app.use(function(req, res) { - res.sendFile(path.resolve('./dist/client/index.html')) - }) - } - - if (io) { - io.on('connection', socket => { - const user = get(socket.handshake.session, 'passport.user') - if (user) addUser(user, socket) - }) - } - - // Return Express server instance - return app -} diff --git a/server1/config/index.js b/server1/config/index.js deleted file mode 100644 index 9ba79316..00000000 --- a/server1/config/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import {merge} from 'lodash' - -const env = process.env.NODE_ENV || 'development' - -let secrets -try { - secrets = require('./env/secrets').default -} catch (err) { - secrets = require('./env/secrets-template').default -} // eslint-disable-line no-empty - -export default merge( - require('./env/all').default, - require(`./env/${env}`).default, - secrets -) diff --git a/server1/config/mailer.js b/server1/config/mailer.js deleted file mode 100644 index c7a1504c..00000000 --- a/server1/config/mailer.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * This module adds functionality for the app to use email - */ - -import {readFileSync} from 'fs' -import mime from 'mime-types' -import sendgrid, {mail as helper} from 'sendgrid' - -import config from './index.js' -import Media from '../models/media' - -const sg = sendgrid(config.sendgrid.API_KEY) -const path = process.env.NODE_ENV === 'production' ? - 'dist/client/' : - 'assets/' - -export default async function sendEmail( - toEmail, - toName, - {html, text, subject, attachments} -) { - if (!config.sendgrid.API_KEY || toEmail.endsWith('@example.com')) return - - const from = new helper.Email(config.mailFrom) - const to = new helper.Email(toEmail, toName) - const content = new helper.Content('text/plain', text) - const mail = new helper.Mail(from, subject, to, content) - mail.addContent(new helper.Content('text/html', html)) - - if (attachments.length) { - const media = await Media.findOne().lean() - - attachments.forEach(attachmentId => { - const file = `${path}${media.path}${media[attachmentId]}` - const content = readFileSync(file).toString('base64') - - const attachment = new helper.Attachment() - attachment.setContent(content) - attachment.setType(mime.lookup(file)) - attachment.setFilename(file) - attachment.setDisposition('inline') - attachment.setContentId(attachmentId) - mail.addAttachment(attachment) - }) - } - - const request = sg.emptyRequest({ - method: 'POST', - path: '/v3/mail/send', - body: mail.toJSON() - }) - - return sg.API(request) -} diff --git a/server1/config/passport.js b/server1/config/passport.js deleted file mode 100644 index 45b64795..00000000 --- a/server1/config/passport.js +++ /dev/null @@ -1,36 +0,0 @@ -import passport from 'passport' -import localStrategy from './strategies/local' -import googleStrategy from './strategies/google' -import config from './index.js' -import User from '../models/user' - -export default function() { - // Serialize sessions - passport.serializeUser(function(user, done) { - done(null, {_id: user._id, roles: user.roles}) - }) - - // Deserialize sessions - passport.deserializeUser(async function({_id}, done) { - try { - const user = await User.findById(_id) - done(null, user) - } catch (err) { - done(err) - } - - }) - - // Initialize strategies - localStrategy() - - if (config.oauth.googleClientID && config.oauth.googleClientSecret) { - googleStrategy() - } else if (process.env.NODE_ENV !== 'test') { - console.warn() - console.warn('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') - console.warn('!!! Google oauth API keys not set. Google login is disabled !!!') - console.warn('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') - console.warn() - } -} diff --git a/server1/config/strategies/google.js b/server1/config/strategies/google.js deleted file mode 100644 index 86597245..00000000 --- a/server1/config/strategies/google.js +++ /dev/null @@ -1,57 +0,0 @@ -import passport from 'passport' -import {Strategy as GoogleStrategy} from 'passport-google-oauth2' - -import User from '../../models/user' -import config from '../index' - -export default function() { - passport.use(new GoogleStrategy( - { - clientID: config.oauth.googleClientID, - clientSecret: config.oauth.googleClientSecret, - callbackURL: config.oauth.googleCallbackURL, - passReqToCallback: true - }, - async function(req, accessToken, refreshToken, profile, cb) { - const query = JSON.parse(req.query.state) - const action = query.action - - try { - const user = await User.findOne({'google.id': profile.id}) - if (action === 'login') { - cb(null, user, profile) - } else if (action === 'signup') { - signup(user, profile, cb) - } else { - throw new Error('An action of type "login" or "signup" must be specified') - } - } catch (err) { - cb(err) - } - } - )) -} - -const signup = (user, profile, cb) => { - if (user) { - throw new Error("An account already exists with that google ID") - } else { - User.create( - { - firstName: profile.name.givenName, - lastName: profile.name.familyName, - email: profile.email, - provider: 'google', - roles: [], - google: {id: profile.id} - }, - (err, newUser) => { - if (err) { - throw err - } else { - cb(null, newUser) - } - } - ) - } -} diff --git a/server1/config/strategies/local.js b/server1/config/strategies/local.js deleted file mode 100644 index 35b1127a..00000000 --- a/server1/config/strategies/local.js +++ /dev/null @@ -1,22 +0,0 @@ -import passport from 'passport' -import {Strategy as LocalStrategy} from 'passport-local' -import User from '../../models/user' - -export default function() { - passport.use(new LocalStrategy({ - usernameField: 'email', - passwordField: 'password' - }, async function(email, password, done) { - try { - const user = await User.findOne({email}).select('+salt +password') - if (!user || !user.authenticate(password)) { - return done(null, false, { - message: 'Unknown user or invalid password' - }) - } - return done(null, user) - } catch (err) { - done(err) - } - })) -} diff --git a/server1/controllers/customer.js b/server1/controllers/customer.js deleted file mode 100644 index aca2a12f..00000000 --- a/server1/controllers/customer.js +++ /dev/null @@ -1,152 +0,0 @@ -import {extend, intersection, includes, omit} from 'lodash' - -import {ForbiddenError, NotFoundError} from '../lib/errors' -import {ADMIN_ROLE, clientRoles, volunteerRoles, customerStatus} from '../../common/constants' -import Customer from '../models/customer' -import Volunteer from '../models/volunteer' -import Package from '../models/package' -import mailer from '../lib/mail/mail-helpers' -import User from '../models/user' - -import {updateFields} from '../lib/update-linked-fields' -import {searchUserAndSetNotification, searchVolunteerAndSetNotification} from '../lib/notification-sender' - -export default { - /** - * Create a customer - */ - async create(req, res) { - let customer = new Customer(omit(req.body, ['status'])) - customer._id = req.user.id - - await User.findOneAndUpdate( - {_id: customer._id}, - {$push: {roles: clientRoles.CUSTOMER}} - ) - - // Sent Notification - searchUserAndSetNotification('roles/admin', {message:`Customer ${customer.fullName} was created!`, url: `/customers/${customer._id}`}, req.user._id) - - const savedCustomer = await customer.save() - updateFields(clientRoles.CUSTOMER, req.body.fields, customer._id) - res.json(savedCustomer) - - // mailer.send(config.mailer.to, 'A new client has applied.', 'create-customer-email') - }, - - /** - * Show the current customer - */ - read(req, res) { - res.json(req.customer) - }, - - /** - * Update a customer - */ - async update(req, res) { - const customer = extend(req.customer, req.body) - - const oldCustomer = await Customer.findById(customer._id) - const newCustomer = await customer.save() - - // Check if status is updated by admin - if (oldCustomer.status === 'Pending' && (newCustomer.status === 'Accepted' || newCustomer.status === 'Rejected')) { - mailer.sendStatus(newCustomer) - // Check if status was updated by someone else than the user - } else if(!(newCustomer.status === 'Away' || newCustomer.status === 'Available' )){ - mailer.sendUpdate(newCustomer) - } - - // Sent Notification - searchUserAndSetNotification('roles/admin', {message:`Customer ${customer.fullName} was updated!`, url: `/customers/${customer._id}`}, req.user._id) - searchVolunteerAndSetNotification({message:`Customer ${customer.fullName} was updated!`, url: `/customers/${customer._id}`}, customer._id) - - updateFields(clientRoles.CUSTOMER, req.body.fields, customer._id) - res.json(newCustomer) - }, - - /** - * List customers - */ - async list(req, res) { - const customers = await Customer.find() - .sort('-dateReceived') - .populate('user', 'displayName') - .populate('assignedTo', 'firstName lastName') - res.json(customers) - }, - - /** - * Delete customer - */ - async delete(req, res) { - const id = req.customer._id - - // Check to see if the customer is associated with any packages - const packageCount = await Package.find({customer: id}).count() - - if (packageCount > 0) { - // Don't delete the customer since a package references its data - res.status(409).json({message: "This customer has packages and can't be deleted"}) - } else { - // Remove the customer if it occurs in any Volunteers.customers - await Volunteer.update({}, { $pull: { "customers": id } }, {multi: true}) - - await Customer.findByIdAndRemove(id) - res.json(req.customer) - } - }, - /** - * Customer middleware - */ - async customerById(req, res, next, id) { - const customer = await Customer.findById(id) - - if (!customer) throw new NotFoundError - - req.customer = customer - next() - }, - - /** - * Customer authorization middleware - */ - hasAuthorization(req, res, next) { - const authorizedRoles = [ - ADMIN_ROLE, - volunteerRoles.DRIVER, - volunteerRoles.PACKING - ] - - if (req.user && intersection(req.user.roles, authorizedRoles).length) { - return next() - } - - if (!req.customer || req.customer._id !== +req.user.id) - throw new ForbiddenError - - next() - }, - - /** - * Customer Middleware - */ - async canChangeStatus(req, res, next) { - const unrestrictedRoles = [ - ADMIN_ROLE, - ] - const restrictedStatus = [ - customerStatus.REJECTED, - customerStatus.PENDING, - ] - - if (req.user && !intersection(req.user.roles, unrestrictedRoles).length) { - const statusChanged = req.customer.status !== req.body.status - if (statusChanged && includes(restrictedStatus, req.customer.status)) - throw new ForbiddenError - } - - next() - } -} diff --git a/server1/controllers/delivery.js b/server1/controllers/delivery.js deleted file mode 100644 index 146049a7..00000000 --- a/server1/controllers/delivery.js +++ /dev/null @@ -1,73 +0,0 @@ -import {difference, intersection, values} from 'lodash' - -import {ForbiddenError} from '../lib/errors' -import {getDirections} from '../lib/mapquest-client' -import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' -import Customer from '../models/customer' -import Volunteer from '../models/volunteer' - - -export default { - async directions(req, res) { - const directions = await getDirections(req.query.waypoints, { - optimize: req.query.optimize - }) - - res.json(directions) - }, - async assign(req, res) { - const customerIds = req.body.customerIds.map(id => Number(id)) - const driverId = Number(req.body.driverId) - - let updatedDrivers = {} - const previousAssigned = (await Volunteer.findById(driverId)).customers - - // unassign customers that were deselected - const unassignedCustomers = await Promise.all( - difference(previousAssigned, customerIds).map(id => - Customer.findByIdAndUpdate(id, {$set: {assignedTo: null}}, {new: true})) - ) - - // assign selected customers - const assignedCustomers = await Promise.all( - customerIds.map(async id => { - const assignedTo = (await Customer.findById(id)).assignedTo - - if (assignedTo && assignedTo !== driverId) { - // customer was assigned to another driver, remove from old driver - const updatedDriver = await Volunteer.findByIdAndUpdate(assignedTo, { - $pull: {customers: id} - }, {new: true}).populate('user', 'roles') - - updatedDrivers[assignedTo] = updatedDriver - } - return Customer.findByIdAndUpdate(id, { - $set: {assignedTo: driverId}}, {new: true}) - }) - ) - - const driver = await Volunteer.findByIdAndUpdate(driverId, { - $set: { - customers: customerIds, - optimized: false - } - }, {new: true}).populate('user', 'roles') - - res.json({ - customers: [...unassignedCustomers, ...assignedCustomers], - volunteers: [driver, ...values(updatedDrivers)] - }) - }, - async hasAuthorization(req, res, next) { - const authorizedRoles = [ - ADMIN_ROLE, - volunteerRoles.DRIVER - ] - - if (req.user && intersection(req.user.roles, authorizedRoles).length) { - return next() - } else { - throw new ForbiddenError - } - } -} diff --git a/server1/controllers/donation.js b/server1/controllers/donation.js deleted file mode 100644 index b98cf313..00000000 --- a/server1/controllers/donation.js +++ /dev/null @@ -1,69 +0,0 @@ -import {pick} from 'lodash' -import {ADMIN_ROLE} from '../../common/constants' -import Donation from '../models/donation' -import Donor from '../models/donor' -import {UnauthorizedError} from '../lib/errors' -import {BadRequestError} from '../lib/errors' -import mailer from '../lib/mail/mail-helpers' - -export default { - async create(req, res) { - let newDonation = { - ...(pick(req.body, ['donor', 'description', 'items'])), - total: req.body.items.reduce((acc, item) => { - if (isNaN(Number(item.value))) { - throw new BadRequestError - } else { - return acc + Number(item.value) - } - },0 ) - } - - if (!req.user.roles.find(r => r === ADMIN_ROLE) && - newDonation.donor !== req.user._id) { - throw new UnauthorizedError - } - - const donation = await Donation.create(newDonation) - const donor = await Donor.findByIdAndUpdate(donation.donor, - {$push: {donations: donation}}, - {new: true} - ) - - mailer.sendThanks({...donation, donor}) - - res.json({ - donation, - donor - }) - }, - - async approve(req, res) { - const donation = await Donation.findByIdAndUpdate( - req.params.donationId, - {$set: {approved: true, dateIssued: Date.now()}}, - {new: true} - ).populate('donor') - - mailer.sendReceipt(donation) - - res.json(donation) - }, - - async sendEmail(req, res) { - const donation = await Donation.findById(req.params.donationId) - .populate('donor') - - mailer.sendReceipt(donation) - res.json(donation) - }, - - hasAuthorization(req, res, next) { - if (req.user.roles.find(r => r === ADMIN_ROLE) || - req.params.donationId === req.user._id) { - return next() - } - - throw new UnauthorizedError - } -} diff --git a/server1/controllers/donor.js b/server1/controllers/donor.js deleted file mode 100644 index 0fcf5a92..00000000 --- a/server1/controllers/donor.js +++ /dev/null @@ -1,89 +0,0 @@ -import extend from 'lodash/extend' -import {omit} from 'lodash' - -import {ForbiddenError, NotFoundError} from '../lib/errors' -import {ADMIN_ROLE, clientRoles} from '../../common/constants' -import Donor from '../models/donor' -import User from '../models/user' -import {updateFields} from '../lib/update-linked-fields' - -export default { - /** - * Create a donor - */ - async create(req, res) { - const donor = new Donor({ - ...(omit(req.body, ['status', 'donations'])), - _id: req.user.id - }) - - const savedDonor = await donor.save() - - await User.findOneAndUpdate({_id: donor._id}, {$push: {roles: clientRoles.DONOR}}) - updateFields(clientRoles.DONOR,req.body.fields,donor._id) - res.json(savedDonor) - }, - - /** - * Show the current donor - */ - read(req, res) { - res.json(req.donor) - }, - - /** - * Update a donor - */ - async update(req, res) { - const donor = extend(req.donor, req.body) - const savedDonor = await donor.save() - updateFields(clientRoles.DONOR,req.body.fields,donor._id) - res.json(savedDonor) - }, - - /** - * List of donors - */ - async list(req, res) { - const donors = await Donor.find() - .sort('-dateReceived') - .populate('donations') - - res.json(donors) - }, - - /** - * Delete donor - */ - async delete(req, res) { - const id = req.donor._id - - await User.findByIdAndRemove(id) - await Donor.findByIdAndRemove(id) - - res.json(req.donor) - }, - - /** - * Donor middleware - */ - async donorById(req, res, next, id) { - const donor = await Donor.findById(id) - .populate('donations') - - if (!donor) throw new NotFoundError - - req.donor = donor - next() - }, - - /** - * Donor authorization middleware - */ - hasAuthorization(req, res, next) { - if (req.user.roles.find(r => r === ADMIN_ROLE) || req.donor._id === +req.user.id) - return next() - - throw new ForbiddenError - } -} diff --git a/server1/controllers/food.js b/server1/controllers/food.js deleted file mode 100644 index a6900250..00000000 --- a/server1/controllers/food.js +++ /dev/null @@ -1,196 +0,0 @@ -import {extend, intersection} from 'lodash' - -import { - BadRequestError, - ForbiddenError, - NotFoundError, - ValidationError -} from '../lib/errors' -import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' -import Customer from '../models/customer' -import Food from '../models/food' - -const {INVENTORY, SCHEDULE} = volunteerRoles - -export default { - /** - * Create a Food category - */ - async create(req, res) { - authorizeByRole(req.user.roles, [INVENTORY]) - - const food = new Food(req.body) - - const existingFoodCategory = await Food.find({'category': food.category, 'deleted': false}).lean() - - if (existingFoodCategory.length) { - throw new ValidationError(['category'], 'That category already exists' ) - } - - const savedFood = await food.save() - res.json(savedFood) - }, - - /** - * Update a Food category - */ - async update(req, res) { - authorizeByRole(req.user.roles, [INVENTORY]) - - const food = extend(req.food, req.body) - - const savedFood = await food.save() - res.json(savedFood) - }, - - /** - * Delete a Food category - */ - async delete(req, res) { - authorizeByRole(req.user.roles, [INVENTORY]) - - const food = req.food - - // Prevent deleting if food category contains food items not marked as deleted - if (food.items.filter(item => !item.deleted).length) { - throw new BadRequestError('Food category must be empty before deleting') - } - - // If the category has items then mark it as deleted instead of deleting from the database - if (food.items.length) { - await Food.findByIdAndUpdate(food._id, {deleted: true}) - } else { - await food.remove() - } - res.json(food) - }, - - /** - * List of Food categories - */ - async list(req, res) { - const foods = await Food.find() - .sort('category') - - res.json(foods) - }, - - /** - * Create a food item - */ - async createItem(req, res) { - authorizeByRole(req.user.roles, [INVENTORY]) - - const item = req.body - item.name = item.name.trim() - - //Check to see if an item with the same name already exists in a category - let categoryWithExistingItem = await Food.findOne( - { items: {$elemMatch:{name: {$regex: `^${item.name}$`, $options: "i"}, deleted: false }} }, - { 'items.$': 1 } - ).lean() - - if (categoryWithExistingItem) { - const existingFoodItem = categoryWithExistingItem.items[0] - existingFoodItem.categoryId = item.categoryId - existingFoodItem.quantity += Number(item.quantity) - - const updatedCategory = await updateItemHelper(categoryWithExistingItem._id, existingFoodItem) - res.json(updatedCategory) - } else { - const savedFood = await Food.findByIdAndUpdate(req.food._id, { $addToSet: { items: item } }, { new: true }) - res.json(savedFood) - - // Add item to every customer's food preferences - // TODO: do we want this? - await Customer.update({}, { $addToSet: { foodPreferences: item._id } }, { multi: true }) - } - }, - - /** - * Update a food item - */ - async updateItem(req, res) { - authorizeByRole(req.user.roles, [INVENTORY, SCHEDULE]) - - const originalCategoryId = req.params.foodId - const updatedItem = req.body - - const involvesCategoryChange = !updatedItem.categoryId || updatedItem.categoryId === originalCategoryId - if (involvesCategoryChange) authorizeByRole(req.user.roles, [INVENTORY]) - - const updatedCategory = await updateItemHelper(originalCategoryId, updatedItem) - res.json(updatedCategory) - }, - - /** - * Delete a food item - */ - async deleteItem(req, res) { - authorizeByRole(req.user.roles, [INVENTORY]) - - const categoryId = req.food._id - const foodItemId = req.itemId - await Food.update({"_id": categoryId, "items._id": foodItemId}, { $set: {"items.$.deleted": true} }) - - // Delete the food item from customer preferences - await Customer.update({}, { $pull: { "foodPreferences": foodItemId } }, {multi: true}) - - res.json({deletedItemId: req.itemId}) - }, - - /** - * Food middleware - */ - async foodById(req, res, next, id) { - const food = await Food.findById(id) - - if (!food) throw new NotFoundError - - req.food = food - next() - }, - - itemById(req, res, next, id) { - req.itemId = id - next() - } -} - -function authorizeByRole(userRoles, roles = []) { - if (!intersection(userRoles, [...roles, ADMIN_ROLE]).length) { - throw new ForbiddenError - } -} - -/** - * Common functionality used by createItem and UpdateItem to update a food item - */ -function updateItemHelper(originalCategoryId, updatedItem) { - if (!updatedItem.categoryId || updatedItem.categoryId === originalCategoryId) { - return updateFoodItemWithoutCategoryChange(originalCategoryId, updatedItem) - } else { - return updateFoodItemWithCategoryChange(originalCategoryId, updatedItem) - } -} - -function updateFoodItemWithoutCategoryChange(categoryId, updatedItem) { - // same food category so just update the item in that category - return Food.findOneAndUpdate( - { _id: categoryId, 'items._id': updatedItem._id }, - { $set: { 'items.$': updatedItem } }, - { new: true } - ) -} - -async function updateFoodItemWithCategoryChange(originalCategoryId, updatedItem) { - // delete from old category - await Food.findByIdAndUpdate(originalCategoryId, { - $pull: { items: { _id: updatedItem._id } } }) - - // add to new - return Food.findByIdAndUpdate(updatedItem.categoryId, - { $addToSet: { items: updatedItem } }, - { new: true } - ) -} diff --git a/server1/controllers/media.js b/server1/controllers/media.js deleted file mode 100644 index b7d52105..00000000 --- a/server1/controllers/media.js +++ /dev/null @@ -1,27 +0,0 @@ -import Media from '../models/media' -import { deleteFile } from '../lib/media-helpers' - -export default { - async read(req, res) { - const media = await Media.findOne() - res.json(media || new Media) - }, - - async upload(req, res) { - const media = (await Media.findOne()) || new Media - - Object.keys(req.files).forEach(type => { - const {filename} = req.files[type][0] - - if (media[type] && media[type] !== filename) { - deleteFile(media[type]) - } - - media[type] = filename - }) - - await media.save() - res.json(media) - } -} - diff --git a/server1/controllers/packing.js b/server1/controllers/packing.js deleted file mode 100644 index f9c56553..00000000 --- a/server1/controllers/packing.js +++ /dev/null @@ -1,219 +0,0 @@ -import {intersection, uniq} from 'lodash' -import moment from 'moment' -import mongoose from 'mongoose' - -import {BadRequestError, ForbiddenError} from '../lib/errors' -import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' -import Customer from '../models/customer' -import Food from '../models/food' -import Package from '../models/package' - -const beginWeek = moment.utc().startOf('isoWeek') - -export default { - list: function(req, res) { - Package.find().then(data => res.json(data)) - }, - complete: async function(req, res) { - const {packageId} = req.body - const deliveredPackage = await Package.findByIdAndUpdate(packageId, {status: 'Delivered'}, {new: true}) - if (!deliveredPackage) { - throw new BadRequestError(`package with _id ${req.body.singlePackage} not found`) - } - res.json(deliveredPackage) - }, - /** - * Creates new food packages - * The req.body post data should be an array of food package objects with the following shape - * {customer: "10000", contents: ["594d6a4f9431ac26453cef08", "594d6a4f9431ac26453cef0b"]} - */ - pack: async function(req, res) { - const packages = req.body - if (!Array.isArray(packages)) { - throw new BadRequestError('Request body must be an array') - } - const now = new Date().toISOString() - const newPackages = [] - const customerIdsToUpdate = [] - const packedItemCounts = {} - - // Iterate through the package data to populate newPackages, customerIdsToUpdate and packedItemCounts - packages.forEach(customerPackage => { - const newPackage = new Package({ - customer: customerPackage.customer, - contents: customerPackage.contents, - datePacked: now, - packedBy: req.user._id, - status: 'Packed' - }) - - newPackages.push(newPackage) - customerIdsToUpdate.push(newPackage.customer) - addPackageContentsToItemCounts(newPackage.contents, packedItemCounts) - }) - - // Validate the data for the new packages - try { - await Promise.all( - newPackages.map(customerPackage => customerPackage.validate()) - ) - } catch (err) { - if (err instanceof mongoose.Error.ValidationError) { - throw new BadRequestError(err.message) - } else { - throw err - } - } - - // Verify the customerIds and foodItemIds in the data are valid id's - const foodItemIdsToUpdate = Object.keys(packedItemCounts) - await Promise.all([ - verifyCustomerIds(customerIdsToUpdate), - verifyFoodItemIds(foodItemIdsToUpdate) - ]) - - // Save the new packages to the database - await Promise.all( - newPackages.map(customerPackage => customerPackage.save()) - ) - - //update customers lastPacked field in database - await Customer.update({ _id: { $in: customerIdsToUpdate } }, { "$set": { lastPacked: now } }, { "multi": true }) - - - // Update the food item inventory quantities - const foodItemUpdates = [] - await Promise.all( - foodItemIdsToUpdate.map(itemId => - Food.findOneAndUpdate( - { 'items._id': itemId }, - { $inc: { 'items.$.quantity': -packedItemCounts[itemId] } }, - { new: true } - ).then(food => { - const quantity = food.items.find(item => item._id.equals(itemId)).quantity - foodItemUpdates.push({ _id: itemId, quantity }) - }) - ) - ) - - // Create a list of customers lastPacked fields that the client needs to update - const customerUpdates = customerIdsToUpdate.map(_id => ({ _id, lastPacked: now })) - - res.json({ - packages: newPackages, - foodItems: foodItemUpdates, - customers: customerUpdates - }) - }, - - /** - * Unpacks a packed food package for a customer. - * In essence it undoes the pack() function by deleting the package, adding the package contents - * back to the foodItem counts and updating the customer.lastPacked field. - * req.body.id must be set to the _id of the package to delete - */ - unpack: async function (req, res) { - if (!req.body._id) { - throw new BadRequestError('package _id must be included in the request') - } - if (!mongoose.Types.ObjectId.isValid(req.body._id)) { - throw new BadRequestError(`${req.body._id} is not a valid package _id`) - } - const deletedPackage = await Package.findByIdAndRemove(req.body._id) - - if (!deletedPackage) { - throw new BadRequestError(`package with _id ${req.body._id} not found`) - } - - // Have to update the lastPacked date for the customer by finding the latest datePacked package for that customer - const customersLastPackedPackage = - await Package.findOne({ customer: deletedPackage.customer }, {}, { sort: { 'datePacked': -1 } }).lean() - - const updatedLastPacked = customersLastPackedPackage ? customersLastPackedPackage.datePacked : null - - await Customer.findByIdAndUpdate(deletedPackage.customer, {lastPacked: updatedLastPacked}) - - const updatedItemCounts = [] - - // Add the items from the unpacked package back to the inventory counts - await Promise.all( - deletedPackage.contents.map(itemId => Food.findOneAndUpdate( - {'items._id': itemId}, - {$inc: {'items.$.quantity': 1 }}, - {new: true} - ).then(foodCategory => { - // store the updated quantity for this foodItem in updatedItemCounts - const foodItem = foodCategory.items.find(item => item._id.equals(itemId)) - updatedItemCounts.push({ - _id: foodItem._id, - quantity: foodItem.quantity - }) - })) - ) - - res.json({ - packages: deletedPackage, - foodItems: updatedItemCounts, - customers: { - _id: deletedPackage.customer, - lastPacked: updatedLastPacked - } - }) - - }, - deliver: async function(req, res) { - const {customerIds} = req.body - const customers = await Promise.all( - customerIds.map(async id => - Customer.findByIdAndUpdate(id, - {lastDelivered: beginWeek}, {new: true})) - ) - res.json({customers}) - }, - hasAuthorization(req, res, next) { - const authorizedRoles = [ - ADMIN_ROLE, - volunteerRoles.PACKING - ] - if (req.user && intersection(req.user.roles, authorizedRoles).length) { - return next() - } - - throw new ForbiddenError - } -} - -// Verify a list of customer Ids exist in the database -async function verifyCustomerIds(customerIds) { - const count = await Customer.count({ _id: { $in: customerIds } }) - - if (count !== uniq(customerIds).length) { - throw new BadRequestError('One or more customerIds are not valid') - } - -} - -// Verify a list of foodItem Ids exist in the database -async function verifyFoodItemIds(foodItemIds) { - await Promise.all( - foodItemIds.map(_id => { - if (!mongoose.Types.ObjectId.isValid(_id)) { - throw new BadRequestError(`${_id} is not a valid foodItem _id`) - } - return Food.count({ 'items._id': _id }).then(count => { - if (!count) throw new BadRequestError(`foodItem ${_id} was not found in the database`) - }) - }) - ) -} - -// Takes an array of foodItem ids and increments packedItemCounts[foodItemId] for each item in contents -function addPackageContentsToItemCounts(contents, packedItemCounts) { - contents.forEach(foodItemId => { - if (packedItemCounts[foodItemId]) { - packedItemCounts[foodItemId] = packedItemCounts[foodItemId] + 1 - } else { - packedItemCounts[foodItemId] = 1 - } - }) -} diff --git a/server1/controllers/page.js b/server1/controllers/page.js deleted file mode 100644 index 8ea91072..00000000 --- a/server1/controllers/page.js +++ /dev/null @@ -1,111 +0,0 @@ -import sanitizeHtml from 'sanitize-html' -import {extend} from 'lodash' -import {writeFileSync, mkdirSync} from 'fs' -import del from 'del' - -import {NotFoundError} from '../lib/errors' -import Page from '../models/page' - -const clientPath = '/media/pages' -const serverPath = process.env.NODE_ENV === 'production' ? - 'dist/client/media/pages' : - 'assets/media/pages' - -try { - mkdirSync(serverPath) -} catch (err) { - if (err.code !== 'EEXIST') throw err -} - -const sanitizeHtmlConfig = { - allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'span', 'h1', 'h2', 's', 'u']), - allowedAttributes: { - img: ['src', 'width', 'style'], - span: ['style', 'data-id', 'data-label', 'class'], - a: ['href'], - p: ['class'] - } -} - -export default { - async list(req, res) { - const {type} = req.query - const pages = await Page.find(type ? {type} : {}) - res.json(pages) - }, - - async update(req, res) { - const {body, identifier} = req.body - let keepImages = [] - - const sanitizedBody = sanitizeHtml(body, { - ...sanitizeHtmlConfig, - transformTags: { - img: processImageTag(identifier, keepImages) - } - }) - - const page = extend(req.page, {body: sanitizedBody}) - const updatedPage = await page.save() - - // delete stale images - await del([ - `${serverPath}/${identifier}-*`, - ...keepImages.map(img => `!${img}`) - ]) - - res.json(updatedPage) - }, - - async read(req, res) { - res.json(req.page) - }, - - async pageByIdentifier(req, res, next, identifier) { - const page = await Page.findOne({identifier}) - if (!page) throw new NotFoundError - - req.page = page - next() - } -} - -/** - * create a sanitizeHtml transform function to save image data and update 'src' - * attribute to the path of the saved image - * - * @param {string} identifier page identifier - * @param {array} keepImages - * @returns {object} - */ -function processImageTag(identifier, keepImages) { - return (tagName, attribs) => { - const src = attribs.src.startsWith(clientPath) ? - attribs.src : - saveImage(identifier, attribs.src) - - keepImages.push(src.replace(clientPath, serverPath)) - - return { - tagName, - attribs: {...attribs, src} - } - } -} - -/** - * save a base64 image for a given page identifier - * - * @param {string} identifier - * @param {string} src - * @returns {string} updated src attribute - */ -function saveImage(identifier, src) { - // eslint-disable-next-line no-unused-vars - const [_, ext, data] = src.match(/^data:image\/(.+);base64(.+)/) - const filename = `${identifier}-${new Date().getTime()}.${ext}` - - writeFileSync(`${serverPath}/${filename}`, data, 'base64') - - return `${clientPath}/${filename}` -} diff --git a/server1/controllers/questionnaire.js b/server1/controllers/questionnaire.js deleted file mode 100644 index bda6042f..00000000 --- a/server1/controllers/questionnaire.js +++ /dev/null @@ -1,45 +0,0 @@ -import {extend} from 'lodash' - -import {NotFoundError} from '../lib/errors' -import Questionnaire from '../models/questionnaire' - -export default { -// Create questionnaire - async create(req, res) { - const questionnaire = new Questionnaire(req.body) - const savedQuestionnaire = await questionnaire.save() - - res.json(savedQuestionnaire) - }, - - // Update a questionnaire - async update(req, res) { - const questionnaire = extend(req.questionnaire, req.body) - - const savedQuestionnaire = await questionnaire.save() - - res.json(savedQuestionnaire) - }, - - // Query questionnaires - async query(req, res) { - const questionnaires = await Questionnaire.find() - - res.json(questionnaires) - }, - - // Get specified questionnaire - async get(req, res) { - res.json(req.questionnaire) - }, - - // Questionnaire middleware - async questionnaireById(req, res, next, id) { - const questionnaire = await Questionnaire.findById(id) - - if (!questionnaire) throw new NotFoundError - - req.questionnaire = questionnaire - next() - } -} diff --git a/server1/controllers/settings.js b/server1/controllers/settings.js deleted file mode 100644 index bd11585f..00000000 --- a/server1/controllers/settings.js +++ /dev/null @@ -1,55 +0,0 @@ -import {includes, intersection} from 'lodash' - -import {ForbiddenError, ValidationError} from '../lib/errors' -import {ADMIN_ROLE, volunteerRoles} from '../../common/constants' -import config from '../config' -import Settings from '../models/settings' - -import {locateAddress} from '../lib/geolocate' - -export default { - async read (req, res) { - const {user} = req - const mapRoles = [ADMIN_ROLE, volunteerRoles.DRIVER] - const projection = user && intersection(user.roles, mapRoles).length ? - '+gmapsApiKey +gmapsClientId ' : '' - - let settings = await Settings.findOne().select(projection).lean() - - // Add property to indicate if google authentication is available - settings.googleAuthentication = !!(config.oauth.googleClientID && config.oauth.googleClientSecret) - - // Remove unnecessary info before sending off the object to the client - delete settings.__v - - res.json(settings) - }, - - async save (req, res) { - const {user} = req - - if (!includes(user.roles, ADMIN_ROLE)) { - throw new ForbiddenError - } - - const location = await locateAddress(req.body.address) - - if (!location) { - throw new ValidationError({address: 'Address not found'}) - } - - const settings = { - ...req.body, - location, - } - - const count = await Settings.count() - - const query = count ? - Settings.findByIdAndUpdate(settings._id, settings, {new: true}) : - Settings.create(settings) - - const savedSettings = await query.select('+gmapsApiKey +gmapsClientId') - res.json(savedSettings) - } -} diff --git a/server1/controllers/users.js b/server1/controllers/users.js deleted file mode 100644 index 4a366c8b..00000000 --- a/server1/controllers/users.js +++ /dev/null @@ -1,13 +0,0 @@ -import {extend} from 'lodash' - -import * as authentication from './users/authentication' -import * as authorization from './users/authorization' -import * as password from './users/password' -import * as profile from './users/profile' - -export default extend( - authentication, - authorization, - password, - profile -) diff --git a/server1/controllers/users/authentication.js b/server1/controllers/users/authentication.js deleted file mode 100644 index 1c5fba29..00000000 --- a/server1/controllers/users/authentication.js +++ /dev/null @@ -1,74 +0,0 @@ -import {map, pick} from 'lodash' -import passport from 'passport' - -import {UnauthorizedError, ValidationError} from '../../lib/errors' -import User from '../../models/user' - -/** - * Signup - */ -export const signup = async function(req, res) { - // Init Variables - let user = new User({ - ...(pick(req.body, ['firstName', 'lastName', 'email', 'password'])), - roles: [], - provider: 'local', - displayName: `${req.body.firstName} ${req.body.lastName}` - }) - - try { - await user.save() - } catch (error) { - // Check for unique key violation from email - if (error.code === 11000 && error.errmsg.match('email')) { - throw new ValidationError({email: 'Email address already has an account'}) - } else if (error.name === 'ValidationError') { - const errors = Object.assign( - {}, - ...map(error.errors, (v, k) => ({[k]: v.message})) - ) - - throw new ValidationError(errors) - } else { - throw error - } - } - - user.password = undefined - user.salt = undefined - - req.login(user, err => { - if (err) throw new UnauthorizedError - }) - return res.json(user) -} - -/** - * Signin after passport authentication - */ -export const signin = function(req, res, next) { - passport.authenticate('local', function(err, user, info) { - if (err || !user) { - res.status(400).send(info) - } else { - // Remove sensitive data before login - user.password = undefined - user.salt = undefined - - req.login(user, function(err) { - if (err) throw new UnauthorizedError - return res.json(user) - }) - } - })(req, res, next) -} - -/** - * Signout - */ -export const signout = function(req, res) { - req.logout() - req.session.destroy(function() { - res.redirect('/') - }) -} diff --git a/server1/controllers/users/authorization.js b/server1/controllers/users/authorization.js deleted file mode 100644 index c2c16ac6..00000000 --- a/server1/controllers/users/authorization.js +++ /dev/null @@ -1,40 +0,0 @@ -import intersection from 'lodash/intersection' - -import {ForbiddenError, NotFoundError, UnauthorizedError} from '../../lib/errors' -import User from '../../models/user' - -/** - * User middleware - */ -export const userByID = async function(req, res, next, id) { - const user = User.findById(id) - if (!user) throw new NotFoundError - - req.profile = user - next() -} - -/** - * Require login routing middleware - */ -export const requiresLogin = function(req, res, next) { - if (!req.isAuthenticated()) { - throw new UnauthorizedError - } - next() -} - -/** - * User authorizations routing middleware - */ -export const hasAuthorization = function(roles) { - return (req, res, next) => { - this.requiresLogin(req, res, function() { - if (!intersection(req.user.roles, roles).length) { - throw new ForbiddenError - } - - next() - }) - } -} diff --git a/server1/controllers/users/password.js b/server1/controllers/users/password.js deleted file mode 100644 index 63ed0457..00000000 --- a/server1/controllers/users/password.js +++ /dev/null @@ -1,88 +0,0 @@ -import crypto from 'crypto' -import thenify from 'thenify' - -import config from '../../config' -import { - BadRequestError, - NotFoundError, - UnauthorizedError - // ValidationError -} from '../../lib/errors' -import mailer from '../../lib/mail/mail-helpers' -import User from '../../models/user' - -const randomBytes = thenify(crypto.randomBytes) - -/** - * Forgot password - */ -export const forgot = async function(req, res) { - if (!config.sendgrid.API_KEY) { - return res.status(500).json({message: "This site does not have email capability at this time. Please contact the site owner for assistance."}) - } - - const token = (await randomBytes(20)).toString('hex') - - // Lookup user by email - if (!req.body.email) throw new BadRequestError('Email is required') - const user = await User.findOne({email: req.body.email}).select('-salt -password') - if (user && user.provider !== 'local') { - if (user.provider === 'google') await mailer.sendPasswordGoogle(user) - } else if (user && user.provider === 'local') { - user.resetPasswordToken = token - user.resetPasswordExpires = Date.now() + 3600000 // 1 hour - - const port = process.env.NODE_ENV === 'production' ? '' : `:${config.port}` - const url = `${config.protocol}://${config.host}${port}/users/reset-password/${token}` - - await mailer.sendPasswordReset(user, url) - await user.save() - } - - res.send({message: 'Password reset email sent'}) -} - -/** - * Reset password POST from email token - */ -export const reset = async function (req, res) { - const user = await User.findOne({ - resetPasswordToken: req.params.token, - resetPasswordExpires: {$gt: Date.now()} - }) - - if (!user) { - throw new BadRequestError('Password reset token is invalid or has expired.') - } - - user.password = req.body.password - user.resetPasswordToken = undefined - user.resetPasswordExpires = undefined - - await user.save() - res.json({message: "Password successfully reset"}) -} - -/** - * Change Password - */ -export const changePassword = async function(req, res) { - const {newPassword, currentPassword, verifyPassword} = req.body.passwordDetails - - if (!req.user || !newPassword || !currentPassword || newPassword !== verifyPassword) { - throw new BadRequestError - } - - const user = await User.findById(req.user.id).select('+salt +password') - if (!user) throw new NotFoundError - if (!user.authenticate(currentPassword)) - throw new BadRequestError('Password is incorrect') - - user.password = newPassword - await user.save() - - req.login(user, function(err) { - if (err) throw new UnauthorizedError - return res.send({message: 'Password changed successfully'}) - }) -} diff --git a/server1/controllers/users/profile.js b/server1/controllers/users/profile.js deleted file mode 100644 index 33428618..00000000 --- a/server1/controllers/users/profile.js +++ /dev/null @@ -1,134 +0,0 @@ -import {extend, includes, omit, pick} from 'lodash' - -import {ADMIN_ROLE} from '../../../common/constants' -import {BadRequestError} from '../../lib/errors' -import User from '../../models/user' - -/** - * List users - */ -export const list = async function(req, res) { - const users = await User.find({}).lean() - res.json(users) -} - -/** - * Get a user by userId - */ -export const getById = async function(req, res) { - if (!req.params.userId.match(/^\d+$/)){ - throw new BadRequestError(`UserId ${req.params.userId} is not valid`) - } - const user = await User.findById(req.params.userId).lean() - if (user) { - res.json(user) - } else { - throw new - BadRequestError(`UserId ${req.params.userId} not found`) - } -} - -/** - * Updating own profile - */ -export const updateProfile = async function(req, res) { - const user = req.user - extend(user, pick(req.body, ['firstName', 'lastName', 'email'])) - - if (!user.firstName || !user.lastName) throw new BadRequestError('First Name and Last Name are required') - - user.updated = Date.now() - user.displayName = user.firstName + ' ' + user.lastName - - const sameEmail = await User.findOne({email: user.email}).lean() - if (sameEmail && sameEmail._id !== user._id) - throw new BadRequestError('Email address is taken') - - await user.save() - res.json(user) -} - -/** - * Updating own notifications profile - */ -export const updateNotifications = async function(req, res) { - const user = req.user - extend(user, pick(req.body, ['notifications'])) - - user.updated = Date.now() - - const sameEmail = await User.findOne({email: user.email}).lean() - if (sameEmail && sameEmail._id !== user._id) - throw new BadRequestError('Email address is taken') - - await user.save() - res.json(user) -} - -/** - * Update user details - */ -export const update = async function(req, res) { - // TODO: why doesn't `req.profile` have a save method? - const user = await User.findById(req.params.userId) - - extend(user, omit(req.body, ['_id', 'isAdmin'])) - - if (!user.firstName || !user.lastName) throw new BadRequestError('First Name and Last Name are required') - - user.updated = Date.now() - user.displayName = user.firstName + ' ' + user.lastName - - const sameEmail = await User.findOne({email: user.email}).lean() - if (sameEmail && sameEmail._id !== user._id) - throw new BadRequestError('Email address is taken') - - // Update admin status - const previouslyAdmin = includes(user.roles, ADMIN_ROLE) - const currentlyAdmin = req.body.isAdmin - - if (currentlyAdmin && !previouslyAdmin) { - user.roles.push(ADMIN_ROLE) - } else if (!currentlyAdmin && previouslyAdmin) { - if (user._id === req.user._id) - throw new BadRequestError('You are not allowed to demote yourself') - - user.roles.splice(user.roles.indexOf(ADMIN_ROLE), 1) - } - - await user.save() - res.json(user) -} - -/** - * Send User - */ -export const me = async function(req, res) { - if (!req.session.passport) return res.json(null) - - const user = await User.findById(req.session.passport.user) - return res.json(user) -} - -/** - * List own notifications profile - */ -export const listNotifications = async function(req, res) { - const user = req.user - res.json(user.notifications) -} - -/** - * Remove own notification profile - */ -export const removeNotification = async function(req, res) { - //console.log('removeNotification') - const id = req.originalUrl.split('?id=')[1] - const user = req.user - if (id !== null && id !== undefined && id !== "") user.notifications.splice(id,1) - else if (id === null || id === undefined || id === "") user.notifications = [] - User.findOneAndUpdate({ '_id': user._id }, - { 'notifications': user.notifications }) - .exec() - res.json(user.notifications) -} diff --git a/server1/controllers/volunteer.js b/server1/controllers/volunteer.js deleted file mode 100644 index d4e8fe41..00000000 --- a/server1/controllers/volunteer.js +++ /dev/null @@ -1,194 +0,0 @@ -import {difference, extend, omit} from 'lodash' - -import {ForbiddenError, NotFoundError} from '../lib/errors' -import {ADMIN_ROLE, clientRoles} from '../../common/constants' -import User from '../models/user' -import Volunteer from '../models/volunteer' -import {updateFields} from '../lib/update-linked-fields' - -export default { - /** - * Create a volunteer - */ - async create(req, res) { - let volunteer = new Volunteer(omit(req.body, ['status', 'customers'])) - volunteer._id = req.user.id - volunteer.user = req.user.id - - await User.findOneAndUpdate( - {_id: volunteer._id}, - {$push: {roles: clientRoles.VOLUNTEER}} - ) - - const savedVolunteer = await volunteer.save() - updateFields(clientRoles.VOLUNTEER,req.body.fields,volunteer._id) - res.json(savedVolunteer) - }, - - /* - * Adds a Volunteer shift - */ - async addShift(req, res) { - - //const volunteer = extend(req.volunteer, req.body) - const new_volunteer = req.body - const shift = new_volunteer.shift - - const old_volunteer = await Volunteer.findById(new_volunteer._id) - const shifts = old_volunteer.shift - shifts.push(shift) - - const updatedVolunteer = await Volunteer.findOneAndUpdate( - { _id: new_volunteer._id}, - { $set: { shift: shifts}}) - - res.json(updatedVolunteer) - }, - - /* - * Deletes a volunteer shift - */ - async deleteShift(req, res) { - const new_volunteer = req.body - const del_shift = new_volunteer.del_shift - - const old_volunteer = await Volunteer.findById(new_volunteer._id) - const shifts = old_volunteer.shift - - var del_time = new Date(del_shift.start) - var new_shifts = [] - - for(var i = 0; i < shifts.length; i++) { - var new_time = new Date(shifts[i].date) - if(del_time.getTime() === new_time.getTime()) { - continue - } - else { - new_shifts.push(shifts[i]) - } - } - - const updatedVolunteer = await Volunteer.findOneAndUpdate( - { _id: new_volunteer._id}, - { $set: {shift: new_shifts}}) - - res.json(updatedVolunteer) - }, - - /* - * Updates a Volunteer shift - */ - async updateShift(req, res) { - const new_volunteer = req.body - const times = new_volunteer.times - - const old_volunteer = await Volunteer.findById(new_volunteer._id) - const shifts = old_volunteer.shift - - var old_date = new Date(times.oldTime) - - for(var i = 0; i < shifts.length; i++) { - var new_time = new Date(shifts[i].date) - if(old_date.getTime() === new_time.getTime()) { - shifts[i].date = times.newTime - } - } - - const updatedVolunteer = await Volunteer.findOneAndUpdate( - { _id: new_volunteer._id}, - { $set: {shift: shifts}}) - - res.json(updatedVolunteer) - }, - - /** - * Show the current volunteer - */ - async read(req, res) { - if (!req.user.roles.find(r => r === ADMIN_ROLE)) - return res.json(req.volunteer) - - const {roles} = await User.findById(req.volunteer._id).lean() - - res.json({ - ...req.volunteer.toObject(), - roles - }) - }, - - /** - * Update a volunteer - */ - async update(req, res) { - const volunteer = extend(req.volunteer, req.body) - - const user = await User.findById(volunteer._id).lean() - const newVolunteer = await volunteer.save() - //Volunteer.findByIdAndUpdate({ _id: 10017}, { $set : { lastName: "hi"}}) - - if (!volunteer.roles || !req.user.roles.find(r => r === ADMIN_ROLE)) { - updateFields(clientRoles.VOLUNTEER,req.body.fields,volunteer._id) - return res.json(newVolunteer) - } - const oldRoles = difference(user.roles, volunteer.roles) - const newRoles = difference(volunteer.roles, user.roles) - const roles = difference(user.roles.concat(newRoles), oldRoles) - - if (newRoles.length || oldRoles.length){ - await User.findByIdAndUpdate(volunteer._id, {$set: {roles}}) - } - updateFields(clientRoles.VOLUNTEER,req.body.fields,volunteer._id) - res.json({ - ...newVolunteer.toObject(), - roles - }) - }, - - /** - * List of volunteers - */ - async list(req, res) { - const volunteers = await Volunteer.find() - .sort('-dateReceived') - .populate('user', 'roles') - - res.json(volunteers) - }, - - /** - * Delete volunteer - */ - async delete(req, res) { - const id = req.volunteer._id - - const volunteer = await Volunteer.findByIdAndRemove(id) - await User.findByIdAndRemove(id) - - res.json(volunteer) - }, - - /** - * Volunteer middleware - */ - async volunteerById(req, res, next, id) { - const volunteer = await Volunteer.findById(id) - .populate('customers') - - if (!volunteer) throw new NotFoundError - - req.volunteer = volunteer - next() - }, - - /** - * Volunteer authorization middleware - */ - async hasAuthorization(req, res, next) { - if (!req.user.roles.find(r => r === ADMIN_ROLE) && - req.volunteer._id !== +req.user.id) { - throw new ForbiddenError - } - next() - } - -} diff --git a/server1/entry.test.js b/server1/entry.test.js deleted file mode 100644 index 765dd1e2..00000000 --- a/server1/entry.test.js +++ /dev/null @@ -1,30 +0,0 @@ -import mongoose from 'mongoose' -import chai from 'chai' -import sinon from 'sinon' -import sinonChai from 'sinon-chai' -import chaiAsPromised from 'chai-as-promised' -import supertest from 'supertest' - -import config from './config' -global.expect = chai.expect -global.sinon = sinon -global.supertest = supertest - -chai.use(sinonChai) -chai.use(chaiAsPromised) - -mongoose.Promise = global.Promise - -global.initDb = async function() { - if (!mongoose.connection.readyState) { - await mongoose.connect(config.db) - } -} - -global.resetDb = async function() { - mongoose.models = {} - mongoose.modelSchemas = {} - if (mongoose.connection.readyState) { - await mongoose.disconnect() - } -} diff --git a/server1/index.js b/server1/index.js deleted file mode 100644 index 04171d26..00000000 --- a/server1/index.js +++ /dev/null @@ -1,2 +0,0 @@ -require('babel-register') -require('./server') diff --git a/server1/lib/enforce-ssl-middleware.js b/server1/lib/enforce-ssl-middleware.js deleted file mode 100644 index 06abda51..00000000 --- a/server1/lib/enforce-ssl-middleware.js +++ /dev/null @@ -1,12 +0,0 @@ -import {BadRequestError} from './errors' - -export default function(req, res, next) { - if (req.headers['x-forwarded-proto'] !== 'https') { - if (req.method === 'GET') { - return res.redirect(301, `https://${req.hostname}${req.url}`) - } else { - throw new BadRequestError - } - } - next() -} diff --git a/server1/lib/errors.js b/server1/lib/errors.js deleted file mode 100644 index 089b702d..00000000 --- a/server1/lib/errors.js +++ /dev/null @@ -1,92 +0,0 @@ -export class HttpError extends Error { - constructor() { - super() - } -} - -export class BadRequestError extends HttpError { - constructor(message) { - super() - this.name = this.constructor.name - this.message = message || 'Invalid request' - this.status = 400 - } -} - -export class UnauthorizedError extends HttpError { - constructor(message) { - super() - this.name = this.constructor.name - this.message = message || 'Unauthorized' - this.status = 401 - } -} - -export class ForbiddenError extends HttpError { - constructor(message) { - super() - this.name = this.constructor.name - this.message = message || 'User is not authorized' - this.status = 403 - } -} - -export class NotFoundError extends HttpError { - constructor(message) { - super() - this.name = this.constructor.name - this.message = message || 'Not found' - this.status = 404 - } -} - -export class ValidationError extends BadRequestError { - constructor(paths, message) { - super(message || 'Validation error') - this.name = this.constructor.name - this.paths = paths - } -} - -export class SimulatedError extends Error { - constructor(message) { - super() - Error.captureStackTrace(this, this.constructor) - this.name = this.constructor.name - this.message = message || 'Simulated error' - this.status = 500 - } -} - -const defaultResponse = { - message: 'Something went wrong on the server', - status: 500 -} - -export default function getErrorMessage(err) { - if (err.code && err.code === 11000 || err.code === 11001) { - return { - message: 'Unique field already exists', - status: 400 - } - } - - if (err instanceof HttpError) { - return { - message: err.message, - status: err.status, - paths: err.paths - } - } - - //This catches when body-parser encounters bad JSON user data in a request - if(err.stack.match(/^SyntaxError:.+in JSON(.|\n)*node_modules\/body-parser/)) { - return { - message: (process.env.NODE_ENV === 'production') ? 'The data received by the server is not properly formatted. Try refreshing your browser.' - : `Bad JSON in HTTP request. ${err.message}: ${err.body}`, - status: 400 - } - } - - return defaultResponse -} diff --git a/server1/lib/geolocate.js b/server1/lib/geolocate.js deleted file mode 100644 index 135e1d1f..00000000 --- a/server1/lib/geolocate.js +++ /dev/null @@ -1,39 +0,0 @@ -import keys from '../config/index' -import nodeGeocoder from 'node-geocoder' -import {fieldTypes} from '../../common/constants' -import {getFieldsByType} from '../lib/questionnaire-helpers' -import {ValidationError} from '../lib/errors' - -const geocoder = nodeGeocoder({ - provider: 'google', - apiKey: keys.gmapsApiKey, - formatter: null -}) - -export async function locateAddress(address) { - const [result] = await geocoder.geocode(address) - if (!result) return - - const {latitude, longitude} = result - return {lat: latitude, lng: longitude} -} - -export async function locateQuestionnaire(fields, identifier) { - if (process.env.NODE_ENV === 'test') return - - const addressFields = await getFieldsByType( - identifier, fields, fieldTypes.ADDRESS) - - const address = addressFields.map(field => field.value).join(', ') - - const location = await locateAddress(address) - - // Mark all address fields as invalid, add error text to first field - if (!location) return new ValidationError({ - fields: Object.assign({}, ...addressFields.map((field, i) => ({ - [field.meta]: i === 0 ? 'Address not found' : ' '} - ))) - }) - - return location -} diff --git a/server1/lib/mail/email-template.html b/server1/lib/mail/email-template.html deleted file mode 100644 index 8c0f2ee8..00000000 --- a/server1/lib/mail/email-template.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - {subject} - - - - - - -
- - {content} -
-
- - diff --git a/server1/lib/mail/html-transform.js b/server1/lib/mail/html-transform.js deleted file mode 100644 index 1942eb69..00000000 --- a/server1/lib/mail/html-transform.js +++ /dev/null @@ -1,116 +0,0 @@ -import {parseFragment, parse, serialize} from 'parse5' -import {trim} from 'lodash' - -/** - * @export - * @param {string} html - * @param {Object} options - * @returns string - */ -export default function transformHtml(html, { - replaceTags = {}, - omitTags = [], - fragment = true -} = {}) { - const node = fragment ? parseFragment(html) : parse(html) - const transformed = transform(node) - - return serialize(transformed) - - function transform(node) { - if (!node.childNodes) return node - - return { - ...node, - childNodes: node.childNodes.reduce(reduceChildren, []) - } - } - - function reduceChildren(acc, node) { - const shouldOmit = omitTags.find(tag => tag === node.nodeName) - const empty = node.value && !trim(node.value) - if (shouldOmit || empty) return acc - return acc.concat(replace(node)) - } - - function replace(node) { - const replacement = replaceTags[node.nodeName] - const replaceAll = replaceTags['*'] - - let childNodes = node.childNodes - if (childNodes) { - childNodes = replaceAll ? childNodes.reduce(replaceAll, []) : childNodes - childNodes = childNodes.reduce(reduceChildren, []) - } - - if (typeof replacement === 'string') { - return [h('#text', replacement), ...childNodes] - } - - if (Array.isArray(replacement)) { - const leading = replacement[0] - const trailing = replacement[1] || '' - return [h('#text', leading), ...childNodes, h('#text', trailing)] - } - - if (typeof replacement === 'function') { - return replacement({...node, childNodes}) - } - - return {...node, childNodes} - } -} - -// utils - -export function getAttr(node, name) { - const attr = node.attrs.find(attr => attr.name === name) - return attr && attr.value -} - -export function hasClass(node, className) { - const classes = getAttr(node, 'class') - return classes && classes.split(' ').find(name => name === className) -} - -/** - * Node factory, usage: - * - * h(htmlString) - * - * h(node) - * - * h('#text', textString) - * - * h(tagName, attrs, children) - * - * @param {[string]|[object]|[string, string]|[string, [object], [object]]} args - * @returns {object} - */ -export function h(...args) { - if (args.length === 1) { - const [node] = typeof args[0] === 'string' ? - parseFragment(args[0]).childNodes : - args - - return h(node.tagName, node.attrs, node.childNodes) - } - - if (args.length === 2) { - const [type, value] = args - - return { - nodeName: type, - value: value || '' - } - } - - const [type, attrs, childNodes] = args - - return { - nodeName: type, - tagName: type, - attrs, - childNodes - } -} diff --git a/server1/lib/mail/html-transform.spec.js b/server1/lib/mail/html-transform.spec.js deleted file mode 100644 index f29ef5e7..00000000 --- a/server1/lib/mail/html-transform.spec.js +++ /dev/null @@ -1,86 +0,0 @@ -import transformHtml, {h, getAttr, hasClass} from './html-transform' - -describe('Html transform', function() { - it('does nothing', function() { - const html = '

Something

' - - const res = transformHtml(html) - expect(res).to.equal(html) - }) - - it('omits tags and their contents', function() { - const input = '

Title

Text

' - const output = '

Text

' - - const res = transformHtml(input, {omitTags: ['h1']}) - expect(res).to.equal(output) - }) - - it('replaces tags with a string', function() { - const input = '

A paragraphNested span

' - const output = '
\nA paragraphNested span
' - const replaceTags = {p: '\n'} - - const res = transformHtml(input, {replaceTags}) - expect(res).to.equal(output) - }) - - it('replaces tags with an array', function() { - const input = '

A paragraphNested span

' - const output = '
\nA paragraphNested span\n
' - const replaceTags = {p: ['\n', '\n']} - - const res = transformHtml(input, {replaceTags}) - expect(res).to.equal(output) - }) - - it('replaces tags with a function', function() { - const input = '

A paragraphNested span

' - const output = '
A paragraphNested span
' - const replaceTags = { - p: node => h('div', [], node.childNodes) - } - - const res = transformHtml(input, {replaceTags}) - expect(res).to.equal(output) - }) - - it('runs a custom reducer on all tags', function() { - const input = '' - const output = '
[foo.com] Link
' - const replaceTags = {'*': stringifyLinks} - - const res = transformHtml(input, {replaceTags}) - expect(res).to.equal(output) - }) - - it('handles complex cases', function() { - const input = '

Some text with a Link

' - const output = '
Some text with a [foo.com] Link
' - - function replaceParagraph(node) { - const tdAttrs = hasClass(node, 'text-center') ? - [{name: 'align', value: 'center'}] : [] - - return h('tr', [], [ - h('td', tdAttrs, node.childNodes) - ]) - } - - const replaceTags = { - p: replaceParagraph, - '*': stringifyLinks - } - - const res = transformHtml(input, {replaceTags}) - expect(res).to.equal(output) - }) -}) - -function stringifyLinks(acc, node) { - if (node.tagName !== 'a') return acc.concat(node) - return acc.concat([ - h('#text', `[${getAttr(node, 'href')}] `), - ...node.childNodes - ]) -} diff --git a/server1/lib/mail/mail-generator.js b/server1/lib/mail/mail-generator.js deleted file mode 100644 index 548e1a66..00000000 --- a/server1/lib/mail/mail-generator.js +++ /dev/null @@ -1,146 +0,0 @@ -import juice from 'juice' -import {readFileSync} from 'fs' -import {resolve} from 'path' -import striptags from 'striptags' -import transformHtml, {h, getAttr, hasClass} from './html-transform' -import {trim, uniq, isUndefined} from 'lodash' - -import config from '../../config' -import Page from '../../models/page' -import placeholders, {placeholderTypes} from '../../../common/placeholders' - -const templatePath = resolve(__dirname, 'email-template.html') -const template = readFileSync(templatePath).toString() - -/** - * bind placeholders, insert mail into template and inline styles - * - * @export - * @param {string} identifier - * @param {object} bindings - * @returns - */ -export default async function generate(identifier, bindings) { - const page = await Page.findOne({identifier}).lean() - if (!page || page.disabled) return - - let attachments = [] - const body = transformBody(page.body, bindings, attachments) - const subject = transformSubject(page.subject, bindings) - const text = transformText(body) - const html = template.replace(/\{content\}/, body) - .replace(/\{subject\}/g, subject) - - return { - html: juice(html), - text, - subject, - attachments: uniq(attachments) - } -} - -function transformText(body) { - const replaceTags = { - td: '\n\n', - img: replaceWithAltText, - '*': stringifyLinks - } - - return trim(striptags(transformHtml(body, {replaceTags}))) -} - -function transformBody(body, bindings, attachments) { - const replaceTags = { - span: node => isPlaceholder(node) ? - bindPlaceholder(node, bindings, attachments) : - node, - img: setFullUrl, - p: replaceWithTrTd, - blockquote: nestInTrTd, - h1: nestInTrTd, - h2: nestInTrTd, - h3: nestInTrTd - } - - return transformHtml(body, {replaceTags}) -} - -function transformSubject(subject, bindings) { - const replaceTags = { - span: node => isPlaceholder(node) ? bindPlaceholder(node, bindings) : node - } - - return striptags(transformHtml(subject, {replaceTags})) -} - -function bindPlaceholder(node, bindings, attachments) { - - const id = getAttr(node, 'data-id') - const placeholder = placeholders.find(pl => pl.id === id) - - //Receipt Placeholder only - if (id === 'receipt') { - return h(placeholder.format(bindings)) - } - - if (!placeholder) throw new Error('Invalid placeholder', id) - - if (isUndefined(bindings[id]) && placeholder.type !== placeholderTypes.ATTACHMENT) - throw new Error('Missing binding for placeholder', id) - - if (Array.isArray(attachments) && placeholder.type === placeholderTypes.ATTACHMENT) - attachments.push(placeholder.id) - - return typeof placeholder.format === 'function' ? - h(placeholder.format(bindings[id])) : - h('#text', bindings[id]) -} - -function isPlaceholder(node) { - return node.nodeName === 'span' && hasClass(node, 'ql-placeholder-content') -} - -function stringifyLinks(acc, node) { - if (node.tagName !== 'a') return acc.concat(node) - return acc.concat([ - ...node.childNodes, - h('#text', `\n[${getAttr(node, 'href')}]\n`), - ]) -} - -function replaceWithAltText(node) { - const text = getAttr(node, 'alt') - return h('#text', text ? `\n[${text}]\n` : '') -} - -function replaceWithTrTd(node) { - return h('tr', [], [ - h('td', node.attrs, node.childNodes) - ]) -} - -function nestInTrTd(node) { - return h('tr', [], [ - h('td', [], [ - h(node) - ]) - ]) -} - -function setFullUrl(node) { - const port = process.env.NODE_ENV === 'production' ? '' : ':8080' - const baseUrl = `${config.protocol}://${config.host}${port}` - - const src = getAttr(node, 'src') - if (!src.startsWith('/')) return node - - const attrs = [ - ...node.attrs.filter(attr => attr.name !== 'src'), - {name: 'src', value: `${baseUrl}${src}`} - ] - - return { - ...node, - attrs - } -} diff --git a/server1/lib/mail/mail-generator.spec.js b/server1/lib/mail/mail-generator.spec.js deleted file mode 100644 index fd13b5c1..00000000 --- a/server1/lib/mail/mail-generator.spec.js +++ /dev/null @@ -1,101 +0,0 @@ -import generate from '../../lib/mail/mail-generator' - -describe('Mail generator', function() { - let pageMock = { - findOne: sinon.stub().returnsThis() - } - - before(function() { - generate.__Rewire__('template', '{content}') - }) - - afterEach(function() { - generate.__ResetDependency__('Page') - }) - - after(function() { - generate.__ResetDependency__('template') - }) - - it('binds placeholders', async function() { - const page = { - body: 'From ', - subject: '' - } - - pageMock.lean = sinon.stub().returns(page) - generate.__Rewire__('Page', pageMock) - - const bindings = {organization: 'foodbank template'} - const result = await generate('', bindings) - - expect(result.html).to.equal(`From ${bindings.organization}`) - }) - - it('binds html placeholders', async function() { - const page = { - body: '', - subject: '' - } - - pageMock.lean = sinon.stub().returns(page) - generate.__Rewire__('Page', pageMock) - - const bindings = {passwordResetLink: 'example.com/reset'} - const result = await generate('', bindings) - - expect(result.html).to.equal(`Reset Password`) - }) - - it('binds subject placeholders', async function() { - const page = { - body: '

', - subject: '

email

' - } - - pageMock.lean = sinon.stub().returns(page) - generate.__Rewire__('Page', pageMock) - - const bindings = {organization: 'foodbank template'} - const result = await generate('', bindings) - - expect(result.subject).to.equal(`${bindings.organization} email`) - }) - - it('replaces tags with email suitable tags', async function() { - const page = { - body: '

Centered

', - subject: '' - } - - pageMock.lean = sinon.stub().returns(page) - generate.__Rewire__('Page', pageMock) - - const result = await generate('', {}) - expect(result.html).to.equal('
Centered
') - }) - - it('generates plain text version', async function() { - const page = { - body: ` - - - - -
-

-

Another paragraph

-

Linklogo

-
- `, - subject: '' - } - - pageMock.lean = sinon.stub().returns(page) - generate.__Rewire__('Page', pageMock) - - const bindings = {organization: 'foodbank template'} - const result = await generate('', bindings) - expect(result.text).to.equal(`${bindings.organization}\n\nAnother paragraph\n\nLink\n[foo.com]\n\n[logo]`) - }) -}) diff --git a/server1/lib/mail/mail-helpers.js b/server1/lib/mail/mail-helpers.js deleted file mode 100644 index 83192184..00000000 --- a/server1/lib/mail/mail-helpers.js +++ /dev/null @@ -1,123 +0,0 @@ -import {pageIdentifiers} from '../../../common/constants' -import mailGenerator from './mail-generator' -import sendEmail from '../../config/mailer' -import Settings from '../../models/settings' - -export default { - /** - * Send an email - * - * @param {string} toEmail - * @param {string} toName - * @param {string} identifier - * @param {object} bindings - */ - async send(toEmail, toName, identifier, bindings = null) { - const settings = await Settings.findOne().lean() - const mail = await mailGenerator(identifier, {...settings, ...bindings}) - if (!mail) return - - try { - await sendEmail(toEmail, toName, mail) - } catch (err) { - handleError(err) - } - }, - - /** - * Send an email for account status update - * - * @param {object} customer - */ - async sendStatus(customer) { - const {firstName, lastName, fullName, email} = customer - const date = customer.dateReceived.toDateString() - - if (customer.status === 'Accepted') { - await this.send( - email, - fullName, - pageIdentifiers.CUSTOMER_ACCEPTED, - {fullName, date} - ) - } else if (customer.status === 'Rejected'){ - await this.send( - email, - fullName, - pageIdentifiers.CUSTOMER_REJECTED, - {firstName, lastName, fullName, date} - ) - } - }, - - /** - * Send an email for account update - * - * @param {object} customer - */ - async sendUpdate(customer) { - const {firstName, lastName, fullName, email, id} = customer - await this.send( - email, - fullName, - pageIdentifiers.CUSTOMER_UPDATED, - {firstName, lastName, fullName, id} - ) - }, - - async sendPasswordReset(user, passwordResetLink) { - const {firstName, lastName, email} = user - const fullName = `${firstName} ${lastName}` - await this.send( - email, - fullName, - pageIdentifiers.PASSWORD_RESET, - {firstName, lastName, fullName, passwordResetLink} - ) - }, - - async sendPasswordGoogle(user) { - const {firstName, lastName, email} = user - const fullName = `${firstName} ${lastName}` - await this.send( - email, - fullName, - pageIdentifiers.PASSWORD_RESET_GOOGLE, - {firstName, lastName, fullName} - ) - }, - - async sendThanks(donation) { - const {donor} = donation - const {firstName, lastName, email} = donor - const fullName = `${firstName} ${lastName}` - await this.send( - email, - fullName, - pageIdentifiers.DONATION_RECEIVED, - {firstName, lastName, fullName} - ) - }, - - async sendReceipt(donation) { - const {donor, items} = donation - const {firstName, lastName, email} = donor - const fullName = `${firstName} ${lastName}` - await this.send( - email, - fullName, - pageIdentifiers.DONATION_RECEIPT, - {firstName, lastName, fullName, items} - ) - } -} - -function handleError(err) { - // suppress errors during testing - if (process.env.NODE_ENV === 'test') return - - console.error('email error', err) - console.error(err.response.body.errors) - - // throw err -} diff --git a/server1/lib/mapquest-client.js b/server1/lib/mapquest-client.js deleted file mode 100644 index 7a64c037..00000000 --- a/server1/lib/mapquest-client.js +++ /dev/null @@ -1,34 +0,0 @@ -import fetch from 'isomorphic-fetch' -import polyline from '@mapbox/polyline' - -import config from '../config' - -const baseUrl = 'http://open.mapquestapi.com/directions/v2' -const directionsUrl = `${baseUrl}/route?key=${config.mapquestKey}` -const optimizeUrl = `${baseUrl}/optimizedroute?key=${config.mapquestKey}` - -const defaultOptions = { - shapeFormat: 'cmp', // return polyline - generalize: 0 // max detail -} - -export const getDirections = (waypoints, options) => { - const body = JSON.stringify({ - locations: polylineToLatLngStrings(waypoints), - options: defaultOptions - }) - - const url = options.optimize ? optimizeUrl : directionsUrl - return fetch(url, { - method: 'POST', - body - }).then(res => res.json().then(json => ({ - geometry: json.route.shape.shapePoints, - waypoints: json.route.locationSequence, - legs: json.route.legs - }))) -} - -function polylineToLatLngStrings(line) { - return polyline.decode(line).map(points => points.join(',')) -} diff --git a/server1/lib/media-helpers.js b/server1/lib/media-helpers.js deleted file mode 100644 index 316d23af..00000000 --- a/server1/lib/media-helpers.js +++ /dev/null @@ -1,34 +0,0 @@ -import fs from 'fs' -import path from 'path' -import crypto from 'crypto' -import multer from 'multer' -import {last} from 'lodash' - -const mediaRoot = process.env.NODE_ENV === 'production' ? 'dist/client/media' : 'assets/media' - -const getPath = filename => { - return path.resolve(`${mediaRoot}/${filename}`) -} - -const generateUniqueFilename = filename => { - const ext = last(filename.split('.')) - const raw = crypto.pseudoRandomBytes(16) - return `${raw.toString('hex')}.${ext}` -} - -const storage = multer.diskStorage({ - destination: (req, file, cb) => { - cb(null, `${mediaRoot}`) - }, - filename: (req, file, cb) => { - const filename = generateUniqueFilename(file.originalname) - cb(null, filename) - } -}) - -export const upload = multer({storage}) - -export const deleteFile = filename => { - return fs.unlink(getPath(filename)) -} - diff --git a/server1/lib/media-helpers.spec.js b/server1/lib/media-helpers.spec.js deleted file mode 100644 index e26cd216..00000000 --- a/server1/lib/media-helpers.spec.js +++ /dev/null @@ -1,54 +0,0 @@ -import { - default as mediaHelpers -} from './media-helpers.js' - - -describe('Media helpers', function() { - context('node env = production', function() { - const mediaRoot = 'dist/client/media' - - beforeEach(function() { - mediaHelpers.__Rewire__('mediaRoot', mediaRoot) - }) - - afterEach(function() { - mediaHelpers.__ResetDependency__('mediaRoot') - }) - - describe('getPath', function() { - const getPath = mediaHelpers.__GetDependency__('getPath') - - it('should return pathname of production environment of type string given a valid filename', function() { - const filename = 'testfile' - const expected = mediaRoot + '/' + filename - const actual = getPath(filename) - - expect(actual).to.contain(expected) - }) - }) - }) - - context('node env = dev', function() { - const mediaRoot = 'assets/media' - - beforeEach(function() { - mediaHelpers.__Rewire__('mediaRoot', mediaRoot) - }) - - afterEach(function() { - mediaHelpers.__ResetDependency__('mediaRoot') - }) - - describe('getPath', function() { - const getPath = mediaHelpers.__GetDependency__('getPath') - - it('should return pathname of dev environment', function() { - const filename = 'testfile' - const expected = mediaRoot + '/' + filename - const actual = getPath(filename) - - expect(actual).to.contain(expected) - }) - }) - }) -}) \ No newline at end of file diff --git a/server1/lib/notification-sender.js b/server1/lib/notification-sender.js deleted file mode 100644 index b75282f6..00000000 --- a/server1/lib/notification-sender.js +++ /dev/null @@ -1,46 +0,0 @@ - -import User from '../models/user' -import Volunteer from '../models/volunteer' - -/** - * Set Notification - */ -// let notification = {message:'', url: '', date: date} -async function setNotification(id, notification, model){ - await model.findOneAndUpdate( - {_id: id}, - {$push: {notifications: notification}} - ) -} - -/** - * Search User and Set Notification to admin role - */ -export async function searchUserAndSetNotification(roleU, notification) { - const users = await User.find({}) - - users.forEach(function(user) { - user.roles.map(role => { - if (role === roleU){ - setNotification(user._id, notification, User) - } - }) - }) -} - -/** - * Search volunteer and Set Notification to admin role - */ -export async function searchVolunteerAndSetNotification(notification, customerId) { - const volunteers = await Volunteer.find({}) - - volunteers.forEach(function(volunteer) { - if(volunteer.customers.length > 0){ - volunteer.customers.map(customer => { - if (customer === customerId){ - setNotification(volunteer._id, notification, User) - } - }) - } - }) -} \ No newline at end of file diff --git a/server1/lib/notification-sender.spec.js b/server1/lib/notification-sender.spec.js deleted file mode 100644 index a172a679..00000000 --- a/server1/lib/notification-sender.spec.js +++ /dev/null @@ -1,155 +0,0 @@ -import { - searchUserAndSetNotification, - searchVolunteerAndSetNotification, - default as notification_sender -} from './notification-sender' -import User from '../models/user' -import Volunteer from '../models/volunteer' -import {clientRoles} from '../../common/constants' - -const sandbox = require('sinon').createSandbox() - -describe('Notification sender', function() { - describe('searchUserAndSetNotification', function() { - let dummy_users = null - let roleU = null - let notification = null - let setNotification = null - - beforeEach(function() { - dummy_users = [ - { - "_id" : 10003, - "firstName" : "Una", - "lastName" : "Deckow", - "roles" : [ - clientRoles.CUSTOMER - ], - "email" : "customer7@example.com" - }, - { - "_id" : 10022, - "firstName" : "dummy", - "lastName" : "dummy", - "roles" : [ - clientRoles.CUSTOMER - ], - "email" : "volunteer5@example.com" - }, - { - "_id" : 10015, - "firstName" : "Fernando", - "lastName" : "Cummerata", - "roles" : [ - clientRoles.CUSTOMER - ], - "email" : "customer25@example.com" - }] - roleU = clientRoles.CUSTOMER - notification = {} - - // stub setNotification function before every test to avoid running db update - setNotification = sandbox.stub() - notification_sender.__Rewire__('setNotification', setNotification) - }) - - afterEach(function() { - notification_sender.__ResetDependency__('setNotification') - sandbox.restore() - - }) - - it('should call setNotification 3 times with 3 matching users and 3 matching roles', async function() { - sandbox.stub(User, 'find').resolves(dummy_users) // stub User.find db query - await searchUserAndSetNotification(roleU, notification) - - sandbox.assert.callCount(setNotification, dummy_users.length) - }) - - it('should call setNotification 2 times with 3 matching users and 2 matching roles', async function() { - dummy_users[1].roles[0] = clientRoles.VOLUNTEER - sandbox.stub(User, 'find').resolves(dummy_users) // stub User.find db query - await searchUserAndSetNotification(roleU, notification) - - sandbox.assert.callCount(setNotification, 2) - }) - - it('should call setNotification 0 times with 0 matching users', async function() { - dummy_users = [] - sandbox.stub(User, 'find').resolves(dummy_users) // stub User.find db query - await searchUserAndSetNotification(roleU, notification) - - sandbox.assert.callCount(setNotification, 0) - }) - }) - - describe('searchVolunteerAndSetNotification', function() { - let dummy_volunteers = null - let setNotification = null - let notification = null - const customerId = 10051 - - beforeEach(function() { - dummy_volunteers = [{ - "_id": 10001, - "customers" : [ 10051, 10052, 10053 ], - "firstName" : "Annamae", - "lastName" : "Kuphal" - }, { - "_id": 10002, - "customers" : [ 10051, 10055, 10056 ], - "firstName" : "tester1", - "lastName" : "tester1" - }, { - "_id": 10003, - "customers" : [ 10051, 10058, 10059 ], - "firstName" : "tester2", - "lastName" : "tester2" - }] - notification = {} - - // stub setNotification function before every test to avoid running db update - setNotification = sandbox.stub() - notification_sender.__Rewire__('setNotification', setNotification) - }) - - afterEach(function() { - notification_sender.__ResetDependency__('setNotification') - sandbox.restore() - }) - - it('should call setNotification 3 times with 3 matching volunteers each with 1 matching customer id', async function() { - sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) // stub Volunteer find db query - await searchVolunteerAndSetNotification(notification, customerId) - - sandbox.assert.callCount(setNotification, 3) - }) - - it('should call setNotification 2 times with 3 matching volunteers with 2 volunteers having 1 matching customer id and 1 volunteer with 0 matching id', async function() { - dummy_volunteers[1].customers = [ 10052, 10055, 10056 ] - sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) - await searchVolunteerAndSetNotification(notification, customerId) - - sandbox.assert.callCount(setNotification, 2) - }) - - it('should call setNotification 0 times with 0 matching volunteers', async function() { - dummy_volunteers = [] - sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) - await searchVolunteerAndSetNotification(notification, customerId) - - sandbox.assert.callCount(setNotification, 0) - }) - - it('should call setNotification 0 times with 3 matching volunteers each with 0 customers', async function() { - dummy_volunteers[0].customers = [] - dummy_volunteers[1].customers = [] - dummy_volunteers[2].customers = [] - - sandbox.stub(Volunteer, 'find').resolves(dummy_volunteers) - await searchVolunteerAndSetNotification(notification, customerId) - - sandbox.assert.callCount(setNotification, 0) - }) - }) -}) \ No newline at end of file diff --git a/server1/lib/questionnaire-helpers.js b/server1/lib/questionnaire-helpers.js deleted file mode 100644 index 41d644f2..00000000 --- a/server1/lib/questionnaire-helpers.js +++ /dev/null @@ -1,48 +0,0 @@ -import {flatMap} from 'lodash' - -import Questionnaire from '../models/questionnaire' -import validate from '../../common/validators' - -async function getQuestionnaireFields (identifier, type) { - const questionnaire = await Questionnaire.findOne({identifier}) - if (!questionnaire) throw new Error('Invalid questionnaire') - - const allFields = flatMap(questionnaire.sections, section => section.fields) - return type ? allFields.filter(field => field.type === type) : allFields -} - -/** - * @export - * @param {string} identifier questionnaire identifier - * @param {[object]} modelFields the fields to select from - * @param {string=} type field type - * @returns {Promise<[object]>} - */ -export async function getFieldsByType(identifier, modelFields, type) { - const qFields = await getQuestionnaireFields(identifier, type) - - return qFields.reduce((acc, qField) => { - const modelField = modelFields.find(modelField => modelField.meta === qField._id) - if (modelField) return acc.concat(modelField) - return acc - }, []) -} - -export function getValidator(identifier) { - return async function validator(fields) { - const qFields = await getQuestionnaireFields(identifier) - - if (!fields.every(field => qFields.find(qField => qField._id === field.meta))) { - return false - } - - return qFields.reduce((valid, qField) => { - if (!valid) return false - - const field = fields.find(f => String(qField._id) === String(f.meta)) || {} - const error = validate(field.value, qField) - - return !Object.keys(error).length - }, true) - } -} diff --git a/server1/lib/questionnaire-helpers.spec.js b/server1/lib/questionnaire-helpers.spec.js deleted file mode 100644 index c2934e8c..00000000 --- a/server1/lib/questionnaire-helpers.spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import { - getFieldsByType, - getValidator, - default as qHelpers -} from './questionnaire-helpers' - -describe('Questionnaire helpers', function() { - const qFields = [ - {_id: 1, type: 'address'}, - {_id: 2, type: 'text'} - ] - - const clientFields = [ - {meta: 2, value: 'Some text'}, - {meta: 1, value: 'An address'} - ] - - describe('getFieldsByType', function() { - afterEach(function() { - qHelpers.__ResetDependency__('getQuestionnaireFields') - }) - - it('maps questionnaire fields to model fields', async function() { - const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) - qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) - - const fields = await getFieldsByType('qClient', clientFields) - - expect(fields).to.eql([clientFields[1], clientFields[0]]) - expect(getQuestionnaireFields).to.have.been.calledWith('qClient') - }) - - it('returns fields by type', async function() { - const getQuestionnaireFields = sinon.stub().returns(Promise.resolve([qFields[0]])) - qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) - - const fields = await getFieldsByType('qClient', clientFields, 'address') - - expect(fields).to.eql([clientFields[1]]) - expect(getQuestionnaireFields).to.have.been.calledWith('qClient', 'address') - }) - }) - - describe('getValidator', function() { - afterEach(function() { - qHelpers.__ResetDependency__('getQuestionnaireFields') - qHelpers.__ResetDependency__('validate') - }) - - it('validator passes valid fields', async function() { - const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) - const validate = sinon.stub().returns({}) - - qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) - qHelpers.__Rewire__('validate', validate) - - const validator = getValidator('qClient') - const valid = await validator(clientFields) - - expect(valid).to.be.true - expect(getQuestionnaireFields).to.have.been.calledWith('qClient') - expect(validate).to.have.been.calledWith('Some text', qFields[1]) - expect(validate).to.have.been.calledWith('An address', qFields[0]) - }) - - it('validator fails with undeclared fields', async function() { - const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) - qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) - - const validator = getValidator('qClient') - const valid = await validator([ - ...clientFields, - {meta: 3, value: 'Undeclared field'} - ]) - - expect(valid).to.be.false - expect(getQuestionnaireFields).to.have.been.calledWith('qClient') - }) - - it('validator fails if validation function returns errors', async function() { - const getQuestionnaireFields = sinon.stub().returns(Promise.resolve(qFields)) - const validate = sinon.stub() - .onFirstCall().returns({}) - .onSecondCall().returns({'2': 'Required'}) - - qHelpers.__Rewire__('getQuestionnaireFields', getQuestionnaireFields) - qHelpers.__Rewire__('validate', validate) - - const validator = getValidator('qClient') - const valid = await validator(clientFields) - - expect(valid).to.be.false - expect(getQuestionnaireFields).to.have.been.calledWith('qClient') - expect(validate).to.have.been.calledWith('Some text', qFields[1]) - expect(validate).to.have.been.calledWith('An address', qFields[0]) - }) - }) -}) diff --git a/server1/lib/seed/address-generator.js b/server1/lib/seed/address-generator.js deleted file mode 100644 index 801be4c9..00000000 --- a/server1/lib/seed/address-generator.js +++ /dev/null @@ -1,34 +0,0 @@ -import {readFileSync} from 'fs' -import {range} from 'lodash' - -export default class AddressGenerator { - constructor() { - try { - const file = readFileSync(`${__dirname}/addresses.csv`).toString() - this.lines = file.split('\n').filter(x => x) - } catch (err) { - this.lines = [] - } - - this.indices = range(this.lines.length) - } - - getOne() { - if (!this.indices.length) throw new Error('No seed addresses remaining') - - const i = Math.floor(Math.random() * this.indices.length) - const index = this.indices[i] - this.indices.splice(i, 1) - - const [lat, lng, street, city, state, zip] = this.lines[index].split(',') - - return { - lat, - lng, - street, - city, - state, - zip - } - } -} diff --git a/server1/lib/seed/address-generator.spec.js b/server1/lib/seed/address-generator.spec.js deleted file mode 100644 index 3133287f..00000000 --- a/server1/lib/seed/address-generator.spec.js +++ /dev/null @@ -1,46 +0,0 @@ -import AddressGenerator from './address-generator' - -describe('Address generator', function() { - afterEach(function() { - AddressGenerator.__ResetDependency__('readFileSync') - }) - - it('pulls addresses from a file', function() { - const addresses = '1,1,street,city,state,zip\n' - const readFileSync = sinon.stub().returns(addresses) - AddressGenerator.__Rewire__('readFileSync', readFileSync) - - const generator = new AddressGenerator - const address1 = generator.getOne() - expect(address1).to.have.property('lat', '1') - expect(address1).to.have.property('lng', '1') - expect(address1).to.have.property('street', 'street') - expect(address1).to.have.property('city', 'city') - expect(address1).to.have.property('state', 'state') - expect(address1).to.have.property('zip', 'zip') - }) - - it('gets unique addresses', function() { - const addresses = '1,1,str,c,s,z\n2,2,str,c,s,z\n3,3,str,c,s,z\n' - const readFileSync = sinon.stub().returns(addresses) - AddressGenerator.__Rewire__('readFileSync', readFileSync) - - const generator = new AddressGenerator - const address1 = generator.getOne() - const address2 = generator.getOne() - const address3 = generator.getOne() - expect(address1.lat).to.not.equal(address2.lat) - expect(address2.lat).to.not.equal(address3.lat) - expect(address1.lat).to.not.equal(address3.lat) - }) - - it('throws if more addresses are requested', function() { - const addresses = '1,1,street,city,state,zip' - const readFileSync = sinon.stub().returns(addresses) - AddressGenerator.__Rewire__('readFileSync', readFileSync) - - const generator = new AddressGenerator - generator.getOne() - expect(generator.getOne).to.throw() - }) -}) diff --git a/server1/lib/seed/addresses.csv b/server1/lib/seed/addresses.csv deleted file mode 100644 index a3ec4679..00000000 --- a/server1/lib/seed/addresses.csv +++ /dev/null @@ -1,64 +0,0 @@ -40.747495,-73.995125,227 W 27th St,New York,NY,10001,USA -40.747618,-73.997635,307 W 26th St,New York,NY,10001,USA -40.745608,-73.998517,309 W 23rd St,New York,NY,10011,USA -40.749241,-73.998775,296 9th Ave,New York,NY,10001,USA -40.750022,-74.002787,287 10th Ave,New York,NY,10001,USA -40.748038,-74.008516,62 Chelsea Piers,New York,NY,10011,USA -40.745494,-74.006811,512 W 19th St,New York,NY,10011,USA -40.741243,-74.006714,420 W 14th St,New York,NY,10014,USA -40.737700,-74.008072,775 Washington St,New York,NY,10014,USA -40.732529,-74.008158,154 Christopher St #1E,New York,NY,10014,USA -40.733448,-74.006130,510 Hudson St,New York,NY,10014,USA -40.731277,-74.005347,38 Commerce St,New York,NY,10014,USA -40.723496,-74.006505,75 Varick St,New York,NY,10013,USA -40.723984,-74.009971,135 Watts St,New York,NY,10013,USA -40.720783,-74.002166,19 Mercer St,New York,NY,10013,USA -40.724173,-74.000240,91 Greene St,New York,NY,10012,USA -40.720401,-73.995266,151 Elizabeth St,New York,NY,10012,USA -40.719861,-73.977384,442 E Houston St,New York,NY,10002,USA -40.724983,-73.974702,454 E 10th St,New York,NY,10009,USA -40.729453,-73.978680,538 E 14th St,New York,NY,10009,USA -40.735168,-73.979420,348 1st Avenue,New York,NY,10010,USA -40.737940,-73.978240,345 E 24th St,New York,NY,10010,USA -40.738908,-73.980826,241 E 24th St,New York,NY,10010,USA -40.743818,-73.979914,446 3rd Ave,New York,NY,10016,USA -40.746858,-73.980161,115 E 34th St,New York,NY,10016,USA -40.745890,-73.981534,1 Park Ave,New York,NY,10016,USA -40.751767,-73.981706,10 East 40th Street,New York,NY,10016,USA -40.751214,-73.986351,980 6th Ave,New York,NY,10018,USA -40.746004,-73.991694,115 W 27th St,New York,NY,10001,USA -40.756392,-73.988867,234 W 42nd St,New York,NY,10036,USA -40.762545,-73.985101,222 W 51st St,New York,NY,10019,USA -40.766320,-73.979045,180 Central Park S,New York,NY,10019,USA -40.780536,-73.980984,2124 Broadway,New York,NY,10023,USA -40.795024,-73.966351,808 Columbus Ave,New York,NY,10025,USA -40.802486,-73.967385,2790 Broadway,New York,NY,10025,USA -40.807056,-73.960296,435 W 116th St,New York,NY,10027,USA -40.792512,-73.951958,1220 5th Ave,New York,NY,10029,USA -40.810013,-73.950062,253 W 125th St,New York,NY,10027,USA -40.815211,-73.944339,200 W 135th St,New York,NY,10030,USA -40.817894,-73.954161,530 W 133rd St,New York,NY,10027,USA -40.833688,-73.946467,3755 Broadway,New York,NY,10032,USA -40.846498,-73.937907,4140 Broadway,New York,NY,10033,USA -40.745654,-73.948559,4602 21st St,Long Island City,NY,11101,USA -40.747257,-73.944427,1 Ct Square W,Long Island City,NY,11120,USA -40.748555,-73.948790,11-11 44th Dr,Queens,NY,11101,USA -40.747770,-73.943793,2 Ct Square W,Long Island City,NY,11101,USA -40.752057,-73.947465,11-01 43rd Ave,Long Island City,NY,11101,USA -40.752679,-73.940352,23-10 41st Ave,Long Island City,NY,11101,USA -40.744017,-73.935038,45-35 Van Dam St,Long Island City,NY,11101,USA -40.728092,-73.957760,52 Noble St,Brooklyn,NY,11222,USA -40.722289,-73.957854,74 Wythe Ave,Brooklyn,NY,11249,USA -40.719113,-73.961770,66 N 6th St,Brooklyn,NY,11211,USA -40.714137,-73.959841,217 Grand St,Brooklyn,NY,11211,USA -40.710256,-73.941124,87 Ten Eyck St,Brooklyn,NY,11206,USA -40.704675,-73.945514,29 Leonard St,Brooklyn,NY,11206,USA -40.752712,-73.927583,37-6 36th St,Long Island City,NY,11101,USA -40.755819,-73.941062,39-34 21st St,Long Island City,NY,11101,USA -40.694789,-73.992446,128 Pierrepont St,Brooklyn,NY,11201,USA -40.690825,-73.992005,106 Court St,Brooklyn,NY,11201,USA -40.679235,-74.012139,159 Pioneer St,Brooklyn,NY,11231,USA -40.671777,-73.995093,118 2nd Ave,Brooklyn,NY,11215,USA -40.679101,-73.983546,622 Degraw St,Brooklyn,NY,11217,USA -40.668588,-73.984398,357 9th St,Brooklyn,NY,11215,USA -40.670865,-73.974593,274 Garfield Pl,Brooklyn,NY,11215,USA diff --git a/server1/lib/seed/index.js b/server1/lib/seed/index.js deleted file mode 100644 index 89525107..00000000 --- a/server1/lib/seed/index.js +++ /dev/null @@ -1,164 +0,0 @@ -import mongoose from 'mongoose' -import {range, values} from 'lodash' -import faker from 'faker' - -import { - foodFields, - getSettingsFields, - pages, - customerQuestionnaire, - donorQuestionnaire, - volunteerQuestionnaire -} from './seed-data' -import seedClients from './seed-clients' -import AddressGenerator from './address-generator' -import { - ADMIN_ROLE, - clientRoles, - volunteerRoles, - modelTypes -} from '../../../common/constants' - -const addressGenerator = new AddressGenerator - -const User = mongoose.model(modelTypes.USER) -const Customer = mongoose.model(modelTypes.CUSTOMER) -const Donor = mongoose.model(modelTypes.DONOR) -const Donation = mongoose.model(modelTypes.DONATION) -const Volunteer = mongoose.model(modelTypes.VOLUNTEER) -const Questionnaire = mongoose.model(modelTypes.QUESTIONNAIRE) -const Food = mongoose.model(modelTypes.FOOD) -const Settings = mongoose.model(modelTypes.SETTINGS) -const Page = mongoose.model(modelTypes.PAGE) -const Media = mongoose.model(modelTypes.MEDIA) - -/** - * populate empty collections with seed data - * - * @param {string} env the environment - * @param {boolean} replace replace all except admin user with fresh data - * @param {boolean} replaceAdmin also replace admin user - * @return {Promise} - */ -export default async function seed(env, replace, replaceAdmin = false) { - if (env === 'test') return - if (replace) await clearDb(replaceAdmin) - seedDb(env) -} - -async function clearDb(replaceAdmin) { - if (replaceAdmin) await User.find().remove() - else await User.find({roles: {$in: values(clientRoles)}}).remove() - - await Promise.all([ - Customer.find().remove(), - Donation.find().remove(), - Donor.find().remove(), - Volunteer.find().remove(), - Food.find().remove(), - Page.find().remove(), - Questionnaire.find().remove(), - Settings.find().remove(), - Media.find().remove() - ]) -} - -async function seedDb(env) { - await seedAdmin() - await seedQuestionnaires() - await seedSettings() - await seedPages() - await Media.create({}) - - // for development also seed clients and foods - if (env !== 'production') { - await seedUsers() - await seedFoods() - await seedClients(addressGenerator) - } -} - -async function seedAdmin() { - const adminCount = await User.count({roles: ADMIN_ROLE}) - if (!adminCount) { - const adminUser = { - firstName: 'admin', - lastName: 'user', - displayName: 'admin', - roles: [ADMIN_ROLE], - email: `admin@example.com`, - password: 'password', - provider: 'local' - } - await User.create(adminUser) - } -} - -async function seedUsers() { - const clientCount = await User.count({roles: {$in: values(clientRoles)}}) - if (clientCount) return - - const customers = range(45).map(i => - createTestUser(`customer ${i + 1}`, clientRoles.CUSTOMER)) - const volunteers = range(8).map(i => - createTestUser(`volunteer ${i + 1}`, clientRoles.VOLUNTEER)) - const donors = range(5).map(i => - createTestUser(`donor ${i + 1}`, clientRoles.DONOR)) - const drivers = range(5).map(i => - createTestUser(`driver ${i + 1}`, clientRoles.VOLUNTEER)) - - await User.create([ - ...customers, - ...volunteers, - ...donors, - ...drivers - ]) -} - -async function seedFoods() { - const count = await Food.count() - if (count) return - - await Food.insertMany(foodFields) -} - -async function seedQuestionnaires() { - const count = await Questionnaire.count() - if (count) return - - await Questionnaire.insertMany([ - customerQuestionnaire, - donorQuestionnaire, - volunteerQuestionnaire - ]) -} - -async function seedSettings() { - const count = await Settings.count() - if (count) return - - await Settings.create(getSettingsFields(addressGenerator)) -} - -async function seedPages() { - const count = await Page.count() - if (count) return - - await Page.insertMany(pages) -} - -// Helpers -function createTestUser(name, role) { - const names = name.split(' ') - if (names.length !== 2) throw new Error('name should be "firstname lastname"') - return { - firstName: faker.name.firstName(), - lastName: faker.name.lastName(), - roles: names[0] === 'driver' ? - [volunteerRoles.DRIVER, role] : - [role], - email: `${names.join('')}@example.com`, - password: 'password', - provider: 'local' - } -} diff --git a/server1/lib/seed/seed-clients.js b/server1/lib/seed/seed-clients.js deleted file mode 100644 index 44f926e2..00000000 --- a/server1/lib/seed/seed-clients.js +++ /dev/null @@ -1,234 +0,0 @@ -import mongoose from 'mongoose' -import faker from 'faker' -import { - extend, - intersection, - map, - random, - range -} from 'lodash' - -import { - clientRoles, - volunteerRoles, - fieldTypes, - modelTypes, - questionnaireIdentifiers -} from '../../../common/constants' - -const User = mongoose.model(modelTypes.USER) -const Donation = mongoose.model(modelTypes.DONATION) -const Food = mongoose.model(modelTypes.FOOD) -const Questionnaire = mongoose.model(modelTypes.QUESTIONNAIRE) - -export default async function seedClients(addressGenerator) { - await Promise.all(map(clientRoles, async (role, key) => { - const Model = mongoose.model(modelTypes[key]) - const count = await Model.count() - if (count) return - - const users = await User.find({roles: role}) - .select('-__v') - - const clients = await Promise.all(users.map(user => { - const client = new Model(user.toObject()) - return seedClientFields(client, user, addressGenerator) - })) - - await Model.insertMany(clients) - })) -} - -/** - * Seed client fields - * - * @param {mongoose.Document} client - * @returns {Promise} - */ -async function seedClientFields(client, user, addressGenerator) { - const address = addressGenerator.getOne() - const dynamicFields = await seedDynamicFields(client, user, address) - const staticFields = await seedStaticFields(client, user, dynamicFields[0].value, address) - - return extend(client, staticFields, {fields: dynamicFields}) -} - -/** - * Seed client dynamic fields - * - * @param {object} client - * @returns {array} - */ -async function seedDynamicFields(client, user, address) { - const roleKey = Object.keys(clientRoles).find(role => - intersection(user.roles, [clientRoles[role]]).length) - - const identifier = questionnaireIdentifiers[roleKey] - const questionnaire = await Questionnaire - .findOne({identifier}) - .lean() - - const generalInfoFields = questionnaire.sections[0].fields - - const addressFields = generalInfoFields.filter(field => - field.type === fieldTypes.ADDRESS) - const dateOfBirthField = generalInfoFields.find(field => - field.label === 'Date of Birth') - - let fields = [{ - meta: addressFields[0], - value: address.street - }, { - meta: addressFields[1], - value: address.city - }, { - meta: addressFields[2], - value: address.state - }, { - meta: addressFields[3], - value: address.zip - }] - - if (dateOfBirthField) { - fields.unshift({ - meta: dateOfBirthField, - value: faker.date.between('1950-01-01', '2000-12-31').toISOString() - }) - } - - return fields -} - -/** - * Seed client static fields - * - * @param {object} client - * @param {string} dateOfBirth - * @returns {object} - */ -async function seedStaticFields(client, user, dateOfBirth, address) { - const {lat, lng} = address - let properties = {} - - if (user.roles.find(role => role === clientRoles.VOLUNTEER)) { - properties.user = user._id - - properties.status = randomIn({ - 'Active': 0.8, - 'Inactive': 0.2 - }) - - if (user.roles.find(r => r === volunteerRoles.DRIVER)) { - properties.status = 'Active' - properties.location = {lat, lng} - } - } - - if (user.roles.find(role => role === clientRoles.DONOR)) { - properties = { - ...properties, - ...await populateDonorFields(client) - } - } - - if (user.roles.find(role => role === clientRoles.CUSTOMER)) { - properties = { - ...properties, - ...await populateCustomerFields(client, dateOfBirth, address) - } - } - - return properties -} - -/** - * return extra fields specific to donors - * - * @param {object} client - * @returns {object} - */ -async function populateDonorFields(client) { - const donations = range(random(3, 30)).map(() => { - const items = range(random(1, 4)).map(() => ({ - name: randomIn({ - 'Cash': 0.75, - 'Food': 0.25, - }), - value: random(1, 50) - })) - - const total = items.reduce((acc, item) => acc + item.value, 0) - const dateReceived = faker.date.past(3).toISOString() - const approved = Boolean(Math.round(Math.random())) - - return { - donor: client._id, - dateReceived, - items, - total, - approved, - ...(approved ? {dateIssued: Date.now()} : {}) - } - }) - - return {donations: await Donation.create(donations)} -} - -/** - * return extra fields specific to customers - * - * @param {object} client - * @param {string} dateOfBirth - * @returns {object} - */ -async function populateCustomerFields(client, dateOfBirth, address) { - // get a random sample of foods for foodPreferences - const foodPreferences = (await Food.aggregate([ - {$unwind: '$items'}, - {$sample: {size: random(4, 10)}} - ])).map(res => res.items._id) - - const household = [{ - name: `${client.firstName} ${client.lastName}`, - relationship: 'Applicant', - dateOfBirth - }] - - const {lat, lng} = address - const location = {lat, lng} - - const status = randomIn({ - 'Accepted': 0.8, - 'Rejected': 0.1, - 'Pending': 0.05, - 'Inactive': 0.05 - }) - - return { - foodPreferences, - household, - location, - status - } -} - -/** - * return a random key with given probability - * - * @param {object} choices object of keys with probability of returning each key - * @returns {string} the key - */ -function randomIn(choices) { - const keys = Object.keys(choices) - const rand = Math.random() - - return keys.reduce((a, key) => { - // already have a key just return it - if (a.key) return a - // add probability for this key to accumulator - a.acc += choices[key] - - if (rand < a.acc) return {key} - else return a - }, {acc: 0}).key -} diff --git a/server1/lib/seed/seed-data.js b/server1/lib/seed/seed-data.js deleted file mode 100644 index ab412514..00000000 --- a/server1/lib/seed/seed-data.js +++ /dev/null @@ -1,279 +0,0 @@ -import {utc} from 'moment' -import faker from 'faker' -import {v4} from 'uuid' - -import config from '../../config' -import { - fieldTypes, - widgetTypes, - pageTypes, - pageIdentifiers, - questionnaireIdentifiers -} from '../../../common/constants' - -const addRandomDate = item => - ({...item, startDate: utc(faker.date.past(2)).startOf('isoWeek')}) -export const foodFields = [{ - category: 'Cheese', - items: [ - {name: 'Cheddar', quantity: 20, frequency: 1}, - {name: 'Mozarella', quantity: 35, frequency: 2}, - {name: 'Brie', quantity: 30, frequency: 4}, - {name: 'Emmental', quantity: 20, frequency: 1}, - {name: 'Monterey Jack', quantity: 20, frequency: 1}, - {name: 'Stilton', quantity: 20, frequency: 1}, - {name: 'Camembert', quantity: 20, frequency: 1}, - {name: 'Port Salut', quantity: 20, frequency: 1}, - {name: 'Gorgonzola', quantity: 20, frequency: 1}, - ].map(addRandomDate) -}, { - category: 'Pasta, rice etc', - items: [ - {name: 'Penne pasta', quantity: 50, frequency: 1}, - {name: 'Basmati rice', quantity: 40, frequency: 1}, - {name: 'Fusilli pasta', quantity: 45, frequency: 2}, - {name: 'Couscous', quantity: 40, frequency: 1}, - {name: 'Spagetti', quantity: 50, frequency: 1}, - {name: 'Jasmine rice', quantity: 50, frequency: 1}, - ].map(addRandomDate) -}, { - category: 'Meat', - items: [ - {name: 'Beef mince', quantity: 25, frequency: 2}, - {name: 'Chicken breast', quantity: 15, frequency: 2}, - {name: 'Pork chop', quantity: 20, frequency: 2}, - {name: 'T-Bone steak', quantity: 25, frequency: 2}, - {name: 'Chicken thighs', quantity: 20, frequency: 2}, - {name: 'Bacon', quantity: 20, frequency: 2}, - {name: 'Salami', quantity: 15, frequency: 2}, - {name: 'Pepperoni', quantity: 15, frequency: 2}, - ].map(addRandomDate) -}, { - category: 'Vegetables', - items: [ - {name: 'Carrots', quantity: 30, frequency: 1}, - {name: 'Onions', quantity: 25, frequency: 2}, - {name: 'Squash', quantity: 25, frequency: 4}, - {name: 'Leek', quantity: 30, frequency: 1}, - {name: 'Potatoes', quantity: 25, frequency: 2}, - {name: 'Turnips', quantity: 25, frequency: 4}, - {name: 'Cauliflower', quantity: 30, frequency: 1}, - {name: 'Broccoli', quantity: 25, frequency: 2}, - {name: 'Peas', quantity: 25, frequency: 4} - ].map(addRandomDate) -}, { - category: 'Milk', - items: [ - {name: 'Whole milk', quantity: 15, frequency: 1}, - {name: 'Semi-skimmed milk', quantity: 15, frequency: 1} - ].map(addRandomDate) -}, { - category: 'Snacks', - items: [ - {name: 'Snickers', quantity: 20, frequency: 3}, - {name: 'Mars bar', quantity: 20, frequency: 3}, - {name: 'Potato chips', quantity: 20, frequency: 3}, - ].map(addRandomDate) -}] - -export const getSettingsFields = () => { - const [lat, lng, street, city, zip] = - [40.720230, -73.984029, '176 Stanton St', 'New York', 'NY 10002'] - - return { - organization: 'Pantry for Good', - url: 'https://github.com/FreeCodeCamp/pantry-for-good', - address: `${street} ${city} ${zip}`, - clientIntakeNumber: faker.phone.phoneNumber(), - supportNumber: faker.phone.phoneNumber(), - location: {lat, lng}, - gmapsApiKey: config.gmapsApiKey, - } -} - -export const pages = [{ - identifier: pageIdentifiers.HOME, - title: 'Home', - body: '

is an application for managing food bank operations created as part of the Open Source for Good project at freeCodeCamp.

The content of this page and the site branding can be changed by an administrator on the settings page.

' -}, { - identifier: pageIdentifiers.CUSTOMER, - title: 'Customers', - body: '

Please carefully read the following information:

  • Boxes will be delivered by our volunteers every Wednesday evening between 8:00PM and 11:00PM.
  • Boxes will be left at your doorstep. Volunteers may knock on your door and quickly leave to keep anonymity. During holidays, or other special times, including severe weather conditions, time and day of delivery may change without notice.
  • We ask that you leave the empty box outside your door the following week, so it can be picked up by the volunteers and reused.
  • Volunteers do their best to check food items for expiry dates, however mistakes do occur. Please use your own judgment and discretion before consuming any food or contact the manufacturer directly if you have any concerns. If you find an item that is expired, you may throw it away. If there are items that you don\'t use, please leave them in the box, and we will make a note so that you will not receive them again.
  • If there is a change in your financial situation and you no longer need our assistance, if you will be away for a certain period, if you have a change in contact information, or any other change, please inform us promptly.
' -}, { - identifier: pageIdentifiers.DONOR, - title: 'Donors', - body: '' -}, { - identifier: pageIdentifiers.VOLUNTEER, - title: 'Volunteers', - body: '

In the past year, over (amount) pounds of food was distributed on a weekly basis by our team of dedicated volunteers. Your assistance helps us respond to the ever-growing demand of our community. Thank you for taking the opportunity to help turn lives around.

With your help, we are able to support the less fortunate families in our community, by providing them with nutritious food and energy to grow, learn, work, and give them hope for a better and brighter future.

' -}, { - identifier: pageIdentifiers.CUSTOMER_ACCEPTED, - title: 'Customer Accepted', - type: pageTypes.EMAIL, - subject: '

Your application was accepted

', - body: `

Dear ,

-

We are pleased to inform you that based on the information provided in your application, you are eligible to receive our assistance.

-

Sincerely,

-

The Team

-

- is committed to protecting your personal information by following responsible information handling practices and in keeping with privacy laws. All information remains strictly confidential as outlined by 's Privacy Policy that can be accessed at . -

-

If you have any questions or concerns, feel free to contact us.

` -}, { - identifier: pageIdentifiers.CUSTOMER_REJECTED, - title: 'Customer Rejected', - type: pageTypes.EMAIL, - subject: '

Your application was rejected

', - body: `

Dear ,

-

We regret to inform you that based on the information provided in your application, you are not eligible to receive our assistance.

-

If your circumstances change please edit your application and we will reevaluate your request

-

Sincerely,

-

The Team

-

- is committed to protecting your personal information by following responsible information handling practices and in keeping with privacy laws. All information remains strictly confidential as outlined by 's Privacy Policy that can be accessed at . -

-

If you have any questions or concerns, feel free to contact us.

` -}, { - identifier: pageIdentifiers.CUSTOMER_APPLIED, - title: 'Customer Application', - type: pageTypes.EMAIL, - subject: '

A new customer has applied

', - body: `

Dear Admin,

-

A new client application has been created.

-

Please visit and login with your admin credentials.

` -}, { - identifier: pageIdentifiers.CUSTOMER_UPDATED, - title: 'Customer Updated', - type: pageTypes.EMAIL, - subject: '

A customers details have changed

', - body: `

Dear Admin,

-

This is a notification email to notify you that either with application ID -{id}} or an admin has changed his/her personal information.

-

Please visit and login with your admin credentials.

` -}, { - identifier: pageIdentifiers.PASSWORD_RESET, - title: 'Password Reset', - type: pageTypes.EMAIL, - subject: '

Password Reset

', - body: `

Dear ,

-

You have requested to have your password reset for your account at

-

Please visit this url to reset your password:

-

-If you didn't make this request, you can ignore this email. -

The support team

` -}, { - identifier: pageIdentifiers.DONATION_RECEIVED, - title: 'Donation Received', - type: pageTypes.EMAIL, - subject: '

Thank You for Your Donation to

', - body: `

Dear ,

-

Thank you for your generous donation.

-

Sincerely,

-

The Team

-

- is committed to protecting your personal information by following responsible information handling practices and in keeping with privacy laws. All information remains strictly confidential as outlined by 's Privacy Policy that can be accessed at . -

-

If you have any questions or concerns, feel free to contact us.

-` -}, { - identifier: pageIdentifiers.DONATION_RECEIPT, - title: 'Donation Receipt', - type: pageTypes.EMAIL, - subject: '

Receipt for Your Donation to

', - body: `

Dear ,

-

Thanks for your donations to:

-

-` -}, { - identifier: pageIdentifiers.PASSWORD_RESET_GOOGLE, - title: 'Password Reset Google', - type: pageTypes.EMAIL, - subject: '

Password Reset

', - body: `

Dear ,

-

You have requested to have your password reset for your account at

-

you appear to have signed up with google, try logging in with google. If you have any problems give us a call to the

-If you didn't make this request, you can ignore this email. -

The support team

-` -}] - -const commonFields = [ - {type: fieldTypes.ADDRESS, label: 'Street', required: true}, - {type: fieldTypes.ADDRESS, label: 'Town/City', required: true}, - {type: fieldTypes.ADDRESS, label: 'State/Province', required: true}, - {type: fieldTypes.ADDRESS, label: 'Zip/Post Code', required: true}, - {type: fieldTypes.TEXT, label: 'Telephone Number'} -].map(field => ({ - ...field, - _id: v4() -})) - -export const customerQuestionnaire = { - name: 'Customer Application', - identifier: questionnaireIdentifiers.CUSTOMER, - sections: [{ - name: 'General Info', - fields: [ - ...commonFields, - {type: fieldTypes.DATE, label: 'Date of Birth', required: true}, - {type: fieldTypes.RADIO, choices: 'Male, Female', label: 'Gender'}, - {type: fieldTypes.RADIO, choices: 'Rental, Own, Subsidized, Other', label: 'Accommodation Type'}, - {type:fieldTypes.RADIO, choices: 'Phone, Email', label: 'Best way to contact'}, - {type: fieldTypes.TEXTAREA, label: 'Delivery instructions'}, - {type: fieldTypes.TEXTAREA, label: 'Other organizations you are currently receiving assistance from'} - ] - }, { - name: 'Employment', - fields: [ - {type: fieldTypes.RADIO, choices: 'Employed, Unemployed, Laid-off, Retired, Disabled, Student', label: 'Current work status'}, - {type: fieldTypes.TEXT, label: 'Hours per week'}, - {type: fieldTypes.TEXT, label: 'Job title'}, - ] - }, { - name: 'Food Preferences', - fields: [ - {type: widgetTypes.FOOD_PREFERENCES, label: 'Please select the foods you are interested in'}, - {type: fieldTypes.TEXTAREA, label: 'Please list any food allergies or sensitivities'}, - {type: fieldTypes.TEXTAREA, label: 'Other food preferences'}, - ] - // }, { - // name: 'Financial Assessment', - // fields: [ - // {label:'Monthly Gross Income', rows: 'Employment Income, Employment Insurance Benefits, Social Assistance, Spousal/Child Support, Self Employment, Pension Income (eg. Employer Plan), Disability Income, Pension Plan, Child Tax Benefits, Income from Rental Property, Severance/Termination Pay, Other income not listed above', columns: 'Self, Other', type: 'table'}, - // {label: 'Monthly Living Expenses', rows: 'Rent mortgage or room and board, Food, Utilities, Transportation, Dependant Care (eg. day care), Disability Needs, Spousal/Child support, Loans, Leases, Insurance, Credit card debt, Property taxes, Other costs not listed above', columns: 'Household', type: 'table'} - // ] - }, { - name: 'Household', - fields: [ - {type: widgetTypes.HOUSEHOLD, label: 'Household'} - ] - }] -} - -export const donorQuestionnaire = { - name: 'Donor Application', - identifier: questionnaireIdentifiers.DONOR, - sections: [{ - name: 'General Info', - fields: [ - ...commonFields, - {type: fieldTypes.TEXTAREA, label: 'How did you hear about us?'} - ] - }] -} - -export const volunteerQuestionnaire = { - name: 'Volunteer Application', - identifier: questionnaireIdentifiers.VOLUNTEER, - sections: [{ - name: 'General Info', - fields: [ - ...commonFields, - {type: fieldTypes.DATE, label: 'Date of Birth', required:true}, - {type: fieldTypes.RADIO, choices: 'Phone, Email', label: 'Best way to contact'}, - {type: fieldTypes.TEXTAREA, label: 'How did you hear about us?'}, - {type: fieldTypes.TEXTAREA, label: 'Why do you want to volunteer with us?'} - ] - }] -} diff --git a/server1/lib/update-linked-fields.js b/server1/lib/update-linked-fields.js deleted file mode 100644 index 29179ac9..00000000 --- a/server1/lib/update-linked-fields.js +++ /dev/null @@ -1,38 +0,0 @@ -import Customer from '../models/customer' -import Volunteer from '../models/volunteer' -import Donor from '../models/donor' -import {clientRoles} from '../../common/constants' - -/** -* Update fields of address -*/ -async function updatedModel(Model, reqBodyFields, userId) { - const model = await Model.findById(userId) - if (model != undefined && model != null && model != {}) { - let modelFields = model.fields - for (let j = 0; j < modelFields.length; j++) { - for (let k = 0; k < reqBodyFields.length; k++) { - if (modelFields[j].meta == reqBodyFields[k].meta) - modelFields[j].value = reqBodyFields[k].value - } - } - await Model.findByIdAndUpdate(userId, {fields: modelFields}) - } -} - -/** -* Update All fields of address -*/ -export function updateFields(clientRole, reqBodyFields, userId) { - //Updating address of the client type role if exists - if (clientRole == clientRoles.CUSTOMER) { - updatedModel(Volunteer, reqBodyFields, userId) - updatedModel(Donor, reqBodyFields, userId) - } else if (clientRole == clientRoles.DONOR) { - updatedModel(Volunteer, reqBodyFields, userId) - updatedModel(Customer, reqBodyFields, userId) - } else if (clientRole == clientRoles.VOLUNTEER) { - updatedModel(Donor, reqBodyFields, userId) - updatedModel(Customer, reqBodyFields, userId) - } -} diff --git a/server1/lib/websocket-middleware.js b/server1/lib/websocket-middleware.js deleted file mode 100644 index 7d755aab..00000000 --- a/server1/lib/websocket-middleware.js +++ /dev/null @@ -1,51 +0,0 @@ -import {normalize, schema} from 'normalizr' -import {intersection} from 'lodash' - -let users = [] - -export default sync => { - if (!sync.syncTo) sync.syncTo = [] - if (typeof(sync.type) !== 'string') - throw new Error('action type must be a string') - - return (req, res, next) => { - const oldJson = res.json - - res.json = json => { - oldJson.call(res, json) - if (res.statusCode !== 200) return - - json = JSON.parse(JSON.stringify(json)) - let response - - if (!sync.schema) { - response = json - } else if (Object.keys(schema).some(type => sync.schema instanceof schema[type])) { - // schema is a normalizr schema - response = normalize(json, sync.schema) - } else { - // schema is a map of entity types to schemas - const entities = Object.assign({}, - ...Object.keys(sync.schema).map(k => ({ - [k]: normalize(json[k], sync.schema[k]).entities[k] - })) - ) - response = {entities} - } - - const action = {type: sync.type, response} - const syncTo = sync.syncTo.concat('admin') - - users.forEach(user => { - if (user.socket.id === req.headers['socket-id']) return - if (intersection(user.roles, syncTo).length) { - user.socket.emit('action', action) - } - }) - } - - next() - } -} - -export const addUser = (user, socket) => users.push({...user, socket}) diff --git a/server1/models/customer.js b/server1/models/customer.js deleted file mode 100644 index 4406e3f3..00000000 --- a/server1/models/customer.js +++ /dev/null @@ -1,131 +0,0 @@ -import mongoose from 'mongoose' -import moment from 'moment' - -import {modelTypes, questionnaireIdentifiers} from '../../common/constants' -import locationSchema from './location-schema' -import {getValidator} from '../lib/questionnaire-helpers' -import {locateQuestionnaire} from '../lib/geolocate' - -const {Schema} = mongoose - -const CustomerSchema = new Schema({ - _id: { - type: Number, - ref: modelTypes.USER - }, - firstName: { - type: String, - required: true - }, - lastName: { - type: String, - required: true - }, - email: { - type: String, - required: true - }, - location: locationSchema, - status: { - type: String, - enum: ['Accepted', 'Rejected', 'Pending', 'Inactive'], - default: 'Pending' - }, - household: [{ - name: { - type: String, - trim: true - }, - relationship: { - type: String, - trim: true - }, - dateOfBirth: { - type: Date - } - }], - disclaimerAgree: { - type: Boolean - }, - disclaimerSign: { - type: String, - trim: true - }, - lastPacked: { - type: Date, - default: null - }, - packingList: [{ - type: Schema.Types.ObjectId, - ref: 'FoodItem' - }], - lastDelivered: { - type: Date - }, - assignedTo: { - type: Number, - ref: modelTypes.VOLUNTEER - }, - foodPreferences: [Schema.Types.ObjectId], - fields: [{ - meta: { - type: String, - required: true - }, - value: String - }], - dateReceived: { - type: Date, - default: Date.now - } -}, { - id: false -}) - -CustomerSchema.path('fields') - .validate(getValidator(questionnaireIdentifiers.CUSTOMER), 'Invalid field') - -/** - * Hook a pre save method for geolocation - */ -CustomerSchema.pre('save', async function(next) { - const result = await locateQuestionnaire(this.fields, questionnaireIdentifiers.CUSTOMER) - if (result instanceof Error) - return next(result) - - if (result) - this.location = result - - return next() -}) - -/** - * Virtual getters & setters - */ -CustomerSchema.virtual('fullName').get(function() { - var fullName = this.firstName ? this.firstName + ' ' : '' - fullName += this.middleName ? this.middleName + ' ' : '' - fullName += this.lastName ? this.lastName : '' - return fullName -}) - -CustomerSchema.virtual('householdSummary').get(function() { - var householdSummary = 'None' - - if (this.household.length) { - householdSummary = '#' + this.household.length + ' -' - this.household.forEach(function(dependant) { - var ageInYears = moment().diff(dependant.dateOfBirth, 'years') - householdSummary += ' ' - householdSummary += ageInYears ? ageInYears + 'y' : moment().diff(dependant.dateOfBirth, 'months') + 'm' - }) - } - return householdSummary -}) - -/** - * Schema options - */ -CustomerSchema.set('toJSON', {virtuals: true}) - -export default mongoose.model(modelTypes.CUSTOMER, CustomerSchema) diff --git a/server1/models/donation.js b/server1/models/donation.js deleted file mode 100644 index c41b5457..00000000 --- a/server1/models/donation.js +++ /dev/null @@ -1,48 +0,0 @@ -import mongoose from 'mongoose' -import autoIncrement from 'mongoose-plugin-autoinc' - -import {modelTypes} from '../../common/constants' - -const Schema = mongoose.Schema - -const DonatedItemSchema = new Schema({ - name: { - type: String, - trim: true - }, - value: { - type: Number, - required: true - } -}, { - _id: false -}) - -const DonationSchema = new Schema({ - donor: { - type: Number, - ref: modelTypes.USER - }, - total: Number, - approved: { - type: Boolean, - default: false - }, - description: { - type: String, - trim: true - }, - dateReceived: { - type: Date, - default: Date.now - }, - dateIssued: Date, - items: [DonatedItemSchema] -}) - -DonationSchema.plugin(autoIncrement, { - model: modelTypes.DONATION, - startAt: 1 -}) - -export default mongoose.model(modelTypes.DONATION, DonationSchema) diff --git a/server1/models/donor.js b/server1/models/donor.js deleted file mode 100644 index c7708a42..00000000 --- a/server1/models/donor.js +++ /dev/null @@ -1,59 +0,0 @@ -import mongoose from 'mongoose' - -import {modelTypes, questionnaireIdentifiers} from '../../common/constants' -import {getValidator} from '../lib/questionnaire-helpers' - -const {Schema} = mongoose - -var DonorSchema = new Schema({ - _id: { - type: Number, - ref: modelTypes.USER - }, - lastName: { - type: String, - trim: true - }, - firstName: { - type: String, - trim: true - }, - email: { - type: String, - trim: true - }, - donations: [{ - type: Number, - ref: modelTypes.DONATION - }], - fields: [{ - meta: { - type: String, - required: true - }, - value: String - }], - dateReceived: { - type: Date, - default: Date.now - } -}, { - id: false -}) - -DonorSchema.path('fields') - .validate(getValidator(questionnaireIdentifiers.DONOR), 'Invalid field') - -/** - * Virtual getters & setters - */ -DonorSchema.virtual('fullName').get(function() { - var fullName = this.firstName ? this.firstName + ' ' : '' - fullName += this.middleName ? this.middleName + ' ' : '' - fullName += this.lastName ? this.lastName : '' - return fullName -}) - -DonorSchema.set('toJSON', {virtuals: true}) - -export default mongoose.model(modelTypes.DONOR, DonorSchema) diff --git a/server1/models/food.js b/server1/models/food.js deleted file mode 100644 index a5fa8a59..00000000 --- a/server1/models/food.js +++ /dev/null @@ -1,44 +0,0 @@ -import mongoose from 'mongoose' - -import {modelTypes} from '../../common/constants' - -const {Schema} = mongoose - -const FoodItemSchema = new Schema({ - name: { - type: String, - trim: true - }, - quantity: { - type: Number, - default: 0 - }, - startDate: { - type: Date, - default: Date.now - }, - frequency: { - type: Number, - default: 1 - }, - deleted: { - type: Boolean, - default: false - } -}) - -const FoodSchema = new Schema({ - category: { - type: String, - required: 'Please fill in a category name', - trim: true - }, - items: [FoodItemSchema], - deleted: { - type: Boolean, - default: false - } -}) - -export default mongoose.model(modelTypes.FOOD, FoodSchema) -export const FoodItem = mongoose.model(modelTypes.FOOD_ITEM, FoodItemSchema) \ No newline at end of file diff --git a/server1/models/index.js b/server1/models/index.js deleted file mode 100644 index a7621e35..00000000 --- a/server1/models/index.js +++ /dev/null @@ -1,14 +0,0 @@ -export {default as Customer} from './customer' -export {default as Donation} from './donation' -export {default as Donor} from './donor' -export {default as Food} from './food' -export {default as Media} from './media' -export {default as Page} from './page' -export { - Field as Field, - Section as Section, - Questionnaire as Questionnaire -} from './questionnaire' -export {default as Settings} from './settings' -export {default as User} from './user' -export {default as Volunteer} from './volunteer' diff --git a/server1/models/location-schema.js b/server1/models/location-schema.js deleted file mode 100644 index c94eb800..00000000 --- a/server1/models/location-schema.js +++ /dev/null @@ -1,16 +0,0 @@ -import mongoose from 'mongoose' - -const {Schema} = mongoose - -export default Schema({ - lat: { - type: Number, - required: true - }, - lng: { - type: Number, - required: true - } -}, { - _id: false -}) diff --git a/server1/models/media.js b/server1/models/media.js deleted file mode 100644 index 189f00cc..00000000 --- a/server1/models/media.js +++ /dev/null @@ -1,29 +0,0 @@ -import mongoose from 'mongoose' - -import {modelTypes} from '../../common/constants' - -const {Schema} = mongoose - -/** - * Settings Schema - */ -const MediaSchema = new Schema({ - path: { - type: String, - default: 'media/' - }, - logo: { - type: String, - default: 'logo.png' - }, - signature: { - type: String, - default: 'signature.png' - }, - favicon: { - type: String, - default: 'favicon.ico' - } -}) - -export default mongoose.model(modelTypes.MEDIA, MediaSchema) diff --git a/server1/models/notification.js b/server1/models/notification.js deleted file mode 100644 index 0f64564b..00000000 --- a/server1/models/notification.js +++ /dev/null @@ -1,25 +0,0 @@ - -import mongoose from 'mongoose' - -const Schema = mongoose.Schema - -/** - * Notification Schema - */ -export const notificationSchema = new Schema({ - message: { - type: String, - required: true - }, - url: { - type: String, - required: true - }, - date: { - type: Date, - required: true, - default: Date.now() - } -}, { - _id: false -}) diff --git a/server1/models/package.js b/server1/models/package.js deleted file mode 100644 index ae6a2b8b..00000000 --- a/server1/models/package.js +++ /dev/null @@ -1,41 +0,0 @@ -import mongoose from 'mongoose' - -import {modelTypes} from '../../common/constants' - -const {Schema} = mongoose - -const PackageSchema = new Schema({ - customer: { - type: Number, - ref: modelTypes.CUSTOMER, - required: true - }, - status: { - type: String, - required: true, - enum: ['Packed', 'Delivered'], - }, - packedBy: { - type: Number, - ref: modelTypes.USER, - required: true - }, - datePacked: { - type: Date, - default: Date.now - }, - dateReceived: { - type: Date - }, - contents: { - type: [{type: Schema.Types.ObjectId, ref: modelTypes.FOOD_ITEM}], - required: true, - validate: [arrayMinElements, 'Path `{PATH}` is required to have at least one element.'] - } -}) - -function arrayMinElements(val) { - return val.length > 0 -} - -export default mongoose.model(modelTypes.PACKAGE, PackageSchema) diff --git a/server1/models/page.js b/server1/models/page.js deleted file mode 100644 index dafddac8..00000000 --- a/server1/models/page.js +++ /dev/null @@ -1,29 +0,0 @@ -import mongoose from 'mongoose' - -import {modelTypes, pageTypes} from '../../common/constants' - -const {Schema} = mongoose - -const PageSchema = new Schema({ - identifier: { - type: String, - required: true - }, - title: { - type: String, - required: true - }, - type: { - type: String, - enum: [pageTypes.PAGE, pageTypes.EMAIL], - default: pageTypes.PAGE - }, - disabled: { - type: Boolean, - default: false - }, - subject: String, - body: String -}) - -export default mongoose.model(modelTypes.PAGE, PageSchema) diff --git a/server1/models/questionnaire.js b/server1/models/questionnaire.js deleted file mode 100644 index 32b30d81..00000000 --- a/server1/models/questionnaire.js +++ /dev/null @@ -1,65 +0,0 @@ -import mongoose from 'mongoose' -import uuid from 'uuid' -import {values} from 'lodash' - -import {fieldTypes, widgetTypes, modelTypes} from '../../common/constants' - -const {Schema} = mongoose - -const FieldSchema = new Schema({ - _id: { - type: String, - default: uuid.v4 - }, - label: { - type: String, - required: true, - trim: true - }, - type: { - type: String, - required: true, - enum: [...values(fieldTypes), ...values(widgetTypes)] - }, - choices: { - type: String, - trim: true - }, - rows: String, - columns: String, - required: { - type: Boolean, - default: false - } -}) - -const SectionSchema = new Schema({ - _id: { - type: String, - default: uuid.v4 - }, - name: { - type: String, - required: true, - trim: true - }, - fields: [FieldSchema] -}) - -const QuestionnaireSchema = new Schema({ - name: { - type: String, - unique: true, - required: true, - trim: true - }, - identifier: { - type: String, - unique: true, - required: true, - trim: true - }, - sections: [SectionSchema] -}) - -export default mongoose.model(modelTypes.QUESTIONNAIRE, QuestionnaireSchema) diff --git a/server1/models/settings.js b/server1/models/settings.js deleted file mode 100644 index cbdc58ad..00000000 --- a/server1/models/settings.js +++ /dev/null @@ -1,73 +0,0 @@ -import mongoose from 'mongoose' - -import {modelTypes} from '../../common/constants' -import locationSchema from './location-schema' - -const {Schema} = mongoose - -const receiptFieldSchema = new Schema({ - name: { - type: String, - required: true - }, - value: { - type: String, - required: true - } -}, { - _id: false -}) - -const SettingsSchema = new Schema({ - organization: { - type: String, - trim: true - }, - url: { - type: String, - trim: true - }, - address:{ - type: String, - trim: true - }, - clientIntakeNumber: { - type: String, - trim: true - }, - supportNumber: { - type: String, - trim: true - }, - location: locationSchema, - receiptFields: [receiptFieldSchema], - gmapsApiKey: { - type: String, - trim: true, - select: false - }, - gmapsClientId: { - type: String, - trim: true, - select: false - }, - distanceUnit: { - type: String, - trim: true, - enum: ['km','mi'], - default: 'mi' - }, - moneyUnit: { - type: String, - trim: true, - default: '$' - }, - ein: { - type: String, - trim: true, - default: '' - }, -}) - -export default mongoose.model(modelTypes.SETTINGS, SettingsSchema) -// TODO: set location on save diff --git a/server1/models/user.js b/server1/models/user.js deleted file mode 100644 index e8ecf0fa..00000000 --- a/server1/models/user.js +++ /dev/null @@ -1,121 +0,0 @@ -import crypto from 'crypto' -import {values} from 'lodash' -import mongoose from 'mongoose' -import autoIncrement from 'mongoose-plugin-autoinc' - -import {ADMIN_ROLE, clientRoles, volunteerRoles, modelTypes} from '../../common/constants' -import {notificationSchema} from './notification' - -const Schema = mongoose.Schema - -/** - * A Validation function for local strategy properties - */ -var validateLocalStrategyProperty = function(property) { - return ((this.provider !== 'local' && !this.updated) || property.length) -} - -/** - * A Validation function for local strategy password - */ -var validateLocalStrategyPassword = function(password) { - return (this.provider !== 'local' || (password && password.length > 6)) -} - -/** - * User Schema - */ -const UserSchema = new Schema({ - firstName: { - type: String, - trim: true, - default: '', - validate: [validateLocalStrategyProperty, 'Please fill in your first name'] - }, - lastName: { - type: String, - trim: true, - default: '', - validate: [validateLocalStrategyProperty, 'Please fill in your last name'] - }, - displayName: { - type: String, - trim: true - }, - email: { - type: String, - trim: true, - unique: true, - validate: [validateLocalStrategyProperty, 'Please fill in your email'], - match: [/.+@.+\..+/, 'Please fill a valid email address'] - }, - password: { - type: String, - default: '', - select: false, - validate: [validateLocalStrategyPassword, 'Password should be longer'] - }, - salt: { - type: String, - select: false - }, - provider: { - type: String, - required: 'Provider is required' - }, - google: {}, - roles: [{ - type: String, - enum: [ADMIN_ROLE, ...values(clientRoles), ...values(volunteerRoles)] - }], - updated: Date, - created: { - type: Date, - default: Date.now - }, - /* For reset password */ - resetPasswordToken: String, - resetPasswordExpires: Date, - /* For notifications */ - notifications: [notificationSchema] -}) - -/** - * Hook a pre save method to hash the password - */ -UserSchema.pre('save', function(next) { - if (this.password && this.password.length > 6) { - this.salt = new Buffer(crypto.randomBytes(16).toString('base64'), 'base64') - this.password = this.hashPassword(this.password) - } - - next() -}) - -/** - * Create instance method for hashing a password - */ -UserSchema.methods.hashPassword = function(password) { - if (this.salt && password) { - return crypto.pbkdf2Sync(password, this.salt, 10000, 64, 'sha512').toString('base64') - } else { - return password - } -} - -/** - * Create instance method for authenticating user - */ -UserSchema.methods.authenticate = function(password) { - return this.password === this.hashPassword(password) -} - -/** - * Schema plugins - */ -UserSchema.plugin(autoIncrement, { - model: modelTypes.USER, - startAt: 10000 -}) - -export default mongoose.model(modelTypes.USER, UserSchema) diff --git a/server1/models/volunteer.js b/server1/models/volunteer.js deleted file mode 100644 index 58a38c07..00000000 --- a/server1/models/volunteer.js +++ /dev/null @@ -1,120 +0,0 @@ -import mongoose from 'mongoose' - -import {modelTypes, questionnaireIdentifiers} from '../../common/constants' -import locationSchema from './location-schema' -import {getValidator} from '../lib/questionnaire-helpers' -import {locateQuestionnaire} from '../lib/geolocate' - -const {Schema} = mongoose - -var VolunteerSchema = new Schema({ - _id: { - type: Number, - ref: modelTypes.USER - }, - lastName: { - type: String, - trim: true - }, - firstName: { - type: String, - trim: true - }, - email: { - type: String, - trim: true - }, - location: locationSchema, - status: { - type: String, - enum: ['Active', 'Inactive', 'Pending'], - default: 'Pending' - }, - disclaimerAgree: { - type: Boolean - }, - customers: [{ - type: Number, - ref: modelTypes.CUSTOMER - }], - optimized: { - type: Boolean, - default: false - }, - generalNotes: { - type: String, - trim: true - }, - disclaimerSign: { - type: String, - trim: true - }, - disclaimerSignGuardian: { - type: String, - trim: true - }, - shift: [{ - role: String, - date: Date, - duration: Number, - notes: String - }], - test: String, - disclaimerGuardianEmail: { - type: String, - trim: true - }, - fields: [{ - meta: { - type: String, - required: true - }, - value: String - }], - // to populate with user roles, nicer way? - user: { - type: Number, - ref: modelTypes.USER - }, - dateReceived: { - type: Date, - default: Date.now - }, - -}, { - id: false -}) - -VolunteerSchema.path('fields') - .validate(getValidator(questionnaireIdentifiers.VOLUNTEER), 'Invalid field') - -/** - * Hook a pre save method for geolocation - */ -VolunteerSchema.pre('save', async function(next) { - const result = await locateQuestionnaire(this.fields, questionnaireIdentifiers.VOLUNTEER) - if (result instanceof Error) - return next(result) - - if (result) - this.location = result - - return next() -}) - -/** - * Virtual getters & setters - */ -VolunteerSchema.virtual('fullName').get(function() { - var fullName = this.firstName ? this.firstName + ' ' : '' - fullName += this.middleName ? this.middleName + ' ' : '' - fullName += this.lastName ? this.lastName : '' - return fullName -}) - -/** - * Schema options - */ -VolunteerSchema.set('toJSON', {virtuals: true}) - -export default mongoose.model(modelTypes.VOLUNTEER, VolunteerSchema) diff --git a/server1/routes/api.js b/server1/routes/api.js deleted file mode 100644 index 1435e19d..00000000 --- a/server1/routes/api.js +++ /dev/null @@ -1,58 +0,0 @@ -import express from 'express' - -import {NotFoundError, SimulatedError} from '../lib/errors' -import {ADMIN_ROLE} from '../../common/constants' -import users from '../controllers/users' -import customerRoutes from './customer' -import donationRoutes from './donation' -import donorRoutes from './donor' -import foodRoutes from './food' -import deliveryRoutes from './delivery' -import mediaRoutes from './media' -import packingRoutes from './packing' -import pageRoutes from './page' -import questionnaireRoutes from './questionnaire' -import settingsRoutes from './settings' -import usersRoutes from './users' -import volunteerRoutes from './volunteer' - -var apiRouter = express.Router() - -export default (delay, errorProb) => apiRouter - // delay api requests - .use(loadingSimulator(delay)) - .all('/admin/*', users.hasAuthorization([ADMIN_ROLE])) - // uncomment to also test core components - // .use(errorSimulator(errorProb)) - .use(usersRoutes()) - .use(deliveryRoutes()) - .use(pageRoutes()) - .use(mediaRoutes()) - .use(settingsRoutes()) - // for testing non-core components - .use(errorSimulator(errorProb)) - .use(customerRoutes()) - .use(donationRoutes) - .use(donorRoutes()) - .use(foodRoutes()) - .use(packingRoutes) - .use(questionnaireRoutes()) - .use(volunteerRoutes()) - .all('*', () => { - throw new NotFoundError - }) - -function loadingSimulator(time = 0) { - return (req, res, next) => { - setTimeout(next, time) - } -} - -function errorSimulator(chance = 0) { - return (req, res, next) => { - if (Math.random() < chance) { - throw new SimulatedError - } - next() - } -} diff --git a/server1/routes/api.spec.js b/server1/routes/api.spec.js deleted file mode 100644 index 7a492bdc..00000000 --- a/server1/routes/api.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -import apiRouterFactory from './api' - -import {ADMIN_ROLE} from '../../common/constants' - -describe('Api router', function() { - let userControllerMock - let apiRouterMock - - before(async function() { - await initDb() - }) - - beforeEach(function() { - userControllerMock = { - hasAuthorization: sinon.spy() - } - - apiRouterMock = { - use: sinon.stub().returnsThis(), - all: sinon.stub().returnsThis() - } - - apiRouterFactory.__Rewire__('users', userControllerMock) - apiRouterFactory.__Rewire__('apiRouter', apiRouterMock) - apiRouterFactory.__Rewire__('customerRoutes', () => sinon.spy()) - - apiRouterFactory() - }) - - afterEach(function() { - apiRouterFactory.__ResetDependency__('users') - apiRouterFactory.__ResetDependency__('apiRouter') - apiRouterFactory.__ResetDependency__('customerRoutes') - }) - - after(async function() { - await resetDb() - }) - - it('uses subrouters', function() { - expect(apiRouterMock.use).to.have.been.called - }) - - it('checks admin/ routes for admin role', function () { - expect(userControllerMock.hasAuthorization) - .to.have.been.calledWith([ADMIN_ROLE]) - expect(apiRouterMock.all).to.have.been - .calledWith('/admin/*') - }) -}) diff --git a/server1/routes/customer.js b/server1/routes/customer.js deleted file mode 100644 index 34636a9e..00000000 --- a/server1/routes/customer.js +++ /dev/null @@ -1,35 +0,0 @@ -import Router from 'express-promise-router' - -import customerController from '../controllers/customer' -import userController from '../controllers/users' -import websocketMiddleware from '../lib/websocket-middleware' -import {customer} from '../../common/schemas' - -const sync = {schema: customer} -const saveSchema = {...sync, type: 'customer/SAVE_SUCCESS'} -const deleteSchema = {...sync, type: 'customer/DELETE_SUCCESS'} - -export default () => { - const {requiresLogin} = userController - const {hasAuthorization, canChangeStatus} = customerController - - const customerRouter = Router({mergeParams: true}) - - customerRouter.route('/customer') - .post(requiresLogin, customerController.create) - - customerRouter.route('/customer/:customerId') - .get(requiresLogin, hasAuthorization, customerController.read) - .put(requiresLogin, hasAuthorization, canChangeStatus, customerController.update) - - customerRouter.get('/admin/customer', customerController.list) - - customerRouter.route('/admin/customers/:customerId') - .put(websocketMiddleware(saveSchema), customerController.update) - .delete(websocketMiddleware(deleteSchema), customerController.delete) - - // Finish by binding the customer middleware - customerRouter.param('customerId', customerController.customerById) - - return customerRouter -} diff --git a/server1/routes/delivery.js b/server1/routes/delivery.js deleted file mode 100644 index da46e9e8..00000000 --- a/server1/routes/delivery.js +++ /dev/null @@ -1,26 +0,0 @@ -import Router from 'express-promise-router' - -import deliveryController from '../controllers/delivery' -import websocketMiddleware from '../lib/websocket-middleware' -import {arrayOfCustomers, arrayOfVolunteers} from '../../common/schemas' - -const sync = { - syncTo: ['volunteer'], - type: 'delivery/assignment/ASSIGN_SUCCESS', - schema: { - customers: arrayOfCustomers, - volunteers: arrayOfVolunteers - } -} - -export default () => { - const deliveryRouter = Router({mergeParams: true}) - - deliveryRouter.route('/delivery/directions') - .get(/*deliveryController.hasAuthorization,*/ deliveryController.directions) - - deliveryRouter.route('/admin/delivery/assign') - .post(websocketMiddleware(sync), deliveryController.assign) - - return deliveryRouter -} diff --git a/server1/routes/donation.js b/server1/routes/donation.js deleted file mode 100644 index df2131fc..00000000 --- a/server1/routes/donation.js +++ /dev/null @@ -1,18 +0,0 @@ -import Router from 'express-promise-router' -import donationController from '../controllers/donation' -import usersController from '../controllers/users' - -const {requiresLogin} = usersController - -const donationRouter = Router({mergeParams: true}) - -donationRouter.route('/donations') - .post(donationController.create) - -donationRouter.route('/admin/donations/:donationId/approve') - .put(donationController.approve) - -donationRouter.route('/donations/:donationId') - .put(requiresLogin, donationController.hasAuthorization, donationController.sendEmail) - -export default donationRouter diff --git a/server1/routes/donor.js b/server1/routes/donor.js deleted file mode 100644 index b8944ff2..00000000 --- a/server1/routes/donor.js +++ /dev/null @@ -1,31 +0,0 @@ -import Router from 'express-promise-router' - -import donorController from '../controllers/donor' -import userController from '../controllers/users' - -export default () => { - const {requiresLogin} = userController - const {hasAuthorization} = donorController - - const donorRouter = Router({mergeParams: true}) - - // Donor routes for users - donorRouter.route('/donor') - .post(requiresLogin, donorController.create) - donorRouter.route('/donor/:donorId') - .get(requiresLogin, hasAuthorization, donorController.read) - .put(requiresLogin, hasAuthorization, donorController.update) - - // Donor routes for admin - donorRouter.route('/admin/donors') - .get(donorController.list) - donorRouter.route('/admin/donors/:donorId') - .get(donorController.read) - .put(donorController.update) - .delete(donorController.delete) - - // Finish by binding the donor middleware - donorRouter.param('donorId', donorController.donorById) - - return donorRouter -} diff --git a/server1/routes/food.js b/server1/routes/food.js deleted file mode 100644 index 61f4df41..00000000 --- a/server1/routes/food.js +++ /dev/null @@ -1,41 +0,0 @@ -import Router from 'express-promise-router' - -import foodController from '../controllers/food' -import userController from '../controllers/users' -import websocketMiddleware from '../lib/websocket-middleware' -import {foodCategory} from '../../common/schemas' - -const sync = { - syncTo: ['volunteer'], - schema: foodCategory -} - -const saveCatSchema = {...sync, type: 'foodCategory/SAVE_SUCCESS'} -const deleteCatSchema = {...sync, type: 'foodCategory/DELETE_SUCCESS'} -const saveItemSchema = {...sync, type: 'foodItem/SAVE_SUCCESS'} -const deleteItemSchema = {syncTo: ['volunteer'], type: 'foodItem/DELETE_SUCCESS'} - -export default () => { - const {requiresLogin} = userController - - const foodRouter = Router({mergeParams: true}) - - foodRouter.use('/foods', requiresLogin) - foodRouter.route('/foods') - .get(foodController.list) - .post(websocketMiddleware(saveCatSchema), foodController.create) - foodRouter.route('/foods/:foodId') - .put(websocketMiddleware(saveCatSchema), foodController.update) - .delete(websocketMiddleware(deleteCatSchema), foodController.delete) - foodRouter.route('/foods/:foodId/items') - .post(websocketMiddleware(saveItemSchema), foodController.createItem) - foodRouter.route('/foods/:foodId/items/:itemId') - .put(websocketMiddleware(saveItemSchema), foodController.updateItem) - .delete(websocketMiddleware(deleteItemSchema), foodController.deleteItem) - - // Finish by binding the food middleware - foodRouter.param('foodId', foodController.foodById) - foodRouter.param('itemId', foodController.itemById) - - return foodRouter -} diff --git a/server1/routes/media.js b/server1/routes/media.js deleted file mode 100644 index 6facbb32..00000000 --- a/server1/routes/media.js +++ /dev/null @@ -1,20 +0,0 @@ -import Router from 'express-promise-router' -import {upload} from '../lib/media-helpers' - -import mediaController from '../controllers/media' - -export default () => { - const mediaRouter = Router({mergeParams: true}) - - mediaRouter.route('/admin/media/upload') - .post(upload.fields([ - {name: 'logo', maxCount: 1}, - {name: 'signature', maxCount: 1}, - {name: 'favicon', maxCount: 1} - ]), mediaController.upload) - - mediaRouter.route('/media') - .get(mediaController.read) - - return mediaRouter -} diff --git a/server1/routes/packing.js b/server1/routes/packing.js deleted file mode 100644 index 70d576e9..00000000 --- a/server1/routes/packing.js +++ /dev/null @@ -1,27 +0,0 @@ -import Router from 'express-promise-router' - -import packingController from '../controllers/packing' -import websocketMiddleware from '../lib/websocket-middleware' -import {arrayOfPackages} from '../../common/schemas' -import usersController from '../controllers/users' - -const {requiresLogin} = usersController - -const packingRouter = Router({mergeParams: true}) - -const sync = { - syncTo: ['volunteer'], - type: 'packing/PACK_SUCCESS', - schema: { - packages: arrayOfPackages, - } -} - -packingRouter.route('/packing') - .all(requiresLogin) - .get(packingController.hasAuthorization, packingController.list) - .post(packingController.hasAuthorization, websocketMiddleware(sync), packingController.pack) - .delete(packingController.hasAuthorization, packingController.unpack) - .put(packingController.hasAuthorization, packingController.complete) - -export default packingRouter diff --git a/server1/routes/page.js b/server1/routes/page.js deleted file mode 100644 index 52333e7a..00000000 --- a/server1/routes/page.js +++ /dev/null @@ -1,20 +0,0 @@ -import Router from 'express-promise-router' - -import pageController from '../controllers/page' - -export default () => { - const pageRouter = Router({mergeParams: true}) - - pageRouter.route('/admin/pages') - .get(pageController.list) - - pageRouter.route('/admin/pages/:identifier') - .put(pageController.update) - - pageRouter.route('/pages/:identifier') - .get(pageController.read) - - pageRouter.param('identifier', pageController.pageByIdentifier) - - return pageRouter -} diff --git a/server1/routes/questionnaire.js b/server1/routes/questionnaire.js deleted file mode 100644 index 78b4d4b7..00000000 --- a/server1/routes/questionnaire.js +++ /dev/null @@ -1,25 +0,0 @@ -import Router from 'express-promise-router' - -import questionnaireController from '../controllers/questionnaire' -import usersController from '../controllers/users' - -const {requiresLogin} = usersController - -export default () => { - const questionnaireRouter = Router({mergeParams: true}) - - questionnaireRouter.use('/questionnaires', requiresLogin) - - questionnaireRouter.route('/questionnaires') - .get(questionnaireController.query) - // .post(questionnaireController.create) - - questionnaireRouter.route('/questionnaires/:questionnaireId') - .get(questionnaireController.get) - .put(questionnaireController.update) - // .delete(questionnaireController.delete) - - questionnaireRouter.param('questionnaireId', questionnaireController.questionnaireById) - - return questionnaireRouter -} diff --git a/server1/routes/settings.js b/server1/routes/settings.js deleted file mode 100644 index 26382c25..00000000 --- a/server1/routes/settings.js +++ /dev/null @@ -1,16 +0,0 @@ -import Router from 'express-promise-router' - -import usersController from '../controllers/users' -import settingsController from '../controllers/settings' - -const {requiresLogin} = usersController - -export default () => { - const settingsRouter = Router({mergeParams: true}) - - settingsRouter.route('/settings') - .post(requiresLogin, settingsController.save) - .get(settingsController.read) - - return settingsRouter -} diff --git a/server1/routes/users.js b/server1/routes/users.js deleted file mode 100644 index e2fdcde9..00000000 --- a/server1/routes/users.js +++ /dev/null @@ -1,76 +0,0 @@ -import Router from 'express-promise-router' -import passport from 'passport' -import users from '../controllers/users' - -const {requiresLogin} = users -const userRouter = Router({mergeParams: true}) - -export default () => { - userRouter.route('/users').get(users.list) - - // Current logged in user - userRouter.route('/users/me') - .get(users.me) - .put(requiresLogin, users.updateProfile) - - userRouter.route('/users/me/notifications') - .get(requiresLogin, users.listNotifications) - .delete(requiresLogin, users.removeNotification) - - userRouter.route('/admin/users/:userId') - .get(users.getById) - .put(users.update) - - // Setting up the users password api - userRouter.route('/users/password').post(users.changePassword) - userRouter.route('/auth/forgot').post(users.forgot) - userRouter.route('/auth/reset/:token').post(users.reset) - - // Setting up the users authentication api - userRouter.route('/auth/signup').post(users.signup) - userRouter.route('/auth/signin').post(users.signin) - userRouter.route('/auth/signout').get(users.signout) - - // Setting the google oauth routes - userRouter.route('/auth/google').get( - (req, res, next) => { - const action = req.query.action || 'login' - passport.authenticate('google', - { - scope: [ - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/userinfo.email'], - state: JSON.stringify({action}) - } - )(req, res, next) - } - ) - - userRouter.route('/auth/google/callback').get( - (req, res, next) => passport.authenticate('google', { - scope: [ - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/userinfo.email' - ] - }, (err, user, googleProfile) => { - if (err) { - return res.status(500).send(err.message) - } else if (user) { - req.login(user, err => { - if (err) return res.status(500).send(err.message) - return res.redirect('/') - }) - } else if (googleProfile) { - //google login but no account exists - return res.redirect('/users/confirm-new-google-account') - } else { - return res.status(500).send("Server error. Could not login the user") - } - })(req, res, next) - ) - - // Finish by binding the user middleware - userRouter.param('userId', users.userByID) - - return userRouter -} diff --git a/server1/routes/volunteer.js b/server1/routes/volunteer.js deleted file mode 100644 index bfdfe7d4..00000000 --- a/server1/routes/volunteer.js +++ /dev/null @@ -1,46 +0,0 @@ -import Router from 'express-promise-router' - -import volunteerController from '../controllers/volunteer' -import userController from '../controllers/users' -//import websocketMiddleware from '../lib/websocket-middleware' -//import {volunteer} from '../../common/schemas' - -export default () => { - const {requiresLogin} = userController - const {hasAuthorization} = volunteerController - - const volunteerRouter = Router({mergeParams: true}) - - // Volunteer routes for user - volunteerRouter.route('/volunteer') - .post(requiresLogin, volunteerController.create) - volunteerRouter.route('/volunteer/:volunteerId') - .get(requiresLogin, hasAuthorization, volunteerController.read) - .put(requiresLogin, hasAuthorization, volunteerController.update) - - // Updates a shift - volunteerRouter.route('/volunteers/updateShift') - .put(volunteerController.updateShift) - - // Volunteer routes for admin for volunteer scheduling - volunteerRouter.route('/volunteers/addShift') - .put(volunteerController.addShift) - - volunteerRouter.route('/volunteers/deleteShift') - .put(volunteerController.deleteShift) - - // Volunteer routes for admin - volunteerRouter.route('/admin/volunteers') - .get(volunteerController.list) - volunteerRouter.route('/admin/volunteers/:volunteerId') - .get(volunteerController.read) - .put(volunteerController.update) - .delete(volunteerController.delete) - - // Finish by binding the volunteer middleware - volunteerRouter.param('volunteerId', volunteerController.volunteerById) - - - - return volunteerRouter -} diff --git a/server1/server.js b/server1/server.js deleted file mode 100644 index d7729bc2..00000000 --- a/server1/server.js +++ /dev/null @@ -1,33 +0,0 @@ -import mongoose from 'mongoose' -import http from 'http' -import socketIO from 'socket.io' -import socketIOSession from 'express-socket.io-session' - -import config from './config' -import setupPassport from './config/passport' -import setupExpress from './config/express' - -process.on('unhandledRejection', err => console.error(err)) - -mongoose.Promise = global.Promise -mongoose.connect(config.db) -const db = mongoose.connection - -db.on('error', function(err) { - console.error('Mongoose error', err) -}) - -db.once('open', function() { - console.info('Connected to', config.db) - - const io = socketIO() - const app = setupExpress(io) - const server = http.createServer(app).listen(config.port) - - setupPassport() - - io.listen(server) - io.use(socketIOSession(app.get('sharedSession'))) - - console.info('Application started on port', config.port) -}) diff --git a/server1/tests/helpers.js b/server1/tests/helpers.js deleted file mode 100644 index c3a79a4b..00000000 --- a/server1/tests/helpers.js +++ /dev/null @@ -1,69 +0,0 @@ -import express from 'express' - -import setupPassport from '../config/passport' -import app from '../config/express' -import User from '../models/user' - -/** - * returns a user and express app where the user is authenticated - * - * @param {object} userModel new or existing user to create mock session for - * @return {promise} - */ -export const createUserSession = async function(userModel) { - setupPassport() - - const user = userModel._id ? - userModel : - await new User(userModel).save() - - const mockApp = express() - mockApp.all('*', mockSessionMiddleware(user)) - mockApp.use(app()) - - return { - user, - app: mockApp - } -} - -/** - * create an express app without an authenticated user - * - * @return {object} - */ -export const createGuestSession = function() { - setupPassport() - const mockApp = express() - mockApp.use(app()) - - return mockApp -} - -/** - * create a user model - * - * @param {string} username - * @param {string} role - * @param {object} props additional properties to set - * @return {object} - */ -export const createTestUser = (username, role, props = null) => ({ - firstName: username, - lastName: 'test', - roles: [role], - email: `${username}@test.com`, - password: 'password', - provider: 'local', - ...props -}) - -function mockSessionMiddleware({_id, roles}) { - return (req, res, next) => { - req.session = { - passport: {user: {_id, roles}} - } - - next() - } -} \ No newline at end of file diff --git a/server1/tests/integration/customer.spec.js b/server1/tests/integration/customer.spec.js deleted file mode 100644 index 065f04f4..00000000 --- a/server1/tests/integration/customer.spec.js +++ /dev/null @@ -1,430 +0,0 @@ -import { - ADMIN_ROLE, - clientRoles, - volunteerRoles, - questionnaireIdentifiers -} from '../../../common/constants' -import Customer from '../../models/customer' -import Volunteer from '../../models/volunteer' -import {createGuestSession, createUserSession, createTestUser} from '../helpers' -import User from '../../models/user' -import Questionnaire from '../../models/questionnaire' -import Food, {FoodItem} from '../../models/food' -import Package from '../../models/package' - -describe('Customer Api', function() { - before(async function() { - await initDb() - await Customer.find().remove() - await Volunteer.find().remove() - await User.find().remove() - await Package.find().remove() - await Questionnaire.find().remove() - await new Questionnaire({ - name: 'Customer Application', - identifier: questionnaireIdentifiers.CUSTOMER - }).save() - await new Questionnaire({ - name: 'Volunteer Application', - identifier: questionnaireIdentifiers.VOLUNTEER - }).save() - }) - - afterEach(async function() { - await Customer.find().remove() - await User.find().remove() - await Package.find().remove() - await Volunteer.find().remove() - }) - - after(async function() { - await Questionnaire.find().remove() - await resetDb() - }) - - describe('POST /api/customer', function() { - it('requires login', async function() { - const app = createGuestSession() - const request = supertest.agent(app) - - return request.post('/api/customer').expect(401) - }) - - it('creates customers', async function() { - const newCustomer = createTestUser('user', clientRoles.CUSTOMER) - const {user, app} = await createUserSession(newCustomer) - const request = supertest.agent(app) - - return request.post('/api/customer') - .send(user) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('status', 'Pending') - }) - .expect(200) - }) - - - it('doesn\'t create duplicate customers', async function() { - const newCustomer = createTestUser('user', clientRoles.CUSTOMER) - const {user, app} = await createUserSession(newCustomer) - const request = supertest.agent(app) - - await request.post('/api/customer') - .send(user) - - return request.post('/api/customer') - .send(user) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('message', 'Unique field already exists') - }) - .expect(400) - }) - }) - - describe('GET /api/customer/:customerId', function() { - it('requires login', async function() { - const customer = createTestUser('customer', clientRoles.CUSTOMER) - const customerSession = await createUserSession(customer) - await supertest.agent(customerSession.app).post('/api/customer').send(customerSession.user) - - const app = createGuestSession() - const request = supertest.agent(app) - - return request.get(`/api/customer/${customerSession.user._id}`).expect(401) - }) - - it('shows a customer', async function() { - const newCustomer = createTestUser('user', clientRoles.CUSTOMER) - const {user, app} = await createUserSession(newCustomer) - const request = supertest.agent(app) - - await request.post('/api/customer') - .send(user) - - return request.get(`/api/customer/${user._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'user') - }) - .expect(200) - }) - - it('doesn\'t show non-existing customer', async function() { - const newCustomer = createTestUser('user', clientRoles.CUSTOMER) - const {user, app} = await createUserSession(newCustomer) - const request = supertest.agent(app) - - return request.get(`/api/customer/${user._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('message', 'Not found') - }) - .expect(404) - }) - - it('doesn\'t show other customers', async function() { - const firstCustomer = createTestUser('user1', clientRoles.CUSTOMER) - const secondCustomer = createTestUser('user2', clientRoles.CUSTOMER) - - const first = await createUserSession(firstCustomer) - const second = await createUserSession(secondCustomer) - - const firstReq = supertest.agent(first.app) - const secondReq = supertest.agent(second.app) - - await secondReq.post('/api/customer') - .send(second.user) - - return firstReq.get(`/api/customer/${second.user._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('message', 'User is not authorized') - }) - .expect(403) - }) - - it('shows any customer to admins', async function() { - const customer = createTestUser('customer', clientRoles.CUSTOMER) - const admin = createTestUser('admin', ADMIN_ROLE) - - const customerSession = await createUserSession(customer) - await supertest.agent(customerSession.app).post('/api/customer').send(customerSession.user) - - const adminSession = await createUserSession(admin) - return supertest.agent(adminSession.app).get(`/api/customer/${customerSession.user._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'customer') - }) - .expect(200) - }) - }) - - describe('PUT /api/customer/:customerId', function() { - it('requires login', async function() { - const customer = createTestUser('customer', clientRoles.CUSTOMER) - const customerSession = await createUserSession(customer) - await supertest.agent(customerSession.app).post('/api/customer').send(customerSession.user) - - const app = createGuestSession() - const request = supertest.agent(app) - - return request.put(`/api/customer/${customerSession.user._id}`).expect(401) - }) - - it('updates customers', async function() { - const newCustomer = createTestUser('user', clientRoles.CUSTOMER) - const {user, app} = await createUserSession(newCustomer) - const request = supertest.agent(app) - - const savedCustomer = (await request.post('/api/customer') - .send(user)).body - - const updatedCustomer = { - ...savedCustomer, - firstName: 'updated' - } - - return request.put(`/api/customer/${user._id}`) - .send(updatedCustomer) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'updated') - }) - .expect(200) - }) - - it('cannot update other customers', async function() { - const {user: customer, app: customerApp} = await createUserSession( - createTestUser('customer', clientRoles.CUSTOMER) - ) - await supertest.agent(customerApp).post('/api/customer').send(customer) - - const {app} = await createUserSession( - createTestUser('user', clientRoles.CUSTOMER) - ) - return supertest.agent(app).put(`/api/customer/${customer._id}`).expect(403) - }) - - it('updates other customers if admin', async function() { - const {user, app: customerApp} = await createUserSession( - createTestUser('customer', clientRoles.CUSTOMER) - ) - const customer = (await supertest.agent(customerApp).post('/api/customer').send(user)).body - - const {app} = await createUserSession( - createTestUser('admin', ADMIN_ROLE) - ) - return supertest.agent(app).put(`/api/customer/${customer._id}`) - .send({...customer, firstName: 'updated'}) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'updated') - }) - .expect(200) - }) - - - - - - - - - - - - - - - - - - }) - - describe('GET /api/admin/customer', function() { - it('lists customers', async function() { - const newAdmin = createTestUser('admin', ADMIN_ROLE) - const newCustomer = createTestUser('user', clientRoles.CUSTOMER) - const admin = await createUserSession(newAdmin) - const customer = await createUserSession(newCustomer) - - const adminReq = supertest.agent(admin.app) - const customerReq = supertest.agent(customer.app) - - await customerReq.post('/api/customer') - .send(customer.user) - - return adminReq.get(`/api/admin/customer`) - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.lengthOf(1) - expect(res.body[0]).to.have.property('firstName', 'user') - }) - .expect(200) - }) - }) - - describe('DELETE /api/admin/customers/:customerId', function() { - it('deletes customers', async function() { - const newAdmin = createTestUser('admin', ADMIN_ROLE) - const newCustomer = createTestUser('user', clientRoles.CUSTOMER) - const admin = await createUserSession(newAdmin) - const customer = await createUserSession(newCustomer) - - const adminReq = supertest.agent(admin.app) - const customerReq = supertest.agent(customer.app) - - await customerReq.post('/api/customer') - .send(newCustomer) - - return adminReq.delete(`/api/admin/customers/${customer.user._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'user') - }) - .expect(200) - }) - - it('Won\'t delete a customer that has packages', async function () { - const foodItems = [new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 })] - const food = await Food.create({ category: 'test', items: foodItems }) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - await Package.create({ - customer: customer._id, - status: 'Packed', - packedBy: 0, - contents: food.items.map(item => item._id.toString()) - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - // delete the customer associated with the package - return request.delete(`/api/admin/customers/${customer._id}`) - .expect(409) - .expect(function(res) { - expect(res.body).to.have.property('message', 'This customer has packages and can\'t be deleted') - }) - }) - - it('When deleting a customer, it deletes its occurance in Volunteers.customers', async function() { - const customer1 = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const customer2 = await Customer.create({ - _id: 2, - firstName: 'Ben', - lastName: 'Franklin', - email: 'bf@example.com', - }) - - const volunteer = await Volunteer.create({ - _id: 3, - customers: [customer1._id, customer2._id] - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - await request.delete(`/api/admin/customers/${customer1._id}`).expect(200) - - const updatedVolunteer = await Volunteer.findById(volunteer._id).lean() - expect(updatedVolunteer.customers).to.eql([customer2._id]) - }) - }) - - describe('PUT /api/admin/customers/:customerId', function() { - it('assigns customers', async function() { - const newAdmin = createTestUser('admin', ADMIN_ROLE) - const newCustomer = createTestUser('customer', clientRoles.CUSTOMER) - const newVolunteer = createTestUser('driver', null, {roles: [ - clientRoles.VOLUNTEER, volunteerRoles.DRIVER]}) - - const admin = await createUserSession(newAdmin) - const customer = await createUserSession(newCustomer) - const volunteer = await createUserSession(newVolunteer) - - const adminReq = supertest.agent(admin.app) - const customerReq = supertest.agent(customer.app) - const volunteerReq = supertest.agent(volunteer.app) - - const savedCustomer = (await customerReq.post('/api/customer') - .send(newCustomer)).body - - const savedVolunteer = (await volunteerReq.post('/api/volunteer') - .send(newVolunteer)).body - - return adminReq.post('/api/admin/delivery/assign') - .send({ - customerIds: [savedCustomer._id], - driverId: savedVolunteer._id - }) - .expect(200) - .expect(res => { - expect(res.body.customers).to.be.an('array') - expect(res.body.customers).to.have.length(1) - expect(res.body.customers[0]).to.have.property('assignedTo', savedVolunteer._id) - }) - }) - }) - - describe('notifications', function(){ - it('admin and volunteer notifications for a customer creation and update', async function() { - const newCustomer = createTestUser('customer', clientRoles.CUSTOMER) - const {app} = await createUserSession(newCustomer) - const request = supertest.agent(app) - - await User.create({ - firstName: 'admin', - lastName: 'test', - email: 'admin@test.com', - roles: [ADMIN_ROLE], - provider: 'local', - password: 'password' - }) - - const customer = (await request.post('/api/customer').send(newCustomer)).body - - await User.create({ - firstName: 'volunteer', - lastName: 'test', - email: 'volunteer@test.com', - password: 'password', - provider: 'local', - status:'Active' - }) - const user = await User.findOne({email:'volunteer@test.com'}) - await Volunteer.create({ - _id: user._id, - firstName: user.firstname, - lastName: user.lastName, - email: user.email, - customers:[customer._id], - user: user._id, - status:'Active' - }) - - await request.put(`/api/customer/${customer._id}`).send({...customer, firstName: 'updated'}) - - const volunteer = (await request.post('/api/auth/signin').send({email: 'volunteer@test.com', password: 'password'})).body - const admin = (await request.post('/api/auth/signin').send({email: 'admin@test.com', password: 'password'})).body - return expect(volunteer.notifications[0].message).to.equal(`Customer updated test was updated!`) && expect(admin.notifications[0].message).to.equal(`Customer customer test was created!`) - }) - }) -}) diff --git a/server1/tests/integration/donation.spec.js b/server1/tests/integration/donation.spec.js deleted file mode 100644 index 98a85b9a..00000000 --- a/server1/tests/integration/donation.spec.js +++ /dev/null @@ -1,149 +0,0 @@ -import { - questionnaireIdentifiers, - clientRoles, - ADMIN_ROLE, -} from '../../../common/constants' -import Donor from '../../models/donor' -import Donation from '../../models/donation' -import Questionnaire from '../../models/questionnaire' -import User from '../../models/user' -import {createUserSession, createTestUser} from '../helpers' - -describe('Donation Api', function() { - before(async function() { - await initDb() - await Donor.find().remove() - await User.find().remove() - await new Questionnaire({ - name: 'Donor Application', - identifier: questionnaireIdentifiers.DONOR - }).save() - }) - - afterEach(async function() { - await Donor.find().remove() - await User.find().remove() - await Donation.find().remove() - }) - - after(async function() { - await Questionnaire.find().remove() - await resetDb() - }) - - describe('User routes', function() { - it('creates donations', async function() { - const testDonor = await createTestUser('user', clientRoles.DONOR) - const {user, app} = await createUserSession(testDonor) - const request = supertest.agent(app) - const donor = await Donor.create({ - ...user, - _id: user.id, - }) - - const items = [ - {name: 'Potatoes', value: 10}, - {name: 'Carrots', value: 5,}, - {name: 'Spaghetti', value: 5,}, - ] - - const donation = { - donor: donor._id, - description: 'User Donation', - items: items, - } - - return request.post('/api/donations') - .send(donation) - .expect(res => { - expect(res.body.donor).to.be.an('object') - expect(res.body.donor).to.have.property('_id', donor._id) - expect(res.body.donation).to.be.an('object') - expect(res.body.donation).to.have.property('total', 20) - expect(res.body.donation).to.have.property('description', donation.description) - expect(res.body.donation.items).to.be.deep.eql(items) - }) - .expect(200) - }) - - it('does not allow non-admins to create donations for other donors', async function() { - const testDonor = await createTestUser('userdonor', clientRoles.DONOR) - const testDonor2 = await createTestUser('userdonor2', clientRoles.DONOR) - const firstSession = await createUserSession(testDonor) - const secondSession = await createUserSession(testDonor2) - const request = supertest.agent(firstSession.app) - - const donor2 = await Donor.create({ - ...secondSession.user, - _id: secondSession.user.id, - }) - - const donation = { - donor: donor2._id, - description: 'User Donation', - items: [ - {name: 'Potatoes', value: 10}, - ] - } - - return request.post('/api/donations') - .send(donation) - .expect(401) - }) - - it('allows admins to create donations for other donors', async function() { - const admin = await createTestUser('admin', ADMIN_ROLE) - const testDonor = await createTestUser('userdonor', clientRoles.DONOR) - const firstSession = await createUserSession(admin) - const secondSession = await createUserSession(testDonor) - const request = supertest.agent(firstSession.app) - - const donor = await Donor.create({ - ...secondSession.user, - _id: secondSession.user.id, - }) - - const donation = { - donor: donor._id, - description: 'User Donation', - items: [ - {name: 'Potatoes', value: 10}, - ] - } - - return request.post('/api/donations') - .send(donation) - .expect(200) - }) - - it('approves a donation', async function() { - const admin = await createTestUser('admin', ADMIN_ROLE) - const testDonor = await createTestUser('userdonor', clientRoles.DONOR) - const firstSession = await createUserSession(admin) - const secondSession = await createUserSession(testDonor) - const request = supertest.agent(firstSession.app) - - const donor = await Donor.create({ - ...secondSession.user, - _id: secondSession.user.id, - }) - - const response = await request.post('/api/donations') - .send({ - donor: donor._id, - description: 'User Donation', - items: [ - {name: 'Potatoes', value: 10}, - ] - }) - - return request.put('/api/admin/donations/' + response.body.donation._id + '/approve') - .expect(res => { - expect(res.body.donor).to.be.an('object') - expect(res.body).to.have.property('approved', true) - }) - .expect(200) - }) - - }) -}) diff --git a/server1/tests/integration/donor.spec.js b/server1/tests/integration/donor.spec.js deleted file mode 100644 index 2187890d..00000000 --- a/server1/tests/integration/donor.spec.js +++ /dev/null @@ -1,127 +0,0 @@ -import {ADMIN_ROLE, clientRoles, questionnaireIdentifiers} from '../../../common/constants' -import Donor from '../../models/donor' -import {createUserSession, createTestUser} from '../helpers' -import User from '../../models/user' -import Questionnaire from '../../models/questionnaire' - -describe('Donor Api', function() { - before(async function() { - await initDb() - await Donor.find().remove() - await User.find().remove() - await Questionnaire.find().remove() - await new Questionnaire({ - name: 'Donor Application', - identifier: questionnaireIdentifiers.DONOR - }).save() - }) - - afterEach(async function() { - await Donor.find().remove() - await User.find().remove() - }) - - after(async function() { - await Questionnaire.find().remove() - await resetDb() - }) - - describe('User routes', function() { - it('creates donors', async function() { - const testDonor = createTestUser('user', clientRoles.DONOR) - const {user, app} = await createUserSession(testDonor) - const request = supertest.agent(app) - - return request.post('/api/donor') - .send(user) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'user') - }) - .expect(200) - }) - - it('shows a donor', async function() { - const testDonor = createTestUser('user', clientRoles.DONOR) - const {user, app} = await createUserSession(testDonor) - const request = supertest.agent(app) - - const newDonor = (await request.post('/api/donor') - .send(user)).body - - return request.get(`/api/donor/${newDonor._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'user') - }) - .expect(200) - }) - - it('updates a donor', async function() { - const testDonor = createTestUser('user', clientRoles.DONOR) - const {user, app} = await createUserSession(testDonor) - const request = supertest.agent(app) - - const newDonor = (await request.post('/api/donor') - .send(user)).body - - const updatedDonor = { - ...newDonor, - firstName: 'updated' - } - - return request.put(`/api/donor/${newDonor._id}`) - .send(updatedDonor) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'updated') - }) - .expect(200) - }) - }) - - describe('Admin routes', function() { - it('lists donors', async function() { - const testDonor = createTestUser('user', clientRoles.DONOR) - const testAdmin = createTestUser('admin', ADMIN_ROLE) - - const donor = await createUserSession(testDonor) - const admin = await createUserSession(testAdmin) - - const donorRequest = supertest.agent(donor.app) - const adminRequest = supertest.agent(admin.app) - - await donorRequest.post('/api/donor') - .send(donor.user) - - return adminRequest.get('/api/admin/donors') - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(1) - expect(res.body[0]).to.have.property('firstName', 'user') - }) - .expect(200) - }) - - it('deletes a donor', async function() { - const testDonor = createTestUser('user', clientRoles.DONOR) - const testAdmin = createTestUser('admin', ADMIN_ROLE) - - const donor = await createUserSession(testDonor) - const admin = await createUserSession(testAdmin) - - const donorRequest = supertest.agent(donor.app) - const adminRequest = supertest.agent(admin.app) - - const newDonor = (await donorRequest.post('/api/donor') - .send(donor.user)).body - - return adminRequest.delete(`/api/admin/donors/${newDonor._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'user') - }) - .expect(200) - }) - }) -}) diff --git a/server1/tests/integration/food.spec.js b/server1/tests/integration/food.spec.js deleted file mode 100644 index dcea5ffd..00000000 --- a/server1/tests/integration/food.spec.js +++ /dev/null @@ -1,297 +0,0 @@ -import {ADMIN_ROLE, clientRoles} from '../../../common/constants' -import Food, {FoodItem} from '../../models/food' -import {createUserSession, createTestUser} from '../helpers' -import User from '../../models/user' - -describe('Food Api', function() { - before(async function() { - await initDb() - await Food.find().remove() - await User.find().remove() - }) - - afterEach(async function() { - await Food.find().remove() - await User.find().remove() - }) - - after(async function() { - await resetDb() - }) - - describe('User routes', function() { - it('lists food categories', async function() { - const testUser = createTestUser('user', clientRoles.CUSTOMER) - const testAdmin = createTestUser('admin', ADMIN_ROLE) - - const user = await createUserSession(testUser) - const admin = await createUserSession(testAdmin) - - const userRequest = supertest.agent(user.app) - const adminRequest = supertest.agent(admin.app) - - const testFood = { - category: 'test food', - items: [ - {name: 'test item'} - ] - } - - await adminRequest.post('/api/foods') - .send(testFood) - - return userRequest.get('/api/foods') - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(1) - expect(res.body[0]).to.have.property('items') - expect(res.body[0].items[0]).to.have.property('name', 'test item') - }) - }) - }) - - describe('Admin routes', function () { - it('creates food categories', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testFood = {category: 'test food'} - - return request.post('/api/foods') - .send(testFood) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('category', 'test food') - }) - .expect(200) - }) - - it('lists food categories', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testFood = {category: 'test food'} - - await request.post('/api/foods') - .send(testFood) - - return request.get('/api/foods') - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(1) - expect(res.body[0]).to.have.property('category', 'test food') - }) - .expect(200) - }) - - it('updates a food categories', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testFood = {category: 'test food'} - - const savedFood = (await request.post('/api/foods') - .send(testFood)).body - - const updatedFood = {category: 'new category'} - - return request.put(`/api/foods/${savedFood._id}`) - .send(updatedFood) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('category', 'new category') - }) - .expect(200) - }) - - it('deletes a food categories', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testFood = {category: 'test food'} - - const savedFood = (await request.post('/api/foods') - .send(testFood)).body - - return request.delete(`/api/foods/${savedFood._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('category', 'test food') - }) - .expect(200) - }) - - it('adds food items', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testCategory = {category: 'test food'} - const testItem = {name: 'test item'} - - const savedCategory = (await request.post('/api/foods') - .send(testCategory)).body - - return request.post(`/api/foods/${savedCategory._id}/items`) - .send(testItem) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('items') - expect(res.body.items).to.be.an('array') - expect(res.body.items).to.have.length(1) - expect(res.body.items[0]).to.have.property('name', 'test item') - }) - .expect(200) - }) - - it('updates food items', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testCategory = {category: 'test category'} - const testItem = {name: 'test item'} - - const savedCategory = (await request.post('/api/foods') - .send(testCategory)).body - - const savedCategoryWithItem = (await request - .post(`/api/foods/${savedCategory._id}/items`) - .send(testItem) - ).body - - const updatedItem = { - ...savedCategoryWithItem.items[0], - name: 'updated item' - } - - const itemId = savedCategoryWithItem.items[0]._id - return request.put(`/api/foods/${savedCategory._id}/items/${itemId}`) - .send(updatedItem) - .expect(res => { - expect(res.body).to.have.property('items') - expect(res.body.items[0]).to.have.property('name', 'updated item') - }) - .expect(200) - }) - - it('changes food items category', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testCategory1 = {category: 'test category 1'} - const testCategory2 = {category: 'test category 2'} - const testItem = {name: 'test item'} - - const savedCategory1 = (await request.post('/api/foods') - .send(testCategory1)).body - - const savedCategory2 = (await request.post('/api/foods') - .send(testCategory2)).body - - const savedCategoryWithItem = (await request - .post(`/api/foods/${savedCategory1._id}/items`) - .send(testItem) - ).body - - const savedItem = savedCategoryWithItem.items[0] - - const updatedItem = { - ...savedItem, - name: 'test item', - categoryId: savedCategory2._id - } - - return request.put(`/api/foods/${savedCategory1._id}/items/${savedItem._id}`) - .send(updatedItem) - .expect(res => { - expect(res.body).to.have.property('_id', savedCategory2._id) - expect(res.body.items[0]).to.have.property('name', 'test item') - }) - .expect(200) - }) - - it('deletes food items', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const testCategory = {category: 'test category'} - const testItem = {name: 'test item'} - - const savedCategory = (await request.post('/api/foods') - .send(testCategory)).body - - const savedCategoryWithItem = (await request - .post(`/api/foods/${savedCategory._id}/items`) - .send(testItem) - ).body - - const itemId = savedCategoryWithItem.items[0]._id - - await request.delete(`/api/foods/${savedCategory._id}/items/${itemId}`) - .expect(200) - .expect(res => { - expect(res.body).to.have.property('deletedItemId') - expect(res.body.deletedItemId).to.equal(itemId) - }) - - // Fetch the foods to make sure it is not still there - return request.get('/api/foods') - .expect(200) - .expect(res => { - expect(res.body).to.have.length(1) - expect(res.body[0].items.filter(item => !item.deleted)).to.have.length(0) - }) - }) - - it('creates a new item when a deleted item with the same name exists', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const apple = await FoodItem.create({name: 'apple', quantity: 2, deleted: true}) - const category = await Food.create({category: 'fruits', items: [apple]}) - - return request.post(`/api/foods/${category._id}/items`) - .send({ name: 'apple', quantity: 10 }) - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('items') - expect(res.body.items).to.be.an('array') - expect(res.body.items).to.have.length(2) - const item = res.body.items.find(i => i.name === 'apple' && i.deleted === false && i.quantity === 10) - expect(item).to.not.be.undefined - }) - }) - - it('creates a new item when deleted item with the same name exists and another item is not deleted', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const {app} = await createUserSession(testAdmin) - const request = supertest.agent(app) - - const apple = await FoodItem.create({name: 'apple', quantity: 2, deleted: true}) - const banana = await FoodItem.create({name: 'banana', quantity: 5, deleted: false}) - const category = await Food.create({category: 'fruits', items: [apple, banana]}) - - return request.post(`/api/foods/${category._id}/items`) - .send({ name: 'apple', quantity: 10 }) - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('items') - expect(res.body.items).to.be.an('array') - expect(res.body.items).to.have.length(3) - const item = res.body.items.find(i => i.name === 'apple' && i.deleted === false && i.quantity === 10) - expect(item).to.not.be.undefined - }) - }) - - }) -}) diff --git a/server1/tests/integration/packing.spec.js b/server1/tests/integration/packing.spec.js deleted file mode 100644 index 2bda22a0..00000000 --- a/server1/tests/integration/packing.spec.js +++ /dev/null @@ -1,643 +0,0 @@ -import {find} from 'lodash' - -import {createUserSession, createTestUser} from '../helpers' -import {ADMIN_ROLE, questionnaireIdentifiers} from '../../../common/constants' -import Customer from '../../models/customer' -import Food, {FoodItem} from '../../models/food' -import Package from '../../models/package' -import User from '../../models/user' -import Questionnaire from '../../models/questionnaire' - -describe('Packing Api', function() { - before(async function() { - await initDb() - await Customer.find().remove() - await User.find().remove() - await Food.find().remove() - await Package.find().remove() - await Questionnaire.find().remove() - await Questionnaire.create({ - name: 'Customer Application', - identifier: questionnaireIdentifiers.CUSTOMER - }) - }) - - afterEach(async function() { - await Customer.find().remove() - await User.find().remove() - await Food.find().remove() - await Package.find().remove() - }) - - after(async function() { - await Questionnaire.find().remove() - await resetDb() - }) - - describe('packing GET route', function() { - it ('Returns empty array when no packages', async function() { - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.get('/api/packing') - .expect(200) - .expect(function (res) { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(0) - }) - }) - - it('Returns one item when there is 1 packed item', async function () { - const foodItems = [ - new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 }), - new FoodItem({ name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1 }), - ] - const food = await Food.create({ category: 'test', items: foodItems }) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const package1 = await Package.create({ - customer: customer._id, - status: 'Packed', - packedBy: 0, - contents: foodItemIds - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - const expected = [{ - __v: package1.__v, - _id: package1._id.toString(), - customer: package1.customer, - datePacked: package1.datePacked.toISOString(), - packedBy: package1.packedBy, - status: package1.status, - contents: package1.contents.map(_id => _id.toString()) - }] - - return request.get('/api/packing') - .expect(200) - .expect(function (res) { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(1) - expect(res.body).to.eql(expected) - }) - }) - - it('Returns 2 packages when there are 2 packages', async function () { - - const foodItems = [ - new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 }), - new FoodItem({ name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1 }), - ] - const food = await Food.create({ category: 'test', items: foodItems }) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const package1 = await Package.create({ - customer: customer._id, - status: 'Packed', - packedBy: 0, - contents: foodItemIds[0] - }) - - const package2 = await Package.create({ - customer: customer._id, - status: 'Packed', - packedBy: 0, - contents: foodItemIds[1] - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - const expected = [package1, package2].map(p => ({ - __v: p.__v, - _id: p._id.toString(), - customer: p.customer, - datePacked: p.datePacked.toISOString(), - packedBy: p.packedBy, - status: p.status, - contents: p.contents.map(_id => _id.toString()) - })) - - return request.get('/api/packing') - .expect(200) - .expect(function (res) { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(2) - expect(res.body).to.eql(expected) - }) - }) - }) - - describe('packing POST route', function() { - - it('Packs a package for one customer', async function() { - let foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - new FoodItem({name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1}), - new FoodItem({name: 'Cantaloupe', quantity: 100, startDate: '2017-06-25', frequency: 1}) - ] - const food = await Food.create({category: 'test', items: foodItems}) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.post('/api/packing').send([{ - customer: customer._id, - contents: foodItemIds - }]) - .expect(200) - .expect(function(res) { - expect(res.body.packages).to.be.an('array') - expect(res.body.packages).to.have.length(1) - expect(res.body.packages[0]).to.have.property('customer', 1) - expect(res.body.packages[0].contents).to.be.an('array') - expect(res.body.packages[0].contents).to.have.length(3) - expect(res.body.packages[0].contents).to.have.same.members(foodItemIds) - }) - }) - - it('Packs a package for two customers', async function() { - let foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - new FoodItem({name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1}), - new FoodItem({name: 'Cantaloupe', quantity: 100, startDate: '2017-06-25', frequency: 1}) - ] - const food = await Food.create({category: 'test', items: foodItems}) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer1 = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const customer2 = await Customer.create({ - _id: 2, - firstName: 'Thomas', - lastName: 'Jefferson', - email: 'tj@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - const package1Contents = [foodItemIds[0]] - const package2Contents = [foodItemIds[1], foodItemIds[2]] - return request.post('/api/packing').send([ - { "customer": customer1._id, "contents": package1Contents }, - { "customer": customer2._id, "contents": package2Contents } - ]) - .expect(200) - .expect(function(res) { - expect(res.body.packages).to.be.an('array') - expect(res.body.packages).to.have.length(2) - - const package1 = find(res.body.packages, {customer: 1}) - expect(package1, 'No package found for customer1').to.exist - expect(package1).to.have.property('customer', 1) - expect(package1.contents).to.be.an('array') - expect(package1.contents).to.have.length(1) - expect(package1.contents).to.have.same.members(package1Contents) - - const package2 = find(res.body.packages, {customer: 2}) - expect(package2, 'No package found for customer2').to.exist - expect(package2).to.have.property('customer', 2) - expect(package2.contents).to.be.an('array') - expect(package2.contents).to.have.length(2) - expect(package2.contents).to.have.same.members(package2Contents) - }) - }) - - it('Updates the customers lastPacked date', async function() { - let foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - ] - const food = await Food.create({category: 'test', items: foodItems}).then(food => food.toObject()) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - status: 'Accepted', - lastPacked: '2017-01-01', - foodPreferences: foodItemIds, - }).then(customer => customer.toObject()) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - const reply = await request.post('/api/packing').send( - [{customer: customer._id, contents: customer.foodPreferences }] - ) - - return request.get(`/api/customer/${customer._id}`) - .expect(200) - .expect(function(res) { - expect(res.body.lastPacked).to.equal(reply.body.packages[0].datePacked) - }) - }) - - it('Updates the food inventory', async function() { - let foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - new FoodItem({name: 'Banana', quantity: 100, startDate: '2017-06-25', frequency: 1}), - new FoodItem({name: 'Cantaloupe', quantity: 100, startDate: '2017-06-25', frequency: 1}) - ] - const food = await Food.create({category: 'test', items: foodItems}) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.post('/api/packing').send([{ - customer: customer._id, - contents: [foodItemIds[0], foodItemIds[1]] - }]) - .expect(200) - .then(function () { - return Food.find({}).lean() - }) - .then(function (food) { - const foodItemsFromDb = food[0].items - const updatedItems = foodItemIds.map(_id => - foodItemsFromDb.find(item => item._id.equals(_id)) - ) - expect(updatedItems[0].quantity).to.equal(99) - expect(updatedItems[1].quantity).to.equal(99) - expect(updatedItems[2].quantity).to.equal(100) - }) - }) - - it('Returns 400 if customer property is not set', async function() { - let foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - ] - const food = await Food.create({category: 'test', items: foodItems}) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.post('/api/packing').send([{ - contents: foodItemIds - }]) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal('Package validation failed: customer: Path `customer` is required.') - }) - }) - - it('Returns 400 if an invalid customerId is given', async function() { - let foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - ] - const food = await Food.create({category: 'test', items: foodItems}) - - const foodItemIds = food.items.map(item => item._id.toString()) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.post('/api/packing').send([{ - customer: 9999, contents: foodItemIds - }]) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal('One or more customerIds are not valid') - }) - }) - - it('Returns 400 if contents are not set', async function () { - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - status: 'Accepted', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.post('/api/packing').send( - [{ customer: customer._id}] - ) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal('Package validation failed: contents: Path `contents` is required to have at least one element.') - }) - }) - - it('Returns 400 if contents is not an array', async function () { - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.post('/api/packing').send( - [{ customer: customer._id, contents: '123456789'}] - ) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal('Package validation failed: contents: Cast to Array failed for value "123456789" at path "contents"') - }) - }) - - it('Returns 400 if an invalid foodItem is specified', async function () { - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.post('/api/packing').send( - [{ customer: customer._id, contents: ['xxxxxxxxxxxxxxxxxxxxxxxx']}] - ) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal('Package validation failed: contents: Cast to Array failed for value "[ \'xxxxxxxxxxxxxxxxxxxxxxxx\' ]" at path "contents"') - }) - }) - - it('Returns 400 if a foodItem is not found in the database', async function () { - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - const nonExistingFoodItemId = '594d6a4f9431ac26453cef07' - return request.post('/api/packing').send( - [{ customer: customer._id, contents: [nonExistingFoodItemId]}] - ) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal(`foodItem ${nonExistingFoodItemId} was not found in the database`) - }) - }) - }) - - describe('packing DELETE route', function() { - - it('Unpacks a package', async function() { - const foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - ] - const food = await Food.create({category: 'test', items: foodItems}) - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - - const testPackage = await Package.create({ - customer: customer._id, - contents: foodItemIds, - status: 'Packed', - packedBy: session.user._id - }) - - const request = supertest.agent(session.app) - - return request.delete('/api/packing').send({ _id: testPackage._id, }) - .expect(200) - .then(function() { - return Package.find({}) - }) - .then(function(packages){ - expect(packages).to.have.length(0) - }) - }) - - it('Does not unpack other packages', async function () { - const foodItems = [ - new FoodItem({ name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1 }), - ] - const food = await Food.create({ category: 'test', items: foodItems }) - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com' - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - - const testPackage1 = await Package.create({ - customer: customer._id, - contents: foodItemIds, - status: 'Packed', - packedBy: session.user._id - }) - - const testPackage2 = await Package.create({ - customer: customer._id, - contents: foodItemIds, - status: 'Packed', - packedBy: session.user._id - }) - - const request = supertest.agent(session.app) - - return request.delete('/api/packing').send({ - _id: testPackage2._id, - }) - .expect(200) - .then(function () { - return (Package.find({})) - }) - .then(function (results) { - expect(results).to.be.an('array') - expect(results).to.have.length(1) - expect(results[0]._id.equals(testPackage1._id)).to.be.true - }) - }) - - it('Adds the foodItems from the package back to the inventory counts', async function() { - const foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - ] - const food = await Food.create({category: 'test', items: foodItems}) - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - - const testPackage = await Package.create({ - customer: customer._id, - contents: foodItemIds, - status: 'Packed', - packedBy: session.user._id - }) - - const request = supertest.agent(session.app) - - return request.delete('/api/packing').send({ _id: testPackage._id, }) - .expect(200) - .then(function() { - return Food.find({}) - }) - .then(function(foods){ - expect(foods[0].items[0].quantity).to.equal(101) - }) - }) - - it('Updates the lastPacked date for the customer', async function() { - const foodItems = [ - new FoodItem({name: 'Apple', quantity: 100, startDate: '2017-06-25', frequency: 1}), - ] - const food = await Food.create({category: 'test', items: foodItems}) - const foodItemIds = food.items.map(item => item._id.toString()) - - const customer = await Customer.create({ - _id: 1, - firstName: 'George', - lastName: 'Washington', - email: 'gw@example.com', - }) - - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - - const testPackage = await Package.create({ - customer: customer._id, - contents: foodItemIds, - status: 'Packed', - packedBy: session.user._id - }) - - const request = supertest.agent(session.app) - - return request.delete('/api/packing').send({ _id: testPackage._id, }) - .expect(200) - .then(function() { - return Customer.findById(1) - }) - .then(function(customer){ - expect(customer.lastPacked).to.be.a('null') - }) - }) - - it('Returns 400 when a package _id is not specified', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.delete('/api/packing') - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal(`package _id must be included in the request`) - }) - }) - - it('Returns 400 when a package _id is not valid', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.delete('/api/packing').send({ _id: 'xxxxxxxx', }) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal('xxxxxxxx is not a valid package _id') - }) - }) - - it('Returns 400 when a package _id is not found in the database', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(testAdmin) - const request = supertest.agent(session.app) - - return request.delete('/api/packing').send({ _id: '594d6a4f9431ac26453cef08', }) - .expect(400) - .expect(function (res) { - expect(res.body.message).to.equal('package with _id 594d6a4f9431ac26453cef08 not found') - }) - }) - - }) - -}) diff --git a/server1/tests/integration/questionnaire.spec.js b/server1/tests/integration/questionnaire.spec.js deleted file mode 100644 index ea7bf8d9..00000000 --- a/server1/tests/integration/questionnaire.spec.js +++ /dev/null @@ -1,123 +0,0 @@ -import Questionnaire from '../../models/questionnaire' -import User from '../../models/user' -import { - ADMIN_ROLE, - questionnaireIdentifiers, - fieldTypes, -} from '../../../common/constants' -import{createTestUser, createUserSession} from '../helpers' - -describe('Questionnaire Api', function() { - before(async function() { - await initDb() - await Questionnaire.find().remove() - await User.find().remove() - }) - - afterEach(async function() { - await Questionnaire.find().remove() - await User.find().remove() - }) - - after(async function() { - await Questionnaire.find().remove() - await User.find().remove() - await resetDb() - }) - - describe('User routes', function() { - it('lists questionnaires', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const admin = await createUserSession(testAdmin) - const request = supertest.agent(admin.app) - - const newQuestionnaire = await new Questionnaire({ - name: 'Customer Application', - identifier: questionnaireIdentifiers.CUSTOMER, - }).save() - - return request.get('/api/questionnaires') - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(1) - expect(res.body[0]).to.have.property('_id', newQuestionnaire.id) - expect(res.body[0]).to.have.property('name', 'Customer Application') - expect(res.body[0]).to.have.property('identifier', questionnaireIdentifiers.CUSTOMER) - }) - .expect(200) - }) - it('shows a questionnaire', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const admin = await createUserSession(testAdmin) - const request = supertest.agent(admin.app) - - const newQuestionnaire = await new Questionnaire({ - name: 'Customer Application', - identifier: questionnaireIdentifiers.CUSTOMER, - }).save() - - return request.get(`/api/questionnaires/${newQuestionnaire._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body.name).to.equal(newQuestionnaire.name) - expect(res.body.identifier).to.equal(newQuestionnaire.identifier) - }) - .expect(200) - }) - it('doesn\'t show non-existing questionnaires', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const admin = await createUserSession(testAdmin) - const request = supertest.agent(admin.app) - - const notSavedQuestionnaire = new Questionnaire({ - name: 'Customer Application', - identifier: questionnaireIdentifiers.CUSTOMER, - }) - - return request.get(`/api/questionnaires/${notSavedQuestionnaire._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('message', 'Not found') - }) - .expect(404) - }) - it('updates a questionnaire', async function() { - const testAdmin = createTestUser('admin', ADMIN_ROLE) - const admin = await createUserSession(testAdmin) - const request = supertest.agent(admin.app) - - const section = { - name: 'Section Info', - fields: [{ - type: fieldTypes.TEXT, label: 'Section Field' - }] - } - - const newSection = { - name: 'New Section Info', - fields: [{ - type: fieldTypes.TEXT, label: 'New Section Field' - }] - } - - const savedQuestionnaire = await new Questionnaire({ - name: 'Customer Application', - identifier: questionnaireIdentifiers.CUSTOMER, - sections: [section] - }).save() - - const updatedQuestionnaire = {sections: [newSection]} - - return request.put(`/api/questionnaires/${savedQuestionnaire._id}`) - .send(updatedQuestionnaire) - .expect(res => { - expect(res.body).to.be.an('object') - // Check section change - expect(res.body.sections[0]).to.have.property('name', 'New Section Info') - // Check field change - expect(res.body.sections[0].fields[0]).to.have.property('label', 'New Section Field') - }) - .expect(200) - }) - }) -}) diff --git a/server1/tests/integration/user.spec.js b/server1/tests/integration/user.spec.js deleted file mode 100644 index 0af795ce..00000000 --- a/server1/tests/integration/user.spec.js +++ /dev/null @@ -1,636 +0,0 @@ -import Customer from '../../models/customer' -import Volunteer from '../../models/volunteer' -import Donor from '../../models/donor' -import app from '../../config/express' -import User from '../../models/user' -import { ADMIN_ROLE } from '../../../common/constants' -import { createUserSession, createTestUser } from '../helpers' - -import {searchUserAndSetNotification} from '../../lib/notification-sender' - -describe('User Api', function() { - before(async function() { - await initDb() - }) - - beforeEach(async function() { - await Customer.find().remove() - await Volunteer.find().remove() - await Donor.find().remove() - await User.find().remove() - }) - - after(async function() { - await Customer.find().remove() - await Volunteer.find().remove() - await Donor.find().remove() - await User.find().remove() - await resetDb() - }) - - describe('signup', function() { - it('signs up users', async function() { - const request = supertest.agent(app()) - const newUser = { - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678' - } - return await request.post('/api/auth/signup') - .send(newUser) - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('_id') - expect(res.body.firstName).to.equal('Frank') - expect(res.body.lastName).to.equal('Harper') - expect(res.body.email).to.equal('fharper@example.com') - }) - }) - - it('it requires a first name', async function() { - const request = supertest.agent(app()) - const newUser = { - lastName: 'Willis', - email: 'mwillis@example.com', - password: '12345678' - } - return await request.post('/api/auth/signup') - .send(newUser) - .expect(400) - .expect(res => { - expect(res.body.paths.firstName).to.equal('Please fill in your first name') - }) - }) - - it('it requires a last name', async function() { - const request = supertest.agent(app()) - const newUser = { - firstName: 'Willis', - email: 'mwillis@example.com', - password: '12345678' - } - return await request.post('/api/auth/signup') - .send(newUser) - .expect(400) - .expect(res => { - expect(res.body.paths.lastName).to.equal('Please fill in your last name') - }) - }) - - it('requires a password', async function() { - const request = supertest.agent(app()) - const newUser = { - firstName: 'Margaret', - lastName: 'Willis', - email: 'mwillis@example.com' - } - return await request.post('/api/auth/signup') - .send(newUser) - .expect(400) - .expect(res => { - expect(res.body.paths.password).to.equal('Password should be longer') - }) - }) - - it('requires a password to be at least 7 characters long', async function() { - const request = supertest.agent(app()) - const newUser = { - firstName: 'Margaret', - lastName: 'Willis', - email: 'mwillis@example.com', - password: '12345' - } - return await request.post('/api/auth/signup') - .send(newUser) - .expect(400) - .expect(res => { - expect(res.body.paths.password).to.equal('Password should be longer') - }) - }) - - it('does not allow signup if email address already has an account', async function() { - const request = supertest.agent(app()) - const newUser = { - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678' - } - - await request.post('/api/auth/signup') - .send(newUser) - - const newUser2 = { - firstName: 'Francine', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678' - } - return await request.post('/api/auth/signup') - .send(newUser2) - .expect(400) - .expect(res => { - expect(res.body.paths.email).to.equal('Email address already has an account') - }) - }) - }) - - describe('logging in', function() { - it('signs in', async function() { - const newUser = { - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678' - } - const request = supertest.agent(app()) - await request.post('/api/auth/signup') - .send(newUser) - - return await request.post('/api/auth/signin') - .send({email: 'fharper@example.com', password: '12345678'}) - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('_id') - }) - }) - - it('fails without an email', async function() { - const newUser = { - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678' - } - const request = supertest.agent(app()) - await request.post('/api/auth/signup') - .send(newUser) - - return await request.post('/api/auth/signin') - .send({password: '12345678'}) - .expect(400) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body.message).to.equal('Missing credentials') - }) - }) - - it('fails without a password', async function() { - const newUser = { - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678' - } - const request = supertest.agent(app()) - await request.post('/api/auth/signup') - .send(newUser) - - return await request.post('/api/auth/signin') - .send({email: 'fharper@example.com'}) - .expect(400) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body.message).to.equal('Missing credentials') - }) - }) - - it('fails with the wrong password', async function() { - const newUser = { - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678' - } - const request = supertest.agent(app()) - await request.post('/api/auth/signup') - .send(newUser) - - return await request.post('/api/auth/signin') - .send({email: 'fharper@example.com', password: 'xxxxxxxxx'}) - .expect(400) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body.message).to.equal('Unknown user or invalid password') - }) - }) - }) - - describe('listing user accounts', function () { - it('returns an empty array when there are no accounts', async function () { - const request = supertest.agent(app()) - return await request.get('/api/users') - .expect(200) - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(0) - }) - }) - - it('returns one account when there is one account', async function () { - await User.create({ - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678', - provider: 'local' - }) - - const request = supertest.agent(app()) - return await request.get('/api/users') - .expect(200) - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(1) - expect(res.body[0].firstName).to.equal('Frank') - expect(res.body[0].lastName).to.equal('Harper') - expect(res.body[0].email).to.equal('fharper@example.com') - }) - }) - - it('returns two account when there is two accounts', async function () { - await User.create({ - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678', - provider: 'local' - }) - await User.create({ - firstName: 'Bill', - lastName: 'Willis', - email: 'mwillis@example.com', - password: '12345678', - provider: 'local' - }) - - const request = supertest.agent(app()) - return await request.get('/api/users') - .expect(200) - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(2) - const frank = res.body.find(user => user.firstName === 'Frank') - expect(frank.firstName).to.equal('Frank') - expect(frank.lastName).to.equal('Harper') - expect(frank.email).to.equal('fharper@example.com') - const bill = res.body.find(user => user.firstName === 'Bill') - expect(bill.firstName).to.equal('Bill') - expect(bill.lastName).to.equal('Willis') - expect(bill.email).to.equal('mwillis@example.com') - }) - }) - }) - - describe('getting a user by userId', function () { - let request - beforeEach(async function() { - const user = createTestUser('admin', ADMIN_ROLE) - const session = await createUserSession(user) - request = supertest.agent(session.app) - }) - - it('Gets the user', async function () { - const user = await User.create({ - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678', - provider: 'local' - }) - await User.create({ - firstName: 'Bill', - lastName: 'Willis', - email: 'mwillis@example.com', - password: '12345678', - provider: 'local' - }) - - await request.get(`/api/admin/users/${user._id}`) - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body.firstName).to.equal('Frank') - expect(res.body.lastName).to.equal('Harper') - expect(res.body.email).to.equal('fharper@example.com') - }) - }) - - it('returns when the user does not exist', async function () { - const user = await User.create({ - firstName: 'Frank', - lastName: 'Harper', - email: 'fharper@example.com', - password: '12345678', - provider: 'local' - }) - await User.create({ - firstName: 'Bill', - lastName: 'Willis', - email: 'mwillis@example.com', - password: '12345678', - provider: 'local' - }) - - await User.remove({_id: user._id}) - - await request.get(`/api/admin/users/${user._id}`) - .expect(400) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body.message).to.equal(`UserId ${user._id} not found`) - }) - }) - - }) - - describe('updating a user', function() { - - - - - - - - - - - - - - - - - it('updates the database', async function(){ - const user = await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - - const adminUser = createTestUser('admin', ADMIN_ROLE) - const adminSession = await createUserSession(adminUser) - const adminReq = supertest.agent(adminSession.app) - - const requestBody = { - _id: user._id, - created: user.created, - displayName: user.displayName, - email: 'new.email@example.com', - firstName: 'newFirstName', - lastName: 'newLastName', - provider: user.provider, - roles: user.roles, - updated: user.updated - } - - await adminReq.put(`/api/admin/users/${user._id}`).send(requestBody).expect(200) - - const updatedUser = await User.findById(requestBody._id).lean() - expect(updatedUser).to.have.property('_id', requestBody._id) - expect(updatedUser).to.have.property('email', requestBody.email) - expect(updatedUser).to.have.property('firstName', requestBody.firstName) - expect(updatedUser).to.have.property('lastName', requestBody.lastName) - }) - - - - - - it('returns the updated user object', async function(){ - const user = await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - - const adminUser = createTestUser('admin', ADMIN_ROLE) - const adminSession = await createUserSession(adminUser) - const adminReq = supertest.agent(adminSession.app) - - const requestBody = { - _id: user._id, - created: user.created, - displayName: user.displayName, - email: 'new.email@example.com', - firstName: 'newFirstName', - lastName: 'newLastName', - provider: user.provider, - roles: user.roles, - updated: user.updated - } - - await adminReq.put(`/api/admin/users/${user._id}`).send(requestBody) - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('_id', requestBody._id) - expect(res.body).to.have.property('email', requestBody.email) - expect(res.body).to.have.property('firstName', requestBody.firstName) - expect(res.body).to.have.property('lastName', requestBody.lastName) - }) - }) - - it('admins can set other users as admins', async function(){ - const user = await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - - const adminUser = createTestUser('admin', ADMIN_ROLE) - const adminSession = await createUserSession(adminUser) - const adminReq = supertest.agent(adminSession.app) - - await adminReq.put(`/api/admin/users/${user._id}`) - .send({ isAdmin: true }) - .expect(200) - - const updatedUser = await User.findById(user._id).lean() - expect(updatedUser).to.have.property('roles') - expect(updatedUser.roles).to.include(ADMIN_ROLE) - }) - - // see https://github.com/freeCodeCamp/pantry-for-good/issues/347 - it('regular users cannot update another user profile', async function(){ - const user1 = await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - - const user2 = await User.create({ - firstName: 'first2', - lastName: 'last2', - email: '345@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - const user2Session = await createUserSession(user2) - const user2Req = supertest.agent(user2Session.app) - - const requestBody = { - _id: user1._id, - created: user1.created, - displayName: user1.displayName, - email: 'new.email@example.com', - firstName: 'newFirstName', - lastName: 'newLastName', - provider: user1.provider, - roles: user1.roles, - updated: user1.updated - } - - await user2Req.put(`/api/admin/users/${user1._id}`).send(requestBody).expect(403) - - const updatedUser = await User.findById(user1._id).lean() - expect(updatedUser).to.have.property('_id', user1._id) - expect(updatedUser).to.have.property('email', user1.email) - expect(updatedUser).to.have.property('firstName', user1.firstName) - expect(updatedUser).to.have.property('lastName', user1.lastName) - }) - }) - - describe('updating profile', function() { - it('regular users can update their own profile', async function(){ - const user = await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - - const userSession = await createUserSession(user) - const userReq = supertest.agent(userSession.app) - - const requestBody = { - _id: user._id, - created: user.created, - displayName: user.displayName, - email: 'new.email@example.com', - firstName: 'newFirstName', - lastName: 'newLastName', - provider: user.provider, - roles: user.roles, - updated: user.updated - } - - await userReq.put('/api/users/me').send(requestBody).expect(200) - - const updatedUser = await User.findById(user._id).lean() - expect(updatedUser).to.have.property('email', requestBody.email) - expect(updatedUser).to.have.property('firstName', requestBody.firstName) - expect(updatedUser).to.have.property('lastName', requestBody.lastName) - }) - - it('update ignores req.body properties: displayName, provider, salt, resetPasswordToken and roles', async function(){ - const user = await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - - const userBeforeUpdate = await User.findById(user._id) - - const userSession = await createUserSession(user) - const userReq = supertest.agent(userSession.app) - - const requestBody = { - _id: user._id, - email: 'new.email@example.com', - firstName: 'newFirstName', - lastName: 'newLastName', - displayName: 'xxxxxx', - provider: 'xxxxxx', - salt: 'xxxxxx', - resetPasswordToken: 'xxxxxx', - resetPasswordExpires: 'xxxxxx', - roles: 'xxxxxx', - } - - await userReq.put('/api/users/me').send(requestBody).expect(200) - - const userAfterUpdate = await User.findById(user._id) - - expect(userAfterUpdate).to.have.property('email', requestBody.email) - expect(userAfterUpdate).to.have.property('firstName', requestBody.firstName) - expect(userAfterUpdate).to.have.property('lastName', requestBody.lastName) - expect(userAfterUpdate).to.have.property('salt', userBeforeUpdate.salt) - expect(userAfterUpdate).to.have.property('provider', userBeforeUpdate.provider) - expect(userAfterUpdate).to.have.property('resetPasswordToken', userBeforeUpdate.resetPasswordToken) - expect(userAfterUpdate).to.have.property('resetPasswordExpires', userBeforeUpdate.resetPasswordExpires) - }) - - it('regular users cannot make themselves admins', async function(){ - const user = await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [], - provider: 'local', - password: '12345678' - }) - - const userSession = await createUserSession(user) - const userReq = supertest.agent(userSession.app) - - await userReq.put('/api/users/me') - .send({ isAdmin: true }) - .expect(200) - - const updatedUser = await User.findById(user._id).lean() - expect(updatedUser).to.have.property('roles') - expect(updatedUser.roles).to.not.include(ADMIN_ROLE) - }) - }) - - describe('users notifications', function() { - it('function for creating an admin notifications', async function(){ - await User.create({ - firstName: 'first', - lastName: 'last', - email: '123@example.com', - roles: [ADMIN_ROLE], - provider: 'local', - password: '12345678' - }) - - // Sent Notifications - await searchUserAndSetNotification('roles/admin', {message:`Customer customer test was created!`, url: `/customers/2018`}, 2018) - - const request = supertest.agent(app()) - return await request.post('/api/auth/signin') - .send({email: '123@example.com', password: '12345678'}) - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('notifications') - expect(res.body.notifications[0]).to.have.property('message') - expect(res.body.notifications[0]).to.have.property('url') - expect(res.body.notifications[0]).to.have.property('date') - expect(res.body.notifications[0].message).to.equal('Customer customer test was created!') - }) - }) - }) -}) diff --git a/server1/tests/integration/volunteer.spec.js b/server1/tests/integration/volunteer.spec.js deleted file mode 100644 index 43943020..00000000 --- a/server1/tests/integration/volunteer.spec.js +++ /dev/null @@ -1,169 +0,0 @@ -import { - ADMIN_ROLE, - clientRoles, - questionnaireIdentifiers -} from '../../../common/constants' -import {createUserSession, createTestUser} from '../helpers' -import User from '../../models/user' -import Questionnaire from '../../models/questionnaire' -import Volunteer from '../../models/volunteer' - -import {searchVolunteerAndSetNotification} from '../../lib/notification-sender' - -describe('Volunteer Api', function() { - before(async function() { - await initDb() - await Volunteer.find().remove() - await User.find().remove() - await Questionnaire.find().remove() - await new Questionnaire({ - name: 'Volunteer Application', - identifier: questionnaireIdentifiers.VOLUNTEER - }).save() - }) - - afterEach(async function() { - await Volunteer.find().remove() - await User.find().remove() - }) - - after(async function() { - await Questionnaire.find().remove() - await resetDb() - }) - - describe('User routes', function() { - it('creates volunteers', async function() { - const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) - const {user, app} = await createUserSession(testVolunteer) - const request = supertest.agent(app) - - return request.post('/api/volunteer') - .send(user) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('status', 'Pending') - }) - .expect(200) - }) - - it('shows a volunteer', async function() { - const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) - const {user, app} = await createUserSession(testVolunteer) - const request = supertest.agent(app) - - const newVolunteer = (await request.post('/api/volunteer') - .send(user)).body - - return request.get(`/api/volunteer/${newVolunteer._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('status', 'Pending') - }) - .expect(200) - }) - - it('updates a volunteer', async function() { - const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) - const {user, app} = await createUserSession(testVolunteer) - const request = supertest.agent(app) - - const newVolunteer = (await request.post('/api/volunteer') - .send(user)).body - - const updatedVolunteer = { - ...newVolunteer, - firstName: 'updated' - } - - return request.put(`/api/volunteer/${updatedVolunteer._id}`) - .send(updatedVolunteer) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'updated') - }) - .expect(200) - }) - }) - - describe('Admin routes', function() { - it('lists volunteers', async function() { - const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) - const testAdmin = createTestUser('admin', ADMIN_ROLE) - - const volunteer = await createUserSession(testVolunteer) - const admin = await createUserSession(testAdmin) - - const volunteerRequest = supertest.agent(volunteer.app) - const adminRequest = supertest.agent(admin.app) - - await volunteerRequest.post('/api/volunteer') - .send(volunteer.user) - - return adminRequest.get('/api/admin/volunteers') - .expect(res => { - expect(res.body).to.be.an('array') - expect(res.body).to.have.length(1) - expect(res.body[0]).to.have.property('firstName', 'user') - }) - .expect(200) - }) - - it('deletes volunteers', async function() { - const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) - const testAdmin = createTestUser('admin', ADMIN_ROLE) - - const volunteer = await createUserSession(testVolunteer) - const admin = await createUserSession(testAdmin) - - const volunteerRequest = supertest.agent(volunteer.app) - const adminRequest = supertest.agent(admin.app) - - const newVolunteer = (await volunteerRequest.post('/api/volunteer') - .send(volunteer.user)).body - - return adminRequest.delete(`/api/admin/volunteers/${newVolunteer._id}`) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('firstName', 'user') - }) - .expect(200) - }) - }) - - describe('Volunteer notifications', function() { - it('creates volunteer notifications', async function(){ - const testVolunteer = createTestUser('user', clientRoles.VOLUNTEER) - const testAdmin = createTestUser('admin', ADMIN_ROLE) - - const volunteer = await createUserSession(testVolunteer) - const admin = await createUserSession(testAdmin) - - const volunteerRequest = supertest.agent(volunteer.app) - const adminRequest = supertest.agent(admin.app) - - const newVolunteer = (await volunteerRequest.post('/api/volunteer') - .send(volunteer.user)).body - - await (adminRequest.put(`/api/admin/volunteers/${newVolunteer._id}`).send({ - status: 'Active', - customers: [10077] - })) - - await searchVolunteerAndSetNotification({message: 'Customer Pepe Gonzales was updated!', url: '/customers/10077'}, 10077) - - return volunteerRequest.get('/api/users/me') - .expect(200) - .expect(res => { - expect(res.body).to.be.an('object') - expect(res.body).to.have.property('notifications') - expect(res.body.notifications).to.have.length(1) - - const notification = res.body.notifications[0] - expect(notification).to.have.property('url', '/customers/10077') - expect(notification).to.have.property('message', 'Customer Pepe Gonzales was updated!') - expect(notification).to.have.property('date') - }) - }) - }) -}) From 7efa73cbf51fc08fe654ad0bc3d20c3503fa0308 Mon Sep 17 00:00:00 2001 From: Samuel Freiberg Date: Fri, 26 Apr 2019 14:57:05 -0700 Subject: [PATCH 9/9] Adding weight input value --- .../customer/components/CustomerList.js | 1 - .../components/MassImportsModal.spec.js | 134 ++++++++++++++++++ .../donor/components/DonationItemRow.js | 11 ++ .../modules/donor/components/DonationView.js | 7 + .../food/components/inventory/FoodItems.js | 3 - .../inventory/MassImportsModal.spec.js | 105 ++++++++++++++ package.json | 4 +- server/models/donation.js | 4 + 8 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 client/modules/customer/components/MassImportsModal.spec.js create mode 100644 client/modules/food/components/inventory/MassImportsModal.spec.js diff --git a/client/modules/customer/components/CustomerList.js b/client/modules/customer/components/CustomerList.js index 358e053c..033d6fbf 100644 --- a/client/modules/customer/components/CustomerList.js +++ b/client/modules/customer/components/CustomerList.js @@ -97,7 +97,6 @@ class CustomerList extends Component { customers={this.props.customers} closeModal={this.closeModal} massUpload={this.props.massUpload} - duplicate={this.props.duplicate} /> diff --git a/client/modules/customer/components/MassImportsModal.spec.js b/client/modules/customer/components/MassImportsModal.spec.js new file mode 100644 index 00000000..d1a95259 --- /dev/null +++ b/client/modules/customer/components/MassImportsModal.spec.js @@ -0,0 +1,134 @@ +import React from 'react' +import Enzyme, { shallow } from 'enzyme' +import Adapter from 'enzyme-adapter-react-15' + +import MassImportsModal from './MassImportsModal' + +Enzyme.configure({adapter: new Adapter()}) + +describe('MassImportsModal', () => { + let props + + beforeEach(() => { + props = { + MassImportsModal: [], + customers: [], + closeModal: sinon.spy(), + massUpload: sinon.spy() + } + }) + + it('sets state when constructed', () => { + const wrapper = shallow() + expect(wrapper.state().validInput).to.be.false + expect(wrapper.state().documents).to.be.empty + }) + + it('isDuplicate returns true if a customer is a duplicate', () => { + const wrapper = shallow() + const instance = wrapper.instance() + var customer = { + firstName: 'Kobe', + lastName: 'Bryant', + email: "goat@gmail.com" + } + var customers = [] + customers.push(customer) + + expect(instance.isDuplicate("Kobe", "Bryant", "goat@gmail.com", customers)).to.be.true + }) + + it('isDuplicate returns false if a customer is not a duplicate', () => { + const wrapper = shallow() + const instance = wrapper.instance() + var customer = { + firstName: 'Kobe', + lastName: 'Bryant', + email: "goat@gmail.com" + } + var customers = [] + customers.push(customer) + + expect(instance.isDuplicate("Sammy", "Bryant", "goat@gmail.com", customers)).to.be.false + }) + + describe("validateHeaders", () => { + it('returns true if the headers are valid', () => { + const wrapper = shallow() + const instance = wrapper.instance() + var headers = ["First Name", "Last Name", "Email", "Birthday", "Street", "City/Town", "State/Province", "ZIP"] + + expect(instance.validateHeaders(headers)).to.be.true + }) + + it('returns false if the length of headers is less than 8', () => { + const wrapper = shallow() + const instance = wrapper.instance() + var headers = ["First Name", "Last Name", "Email", "Birthday", "Street", "City/Town", "State/Province"] + + expect(instance.validateHeaders(headers)).to.be.false + }) + + it('returns false if the length of headers is greater than 8', () => { + const wrapper = shallow() + const instance = wrapper.instance() + var headers = ["First Name", "Last Name", "Email", "Birthday", "Street", "City/Town", "State/Province", "ZIP", "Test"] + + expect(instance.validateHeaders(headers)).to.be.false + }) + + it('returns false if the length of headers is 8, but wrong values in headrs', () => { + const wrapper = shallow() + const instance = wrapper.instance() + var headers = ["First Name", "Wrong", "Email", "Birthday", "Street", "City/Town", "State/Province", "ZIP"] + + expect(instance.validateHeaders(headers)).to.be.false + }) + }) + + it("validateRow returns true for valid row", () => { + const wrapper = shallow() + const instance = wrapper.instance() + var row = ["1", "2", "3", "4", "5", "6", "7", "8"] + + expect(instance.validateRow(row)).to.be.true + }) + + it("validateRow returns false for row length less than 8", () => { + const wrapper = shallow() + const instance = wrapper.instance() + var row = ["1", "2", "3", "4", "5", "6", "7"] + + expect(instance.validateRow(row)).to.be.false + }) + + it("validateRow returns false for row length greater than 8", () => { + const wrapper = shallow() + const instance = wrapper.instance() + var row = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] + + expect(instance.validateRow(row)).to.be.false + }) + + it("validateRow returns false for empty values", () => { + const wrapper = shallow() + const instance = wrapper.instance() + var row = ["1", "2", "", "4", "5", "6", "7", "8"] + + expect(instance.validateRow(row)).to.be.false + }) + + it("importData calls props.massUpload", () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.importData() + expect(instance.props.massUpload.called).to.be.true + }) + + it("importData calls props.closeModal", () => { + const wrapper = shallow() + const instance = wrapper.instance() + instance.importData() + expect(instance.props.closeModal.called).to.be.true + }) +}) diff --git a/client/modules/donor/components/DonationItemRow.js b/client/modules/donor/components/DonationItemRow.js index 3824f664..067648e3 100644 --- a/client/modules/donor/components/DonationItemRow.js +++ b/client/modules/donor/components/DonationItemRow.js @@ -26,6 +26,17 @@ const DonationItemRow = ({item, showDelete, handleDelete}) => margin: '10px 0 0 0' }} /> + {showDelete &&