From bf956be0c4d40fb6dd43fcbc613937e1f419e5f2 Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 13:47:20 -0700 Subject: [PATCH 01/10] Adds Slugbyte backend --- backend/.babelrc | 4 + backend/.gitignore | 147 + backend/.yarnclean | 42 + backend/README.md | 251 + backend/index.js | 3 + backend/package.json | 56 + backend/src/__test__/asset/test-asset.png | Bin 0 -> 54514 bytes backend/src/__test__/db.test.js | 8 + backend/src/__test__/lib/clean-db.js | 12 + backend/src/__test__/lib/mock-photo.js | 27 + backend/src/__test__/lib/mock-profile.js | 27 + backend/src/__test__/lib/mock-s3.js | 25 + backend/src/__test__/lib/mock-user.js | 22 + backend/src/__test__/lib/test.env.js | 9 + backend/src/__test__/router-auth.test.js | 86 + backend/src/__test__/router-photo.test.js | 193 + backend/src/__test__/router-profile.test.js | 255 + backend/src/__test__/test-setup.js | 1 + backend/src/__test__/user.test.js | 95 + backend/src/__test__/util.test.js | 94 + backend/src/lib/db.js | 26 + backend/src/lib/server.js | 48 + backend/src/lib/util.js | 76 + backend/src/main.js | 2 + .../src/middleware/bind-response-methods.js | 16 + backend/src/middleware/error-handler.js | 26 + backend/src/middleware/four-oh-four.js | 6 + backend/src/middleware/index.js | 33 + backend/src/middleware/parser-auth.js | 54 + backend/src/middleware/parser-body.js | 20 + backend/src/middleware/router-auth.js | 41 + backend/src/middleware/router-photo.js | 36 + backend/src/middleware/router-profile.js | 41 + backend/src/model/index.js | 9 + backend/src/model/photo.js | 109 + backend/src/model/profile.js | 112 + backend/src/model/user.js | 61 + backend/yarn.lock | 4585 +++++++++++++++++ 38 files changed, 6658 insertions(+) create mode 100644 backend/.babelrc create mode 100644 backend/.gitignore create mode 100644 backend/.yarnclean create mode 100644 backend/README.md create mode 100644 backend/index.js create mode 100644 backend/package.json create mode 100644 backend/src/__test__/asset/test-asset.png create mode 100644 backend/src/__test__/db.test.js create mode 100644 backend/src/__test__/lib/clean-db.js create mode 100644 backend/src/__test__/lib/mock-photo.js create mode 100644 backend/src/__test__/lib/mock-profile.js create mode 100644 backend/src/__test__/lib/mock-s3.js create mode 100644 backend/src/__test__/lib/mock-user.js create mode 100644 backend/src/__test__/lib/test.env.js create mode 100644 backend/src/__test__/router-auth.test.js create mode 100644 backend/src/__test__/router-photo.test.js create mode 100644 backend/src/__test__/router-profile.test.js create mode 100644 backend/src/__test__/test-setup.js create mode 100644 backend/src/__test__/user.test.js create mode 100644 backend/src/__test__/util.test.js create mode 100644 backend/src/lib/db.js create mode 100644 backend/src/lib/server.js create mode 100644 backend/src/lib/util.js create mode 100644 backend/src/main.js create mode 100644 backend/src/middleware/bind-response-methods.js create mode 100644 backend/src/middleware/error-handler.js create mode 100644 backend/src/middleware/four-oh-four.js create mode 100644 backend/src/middleware/index.js create mode 100644 backend/src/middleware/parser-auth.js create mode 100644 backend/src/middleware/parser-body.js create mode 100644 backend/src/middleware/router-auth.js create mode 100644 backend/src/middleware/router-photo.js create mode 100644 backend/src/middleware/router-profile.js create mode 100644 backend/src/model/index.js create mode 100644 backend/src/model/photo.js create mode 100644 backend/src/model/profile.js create mode 100644 backend/src/model/user.js create mode 100644 backend/yarn.lock diff --git a/backend/.babelrc b/backend/.babelrc new file mode 100644 index 0000000..831f20a --- /dev/null +++ b/backend/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["es2015"], + "plugins": ["transform-object-rest-spread"] +} diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..5db738b --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,147 @@ +db +*.env +.*.env +coverage +temp + + +# Created by https://www.gitignore.io/api/osx,vim,node,linux,windows + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/osx,vim,node,linux,windows diff --git a/backend/.yarnclean b/backend/.yarnclean new file mode 100644 index 0000000..25bdd14 --- /dev/null +++ b/backend/.yarnclean @@ -0,0 +1,42 @@ +# test directories +__tests__ +test +tests +powered-test + +# asset directories +docs +doc +website +images +assets + +# examples +example +examples + +# code coverage directories +coverage +.nyc_output + +# build scripts +Makefile +Gulpfile.js +Gruntfile.js + +# configs +.tern-project +.gitattributes +.editorconfig +.*ignore +.eslintrc +.jshintrc +.flowconfig +.documentup.json +.yarn-metadata.json +.*.yml +*.yml + +# misc +*.gz +*.md diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..3d48417 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,251 @@ +SLUGGRAM +=== +> a social photo platform REST API + +## Configureation +Create a `.env` file and configure it with the following enviroment variables +``` bash +PORT=3000 +DEBUG=true +CORS_ORIGINS='' +MONGO_URI='' +SECRET='' +AWS_ACCESS_KEY_ID='' +AWS_SECRET_ACCESS_KEY='' +AWS_BUCKET='' +``` + +## Running Sluggam +* Start a mongodb `yarn db-on` +* Start the server `yarn start` + +## API Resources +#### User Model +The user model is used in the backend strickly for authentication and authorization. The user model will never be returned from the API, however userID's are stored on Profiles, Photos, and Comments for authorzation validation. + +* `_id` - an unique database genorated string which uniqly identifys a user +* `email` - a unique string which stores the users email +* `username` - a unique string that stores the users username +* `passwordHash` - a string that holds a users hashed password +* `tokenSeed` - a unique and random string used to genorate authorization tokens + +#### Profile Model +Each user can have a single profile. Authorization is required for Creating, Updating, and Deleteing Profiles but they have public read access. + +* `_id` - an unique database genorated string which uniqly identifys a profile +* `owner` - the user id of the profiles creator +* `email` - a unique string which stores the profiles email +* `username` - a unique string that stores the profiles profilename +* `avatar` - a string holding a URL to a profile photo +* `bio` - a string holding a profiles bio + +#### Photo Model +Each user can have may photos. Authorization is required for Creating, Updating, and Deleteing Photos but they have public read access. + +* `_id` - an unique database genorated string which uniqly identifys a profile +* `owner` - the user id of the photos creator +* `profile` - stores a the creators profile ID. the profile is populated on GET requests +* `comments` - stores an array of comment IDs. the comments are populated on GET requests +* `url` - a string which store a url to the photo +* `description` - a string with a description of the photo + +#### Comment Model +Each user can have many comments, and each photo can have may comments. Authorization is required for Creating, Updating, and Deleteing Comments but they have public read access. + +* `_id` - an unique database genorated string which uniqly identifys a profile +* `owner` - the user id of the photos creator +* `profile` - stores a the creators profile ID. the profile is populated on GET requests +* `photoID` - stores the photo id of the photo the comment is a response to +* `content` - a string with the users comment + +## Auth +Sluggram uses Basic authentication and Bearer authorization to enforce access controls. Basic and Bearer auth both use the HTTP `Authorization` header to pass credentials on a request. + +#### Basic Authentication +Once a user account has been created Basic Authentication can be used to make a request on behalf of the account. To create a Basic Authorzation Header the client must base64 encode a string with the username and password seporated by a colon. Then the encoded string can then be appened to the string `'Basic '` and set to an `Authorization` header on an HTTP Request. + +``` javascript +// Example of formating a Basic Authentication header in Javascript +let username = 'slugbyte' +let password = 'abcd1234' + +let encoded = window.btoa(`${username}:${password}`) +let headers = { + Authorization: `Basic ${encoded}` +} +``` + +#### Bearer Authorization +After a successfull signup or login request the client will receive a token. Bearer Authorization uses that token to make a request on behalf of that user account. The token should be append to the string `'Bearer '` and set to an Authorization header on an HTTP Request. + +``` javascript +// Example of formating a Bearer Authorization header in Javascript +let token = '11983261983261982643918649814613298619823698243' + +let headers = { + Authorization: `Beaer ${token}` +} +``` + +--- + + +#### POST `/signup` +a HTTP POST request to /signup will create a new user account. + +###### request +* Expected Headers + * Content-Type: application/json +* Request Body + * JSON containing a username, email and password + +``` json +{ + "username": "slugbyte", + "email": "slugbyte@slugbyte.com", + "password": "abcd1234" +} +``` + +###### response +The response body will be a **bearer token**. + +--- + +#### GET `/login` +A HTTP GET request to /login will login (fetch a token) to an existing user account. + +###### request +* Expected Headers + * Basic Authorization for the user account + +###### response +The response body will be a **bearer token**. + +## Profiles +#### POST `/profiles` +A HTTP POST request to /profiles will create a new profile. + +###### request +* Expected Headers + * Bearer authorization + * Content-Type: multipart/form-data +* Expected Body + * a `bio` field containing string with the users bio + * a `image` filed with the users avatar image + +###### response +the response will be a JSON profile + +--- + +#### GET `/profiles` +a HTTP GET request to /profiles will return an array of profiles +###### request +* Optional Query Paramiters + * SEE PAGINATION + +###### response +See pageination + +--- + +#### GET `/profiles/:id` +a HTTP GET request to /profiles/:id will return a profile +###### response +the response will return a JSON profile + +--- + +#### GET `/profiles/me` +a HTTP GET request to /profiles/:id will return a profile +###### request +* Expected Headers + * Bearer authorization +###### response +the response will return a users JSON profile + + +--- + +#### PUT `/profiles/:id` +a HTTP PUT request to /profiles/:id will update a profile +###### request +* Expected Headers + * Bearer authorization + * Content-Type: multipart/form-data or application/json +* Optional Body Fields + * an optional `image` filed with the users avatar image + * photo uploads are only posible for Content-Type: multipart/form-data + * an optional `bio` field containing string with the users bio + +###### response +the response will return a JSON profile + +--- + +#### DELETE `/profiles/:id` +a HTTP DELETE request to /profiles/:id will delete a profile +###### request +* Expected Headers + * Bearer authorization + +###### response +the response will have no body and a status of **204** + +## Photos +#### POST `/photos` +A HTTP POST request to /photos will create a new photo. A photo cannot be created until the User has created a profile. + +###### request +* Expected Headers + * Bearer authorization + * Content-Type: multipart/form-data +* Expected Body + * a `photo` filed with the file asset + * a `description` field + +###### response +the response will be a JSON photo + +#### GET `/photos` +a HTTP GET request to /photos will return an array of photos +###### request +* Optional Query Paramiters + * SEE PAGINATION + +###### response +See pageination + +#### GET `/photos/:id` +a HTTP GET request to /photos/:id will return a photo +###### response +the response will return a JSON profile + +#### PUT `/photos/:id` +a HTTP PUT request to /photos/:id will update a profile + +###### request +* Expected Headers + * Bearer authorization + * Content-Type: multipart/form-data or application/json +* Optional Body Fields + * an optional `photo` filed with a replacement photo + * photo uploads are only posible for Content-Type: multipart/form-data + * an optional `description` + +#### DELETE `/photos/:id` +a HTTP DELETE request to /photos/:id will delete a profile +###### request +* Expected Headers + * Bearer authorization + +###### response +the response will have no body and a status of **204** + +## Comments +#### POST `/comments` +#### GET `/comments` +#### GET `/comments/:id` +#### PUT `/comments/:id` +#### DELETE `/comments/:id` diff --git a/backend/index.js b/backend/index.js new file mode 100644 index 0000000..148f5a6 --- /dev/null +++ b/backend/index.js @@ -0,0 +1,3 @@ +require('dotenv').config() +require('babel-register') +require('./src/main.js') diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..ca64076 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,56 @@ +{ + "name": "sluggram", + "version": "1.0.0", + "main": "index.js", + "repository": "git@github.com:slugbyte/sluggram.git", + "author": "Duncan Marsh ", + "license": "MIT", + "jest": { + "setupFiles": [ + "/src/__test__/lib/test.env.js", + "/src/__test__/lib/mock-s3.js" + ] + }, + "scripts": { + "lint": "eslint .", + "start": "node index.js", + "watch": "nodemon index.js", + "test": "jest --runInBand", + "test-watch": "jest --runInBand --watchAll", + "db-on": "mkdir -p ./db && mongod --dbpath ./db", + "db-off": "killall mongod" + }, + "dependencies": { + "aws-sdk": "^2.92.0", + "aws-sdk-mock": "^1.7.0", + "babel": "^6.23.0", + "babel-cli": "^6.24.1", + "babel-core": "^6.25.0", + "babel-loader": "^7.1.1", + "babel-plugin-transform-object-rest-spread": "^6.23.0", + "babel-preset-es2015": "^6.24.1", + "babel-register": "^6.24.1", + "bcrypt": "^1.0.2", + "body-parser": "^1.17.2", + "cookie-parser": "^1.4.3", + "cors": "^2.8.4", + "dotenv": "^4.0.0", + "express": "^4.15.3", + "faker": "^4.1.0", + "fs-extra": "^4.0.0", + "http-errors": "^1.6.1", + "json-parser": "^1.1.5", + "jsonwebtoken": "^7.4.1", + "mongoose": "^4.11.4", + "morgan": "^1.8.2", + "multer": "^1.3.0", + "nodemon": "^1.11.0", + "ramda": "^0.24.1", + "webpack": "^3.4.1" + }, + "devDependencies": { + "babel-jest": "^20.0.3", + "jest": "^20.0.4", + "superagent": "^3.5.2" + } +} diff --git a/backend/src/__test__/asset/test-asset.png b/backend/src/__test__/asset/test-asset.png new file mode 100644 index 0000000000000000000000000000000000000000..22f4c0ac32bdd74d4132e29c46cf93f34cabc43d GIT binary patch literal 54514 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 { + test('#start and #stop should not reject', () => { + return db.start() + .then(() => db.stop()) + }) +}) diff --git a/backend/src/__test__/lib/clean-db.js b/backend/src/__test__/lib/clean-db.js new file mode 100644 index 0000000..782ed15 --- /dev/null +++ b/backend/src/__test__/lib/clean-db.js @@ -0,0 +1,12 @@ +import fs from 'fs-extra' +import User from '../../model/user.js' +import Photo from '../../model/photo.js' +import Profile from '../../model/profile.js' + +export default () => Promise.all([ + fs.remove(`${__dirname}/../../../temp/*`), + User.remove({}), + Photo.remove({}), + Profile.remove({}), + ]) + diff --git a/backend/src/__test__/lib/mock-photo.js b/backend/src/__test__/lib/mock-photo.js new file mode 100644 index 0000000..fa318d3 --- /dev/null +++ b/backend/src/__test__/lib/mock-photo.js @@ -0,0 +1,27 @@ +import faker from 'faker' +import * as _ from 'ramda' +import Photo from '../../model/photo.js' +import {mockProfile} from './mock-profile.js' + +export const mockPhoto = () => { + return mockProfile() + .then(({profile, userData}) => { + return new Photo({ + url: faker.image.image(), + description: faker.lorem.sentence(), + profile: profile._id, + owner: userData.user._id, + }) + .save() + .then(photo => ({photo, profile, userData})) + }) +} + +export const mockManyPhotos = (count=100) => { + return Promise.all(_.map(() => mockPhoto(), Array(count))) + .then(photosData => { + return _.reduce((result, next) => { + return {...result, [next.photo._id]: next} + }, {}, photosData) + }) +} diff --git a/backend/src/__test__/lib/mock-profile.js b/backend/src/__test__/lib/mock-profile.js new file mode 100644 index 0000000..6d9fea8 --- /dev/null +++ b/backend/src/__test__/lib/mock-profile.js @@ -0,0 +1,27 @@ +import faker from 'faker' +import * as _ from 'ramda' +import {mockUser} from './mock-user.js' +import Profile from '../../model/profile.js' + +export const mockProfile = () => { + return mockUser() + .then((userData) => { + return new Profile({ + owner: userData.user.id, + email: userData.user.email, + username: userData.user.username, + bio: faker.lorem.words(10), + avatar: faker.image.image(), + }).save() + .then(profile => { + userData.user.profile = profile._id + return userData.user.save() + .then(() => profile) + }) + .then(profile => ({userData, profile})) + }) +} + +export const mockManyProfiles = (num=100) => { + return Promise.all(_.map(() => mockProfile(), Array(num))) +} diff --git a/backend/src/__test__/lib/mock-s3.js b/backend/src/__test__/lib/mock-s3.js new file mode 100644 index 0000000..28e17ae --- /dev/null +++ b/backend/src/__test__/lib/mock-s3.js @@ -0,0 +1,25 @@ +import faker from 'faker' +import AWS from 'aws-sdk-mock' + +AWS.mock('S3', 'upload', (params, cb) => { + if(params.ACL !== 'public-read') + return cb(new Error('VALIDATION ERROR: ACL must be public read')) + + if(params.Bucket !== 'test-bucket') + return cb(new Error('VALIDATION ERROR: ACL must be public read')) + + if(!params.Key || !params.Body) + return cb(new Error('VALIDATION ERROR: requires Key and Body')) + + return cb(null, { Location: faker.image.avatar() }) +}) + +AWS.mock('S3', 'deleteObject', (params, cb) => { + if(params.Bucket !== 'test-bucket') + return cb(new Error('VALIDATION ERROR: ACL must be public read')) + + if(!params.Key) + return cb(new Error('VALIDATION ERROR: requires Key')) + + return cb(null, {}) +}) diff --git a/backend/src/__test__/lib/mock-user.js b/backend/src/__test__/lib/mock-user.js new file mode 100644 index 0000000..8b6e913 --- /dev/null +++ b/backend/src/__test__/lib/mock-user.js @@ -0,0 +1,22 @@ +'use strict' + +import faker from 'faker' +import User from '../../model/user.js' + +export const mockUser = () => { + let result = { password: faker.internet.password() } + return User.create({ + username: faker.internet.userName(), + email: faker.internet.email(), + password: result.password, + randomHash: faker.random.uuid(), + }) + .then(user => { + result.user = user + return user.tokenCreate() + }) + .then(token => { + result.token = token + return result + }) +} diff --git a/backend/src/__test__/lib/test.env.js b/backend/src/__test__/lib/test.env.js new file mode 100644 index 0000000..38786e8 --- /dev/null +++ b/backend/src/__test__/lib/test.env.js @@ -0,0 +1,9 @@ +process.env.PORT = 7777 +process.env.MONGO_URI = 'mongodb://localhost/testing' +process.env.CORS_ORIGINS = `http://localhost:${process.env.PORT}` +process.env.SECRET = 'cool beans' +process.env.DEBUG = true +process.env.API_URL = `http://localhost:${process.env.PORT}` +process.env.AWS_ACCESS_KEY_ID='test-id' +process.env.AWS_SECRET_ACCESS_KEY='test-secret' +process.env.AWS_BUCKET='test-bucket' diff --git a/backend/src/__test__/router-auth.test.js b/backend/src/__test__/router-auth.test.js new file mode 100644 index 0000000..5323bd3 --- /dev/null +++ b/backend/src/__test__/router-auth.test.js @@ -0,0 +1,86 @@ +import request from 'superagent' +import cleanDB from './lib/clean-db.js' +import * as server from '../lib/server.js' +import {mockUser} from './lib/mock-user.js' +import * as _ from 'ramda' +import {each, partialRight} from '../lib/util.js' + +const API_URL = process.env.API_URL + +describe('routerAuth', () => { + beforeAll(server.start) + afterAll(server.stop) + afterEach(cleanDB) + + describe('POST /signup', () => { + let signupRequest = (data) => + request.post(`${API_URL}/signup`) + .set('Content-Type', 'application/json') + .send(data) + + let signupData = { + username: 'sharky', + password: 'redpill', + email: 'sharky@example.com', + } + + test('should respond with a token', () => { + return signupRequest(signupData) + .then(res => { + expect(res.status).toEqual(200) + expect(res.text).toBeTruthy() + }) + }) + + test('should respond with 400 bad request', () => { + return Promise.all([ + signupRequest({}).catch(res => res), + signupRequest({...signupData, email: undefined}).catch(res => res), + signupRequest({...signupData, username: undefined}).catch(res => res), + signupRequest({...signupData, password: undefined}).catch(res => res), + ]) + .then(partialRight(each, (res) => { + expect(res.status).toEqual(400) + })) + }) + }) + + describe('GET /login', () => { + let loginRequest = (data) => + request.get(`${API_URL}/login`) + .auth(data.user.username, data.password) + + test('should respond with a token', () => { + return mockUser() + .then(userData => loginRequest(userData)) + .then(res => { + expect(res.status).toEqual(200) + expect(res.text).toBeTruthy() + let XSlugramToken = res.headers['set-cookie'][0] + expect(XSlugramToken.indexOf(`X-Sluggram-Token=${res.text}`)).toBeGreaterThan(-1) + }) + }) + + test('should respond with 400', () => { + return Promise.all([ + request.get(`${API_URL}/login`).catch(res => res), + request.get(`${API_URL}/login`) + .set('Authorizaton', 'not valid').catch(res => res), + ]) + .then(_.forEach( res => { + expect(res.status).toEqual(400) + })) + }) + + test('should respond with 401', () => { + return mockUser() + .then(user => Promise.all([ + loginRequest({...user, password: 'bad-password'}).catch(res => res), + loginRequest({...user, user: {username: 'bad-username'}}).catch(res => res), + ])) + .then(_.forEach(res => { + expect(res.status).toEqual(401) + })) + }) + }) +}) diff --git a/backend/src/__test__/router-photo.test.js b/backend/src/__test__/router-photo.test.js new file mode 100644 index 0000000..537d1dc --- /dev/null +++ b/backend/src/__test__/router-photo.test.js @@ -0,0 +1,193 @@ +import * as _ from 'ramda' +import request from 'superagent' +import cleanDB from './lib/clean-db.js' +import * as server from '../lib/server.js' +import {mockProfile} from './lib/mock-profile.js' +import {mockPhoto, mockManyPhotos} from './lib/mock-photo.js' + +const API_URL = process.env.API_URL + +describe('router-photo.test.js', () => { + beforeAll(server.start) + afterAll(server.stop) + afterEach(cleanDB) + + describe('POST /api/photos', () => { + test('should create a photo', () => { + return mockProfile() + .then(({userData, profile}) => { + return request.post(`${API_URL}/photos`) + .set('Authorization', `Bearer ${userData.token}`) + .attach('photo', `${__dirname}/asset/test-asset.png`) + .field('description', 'example data') + .then(res => { + expect(res.status).toEqual(200) + profile = JSON.parse(JSON.stringify(profile)) + expect(res.body.owner).toEqual(userData.user._id.toString()) + expect(res.body.profile).toEqual(profile) + expect(res.body.url).toBeTruthy() + expect(res.body.description).toEqual('example data') + expect(res.body.comments).toEqual([]) + }) + }) + }) + + test('should respond with 400', () => { + return mockProfile() + .then(({userData, profile}) => { + return request.post(`${API_URL}/photos`) + .set('Authorization', `Bearer ${userData.token}`) + .field('description', 'example data') + .then(res => {throw res}) + .catch(res => { + expect(res.status).toEqual(400) + }) + }) + + }) + + test('should respond with 400', () => { + return mockProfile() + .then(({userData, profile}) => { + return request.post(`${API_URL}/photos`) + .set('Authorization', `Bearer ${userData.token}`) + .attach('photo', `${__dirname}/asset/test-asset.png`) + .then(res => {throw res}) + .catch(res => { + expect(res.status).toEqual(400) + }) + }) + }) + }) + + describe('GET /api/photos', () => { + let fetchPage = (page) => { + return request(`${API_URL}/photos?page=${page}`) + .catch(err => err) + } + + let compareBodyWithMock = (body , mock) => { + _.forEach((photo) => { + let mockPhoto = JSON.parse(JSON.stringify(mock[photo._id])) + expect(photo._id).toEqual(mockPhoto.photo._id) + expect(photo.owner).toEqual(mockPhoto.photo.owner) + expect(photo.description).toEqual(mockPhoto.photo.description) + expect(photo.url).toEqual(mockPhoto.photo.url) + expect(photo.profile).toEqual(mockPhoto.profile) + })(body) + } + + test('should respond with 100 photos', () => { + return mockManyPhotos() + .then(photosData => { + return fetchPage(1) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.count).toEqual(100) + expect(res.body.prev).toEqual(null) + expect(res.body.next).toEqual(null) + expect(res.body.last).toEqual(`${API_URL}/photos?page=1`) + compareBodyWithMock(res.body.data, photosData) + }) + }) + }) + + test('should respond with 50 photos', () => { + return mockManyPhotos(150) + .then(photosData => { + return fetchPage(2) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.count).toEqual(150) + expect(res.body.prev).toEqual(`${API_URL}/photos?page=1`) + expect(res.body.next).toEqual(null) + expect(res.body.last).toEqual(`${API_URL}/photos?page=2`) + expect(res.body.data.length).toEqual(50) + compareBodyWithMock(res.body.data, photosData) + }) + }) + }) + }) + + describe('GET /api/photos/:id', () => { + test('should fetch a photo', () => { + return mockPhoto() + .then(mock => { + return request.get(`${API_URL}/photos/${mock.photo._id}`) + .then(res => { + expect(res.status).toEqual(200) + }) + }) + }) + + test('should 404', () => { + return mockPhoto() + .then(mock => { + return request.get(`${API_URL}/photos/${mock.photo.owner}`) + .catch(res => res) + .then(res => { + expect(res.status).toEqual(404) + }) + }) + }) + }) + + describe('PUT /api/photos/:id', () => { + test('should respond with updated photo', () => { + return mockPhoto() + .then(mock => { + return request.put(`${API_URL}/photos/${mock.photo._id}`) + .set('Authorization', `Bearer ${mock.userData.token}`) + .attach('photo', `${__dirname}/asset/test-asset.png`) + .field('description', 'cool beans') + .then(res => { + expect(res.status).toEqual(200) + expect(res.body._id).toEqual(mock.photo._id.toString()) + expect(res.body.description).toEqual('cool beans') + expect(res.body.url).not.toEqual(mock.photo.url) + }) + }) + }) + + test('should respond with updated photo', () => { + return mockPhoto() + .then(mock => { + return request.put(`${API_URL}/photos/${mock.photo._id}`) + .set('Authorization', `Bearer ${mock.userData.token}`) + .attach('photo', `${__dirname}/asset/test-asset.png`) + .attach('photo', `${__dirname}/asset/test-asset.png`) + .field('description', 'cool beans') + .catch(res => res) + .then(res => { + expect(res.status).toEqual(400) + }) + }) + }) + }) + + describe('DELETE /api/photos/:id', () => { + test('should delete a photo', () => { + return mockPhoto() + .then(mock => { + return request.delete(`${API_URL}/photos/${mock.photo._id}`) + .set('Authorization', `Bearer ${mock.userData.token}`) + .then(res => { + expect(res.status).toEqual(204) + }) + }) + }) + + test('should 404', () => { + return mockPhoto() + .then(mock => { + return request.delete(`${API_URL}/photos/${mock.photo.owner}`) + .set('Authorization', `Bearer ${mock.userData.token}`) + .catch(res => res) + .then(res => { + expect(res.status).toEqual(404) + }) + }) + }) + }) + +}) diff --git a/backend/src/__test__/router-profile.test.js b/backend/src/__test__/router-profile.test.js new file mode 100644 index 0000000..e18ff7a --- /dev/null +++ b/backend/src/__test__/router-profile.test.js @@ -0,0 +1,255 @@ +import * as _ from 'ramda' +import request from 'superagent' +import cleanDB from './lib/clean-db.js' +import * as server from '../lib/server.js' +import {mockUser} from './lib/mock-user.js' +import {mockProfile, mockManyProfiles} from './lib/mock-profile.js' + +const API_URL = process.env.API_URL + +describe('router-profile', () => { + beforeAll(server.start) + afterAll(server.stop) + afterEach(cleanDB) + + describe('POST /profiles', () => { + let postJSONProfie = (data) => + mockUser() + .then(userData => { + return request.post(`${API_URL}/profiles`) + .set('Content-Type', 'application/json') + .set('Authorization', `Bearer ${userData.token}`) + .send(data) + .then(res => ({res, userData})) + }) + + let postMultipartProfie = (data) => + mockUser() + .then(userData => { + return request.post(`${API_URL}/profiles`) + .set('Content-Type', 'multipart/form-data') + .set('Authorization', `Bearer ${userData.token}`) + .attach('avatar', `${__dirname}/asset/test-asset.png`) + .field('bio', data.bio) + .then(res => ({res, userData})) + }) + + test('json post should respond with a profile', () => { + return postJSONProfie({bio: 'cool beans'}) + .then(({res, userData}) => { + expect(res.status).toEqual(200) + }) + }) + + test('multiparty post should respond with a profile', () => { + return postMultipartProfie({bio: 'cool beans'}) + .then(({res, userData}) => { + expect(res.status).toEqual(200) + expect(res.body.bio).toEqual('cool beans') + expect(res.body.owner).toEqual(userData.user._id.toString()) + expect(res.body.email).toEqual(userData.user.email) + expect(res.body.username).toEqual(userData.user.username) + }) + }) + }) + + describe('GET /profiles', () => { + let getProfileIdMap = _.reduce((result, next) => + ({...result, [next.profile._id]: JSON.parse(JSON.stringify(next))}) , {}) + + let compareMockWithResponse = (data) => { + let dataById = getProfileIdMap(data) + return _.forEach(profile => { + let mockedData = dataById[profile._id] + expect(profile.owner).toEqual(mockedData.profile.owner) + expect(profile.username).toEqual(mockedData.profile.username) + expect(profile.email).toEqual(mockedData.profile.email) + expect(profile.bio).toEqual(mockedData.profile.bio) + expect(profile.avatar).toEqual(mockedData.profile.avatar) + }) + } + + test('should return 100 profiles', () => { + return mockManyProfiles(175) + .then((profileData) => { + return request.get(`${API_URL}/profiles`) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.count).toEqual(175) + expect(res.body.data.length).toEqual(100) + expect(res.body.prev).toEqual(null) + expect(res.body.next).toEqual(`${API_URL}/profiles?page=2`) + expect(res.body.last).toEqual(`${API_URL}/profiles?page=2`) + compareMockWithResponse(profileData)(res.body.data) + }) + }) + }) + + test('?page=2 should return 50 profiles', () => { + return mockManyProfiles(150) + .then((profileData) => { + return request.get(`${API_URL}/profiles?page=2`) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.count).toEqual(150) + expect(res.body.data.length).toEqual(50) + expect(res.body.next).toEqual(null) + expect(res.body.prev).toEqual(`${API_URL}/profiles?page=1`) + expect(res.body.last).toEqual(`${API_URL}/profiles?page=2`) + compareMockWithResponse(profileData)(res.body.data) + }) + }) + }) + + test('?page=-1 should return 10 profiles', () => { + return mockManyProfiles(10) + .then((profileData) => { + return request.get(`${API_URL}/profiles?page=-1`) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.count).toEqual(10) + expect(res.body.prev).toEqual(null) + expect(res.body.next).toEqual(null) + expect(res.body.last).toEqual(`${API_URL}/profiles?page=1`) + compareMockWithResponse(profileData)(res.body.data) + }) + }) + }) + + test('?page=2 should return 100 profiles', () => { + return mockManyProfiles(300) + .then((profileData) => { + return request.get(`${API_URL}/profiles?page=2`) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.count).toEqual(300) + expect(res.body.prev).toEqual(`${API_URL}/profiles?page=1`) + expect(res.body.next).toEqual(`${API_URL}/profiles?page=3`) + expect(res.body.last).toEqual(`${API_URL}/profiles?page=3`) + expect(res.body.data.length).toEqual(100) + compareMockWithResponse(profileData)(res.body.data) + }) + }) + }) + + test('?page=3 should return 0 profiles', () => { + return mockManyProfiles(10) + .then(({userData, profiles}) => { + return request.get(`${API_URL}/profiles?page=3`) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.count).toEqual(10) + expect(res.body.prev).toEqual(null) + expect(res.body.next).toEqual(null) + expect(res.body.last).toEqual(`${API_URL}/profiles?page=1`) + expect(res.body.data.length).toEqual(0) + }) + }) + }) + }) + + describe('GET /profiles/:id', () => { + test('should return a profile', () => { + return mockProfile() + .then(({userData, profile}) => { + return request.get(`${API_URL}/profiles/${profile._id}`) + .then(res => { + expect(res.status).toEqual(200) + profile = JSON.parse(JSON.stringify(profile)) + expect(res.body).toEqual(profile) + }) + }) + }) + + test('should return a 404', () => { + return request.get(`${API_URL}/profiles/597e89cbcc524228f3c8092e`) + .catch(res => { + expect(res.status).toEqual(404) + }) + }) + }) + + describe('GET /profiles/me', () => { + test('should return user profile', () => { + return mockProfile() + .then(mock => { + return request(`${API_URL}/profiles/me`) + .set('Authorization', `Bearer ${mock.userData.token}`) + .then(res => { + expect(res.status).toEqual(200) + expect(res.body.owner).toEqual(mock.userData.user._id.toString()) + }) + }) + }) + }) + + describe('PUT /profiles/:id', () => { + let putJSONProfile = (bio) => { + return mockProfile() + .then(({userData, profile}) => { + return request.put(`${API_URL}/profiles/${profile._id}`) + .set('Authorization', `Bearer ${userData.token}`) + .send({bio}) + .then(res => ({res, userData, profile})) + }) + } + + let putMultipartProfile = (bio) => { + return mockProfile() + .then(({userData, profile}) => { + return request.put(`${API_URL}/profiles/${profile._id}`) + .set('Authorization', `Bearer ${userData.token}`) + .field('bio', bio) + .attach('avatar', `${__dirname}/asset/test-asset.png`) + .then(res => ({res, userData, profile})) + }) + } + + test('should update the bio', () => { + return putJSONProfile('cool beans') + .then(({res, profile}) => { + expect(res.status).toEqual(200) + profile = JSON.parse(JSON.stringify(profile)) + expect(res.body).toEqual({...profile, bio: 'cool beans'}) + }) + }) + + test('should update the bio and avatar', () => { + return putMultipartProfile('cool beans') + .then(({res, profile}) => { + expect(res.status).toEqual(200) + profile = JSON.parse(JSON.stringify(profile)) + expect(res.body._id).toEqual(profile._id) + expect(res.body.email).toEqual(profile.email) + expect(res.body.username).toEqual(profile.username) + expect(res.body.bio).toEqual('cool beans') + expect(res.body.avatar).not.toEqual(profile.avatar) + }) + }) + }) + + describe('DELETE /profiles/:id', () => { + test('should return a 204 status', () => { + return mockProfile() + .then(({userData, profile}) => { + return request.delete(`${API_URL}/profiles/${profile._id}`) + .set('Authorization', `Bearer ${userData.token}`) + .then(res => { + expect(res.status).toEqual(204) + }) + }) + }) + + test('should return a 404 status', () => { + return mockProfile() + .then(({userData, profile}) => { + return request.delete(`${API_URL}/profiles/${userData.user._id}`) + .set('Authorization', `Bearer ${userData.token}`) + .then(res => {throw res}) + .catch(res => { + expect(res.status).toEqual(404) + }) + }) + }) + }) +}) diff --git a/backend/src/__test__/test-setup.js b/backend/src/__test__/test-setup.js new file mode 100644 index 0000000..1abbde1 --- /dev/null +++ b/backend/src/__test__/test-setup.js @@ -0,0 +1 @@ +require('dotenv').config() diff --git a/backend/src/__test__/user.test.js b/backend/src/__test__/user.test.js new file mode 100644 index 0000000..42ff37a --- /dev/null +++ b/backend/src/__test__/user.test.js @@ -0,0 +1,95 @@ +'use stirct' + +import * as db from '../lib/db.js' +import {compare} from 'bcrypt' +import User from '../model/user.js' +import {mockUser} from './lib/mock-user.js' + +describe('USER', () => { + beforeAll(() => db.start()) + afterAll(db.stop) + afterEach(() => User.remove({})) + + describe('mockUser', () => { + test('should resolve a password, user, and token', () => { + return mockUser() + .then(({password, user, token}) => { + expect(token).toBeTruthy() + expect(password).toBeTruthy() + expect(user).toBeTruthy() + }) + }) + }) + + describe('%create', () => { + test('should not reject with valid data', () => { + let data = { + username: 'testuser', + password: 'abcd1234', + email: 'testuser@example.com' + } + + return User.create(data) + .then(user => { + expect(user._id).toBeTruthy() + expect(user.passwordHash).toBeTruthy() + expect(user.username).toEqual(data.username) + expect(user.email).toEqual(data.email) + expect(user.randomHash).toBe('') + return compare(data.password, user.passwordHash) + }) + .then(success => { + expect(success).toBeTruthy() + }) + }) + + test('should reject with no invalid data', () => { + let data = { + username: 'testuser', + password: 'abcd1234', + email: 'testuser@example.com' + } + + return Promise.all([ + User.create({...data, username: undefined}).catch(err => err), + User.create({...data, email: undefined}).catch(err => err), + User.create({...data, password: undefined}).catch(err => err), + ]) + .then(errors => { + errors.forEach(err => { + expect(err).toBeInstanceOf(Error) + expect(err.status).toBe(400) + }) + }) + }) + }) + + describe('#tokenCreate', () => { + test('should create a new token', () => { + let tokenCache + return mockUser() + .then(({token, user}) => { + tokenCache = token + return user.tokenCreate() + }) + .then((token) => { + expect(token).toBeTruthy() + expect(token).not.toEqual(tokenCache) + }) + }) + }) + + describe('#passwordCompare', () => { + test('to resolve the user', () => { + let userCache + return mockUser() + .then(({password, user}) => { + userCache = user + return user.passwordCompare(password) + }) + .then((user) => { + expect(user).toEqual(userCache) + }) + }) + }) +}) diff --git a/backend/src/__test__/util.test.js b/backend/src/__test__/util.test.js new file mode 100644 index 0000000..75ff9b2 --- /dev/null +++ b/backend/src/__test__/util.test.js @@ -0,0 +1,94 @@ +import * as util from '../lib/util.js' + +describe('util', () => { + test('#partial', () => { + let add = (...args) => args.reduce((a, b) => a + b) + let add10 = util.partial(add, 5, 2, 3) + + expect(add10(2)).toBe(12) + expect(add10(2, 50, 25, 20, 5)).toBe(112) + + let concat = (...args) => args.join(' ') + expect(util.partial(concat, 'hello')('world')).toBe('hello world') + }) + + test('#partialRight', () => { + let add = (...args) => args.reduce((a, b) => a + b) + let add10 = util.partialRight(add, 5, 2, 3) + + expect(add10(2)).toBe(12) + expect(add10(2, 50, 25, 20, 5)).toBe(112) + + let concat = (...args) => args.join(' ') + expect(util.partialRight(concat, 'hello')('world')).toBe('world hello') + }) + + test('#compose', () => { + let add10 = (arg) => arg + 10 + let sub10 = (arg) => arg - 10 + let div10 = (arg) => arg / 10 + let mul10 = (arg) => arg * 10 + + let composed = util.compose(mul10, add10, div10, sub10) + expect(composed(4)).toBe(-5) + }) + + test('#promisify', () => { + let mock = (arg, cb) => + arg == 'error' ? cb(new Error('TEST ERROR')) : cb(null, arg) + + let result = util.promisify(mock) + + return result('error') + .catch(err => { + expect(err).toBeInstanceOf(Error) + expect(err.message).toEqual('TEST ERROR') + return result('success') + }) + .then(data => { + expect(data).toEqual('success') + }) + }) + + describe('loggers', () => { + let cache = {} + + beforeAll(() => { + cache.DEBUG = process.env.DEBUG + cache.log = console.log + cache.error = console.error + console.log = jest.fn() + console.error = jest.fn() + }) + + afterAll(() => { + process.env.DEBUG = cache.DEBUG + console.log = cache.log + console.error = cache.error + }) + + test('#log', () => { + process.env.DEBUG = true + util.log() + expect(console.log).toHaveBeenCalled() + + console.log.mockClear() + + process.env.DEBUG = null + util.log('cool') + expect(console.log).not.toHaveBeenCalled() + }) + + test('#logError', () => { + process.env.DEBUG = true + util.logError() + expect(console.error).toHaveBeenCalled() + + console.error.mockClear() + + process.env.DEBUG = null + util.logError('cool') + expect(console.error).not.toHaveBeenCalled() + }) + }) +}) diff --git a/backend/src/lib/db.js b/backend/src/lib/db.js new file mode 100644 index 0000000..ebfbc37 --- /dev/null +++ b/backend/src/lib/db.js @@ -0,0 +1,26 @@ +'use strict' + +// DEPENDENCIES +import {log, error} from './util.js' +const mongoose = require('mongoose') +mongoose.Promise = Promise + +// STATE +const state = { isOn: false } + +// INTERFACE +export const start = () => { + log('__DB_UP__', process.env.MONGO_URI) + if(state.isOn) + return Promise.reject(new Error('USER ERROR: db is connected')) + state.isOn = true + return mongoose.connect(process.env.MONGO_URI, {useMongoClient: true}) +} + +export const stop = () => { + log('__DB_DOWN__') + if(!state.isOn) + return Promise.reject(new Error('USER ERROR: db is disconnected')) + state.isOn = false + return mongoose.disconnect() +} diff --git a/backend/src/lib/server.js b/backend/src/lib/server.js new file mode 100644 index 0000000..57cb11d --- /dev/null +++ b/backend/src/lib/server.js @@ -0,0 +1,48 @@ +'use strict' + +// DEPENDENCIES +import * as db from './db' +import express from 'express' +import middleware from '../middleware' +import {log, logError} from './util.js' + +// STATE +const app = express().use(middleware) +const state = { + isOn: false, + http: null, +} + +// INTERFACE +export const start = () => { + return new Promise((resolve, reject) => { + if (state.isOn) + return reject(new Error('USAGE ERROR: the state is on')) + state.isOn = true + db.start() + .then(() => { + state.http = app.listen(process.env.PORT, () => { + log('__SERVER_UP__', process.env.PORT) + resolve() + }) + }) + .catch(reject) + }) +} + +export const stop = () => { + return new Promise((resolve, reject) => { + if(!state.isOn) + return reject(new Error('USAGE ERROR: the state is off')) + return db.stop() + .then(() => { + state.http.close(() => { + log('__SERVER_DOWN__') + state.isOn = false + state.http = null + resolve() + }) + }) + .catch(reject) + }) +} diff --git a/backend/src/lib/util.js b/backend/src/lib/util.js new file mode 100644 index 0000000..74e5f4f --- /dev/null +++ b/backend/src/lib/util.js @@ -0,0 +1,76 @@ +// DEPENDENCIES +import AWS from 'aws-sdk' +import {extname} from 'path' +import fs from 'fs-extra' + +const s3 = new AWS.S3() + +// INTERFACE +export const partial = (fn, ...defaults) => + (...args) => fn(...defaults, ...args) + +export const partialRight = (fn, ...defaults) => + (...args) => fn(...args, ...defaults) + +export const compose = (...fns) => + (arg) => fns.reduce((result, next) => next(result), arg) + +export const promisify = (fn) => (...args) => + new Promise((resolve, reject) => + fn(...args, (err, data) => err ? reject(err) : resolve(data))) + +export const log = (...args) => + process.env.DEBUG === 'true' ? console.log(...args): undefined + +export const logError = (...args) => + process.env.DEBUG === 'true' ? console.error(...args) : undefined + +export const daysToMilliseconds = (days) => days * 1000 * 60 * 60 * 24 + +export const map = (list, cb) => + Array.prototype.map.call(list, cb) + +export const filter = (list, cb) => + Array.prototype.filter.call(list, cb) + +export const reduce = (list, ...args) => + Array.prototype.reduce.apply(list, args) + +export const each = (list, cb) => + Array.prototype.forEach.call(list, cb) + +export const removeMulterFile = (data) => fs.remove(data.path) +export const removeMulterFiles = (list) => Promise.all(list.map(removeMulterFile)) + +export const s3UploadMulterFileAndClean = (data) => { + return s3.upload({ + ACL: 'public-read', + Bucket: process.env.AWS_BUCKET, + Key: `${data.filename}.${data.originalname}`, + Body: fs.createReadStream(data.path), + }).promise() + // allways remove file and either pass on failure or success + .catch(err => fs.remove(data.path).then(() => {throw err})) + .then(s3Data => fs.remove(data.path).then(() => s3Data)) +} + +export const pagerCreate = (model, populate='') => (req, query={}) => { + let offset = (Number(req.query.page) - 1) || 0 + let itemLimit = 100 + let route = `${process.env.API_URL}/${model.modelName}s?page=` + return model.count() + .then(count => { + let remaining = count - offset * itemLimit + return model.find(query) + .populate(populate) + .skip(offset > 0 ? offset * itemLimit : 0) + .limit(itemLimit) + .then(profiles => ({ + count: count, + data: profiles, + last: `${route}${Math.floor((count - 1) / itemLimit) + 1}`, + prev: offset > 0 && remaining > 0 ? `${route}${offset}` : null, + next: offset > -1 && remaining > itemLimit ? `${route}${offset + 2}` : null, + })) + }) +} diff --git a/backend/src/main.js b/backend/src/main.js new file mode 100644 index 0000000..cb41504 --- /dev/null +++ b/backend/src/main.js @@ -0,0 +1,2 @@ +import {start} from './lib/server.js' +start({PORT: process.env.PORT, MONGO_URI: process.env.MONGO_URI}) diff --git a/backend/src/middleware/bind-response-methods.js b/backend/src/middleware/bind-response-methods.js new file mode 100644 index 0000000..4f1d8c2 --- /dev/null +++ b/backend/src/middleware/bind-response-methods.js @@ -0,0 +1,16 @@ +export default (req, res, next) => { + res.send = res.send.bind(res) + res.json = res.json.bind(res) + res.status = res.status.bind(res) + res.sendFile = res.sendFile.bind(res) + res.sendStatus = res.sendStatus.bind(res) + res.page = (data) => { + res.links({ + next: data.next, + prev: data.prev, + last: data.last, + }) + res.json(data) + } + next() +} diff --git a/backend/src/middleware/error-handler.js b/backend/src/middleware/error-handler.js new file mode 100644 index 0000000..92e98fe --- /dev/null +++ b/backend/src/middleware/error-handler.js @@ -0,0 +1,26 @@ +import {logError} from '../lib/util.js' + +// INTERFACE +export default (err, req, res, next) => { + console.log(err) + logError(err) + if(err.status) + return res.sendStatus(err.status) + + err.message = err.message.toLowerCase() + + if(err.message.includes('validation failed')) + return res.sendStatus(400) + + // if duplacte key respond with 409 + if(err.message.includes('duplicate key')) + return res.sendStatus(409) + + if(err.message.includes('objectid failed')) + return res.sendStatus(404) + + if(err.message.includes('unauthorized')) + return res.sendStatus(401) + + res.sendStatus(500) +} diff --git a/backend/src/middleware/four-oh-four.js b/backend/src/middleware/four-oh-four.js new file mode 100644 index 0000000..bd169c8 --- /dev/null +++ b/backend/src/middleware/four-oh-four.js @@ -0,0 +1,6 @@ +// DEPENDENCIES +import createError from 'http-errors' + +// INTERFACE +export default (req, res, next) => + next(createError(404, `USER ERROR: ${req.url.path} not a route`)) diff --git a/backend/src/middleware/index.js b/backend/src/middleware/index.js new file mode 100644 index 0000000..efaf015 --- /dev/null +++ b/backend/src/middleware/index.js @@ -0,0 +1,33 @@ +'use strict' + +// DEPENDENCIES +import cors from 'cors' +import morgan from 'morgan' +import {Router} from 'express' +import cookieParser from 'cookie-parser' +import routerAuth from './router-auth.js' +import fourOhFour from './four-oh-four.js' +import routerPhoto from './router-photo.js' +import errorHandler from './error-handler.js' +import routerProfile from './router-profile.js' +import bindResponseMethods from './bind-response-methods.js' + +// INTERFACE +export default new Router() +.use([ + // GLOBAL MIDDLEWARE + cors({ + origin: process.env.CORS_ORIGINS.split(' '), + credentials: true, + }), + morgan('dev'), + cookieParser(), + bindResponseMethods, + // ROUTERS + routerAuth, + routerPhoto, + routerProfile, + // ERROR HANDLERS + fourOhFour, + errorHandler, +]) diff --git a/backend/src/middleware/parser-auth.js b/backend/src/middleware/parser-auth.js new file mode 100644 index 0000000..6109a3c --- /dev/null +++ b/backend/src/middleware/parser-auth.js @@ -0,0 +1,54 @@ +import * as jwt from 'jsonwebtoken' +import User from '../model/user.js' +import createError from 'http-errors' +import {promisify, partial} from '../lib/util.js' + +export const basicAuth = (req, res, next) => { + let {authorization} = req.headers + if(!authorization) + return next(createError(400, 'AUTH ERROR: no authorization header')) + + let encoded = authorization.split('Basic ')[1] + if(!encoded) + return next(createError(400, 'AUTH ERROR: not basic auth')) + + let decoded = new Buffer(encoded, 'base64').toString() + let [username, password] = decoded.split(':') + if(!username || !password) + return next(createError(401, 'AUTH ERROR: username or password missing')) + + User.findOne({username}) + .then(user => { + if(!user) + throw createError(401, 'AUTH ERROR: user not found') + return user.passwordCompare(password) + }) + .then(user => { + req.user = user + next() + }) + .catch(next) +} + +export const bearerAuth = (req, res, next) => { + let {authorization} = req.headers + if(!authorization) + return next(createError(400, 'AUTH ERROR: no authorization header')) + + let token = authorization.split('Bearer ')[1] + if(!token) + return next(createError(400, 'AUTH ERROR: not bearer auth')) + + promisify(jwt.verify)(token, process.env.SECRET) + .then(({randomHash}) => User.findOne({randomHash})) + .then((user) => { + if(!user) + throw createError(401, 'AUTH ERROR: user not found') + req.user = user + next() + }) + .catch(partial(createError, 401)) +} + + + diff --git a/backend/src/middleware/parser-body.js b/backend/src/middleware/parser-body.js new file mode 100644 index 0000000..ba96573 --- /dev/null +++ b/backend/src/middleware/parser-body.js @@ -0,0 +1,20 @@ +// DEPENDENCIES +import multer from 'multer' +import bodyParser from 'body-parser' +import createError from 'http-errors' + +const upload = multer({dest: `${__dirname}/../../temp`}) + +// INTERFACE +export default (req, res, next) => { + let contentType = req.headers['content-type'] + + if(contentType.indexOf('application/json') > -1) + return bodyParser.json()(req, res, next) + + if(contentType.indexOf('multipart/form-data') > -1) + return upload.any()(req, res, next) + + next(createError(400, + `VALIDATION ERROR: content-type (${contentType}) not supported`)) +} diff --git a/backend/src/middleware/router-auth.js b/backend/src/middleware/router-auth.js new file mode 100644 index 0000000..c45a4fd --- /dev/null +++ b/backend/src/middleware/router-auth.js @@ -0,0 +1,41 @@ +'use strict' + +import {Router} from 'express' +import User from '../model/user.js' +import parserBody from './parser-body.js' +import {basicAuth} from './parser-auth.js' +import {log, daysToMilliseconds} from '../lib/util.js' + +export default new Router() +.post('/signup', parserBody, (req, res, next) => { + log('__ROUTE__ POST /signup') + + new User.create(req.body) + .then(user => user.tokenCreate()) + .then(token => { + res.cookie('X-Sluggram-Token', token, {maxAge: 900000}) + res.cookie('snark-in-the-dark', 'hahahah', {maxAge: 900000}) + + res.send(token) + }) + .catch(next) +}) +.get('/usernames/:username', (req, res, next) => { + User.findOne({username: username}) + .then(user => { + if(!user) + return res.sendStatus(409) + return res.sendStatus(200) + }) + .catch(next) +}) +.get('/login', basicAuth, (req, res, next) => { + log('__ROUTE__ GET /login') + req.user.tokenCreate() + .then((token) => { + let cookieOptions = {maxAge: daysToMilliseconds(7)} + res.cookie('X-Sluggram-Token', token, cookieOptions) + res.send(token) + }) + .catch(next) +}) diff --git a/backend/src/middleware/router-photo.js b/backend/src/middleware/router-photo.js new file mode 100644 index 0000000..ea6b278 --- /dev/null +++ b/backend/src/middleware/router-photo.js @@ -0,0 +1,36 @@ +import {Router} from 'express' +import {bearerAuth} from './parser-auth.js' +import parserBody from './parser-body.js' +import Photo from '../model/photo.js' + +export default new Router() +.post('/photos', bearerAuth, parserBody, (req, res, next) => { + Photo.create(req) + .then(res.json) + .catch(next) +}) +.get('/photos', (req, res, next) => { + Photo.fetch(req) + .then(res.page) + .catch(next) +}) +.get('/photos/me', bearerAuth, (req, res, next) => { + Photo.fetch(req, {username: req.user.usrename}) + .then(res.page) + .catch(next) +}) +.get('/photos/:id', (req, res, next) => { + Photo.fetchOne(req) + .then(res.json) + .catch(next) +}) +.put('/photos/:id', bearerAuth, parserBody, (req, res, next) => { + Photo.update(req) + .then(res.json) + .catch(next) +}) +.delete('/photos/:id', bearerAuth, (req, res, next) => { + Photo.delete(req) + .then(() => res.sendStatus(204)) + .catch(next) +}) diff --git a/backend/src/middleware/router-profile.js b/backend/src/middleware/router-profile.js new file mode 100644 index 0000000..a90ba1d --- /dev/null +++ b/backend/src/middleware/router-profile.js @@ -0,0 +1,41 @@ +import {Router} from 'express' +import createError from 'http-errors' +import parserBody from './parser-body.js' +import Profile from '../model/profile.js' +import {bearerAuth} from './parser-auth.js' + +export default new Router() +.post('/profiles', bearerAuth, parserBody, (req, res, next) => { + Profile.create(req) + .then(res.json) + .catch(next) +}) +.get('/profiles', (req, res, next) => { + Profile.fetch(req) + .then(res.page) + .catch(next) +}) +.get('/profiles/me', bearerAuth, (req, res, next) => { + Profile.findOne({owner: req.user._id}) + .then(profile => { + if(!profile) + return next(createError(404, 'NOT FOUND ERROR: profile not found')) + res.json(profile) + }) + .catch(next) +}) +.get('/profiles/:id', (req, res, next) => { + Profile.fetchOne(req) + .then(res.json) + .catch(next) +}) +.put('/profiles/:id', bearerAuth, parserBody, (req, res, next) => { + Profile.update(req) + .then(res.json) + .catch(next) +}) +.delete('/profiles/:id', bearerAuth, (req, res, next) => { + Profile.delete(req) + .then(() => res.sendStatus(204)) + .catch(next) +}) diff --git a/backend/src/model/index.js b/backend/src/model/index.js new file mode 100644 index 0000000..ec58914 --- /dev/null +++ b/backend/src/model/index.js @@ -0,0 +1,9 @@ +import User from './user.js' +import Profile from './profile.js' +import Photo from './photo.js' + +export default (db) => { + User(db) + Profile(db) + Photo(db) +} diff --git a/backend/src/model/photo.js b/backend/src/model/photo.js new file mode 100644 index 0000000..0ed5003 --- /dev/null +++ b/backend/src/model/photo.js @@ -0,0 +1,109 @@ +import * as _ from 'ramda' +import * as util from '../lib/util.js' +import createError from 'http-errors' +import Mongoose, {Schema} from 'mongoose' + +const photoSchema = new Schema({ + url: {type: String, required: true}, + description: {type: String, required: true}, + owner: {type: Schema.Types.ObjectId, required: true}, + profile: {type: Schema.Types.ObjectId, required: true, ref: 'profile'}, + comments: [{type: Schema.Types.ObjectId}], +}) + +const Photo = Mongoose.model('photo', photoSchema) + +Photo.validateRequest = function(req){ + if(req.method === 'POST' && !req.files) + return Promise.reject(createError(400, 'VALIDATION ERROR: must have a file')) + + if(req.method === 'POST' && req.files.length < 1) + return Promise.reject(createError(400, 'VALIDATION ERROR: must have a file')) + + if(req.files.length > 1) { + let err = createError(400, 'VALIDATION ERROR: must have one file') + return util.removeMulterFiles(req.files) + .then(() => {throw err}) + } + + let [file] = req.files + if(file){ + if(file.fieldname !== 'photo'){ + let err = createError(400, 'VALIDATION ERROR: file must be on field photo') + return util.removeMulterFiles(req.files) + .then(() => {throw err}) + } + } + + return Promise.resolve(file) +} + +Photo.create = function(req){ + return Photo.validateRequest(req) + .then(file => { + return util.s3UploadMulterFileAndClean(file) + .then(s3Data => { + return new Photo({ + owner: req.user._id, + profile: req.user.profile, + url: s3Data.Location, + description: req.body.description, + }).save() + }) + }) + .then(photo => { + return Photo.findById(photo._id) + .populate('profile') + }) +} + +Photo.fetch = util.pagerCreate(Photo, 'comments profile') + +Photo.fetchOne = function(req){ + return Photo.findById(req.params.id) + .populate('profile comments') + .then(photo => { + if(!photo) + throw createError(404, 'NOT FOUND ERROR: photo not found') + return photo + }) +} + +Photo.updatePhotoWithFile = function(req){ + return Photo.validateRequest(req) + .then(file => { + return util.s3UploadMulterFileAndClean(file) + .then(s3Data => { + let update = {url: s3Data.Location} + if(req.body.description) update.description = req.body.description + return Photo.findByIdAndUpdate(req.params.id, update, {new: true, runValidators: true}) + }) + }) +} + +Photo.update = function(req){ + if(req.files && req.files[0]) + return Photo.updatePhotoWithFile(req) + .then(photo => { + return Photo.findById(photo._id) + .populate('comments profile') + }) + let options = {new: true, runValidators: true} + let update = {description: req.body.description} + return Photo.findByIdAndUpdate(req.params.id, update, options) + .then(photo => { + return Photo.findById(photo._id) + .populate('comments profile') + }) +} + +Photo.delete = function(req){ + return Photo.findOneAndRemove({_id: req.params.id, owner: req.user._id}) + .then(profile => { + if(!profile) + throw createError(404, 'NOT FOUND ERROR: profile not found') + }) +} + +export default Photo + diff --git a/backend/src/model/profile.js b/backend/src/model/profile.js new file mode 100644 index 0000000..54e423b --- /dev/null +++ b/backend/src/model/profile.js @@ -0,0 +1,112 @@ +import createError from 'http-errors' +import * as util from '../lib/util.js' +import Mongoose, {Schema} from 'mongoose' + +const profileScheama = new Schema({ + owner: {type: Schema.Types.ObjectId, required: true, unique: true}, + email: {type: String, required: true}, + username: {type: String, required: true}, + avatar: {type: String}, + bio: {type: String}, +}) + +const Profile = Mongoose.model('profile', profileScheama) + +Profile.validateReqFile = function (req) { + if(req.files.length > 1){ + return util.removeMulterFiles(req.files) + .then(() => { + throw createError(400, 'VALIDATION ERROR: only one file permited') + }) + } + + let [file] = req.files + if(file) + if(file.fieldname !== 'avatar') + return util.removeMulterFiles(req.files) + .then(() => { + throw createError(400, 'VALIDATION ERROR: file must be for avatar') + }) + + return Promise.resolve(file) +} + +Profile.createProfileWithPhoto = function(req){ + return Profile.validateReqFile(req) + .then((file) => { + return util.s3UploadMulterFileAndClean(file) + .then((s3Data) => { + return new Profile({ + owner: req.user._id, + username: req.user.username, + email: req.user.email, + bio: req.body.bio, + avatar: s3Data.Location, + }).save() + }) + }) +} + +Profile.create = function(req){ + if(req.files){ + return Profile.createProfileWithPhoto(req) + .then(profile => { + req.user.profile = profile._id + return req.user.save() + .then(() => profile) + }) + } + + return new Profile({ + owner: req.user._id, + username: req.user.username, + email: req.user.email, + bio: req.body.bio, + }) + .save() + .then(profile => { + req.user.profile = profile._id + return req.user.save() + .then(() => profile) + }) +} + +Profile.fetch = util.pagerCreate(Profile) + +Profile.fetchOne = function(req){ + return Profile.findById(req.params.id) + .then(profile => { + if(!profile) + throw createError(404, 'NOT FOUND ERROR: profile not found') + return profile + }) +} + +Profile.updateProfileWithPhoto = function(req) { + return Profile.validateReqFile(req) + .then(file => { + return util.s3UploadMulterFileAndClean(file) + .then((s3Data) => { + let update = {avatar: s3Data.Location} + if(req.body.bio) update.bio = req.body.bio + return Profile.findByIdAndUpdate(req.params.id, update, {new: true, runValidators: true}) + }) + }) +} + +Profile.update = function(req){ + if(req.files && req.files[0]) + return Profile.updateProfileWithPhoto(req) + let options = {new: true, runValidators: true} + return Profile.findByIdAndUpdate(req.params.id, {bio: req.body.bio}, options) +} + +Profile.delete = function(req){ + return Profile.findOneAndRemove({_id: req.params.id, owner: req.user._id}) + .then(profile => { + if(!profile) + throw createError(404, 'NOT FOUND ERROR: profile not found') + }) +} + +export default Profile diff --git a/backend/src/model/user.js b/backend/src/model/user.js new file mode 100644 index 0000000..f88eb8f --- /dev/null +++ b/backend/src/model/user.js @@ -0,0 +1,61 @@ +'use strict' + +// DEPENDECIES +import * as bcrypt from 'bcrypt' +import {randomBytes} from 'crypto' +import * as jwt from 'jsonwebtoken' +import createError from 'http-errors' +import {promisify} from '../lib/util.js' +import Mongoose, {Schema} from 'mongoose' + +// SCHEMA +const userSchema = new Schema({ + email: {type: String, required: true, unique: true}, + username: {type: String, required: true, unique: true}, + passwordHash: {type: String, required: true}, + randomHash: {type: String, unique: true, default: ''}, + profile: {type: Schema.Types.ObjectId}, +}) + +// INSTANCE METHODS +userSchema.methods.passwordCompare = function(password){ + return bcrypt.compare(password, this.passwordHash) + .then(success => { + if (!success) + throw createError(401, 'AUTH ERROR: wrong password') + return this + }) +} + +userSchema.methods.tokenCreate = function(){ + this.randomHash = randomBytes(32).toString('base64') + return this.save() + .then(user => { + return jwt.sign({randomHash: this.randomHash}, process.env.SECRET) + }) + .then(token => { + return token + }) +} + +// MODEL +const User = Mongoose.model('user', userSchema) + +// STATIC METHODS +User.create = function (user) { + if(!user.password || !user.email || !user.username) + return Promise.reject( + createError(400, 'VALIDATION ERROR: missing username email or password ')) + + let {password} = user + user = Object.assign({}, user, {password: undefined}) + + return bcrypt.hash(password, 1) + .then(passwordHash => { + let data = Object.assign({}, user, {passwordHash}) + return new User(data).save() + }) +} + +// INTERFACE +export default User diff --git a/backend/yarn.lock b/backend/yarn.lock new file mode 100644 index 0000000..c95bd2f --- /dev/null +++ b/backend/yarn.lock @@ -0,0 +1,4585 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abab@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" + +abbrev@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" + +accepts@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" + dependencies: + mime-types "~2.1.11" + negotiator "0.6.1" + +acorn-dynamic-import@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" + dependencies: + acorn "^4.0.3" + +acorn-globals@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" + dependencies: + acorn "^4.0.4" + +acorn@^4.0.3, acorn@^4.0.4: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + +acorn@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75" + +ajv-keywords@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^5.1.5: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + json-schema-traverse "^0.3.0" + json-stable-stringify "^1.0.1" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-escapes@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + +ansi-regex@^2.0.0, ansi-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +append-field@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-0.1.0.tgz#6ddc58fa083c7bc545d3c5995b2830cc2366d44a" + +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + dependencies: + default-require-extensions "^1.0.0" + +aproba@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.0, arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1.js@^4.0.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4" + dependencies: + lodash "^4.14.0" + +async@^1.4.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^2.1.2, async@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d" + dependencies: + lodash "^4.14.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sdk-mock@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/aws-sdk-mock/-/aws-sdk-mock-1.7.0.tgz#7698b3ba82f493f71ff060ae2123cd0806ad8676" + dependencies: + aws-sdk "^2.3.0" + sinon "^1.17.3" + traverse "^0.6.6" + +aws-sdk@^2.3.0, aws-sdk@^2.92.0: + version "2.92.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.92.0.tgz#46944fef0e2d7932a04ac3497cc4adc4467fae09" + dependencies: + buffer "4.9.1" + crypto-browserify "1.0.9" + events "^1.1.1" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.0.1" + xml2js "0.4.17" + xmlbuilder "4.2.1" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-cli@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.24.1.tgz#207cd705bba61489b2ea41b5312341cf6aca2283" + dependencies: + babel-core "^6.24.1" + babel-polyfill "^6.23.0" + babel-register "^6.24.1" + babel-runtime "^6.22.0" + 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" + optionalDependencies: + chokidar "^1.6.1" + +babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-core@^6.0.0, babel-core@^6.24.1, babel-core@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729" + dependencies: + babel-code-frame "^6.22.0" + babel-generator "^6.25.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.25.0" + babel-traverse "^6.25.0" + babel-types "^6.25.0" + babylon "^6.17.2" + 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-generator@^6.18.0, babel-generator@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.25.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-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-jest@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-20.0.3.tgz#e4a03b13dc10389e140fc645d09ffc4ced301671" + dependencies: + babel-core "^6.0.0" + babel-plugin-istanbul "^4.0.0" + babel-preset-jest "^20.0.3" + +babel-loader@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.1.tgz#b87134c8b12e3e4c2a94e0546085bc680a2b8488" + dependencies: + find-cache-dir "^1.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-istanbul@^4.0.0: + version "4.1.4" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz#18dde84bf3ce329fddf3f4103fae921456d8e587" + dependencies: + find-up "^2.1.0" + istanbul-lib-instrument "^1.7.2" + test-exclude "^4.1.1" + +babel-plugin-jest-hoist@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz#afedc853bd3f8dc3548ea671fbe69d03cc2c1767" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-plugin-transform-es2015-classes@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-modules-systemjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-object-rest-spread@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418" + dependencies: + regenerator-transform "0.9.11" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-polyfill@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" + dependencies: + babel-runtime "^6.22.0" + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-preset-es2015@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" + dependencies: + 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-preset-jest@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz#cbacaadecb5d689ca1e1de1360ebfc66862c178a" + dependencies: + babel-plugin-jest-hoist "^20.0.3" + +babel-register@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f" + dependencies: + babel-core "^6.24.1" + 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-runtime@^6.18.0, babel-runtime@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.25.0" + babel-types "^6.25.0" + babylon "^6.17.2" + lodash "^4.2.0" + +babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.25.0" + babylon "^6.17.2" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babel@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel/-/babel-6.23.0.tgz#d0d1e7d803e974765beea3232d4e153c0efb90f4" + +babylon@^6.17.2, babylon@^6.17.4: + version "6.17.4" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-js@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" + +base64url@2.0.0, base64url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb" + +basic-auth@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.1.0.tgz#45221ee429f7ee1e5035be3f51533f1cdfd29884" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +bcrypt@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-1.0.2.tgz#d05fc5d223173e0e28ec381c0f00cc25ffaf2736" + dependencies: + bindings "1.2.1" + nan "2.5.0" + node-pre-gyp "0.6.32" + +big.js@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" + +binary-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.9.0.tgz#66506c16ce6f4d6928a5b3cd6a33ca41e941e37b" + +bindings@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@2.10.2: + version "2.10.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.10.2.tgz#024a5517295308857f14f91f1106fc3b555f446b" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.7" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.7.tgz#ddb048e50d9482790094c13eb3fcfc833ce7ab46" + +body-parser@^1.17.2: + version "1.17.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee" + dependencies: + bytes "2.4.0" + content-type "~1.0.2" + debug "2.6.7" + depd "~1.1.0" + http-errors "~1.6.1" + iconv-lite "0.4.15" + on-finished "~2.3.0" + qs "6.4.0" + raw-body "~2.2.0" + type-is "~1.6.15" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browser-resolve@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + dependencies: + resolve "1.1.7" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + dependencies: + buffer-xor "^1.0.2" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + inherits "^2.0.1" + +browserify-cipher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + 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" + +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + dependencies: + pako "~0.2.0" + +bser@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169" + dependencies: + node-int64 "^0.4.0" + +bser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" + dependencies: + node-int64 "^0.4.0" + +bson@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/bson/-/bson-1.0.4.tgz#93c10d39eaa5b58415cbc4052f3e53e562b0b72c" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + +buffer-shims@^1.0.0, buffer-shims@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +buffer-xor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@4.9.1, buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +busboy@^0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" + dependencies: + dicer "0.2.5" + readable-stream "1.1.x" + +bytes@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chokidar@^1.4.3, chokidar@^1.6.1, chokidar@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +ci-info@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +color-convert@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" + dependencies: + color-name "^1.1.1" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.8.1: + version "2.11.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +component-emitter@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +configstore@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021" + dependencies: + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + object-assign "^4.0.1" + os-tmpdir "^1.0.0" + osenv "^0.1.0" + uuid "^2.0.1" + write-file-atomic "^1.1.2" + xdg-basedir "^2.0.0" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type-parser@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94" + +content-type@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" + +convert-source-map@^1.1.0, convert-source-map@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" + +cookie-parser@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5" + dependencies: + cookie "0.3.1" + cookie-signature "1.0.6" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +cookiejar@^2.0.6: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" + +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cors@^2.8.4: + version "2.8.4" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.4.tgz#2bd381f2eb201020105cd50ea59da63090694686" + dependencies: + object-assign "^4" + vary "^1" + +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^2.0.0" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" + dependencies: + 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" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +crypto-browserify@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-1.0.9.tgz#cc5449685dfb85eb11c9828acc7cb87ab5bbfcc0" + +crypto-browserify@^3.11.0: + version "3.11.1" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.1.tgz#948945efc6757a400d6e5e5af47194d10064279f" + dependencies: + 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" + +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" + +"cssstyle@>= 0.2.37 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + dependencies: + cssom "0.3.x" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +debug@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" + dependencies: + ms "2.0.0" + +debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.6.3: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + dependencies: + ms "2.0.0" + +debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +decamelize@^1.0.0, decamelize@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + dependencies: + strip-bom "^2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@1.1.0, depd@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +dicer@0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" + dependencies: + readable-stream "1.1.x" + streamsearch "0.1.2" + +diff@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.0.tgz#056695150d7aa93237ca7e378ac3b1682b7963b9" + +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +domain-browser@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + +dotenv@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" + +duplexer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + +duplexify@^3.2.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.1.tgz#4e1516be68838bc90a49994f0b39a6e5960befcd" + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +ecdsa-sig-formatter@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz#4bc926274ec3b5abb5016e7e1d60921ac262b2a1" + dependencies: + base64url "^2.0.0" + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +elliptic@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + 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" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + +end-of-stream@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" + dependencies: + once "^1.4.0" + +enhanced-resolve@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.7" + +errno@^0.1.3, errno@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.24" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.24.tgz#a55877c9924bc0c8d9bd3c2cbe17495ac1709b14" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-symbol "^3.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + dependencies: + 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" + +es6-promise@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.2.1.tgz#ec56233868032909207170c39448e24449dd1fc4" + +es6-promise@^3.0.2: + version "3.3.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-weak-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@^1.6.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +esprima@^2.7.0, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +esrecurse@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + dependencies: + estraverse "^4.1.0" + object-assign "^4.0.1" + +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + +estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + +event-stream@~3.3.0: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +events@^1.0.0, events@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +evp_bytestokey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" + dependencies: + create-hash "^1.1.1" + +exec-sh@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.0.tgz#14f75de3f20d286ef933099b2ce50a90359cef10" + dependencies: + merge "^1.1.3" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +express@^4.15.3: + version "4.15.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.15.3.tgz#bab65d0f03aa80c358408972fc700f916944b662" + dependencies: + accepts "~1.3.3" + array-flatten "1.1.1" + content-disposition "0.5.2" + content-type "~1.0.2" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.7" + depd "~1.1.0" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.8.0" + finalhandler "~1.0.3" + fresh "0.5.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.1" + path-to-regexp "0.1.7" + proxy-addr "~1.1.4" + qs "6.4.0" + range-parser "~1.2.0" + send "0.15.3" + serve-static "1.12.3" + setprototypeof "1.0.3" + statuses "~1.3.1" + type-is "~1.6.15" + utils-merge "1.0.0" + vary "~1.1.1" + +extend@^3.0.0, extend@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +faker@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f" + +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fb-watchman@^1.8.0: + version "1.9.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.2.tgz#a24cf47827f82d38fb59a69ad70b76e3b6ae7383" + dependencies: + bser "1.0.2" + +fb-watchman@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" + dependencies: + bser "^2.0.0" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fileset@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" + dependencies: + glob "^7.0.3" + minimatch "^3.0.3" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +finalhandler@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89" + dependencies: + debug "2.6.7" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.1" + statuses "~1.3.1" + unpipe "~1.0.0" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.2.0.tgz#9a5e3b9295f980b2623cf64fa238b14cebca707b" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +formatio@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9" + dependencies: + samsam "~1.1" + +formidable@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" + +forwarded@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" + +fresh@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + +fs-extra@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.0.tgz#414fb4ca2d2170ba0014159d3a8aec3303418d9e" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^3.0.0" + universalify "^0.1.0" + +fs-readdir-recursive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.36" + +fstream-ignore@^1.0.5, fstream-ignore@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2, fstream@~1.0.10: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.0.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +got@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca" + dependencies: + duplexify "^3.2.0" + infinity-agent "^2.0.0" + is-redirect "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + nested-error-stacks "^1.0.0" + object-assign "^3.0.0" + prepend-http "^1.0.0" + read-all-stream "^3.0.0" + timed-out "^2.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + +handlebars@^4.0.3: + version "4.0.10" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +hash-base@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" + dependencies: + inherits "^2.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hooks-fixed@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hooks-fixed/-/hooks-fixed-2.0.0.tgz#a01d894d52ac7f6599bbb1f63dfc9c411df70cba" + +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +html-encoding-sniffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da" + dependencies: + whatwg-encoding "^1.0.1" + +http-errors@^1.6.1, http-errors@~1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257" + dependencies: + depd "1.1.0" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + +iconv-lite@0.4.13: + version "0.4.13" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" + +iconv-lite@0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + +ignore-by-default@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +infinity-agent@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/infinity-agent/-/infinity-agent-2.0.3.tgz#45e0e2ff7a9eb030b27d62b74b3744b7a7ac4216" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +interpret@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" + +invariant@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ipaddr.js@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-ci@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" + dependencies: + ci-info "^1.0.0" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-npm@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + +is-stream@^1.0.0, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isemail@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/isemail/-/isemail-1.2.0.tgz#be03df8cc3e29de4d2c5df6501263f1fa4595e9a" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +istanbul-api@^1.1.1: + version "1.1.11" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.11.tgz#fcc0b461e2b3bda71e305155138238768257d9de" + dependencies: + async "^2.1.4" + fileset "^2.0.2" + istanbul-lib-coverage "^1.1.1" + istanbul-lib-hook "^1.0.7" + istanbul-lib-instrument "^1.7.4" + istanbul-lib-report "^1.1.1" + istanbul-lib-source-maps "^1.2.1" + istanbul-reports "^1.1.1" + js-yaml "^3.7.0" + mkdirp "^0.5.1" + once "^1.4.0" + +istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da" + +istanbul-lib-hook@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz#dd6607f03076578fe7d6f2a630cf143b49bacddc" + dependencies: + append-transform "^0.4.0" + +istanbul-lib-instrument@^1.4.2, istanbul-lib-instrument@^1.7.2, istanbul-lib-instrument@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz#e9fd920e4767f3d19edc765e2d6b3f5ccbd0eea8" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.17.4" + istanbul-lib-coverage "^1.1.1" + semver "^5.3.0" + +istanbul-lib-report@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#f0e55f56655ffa34222080b7a0cd4760e1405fc9" + dependencies: + istanbul-lib-coverage "^1.1.1" + mkdirp "^0.5.1" + path-parse "^1.0.5" + supports-color "^3.1.2" + +istanbul-lib-source-maps@^1.1.0, istanbul-lib-source-maps@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz#a6fe1acba8ce08eebc638e572e294d267008aa0c" + dependencies: + debug "^2.6.3" + istanbul-lib-coverage "^1.1.1" + mkdirp "^0.5.1" + rimraf "^2.6.1" + source-map "^0.5.3" + +istanbul-reports@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.1.tgz#042be5c89e175bc3f86523caab29c014e77fee4e" + dependencies: + handlebars "^4.0.3" + +jest-changed-files@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-20.0.3.tgz#9394d5cc65c438406149bef1bf4d52b68e03e3f8" + +jest-cli@^20.0.4: + version "20.0.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-20.0.4.tgz#e532b19d88ae5bc6c417e8b0593a6fe954b1dc93" + dependencies: + ansi-escapes "^1.4.0" + callsites "^2.0.0" + chalk "^1.1.3" + graceful-fs "^4.1.11" + is-ci "^1.0.10" + istanbul-api "^1.1.1" + istanbul-lib-coverage "^1.0.1" + istanbul-lib-instrument "^1.4.2" + istanbul-lib-source-maps "^1.1.0" + jest-changed-files "^20.0.3" + jest-config "^20.0.4" + jest-docblock "^20.0.3" + jest-environment-jsdom "^20.0.3" + jest-haste-map "^20.0.4" + jest-jasmine2 "^20.0.4" + jest-message-util "^20.0.3" + jest-regex-util "^20.0.3" + jest-resolve-dependencies "^20.0.3" + jest-runtime "^20.0.4" + jest-snapshot "^20.0.3" + jest-util "^20.0.3" + micromatch "^2.3.11" + node-notifier "^5.0.2" + pify "^2.3.0" + slash "^1.0.0" + string-length "^1.0.1" + throat "^3.0.0" + which "^1.2.12" + worker-farm "^1.3.1" + yargs "^7.0.2" + +jest-config@^20.0.4: + version "20.0.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-20.0.4.tgz#e37930ab2217c913605eff13e7bd763ec48faeea" + dependencies: + chalk "^1.1.3" + glob "^7.1.1" + jest-environment-jsdom "^20.0.3" + jest-environment-node "^20.0.3" + jest-jasmine2 "^20.0.4" + jest-matcher-utils "^20.0.3" + jest-regex-util "^20.0.3" + jest-resolve "^20.0.4" + jest-validate "^20.0.3" + pretty-format "^20.0.3" + +jest-diff@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-20.0.3.tgz#81f288fd9e675f0fb23c75f1c2b19445fe586617" + dependencies: + chalk "^1.1.3" + diff "^3.2.0" + jest-matcher-utils "^20.0.3" + pretty-format "^20.0.3" + +jest-docblock@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712" + +jest-environment-jsdom@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz#048a8ac12ee225f7190417713834bb999787de99" + dependencies: + jest-mock "^20.0.3" + jest-util "^20.0.3" + jsdom "^9.12.0" + +jest-environment-node@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-20.0.3.tgz#d488bc4612af2c246e986e8ae7671a099163d403" + dependencies: + jest-mock "^20.0.3" + jest-util "^20.0.3" + +jest-haste-map@^20.0.4: + version "20.0.5" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-20.0.5.tgz#abad74efb1a005974a7b6517e11010709cab9112" + dependencies: + fb-watchman "^2.0.0" + graceful-fs "^4.1.11" + jest-docblock "^20.0.3" + micromatch "^2.3.11" + sane "~1.6.0" + worker-farm "^1.3.1" + +jest-jasmine2@^20.0.4: + version "20.0.4" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-20.0.4.tgz#fcc5b1411780d911d042902ef1859e852e60d5e1" + dependencies: + chalk "^1.1.3" + graceful-fs "^4.1.11" + jest-diff "^20.0.3" + jest-matcher-utils "^20.0.3" + jest-matchers "^20.0.3" + jest-message-util "^20.0.3" + jest-snapshot "^20.0.3" + once "^1.4.0" + p-map "^1.1.1" + +jest-matcher-utils@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz#b3a6b8e37ca577803b0832a98b164f44b7815612" + dependencies: + chalk "^1.1.3" + pretty-format "^20.0.3" + +jest-matchers@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-20.0.3.tgz#ca69db1c32db5a6f707fa5e0401abb55700dfd60" + dependencies: + jest-diff "^20.0.3" + jest-matcher-utils "^20.0.3" + jest-message-util "^20.0.3" + jest-regex-util "^20.0.3" + +jest-message-util@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-20.0.3.tgz#6aec2844306fcb0e6e74d5796c1006d96fdd831c" + dependencies: + chalk "^1.1.3" + micromatch "^2.3.11" + slash "^1.0.0" + +jest-mock@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-20.0.3.tgz#8bc070e90414aa155c11a8d64c869a0d5c71da59" + +jest-regex-util@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-20.0.3.tgz#85bbab5d133e44625b19faf8c6aa5122d085d762" + +jest-resolve-dependencies@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz#6e14a7b717af0f2cb3667c549de40af017b1723a" + dependencies: + jest-regex-util "^20.0.3" + +jest-resolve@^20.0.4: + version "20.0.4" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-20.0.4.tgz#9448b3e8b6bafc15479444c6499045b7ffe597a5" + dependencies: + browser-resolve "^1.11.2" + is-builtin-module "^1.0.0" + resolve "^1.3.2" + +jest-runtime@^20.0.4: + version "20.0.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-20.0.4.tgz#a2c802219c4203f754df1404e490186169d124d8" + dependencies: + babel-core "^6.0.0" + babel-jest "^20.0.3" + babel-plugin-istanbul "^4.0.0" + chalk "^1.1.3" + convert-source-map "^1.4.0" + graceful-fs "^4.1.11" + jest-config "^20.0.4" + jest-haste-map "^20.0.4" + jest-regex-util "^20.0.3" + jest-resolve "^20.0.4" + jest-util "^20.0.3" + json-stable-stringify "^1.0.1" + micromatch "^2.3.11" + strip-bom "3.0.0" + yargs "^7.0.2" + +jest-snapshot@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-20.0.3.tgz#5b847e1adb1a4d90852a7f9f125086e187c76566" + dependencies: + chalk "^1.1.3" + jest-diff "^20.0.3" + jest-matcher-utils "^20.0.3" + jest-util "^20.0.3" + natural-compare "^1.4.0" + pretty-format "^20.0.3" + +jest-util@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-20.0.3.tgz#0c07f7d80d82f4e5a67c6f8b9c3fe7f65cfd32ad" + dependencies: + chalk "^1.1.3" + graceful-fs "^4.1.11" + jest-message-util "^20.0.3" + jest-mock "^20.0.3" + jest-validate "^20.0.3" + leven "^2.1.0" + mkdirp "^0.5.1" + +jest-validate@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-20.0.3.tgz#d0cfd1de4f579f298484925c280f8f1d94ec3cab" + dependencies: + chalk "^1.1.3" + jest-matcher-utils "^20.0.3" + leven "^2.1.0" + pretty-format "^20.0.3" + +jest@^20.0.4: + version "20.0.4" + resolved "https://registry.yarnpkg.com/jest/-/jest-20.0.4.tgz#3dd260c2989d6dad678b1e9cc4d91944f6d602ac" + dependencies: + jest-cli "^20.0.4" + +jmespath@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" + +joi@^6.10.1: + version "6.10.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-6.10.1.tgz#4d50c318079122000fe5f16af1ff8e1917b77e06" + dependencies: + hoek "2.x.x" + isemail "1.x.x" + moment "2.x.x" + topo "1.x.x" + +js-tokens@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.7.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.0.tgz#4ffbbf25c2ac963b8299dc74da7e3740de1c18ce" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsdom@^9.12.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4" + dependencies: + abab "^1.0.3" + acorn "^4.0.4" + acorn-globals "^3.1.0" + array-equal "^1.0.0" + content-type-parser "^1.0.1" + cssom ">= 0.3.2 < 0.4.0" + cssstyle ">= 0.2.37 < 0.3.0" + escodegen "^1.6.1" + html-encoding-sniffer "^1.0.1" + nwmatcher ">= 1.3.9 < 2.0.0" + parse5 "^1.5.1" + request "^2.79.0" + sax "^1.2.1" + symbol-tree "^3.2.1" + tough-cookie "^2.3.2" + webidl-conversions "^4.0.0" + whatwg-encoding "^1.0.1" + whatwg-url "^4.3.0" + xml-name-validator "^2.0.1" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-loader@^0.5.4: + version "0.5.7" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + +json-parser@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/json-parser/-/json-parser-1.1.5.tgz#e62ec5261d1a6a5fc20e812a320740c6d9005677" + dependencies: + esprima "^2.7.0" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonwebtoken@^7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.1.tgz#7ca324f5215f8be039cd35a6c45bb8cb74a448fb" + dependencies: + joi "^6.10.1" + jws "^3.1.4" + lodash.once "^4.0.0" + ms "^2.0.0" + xtend "^4.0.1" + +jsprim@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + dependencies: + assert-plus "1.0.0" + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +jwa@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5" + dependencies: + base64url "2.0.0" + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.9" + safe-buffer "^5.0.1" + +jws@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.4.tgz#f9e8b9338e8a847277d6444b1464f61880e050a2" + dependencies: + base64url "^2.0.0" + jwa "^1.1.4" + safe-buffer "^5.0.1" + +kareem@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/kareem/-/kareem-1.5.0.tgz#e3e4101d9dcfde299769daf4b4db64d895d17448" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +latest-version@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb" + dependencies: + package-json "^1.0.0" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + +lodash._bindcallback@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" + +lodash._createassigner@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" + dependencies: + lodash._bindcallback "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash.restparam "^3.0.0" + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + +lodash.assign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" + dependencies: + lodash._baseassign "^3.0.0" + lodash._createassigner "^3.0.0" + lodash.keys "^3.0.0" + +lodash.defaults@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c" + dependencies: + lodash.assign "^3.0.0" + lodash.restparam "^3.0.0" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +lolex@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +lowercase-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +make-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978" + dependencies: + pify "^2.3.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + dependencies: + tmpl "1.0.x" + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +merge@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" + +methods@^1.1.1, methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^2.1.5, micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +miller-rabin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@~1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878" + +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: + version "2.1.16" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.16.tgz#2b858a52e5ecd516db897ac2be87487830698e23" + dependencies: + mime-db "~1.29.0" + +mime@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + +mime@^1.3.4: + version "1.3.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" + +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + +minimalistic-assert@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.1, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +moment@2.x.x: + version "2.18.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" + +mongodb-core@2.1.11: + version "2.1.11" + resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.11.tgz#1c38776ceb174997a99c28860eed9028da9b3e1a" + dependencies: + bson "~1.0.4" + require_optional "~1.0.0" + +mongodb@2.2.27: + version "2.2.27" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-2.2.27.tgz#34122034db66d983bcf6ab5adb26a24a70fef6e6" + dependencies: + es6-promise "3.2.1" + mongodb-core "2.1.11" + readable-stream "2.2.7" + +mongoose@^4.11.4: + version "4.11.4" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-4.11.4.tgz#3d74111105ee8b619eb1c1d790e7bfc4e8cba7bb" + dependencies: + async "2.1.4" + bson "~1.0.4" + hooks-fixed "2.0.0" + kareem "1.5.0" + mongodb "2.2.27" + mpath "0.3.0" + mpromise "0.5.5" + mquery "2.3.1" + ms "2.0.0" + muri "1.2.2" + regexp-clone "0.0.1" + sliced "1.0.1" + +morgan@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.8.2.tgz#784ac7734e4a453a9c6e6e8680a9329275c8b687" + dependencies: + basic-auth "~1.1.0" + debug "2.6.8" + depd "~1.1.0" + on-finished "~2.3.0" + on-headers "~1.0.1" + +mpath@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.3.0.tgz#7a58f789e9b5fd3c94520634157960f26bd5ef44" + +mpromise@0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mpromise/-/mpromise-0.5.5.tgz#f5b24259d763acc2257b0a0c8c6d866fd51732e6" + +mquery@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/mquery/-/mquery-2.3.1.tgz#9ab36749714800ff0bb53a681ce4bc4d5f07c87b" + dependencies: + bluebird "2.10.2" + debug "2.6.8" + regexp-clone "0.0.1" + sliced "0.0.5" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +ms@2.0.0, ms@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +multer@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.3.0.tgz#092b2670f6846fa4914965efc8cf94c20fec6cd2" + dependencies: + 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" + +muri@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/muri/-/muri-1.2.2.tgz#63198132650db08a04cc79ccd00dd389afd2631c" + +nan@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.0.tgz#aa8f1e34531d807e9e27755b234b4a6ec0c152a8" + +nan@^2.3.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +nested-error-stacks@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz#19f619591519f096769a5ba9a86e6eeec823c3cf" + dependencies: + inherits "~2.0.1" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + +node-libs-browser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646" + dependencies: + 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" + https-browserify "0.0.1" + os-browserify "^0.2.0" + 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" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-notifier@^5.0.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.1.2.tgz#2fa9e12605fa10009d44549d6fcd8a63dde0e4ff" + dependencies: + growly "^1.3.0" + semver "^5.3.0" + shellwords "^0.1.0" + which "^1.2.12" + +node-pre-gyp@0.6.32: + version "0.6.32" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz#fc452b376e7319b3d255f5f34853ef6fd8fe1fd5" + dependencies: + 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" + +node-pre-gyp@^0.6.36: + version "0.6.36" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" + dependencies: + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "^2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +nodemon@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.11.0.tgz#226c562bd2a7b13d3d7518b49ad4828a3623d06c" + dependencies: + chokidar "^1.4.3" + debug "^2.2.0" + es6-promise "^3.0.2" + ignore-by-default "^1.0.0" + lodash.defaults "^3.1.2" + minimatch "^3.0.0" + ps-tree "^1.0.1" + touch "1.0.0" + undefsafe "0.0.3" + update-notifier "0.5.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + dependencies: + abbrev "1" + +nopt@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +npmlog@^4.0.1, npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +"nwmatcher@>= 1.3.9 < 2.0.0": + version "1.4.1" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +on-finished@^2.3.0, on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + +once@^1.3.0, once@^1.3.3, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-browserify@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.0, osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +output-file-sync@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" + dependencies: + graceful-fs "^4.1.4" + mkdirp "^0.5.1" + object-assign "^4.1.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-map@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a" + +package-json@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0" + dependencies: + got "^3.2.0" + registry-url "^3.0.0" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +parse-asn1@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse5@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" + +parseurl@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + dependencies: + through "~2.3" + +pbkdf2@^3.0.3: + version "3.0.12" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +pretty-format@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14" + dependencies: + ansi-regex "^2.1.1" + ansi-styles "^3.0.0" + +private@^0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process@^0.11.0: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +proxy-addr@~1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" + dependencies: + forwarded "~0.1.0" + ipaddr.js "1.4.0" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +ps-tree@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" + dependencies: + event-stream "~3.3.0" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@6.4.0, qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +qs@^6.1.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +ramda@^0.24.1: + version "0.24.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" + +randomatic@^1.1.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79" + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96" + dependencies: + bytes "2.4.0" + iconv-lite "0.4.15" + unpipe "1.0.0" + +rc@^1.0.1, rc@^1.1.7: + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +rc@~1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-all-stream@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" + dependencies: + pinkie-promise "^2.0.0" + readable-stream "^2.0.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@2.2.7: + version "2.2.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.7.tgz#07057acbe2467b22042d36f98c5ad507054e95b1" + dependencies: + 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" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +readable-stream@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + 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" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +regenerate@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" + +regenerator-runtime@^0.10.0: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +regenerator-transform@0.9.11: + version "0.9.11" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +regexp-clone@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-0.0.1.tgz#a7c2e09891fdbf38fbb10d376fb73003e68ac589" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +registry-url@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + dependencies: + rc "^1.0.1" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" + dependencies: + is-finite "^1.0.0" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@^2.79.0, request@^2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +require_optional@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" + dependencies: + resolve-from "^2.0.0" + semver "^5.1.0" + +resolve-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" + dependencies: + path-parse "^1.0.5" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + dependencies: + glob "^7.0.5" + +rimraf@~2.5.1, rimraf@~2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" + dependencies: + glob "^7.0.5" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" + dependencies: + hash-base "^2.0.0" + inherits "^2.0.1" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +samsam@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" + +samsam@~1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" + +sane@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-1.6.0.tgz#9610c452307a135d29c1fdfe2547034180c46775" + dependencies: + anymatch "^1.3.0" + exec-sh "^0.2.0" + fb-watchman "^1.8.0" + minimatch "^3.0.2" + minimist "^1.1.1" + walker "~1.0.5" + watch "~0.10.0" + +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + +sax@>=0.6.0, sax@^1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +semver-diff@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + dependencies: + semver "^5.0.3" + +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +send@0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/send/-/send-0.15.3.tgz#5013f9f99023df50d1bd9892c19e3defd1d53309" + dependencies: + debug "2.6.7" + depd "~1.1.0" + destroy "~1.0.4" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.8.0" + fresh "0.5.0" + http-errors "~1.6.1" + mime "1.3.4" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.3.1" + +serve-static@1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.3.tgz#9f4ba19e2f3030c547f8af99107838ec38d5b1e2" + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.1" + send "0.15.3" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.8" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" + dependencies: + inherits "^2.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +shellwords@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +sinon@^1.17.3: + version "1.17.7" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf" + dependencies: + formatio "1.1.1" + lolex "1.3.2" + samsam "1.1.2" + util ">=0.10.3 <1" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +sliced@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/sliced/-/sliced-0.0.5.tgz#5edc044ca4eb6f7816d50ba2fc63e25d8fe4707f" + +sliced@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" + +slide@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + +source-map-support@^0.4.2: + version "0.4.15" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1" + dependencies: + source-map "^0.5.6" + +source-map@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + dependencies: + amdefine ">=0.0.4" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + dependencies: + through "2" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +"statuses@>= 1.3.1 < 2", statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + dependencies: + duplexer "~0.1.1" + +stream-http@^2.3.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.2.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + +string-length@^1.0.0, string-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" + dependencies: + strip-ansi "^3.0.0" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.0, string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@3.0.0, strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +superagent@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.5.2.tgz#3361a3971567504c351063abeaae0faa23dbf3f8" + dependencies: + component-emitter "^1.2.0" + cookiejar "^2.0.6" + debug "^2.2.0" + extend "^3.0.0" + form-data "^2.1.1" + formidable "^1.1.1" + methods "^1.1.1" + mime "^1.3.4" + qs "^6.1.0" + readable-stream "^2.0.5" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +supports-color@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.2.1.tgz#65a4bb2631e90e02420dba5554c375a4754bb836" + dependencies: + has-flag "^2.0.0" + +symbol-tree@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + +tapable@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.7.tgz#e46c0daacbb2b8a98b9b0cea0f4052105817ed5c" + +tar-pack@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar-pack@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" + dependencies: + debug "~2.2.0" + fstream "~1.0.10" + fstream-ignore "~1.0.5" + once "~1.3.3" + readable-stream "~2.1.4" + rimraf "~2.5.1" + tar "~2.2.1" + uid-number "~0.0.6" + +tar@^2.2.1, tar@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +test-exclude@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26" + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + +throat@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-3.2.0.tgz#50cb0670edbc40237b9e347d7e1f88e4620af836" + +through@2, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timed-out@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a" + +timers-browserify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" + dependencies: + setimmediate "^1.0.4" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +topo@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/topo/-/topo-1.1.0.tgz#e9d751615d1bb87dc865db182fa1ca0a5ef536d5" + dependencies: + hoek "2.x.x" + +touch@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" + dependencies: + nopt "~1.0.10" + +tough-cookie@^2.3.2, tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + +traverse@^0.6.6: + version "0.6.6" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-is@^1.6.4, type-is@~1.6.15: + version "1.6.15" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.15" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +uglify-js@^2.6, uglify-js@^2.8.29: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uglifyjs-webpack-plugin@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" + dependencies: + source-map "^0.5.6" + uglify-js "^2.8.29" + webpack-sources "^1.0.1" + +uid-number@^0.0.6, uid-number@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +undefsafe@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f" + +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +update-notifier@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" + dependencies: + chalk "^1.0.0" + configstore "^1.0.0" + is-npm "^1.0.0" + latest-version "^1.0.0" + repeating "^1.1.2" + semver-diff "^2.0.0" + string-length "^1.0.0" + +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3, "util@>=0.10.3 <1", util@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +utils-merge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" + +uuid@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +uuid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + +uuid@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +v8flags@^2.0.10: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" + dependencies: + user-home "^1.1.1" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +vary@^1, vary@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + dependencies: + makeerror "1.0.x" + +watch@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc" + +watchpack@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" + dependencies: + async "^2.1.2" + chokidar "^1.7.0" + graceful-fs "^4.1.2" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + +webidl-conversions@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.1.tgz#8015a17ab83e7e1b311638486ace81da6ce206a0" + +webpack-sources@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf" + dependencies: + source-list-map "^2.0.0" + source-map "~0.5.3" + +webpack@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.4.1.tgz#4c3f4f3fb318155a4db0cb6a36ff05c5697418f4" + dependencies: + acorn "^5.0.0" + acorn-dynamic-import "^2.0.0" + ajv "^5.1.5" + ajv-keywords "^2.0.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" + +whatwg-encoding@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4" + dependencies: + iconv-lite "0.4.13" + +whatwg-url@^4.3.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0" + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@^1.2.12, which@^1.2.9: + version "1.2.14" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +worker-farm@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.4.1.tgz#a438bc993a7a7d133bcb6547c95eca7cff4897d8" + dependencies: + errno "^0.1.4" + xtend "^4.0.1" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-atomic@^1.1.2: + version "1.3.4" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + slide "^1.1.5" + +xdg-basedir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" + dependencies: + os-homedir "^1.0.0" + +xml-name-validator@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + +xml2js@0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.17.tgz#17be93eaae3f3b779359c795b419705a8817e868" + dependencies: + sax ">=0.6.0" + xmlbuilder "^4.1.0" + +xmlbuilder@4.2.1, xmlbuilder@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.2.1.tgz#aa58a3041a066f90eaa16c2f5389ff19f3f461a5" + dependencies: + lodash "^4.0.0" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" + dependencies: + camelcase "^4.1.0" + +yargs@^7.0.2: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + dependencies: + 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 "^5.0.0" + +yargs@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" + dependencies: + camelcase "^4.1.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^7.0.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" From aa0d493149bbbc812f26cc24ff1d2503a31508c2 Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 13:57:34 -0700 Subject: [PATCH 02/10] Builds Frontend Scaffolding --- frontend/.babelrc | 0 frontend/.dev.env | 0 frontend/.eslintignore | 0 frontend/.eslintrc | 0 frontend/.gitignore | 0 frontend/package.json | 12 ++++ frontend/src/action/auth-actions.js | 0 frontend/src/component/app/index.js | 0 frontend/src/component/auth-form/index.js | 0 frontend/src/component/landing/index.js | 0 frontend/src/index.html | 0 frontend/src/lib/app-create-store.js | 8 +++ frontend/src/lib/redux-reporter.js | 15 ++++ frontend/src/lib/redux-thunk.js | 4 ++ frontend/src/lib/util.js | 20 ++++++ frontend/src/main.js | 0 frontend/src/reducer/index.js | 0 frontend/webpack.config.js | 86 +++++++++++++++++++++++ 18 files changed, 145 insertions(+) create mode 100644 frontend/.babelrc create mode 100644 frontend/.dev.env create mode 100644 frontend/.eslintignore create mode 100644 frontend/.eslintrc create mode 100644 frontend/.gitignore create mode 100644 frontend/package.json create mode 100644 frontend/src/action/auth-actions.js create mode 100644 frontend/src/component/app/index.js create mode 100644 frontend/src/component/auth-form/index.js create mode 100644 frontend/src/component/landing/index.js create mode 100644 frontend/src/index.html create mode 100644 frontend/src/lib/app-create-store.js create mode 100644 frontend/src/lib/redux-reporter.js create mode 100644 frontend/src/lib/redux-thunk.js create mode 100644 frontend/src/lib/util.js create mode 100644 frontend/src/main.js create mode 100644 frontend/src/reducer/index.js create mode 100644 frontend/webpack.config.js diff --git a/frontend/.babelrc b/frontend/.babelrc new file mode 100644 index 0000000..e69de29 diff --git a/frontend/.dev.env b/frontend/.dev.env new file mode 100644 index 0000000..e69de29 diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 0000000..e69de29 diff --git a/frontend/.eslintrc b/frontend/.eslintrc new file mode 100644 index 0000000..e69de29 diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..4f6366c --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,12 @@ +{ + "name": "frontend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/frontend/src/action/auth-actions.js b/frontend/src/action/auth-actions.js new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/component/app/index.js b/frontend/src/component/app/index.js new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/component/auth-form/index.js b/frontend/src/component/auth-form/index.js new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/component/landing/index.js b/frontend/src/component/landing/index.js new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/index.html b/frontend/src/index.html new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/lib/app-create-store.js b/frontend/src/lib/app-create-store.js new file mode 100644 index 0000000..a8bad65 --- /dev/null +++ b/frontend/src/lib/app-create-store.js @@ -0,0 +1,8 @@ +import reducer from '../reducer'; +import thunk from './redux-thunk.js'; +import reporter from './redux-reporter'; +import {createStore, applyMiddleware} from 'redux'; + +let appStoreCreate = () => createStore(reducer, applyMiddleware(thunk, reporter)); + +export default appStoreCreate; \ No newline at end of file diff --git a/frontend/src/lib/redux-reporter.js b/frontend/src/lib/redux-reporter.js new file mode 100644 index 0000000..0a347af --- /dev/null +++ b/frontend/src/lib/redux-reporter.js @@ -0,0 +1,15 @@ +let reporter = store => next => action => { + console.log('__ACTION__', action); + + try { + let result = next(action); + console.log('__STATE__', store.getState()); + return result; + } catch (error) { + error.action = action; + console.error('__ERROR__', error); + return error; + } +} + +export default reporter; diff --git a/frontend/src/lib/redux-thunk.js b/frontend/src/lib/redux-thunk.js new file mode 100644 index 0000000..6a7407c --- /dev/null +++ b/frontend/src/lib/redux-thunk.js @@ -0,0 +1,4 @@ +export default store => next => action => + typeof action === 'function' ? + action(store.dispatch, store.getState) + : next(action); diff --git a/frontend/src/lib/util.js b/frontend/src/lib/util.js new file mode 100644 index 0000000..a91a73c --- /dev/null +++ b/frontend/src/lib/util.js @@ -0,0 +1,20 @@ +export const log = (...args) => + __DEBUG__ ? console.log(...args) : undefined; + +export const logError = (...args) => + __DEBUG__ ? console.error(...args) : undefined; + +export const renderIf = (test, component) => + test ? component : undefined; + +export const classToggler = (options) => + Object.keys(options).filter(key => !!options[key]).join(' '); + +export const map = (list, ...args) => + Array.prototype.map.apply(list, args); + +export const filter = (list, ...args) => + Array.prototype.filter.apply(list, args); + +export const reduce = (list, ...args) => + Array.prototype.reduce.apply(list, args); \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/reducer/index.js b/frontend/src/reducer/index.js new file mode 100644 index 0000000..e69de29 diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js new file mode 100644 index 0000000..483eb2d --- /dev/null +++ b/frontend/webpack.config.js @@ -0,0 +1,86 @@ +'use strict'; + +require('dotenv').config({ path: `${__dirname}/.dev.env` }); +const production = process.env.NODE_ENV === 'production'; + +const {DefinePlugin, EnvironmentPlugin} = require('webpack'); +const HtmlPlugin = require('html-webpack-plugin'); +const CleanPlugin = require('clean-webpack-plugin'); +const UglifyPlugin = require('uglifyjs-webpack-plugin'); +const ExtractPlugin = require('extract-text-webpack-plugin'); + +let plugins = [ + new EnvironmentPlugin(['NODE_ENV']), + new ExtractPlugin('bundle-[hash].css'), + new HtmlPlugin({ template: `${__dirname}/src/index.html` }), + new DefinePlugin({ + __DEBUG__: JSON.stringify(!production), + __API_URL__: JSON.stringify(process.env.API_URL) + }) +]; + +if (production) { + plugins = plugins.concat([ new CleanPlugin(), new UglifyPlugin() ]); +} + +module.exports = { + plugins, + entry: `${__dirname}/src/main.js`, + devServer: { + historyApiFallback: true + }, + devtool: production ? undefined : 'eval', + output: { + path: `${__dirname}/build`, + filename: 'bundle-[hash].js', + publicPath: process.env.CDN_URL + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader' + }, + { + test: /\.scss$/, + loader: ExtractPlugin.extract(['css-loader', 'sass-loader']) + }, + { + test: /\.(woff|woff2|ttf|eot|glyph|\.svg)$/, + use: [ + { + loader: 'url-loader', + options: { + limit: 10000, + name: 'font/[name].[ext]' + } + } + ] + }, + { + test: /\.(jpg|jpeg|gif|png|tiff|svg)$/, + exclude: /\.glyph.svg/, + use: [ + { + loader: 'url-loader', + options: { + limit: 6000, + name: 'image/[name].[ext]' + } + } + ] + }, + { + test: /\.(mp3|aac|aiff|wav|flac|m4a|mp4|ogg)$/, + exclude: /\.glyph.svg/, + use: [ + { + loader: 'file-loader', + options: { name: 'audio/[name].[ext]' } + } + ] + } + ] + } +} From 34d0cf3913895980b1a8adfffbc3418a3fd0777d Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 15:59:17 -0700 Subject: [PATCH 03/10] Adds Dependencies and Builds Scaffolding --- frontend/.babelrc | 4 + frontend/.dev.env | 1 + frontend/.eslintignore | 5 + frontend/.eslintrc | 21 +++++ frontend/.gitignore | 136 ++++++++++++++++++++++++++++ frontend/package.json | 33 ++++++- frontend/src/component/app/index.js | 1 + frontend/src/index.html | 10 ++ frontend/src/main.js | 5 + 9 files changed, 213 insertions(+), 3 deletions(-) diff --git a/frontend/.babelrc b/frontend/.babelrc index e69de29..6575f38 100644 --- a/frontend/.babelrc +++ b/frontend/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["es2015", "react"], + "plugins": ["transform-object-spread-rest"] +} \ No newline at end of file diff --git a/frontend/.dev.env b/frontend/.dev.env index e69de29..667e45c 100644 --- a/frontend/.dev.env +++ b/frontend/.dev.env @@ -0,0 +1 @@ +API_URL='http://localhost:3000' \ No newline at end of file diff --git a/frontend/.eslintignore b/frontend/.eslintignore index e69de29..82ff623 100644 --- a/frontend/.eslintignore +++ b/frontend/.eslintignore @@ -0,0 +1,5 @@ +**/node_modules/* +**/vendor/* +**/*.min.js +**/coverage/* +**/build/* \ No newline at end of file diff --git a/frontend/.eslintrc b/frontend/.eslintrc index e69de29..b663d77 100644 --- a/frontend/.eslintrc +++ b/frontend/.eslintrc @@ -0,0 +1,21 @@ +{ + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "semi": ["error", "always"], + "linebreak-style": [ "error", "unix" ] + }, + "env": { + "es6": true, + "node": true, + "mocha": true, + "jasmine": true + }, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "impliedStrict": true + }, + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index e69de29..393ef53 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -0,0 +1,136 @@ +# Created by https://www.gitignore.io/api/osx,vim,node,macos,windows + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +### OSX ### + +# Icon must end with two \r + +# Thumbnails + +# Files that might appear in the root of a volume + +# Directories potentially created on remote AFP share + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/osx,vim,node,macos,windows \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 4f6366c..dc51e76 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,11 +2,38 @@ "name": "frontend", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "webpack.config.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "webpack", + "watch": "webpack-dev-server --inline --hot" }, "keywords": [], "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "babel-core": "^6.26.0", + "babel-loader": "^7.1.2", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "clean-webpack-plugin": "^0.1.16", + "css-loader": "^0.28.7", + "dotenv": "^4.0.0", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^0.11.2", + "html-webpack-plugin": "^2.30.1", + "node-sass": "^4.5.3", + "react": "^15.6.1", + "react-dom": "^15.6.1", + "react-redux": "^5.0.6", + "react-router": "^4.2.0", + "react-router-dom": "^4.2.2", + "redux": "^3.7.2", + "sass-loader": "^6.0.6", + "superagent": "^3.6.0", + "uglifyjs-webpack-plugin": "^0.4.6", + "url-loader": "^0.5.9", + "webpack": "^3.5.6", + "webpack-dev-server": "^2.7.1" + } } diff --git a/frontend/src/component/app/index.js b/frontend/src/component/app/index.js index e69de29..1becc81 100644 --- a/frontend/src/component/app/index.js +++ b/frontend/src/component/app/index.js @@ -0,0 +1 @@ +console.log('HI THERE') \ No newline at end of file diff --git a/frontend/src/index.html b/frontend/src/index.html index e69de29..45bc1d6 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -0,0 +1,10 @@ + + + + + Auth Demo App + + +

+ + \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index e69de29..d209467 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -0,0 +1,5 @@ +import React from 'react'; +import ReactDom from 'react-dom'; +import App from './component/app'; + +ReactDom.render(, document.getElementById('root')); \ No newline at end of file From 1039aea51d54e3278c3576fe66d7a5da4475c688 Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 16:23:11 -0700 Subject: [PATCH 04/10] Creates Auth Actions --- frontend/.babelrc | 4 ++-- frontend/src/action/auth-actions.js | 36 +++++++++++++++++++++++++++++ frontend/src/component/app/index.js | 15 +++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/frontend/.babelrc b/frontend/.babelrc index 6575f38..47c9ace 100644 --- a/frontend/.babelrc +++ b/frontend/.babelrc @@ -1,4 +1,4 @@ { "presets": ["es2015", "react"], - "plugins": ["transform-object-spread-rest"] -} \ No newline at end of file + "plugins": ["transform-object-rest-spread"] +} diff --git a/frontend/src/action/auth-actions.js b/frontend/src/action/auth-actions.js index e69de29..011ad65 100644 --- a/frontend/src/action/auth-actions.js +++ b/frontend/src/action/auth-actions.js @@ -0,0 +1,36 @@ +import superagent from 'superagent'; + +export const tokenSet = (token) => ({ + type: 'TOKEN_SET', + payload: token +}) + +export const tokenDelete = () => ({ + type: 'TOKEN_DELETE' +}) + +export const signupRequest = (user) => (dispatch) => { + return superagent.post(`${__API_URL__}/signup`) + .withCredentials() + .send(user) + .then(response => { + dispatch(tokenSet(response.text)); + + try { + localStorage.token = response.text; + } catch (Err) { + console.log(err); + } + return response; + }); +}; + +export const loginRequest = (user) => (dispatch) => { + return superagent.get(`${__API_URL__}/login`) + .withCredentials() + .auth(user.username, user.password) + .then(response => { + dispatch(tokenSet(response.text)); + return response; + }); +}; \ No newline at end of file diff --git a/frontend/src/component/app/index.js b/frontend/src/component/app/index.js index 1becc81..9450355 100644 --- a/frontend/src/component/app/index.js +++ b/frontend/src/component/app/index.js @@ -1 +1,14 @@ -console.log('HI THERE') \ No newline at end of file +import React from 'react'; + +class App extends React.Component { + + render() { + return ( +
+ HI +
+ ) + } +} + +export default App; \ No newline at end of file From 78b0ae8934698fd3402b6f0850776566c6bb85ad Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 16:36:17 -0700 Subject: [PATCH 05/10] Adds Reducers --- frontend/src/reducer/auth-reducer.js | 9 +++++++++ frontend/src/reducer/index.js | 4 ++++ 2 files changed, 13 insertions(+) create mode 100644 frontend/src/reducer/auth-reducer.js diff --git a/frontend/src/reducer/auth-reducer.js b/frontend/src/reducer/auth-reducer.js new file mode 100644 index 0000000..2b881f2 --- /dev/null +++ b/frontend/src/reducer/auth-reducer.js @@ -0,0 +1,9 @@ +export default (state=null, action) => { + let {type, payload} = action; + + switch(type) { + case 'TOKEN_SET': return payload; + case 'TOKEN_DELETE': return null; + default: return state; + } +} \ No newline at end of file diff --git a/frontend/src/reducer/index.js b/frontend/src/reducer/index.js index e69de29..b099d04 100644 --- a/frontend/src/reducer/index.js +++ b/frontend/src/reducer/index.js @@ -0,0 +1,4 @@ +import {combineReducers} from 'redux'; +import auth from './auth-reducer.js'; + +export default combineReducers({ auth }); \ No newline at end of file From 03e8884f1898f77d6055cc9d75f2b129b7988cea Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 19:49:06 -0700 Subject: [PATCH 06/10] Adds Auth Form Component --- frontend/src/component/landing/index.js | 104 ++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/frontend/src/component/landing/index.js b/frontend/src/component/landing/index.js index e69de29..c305f25 100644 --- a/frontend/src/component/landing/index.js +++ b/frontend/src/component/landing/index.js @@ -0,0 +1,104 @@ +import React from 'react'; +import * as util from '../../lib/util.js'; + +class AuthForm extends React.Component { + constructor(props) { + super(props); + + this.state = { + username: '', + email: '', + password: '', + usernameError: null, + passwordError: null, + emailError: null, + error: null + } + + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleChange(e) { + let {name, value} = e.target; + + this.setState({ + [name]: value, + usernameError: name === 'username' && !value ? 'username required' : null, + emailError: name === 'email' && !value ? 'email required' : null, + passwordError: name === 'password' && !value ? 'password required' : null + }) + } + + handleSubmit({ + e.preventDefault(); + this.props.onComplete(this.state) + .then(() => { + this.setState({ + username: '', + email: '', + password: '' + }) + }) + .catch(error => { + console.error(error); + this.setState({error}); + }) + }) + + render() { + return ( +
+ + { + util.renderIf(this.props.auth === 'signup', + + ) + } + + { + util.renderIf(this.state.usernameError, + + {this.state.usernameError} + + ) + } + + + + { + util.renderIf(this.state.passwordError, + + {this.state.passwordError} + + ) + } + + + + +
+ ) + } +} + +export default AuthForm; \ No newline at end of file From 92d3885487b0101f6482fdda05ebc35c87237c46 Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 19:50:03 -0700 Subject: [PATCH 07/10] Adds AuthForm Component --- frontend/src/component/auth-form/index.js | 104 ++++++++++++++++++++++ frontend/src/component/landing/index.js | 104 ---------------------- 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/frontend/src/component/auth-form/index.js b/frontend/src/component/auth-form/index.js index e69de29..c305f25 100644 --- a/frontend/src/component/auth-form/index.js +++ b/frontend/src/component/auth-form/index.js @@ -0,0 +1,104 @@ +import React from 'react'; +import * as util from '../../lib/util.js'; + +class AuthForm extends React.Component { + constructor(props) { + super(props); + + this.state = { + username: '', + email: '', + password: '', + usernameError: null, + passwordError: null, + emailError: null, + error: null + } + + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleChange(e) { + let {name, value} = e.target; + + this.setState({ + [name]: value, + usernameError: name === 'username' && !value ? 'username required' : null, + emailError: name === 'email' && !value ? 'email required' : null, + passwordError: name === 'password' && !value ? 'password required' : null + }) + } + + handleSubmit({ + e.preventDefault(); + this.props.onComplete(this.state) + .then(() => { + this.setState({ + username: '', + email: '', + password: '' + }) + }) + .catch(error => { + console.error(error); + this.setState({error}); + }) + }) + + render() { + return ( +
+ + { + util.renderIf(this.props.auth === 'signup', + + ) + } + + { + util.renderIf(this.state.usernameError, + + {this.state.usernameError} + + ) + } + + + + { + util.renderIf(this.state.passwordError, + + {this.state.passwordError} + + ) + } + + + + +
+ ) + } +} + +export default AuthForm; \ No newline at end of file diff --git a/frontend/src/component/landing/index.js b/frontend/src/component/landing/index.js index c305f25..e69de29 100644 --- a/frontend/src/component/landing/index.js +++ b/frontend/src/component/landing/index.js @@ -1,104 +0,0 @@ -import React from 'react'; -import * as util from '../../lib/util.js'; - -class AuthForm extends React.Component { - constructor(props) { - super(props); - - this.state = { - username: '', - email: '', - password: '', - usernameError: null, - passwordError: null, - emailError: null, - error: null - } - - this.handleChange = this.handleChange.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleChange(e) { - let {name, value} = e.target; - - this.setState({ - [name]: value, - usernameError: name === 'username' && !value ? 'username required' : null, - emailError: name === 'email' && !value ? 'email required' : null, - passwordError: name === 'password' && !value ? 'password required' : null - }) - } - - handleSubmit({ - e.preventDefault(); - this.props.onComplete(this.state) - .then(() => { - this.setState({ - username: '', - email: '', - password: '' - }) - }) - .catch(error => { - console.error(error); - this.setState({error}); - }) - }) - - render() { - return ( -
- - { - util.renderIf(this.props.auth === 'signup', - - ) - } - - { - util.renderIf(this.state.usernameError, - - {this.state.usernameError} - - ) - } - - - - { - util.renderIf(this.state.passwordError, - - {this.state.passwordError} - - ) - } - - - - -
- ) - } -} - -export default AuthForm; \ No newline at end of file From 0c19308238232279506b504742977082ac526961 Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 20:14:41 -0700 Subject: [PATCH 08/10] Adds Landing --- frontend/src/component/landing/index.js | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/frontend/src/component/landing/index.js b/frontend/src/component/landing/index.js index e69de29..9ae15f1 100644 --- a/frontend/src/component/landing/index.js +++ b/frontend/src/component/landing/index.js @@ -0,0 +1,32 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import AuthForm from '../auth-form'; +import * as util from '../../lib/util.js'; +import {signupRequest, loginRequest} from '../../action/auth-actions.js'; + +class Landing extends React.Component { + render() { + let {params} = this.props.match; + + let handle Complete = + params.auth === 'login' ? + this.props.login : this.props.signup; + + return ( +
+ +
+ ) + } +} + +let mapDispatchToProps = (dispatch) => { + return { + signup: (user) => dispatch(signupRequest(user)), + login: (user) => dispatch(loginRequest(user)) + } +} + +export default connect(undefined, mapDispatchToProps)(Landing); \ No newline at end of file From 377d9e5455c664d411ee37f25614bb8bb04ab92e Mon Sep 17 00:00:00 2001 From: ohjonah Date: Wed, 6 Sep 2017 23:17:46 -0700 Subject: [PATCH 09/10] TS Components --- frontend/src/component/app/index.js | 28 +++++++++++++++++++---- frontend/src/component/auth-form/index.js | 5 ++-- frontend/src/component/landing/index.js | 2 +- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/frontend/src/component/app/index.js b/frontend/src/component/app/index.js index 9450355..88d842d 100644 --- a/frontend/src/component/app/index.js +++ b/frontend/src/component/app/index.js @@ -1,14 +1,34 @@ import React from 'react'; +import {Provider} from 'react-redux'; +import {BrowserRouter, Route, Link} from 'react-router-dom'; +import appStoreCreate from '../../lib/app-create-store.js'; +import LandingContainer from '../landing'; -class App extends React.Component { +let store = appStoreCreate(); +class App extends React.Component { render() { return ( -
- HI +
+ + +
+
+

cfgram

+ +
+ +
+
+
) } } -export default App; \ No newline at end of file +export default App; diff --git a/frontend/src/component/auth-form/index.js b/frontend/src/component/auth-form/index.js index c305f25..9c77435 100644 --- a/frontend/src/component/auth-form/index.js +++ b/frontend/src/component/auth-form/index.js @@ -30,8 +30,9 @@ class AuthForm extends React.Component { }) } - handleSubmit({ + handleSubmit(e) { e.preventDefault(); + this.props.onComplete(this.state) .then(() => { this.setState({ @@ -44,7 +45,7 @@ class AuthForm extends React.Component { console.error(error); this.setState({error}); }) - }) + } render() { return ( diff --git a/frontend/src/component/landing/index.js b/frontend/src/component/landing/index.js index 9ae15f1..dcfdeca 100644 --- a/frontend/src/component/landing/index.js +++ b/frontend/src/component/landing/index.js @@ -8,7 +8,7 @@ class Landing extends React.Component { render() { let {params} = this.props.match; - let handle Complete = + let handleComplete = params.auth === 'login' ? this.props.login : this.props.signup; From 7e5735c53c6eb749a631409de6b61856d0746ada Mon Sep 17 00:00:00 2001 From: ohjonah Date: Thu, 7 Sep 2017 07:31:51 -0700 Subject: [PATCH 10/10] TS API URL --- frontend/.dev.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/.dev.env b/frontend/.dev.env index 667e45c..3a2fb15 100644 --- a/frontend/.dev.env +++ b/frontend/.dev.env @@ -1 +1 @@ -API_URL='http://localhost:3000' \ No newline at end of file +API_URL='http://localhost:8000' \ No newline at end of file