From d82f3d0f0d74691b91cd8dfa6516b86972388c12 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Mon, 6 Mar 2017 15:26:44 -0700
Subject: [PATCH 01/56] Added initial recaptcha + RL support
---
client/coral-admin/src/translations.json | 6 +-
errors.js | 8 +-
models/user.js | 20 +
package.json | 3 +-
services/passport.js | 262 ++++++++-
services/users.js | 104 +++-
test/services/users.js | 12 +-
yarn.lock | 673 ++++++++++++-----------
8 files changed, 694 insertions(+), 394 deletions(-)
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index cebc499ca..ecc8c4b95 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -1,7 +1,8 @@
{
"en": {
"errors": {
- "NOT_AUTHORIZED": "Your username or password is not recognized by our system."
+ "NOT_AUTHORIZED": "Your username or password is not recognized by our system.",
+ "LOGIN_MAXIMUM_EXCEEDED": "You have made too many unsuccessful password attempts. Please wait."
},
"community": {
"username_and_email": "Username and Email",
@@ -139,7 +140,8 @@
},
"es": {
"errors": {
- "NOT_AUTHORIZED": "Acción no autorizada."
+ "NOT_AUTHORIZED": "Acción no autorizada.",
+ "LOGIN_MAXIMUM_EXCEEDED": "Ha realizado demasiados intentos fallidos de contraseña. Por favor espera."
},
"community": {
"username_and_email": "Usuario y E-mail",
diff --git a/errors.js b/errors.js
index 6d9abbcfd..6cd8d8da0 100644
--- a/errors.js
+++ b/errors.js
@@ -148,6 +148,11 @@ const ErrPermissionUpdateUsername = new APIError('You do not have permission to
status: 500
});
+const ErrLoginAttemptMaximumExceeded = new APIError('You have made too many incorrect password attempts.', {
+ translation_key: 'LOGIN_MAXIMUM_EXCEEDED',
+ status: 429
+});
+
module.exports = {
ExtendableError,
APIError,
@@ -168,5 +173,6 @@ module.exports = {
ErrNotAuthorized,
ErrPermissionUpdateUsername,
ErrSettingsInit,
- ErrInstallLock
+ ErrInstallLock,
+ ErrLoginAttemptMaximumExceeded
};
diff --git a/models/user.js b/models/user.js
index 2b65bd300..e23acaa56 100644
--- a/models/user.js
+++ b/models/user.js
@@ -1,4 +1,5 @@
const mongoose = require('../services/mongoose');
+const bcrypt = require('bcrypt');
const uuid = require('uuid');
// USER_ROLES is the array of roles that is permissible as a user role.
@@ -146,6 +147,25 @@ UserSchema.method('hasRoles', function(...roles) {
});
});
+/**
+ * This verifies that a password is valid.
+ */
+UserSchema.method('verifyPassword', function(password) {
+ return new Promise((resolve, reject) => {
+ bcrypt.compare(password, this.password, (err, res) => {
+ if (err) {
+ return reject(err);
+ }
+
+ if (!res) {
+ return resolve(false);
+ }
+
+ return resolve(true);
+ });
+ });
+});
+
/**
* All the graph operations that are available for a user.
* @type {Array}
diff --git a/package.json b/package.json
index f67a36908..48eb71f74 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
"env-rewrite": "^1.0.2",
"express": "^4.14.0",
"express-session": "^1.14.2",
+ "form-data": "^2.1.2",
"graphql": "^0.8.2",
"graphql-errors": "^2.1.0",
"graphql-server-express": "^0.5.0",
@@ -76,6 +77,7 @@
"mongoose": "^4.6.5",
"morgan": "^1.7.0",
"natural": "^0.4.0",
+ "node-fetch": "^1.6.3",
"nodemailer": "^2.6.4",
"parse-duration": "^0.1.1",
"passport": "^0.3.2",
@@ -136,7 +138,6 @@
"mocha": "^3.1.2",
"mocha-junit-reporter": "^1.12.1",
"nightwatch": "^0.9.11",
- "node-fetch": "^1.6.3",
"nodemon": "^1.11.0",
"postcss-loader": "^1.1.0",
"postcss-modules": "^0.5.2",
diff --git a/services/passport.js b/services/passport.js
index 229541521..4ec80bb60 100644
--- a/services/passport.js
+++ b/services/passport.js
@@ -1,9 +1,12 @@
const passport = require('passport');
const UsersService = require('./users');
const SettingsService = require('./settings');
+const fetch = require('node-fetch');
+const FormData = require('form-data');
const LocalStrategy = require('passport-local').Strategy;
const FacebookStrategy = require('passport-facebook').Strategy;
const errors = require('../errors');
+const debug = require('debug')('talk:passport');
//==============================================================================
// SESSION SERIALIZATION
@@ -74,27 +77,233 @@ function ValidateUserLogin(loginProfile, user, done) {
// STRATEGIES
//==============================================================================
+/**
+ * This looks at the request headers to see if there is a recaptcha response on
+ * the input request.
+ */
+const CheckIfRecaptcha = (req) => {
+ let response = req.get('X-Recaptcha-Response');
+
+ if (response && response.length > 0) {
+ return true;
+ }
+
+ return false;
+};
+
+/**
+ * This checks the user to see if the current email profile needs to get checked
+ * for recaptcha compliance before being allowed to login.
+ */
+const CheckIfNeedsRecaptcha = (user, email) => {
+
+ // Get the profile representing the local account.
+ let profile = user.profiles.find((profile) => profile.id === email);
+
+ // This should never get to this point, if it does, don't let this past.
+ if (!profile) {
+ throw new Error('ID indicated by loginProfile is not on user object');
+ }
+
+ if (profile.metadata && profile.metadata.recaptcha_required) {
+ return true;
+ }
+
+ return false;
+};
+
+/**
+ * This stores the Recaptcha secret.
+ */
+const RECAPTCHA_SECRET = process.env.TALK_RECAPTCHA_SECRET;
+
+/**
+ * This is true when the recaptcha secret is provided and the Recaptcha feature
+ * is to be enabled.
+ */
+const RECAPTCHA_ENABLED = RECAPTCHA_SECRET && RECAPTCHA_SECRET.length > 0;
+if (!RECAPTCHA_ENABLED) {
+ console.log('Recaptcha is not enabled for login/signup abuse prevention, set TALK_RECAPTCHA_SECRET to enable Recaptcha.');
+}
+
+/**
+ * This sends the request details down Google to check to see if the response is
+ * genuine or not.
+ * @return {Promise} resolves with the success status of the recaptcha
+ */
+const CheckRecaptcha = async (req) => {
+
+ // Ask Google to verify the recaptcha response: https://developers.google.com/recaptcha/docs/verify
+ const form = new FormData();
+
+ form.append('secret', RECAPTCHA_SECRET);
+ form.append('response', req.get('X-Recaptcha-Response'));
+ form.append('remoteip', req.ip);
+
+ // Perform the request.
+ let res = await fetch('https://www.google.com/recaptcha/api/siteverify', {
+ method: 'POST',
+ body: form,
+ headers: form.getHeaders()
+ });
+
+ // Parse the JSON response.
+ let json = await res.json();
+
+ return json.success;
+};
+
+/**
+ * This records a login attempt failure as well as optionally flags an account
+ * for requiring a recaptcha in the future outside the temporary window.
+ * @return {Promise} resolves with nothing if rate limit not exeeded, errors if
+ * there is a rate limit error
+ */
+const HandleFailedAttempt = async (email, userNeedsRecaptcha) => {
+ try {
+ await UsersService.recordLoginAttempt(email);
+ } catch (err) {
+ if (err === errors.ErrLoginAttemptMaximumExceeded && !userNeedsRecaptcha && RECAPTCHA_ENABLED) {
+
+ debug(`flagging user email=${email}`);
+ await UsersService.flagForRecaptchaRequirement(email, true);
+ }
+
+ throw err;
+ }
+};
+
passport.use(new LocalStrategy({
usernameField: 'email',
- passwordField: 'password'
-}, (email, password, done) => {
- UsersService
- .findLocalUser(email, password)
- .then((user) => {
- if (!user) {
- return done(null, false, {message: 'Incorrect email/password combination'});
+ passwordField: 'password',
+ passReqToCallback: true
+}, async (req, email, password, done) => {
+
+ // We need to check if this request has a recaptcha on it at all, if it does,
+ // we must verify it first. If verification fails, we fail the request early.
+ // We can only do this obviously when recaptcha is enabled.
+ let hasRecaptcha = CheckIfRecaptcha(req);
+ let recaptchaPassed = false;
+ if (RECAPTCHA_ENABLED && hasRecaptcha) {
+
+ try {
+
+ // Check to see if this recaptcha passed.
+ recaptchaPassed = await CheckRecaptcha(req);
+ } catch (err) {
+ return done(err);
+ }
+
+ if (!recaptchaPassed) {
+ try {
+ await HandleFailedAttempt(email);
+ } catch (err) {
+ return done(err);
}
- // Define the loginProfile being used to perform an additional
- // verificaiton.
- let loginProfile = {id: email, provider: 'local'};
+ return done(null, false, {message: 'Incorrect recaptcha'});
+ }
+ }
- // Validate the user login.
- return ValidateUserLogin(loginProfile, user, done);
- })
- .catch((err) => {
- done(err);
- });
+ debug(`hasRecaptcha=${hasRecaptcha}, recaptchaPassed=${recaptchaPassed}`);
+
+ // If the request didn't have a recaptcha, check to see if we did need one by
+ // checking the rate limit against failed attempts on this email
+ // address/login.
+ if (!hasRecaptcha) {
+ try {
+ await UsersService.checkLoginAttempts(email);
+ } catch (err) {
+ if (err === errors.ErrLoginAttemptMaximumExceeded) {
+
+ // This says, we didn't have a recaptcha, yet we needed one.. Reject
+ // here.
+
+ try {
+ await HandleFailedAttempt(email);
+ } catch (err) {
+ return done(err);
+ }
+
+ return done(null, false, {message: 'Incorrect recaptcha'});
+ }
+
+ // Some other unexpected error occured.
+ return done(err);
+ }
+ }
+
+ // Let's find the user for which this login is connected to.
+ let user;
+ try {
+ user = await UsersService.findLocalUser(email);
+ } catch (err) {
+ return done(err);
+ }
+
+ debug(`user=${user != null}`);
+
+ // If the user doesn't exist, then mark this as a failed attempt at logging in
+ // this non-existant user and continue.
+ if (!user) {
+ try {
+ await HandleFailedAttempt(email);
+ } catch (err) {
+ return done(err);
+ }
+
+ return done(null, false, {message: 'Incorrect email/password combination'});
+ }
+
+ // Let's check if the user indeed needed recaptcha in order to authenticate.
+ // We can only do this obviously when recaptcha is enabled.
+ let userNeedsRecaptcha = false;
+ if (RECAPTCHA_ENABLED && user) {
+ userNeedsRecaptcha = CheckIfNeedsRecaptcha(user, email);
+ }
+
+ debug(`userNeedsRecaptcha=${userNeedsRecaptcha}`);
+
+ // Let's check now if their password is correct.
+ let userPasswordCorrect;
+ try {
+ userPasswordCorrect = await user.verifyPassword(password);
+ } catch (err) {
+ return done(err);
+ }
+
+ debug(`userPasswordCorrect=${userPasswordCorrect}`);
+
+ // If their password wasn't correct, mark their attempt as failed and
+ // continue.
+ if (!userPasswordCorrect) {
+ try {
+ await HandleFailedAttempt(email, userNeedsRecaptcha);
+ } catch (err) {
+ return done(err);
+ }
+
+ return done(null, false, {message: 'Incorrect email/password combination'});
+ }
+
+ // If the user needed a recaptcha, yet we have gotten this far, this indicates
+ // that the password was correct, so let's unflag their account for logins. We
+ // can only do this obviously when recaptcha is enabled. The account wouldn't
+ // have been flagged otherwise.
+ if (RECAPTCHA_ENABLED && userNeedsRecaptcha) {
+ try {
+ await UsersService.flagForRecaptchaRequirement(email, false);
+ } catch (err) {
+ return done(err);
+ }
+ }
+
+ // Define the loginProfile being used to perform an additional
+ // verificaiton.
+ let loginProfile = {id: email, provider: 'local'};
+
+ // Perform final steps to login the user.
+ return ValidateUserLogin(loginProfile, user, done);
}));
if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET && process.env.TALK_ROOT_URL) {
@@ -102,17 +311,18 @@ if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET &&
clientID: process.env.TALK_FACEBOOK_APP_ID,
clientSecret: process.env.TALK_FACEBOOK_APP_SECRET,
callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`,
-
+ passReqToCallback: true,
profileFields: ['id', 'displayName', 'picture.type(large)']
- }, (accessToken, refreshToken, profile, done) => {
- UsersService
- .findOrCreateExternalUser(profile)
- .then((user) => {
- return ValidateUserLogin(profile, user, done);
- })
- .catch((err) => {
- done(err);
- });
+ }, async (req, accessToken, refreshToken, profile, done) => {
+
+ let user;
+ try {
+ user = await UsersService.findOrCreateExternalUser(profile);
+ } catch (err) {
+ return done(err);
+ }
+
+ return ValidateUserLogin(profile, user, done);
}));
} else {
console.error('Facebook cannot be enabled, missing one of TALK_FACEBOOK_APP_ID, TALK_FACEBOOK_APP_SECRET, TALK_ROOT_URL');
diff --git a/services/users.js b/services/users.js
index b58776a75..1810cf2a5 100644
--- a/services/users.js
+++ b/services/users.js
@@ -3,11 +3,16 @@ const jwt = require('jsonwebtoken');
const Wordlist = require('./wordlist');
const errors = require('../errors');
const uuid = require('uuid');
+const redis = require('./redis');
+const redisClient = redis.createClient();
const UserModel = require('../models/user');
const USER_STATUS = require('../models/user').USER_STATUS;
const USER_ROLES = require('../models/user').USER_ROLES;
+const RECAPTCHA_WINDOW_SECONDS = 60 * 10; // 10 minutes.
+const RECAPTCHA_INCORRECT_TRIGGER = 5; // after 3 incorrect attempts, recaptcha will be required.
+
const ActionsService = require('./actions');
// In the event that the TALK_SESSION_SECRET is missing but we are testing, then
@@ -30,13 +35,9 @@ const SALT_ROUNDS = 10;
module.exports = class UsersService {
/**
- * Finds a user given their email address that we have for them in the system
- * and ensures that the retuned user matches the password passed in as well.
- * @param {string} email - email to look up the user by
- * @param {string} password - password to match against the found user
- * @param {Function} done [description]
+ * Returns a user (if found) for the given email address.
*/
- static findLocalUser(email, password) {
+ static findLocalUser(email) {
if (!email || typeof email !== 'string') {
return Promise.reject('email is required for findLocalUser');
}
@@ -48,32 +49,99 @@ module.exports = class UsersService {
provider: 'local'
}
}
- })
- .then((user) => {
- if (!user) {
- return false;
- }
+ });
+ }
- return new Promise((resolve, reject) => {
- bcrypt.compare(password, user.password, (err, res) => {
+ /**
+ * This records an unsucesfull login attempt for the given email address. If
+ * the maximum has been reached, the promise will be rejected with:
+ *
+ * errors.ErrLoginAttemptMaximumExceeded
+ *
+ * Indicating that the account should be flagged as "login recaptcha required"
+ * where future login attempts must be made with the recaptcha flag.
+ */
+ static recordLoginAttempt(email) {
+ const rdskey = `la[${email.toLowerCase().trim()}]`;
+
+ return new Promise((resolve, reject) => {
+ redisClient
+ .multi()
+ .incr(rdskey)
+ .expire(rdskey, RECAPTCHA_WINDOW_SECONDS)
+ .exec((err, replies) => {
if (err) {
return reject(err);
}
- if (!res) {
- return resolve(false);
+ // if this is new or has no expiry
+ if (replies[0] === 1 || replies[1] === -1) {
+
+ // then expire it after the timeout
+ redisClient.expire(rdskey, RECAPTCHA_WINDOW_SECONDS);
}
- return resolve(user);
+ if (replies[0] >= RECAPTCHA_INCORRECT_TRIGGER) {
+ return reject(errors.ErrLoginAttemptMaximumExceeded);
+ }
+
+ resolve();
});
- });
+ });
+ }
+
+ /**
+ * This checks to see if the current login attempts against a user exeeds the
+ * maximum value allowed, if so, it rejects with:
+ *
+ * errors.ErrLoginAttemptMaximumExceeded
+ */
+ static checkLoginAttempts(email) {
+ const rdskey = `la[${email.toLowerCase().trim()}]`;
+
+ return new Promise((resolve, reject) => {
+ redisClient
+ .get(rdskey, (err, reply) => {
+ if (err) {
+ return reject(err);
+ }
+
+ if (!reply) {
+ return resolve();
+ }
+
+ if (reply >= RECAPTCHA_INCORRECT_TRIGGER) {
+ return reject(errors.ErrLoginAttemptMaximumExceeded);
+ }
+
+ resolve();
+ });
+ });
+ }
+
+ /**
+ * Sets or unsets the recaptcha_required flag on a user's local profile.
+ */
+ static flagForRecaptchaRequirement(email, required) {
+ return UserModel.update({
+ profiles: {
+ $elemMatch: {
+ id: email.toLowerCase(),
+ provider: 'local'
+ }
+ }
+ }, {
+ $set: {
+ 'profiles.$.metadata.recaptcha_required': required
+ }
});
}
/**
* Merges two users together by taking all the profiles on a given user and
* pushing them into the source user followed by deleting the destination user's
- * user account. This will not merge the roles associated with the source user.
+ * user account. This will
+ * not merge the roles associated with the source user.
* @param {String} dstUserID id of the user to which is the target of the merge
* @param {String} srcUserID id of the user to which is the source of the merge
* @return {Promise} resolves when the users are merged
diff --git a/test/services/users.js b/test/services/users.js
index 51ba01403..94aa92432 100644
--- a/test/services/users.js
+++ b/test/services/users.js
@@ -64,22 +64,14 @@ describe('services.UsersService', () => {
describe('#findLocalUser', () => {
- it('should find a user when we give the right credentials', () => {
+ it('should find a user', () => {
return UsersService
- .findLocalUser(mockUsers[0].profiles[0].id, '1Coral!-')
+ .findLocalUser(mockUsers[0].profiles[0].id)
.then((user) => {
expect(user).to.have.property('username', mockUsers[0].username);
});
});
- it('should not find the user when we give the wrong credentials', () => {
- return UsersService
- .findLocalUser(mockUsers[0].profiles[0].id, '1Coral!-')
- .then((user) => {
- expect(user).to.equal(false);
- });
- });
-
});
describe('#createLocalUser', () => {
diff --git a/yarn.lock b/yarn.lock
index 82b5e92f3..f4de3a813 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1,5 +1,7 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
+
+
"@kadira/storybook-deployer@^1.1.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@kadira/storybook-deployer/-/storybook-deployer-1.2.0.tgz#1708f5cb37fa08ab4173b1bd99df6f4717dfae12"
@@ -289,14 +291,14 @@ assert@^1.1.1:
dependencies:
util "0.10.3"
-assertion-error@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c"
-
assertion-error@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.0.tgz#c7f85438fdd466bc7ca16ab90c81513797a5d23b"
+assertion-error@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c"
+
ast-traverse@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6"
@@ -313,11 +315,15 @@ async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
-async@^1.4.0, async@^1.4.2, async@^1.5.2, async@1.x:
+async@1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/async/-/async-1.2.1.tgz#a4816a17cd5ff516dfa2c7698a453369b9790de0"
+
+async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
-async@^2.1.2, async@^2.1.4, async@2.1.4:
+async@2.1.4, async@^2.1.2, async@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4"
dependencies:
@@ -331,10 +337,6 @@ async@~0.9.0:
version "0.9.2"
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
-async@1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/async/-/async-1.2.1.tgz#a4816a17cd5ff516dfa2c7698a453369b9790de0"
-
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1053,6 +1055,10 @@ babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0:
version "6.15.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e"
+balanced-match@0.1.0, balanced-match@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
+
balanced-match@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.2.1.tgz#7bc658b4bed61eee424ad74f75f5c3e2c4df3cc7"
@@ -1061,10 +1067,6 @@ balanced-match@^0.4.1, balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
-balanced-match@~0.1.0, balanced-match@0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
-
base64-js@^1.0.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
@@ -1073,7 +1075,7 @@ base64-url@1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/base64-url/-/base64-url-1.3.3.tgz#f8b6c537f09a4fc58c99cb86e0b0e9c61461a20f"
-base64url@^2.0.0, base64url@2.0.0:
+base64url@2.0.0, base64url@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb"
@@ -1119,10 +1121,6 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bluebird@^2.10.2:
- version "2.11.0"
- resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
-
bluebird@2.10.2:
version "2.10.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.10.2.tgz#024a5517295308857f14f91f1106fc3b555f446b"
@@ -1139,6 +1137,10 @@ bluebird@3.4.6:
version "3.4.6"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f"
+bluebird@^2.10.2:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
+
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.6"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
@@ -1383,7 +1385,7 @@ chai@^3.5.0:
deep-eql "^0.1.3"
type-detect "^1.0.0"
-chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3, chalk@1.1.3:
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
@@ -1542,7 +1544,7 @@ cli-cursor@^2.1.0:
dependencies:
restore-cursor "^2.0.0"
-cli-table@^0.3.1, cli-table@0.3.1:
+cli-table@0.3.1, cli-table@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
dependencies:
@@ -1626,18 +1628,18 @@ colormin@^1.0.5:
css-color-names "0.0.4"
has "^1.0.1"
-colors@~0.6.0-1:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
-
-colors@~1.1.2, colors@1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
-
colors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
+colors@1.1.2, colors@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+
+colors@~0.6.0-1:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
+
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"
@@ -1650,16 +1652,6 @@ combined-stream@~0.0.4:
dependencies:
delayed-stream "0.0.5"
-commander@^2.5.0, commander@^2.9.0, commander@2.9.0:
- version "2.9.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
- dependencies:
- graceful-readlink ">= 1.0.0"
-
-commander@~2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781"
-
commander@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06"
@@ -1678,6 +1670,16 @@ commander@2.8.x:
dependencies:
graceful-readlink ">= 1.0.0"
+commander@2.9.0, commander@^2.5.0, commander@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
+ dependencies:
+ graceful-readlink ">= 1.0.0"
+
+commander@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781"
+
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -1805,7 +1807,7 @@ cookie@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
-cookiejar@^2.0.6, cookiejar@2.0.x:
+cookiejar@2.0.x, cookiejar@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.0.6.tgz#0abf356ad00d1c5a219d88d44518046dd026acfe"
@@ -1830,7 +1832,7 @@ 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, core-util-is@1.0.2:
+core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -2020,7 +2022,7 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"
-"cssom@>= 0.3.0 < 0.4.0", cssom@0.3.x:
+cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
@@ -2045,16 +2047,16 @@ cz-conventional-changelog@1.1.5:
dependencies:
word-wrap "^1.0.3"
+d3-helpers@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/d3-helpers/-/d3-helpers-0.3.0.tgz#4b31dce4a2121a77336384574d893fbed5fb293d"
+
d@^0.1.1, d@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309"
dependencies:
es5-ext "~0.10.2"
-d3-helpers@0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/d3-helpers/-/d3-helpers-0.3.0.tgz#4b31dce4a2121a77336384574d893fbed5fb293d"
-
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -2085,17 +2087,13 @@ date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
-debug@*, debug@^2.1.1, debug@^2.2.0, debug@2, debug@2.6.0:
+debug@*, debug@2, debug@2.6.0, debug@^2.1.1, debug@^2.2.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b"
dependencies:
ms "0.7.2"
-debug@^0.7.2, debug@~0.7.4:
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
-
-debug@~2.2.0, debug@2.2.0:
+debug@2.2.0, debug@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
dependencies:
@@ -2107,11 +2105,15 @@ debug@2.3.3:
dependencies:
ms "0.7.2"
+debug@^0.7.2, debug@~0.7.4:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
+
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
-deep-eql@^0.1.3, deep-eql@0.1.3:
+deep-eql@0.1.3, deep-eql@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2"
dependencies:
@@ -2177,14 +2179,14 @@ del@^2.0.2:
pinkie-promise "^2.0.0"
rimraf "^2.2.8"
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
-
delayed-stream@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.5.tgz#d4b1f43a93e8296dfe02694f4680bc37a313c73f"
+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"
@@ -2241,7 +2243,7 @@ dns-prefetch-control@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2"
-doctrine@^1.2.2, doctrine@1.5.0:
+doctrine@1.5.0, doctrine@^1.2.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
dependencies:
@@ -2252,7 +2254,7 @@ doctypes@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9"
-dom-serializer@~0.1.0, dom-serializer@0:
+dom-serializer@0, dom-serializer@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
dependencies:
@@ -2263,21 +2265,21 @@ domain-browser@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+domelementtype@1, domelementtype@~1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
domelementtype@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
-domelementtype@~1.1.1, domelementtype@1:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
-
-domhandler@^2.3.0, domhandler@2.3:
+domhandler@2.3, domhandler@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738"
dependencies:
domelementtype "1"
-domutils@^1.5.1, domutils@1.5, domutils@1.5.1:
+domutils@1.5, domutils@1.5.1, domutils@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
dependencies:
@@ -2361,18 +2363,18 @@ encoding@^0.1.11:
dependencies:
iconv-lite "~0.4.13"
-end-of-stream@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07"
- dependencies:
- once "~1.3.0"
-
end-of-stream@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.0.0.tgz#d4596e702734a93e40e9af864319eabd99ff2f0e"
dependencies:
once "~1.3.0"
+end-of-stream@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07"
+ dependencies:
+ once "~1.3.0"
+
enhanced-resolve@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.0.3.tgz#df14c06b5fc5eecade1094c9c5a12b4b3edc0b62"
@@ -2382,14 +2384,14 @@ enhanced-resolve@^3.0.0:
object-assign "^4.0.1"
tapable "^0.2.5"
-entities@^1.1.1, entities@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
-
entities@1.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26"
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
env-rewrite@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/env-rewrite/-/env-rewrite-1.0.2.tgz#3e344a95af1bdaab34a559479b8be3abd0804183"
@@ -2463,14 +2465,14 @@ es6-map@^0.1.3:
es6-symbol "~3.1.0"
event-emitter "~0.3.4"
-es6-promise@^3.0.2:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
-
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.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8"
@@ -2485,7 +2487,7 @@ es6-shim@^0.35.3:
version "0.35.3"
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.3.tgz#9bfb7363feffff87a6cdb6cd93e405ec3c4b6f26"
-es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3:
+es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa"
dependencies:
@@ -2509,15 +2511,15 @@ escape-regexp-component@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz#9c63b6d0b25ff2a88c3adbd18c5b61acc3b9faa2"
-escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5, escape-string-regexp@1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
-
escape-string-regexp@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1"
-escodegen@^1.6.1, escodegen@1.x.x:
+escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+escodegen@1.x.x, escodegen@^1.6.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
dependencies:
@@ -2653,14 +2655,14 @@ esprima-fb@~15001.1001.0-dev-harmony-fb:
version "15001.1001.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659"
+esprima@3.x.x, esprima@~3.1.0:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+
esprima@^2.6.0, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
-esprima@~3.1.0, esprima@3.x.x:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
-
esrecurse@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220"
@@ -2785,14 +2787,14 @@ express@^4.12.2, express@^4.14.0:
utils-merge "1.0.0"
vary "~1.1.0"
+extend@3, extend@^3.0.0, extend@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
+
extend@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extend/-/extend-1.3.0.tgz#d1516fb0ff5624d2ebf9123ea1dac5a1994004f8"
-extend@^3.0.0, extend@~3.0.0, extend@3:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
-
external-editor@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.1.tgz#4c597c6c88fa6410e41dbbaa7b1be2336aa31095"
@@ -2961,6 +2963,14 @@ forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+form-data@1.0.0-rc4:
+ version "1.0.0-rc4"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.0-rc4.tgz#05ac6bc22227b43e4461f488161554699d4f8b5e"
+ dependencies:
+ async "^1.5.2"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.10"
+
form-data@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-0.2.0.tgz#26f8bc26da6440e299cbdcfb69035c4f77a6e466"
@@ -2969,7 +2979,7 @@ form-data@^0.2.0:
combined-stream "~0.0.4"
mime-types "~2.0.3"
-form-data@~2.1.1:
+form-data@^2.1.2, form-data@~2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4"
dependencies:
@@ -2977,14 +2987,6 @@ form-data@~2.1.1:
combined-stream "^1.0.5"
mime-types "^2.1.12"
-form-data@1.0.0-rc4:
- version "1.0.0-rc4"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.0-rc4.tgz#05ac6bc22227b43e4461f488161554699d4f8b5e"
- dependencies:
- async "^1.5.2"
- combined-stream "^1.0.5"
- mime-types "^2.1.10"
-
formidable@^1.0.17:
version "1.1.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9"
@@ -3199,37 +3201,6 @@ glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
-glob@^5.0.15, glob@^5.0.3:
- version "5.0.15"
- resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
- dependencies:
- inflight "^1.0.4"
- inherits "2"
- minimatch "2 || 3"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^6.0.4:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
- dependencies:
- inflight "^1.0.4"
- inherits "2"
- minimatch "2 || 3"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@7.1.1:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.2"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
glob@3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.3.tgz#e313eeb249c7affaa5c475286b0e115b59839467"
@@ -3259,6 +3230,37 @@ glob@7.0.5, glob@7.0.x:
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^5.0.15, glob@^5.0.3:
+ version "5.0.15"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^6.0.4:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
globals@^9.0.0, globals@^9.14.0:
version "9.14.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034"
@@ -3600,15 +3602,15 @@ https-proxy-agent@1:
debug "2"
extend "3"
-iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13, iconv-lite@0.4.15:
- version "0.4.15"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
-
iconv-lite@0.4.13:
version "0.4.13"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
-icss-replace-symbols@^1.0.2, icss-replace-symbols@1.0.2:
+iconv-lite@0.4.15, iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
+ version "0.4.15"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
+
+icss-replace-symbols@1.0.2, icss-replace-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5"
@@ -3666,7 +3668,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@2, inherits@2.0.3:
+inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
@@ -3685,7 +3687,24 @@ inquirer-confirm@0.2.2:
bluebird "2.9.24"
inquirer "0.8.2"
-inquirer@^0.12.0, inquirer@0.12.0:
+inquirer@0.11.1:
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.11.1.tgz#623b6e0c101d2fe9e8e8ed902e651e0519d143e5"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ ansi-regex "^2.0.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^1.0.1"
+ run-async "^0.1.0"
+ rx-lite "^3.1.2"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+inquirer@0.12.0, inquirer@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
dependencies:
@@ -3703,6 +3722,19 @@ inquirer@^0.12.0, inquirer@0.12.0:
strip-ansi "^3.0.0"
through "^2.3.6"
+inquirer@0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.8.2.tgz#41586548e1c5d9b3f81df7325034baacab6f58ab"
+ dependencies:
+ ansi-regex "^1.1.1"
+ chalk "^1.0.0"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^0.1.1"
+ rx "^2.4.3"
+ through "^2.3.6"
+
inquirer@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.1.tgz#6dfbffaf4d697dd76c8fe349f919de01c28afc4d"
@@ -3721,36 +3753,6 @@ inquirer@^3.0.1:
strip-ansi "^3.0.0"
through "^2.3.6"
-inquirer@0.11.1:
- version "0.11.1"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.11.1.tgz#623b6e0c101d2fe9e8e8ed902e651e0519d143e5"
- dependencies:
- ansi-escapes "^1.1.0"
- ansi-regex "^2.0.0"
- chalk "^1.0.0"
- cli-cursor "^1.0.1"
- cli-width "^1.0.1"
- figures "^1.3.5"
- lodash "^3.3.1"
- readline2 "^1.0.1"
- run-async "^0.1.0"
- rx-lite "^3.1.2"
- strip-ansi "^3.0.0"
- through "^2.3.6"
-
-inquirer@0.8.2:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.8.2.tgz#41586548e1c5d9b3f81df7325034baacab6f58ab"
- dependencies:
- ansi-regex "^1.1.1"
- chalk "^1.0.0"
- cli-width "^1.0.1"
- figures "^1.3.5"
- lodash "^3.3.1"
- readline2 "^0.1.1"
- rx "^2.4.3"
- through "^2.3.6"
-
interpret@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
@@ -3998,14 +4000,14 @@ is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
-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"
-
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"
@@ -4146,7 +4148,7 @@ js-tokens@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
-js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@~3.7.0, js-yaml@3.x:
+js-yaml@3.x, js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@~3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
dependencies:
@@ -4420,7 +4422,7 @@ 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@^0.2.11, loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.7, loader-utils@~0.2.2, loader-utils@0.2.x:
+loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.7, loader-utils@~0.2.2:
version "0.2.16"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d"
dependencies:
@@ -4540,7 +4542,7 @@ lodash.cond@^4.3.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
-lodash.create@^3.1.1, lodash.create@3.1.1:
+lodash.create@3.1.1, lodash.create@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7"
dependencies:
@@ -4633,7 +4635,7 @@ lodash.pickby@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff"
-lodash.reduce@^4.4.0, lodash.reduce@4.6.0:
+lodash.reduce@4.6.0, lodash.reduce@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
@@ -4659,18 +4661,18 @@ lodash.words@^3.0.0:
dependencies:
lodash._root "^3.0.0"
-lodash@^3.3.1, lodash@3.10.1:
+lodash@3.10.1, lodash@^3.3.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
-lodash@^4.0.0, lodash@^4.1.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
- version "4.17.4"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-
lodash@3.9.3:
version "3.9.3"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.9.3.tgz#0159e86832feffc6d61d852b12a953b99496bd32"
+lodash@^4.0.0, lodash@^4.1.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
log4js@*:
version "1.1.0"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-1.1.0.tgz#c7d2b616d91bbf47cc65fb79d6fe04581c8096fa"
@@ -4693,7 +4695,7 @@ 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@^2.5.0, lru-cache@2:
+lru-cache@2, lru-cache@^2.5.0:
version "2.7.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
@@ -4774,7 +4776,7 @@ metascraper@^1.0.6:
popsicle "^6.2.0"
to-title-case "^1.0.0"
-methods@^1.1.1, methods@^1.1.2, methods@~1.1.2, methods@1.x:
+methods@1.x, methods@^1.1.1, methods@^1.1.2, methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -4823,7 +4825,7 @@ mime-types@~2.0.3:
dependencies:
mime-db "~1.12.0"
-mime@^1.3.4, mime@1.3.4:
+mime@1.3.4, mime@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
@@ -4835,7 +4837,7 @@ minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
-minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, "minimatch@2 || 3":
+"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
dependencies:
@@ -4848,11 +4850,7 @@ minimatch@~0.2.11, minimatch@~0.2.14:
lru-cache "2"
sigmund "~1.0.0"
-minimist@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-
-minimist@~0.0.1, minimist@0.0.8:
+minimist@0.0.8, minimist@~0.0.1:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -4860,11 +4858,9 @@ minimist@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.0.tgz#cdf225e8898f840a258ded44fc91776770afdc93"
-mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.1, mkdirp@0.5.x:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
- dependencies:
- minimist "0.0.8"
+minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
mkdirp@0.3.0:
version "0.3.0"
@@ -4876,6 +4872,12 @@ mkdirp@0.5.0:
dependencies:
minimist "0.0.8"
+mkdirp@0.5.1, mkdirp@0.5.x, "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"
+
mkpath@>=0.1.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-1.0.0.tgz#ebb3a977e7af1c683ae6fda12b545a6ba6c5853d"
@@ -4920,10 +4922,6 @@ mocha@^3.1.2:
mkdirp "0.5.1"
supports-color "3.1.2"
-moment@^2.10.3, moment@2.x.x:
- version "2.17.1"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82"
-
moment@2.12.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.12.0.tgz#dc2560d19838d6c0731b1a6afa04675264d360d6"
@@ -4932,6 +4930,10 @@ moment@2.17.0:
version "2.17.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.0.tgz#a4c292e02aac5ddefb29a6eed24f51938dd3b74f"
+moment@2.x.x, moment@^2.10.3:
+ version "2.17.1"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82"
+
mongodb-core@2.1.7:
version "2.1.7"
resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.7.tgz#6a27909b98142ef2508d924c274969008954fa29"
@@ -4991,14 +4993,14 @@ mquery@2.2.1:
regexp-clone "0.0.1"
sliced "0.0.5"
-ms@^0.7.1, ms@0.7.2:
- version "0.7.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
-
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
+ms@0.7.2, ms@^0.7.1:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
+
muri@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/muri/-/muri-1.2.0.tgz#b86383c902920b09ebe62af0e75c94de5f33cd3d"
@@ -5015,7 +5017,7 @@ mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
-nan@2.5.0:
+nan@2.5.0, nan@^2.3.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.0.tgz#aa8f1e34531d807e9e27755b234b4a6ec0c152a8"
@@ -5212,15 +5214,15 @@ nodemon@^1.11.0:
undefsafe "0.0.3"
update-notifier "0.5.0"
-nopt@~1.0.10:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
+nopt@3.x, nopt@~3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
dependencies:
abbrev "1"
-nopt@~3.0.6, nopt@3.x:
- version "3.0.6"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+nopt@~1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
dependencies:
abbrev "1"
@@ -5366,7 +5368,7 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
-optimist@^0.6.1, optimist@>=0.3.5, optimist@0.6.1:
+optimist@0.6.1, optimist@>=0.3.5, optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
dependencies:
@@ -5542,16 +5544,16 @@ 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-to-regexp@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
dependencies:
isarray "0.0.1"
-path-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"
@@ -5857,33 +5859,33 @@ postcss-mixins@^2.1.0:
postcss "^5.0.10"
postcss-simple-vars "^1.0.1"
-postcss-modules-extract-imports@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
- dependencies:
- postcss "^5.0.4"
-
postcss-modules-extract-imports@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.0.tgz#5b07f368e350cda6fd5c8844b79123a7bd3e37be"
dependencies:
postcss "^5.0.4"
-postcss-modules-local-by-default@^1.0.1, postcss-modules-local-by-default@1.1.1:
+postcss-modules-extract-imports@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-modules-local-by-default@1.1.1, postcss-modules-local-by-default@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce"
dependencies:
css-selector-tokenizer "^0.6.0"
postcss "^5.0.4"
-postcss-modules-scope@^1.0.0, postcss-modules-scope@1.0.2:
+postcss-modules-scope@1.0.2, postcss-modules-scope@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29"
dependencies:
css-selector-tokenizer "^0.6.0"
postcss "^5.0.4"
-postcss-modules-values@^1.1.0, postcss-modules-values@1.2.2:
+postcss-modules-values@1.2.2, postcss-modules-values@^1.1.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1"
dependencies:
@@ -6049,6 +6051,14 @@ postcss-zindex@^2.0.1:
postcss "^5.0.4"
uniqs "^2.0.0"
+postcss@5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.1.2.tgz#bd84886a66bcad489afaf7c673eed5ef639551e2"
+ dependencies:
+ js-base64 "^2.1.9"
+ source-map "^0.5.6"
+ supports-color "^3.1.2"
+
postcss@^5.0.0, postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.19, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.1.2, postcss@^5.2.11, postcss@^5.2.4, postcss@^5.2.5, postcss@^5.2.9:
version "5.2.11"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.11.tgz#ff29bcd6d2efb98bfe08a022055ec599bbe7b761"
@@ -6058,14 +6068,6 @@ postcss@^5.0.0, postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.
source-map "^0.5.6"
supports-color "^3.2.3"
-postcss@5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.1.2.tgz#bd84886a66bcad489afaf7c673eed5ef639551e2"
- dependencies:
- js-base64 "^2.1.9"
- source-map "^0.5.6"
- supports-color "^3.1.2"
-
pre-git@^3.10.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/pre-git/-/pre-git-3.12.0.tgz#8291899a15ba86f9cdaa8ca180e47096deff8dc3"
@@ -6130,7 +6132,7 @@ process@^0.11.0:
version "0.11.9"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1"
-progress@^1.1.8, progress@1.1.8:
+progress@1.1.8, progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
@@ -6283,22 +6285,18 @@ pug@^2.0.0-beta3:
pug-runtime "^2.0.3"
pug-strip-comments "^1.0.2"
-punycode@^1.2.4, punycode@^1.4.1, punycode@1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
-
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+punycode@1.4.1, punycode@^1.2.4, punycode@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
pym.js@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/pym.js/-/pym.js-1.1.1.tgz#3b5eb9e8499e5c95e5cb8fe29888ea7edf10a507"
-q@^1.1.2:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
-
q@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/q/-/q-1.1.2.tgz#6357e291206701d99f197ab84e57e8ad196f2a89"
@@ -6311,7 +6309,15 @@ q@2.0.3:
pop-iterate "^1.0.1"
weak-map "^1.0.5"
-qs@^6.1.0, qs@^6.2.0, qs@6.2.1:
+q@^1.1.2:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
+
+qs@6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b"
+
+qs@6.2.1, qs@^6.1.0, qs@^6.2.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625"
@@ -6319,10 +6325,6 @@ qs@~6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442"
-qs@6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b"
-
query-string@^4.1.0, query-string@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.1.tgz#54baada6713eafc92be75c47a731f2ebd09cd11d"
@@ -6342,14 +6344,14 @@ quote@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/quote/-/quote-0.4.0.tgz#10839217f6c1362b89194044d29b233fd7f32f01"
-ramda@^0.22.1:
- version "0.22.1"
- resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.22.1.tgz#031da0c3df417c5b33c96234757eb37033f36a0e"
-
ramda@0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.9.1.tgz#cc914dc3a82c608d003090203787c3f6826c1d87"
+ramda@^0.22.1:
+ version "0.22.1"
+ resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.22.1.tgz#031da0c3df417c5b33c96234757eb37033f36a0e"
+
random-bytes@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
@@ -6405,7 +6407,7 @@ react-apollo@^0.10.0:
optionalDependencies:
react-dom "0.14.x || 15.* || ^15.0.0"
-react-dom@^15.3.1, "react-dom@0.14.x || 15.* || ^15.0.0", react-dom@15.3.2:
+"react-dom@0.14.x || 15.* || ^15.0.0", react-dom@15.3.2, react-dom@^15.3.1:
version "15.3.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f"
@@ -6467,7 +6469,7 @@ react-tagsinput@^3.14.0:
version "3.14.0"
resolved "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.14.0.tgz#6729f8d5b313ed8fbb35496205ec2a97654af6bc"
-react@^15.3.1, react@15.3.2:
+react@15.3.2, react@^15.3.1:
version "15.3.2"
resolved "https://registry.yarnpkg.com/react/-/react-15.3.2.tgz#a7bccd2fee8af126b0317e222c28d1d54528d09e"
dependencies:
@@ -6503,7 +6505,7 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
-readable-stream@^1.1.7, readable-stream@1.1, readable-stream@1.1.x:
+readable-stream@1.1, readable-stream@1.1.x, readable-stream@^1.1.7:
version "1.1.13"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e"
dependencies:
@@ -6512,7 +6514,7 @@ readable-stream@^1.1.7, readable-stream@1.1, readable-stream@1.1.x:
isarray "0.0.1"
string_decoder "~0.10.x"
-readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.2.2, readable-stream@2:
+readable-stream@2, readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e"
dependencies:
@@ -6524,7 +6526,7 @@ readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
-readable-stream@^2.0.5, readable-stream@~2.1.4, readable-stream@2.1.5:
+readable-stream@2.1.5, readable-stream@^2.0.5, readable-stream@~2.1.4:
version "2.1.5"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0"
dependencies:
@@ -6560,15 +6562,6 @@ readline2@^1.0.1:
is-fullwidth-code-point "^1.0.0"
mute-stream "0.0.5"
-recast@^0.11.17:
- version "0.11.20"
- resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.20.tgz#2cb9bec269c03b36d0598118a936cd0a293ca3f3"
- dependencies:
- ast-types "0.9.4"
- esprima "~3.1.0"
- private "~0.1.5"
- source-map "~0.5.0"
-
recast@0.10.33:
version "0.10.33"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697"
@@ -6578,6 +6571,15 @@ recast@0.10.33:
private "~0.1.5"
source-map "~0.5.0"
+recast@^0.11.17:
+ version "0.11.20"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.20.tgz#2cb9bec269c03b36d0598118a936cd0a293ca3f3"
+ dependencies:
+ ast-types "0.9.4"
+ esprima "~3.1.0"
+ private "~0.1.5"
+ source-map "~0.5.0"
+
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
@@ -6741,7 +6743,7 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
-request@^2.55.0, request@^2.74.0, request@^2.79.0, request@2.79.0:
+request@2.79.0, request@^2.55.0, request@^2.74.0, request@^2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
dependencies:
@@ -6766,13 +6768,6 @@ request@^2.55.0, request@^2.74.0, request@^2.79.0, request@2.79.0:
tunnel-agent "~0.4.1"
uuid "^3.0.0"
-require_optional@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.0.tgz#52a86137a849728eb60a55533617f8f914f59abf"
- dependencies:
- resolve-from "^2.0.0"
- semver "^5.1.0"
-
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -6796,6 +6791,13 @@ require-uncached@^1.0.2:
caller-path "^0.1.0"
resolve-from "^1.0.0"
+require_optional@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.0.tgz#52a86137a849728eb60a55533617f8f914f59abf"
+ dependencies:
+ resolve-from "^2.0.0"
+ semver "^5.1.0"
+
resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
@@ -6832,7 +6834,7 @@ right-align@^0.1.1:
dependencies:
align-text "^0.1.1"
-rimraf@^2.2.8, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2:
+rimraf@2, rimraf@^2.2.8, rimraf@^2.4.3, rimraf@^2.4.4, 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:
@@ -6874,14 +6876,14 @@ safe-buffer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
-sax@^1.1.4, sax@~1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
-
sax@0.5.x:
version "0.5.8"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
+sax@^1.1.4, sax@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
+
selenium-standalone@^5.11.2:
version "5.11.2"
resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-5.11.2.tgz#724ccaa72fb26f3711e0e20989e478c4133df844"
@@ -6908,18 +6910,18 @@ semver-regex@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-1.0.0.tgz#92a4969065f9c70c694753d55248fc68f8f652c9"
-semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5":
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
-semver@~5.0.1:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
-
semver@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.0.tgz#85f2cf8550465c4df000cf7d86f6b054106ab9e5"
+semver@~5.0.1:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
+
send@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/send/-/send-0.14.2.tgz#39b0438b3f510be5dc6f667a11f71689368cdeef"
@@ -6969,6 +6971,10 @@ sha.js@^2.3.6:
dependencies:
inherits "^2.0.1"
+shelljs@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.0.tgz#ce1ed837b4b0e55b5ec3dab84251ab9dbdc0c7ec"
+
shelljs@^0.7.0, shelljs@^0.7.5:
version "0.7.6"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad"
@@ -6977,10 +6983,6 @@ shelljs@^0.7.0, shelljs@^0.7.5:
interpret "^1.0.0"
rechoir "^0.6.2"
-shelljs@0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.0.tgz#ce1ed837b4b0e55b5ec3dab84251ab9dbdc0c7ec"
-
sigmund@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
@@ -7055,7 +7057,7 @@ socks-proxy-agent@2:
extend "3"
socks "~1.1.5"
-socks@~1.1.5, socks@1.1.9:
+socks@1.1.9, socks@~1.1.5:
version "1.1.9"
resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691"
dependencies:
@@ -7078,7 +7080,13 @@ source-map-support@^0.4.2:
dependencies:
source-map "^0.5.3"
-source-map@^0.4.4, source-map@0.4.x:
+source-map@0.1.x:
+ version "0.1.43"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
+ dependencies:
+ amdefine ">=0.0.4"
+
+source-map@0.4.x, source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
@@ -7094,12 +7102,6 @@ source-map@~0.2.0:
dependencies:
amdefine ">=0.0.4"
-source-map@0.1.x:
- version "0.1.43"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
- 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"
@@ -7200,10 +7202,6 @@ strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
-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-hash@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.1.tgz#8e85bed291e0763b8f6809d9c3368fea048db3dc"
@@ -7229,6 +7227,10 @@ string-width@^2.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^3.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"
+
stringmap@~0.2.2:
version "0.2.2"
resolved "http://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1"
@@ -7277,7 +7279,7 @@ style-loader@^0.13.1:
dependencies:
loader-utils "^0.2.7"
-stylus@~0.54.5, stylus@0.54.5:
+stylus@0.54.5, stylus@~0.54.5:
version "0.54.5"
resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79"
dependencies:
@@ -7316,6 +7318,16 @@ supertest@^2.0.1:
methods "1.x"
superagent "^2.0.0"
+supports-color@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e"
+
+supports-color@3.1.2, supports-color@^3.1.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
+ dependencies:
+ has-flag "^1.0.0"
+
supports-color@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"
@@ -7324,22 +7336,12 @@ 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.0, supports-color@3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
- dependencies:
- has-flag "^1.0.0"
-
supports-color@^3.1.2, supports-color@^3.2.3:
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@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e"
-
svgo@^0.7.0:
version "0.7.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
@@ -7427,7 +7429,7 @@ text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
-through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.8, through@2:
+through@2, through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -7564,14 +7566,14 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
-type-detect@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
-
type-detect@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822"
+type-detect@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
+
type-is@~1.6.14:
version "1.6.14"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2"
@@ -7608,7 +7610,7 @@ uid-number@~0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
-uid-safe@~2.1.3, uid-safe@2.1.3:
+uid-safe@2.1.3, uid-safe@~2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.3.tgz#077e264a00b3187936b270bb7376a26473631071"
dependencies:
@@ -7645,7 +7647,7 @@ uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
-unpipe@~1.0.0, unpipe@1.0.0:
+unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@@ -7682,7 +7684,7 @@ 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:
+util@0.10.3, util@^0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
@@ -7802,7 +7804,7 @@ whatwg-encoding@^1.0.1:
dependencies:
iconv-lite "0.4.13"
-whatwg-fetch@^2.0.0, whatwg-fetch@>=0.10.0:
+whatwg-fetch@>=0.10.0, whatwg-fetch@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e"
@@ -7827,24 +7829,28 @@ which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
-which@^1.1.1:
- version "1.2.12"
- resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192"
- dependencies:
- isexe "^1.1.1"
-
which@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.1.1.tgz#9ce512459946166e12c083f08ec073380fc8cbbb"
dependencies:
is-absolute "^0.1.7"
+which@^1.1.1:
+ version "1.2.12"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192"
+ dependencies:
+ isexe "^1.1.1"
+
wide-align@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad"
dependencies:
string-width "^1.0.1"
+window-size@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+
window-size@^0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
@@ -7853,10 +7859,6 @@ window-size@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
-window-size@0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
-
with@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe"
@@ -7864,10 +7866,14 @@ with@^5.0.0:
acorn "^3.1.0"
acorn-globals "^3.0.0"
-word-wrap@^1.0.3, word-wrap@1.1.0:
+word-wrap@1.1.0, word-wrap@^1.0.3:
version "1.1.0"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.1.0.tgz#356153d61d10610d600785c5d701288e0ae764a6"
+wordwrap@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
+
wordwrap@^1.0.0, wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
@@ -7876,10 +7882,6 @@ wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
-wordwrap@0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
-
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
@@ -8037,4 +8039,3 @@ yauzl@^2.5.0:
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.0.1"
-
From d5b46f219ed81cd1fa05c0eec0dd676de37c69aa Mon Sep 17 00:00:00 2001
From: David Jay
Date: Tue, 7 Mar 2017 11:54:44 -0500
Subject: [PATCH 02/56] Adding load more button.
---
.../ModerationQueue/ModerationContainer.js | 16 ++++++--
.../ModerationQueue/ModerationQueue.js | 12 +++++-
.../components/ModerationMenu.js | 15 ++------
.../coral-admin/src/graphql/queries/index.js | 38 ++++++++++++++++++-
.../src/graphql/queries/loadMore.graphql | 13 +++++++
5 files changed, 76 insertions(+), 18 deletions(-)
create mode 100644 client/coral-admin/src/graphql/queries/loadMore.graphql
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index 25a7a9511..99a1ec656 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -21,7 +21,8 @@ import ModerationKeysModal from '../../components/ModerationKeysModal';
class ModerationContainer extends Component {
state = {
- selectedIndex: 0
+ selectedIndex: 0,
+ sort: 'REVERSE_CHRONOLOGICAL'
}
componentWillMount() {
@@ -53,6 +54,11 @@ class ModerationContainer extends Component {
}
+ selectSort = (sort) => {
+ this.setState({sort});
+ this.props.modQueueResort(sort);
+ }
+
componentWillUnmount() {
key.unbind('s');
key.unbind('shift+/');
@@ -71,7 +77,7 @@ class ModerationContainer extends Component {
}
render () {
- const {data, moderation, settings, assets, modQueueResort, onClose, ...props} = this.props;
+ const {data, moderation, settings, assets, onClose, ...props} = this.props;
const providedAssetId = this.props.params.id;
const activeTab = this.props.route.path === ':id' ? 'premod' : this.props.route.path;
@@ -103,7 +109,8 @@ class ModerationContainer extends Component {
premodCount={data.premodCount}
rejectedCount={data.rejectedCount}
flaggedCount={data.flaggedCount}
- modQueueResort={modQueueResort}
+ selectSort={this.selectSort}
+ sort={this.state.sort}
/>
{
+const ModerationQueue = ({comments, selectedIndex, singleView, loadMore, activeTab, sort, ...props}) => {
return (
@@ -20,7 +20,7 @@ const ModerationQueue = ({comments, selectedIndex, singleView, ...props}) => {
key={i}
index={i}
comment={comment}
- commentType={props.activeTab}
+ commentType={activeTab}
selected={i === selectedIndex}
suspectWords={props.suspectWords}
actions={actionsMap[status]}
@@ -33,6 +33,14 @@ const ModerationQueue = ({comments, selectedIndex, singleView, ...props}) => {
: {lang.t('modqueue.emptyqueue')}
}
+
+ loadMore({
+ cursor: comments[comments.length - 1].created_at,
+ sort,
+ tab: activeTab,
+ asset_id: props.assetId
+ })}
+ >Load More
);
};
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
index f9b0c266f..8d6a0e883 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
@@ -9,10 +9,6 @@ import {Link} from 'react-router';
const lang = new I18n(translations);
class ModerationMenu extends Component {
- state = {
- sort: 'REVERSE_CHRONOLOGICAL',
- }
-
static propTypes = {
premodCount: PropTypes.number.isRequired,
rejectedCount: PropTypes.number.isRequired,
@@ -22,13 +18,8 @@ class ModerationMenu extends Component {
})
}
- selectSort = (sort) => {
- this.setState({sort});
- this.props.modQueueResort(sort);
- }
-
render() {
- const {asset, premodCount, rejectedCount, flaggedCount} = this.props;
+ const {asset, premodCount, rejectedCount, flaggedCount, selectSort, sort} = this.props;
const premodPath = asset ? `/admin/moderate/premod/${asset.id}` : '/admin/moderate/premod';
const rejectPath = asset ? `/admin/moderate/rejected/${asset.id}` : '/admin/moderate/rejected';
const flagPath = asset ? `/admin/moderate/flagged/${asset.id}` : '/admin/moderate/flagged';
@@ -50,8 +41,8 @@ class ModerationMenu extends Component {
this.selectSort(sort)}>
+ value={sort}
+ onChange={sort => selectSort(sort)}>
Newest First
Oldest First
diff --git a/client/coral-admin/src/graphql/queries/index.js b/client/coral-admin/src/graphql/queries/index.js
index c4a30ad44..13164bca2 100644
--- a/client/coral-admin/src/graphql/queries/index.js
+++ b/client/coral-admin/src/graphql/queries/index.js
@@ -1,6 +1,7 @@
import {graphql} from 'react-apollo';
import MOD_QUEUE_QUERY from './modQueueQuery.graphql';
+import MOD_QUEUE_LOAD_MORE from './loadMore.graphql';
import METRICS from './metricsQuery.graphql';
export const modQueueQuery = graphql(MOD_QUEUE_QUERY, {
@@ -14,7 +15,8 @@ export const modQueueQuery = graphql(MOD_QUEUE_QUERY, {
},
props: ({ownProps: {params: {id = null}}, data}) => ({
data,
- modQueueResort: modQueueResort(id, data.fetchMore)
+ modQueueResort: modQueueResort(id, data.fetchMore),
+ loadMore: loadMore(data.fetchMore)
})
});
@@ -30,6 +32,40 @@ export const getMetrics = graphql(METRICS, {
}
});
+export const loadMore = (fetchMore) => ({limit, cursor, sort, tab, asset_id}) => {
+ let statuses;
+ switch(tab) {
+ case 'premod':
+ statuses = ['PREMOD'];
+ break;
+ case 'flagged':
+ statuses = ['NONE', 'PREMOD'];
+ break;
+ case 'rejected':
+ statuses = ['REJECTED'];
+ break;
+ }
+ return fetchMore({
+ query: MOD_QUEUE_LOAD_MORE,
+ variables: {
+ limit,
+ cursor,
+ sort,
+ statuses,
+ asset_id
+ },
+ updateQuery: (oldData, {fetchMoreResult:{data:{comments}}}) => {
+ return {
+ ...oldData,
+ [tab]: [
+ ...oldData[tab],
+ ...comments
+ ]
+ };
+ }
+ });
+};
+
export const modQueueResort = (id, fetchMore) => (sort) => {
return fetchMore({
query: MOD_QUEUE_QUERY,
diff --git a/client/coral-admin/src/graphql/queries/loadMore.graphql b/client/coral-admin/src/graphql/queries/loadMore.graphql
new file mode 100644
index 000000000..56966a804
--- /dev/null
+++ b/client/coral-admin/src/graphql/queries/loadMore.graphql
@@ -0,0 +1,13 @@
+#import "../fragments/commentView.graphql"
+
+query LoadMoreModQueue($limit: Int = 10, $cursor: Date, $sort: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!]) {
+ comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sort: $sort}) {
+ ...commentView
+ action_summaries {
+ count
+ ... on FlagActionSummary {
+ reason
+ }
+ }
+ }
+}
From 54aa3bc8fa4a0e3fb0c67a16704409b22c7ba55f Mon Sep 17 00:00:00 2001
From: David Jay
Date: Tue, 7 Mar 2017 12:31:43 -0500
Subject: [PATCH 03/56] Styling load more button.
---
.../ModerationQueue/ModerationContainer.js | 13 +++++++++++
.../ModerationQueue/ModerationQueue.js | 19 ++++++++--------
.../ModerationQueue/components/LoadMore.js | 22 +++++++++++++++++++
.../ModerationQueue/components/styles.css | 20 +++++++++++++++++
4 files changed, 65 insertions(+), 9 deletions(-)
create mode 100644 client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index 99a1ec656..16de8a32e 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -100,6 +100,18 @@ class ModerationContainer extends Component {
}
const comments = data[activeTab];
+ let activeTabCount;
+ switch(activeTab) {
+ case 'premod':
+ activeTabCount = data.premodCount;
+ break;
+ case 'flagged':
+ activeTabCount = data.flaggedCount;
+ break;
+ case 'rejected':
+ activeTabCount = data.rejectedCount;
+ break;
+ }
return (
@@ -125,6 +137,7 @@ class ModerationContainer extends Component {
loadMore={props.loadMore}
assetId={providedAssetId}
sort={this.state.sort}
+ commentCount={activeTabCount}
/>
{
+const ModerationQueue = ({comments, selectedIndex, commentCount, singleView, loadMore, activeTab, sort, ...props}) => {
return (
@@ -33,14 +34,14 @@ const ModerationQueue = ({comments, selectedIndex, singleView, loadMore, activeT
: {lang.t('modqueue.emptyqueue')}
}
-
- loadMore({
- cursor: comments[comments.length - 1].created_at,
- sort,
- tab: activeTab,
- asset_id: props.assetId
- })}
- >Load More
+
);
};
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
new file mode 100644
index 000000000..5ac193525
--- /dev/null
+++ b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
@@ -0,0 +1,22 @@
+import React from 'react';
+import {Button} from 'coral-ui';
+import styles from './styles.css';
+
+const LoadMore = ({comments, loadMore, sort, tab, assetId, showLoadMore}) =>
+
+ {
+ showLoadMore &&
+ loadMore({
+ cursor: comments[comments.length - 1].created_at,
+ sort,
+ tab,
+ asset_id: assetId
+ })}>
+ Load More
+
+ }
+
;
+
+export default LoadMore;
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/styles.css b/client/coral-admin/src/containers/ModerationQueue/components/styles.css
index 59f47faad..5abbb6b7d 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/styles.css
+++ b/client/coral-admin/src/containers/ModerationQueue/components/styles.css
@@ -387,3 +387,23 @@ span {
cursor: pointer;
}
}
+
+.loadMoreContainer {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+};
+
+.loadMore {
+ width: 100%;
+ text-align: center;
+ color: #FFF;
+ max-width: 660px;
+ margin-bottom: 30px;
+ background-color: #2376D8;
+ cursor: pointer;
+}
+
+.loadMore:hover {
+ background-color: #4399FF;
+}
From 2a6236b61823525d181bbd4c97c25467d5cd7f35 Mon Sep 17 00:00:00 2001
From: David Jay
Date: Tue, 7 Mar 2017 12:37:12 -0500
Subject: [PATCH 04/56] Moving ModerationMenu to a stateless compenent.
---
.../components/ModerationMenu.js | 81 +++++++++----------
1 file changed, 39 insertions(+), 42 deletions(-)
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
index 8d6a0e883..b8160e21b 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
@@ -1,4 +1,4 @@
-import React, {PropTypes, Component} from 'react';
+import React, {PropTypes} from 'react';
import CommentCount from './CommentCount';
import styles from './styles.css';
import {SelectField, Option} from 'react-mdl-selectfield';
@@ -8,48 +8,45 @@ import {Link} from 'react-router';
const lang = new I18n(translations);
-class ModerationMenu extends Component {
- static propTypes = {
- premodCount: PropTypes.number.isRequired,
- rejectedCount: PropTypes.number.isRequired,
- flaggedCount: PropTypes.number.isRequired,
- asset: PropTypes.shape({
- id: PropTypes.string
- })
- }
-
- render() {
- const {asset, premodCount, rejectedCount, flaggedCount, selectSort, sort} = this.props;
- const premodPath = asset ? `/admin/moderate/premod/${asset.id}` : '/admin/moderate/premod';
- const rejectPath = asset ? `/admin/moderate/rejected/${asset.id}` : '/admin/moderate/rejected';
- const flagPath = asset ? `/admin/moderate/flagged/${asset.id}` : '/admin/moderate/flagged';
- return (
-
-
-
-
-
- {lang.t('modqueue.premod')}
-
-
- {lang.t('modqueue.rejected')}
-
-
- {lang.t('modqueue.flagged')}
-
-
-
selectSort(sort)}>
- Newest First
- Oldest First
-
+const ModerationMenu = ({asset, premodCount, rejectedCount, flaggedCount, selectSort, sort}) => {
+ const premodPath = asset ? `/admin/moderate/premod/${asset.id}` : '/admin/moderate/premod';
+ const rejectPath = asset ? `/admin/moderate/rejected/${asset.id}` : '/admin/moderate/rejected';
+ const flagPath = asset ? `/admin/moderate/flagged/${asset.id}` : '/admin/moderate/flagged';
+ return (
+
+
+
+
+
+ {lang.t('modqueue.premod')}
+
+
+ {lang.t('modqueue.rejected')}
+
+
+ {lang.t('modqueue.flagged')}
+
+
selectSort(sort)}>
+ Newest First
+ Oldest First
+
- );
- }
-}
+
+ );
+};
+
+ModerationMenu.propTypes = {
+ premodCount: PropTypes.number.isRequired,
+ rejectedCount: PropTypes.number.isRequired,
+ flaggedCount: PropTypes.number.isRequired,
+ asset: PropTypes.shape({
+ id: PropTypes.string
+ })
+};
export default ModerationMenu;
From 95aa89faed81ed5b1530d19f5ffb1a7acae877ec Mon Sep 17 00:00:00 2001
From: David Jay
Date: Tue, 7 Mar 2017 14:16:44 -0500
Subject: [PATCH 05/56] Switching premod status update to updateQueries
function.
---
.../src/graphql/mutations/index.js | 43 ++++++++++++++++++-
1 file changed, 41 insertions(+), 2 deletions(-)
diff --git a/client/coral-admin/src/graphql/mutations/index.js b/client/coral-admin/src/graphql/mutations/index.js
index fe3a1faf9..884e30e0c 100644
--- a/client/coral-admin/src/graphql/mutations/index.js
+++ b/client/coral-admin/src/graphql/mutations/index.js
@@ -22,7 +22,26 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, {
commentId,
status: 'ACCEPTED'
},
- refetchQueries: ['ModQueue']
+ updateQueries: {
+ ModQueue: (oldData) => {
+ const premod = oldData.premod.filter(c => c.id !== commentId);
+ const flagged = oldData.flagged.filter(c => c.id !== commentId);
+ const rejected = oldData.rejected.filter(c => c.id !== commentId);
+ const premodCount = premod.length < oldData.premod.length ? oldData.premodCount - 1 : oldData.premodCount;
+ const flaggedCount = flagged.length < oldData.flagged.length ? oldData.flaggedCount - 1 : oldData.flaggedCount;
+ const rejectedCount = rejected.length < oldData.rejected.length ? oldData.rejectedCount - 1 : oldData.rejectedCount;
+
+ return {
+ ...oldData,
+ premodCount,
+ flaggedCount,
+ rejectedCount,
+ premod,
+ flagged,
+ rejected,
+ };
+ }
+ }
});
},
rejectComment: ({commentId}) => {
@@ -31,7 +50,27 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, {
commentId,
status: 'REJECTED'
},
- refetchQueries: ['ModQueue']
+ updateQueries: {
+ ModQueue: (oldData) => {
+ const comment = oldData.premod.concat(oldData.flagged).filter(c => c.id === commentId)[0];
+ const rejected = [comment].concat(oldData.rejected);
+ const premod = oldData.premod.filter(c => c.id !== commentId);
+ const flagged = oldData.flagged.filter(c => c.id !== commentId);
+ const premodCount = premod.length < oldData.premod.length ? oldData.premodCount - 1 : oldData.premodCount;
+ const flaggedCount = flagged.length < oldData.flagged.length ? oldData.flaggedCount - 1 : oldData.flaggedCount;
+ const rejectedCount = oldData.rejectedCount + 1;
+
+ return {
+ ...oldData,
+ premodCount,
+ flaggedCount,
+ rejectedCount,
+ premod,
+ flagged,
+ rejected
+ };
+ }
+ }
});
}
})
From e578f901b2c01cf463b4420a03f8334c32154c79 Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Tue, 7 Mar 2017 13:38:01 -0700
Subject: [PATCH 06/56] fix some firefox display things
---
.../coral-admin/src/containers/Configure/StreamSettings.js | 1 +
client/coral-admin/src/containers/Dashboard/FlagWidget.js | 3 ++-
client/coral-admin/src/containers/Dashboard/LikeWidget.js | 5 +++--
client/coral-admin/src/containers/Dashboard/Widget.css | 4 ++++
client/coral-admin/src/containers/Streams/Streams.js | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/client/coral-admin/src/containers/Configure/StreamSettings.js b/client/coral-admin/src/containers/Configure/StreamSettings.js
index 5530bc6cd..f77013bc0 100644
--- a/client/coral-admin/src/containers/Configure/StreamSettings.js
+++ b/client/coral-admin/src/containers/Configure/StreamSettings.js
@@ -171,6 +171,7 @@ const StreamSettings = ({updateSettings, settingsError, settings, errors}) => {
+ {/* the above card should be the last one if at all possible because of z-index issues with the selects */}
);
};
diff --git a/client/coral-admin/src/containers/Dashboard/FlagWidget.js b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
index da71ac04a..5ce93f11b 100644
--- a/client/coral-admin/src/containers/Dashboard/FlagWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
@@ -3,6 +3,7 @@ import {Link} from 'react-router';
import styles from './Widget.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
+import range from 'lodash/range';
const lang = new I18n(translations);
@@ -44,7 +45,7 @@ const FlagWidget = (props) => {
}
{ /* rows in a table with a fixed height will expand and ignore height.
this extra row will expand to fill the extra space. */
- assets.length < 10 ?
: null
+ range(10 - Math.max(assets.length, 1)).map(() =>
)
}
diff --git a/client/coral-admin/src/containers/Dashboard/LikeWidget.js b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
index 25e851966..39ee708f8 100644
--- a/client/coral-admin/src/containers/Dashboard/LikeWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
@@ -3,6 +3,7 @@ import {Link} from 'react-router';
import styles from './Widget.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
+import range from 'lodash/range';
const lang = new I18n(translations);
@@ -44,8 +45,8 @@ const LikeWidget = (props) => {
:
{lang.t('dashboard.no_likes')}
}
{ /* rows in a table with a fixed height will expand and ignore height.
- this extra row will expand to fill the extra space. */
- assets.length < 10 ?
: null
+ put in some extra rows. */
+ range(10 - Math.max(assets.length, 1)).map(() =>
)
}
diff --git a/client/coral-admin/src/containers/Dashboard/Widget.css b/client/coral-admin/src/containers/Dashboard/Widget.css
index cb764d9cc..96c773bef 100644
--- a/client/coral-admin/src/containers/Dashboard/Widget.css
+++ b/client/coral-admin/src/containers/Dashboard/Widget.css
@@ -43,6 +43,10 @@
height: var(--row-height);
}
+.emptyRow {
+ height: var(--row-height);
+}
+
.rowLinkify:last-child {
border-bottom: none;
}
diff --git a/client/coral-admin/src/containers/Streams/Streams.js b/client/coral-admin/src/containers/Streams/Streams.js
index 96130fcba..bd5ce6e85 100644
--- a/client/coral-admin/src/containers/Streams/Streams.js
+++ b/client/coral-admin/src/containers/Streams/Streams.js
@@ -80,8 +80,8 @@ class Streams extends Component {
- {closed ? lang.t('streams.closed') : lang.t('streams.open')}
{!statusMenuOpen && }
+ {closed ? lang.t('streams.closed') : lang.t('streams.open')}
{
statusMenuOpen &&
From 47f6f4cb6c1736f86105dee782c607df777f3fab Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Tue, 7 Mar 2017 16:51:22 -0700
Subject: [PATCH 07/56] styles and auto-update Dashboard
---
.../src/containers/Dashboard/Dashboard.css | 25 ++++++++++
.../src/containers/Dashboard/Dashboard.js | 47 +++++++++++++++++--
.../src/containers/Dashboard/Widget.css | 10 ++--
client/coral-admin/src/translations.json | 4 ++
4 files changed, 79 insertions(+), 7 deletions(-)
diff --git a/client/coral-admin/src/containers/Dashboard/Dashboard.css b/client/coral-admin/src/containers/Dashboard/Dashboard.css
index 72b9bd90c..1bd1599b4 100644
--- a/client/coral-admin/src/containers/Dashboard/Dashboard.css
+++ b/client/coral-admin/src/containers/Dashboard/Dashboard.css
@@ -7,3 +7,28 @@
font-size: 1.5rem;
font-weight: bold;
}
+
+.autoUpdate {
+ background-color: #ffdb87;
+ padding: 3px 10px 10px 10px;
+ margin-bottom: 0;
+
+ i {
+ position: relative;
+ top: 7px;
+ }
+
+ b {
+ float: right;
+ border-radius: 20px;
+ cursor: pointer;
+ background-color: #e6ba52;
+ width: 30px;
+ height: 30px;
+ text-align: center;
+ top: 4px;
+ position: relative;
+ line-height: 1.7em;
+ font-size: 1.3em;
+ }
+}
diff --git a/client/coral-admin/src/containers/Dashboard/Dashboard.js b/client/coral-admin/src/containers/Dashboard/Dashboard.js
index 96e837a9e..5b4a20282 100644
--- a/client/coral-admin/src/containers/Dashboard/Dashboard.js
+++ b/client/coral-admin/src/containers/Dashboard/Dashboard.js
@@ -6,11 +6,41 @@ import {getMetrics} from 'coral-admin/src/graphql/queries';
import FlagWidget from './FlagWidget';
import LikeWidget from './LikeWidget';
import {showBanUserDialog, hideBanUserDialog} from 'coral-admin/src/actions/moderation';
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from 'coral-admin/src/translations';
+import {Spinner, Icon} from 'coral-ui';
-import {Spinner} from 'coral-ui';
+const lang = new I18n(translations);
+const refreshIntervalSeconds = 60 * 5;
class Dashboard extends React.Component {
+ state = {
+ noteHidden: false,
+ secondsUntilRefresh: refreshIntervalSeconds
+ }
+
+ componentWillMount () {
+ setInterval(() => { // the countdown timer
+ let nextCount = this.state.secondsUntilRefresh - 1;
+ if (nextCount < 0) {
+ nextCount = refreshIntervalSeconds;
+ this.props.data.refetch();
+ }
+ this.setState({secondsUntilRefresh: nextCount});
+ }, 1000);
+ }
+
+ formatTime = () => {
+ const minutes = Math.floor(this.state.secondsUntilRefresh / 60);
+ let seconds = (this.state.secondsUntilRefresh % 60).toString();
+ if (seconds.length < 2) {
+ seconds = `0${seconds}`;
+ }
+
+ return `${minutes}:${seconds}`;
+ }
+
render () {
if (this.props.data && this.props.data.loading) {
@@ -20,9 +50,18 @@ class Dashboard extends React.Component {
const {data: {assetsByLike, assetsByFlag}} = this.props;
return (
-
-
-
+
+
this.setState({noteHidden: true})}>
+ ×
+ {lang.t('dashboard.next-update', this.formatTime())} {lang.t('dashboard.auto-update')}
+
+
+
+
+
);
}
diff --git a/client/coral-admin/src/containers/Dashboard/Widget.css b/client/coral-admin/src/containers/Dashboard/Widget.css
index cb764d9cc..42e7e2056 100644
--- a/client/coral-admin/src/containers/Dashboard/Widget.css
+++ b/client/coral-admin/src/containers/Dashboard/Widget.css
@@ -3,12 +3,15 @@
}
.widget {
- box-sizing: border-box;
margin: 10px 5px 5px 5px;
box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.2);
- padding: 15px;
flex: 1;
background-color: white;
+ box-sizing: border-box;
+}
+
+.widget * {
+ box-sizing: border-box;
}
.heading {
@@ -16,12 +19,13 @@
padding-left: 10px;
font-size: 1.5rem;
font-weight: bold;
+ background-color: #e0e0e0;
}
.widgetTable {
- width: 100%;
border-collapse: collapse;
user-select: none;
+ margin: 5px 15px 15px 15px;
height: calc(var(--row-height) * 10);
}
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index ac9131685..0278d37da 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -117,6 +117,8 @@
"write_message": "Write a message"
},
"dashboard": {
+ "next-update": "{0} minutes until next update.",
+ "auto-update": "Data automatically updates every five minutes or when you Reload.",
"no_flags": "There have been no flags in the last 5 minutes! Hooray!",
"no_likes": "There have been no likes in the last 5 minutes. All quiet.",
"flags": "Flags",
@@ -235,6 +237,8 @@
"yes_ban_user": "Si, Suspendan el usuario"
},
"dashbord": {
+ "next-update": "",
+ "auto-update": "",
"no_flags": "¡Nadie ha marcado nada en los últimos 5 minutos! ¡Bravo!",
"no_likes": "A nadie le ha gustado algún comentario en los últimos 5 minutos. Todo tranquilo.",
"flags": "Marcados",
From ec634fa3c0f1f1ea18621694c190fb4ddc040527 Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Wed, 8 Mar 2017 11:12:44 -0700
Subject: [PATCH 08/56] add translations
---
client/coral-admin/src/translations.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index 0278d37da..257148837 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -237,8 +237,8 @@
"yes_ban_user": "Si, Suspendan el usuario"
},
"dashbord": {
- "next-update": "",
- "auto-update": "",
+ "next-update": "{0} minutos hasta la siguiente actualización.",
+ "auto-update": "Los datos se actualizan automaticamente cada 5 minutos o cuando Recargas.",
"no_flags": "¡Nadie ha marcado nada en los últimos 5 minutos! ¡Bravo!",
"no_likes": "A nadie le ha gustado algún comentario en los últimos 5 minutos. Todo tranquilo.",
"flags": "Marcados",
From 3262d136b730d7b046984396fbdbb4ea6c4ee51e Mon Sep 17 00:00:00 2001
From: David Jay
Date: Wed, 8 Mar 2017 15:44:19 -0500
Subject: [PATCH 09/56] Resetting report menu on close.
---
client/coral-plugin-flags/FlagButton.js | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/client/coral-plugin-flags/FlagButton.js b/client/coral-plugin-flags/FlagButton.js
index 537d899cd..45110f407 100644
--- a/client/coral-plugin-flags/FlagButton.js
+++ b/client/coral-plugin-flags/FlagButton.js
@@ -32,18 +32,30 @@ class FlagButton extends Component {
if (flagged) {
this.setState((prev) => prev.localPost ? {...prev, localPost: null, step: 0} : {...prev, localDelete: true});
deleteAction(localPost || flag.current_user.id);
+ } else if (this.state.showMenu){
+ this.closeMenu();
} else {
- this.setState({showMenu: !this.state.showMenu});
+ this.setState({showMenu: true});
}
}
+ closeMenu = () => {
+ this.setState({
+ showMenu: false,
+ itemType: '',
+ reason: '',
+ message: '',
+ step: 0
+ });
+ }
+
onPopupContinue = () => {
const {postFlag, postDontAgree, id, author_id} = this.props;
const {itemType, reason, step, posted, message} = this.state;
// Proceed to the next step or close the menu if we've reached the end
if (step + 1 >= this.props.getPopupMenu.length) {
- this.setState({showMenu: false});
+ this.closeMenu();
} else {
this.setState({step: step + 1});
}
@@ -114,7 +126,7 @@ class FlagButton extends Component {
}
handleClickOutside () {
- this.setState({showMenu: false});
+ this.closeMenu();
}
render () {
From e4cdc73bf82569a55f4100e5a825e05e79c8123d Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Wed, 8 Mar 2017 13:53:39 -0700
Subject: [PATCH 10/56] fix for react-mdn-selectfield in FF
---
client/coral-admin/src/containers/Configure/Configure.css | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/client/coral-admin/src/containers/Configure/Configure.css b/client/coral-admin/src/containers/Configure/Configure.css
index 6cb623978..b1b1ebbaf 100644
--- a/client/coral-admin/src/containers/Configure/Configure.css
+++ b/client/coral-admin/src/containers/Configure/Configure.css
@@ -90,6 +90,11 @@
.configTimeoutSelect {
display: inline-block;
margin-left: 20px;
+
+ i { /* fix for firefox and react-mdl-selectfield@0.2.0 */
+ padding: 20px 0;
+ vertical-align: top;
+ }
}
.charCountTexfield {
From d203a9afe0e7af60f47dd16e5e2c7123b489e30d Mon Sep 17 00:00:00 2001
From: David Jay
Date: Wed, 8 Mar 2017 16:06:34 -0500
Subject: [PATCH 11/56] Auto-rejecting comments on ban.
---
client/coral-admin/src/components/BanUserDialog.js | 10 ++++++++--
.../containers/ModerationQueue/ModerationContainer.js | 4 +++-
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/client/coral-admin/src/components/BanUserDialog.js b/client/coral-admin/src/components/BanUserDialog.js
index b23d8f9d1..f6a7227b8 100644
--- a/client/coral-admin/src/components/BanUserDialog.js
+++ b/client/coral-admin/src/components/BanUserDialog.js
@@ -8,7 +8,13 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
-const BanUserDialog = ({open, handleClose, handleBanUser, user}) => (
+const onBanClick = (userId, commentId, handleBanUser, rejectComment) => (e) => {
+ e.preventDefault();
+ handleBanUser({userId})
+ .then(() => rejectComment({commentId}));
+};
+
+const BanUserDialog = ({open, handleClose, handleBanUser, rejectComment, user, commentId}) => (
(
{lang.t('bandialog.cancel')}
- handleBanUser({userId: user.id})} raised>
+
{lang.t('bandialog.yes_ban_user')}
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index 2f43911b1..1d716ff41 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -26,7 +26,7 @@ class ModerationContainer extends Component {
componentWillMount() {
const {toggleModal, singleView} = this.props;
-
+
this.props.fetchSettings();
key('s', () => singleView());
key('shift+/', () => toggleModal(true));
@@ -140,8 +140,10 @@ class ModerationContainer extends Component {
Date: Wed, 8 Mar 2017 14:16:20 -0700
Subject: [PATCH 12/56] incorrectly using dispatch now that we do pym directly
---
client/coral-embed-stream/src/Embed.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js
index 1c52fb68c..0b9bc4fc5 100644
--- a/client/coral-embed-stream/src/Embed.js
+++ b/client/coral-embed-stream/src/Embed.js
@@ -256,7 +256,7 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
requestConfirmEmail: () => dispatch(requestConfirmEmail()),
loadAsset: (asset) => dispatch(fetchAssetSuccess(asset)),
- addNotification: (type, text) => dispatch(addNotification(type, text)),
+ addNotification: (type, text) => addNotification(type, text),
clearNotification: () => dispatch(clearNotification()),
editName: (username) => dispatch(editName(username)),
showSignInDialog: (offset) => dispatch(showSignInDialog(offset)),
From fac3d26c2278cbc92a009352ed9a295f097d3b83 Mon Sep 17 00:00:00 2001
From: David Jay
Date: Wed, 8 Mar 2017 16:39:53 -0500
Subject: [PATCH 13/56] Closing ban dialog after ban is complete.
---
client/coral-admin/src/components/BanUserDialog.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/client/coral-admin/src/components/BanUserDialog.js b/client/coral-admin/src/components/BanUserDialog.js
index f6a7227b8..4f2296409 100644
--- a/client/coral-admin/src/components/BanUserDialog.js
+++ b/client/coral-admin/src/components/BanUserDialog.js
@@ -8,9 +8,10 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
-const onBanClick = (userId, commentId, handleBanUser, rejectComment) => (e) => {
+const onBanClick = (userId, commentId, handleBanUser, rejectComment, handleClose) => (e) => {
e.preventDefault();
handleBanUser({userId})
+ .then(handleClose)
.then(() => rejectComment({commentId}));
};
@@ -35,7 +36,7 @@ const BanUserDialog = ({open, handleClose, handleBanUser, rejectComment, user, c
{lang.t('bandialog.cancel')}
-
+
{lang.t('bandialog.yes_ban_user')}
From eaf035acd6ae4ccd1f065acd223a4cb06abd02d5 Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Wed, 8 Mar 2017 14:51:50 -0700
Subject: [PATCH 14/56] simplify dashboard table rendering
---
.../src/containers/Dashboard/FlagWidget.js | 44 +++++++------------
.../src/containers/Dashboard/LikeWidget.js | 44 +++++++------------
.../src/containers/Dashboard/Widget.css | 25 +++++------
3 files changed, 44 insertions(+), 69 deletions(-)
diff --git a/client/coral-admin/src/containers/Dashboard/FlagWidget.js b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
index 5ce93f11b..48f39fc9e 100644
--- a/client/coral-admin/src/containers/Dashboard/FlagWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
@@ -13,42 +13,30 @@ const FlagWidget = (props) => {
return (
Articles with the most flags
-
-
-
- {lang.t('streams.article')}
- {lang.t('dashboard.flags')}
-
-
-
+
+
+
{lang.t('streams.article')}
+
{lang.t('dashboard.flags')}
+
+
{
assets.length
? assets.map(asset => {
const flagSummary = asset.action_summaries.find(s => s.type === 'FlagAssetActionSummary');
return (
-
-
-
- {asset.title}
- {asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
-
-
-
-
- {flagSummary ? flagSummary.actionCount : 0}
-
-
-
+
+
{flagSummary ? flagSummary.actionCount : 0}
+
+
{asset.title}
+
{asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
+
+
);
})
- :
{lang.t('dashboard.no_flags')}
+ :
{lang.t('dashboard.no_flags')}
}
- { /* rows in a table with a fixed height will expand and ignore height.
- this extra row will expand to fill the extra space. */
- range(10 - Math.max(assets.length, 1)).map(() =>
)
- }
-
-
+
+
);
};
diff --git a/client/coral-admin/src/containers/Dashboard/LikeWidget.js b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
index 39ee708f8..e627daece 100644
--- a/client/coral-admin/src/containers/Dashboard/LikeWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
@@ -14,42 +14,30 @@ const LikeWidget = (props) => {
return (
Articles with the most likes
-
-
-
- {lang.t('streams.article')}
- {lang.t('modqueue.likes')}
-
-
-
+
+
+
{lang.t('streams.article')}
+
{lang.t('modqueue.likes')}
+
+
{
assets.length
? assets.map(asset => {
const likeSummary = asset.action_summaries.find(s => s.type === 'LikeAssetActionSummary');
return (
-
-
-
- {asset.title}
- {asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
-
-
-
-
- {likeSummary ? likeSummary.actionCount : 0}
-
-
-
+
+
{likeSummary ? likeSummary.actionCount : 0}
+
+
{asset.title}
+
{asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
+
+
);
})
- :
{lang.t('dashboard.no_likes')}
+ :
{lang.t('dashboard.no_likes')}
}
- { /* rows in a table with a fixed height will expand and ignore height.
- put in some extra rows. */
- range(10 - Math.max(assets.length, 1)).map(() =>
)
- }
-
-
+
+
);
};
diff --git a/client/coral-admin/src/containers/Dashboard/Widget.css b/client/coral-admin/src/containers/Dashboard/Widget.css
index 96c773bef..16f86336b 100644
--- a/client/coral-admin/src/containers/Dashboard/Widget.css
+++ b/client/coral-admin/src/containers/Dashboard/Widget.css
@@ -1,5 +1,5 @@
:root {
- --row-height: 80px;
+ --row-height: 50px;
}
.widget {
@@ -19,20 +19,25 @@
}
.widgetTable {
- width: 100%;
- border-collapse: collapse;
user-select: none;
height: calc(var(--row-height) * 10);
}
-.widgetTable thead th {
+.widgetHead p {
color: rgb(35, 102, 223);
padding: 10px;
text-align: left;
text-transform: capitalize;
+ display: inline-block;
+ box-sizing: border-box;
+ margin-bottom: 0;
}
-.widgetTable thead th:last-child {
+.widgetHead p:first-child {
+ width: 90%;
+}
+
+.widgetHead p:last-child {
width: 10%;
}
@@ -41,10 +46,7 @@
border-bottom: 1px solid lightgrey;
color: #555;
height: var(--row-height);
-}
-
-.emptyRow {
- height: var(--row-height);
+ padding: 10px;
}
.rowLinkify:last-child {
@@ -55,10 +57,6 @@
background-color: #f8f8f8;
}
-.widgetTable tbody td {
- padding: 10px;
-}
-
.linkToAsset {
display: block;
text-decoration: none;
@@ -85,4 +83,5 @@
color: #555;
font-size: 1.3em;
font-weight: 400;
+ float: right;
}
From b34758e8cce417ac96d3e25d8cf9813a070e27ff Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Wed, 8 Mar 2017 14:59:55 -0700
Subject: [PATCH 15/56] unused vars
---
client/coral-admin/src/containers/Dashboard/FlagWidget.js | 1 -
client/coral-admin/src/containers/Dashboard/LikeWidget.js | 1 -
2 files changed, 2 deletions(-)
diff --git a/client/coral-admin/src/containers/Dashboard/FlagWidget.js b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
index 48f39fc9e..aab70ddaf 100644
--- a/client/coral-admin/src/containers/Dashboard/FlagWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
@@ -3,7 +3,6 @@ import {Link} from 'react-router';
import styles from './Widget.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
-import range from 'lodash/range';
const lang = new I18n(translations);
diff --git a/client/coral-admin/src/containers/Dashboard/LikeWidget.js b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
index e627daece..9c72be02e 100644
--- a/client/coral-admin/src/containers/Dashboard/LikeWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
@@ -3,7 +3,6 @@ import {Link} from 'react-router';
import styles from './Widget.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
-import range from 'lodash/range';
const lang = new I18n(translations);
From 377563c79882135a370ed869778768d0ee51754d Mon Sep 17 00:00:00 2001
From: David Jay
Date: Wed, 8 Mar 2017 17:02:47 -0500
Subject: [PATCH 16/56] Hiding rejected note on ban dialog for rejected
comments.
---
client/coral-admin/src/actions/moderation.js | 2 +-
client/coral-admin/src/components/BanUserDialog.js | 4 ++--
.../src/containers/ModerationQueue/ModerationContainer.js | 5 +++--
.../src/containers/ModerationQueue/components/Comment.js | 2 +-
client/coral-admin/src/reducers/moderation.js | 1 +
5 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/client/coral-admin/src/actions/moderation.js b/client/coral-admin/src/actions/moderation.js
index 5116700c7..75ec5fc09 100644
--- a/client/coral-admin/src/actions/moderation.js
+++ b/client/coral-admin/src/actions/moderation.js
@@ -4,5 +4,5 @@ export const toggleModal = open => ({type: actions.TOGGLE_MODAL, open});
export const singleView = () => ({type: actions.SINGLE_VIEW});
// Ban User Dialog
-export const showBanUserDialog = (user, commentId) => ({type: actions.SHOW_BANUSER_DIALOG, user, commentId});
+export const showBanUserDialog = (user, commentId, showRejectedNote) => ({type: actions.SHOW_BANUSER_DIALOG, user, commentId, showRejectedNote});
export const hideBanUserDialog = (showDialog) => ({type: actions.HIDE_BANUSER_DIALOG, showDialog});
diff --git a/client/coral-admin/src/components/BanUserDialog.js b/client/coral-admin/src/components/BanUserDialog.js
index b23d8f9d1..86d18d879 100644
--- a/client/coral-admin/src/components/BanUserDialog.js
+++ b/client/coral-admin/src/components/BanUserDialog.js
@@ -8,7 +8,7 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
-const BanUserDialog = ({open, handleClose, handleBanUser, user}) => (
+const BanUserDialog = ({open, handleClose, handleBanUser, user, showRejectedNote}) => (
(
{lang.t('bandialog.are_you_sure', user.name)}
- {lang.t('bandialog.note')}
+ {showRejectedNote && lang.t('bandialog.note')}
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index 2f43911b1..706070fbe 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -26,7 +26,7 @@ class ModerationContainer extends Component {
componentWillMount() {
const {toggleModal, singleView} = this.props;
-
+
this.props.fetchSettings();
key('s', () => singleView());
key('shift+/', () => toggleModal(true));
@@ -142,6 +142,7 @@ class ModerationContainer extends Component {
user={moderation.user}
handleClose={props.hideBanUserDialog}
handleBanUser={props.banUser}
+ showRejectedNote={moderation.showRejectedNote}
/>
({
singleView: () => dispatch(singleView()),
updateAssets: assets => dispatch(updateAssets(assets)),
fetchSettings: () => dispatch(fetchSettings()),
- showBanUserDialog: (user, commentId) => dispatch(showBanUserDialog(user, commentId)),
+ showBanUserDialog: (user, commentId, showRejectedNote) => dispatch(showBanUserDialog(user, commentId, showRejectedNote)),
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
});
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
index 8be1262f5..36ff7763a 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
@@ -31,7 +31,7 @@ const Comment = ({actions = [], ...props}) => {
{timeago().format(props.comment.created_at || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}
- props.showBanUserDialog(props.comment.user, props.comment.id)} />
+ props.showBanUserDialog(props.comment.user, props.comment.id, props.comment.status !== 'REJECTED')} />
{props.comment.user.status === 'banned' ?
diff --git a/client/coral-admin/src/reducers/moderation.js b/client/coral-admin/src/reducers/moderation.js
index 09a0b39e7..cf270543c 100644
--- a/client/coral-admin/src/reducers/moderation.js
+++ b/client/coral-admin/src/reducers/moderation.js
@@ -19,6 +19,7 @@ export default function moderation (state = initialState, action) {
.merge({
user: Map(action.user),
commentId: action.commentId,
+ showRejectedNote: action.showRejectedNote,
banDialog: true
});
case actions.SET_ACTIVE_TAB:
From feaad2d6ca14e3c353b820c7803bdd25c3e2ffea Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Wed, 8 Mar 2017 19:01:08 -0700
Subject: [PATCH 17/56] Added loader, graphql
---
graph/loaders/metrics.js | 49 ++++++++++++++++++++++++++++++++++-
graph/resolvers/asset.js | 6 ++++-
graph/resolvers/root_query.js | 4 +++
graph/typeDefs.graphql | 21 ++++++++++++---
4 files changed, 75 insertions(+), 5 deletions(-)
diff --git a/graph/loaders/metrics.js b/graph/loaders/metrics.js
index a6fe5afe4..d3c602de8 100644
--- a/graph/loaders/metrics.js
+++ b/graph/loaders/metrics.js
@@ -4,6 +4,52 @@ const {objectCacheKeyFn} = require('./util');
const ActionModel = require('../../models/action');
+/**
+ * Returns the assets which have had comments made within the last time period.
+ */
+const getCommentActivityMetrics = ({loaders: {Assets, Comments}}, {from, to, limit}) => {
+ let assetMetrics = [];
+
+ return Comments.aggregate([
+ {$match: {
+ parent_id: null,
+ created_at: {
+ $gt: from,
+ $lt: to
+ }
+ }},
+ {$group: {
+ _id: '$asset_id',
+ commentCount: {
+ $sum: 1
+ }
+ }},
+ {$project: {
+ _id: false,
+ asset_id: '$_id',
+ commentCount: '$commentCount'
+ }},
+ {$sort: {
+ commentCount: -1
+ }},
+ {$limit: limit}
+ ])
+ .then((results) => {
+ results = assetMetrics;
+
+ return Assets.getByID.loadMany(results.map((result) => result.asset_id));
+ })
+ .then((assets) => assets.map((asset, i) => {
+
+ // We're leveraging the fact that the comments returned by the aggregation
+ // query are in the request order that we just made, it's what the
+ // Assets.getByID loader does.
+ asset.commentCount = assetMetrics[i].commentCount;
+
+ return asset;
+ }));
+};
+
/**
* Returns a list of assets with action metadata included on the models.
*/
@@ -211,7 +257,8 @@ module.exports = (context) => ({
get: ({from, to, sort, limit}) => getAssetMetrics(context, {from, to, sort, limit})
},
Comments: {
- get: ({from, to, sort, limit}) => getCommentMetrics(context, {from, to, sort, limit})
+ get: ({from, to, sort, limit}) => getCommentMetrics(context, {from, to, sort, limit}),
+ getActivity: ({from, to, limit}) => getCommentActivityMetrics(context, {from, to, limit}),
}
}
});
diff --git a/graph/resolvers/asset.js b/graph/resolvers/asset.js
index 2077b9e7a..c0cffd1ae 100644
--- a/graph/resolvers/asset.js
+++ b/graph/resolvers/asset.js
@@ -10,7 +10,11 @@ const Asset = {
parent_id: null
});
},
- commentCount({id}, _, {loaders: {Comments}}) {
+ commentCount({id, commentCount}, _, {loaders: {Comments}}) {
+ if (commentCount) {
+ return commentCount;
+ }
+
return Comments.countByAssetID.load(id);
},
settings({settings = null}, _, {loaders: {Settings}}) {
diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js
index bdcdcef78..083526eff 100644
--- a/graph/resolvers/root_query.js
+++ b/graph/resolvers/root_query.js
@@ -71,6 +71,10 @@ const RootQuery = {
return null;
}
+ if (sort === 'COMMENTS') {
+ return Comments.getActivity({from, to, limit});
+ }
+
return Comments.get({from, to, sort, limit});
},
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 6cb4cb046..2821952e4 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -476,6 +476,21 @@ enum USER_STATUS {
APPROVED
}
+enum COMMENT_METRICS_SORT {
+
+ # Represents a LikeAction.
+ LIKE
+
+ # Represents a FlagAction.
+ FLAG
+
+ # Represents a don't agree action.
+ DONTAGREE
+
+ # Represents activity.
+ ACTIVITY
+}
+
type RootQuery {
# Site wide settings and defaults.
@@ -507,7 +522,7 @@ type RootQuery {
# Comment metrics related to user actions are saturated into the comments
# returned.
- commentMetrics(from: Date!, to: Date!, sort: ACTION_TYPE!, limit: Int = 10): [Comment!]
+ commentMetrics(from: Date!, to: Date!, sort: COMMENT_METRICS_SORT!, limit: Int = 10): [Comment!]
}
################################################################################
@@ -646,14 +661,14 @@ type SetCommentStatusResponse implements Response {
type AddCommentTagResponse implements Response {
# An array of errors relating to the mutation that occured.
comment: Comment
- errors: [UserError]
+ errors: [UserError]
}
# Response to removeCommentTag mutation
type RemoveCommentTagResponse implements Response {
# An array of errors relating to the mutation that occured.
comment: Comment
- errors: [UserError]
+ errors: [UserError]
}
# All mutations for the application are defined on this object.
From 63220cb6a721ea4b88221f420d38616c07152590 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Wed, 8 Mar 2017 19:14:02 -0700
Subject: [PATCH 18/56] Some fixes to make it work
---
graph/loaders/metrics.js | 11 ++++++-----
graph/resolvers/root_query.js | 10 ++++++----
graph/typeDefs.graphql | 7 ++++---
3 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/graph/loaders/metrics.js b/graph/loaders/metrics.js
index d3c602de8..a0408ed4d 100644
--- a/graph/loaders/metrics.js
+++ b/graph/loaders/metrics.js
@@ -3,14 +3,15 @@ const DataLoader = require('dataloader');
const {objectCacheKeyFn} = require('./util');
const ActionModel = require('../../models/action');
+const CommentModel = require('../../models/comment');
/**
* Returns the assets which have had comments made within the last time period.
*/
-const getCommentActivityMetrics = ({loaders: {Assets, Comments}}, {from, to, limit}) => {
+const getAssetActivityMetrics = ({loaders: {Assets}}, {from, to, limit}) => {
let assetMetrics = [];
- return Comments.aggregate([
+ return CommentModel.aggregate([
{$match: {
parent_id: null,
created_at: {
@@ -35,7 +36,7 @@ const getCommentActivityMetrics = ({loaders: {Assets, Comments}}, {from, to, lim
{$limit: limit}
])
.then((results) => {
- results = assetMetrics;
+ assetMetrics = results;
return Assets.getByID.loadMany(results.map((result) => result.asset_id));
})
@@ -254,11 +255,11 @@ module.exports = (context) => ({
cacheKeyFn: objectCacheKeyFn('from', 'to')
}),
Assets: {
- get: ({from, to, sort, limit}) => getAssetMetrics(context, {from, to, sort, limit})
+ get: ({from, to, sort, limit}) => getAssetMetrics(context, {from, to, sort, limit}),
+ getActivity: ({from, to, limit}) => getAssetActivityMetrics(context, {from, to, limit}),
},
Comments: {
get: ({from, to, sort, limit}) => getCommentMetrics(context, {from, to, sort, limit}),
- getActivity: ({from, to, limit}) => getCommentActivityMetrics(context, {from, to, limit}),
}
}
});
diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js
index 083526eff..98dbfb7ed 100644
--- a/graph/resolvers/root_query.js
+++ b/graph/resolvers/root_query.js
@@ -63,6 +63,12 @@ const RootQuery = {
return null;
}
+ console.log({from, to, sort, limit});
+
+ if (sort === 'ACTIVITY') {
+ return Assets.getActivity({from, to, limit});
+ }
+
return Assets.get({from, to, sort, limit});
},
@@ -71,10 +77,6 @@ const RootQuery = {
return null;
}
- if (sort === 'COMMENTS') {
- return Comments.getActivity({from, to, limit});
- }
-
return Comments.get({from, to, sort, limit});
},
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 2821952e4..7e0cc534d 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -476,7 +476,8 @@ enum USER_STATUS {
APPROVED
}
-enum COMMENT_METRICS_SORT {
+# Metrics for the assets.
+enum ASSET_METRICS_SORT {
# Represents a LikeAction.
LIKE
@@ -518,11 +519,11 @@ type RootQuery {
# Asset metrics related to user actions are saturated into the assets
# returned.
- assetMetrics(from: Date!, to: Date!, sort: ACTION_TYPE!, limit: Int = 10): [Asset!]
+ assetMetrics(from: Date!, to: Date!, sort: ASSET_METRICS_SORT!, limit: Int = 10): [Asset!]
# Comment metrics related to user actions are saturated into the comments
# returned.
- commentMetrics(from: Date!, to: Date!, sort: COMMENT_METRICS_SORT!, limit: Int = 10): [Comment!]
+ commentMetrics(from: Date!, to: Date!, sort: ACTION_TYPE!, limit: Int = 10): [Comment!]
}
################################################################################
From a0953c068906209a3265d1d5458185a35f372ac0 Mon Sep 17 00:00:00 2001
From: David Jay
Date: Thu, 9 Mar 2017 13:33:00 -0500
Subject: [PATCH 19/56] Adding proptypes to loadmore component.
---
.../containers/ModerationQueue/components/LoadMore.js | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
index 5ac193525..f8d3f0ed4 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {PropTypes} from 'react';
import {Button} from 'coral-ui';
import styles from './styles.css';
@@ -19,4 +19,13 @@ const LoadMore = ({comments, loadMore, sort, tab, assetId, showLoadMore}) =>
}
;
+LoadMore.propTypes = {
+ comments: PropTypes.array.isRequired,
+ loadMore: PropTypes.func.isRequired,
+ sort: PropTypes.oneOf(['CHRONOLOGICAL', 'REVERSE_CHRONOLOGICAL']).isRequired,
+ tab: PropTypes.oneOf(['rejected', 'premod', 'flagged']).isRequired,
+ assetId: PropTypes.string,
+ showLoadMore: PropTypes.bool.isRequired
+};
+
export default LoadMore;
From b94401a8cfeb93e28dbab47d1420117c61dcc938 Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Thu, 9 Mar 2017 11:05:18 -0700
Subject: [PATCH 20/56] make only the title clickable
---
client/coral-admin/src/containers/Dashboard/FlagWidget.js | 2 +-
client/coral-admin/src/containers/Dashboard/LikeWidget.js | 2 +-
client/coral-admin/src/containers/Dashboard/Widget.css | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/client/coral-admin/src/containers/Dashboard/FlagWidget.js b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
index 6412df7f0..7d2ffe17a 100644
--- a/client/coral-admin/src/containers/Dashboard/FlagWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/FlagWidget.js
@@ -27,8 +27,8 @@ const FlagWidget = (props) => {
{flagSummary ? flagSummary.actionCount : 0}
{asset.title}
- {asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
+ {asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
);
})
diff --git a/client/coral-admin/src/containers/Dashboard/LikeWidget.js b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
index d01259c5d..436fbcaf3 100644
--- a/client/coral-admin/src/containers/Dashboard/LikeWidget.js
+++ b/client/coral-admin/src/containers/Dashboard/LikeWidget.js
@@ -28,8 +28,8 @@ const LikeWidget = (props) => {
{likeSummary ? likeSummary.actionCount : 0}
{asset.title}
- {asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
+ {asset.author} — Published: {new Date(asset.created_at).toLocaleDateString()}
);
})
diff --git a/client/coral-admin/src/containers/Dashboard/Widget.css b/client/coral-admin/src/containers/Dashboard/Widget.css
index db4041dbd..124663cea 100644
--- a/client/coral-admin/src/containers/Dashboard/Widget.css
+++ b/client/coral-admin/src/containers/Dashboard/Widget.css
@@ -64,7 +64,7 @@
}
.linkToAsset {
- display: block;
+ display: inline-block;
text-decoration: none;
}
From db38bcb477459fb16c8e19ab3e76b55a3e4f911f Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Thu, 9 Mar 2017 12:57:54 -0700
Subject: [PATCH 21/56] modify to light grey
---
client/coral-admin/src/containers/Dashboard/Dashboard.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/coral-admin/src/containers/Dashboard/Dashboard.css b/client/coral-admin/src/containers/Dashboard/Dashboard.css
index 1bd1599b4..cdad990ba 100644
--- a/client/coral-admin/src/containers/Dashboard/Dashboard.css
+++ b/client/coral-admin/src/containers/Dashboard/Dashboard.css
@@ -9,7 +9,7 @@
}
.autoUpdate {
- background-color: #ffdb87;
+ background-color: #d5d5d5;
padding: 3px 10px 10px 10px;
margin-bottom: 0;
@@ -22,7 +22,7 @@
float: right;
border-radius: 20px;
cursor: pointer;
- background-color: #e6ba52;
+ background-color: #c0c0c0;
width: 30px;
height: 30px;
text-align: center;
From fcc93963c44ac1703bedd32f6a9fd9a8346c7d42 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Thu, 9 Mar 2017 17:07:07 -0700
Subject: [PATCH 22/56] Added support for total comment count
---
graph/loaders/comments.js | 53 ++-
graph/loaders/util.js | 26 +-
graph/mutators/comment.js | 30 +-
graph/resolvers/asset.js | 13 +-
graph/typeDefs.graphql | 3 +
package.json | 2 +-
services/cache.js | 152 +++++++++
yarn.lock | 679 +++++++++++++++++++-------------------
8 files changed, 598 insertions(+), 360 deletions(-)
diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js
index afa79ea5c..e2ae29f65 100644
--- a/graph/loaders/comments.js
+++ b/graph/loaders/comments.js
@@ -1,4 +1,8 @@
-const util = require('./util');
+const {
+ SharedCounterDataLoader,
+ singleJoinBy,
+ arrayJoinBy
+} = require('./util');
const DataLoader = require('dataloader');
const CommentModel = require('../../models/comment');
@@ -11,6 +15,38 @@ const CommentModel = require('../../models/comment');
* comments that we want to get
*/
const getCountsByAssetID = (context, asset_ids) => {
+ return CommentModel.aggregate([
+ {
+ $match: {
+ asset_id: {
+ $in: asset_ids
+ },
+ status: {
+ $in: ['NONE', 'ACCEPTED']
+ }
+ }
+ },
+ {
+ $group: {
+ _id: '$asset_id',
+ count: {
+ $sum: 1
+ }
+ }
+ }
+ ])
+ .then(singleJoinBy(asset_ids, '_id'))
+ .then((results) => results.map((result) => result ? result.count : 0));
+};
+
+/**
+ * Returns the comment count for all comments that are public based on their
+ * asset ids.
+ * @param {Object} context graph context
+ * @param {Array} asset_ids the ids of assets for which there are
+ * comments that we want to get
+ */
+const getParentCountsByAssetID = (context, asset_ids) => {
return CommentModel.aggregate([
{
$match: {
@@ -32,7 +68,7 @@ const getCountsByAssetID = (context, asset_ids) => {
}
}
])
- .then(util.singleJoinBy(asset_ids, '_id'))
+ .then(singleJoinBy(asset_ids, '_id'))
.then((results) => results.map((result) => result ? result.count : 0));
};
@@ -64,7 +100,7 @@ const getCountsByParentID = (context, parent_ids) => {
}
}
])
- .then(util.singleJoinBy(parent_ids, '_id'))
+ .then(singleJoinBy(parent_ids, '_id'))
.then((results) => results.map((result) => result ? result.count : 0));
};
@@ -216,7 +252,7 @@ const genRecentReplies = (context, ids) => {
])
.then((replies) => replies.map((reply) => reply.replies))
- .then(util.arrayJoinBy(ids, 'parent_id'));
+ .then(arrayJoinBy(ids, 'parent_id'));
};
/**
@@ -267,7 +303,7 @@ const genRecentComments = (_, ids) => {
])
.then((replies) => replies.map((reply) => reply.comments))
- .then(util.arrayJoinBy(ids, 'asset_id'));
+ .then(arrayJoinBy(ids, 'asset_id'));
};
/**
@@ -294,7 +330,7 @@ const genComments = ({user}, ids) => {
}
});
}
- return comments.then(util.singleJoinBy(ids, 'id'));
+ return comments.then(singleJoinBy(ids, 'id'));
};
/**
@@ -307,8 +343,9 @@ module.exports = (context) => ({
get: new DataLoader((ids) => genComments(context, ids)),
getByQuery: (query) => getCommentsByQuery(context, query),
getCountByQuery: (query) => getCommentCountByQuery(context, query),
- countByAssetID: new util.SharedCacheDataLoader('Comments.countByAssetID', 3600, (ids) => getCountsByAssetID(context, ids)),
- countByParentID: new util.SharedCacheDataLoader('Comments.countByParentID', 3600, (ids) => getCountsByParentID(context, ids)),
+ countByAssetID: new SharedCounterDataLoader('Comments.totalCommentCount', 3600, (ids) => getCountsByAssetID(context, ids)),
+ parentCountByAssetID: new SharedCounterDataLoader('Comments.countByAssetID', 3600, (ids) => getParentCountsByAssetID(context, ids)),
+ countByParentID: new SharedCounterDataLoader('Comments.countByParentID', 3600, (ids) => getCountsByParentID(context, ids)),
genRecentReplies: new DataLoader((ids) => genRecentReplies(context, ids)),
genRecentComments: new DataLoader((ids) => genRecentComments(context, ids))
}
diff --git a/graph/loaders/util.js b/graph/loaders/util.js
index ab6f8a1f3..fe2a4e3d0 100644
--- a/graph/loaders/util.js
+++ b/graph/loaders/util.js
@@ -121,6 +121,29 @@ class SharedCacheDataLoader extends DataLoader {
}
}
+/**
+ * SharedCounterDataLoader is identical to SharedCacheDataLoader with the
+ * exception in that it is designed to work with numerical cached data.
+ */
+class SharedCounterDataLoader extends SharedCacheDataLoader {
+
+ /**
+ * Increments the key in the cache if it already exists in the cache, if not
+ * it does nothing.
+ */
+ incr(key) {
+ return cache.incr(key, this._expiry, this._keyFunc);
+ }
+
+ /**
+ * Decrements the key in the cache if it already exists in the cache, if not
+ * it does nothing.
+ */
+ decr(key) {
+ return cache.decr(key, this._expiry, this._keyFunc);
+ }
+}
+
/**
* Maps an object's paths to a string that can be used as a cache key.
* @param {Array} paths paths on the object to be used to generate the cache
@@ -145,5 +168,6 @@ module.exports = {
objectCacheKeyFn,
arrayCacheKeyFn,
SingletonResolver,
- SharedCacheDataLoader
+ SharedCacheDataLoader,
+ SharedCounterDataLoader
};
diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js
index bcab347d7..728603ae2 100644
--- a/graph/mutators/comment.js
+++ b/graph/mutators/comment.js
@@ -33,16 +33,17 @@ const createComment = ({user, loaders: {Comments}}, {body, asset_id, parent_id =
})
.then((comment) => {
- // TODO: explore using an `INCR` operation to update the counts here
-
// If the loaders are present, clear the caches for these values because we
- // just added a new comment, hence the counts should be updated.
- if (Comments && Comments.countByAssetID && Comments.countByParentID) {
+ // just added a new comment, hence the counts should be updated. We should
+ // perform these increments in the event that we do have a new comment that
+ // is approved or without a comment.
+ if (status === 'NONE' || status === 'APPROVED') {
if (parent_id != null) {
- Comments.countByParentID.clear(parent_id);
+ Comments.countByParentID.incr(parent_id);
} else {
- Comments.countByAssetID.clear(asset_id);
+ Comments.parentCountByAssetID.incr(asset_id);
}
+ Comments.countByAssetID.incr(asset_id);
}
return comment;
@@ -182,15 +183,18 @@ const setCommentStatus = ({loaders: {Comments}}, {id, status}) => {
.then((comment) => {
// If the loaders are present, clear the caches for these values because we
- // just added a new comment, hence the counts should be updated.
- if (Comments && Comments.countByAssetID && Comments.countByParentID) {
- if (comment.parent_id != null) {
- Comments.countByParentID.clear(comment.parent_id);
- } else {
- Comments.countByAssetID.clear(comment.asset_id);
- }
+ // just added a new comment, hence the counts should be updated. It would
+ // be nice if we could decrement the counters here, but that would result
+ // in us having to know the initial state of the comment, which would
+ // require another database query.
+ if (comment.parent_id != null) {
+ Comments.countByParentID.clear(comment.parent_id);
+ } else {
+ Comments.parentCountByAssetID.clear(comment.asset_id);
}
+ Comments.countByAssetID.clear(comment.asset_id);
+
return comment;
});
};
diff --git a/graph/resolvers/asset.js b/graph/resolvers/asset.js
index 2077b9e7a..c2a0a2b66 100644
--- a/graph/resolvers/asset.js
+++ b/graph/resolvers/asset.js
@@ -10,7 +10,18 @@ const Asset = {
parent_id: null
});
},
- commentCount({id}, _, {loaders: {Comments}}) {
+ commentCount({id, commentCount}, _, {loaders: {Comments}}) {
+ if (commentCount != null) {
+ return commentCount;
+ }
+
+ return Comments.parentCountByAssetID.load(id);
+ },
+ totalCommentCount({id, totalCommentCount}, _, {loaders: {Comments}}) {
+ if (totalCommentCount != null) {
+ return totalCommentCount;
+ }
+
return Comments.countByAssetID.load(id);
},
settings({settings = null}, _, {loaders: {Settings}}) {
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 29000c84d..9655354fb 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -422,6 +422,9 @@ type Asset {
# The count of top level comments on the asset.
commentCount: Int
+ # The total count of all comments made on the asset.
+ totalCommentCount: Int
+
# The settings (rectified with the global settings) that should be applied to
# this asset.
settings: Settings!
diff --git a/package.json b/package.json
index 92442efd2..373e2adde 100644
--- a/package.json
+++ b/package.json
@@ -83,7 +83,7 @@
"passport-facebook": "^2.1.1",
"passport-local": "^1.0.0",
"react-apollo": "^0.10.0",
- "redis": "^2.6.3",
+ "redis": "^2.6.5",
"uuid": "^2.0.3"
},
"devDependencies": {
diff --git a/services/cache.js b/services/cache.js
index 21ae9911e..92da6df0a 100644
--- a/services/cache.js
+++ b/services/cache.js
@@ -1,5 +1,6 @@
const redis = require('./redis');
const debug = require('debug')('talk:cache');
+const crypto = require('crypto');
const cache = module.exports = {
client: redis.createClient()
@@ -60,6 +61,157 @@ cache.wrap = (key, expiry, work, kf = keyfunc) => {
});
};
+// This is designed to increment a key and add an expiry iff the key already
+// exists.
+const INCR_SCRIPT = `
+if redis.call('GET', KEYS[1]) ~= false then
+ redis.call('INCR', KEYS[1])
+ redis.call('EXPIRE', KEYS[1], ARGV[1])
+end
+`;
+
+// Stores the SHA1 hash of INCR_SCRIPT, used for executing via EVALSHA.
+let INCR_SCRIPT_HASH;
+
+// This is designed to decrement a key and add an expiry iff the key already
+// exists.
+const DECR_SCRIPT = `
+if redis.call('GET', KEYS[1]) ~= false then
+ redis.call('DECR', KEYS[1])
+ redis.call('EXPIRE', KEYS[1], ARGV[1])
+end
+`;
+
+// Stores the SHA1 hash of DECR_SCRIPT, used for executing via EVALSHA.
+let DECR_SCRIPT_HASH;
+
+// Load the script into redis and track the script hash that we will use to exec
+// increments on.
+const loadScript = (name, script) => new Promise((resolve, reject) => {
+
+ let shasum = crypto.createHash('sha1');
+ shasum.update(script);
+
+ let hash = shasum.digest('hex');
+
+ cache.client
+ .script('EXISTS', hash, (err, [exists]) => {
+ if (err) {
+ return reject(err);
+ }
+
+ if (exists) {
+ debug(`already loaded ${name} as SHA[${hash}], not loading again`);
+
+ return resolve(hash);
+ }
+
+ debug(`${name} not loaded as SHA[${hash}], loading`);
+
+ cache.client
+ .script('load', script, (err, hash) => {
+ if (err) {
+ return reject(err);
+ }
+
+ debug(`loaded ${name} as SHA[${hash}]`);
+
+ resolve(hash);
+ });
+ });
+});
+
+// Load the INCR_SCRIPT and DECR_SCRIPT into Redis.
+Promise.all([
+ loadScript('INCR_SCRIPT', INCR_SCRIPT),
+ loadScript('DECR_SCRIPT', DECR_SCRIPT)
+])
+.then(([incrScriptHash, decrScriptHash]) => {
+ INCR_SCRIPT_HASH = incrScriptHash;
+ DECR_SCRIPT_HASH = decrScriptHash;
+})
+.catch((err) => {
+ throw err;
+});
+
+/**
+ * This will increment a key in redis and update the expiry iff it already
+ * exists, otherwise it will do nothing.
+ */
+cache.incr = (key, expiry, kf = keyfunc) => new Promise((resolve, reject) => {
+ cache.client
+ .evalsha(INCR_SCRIPT_HASH, 1, kf(key), expiry, (err) => {
+ if (err) {
+ return reject(err);
+ }
+
+ return resolve();
+ });
+});
+
+/**
+ * This will decrement a key in redis and update the expiry iff it already
+ * exists, otherwise it will do nothing.
+ */
+cache.decr = (key, expiry, kf = keyfunc) => new Promise((resolve, reject) => {
+ cache.client
+ .evalsha(DECR_SCRIPT_HASH, 1, kf(key), expiry, (err) => {
+ if (err) {
+ return reject(err);
+ }
+
+ return resolve();
+ });
+});
+
+/**
+ * This will increment many keys in redis and update the expiry iff it already
+ * exists, otherwise it will do nothing.
+ */
+cache.incrMany = (keys, expiry, kf = keyfunc) => {
+ let multi = cache.client.multi();
+
+ keys.forEach((key) => {
+
+ // Queue up the evalsha command.
+ multi.evalsha(INCR_SCRIPT_HASH, 1, kf(key), expiry);
+ });
+
+ return new Promise((resolve, reject) => {
+ multi.exec((err) => {
+ if (err) {
+ return reject(err);
+ }
+
+ resolve();
+ });
+ });
+};
+
+/**
+ * This will decrement many keys in redis and update the expiry iff it already
+ * exists, otherwise it will do nothing.
+ */
+cache.decrMany = (keys, expiry, kf = keyfunc) => {
+ let multi = cache.client.multi();
+
+ keys.forEach((key) => {
+
+ // Queue up the evalsha command.
+ multi.evalsha(DECR_SCRIPT_HASH, 1, kf(key), expiry);
+ });
+
+ return new Promise((resolve, reject) => {
+ multi.exec((err) => {
+ if (err) {
+ return reject(err);
+ }
+
+ resolve();
+ });
+ });
+};
+
/**
* [wrapMany description]
* @param {Array} keys Either an array of objects represening
diff --git a/yarn.lock b/yarn.lock
index 82b5e92f3..32f0a4c2f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1,5 +1,7 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
+
+
"@kadira/storybook-deployer@^1.1.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@kadira/storybook-deployer/-/storybook-deployer-1.2.0.tgz#1708f5cb37fa08ab4173b1bd99df6f4717dfae12"
@@ -289,14 +291,14 @@ assert@^1.1.1:
dependencies:
util "0.10.3"
-assertion-error@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c"
-
assertion-error@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.0.tgz#c7f85438fdd466bc7ca16ab90c81513797a5d23b"
+assertion-error@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c"
+
ast-traverse@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6"
@@ -313,11 +315,15 @@ async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
-async@^1.4.0, async@^1.4.2, async@^1.5.2, async@1.x:
+async@1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/async/-/async-1.2.1.tgz#a4816a17cd5ff516dfa2c7698a453369b9790de0"
+
+async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
-async@^2.1.2, async@^2.1.4, async@2.1.4:
+async@2.1.4, async@^2.1.2, async@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4"
dependencies:
@@ -331,10 +337,6 @@ async@~0.9.0:
version "0.9.2"
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
-async@1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/async/-/async-1.2.1.tgz#a4816a17cd5ff516dfa2c7698a453369b9790de0"
-
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1053,6 +1055,10 @@ babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0:
version "6.15.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e"
+balanced-match@0.1.0, balanced-match@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
+
balanced-match@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.2.1.tgz#7bc658b4bed61eee424ad74f75f5c3e2c4df3cc7"
@@ -1061,10 +1067,6 @@ balanced-match@^0.4.1, balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
-balanced-match@~0.1.0, balanced-match@0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
-
base64-js@^1.0.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
@@ -1073,7 +1075,7 @@ base64-url@1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/base64-url/-/base64-url-1.3.3.tgz#f8b6c537f09a4fc58c99cb86e0b0e9c61461a20f"
-base64url@^2.0.0, base64url@2.0.0:
+base64url@2.0.0, base64url@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb"
@@ -1119,10 +1121,6 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bluebird@^2.10.2:
- version "2.11.0"
- resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
-
bluebird@2.10.2:
version "2.10.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.10.2.tgz#024a5517295308857f14f91f1106fc3b555f446b"
@@ -1139,6 +1137,10 @@ bluebird@3.4.6:
version "3.4.6"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f"
+bluebird@^2.10.2:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
+
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.6"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
@@ -1383,7 +1385,7 @@ chai@^3.5.0:
deep-eql "^0.1.3"
type-detect "^1.0.0"
-chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3, chalk@1.1.3:
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
@@ -1542,7 +1544,7 @@ cli-cursor@^2.1.0:
dependencies:
restore-cursor "^2.0.0"
-cli-table@^0.3.1, cli-table@0.3.1:
+cli-table@0.3.1, cli-table@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
dependencies:
@@ -1626,18 +1628,18 @@ colormin@^1.0.5:
css-color-names "0.0.4"
has "^1.0.1"
-colors@~0.6.0-1:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
-
-colors@~1.1.2, colors@1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
-
colors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
+colors@1.1.2, colors@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+
+colors@~0.6.0-1:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
+
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"
@@ -1650,16 +1652,6 @@ combined-stream@~0.0.4:
dependencies:
delayed-stream "0.0.5"
-commander@^2.5.0, commander@^2.9.0, commander@2.9.0:
- version "2.9.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
- dependencies:
- graceful-readlink ">= 1.0.0"
-
-commander@~2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781"
-
commander@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06"
@@ -1678,6 +1670,16 @@ commander@2.8.x:
dependencies:
graceful-readlink ">= 1.0.0"
+commander@2.9.0, commander@^2.5.0, commander@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
+ dependencies:
+ graceful-readlink ">= 1.0.0"
+
+commander@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781"
+
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -1805,7 +1807,7 @@ cookie@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
-cookiejar@^2.0.6, cookiejar@2.0.x:
+cookiejar@2.0.x, cookiejar@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.0.6.tgz#0abf356ad00d1c5a219d88d44518046dd026acfe"
@@ -1830,7 +1832,7 @@ 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, core-util-is@1.0.2:
+core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -2020,7 +2022,7 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"
-"cssom@>= 0.3.0 < 0.4.0", cssom@0.3.x:
+cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
@@ -2045,16 +2047,16 @@ cz-conventional-changelog@1.1.5:
dependencies:
word-wrap "^1.0.3"
+d3-helpers@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/d3-helpers/-/d3-helpers-0.3.0.tgz#4b31dce4a2121a77336384574d893fbed5fb293d"
+
d@^0.1.1, d@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309"
dependencies:
es5-ext "~0.10.2"
-d3-helpers@0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/d3-helpers/-/d3-helpers-0.3.0.tgz#4b31dce4a2121a77336384574d893fbed5fb293d"
-
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -2085,17 +2087,13 @@ date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
-debug@*, debug@^2.1.1, debug@^2.2.0, debug@2, debug@2.6.0:
+debug@*, debug@2, debug@2.6.0, debug@^2.1.1, debug@^2.2.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b"
dependencies:
ms "0.7.2"
-debug@^0.7.2, debug@~0.7.4:
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
-
-debug@~2.2.0, debug@2.2.0:
+debug@2.2.0, debug@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
dependencies:
@@ -2107,11 +2105,15 @@ debug@2.3.3:
dependencies:
ms "0.7.2"
+debug@^0.7.2, debug@~0.7.4:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
+
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
-deep-eql@^0.1.3, deep-eql@0.1.3:
+deep-eql@0.1.3, deep-eql@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2"
dependencies:
@@ -2177,14 +2179,14 @@ del@^2.0.2:
pinkie-promise "^2.0.0"
rimraf "^2.2.8"
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
-
delayed-stream@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.5.tgz#d4b1f43a93e8296dfe02694f4680bc37a313c73f"
+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"
@@ -2241,7 +2243,7 @@ dns-prefetch-control@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2"
-doctrine@^1.2.2, doctrine@1.5.0:
+doctrine@1.5.0, doctrine@^1.2.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
dependencies:
@@ -2252,7 +2254,7 @@ doctypes@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9"
-dom-serializer@~0.1.0, dom-serializer@0:
+dom-serializer@0, dom-serializer@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
dependencies:
@@ -2263,21 +2265,21 @@ domain-browser@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+domelementtype@1, domelementtype@~1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
domelementtype@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
-domelementtype@~1.1.1, domelementtype@1:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
-
-domhandler@^2.3.0, domhandler@2.3:
+domhandler@2.3, domhandler@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738"
dependencies:
domelementtype "1"
-domutils@^1.5.1, domutils@1.5, domutils@1.5.1:
+domutils@1.5, domutils@1.5.1, domutils@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
dependencies:
@@ -2361,18 +2363,18 @@ encoding@^0.1.11:
dependencies:
iconv-lite "~0.4.13"
-end-of-stream@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07"
- dependencies:
- once "~1.3.0"
-
end-of-stream@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.0.0.tgz#d4596e702734a93e40e9af864319eabd99ff2f0e"
dependencies:
once "~1.3.0"
+end-of-stream@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07"
+ dependencies:
+ once "~1.3.0"
+
enhanced-resolve@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.0.3.tgz#df14c06b5fc5eecade1094c9c5a12b4b3edc0b62"
@@ -2382,14 +2384,14 @@ enhanced-resolve@^3.0.0:
object-assign "^4.0.1"
tapable "^0.2.5"
-entities@^1.1.1, entities@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
-
entities@1.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26"
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
env-rewrite@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/env-rewrite/-/env-rewrite-1.0.2.tgz#3e344a95af1bdaab34a559479b8be3abd0804183"
@@ -2463,14 +2465,14 @@ es6-map@^0.1.3:
es6-symbol "~3.1.0"
event-emitter "~0.3.4"
-es6-promise@^3.0.2:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
-
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.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8"
@@ -2485,7 +2487,7 @@ es6-shim@^0.35.3:
version "0.35.3"
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.3.tgz#9bfb7363feffff87a6cdb6cd93e405ec3c4b6f26"
-es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3:
+es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa"
dependencies:
@@ -2509,15 +2511,15 @@ escape-regexp-component@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz#9c63b6d0b25ff2a88c3adbd18c5b61acc3b9faa2"
-escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5, escape-string-regexp@1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
-
escape-string-regexp@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1"
-escodegen@^1.6.1, escodegen@1.x.x:
+escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+escodegen@1.x.x, escodegen@^1.6.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
dependencies:
@@ -2653,14 +2655,14 @@ esprima-fb@~15001.1001.0-dev-harmony-fb:
version "15001.1001.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659"
+esprima@3.x.x, esprima@~3.1.0:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+
esprima@^2.6.0, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
-esprima@~3.1.0, esprima@3.x.x:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
-
esrecurse@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220"
@@ -2785,14 +2787,14 @@ express@^4.12.2, express@^4.14.0:
utils-merge "1.0.0"
vary "~1.1.0"
+extend@3, extend@^3.0.0, extend@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
+
extend@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extend/-/extend-1.3.0.tgz#d1516fb0ff5624d2ebf9123ea1dac5a1994004f8"
-extend@^3.0.0, extend@~3.0.0, extend@3:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
-
external-editor@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.1.tgz#4c597c6c88fa6410e41dbbaa7b1be2336aa31095"
@@ -2961,6 +2963,14 @@ forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+form-data@1.0.0-rc4:
+ version "1.0.0-rc4"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.0-rc4.tgz#05ac6bc22227b43e4461f488161554699d4f8b5e"
+ dependencies:
+ async "^1.5.2"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.10"
+
form-data@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-0.2.0.tgz#26f8bc26da6440e299cbdcfb69035c4f77a6e466"
@@ -2977,14 +2987,6 @@ form-data@~2.1.1:
combined-stream "^1.0.5"
mime-types "^2.1.12"
-form-data@1.0.0-rc4:
- version "1.0.0-rc4"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.0-rc4.tgz#05ac6bc22227b43e4461f488161554699d4f8b5e"
- dependencies:
- async "^1.5.2"
- combined-stream "^1.0.5"
- mime-types "^2.1.10"
-
formidable@^1.0.17:
version "1.1.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9"
@@ -3199,37 +3201,6 @@ glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
-glob@^5.0.15, glob@^5.0.3:
- version "5.0.15"
- resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
- dependencies:
- inflight "^1.0.4"
- inherits "2"
- minimatch "2 || 3"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^6.0.4:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
- dependencies:
- inflight "^1.0.4"
- inherits "2"
- minimatch "2 || 3"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@7.1.1:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.2"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
glob@3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.3.tgz#e313eeb249c7affaa5c475286b0e115b59839467"
@@ -3259,6 +3230,37 @@ glob@7.0.5, glob@7.0.x:
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^5.0.15, glob@^5.0.3:
+ version "5.0.15"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^6.0.4:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
globals@^9.0.0, globals@^9.14.0:
version "9.14.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034"
@@ -3600,15 +3602,15 @@ https-proxy-agent@1:
debug "2"
extend "3"
-iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13, iconv-lite@0.4.15:
- version "0.4.15"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
-
iconv-lite@0.4.13:
version "0.4.13"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
-icss-replace-symbols@^1.0.2, icss-replace-symbols@1.0.2:
+iconv-lite@0.4.15, iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
+ version "0.4.15"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
+
+icss-replace-symbols@1.0.2, icss-replace-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5"
@@ -3666,7 +3668,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@2, inherits@2.0.3:
+inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
@@ -3685,7 +3687,24 @@ inquirer-confirm@0.2.2:
bluebird "2.9.24"
inquirer "0.8.2"
-inquirer@^0.12.0, inquirer@0.12.0:
+inquirer@0.11.1:
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.11.1.tgz#623b6e0c101d2fe9e8e8ed902e651e0519d143e5"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ ansi-regex "^2.0.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^1.0.1"
+ run-async "^0.1.0"
+ rx-lite "^3.1.2"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+inquirer@0.12.0, inquirer@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
dependencies:
@@ -3703,6 +3722,19 @@ inquirer@^0.12.0, inquirer@0.12.0:
strip-ansi "^3.0.0"
through "^2.3.6"
+inquirer@0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.8.2.tgz#41586548e1c5d9b3f81df7325034baacab6f58ab"
+ dependencies:
+ ansi-regex "^1.1.1"
+ chalk "^1.0.0"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^0.1.1"
+ rx "^2.4.3"
+ through "^2.3.6"
+
inquirer@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.1.tgz#6dfbffaf4d697dd76c8fe349f919de01c28afc4d"
@@ -3721,36 +3753,6 @@ inquirer@^3.0.1:
strip-ansi "^3.0.0"
through "^2.3.6"
-inquirer@0.11.1:
- version "0.11.1"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.11.1.tgz#623b6e0c101d2fe9e8e8ed902e651e0519d143e5"
- dependencies:
- ansi-escapes "^1.1.0"
- ansi-regex "^2.0.0"
- chalk "^1.0.0"
- cli-cursor "^1.0.1"
- cli-width "^1.0.1"
- figures "^1.3.5"
- lodash "^3.3.1"
- readline2 "^1.0.1"
- run-async "^0.1.0"
- rx-lite "^3.1.2"
- strip-ansi "^3.0.0"
- through "^2.3.6"
-
-inquirer@0.8.2:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.8.2.tgz#41586548e1c5d9b3f81df7325034baacab6f58ab"
- dependencies:
- ansi-regex "^1.1.1"
- chalk "^1.0.0"
- cli-width "^1.0.1"
- figures "^1.3.5"
- lodash "^3.3.1"
- readline2 "^0.1.1"
- rx "^2.4.3"
- through "^2.3.6"
-
interpret@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
@@ -3998,14 +4000,14 @@ is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
-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"
-
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"
@@ -4146,7 +4148,7 @@ js-tokens@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
-js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@~3.7.0, js-yaml@3.x:
+js-yaml@3.x, js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@~3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
dependencies:
@@ -4406,6 +4408,12 @@ linkify-it@^1.2.0:
dependencies:
uc.micro "^1.0.1"
+linkify-it@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f"
+ dependencies:
+ uc.micro "^1.0.1"
+
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -4420,7 +4428,7 @@ 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@^0.2.11, loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.7, loader-utils@~0.2.2, loader-utils@0.2.x:
+loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.7, loader-utils@~0.2.2:
version "0.2.16"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d"
dependencies:
@@ -4540,7 +4548,7 @@ lodash.cond@^4.3.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
-lodash.create@^3.1.1, lodash.create@3.1.1:
+lodash.create@3.1.1, lodash.create@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7"
dependencies:
@@ -4633,7 +4641,7 @@ lodash.pickby@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff"
-lodash.reduce@^4.4.0, lodash.reduce@4.6.0:
+lodash.reduce@4.6.0, lodash.reduce@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
@@ -4659,18 +4667,18 @@ lodash.words@^3.0.0:
dependencies:
lodash._root "^3.0.0"
-lodash@^3.3.1, lodash@3.10.1:
+lodash@3.10.1, lodash@^3.3.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
-lodash@^4.0.0, lodash@^4.1.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
- version "4.17.4"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-
lodash@3.9.3:
version "3.9.3"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.9.3.tgz#0159e86832feffc6d61d852b12a953b99496bd32"
+lodash@^4.0.0, lodash@^4.1.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
log4js@*:
version "1.1.0"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-1.1.0.tgz#c7d2b616d91bbf47cc65fb79d6fe04581c8096fa"
@@ -4693,7 +4701,7 @@ 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@^2.5.0, lru-cache@2:
+lru-cache@2, lru-cache@^2.5.0:
version "2.7.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
@@ -4774,7 +4782,7 @@ metascraper@^1.0.6:
popsicle "^6.2.0"
to-title-case "^1.0.0"
-methods@^1.1.1, methods@^1.1.2, methods@~1.1.2, methods@1.x:
+methods@1.x, methods@^1.1.1, methods@^1.1.2, methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -4823,7 +4831,7 @@ mime-types@~2.0.3:
dependencies:
mime-db "~1.12.0"
-mime@^1.3.4, mime@1.3.4:
+mime@1.3.4, mime@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
@@ -4835,7 +4843,7 @@ minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
-minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, "minimatch@2 || 3":
+"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
dependencies:
@@ -4848,11 +4856,7 @@ minimatch@~0.2.11, minimatch@~0.2.14:
lru-cache "2"
sigmund "~1.0.0"
-minimist@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-
-minimist@~0.0.1, minimist@0.0.8:
+minimist@0.0.8, minimist@~0.0.1:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -4860,11 +4864,9 @@ minimist@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.0.tgz#cdf225e8898f840a258ded44fc91776770afdc93"
-mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.1, mkdirp@0.5.x:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
- dependencies:
- minimist "0.0.8"
+minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
mkdirp@0.3.0:
version "0.3.0"
@@ -4876,6 +4878,12 @@ mkdirp@0.5.0:
dependencies:
minimist "0.0.8"
+mkdirp@0.5.1, mkdirp@0.5.x, "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"
+
mkpath@>=0.1.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-1.0.0.tgz#ebb3a977e7af1c683ae6fda12b545a6ba6c5853d"
@@ -4920,10 +4928,6 @@ mocha@^3.1.2:
mkdirp "0.5.1"
supports-color "3.1.2"
-moment@^2.10.3, moment@2.x.x:
- version "2.17.1"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82"
-
moment@2.12.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.12.0.tgz#dc2560d19838d6c0731b1a6afa04675264d360d6"
@@ -4932,6 +4936,10 @@ moment@2.17.0:
version "2.17.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.0.tgz#a4c292e02aac5ddefb29a6eed24f51938dd3b74f"
+moment@2.x.x, moment@^2.10.3:
+ version "2.17.1"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82"
+
mongodb-core@2.1.7:
version "2.1.7"
resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.7.tgz#6a27909b98142ef2508d924c274969008954fa29"
@@ -4991,14 +4999,14 @@ mquery@2.2.1:
regexp-clone "0.0.1"
sliced "0.0.5"
-ms@^0.7.1, ms@0.7.2:
- version "0.7.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
-
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
+ms@0.7.2, ms@^0.7.1:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
+
muri@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/muri/-/muri-1.2.0.tgz#b86383c902920b09ebe62af0e75c94de5f33cd3d"
@@ -5015,7 +5023,7 @@ mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
-nan@2.5.0:
+nan@2.5.0, nan@^2.3.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.0.tgz#aa8f1e34531d807e9e27755b234b4a6ec0c152a8"
@@ -5212,15 +5220,15 @@ nodemon@^1.11.0:
undefsafe "0.0.3"
update-notifier "0.5.0"
-nopt@~1.0.10:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
+nopt@3.x, nopt@~3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
dependencies:
abbrev "1"
-nopt@~3.0.6, nopt@3.x:
- version "3.0.6"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+nopt@~1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
dependencies:
abbrev "1"
@@ -5366,7 +5374,7 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
-optimist@^0.6.1, optimist@>=0.3.5, optimist@0.6.1:
+optimist@0.6.1, optimist@>=0.3.5, optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
dependencies:
@@ -5542,16 +5550,16 @@ 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-to-regexp@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
dependencies:
isarray "0.0.1"
-path-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"
@@ -5857,33 +5865,33 @@ postcss-mixins@^2.1.0:
postcss "^5.0.10"
postcss-simple-vars "^1.0.1"
-postcss-modules-extract-imports@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
- dependencies:
- postcss "^5.0.4"
-
postcss-modules-extract-imports@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.0.tgz#5b07f368e350cda6fd5c8844b79123a7bd3e37be"
dependencies:
postcss "^5.0.4"
-postcss-modules-local-by-default@^1.0.1, postcss-modules-local-by-default@1.1.1:
+postcss-modules-extract-imports@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-modules-local-by-default@1.1.1, postcss-modules-local-by-default@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce"
dependencies:
css-selector-tokenizer "^0.6.0"
postcss "^5.0.4"
-postcss-modules-scope@^1.0.0, postcss-modules-scope@1.0.2:
+postcss-modules-scope@1.0.2, postcss-modules-scope@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29"
dependencies:
css-selector-tokenizer "^0.6.0"
postcss "^5.0.4"
-postcss-modules-values@^1.1.0, postcss-modules-values@1.2.2:
+postcss-modules-values@1.2.2, postcss-modules-values@^1.1.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1"
dependencies:
@@ -6049,6 +6057,14 @@ postcss-zindex@^2.0.1:
postcss "^5.0.4"
uniqs "^2.0.0"
+postcss@5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.1.2.tgz#bd84886a66bcad489afaf7c673eed5ef639551e2"
+ dependencies:
+ js-base64 "^2.1.9"
+ source-map "^0.5.6"
+ supports-color "^3.1.2"
+
postcss@^5.0.0, postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.19, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.1.2, postcss@^5.2.11, postcss@^5.2.4, postcss@^5.2.5, postcss@^5.2.9:
version "5.2.11"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.11.tgz#ff29bcd6d2efb98bfe08a022055ec599bbe7b761"
@@ -6058,14 +6074,6 @@ postcss@^5.0.0, postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.
source-map "^0.5.6"
supports-color "^3.2.3"
-postcss@5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.1.2.tgz#bd84886a66bcad489afaf7c673eed5ef639551e2"
- dependencies:
- js-base64 "^2.1.9"
- source-map "^0.5.6"
- supports-color "^3.1.2"
-
pre-git@^3.10.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/pre-git/-/pre-git-3.12.0.tgz#8291899a15ba86f9cdaa8ca180e47096deff8dc3"
@@ -6130,7 +6138,7 @@ process@^0.11.0:
version "0.11.9"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1"
-progress@^1.1.8, progress@1.1.8:
+progress@1.1.8, progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
@@ -6283,22 +6291,18 @@ pug@^2.0.0-beta3:
pug-runtime "^2.0.3"
pug-strip-comments "^1.0.2"
-punycode@^1.2.4, punycode@^1.4.1, punycode@1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
-
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+punycode@1.4.1, punycode@^1.2.4, punycode@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
pym.js@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/pym.js/-/pym.js-1.1.1.tgz#3b5eb9e8499e5c95e5cb8fe29888ea7edf10a507"
-q@^1.1.2:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
-
q@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/q/-/q-1.1.2.tgz#6357e291206701d99f197ab84e57e8ad196f2a89"
@@ -6311,7 +6315,15 @@ q@2.0.3:
pop-iterate "^1.0.1"
weak-map "^1.0.5"
-qs@^6.1.0, qs@^6.2.0, qs@6.2.1:
+q@^1.1.2:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
+
+qs@6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b"
+
+qs@6.2.1, qs@^6.1.0, qs@^6.2.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625"
@@ -6319,10 +6331,6 @@ qs@~6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442"
-qs@6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b"
-
query-string@^4.1.0, query-string@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.1.tgz#54baada6713eafc92be75c47a731f2ebd09cd11d"
@@ -6342,14 +6350,14 @@ quote@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/quote/-/quote-0.4.0.tgz#10839217f6c1362b89194044d29b233fd7f32f01"
-ramda@^0.22.1:
- version "0.22.1"
- resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.22.1.tgz#031da0c3df417c5b33c96234757eb37033f36a0e"
-
ramda@0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.9.1.tgz#cc914dc3a82c608d003090203787c3f6826c1d87"
+ramda@^0.22.1:
+ version "0.22.1"
+ resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.22.1.tgz#031da0c3df417c5b33c96234757eb37033f36a0e"
+
random-bytes@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
@@ -6405,7 +6413,7 @@ react-apollo@^0.10.0:
optionalDependencies:
react-dom "0.14.x || 15.* || ^15.0.0"
-react-dom@^15.3.1, "react-dom@0.14.x || 15.* || ^15.0.0", react-dom@15.3.2:
+"react-dom@0.14.x || 15.* || ^15.0.0", react-dom@15.3.2, react-dom@^15.3.1:
version "15.3.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f"
@@ -6467,7 +6475,7 @@ react-tagsinput@^3.14.0:
version "3.14.0"
resolved "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.14.0.tgz#6729f8d5b313ed8fbb35496205ec2a97654af6bc"
-react@^15.3.1, react@15.3.2:
+react@15.3.2, react@^15.3.1:
version "15.3.2"
resolved "https://registry.yarnpkg.com/react/-/react-15.3.2.tgz#a7bccd2fee8af126b0317e222c28d1d54528d09e"
dependencies:
@@ -6503,7 +6511,7 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
-readable-stream@^1.1.7, readable-stream@1.1, readable-stream@1.1.x:
+readable-stream@1.1, readable-stream@1.1.x, readable-stream@^1.1.7:
version "1.1.13"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e"
dependencies:
@@ -6512,7 +6520,7 @@ readable-stream@^1.1.7, readable-stream@1.1, readable-stream@1.1.x:
isarray "0.0.1"
string_decoder "~0.10.x"
-readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.2.2, readable-stream@2:
+readable-stream@2, readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e"
dependencies:
@@ -6524,7 +6532,7 @@ readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
-readable-stream@^2.0.5, readable-stream@~2.1.4, readable-stream@2.1.5:
+readable-stream@2.1.5, readable-stream@^2.0.5, readable-stream@~2.1.4:
version "2.1.5"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0"
dependencies:
@@ -6560,15 +6568,6 @@ readline2@^1.0.1:
is-fullwidth-code-point "^1.0.0"
mute-stream "0.0.5"
-recast@^0.11.17:
- version "0.11.20"
- resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.20.tgz#2cb9bec269c03b36d0598118a936cd0a293ca3f3"
- dependencies:
- ast-types "0.9.4"
- esprima "~3.1.0"
- private "~0.1.5"
- source-map "~0.5.0"
-
recast@0.10.33:
version "0.10.33"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697"
@@ -6578,6 +6577,15 @@ recast@0.10.33:
private "~0.1.5"
source-map "~0.5.0"
+recast@^0.11.17:
+ version "0.11.20"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.20.tgz#2cb9bec269c03b36d0598118a936cd0a293ca3f3"
+ dependencies:
+ ast-types "0.9.4"
+ esprima "~3.1.0"
+ private "~0.1.5"
+ source-map "~0.5.0"
+
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
@@ -6596,7 +6604,7 @@ redis@^0.12.1:
version "0.12.1"
resolved "https://registry.yarnpkg.com/redis/-/redis-0.12.1.tgz#64df76ad0fc8acebaebd2a0645e8a48fac49185e"
-redis@^2.1.0, redis@^2.6.3, redis@~2.6.0-2:
+redis@^2.1.0, redis@^2.6.5, redis@~2.6.0-2:
version "2.6.5"
resolved "https://registry.yarnpkg.com/redis/-/redis-2.6.5.tgz#87c1eff4a489f94b70871f3d08b6988f23a95687"
dependencies:
@@ -6741,7 +6749,7 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
-request@^2.55.0, request@^2.74.0, request@^2.79.0, request@2.79.0:
+request@2.79.0, request@^2.55.0, request@^2.74.0, request@^2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
dependencies:
@@ -6766,13 +6774,6 @@ request@^2.55.0, request@^2.74.0, request@^2.79.0, request@2.79.0:
tunnel-agent "~0.4.1"
uuid "^3.0.0"
-require_optional@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.0.tgz#52a86137a849728eb60a55533617f8f914f59abf"
- dependencies:
- resolve-from "^2.0.0"
- semver "^5.1.0"
-
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -6796,6 +6797,13 @@ require-uncached@^1.0.2:
caller-path "^0.1.0"
resolve-from "^1.0.0"
+require_optional@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.0.tgz#52a86137a849728eb60a55533617f8f914f59abf"
+ dependencies:
+ resolve-from "^2.0.0"
+ semver "^5.1.0"
+
resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
@@ -6832,7 +6840,7 @@ right-align@^0.1.1:
dependencies:
align-text "^0.1.1"
-rimraf@^2.2.8, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2:
+rimraf@2, rimraf@^2.2.8, rimraf@^2.4.3, rimraf@^2.4.4, 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:
@@ -6874,14 +6882,14 @@ safe-buffer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
-sax@^1.1.4, sax@~1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
-
sax@0.5.x:
version "0.5.8"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
+sax@^1.1.4, sax@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
+
selenium-standalone@^5.11.2:
version "5.11.2"
resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-5.11.2.tgz#724ccaa72fb26f3711e0e20989e478c4133df844"
@@ -6908,18 +6916,18 @@ semver-regex@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-1.0.0.tgz#92a4969065f9c70c694753d55248fc68f8f652c9"
-semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5":
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
-semver@~5.0.1:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
-
semver@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.0.tgz#85f2cf8550465c4df000cf7d86f6b054106ab9e5"
+semver@~5.0.1:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
+
send@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/send/-/send-0.14.2.tgz#39b0438b3f510be5dc6f667a11f71689368cdeef"
@@ -6969,6 +6977,10 @@ sha.js@^2.3.6:
dependencies:
inherits "^2.0.1"
+shelljs@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.0.tgz#ce1ed837b4b0e55b5ec3dab84251ab9dbdc0c7ec"
+
shelljs@^0.7.0, shelljs@^0.7.5:
version "0.7.6"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad"
@@ -6977,10 +6989,6 @@ shelljs@^0.7.0, shelljs@^0.7.5:
interpret "^1.0.0"
rechoir "^0.6.2"
-shelljs@0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.0.tgz#ce1ed837b4b0e55b5ec3dab84251ab9dbdc0c7ec"
-
sigmund@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
@@ -7055,7 +7063,7 @@ socks-proxy-agent@2:
extend "3"
socks "~1.1.5"
-socks@~1.1.5, socks@1.1.9:
+socks@1.1.9, socks@~1.1.5:
version "1.1.9"
resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691"
dependencies:
@@ -7078,7 +7086,13 @@ source-map-support@^0.4.2:
dependencies:
source-map "^0.5.3"
-source-map@^0.4.4, source-map@0.4.x:
+source-map@0.1.x:
+ version "0.1.43"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
+ dependencies:
+ amdefine ">=0.0.4"
+
+source-map@0.4.x, source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
@@ -7094,12 +7108,6 @@ source-map@~0.2.0:
dependencies:
amdefine ">=0.0.4"
-source-map@0.1.x:
- version "0.1.43"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
- 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"
@@ -7200,10 +7208,6 @@ strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
-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-hash@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.1.tgz#8e85bed291e0763b8f6809d9c3368fea048db3dc"
@@ -7229,6 +7233,10 @@ string-width@^2.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^3.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"
+
stringmap@~0.2.2:
version "0.2.2"
resolved "http://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1"
@@ -7277,7 +7285,7 @@ style-loader@^0.13.1:
dependencies:
loader-utils "^0.2.7"
-stylus@~0.54.5, stylus@0.54.5:
+stylus@0.54.5, stylus@~0.54.5:
version "0.54.5"
resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79"
dependencies:
@@ -7316,6 +7324,16 @@ supertest@^2.0.1:
methods "1.x"
superagent "^2.0.0"
+supports-color@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e"
+
+supports-color@3.1.2, supports-color@^3.1.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
+ dependencies:
+ has-flag "^1.0.0"
+
supports-color@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"
@@ -7324,22 +7342,12 @@ 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.0, supports-color@3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
- dependencies:
- has-flag "^1.0.0"
-
supports-color@^3.1.2, supports-color@^3.2.3:
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@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e"
-
svgo@^0.7.0:
version "0.7.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
@@ -7427,7 +7435,7 @@ text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
-through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.8, through@2:
+through@2, through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -7564,14 +7572,14 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
-type-detect@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
-
type-detect@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822"
+type-detect@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
+
type-is@~1.6.14:
version "1.6.14"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2"
@@ -7608,7 +7616,7 @@ uid-number@~0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
-uid-safe@~2.1.3, uid-safe@2.1.3:
+uid-safe@2.1.3, uid-safe@~2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.3.tgz#077e264a00b3187936b270bb7376a26473631071"
dependencies:
@@ -7645,7 +7653,7 @@ uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
-unpipe@~1.0.0, unpipe@1.0.0:
+unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@@ -7682,7 +7690,7 @@ 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:
+util@0.10.3, util@^0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
@@ -7802,7 +7810,7 @@ whatwg-encoding@^1.0.1:
dependencies:
iconv-lite "0.4.13"
-whatwg-fetch@^2.0.0, whatwg-fetch@>=0.10.0:
+whatwg-fetch@>=0.10.0, whatwg-fetch@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e"
@@ -7827,24 +7835,28 @@ which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
-which@^1.1.1:
- version "1.2.12"
- resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192"
- dependencies:
- isexe "^1.1.1"
-
which@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.1.1.tgz#9ce512459946166e12c083f08ec073380fc8cbbb"
dependencies:
is-absolute "^0.1.7"
+which@^1.1.1:
+ version "1.2.12"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192"
+ dependencies:
+ isexe "^1.1.1"
+
wide-align@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad"
dependencies:
string-width "^1.0.1"
+window-size@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+
window-size@^0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
@@ -7853,10 +7865,6 @@ window-size@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
-window-size@0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
-
with@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe"
@@ -7864,10 +7872,14 @@ with@^5.0.0:
acorn "^3.1.0"
acorn-globals "^3.0.0"
-word-wrap@^1.0.3, word-wrap@1.1.0:
+word-wrap@1.1.0, word-wrap@^1.0.3:
version "1.1.0"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.1.0.tgz#356153d61d10610d600785c5d701288e0ae764a6"
+wordwrap@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
+
wordwrap@^1.0.0, wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
@@ -7876,10 +7888,6 @@ wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
-wordwrap@0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
-
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
@@ -8037,4 +8045,3 @@ yauzl@^2.5.0:
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.0.1"
-
From e5c108f8fb3016286d332898360f41270be9428b Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Fri, 10 Mar 2017 13:54:33 -0700
Subject: [PATCH 23/56] make notifications wider
---
client/coral-embed/src/index.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/coral-embed/src/index.js b/client/coral-embed/src/index.js
index db4cbf37e..21b159e8c 100644
--- a/client/coral-embed/src/index.js
+++ b/client/coral-embed/src/index.js
@@ -13,7 +13,7 @@ const snackbarStyles = {
color: '#fff',
borderRadius: '3px 3px 0 0',
textAlign: 'center',
- maxWidth: '300px',
+ maxWidth: '400px',
left: '50%',
opacity: 0,
transform: 'translate(-50%, 20px)',
From 894395bc65fd7a9cc726d40ad4f0b69f0c33dd53 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Mon, 13 Mar 2017 09:43:47 -0600
Subject: [PATCH 24/56] Resolved #357
---
Dockerfile | 2 +-
package.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index fc0b3aca2..810d2a71f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:7.6
+FROM node:7
# Create app directory
RUN mkdir -p /usr/src/app
diff --git a/package.json b/package.json
index 373e2adde..72b2e9b19 100644
--- a/package.json
+++ b/package.json
@@ -167,6 +167,6 @@
"webpack": "^2.2.1"
},
"engines": {
- "node": "~7.6.0"
+ "node": "^7.7.0"
}
}
From 258fd8b25aebafe1a55cb0be229915cdc272b123 Mon Sep 17 00:00:00 2001
From: gaba
Date: Mon, 20 Mar 2017 07:33:34 -0700
Subject: [PATCH 25/56] Initial implementation at plugins
---
.gitignore | 4 ++
Dockerfile | 7 ++-
graph/hooks.js | 115 +++++++++++++++++++++++++++++++++++++++
graph/loaders/index.js | 31 ++++++++---
graph/mutators/index.js | 30 ++++++++--
graph/resolvers/index.js | 21 ++++++-
graph/schema.js | 12 +++-
graph/typeDefs.js | 22 +++++++-
package.json | 1 +
plugins.js | 81 +++++++++++++++++++++++++++
yarn.lock | 33 ++++++++++-
11 files changed, 334 insertions(+), 23 deletions(-)
create mode 100644 graph/hooks.js
create mode 100644 plugins.js
diff --git a/.gitignore b/.gitignore
index fbc3c54d3..d91635c94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,7 @@ dump.rdb
coverage/
.tags
.tags1
+
+# remove plugin folders
+plugins
+plugins.json
diff --git a/Dockerfile b/Dockerfile
index 810d2a71f..eb3cc9c60 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,9 +12,12 @@ EXPOSE 5000
# Install app dependencies
COPY package.json yarn.lock /usr/src/app/
-RUN yarn install --production
+RUN yarn install
# Bundle app source
COPY . /usr/src/app
-CMD [ "yarn", "start" ]
+# Build static assets
+RUN yarn build
+
+CMD ["yarn", "start"]
diff --git a/graph/hooks.js b/graph/hooks.js
new file mode 100644
index 000000000..152e02b9b
--- /dev/null
+++ b/graph/hooks.js
@@ -0,0 +1,115 @@
+const {forEachField} = require('graphql-tools');
+const debug = require('debug')('talk:graph:schema');
+
+/**
+ * XXX taken from graphql-js: src/execution/execute.js, because that function
+ * is not exported
+ *
+ * If a resolve function is not given, then a default resolve behavior is used
+ * which takes the property of the source object of the same name as the field
+ * and returns it as the result, or if it's a function, returns the result
+ * of calling that function.
+ */
+const defaultResolveFn = (source, args, context, {fieldName}) => {
+
+ // ensure source is a value for which property access is acceptable.
+ if (typeof source === 'object' || typeof source === 'function') {
+ const property = source[fieldName];
+ if (typeof property === 'function') {
+ return source[fieldName](args, context);
+ }
+ return property;
+ }
+};
+
+/**
+ * Decorates the schema with before and after hooks as provided by the Plugin
+ * Manager.
+ * @param {GraphQLSchema} schema the schema to decorate
+ * @param {Array} hooks hooks to apply to the schema
+ * @return {void}
+ */
+const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeName, fieldName) => {
+
+ // Pull out the before/after hooks from the available hooks.
+ const {
+ before,
+ after
+ } = hooks
+
+ // Only grab hooks that are associated with thie field and typeName.
+ .filter(({hooks}) => (typeName in hooks) && (fieldName in hooks[typeName]))
+
+ // Grab the hooks we need.
+ .map(({plugin, hooks}) => ({plugin, hooks: hooks[typeName][fieldName]}))
+
+ // Combine the before/after hooks from each plugin into an array we can
+ // execute.
+ .reduce((acc, {plugin, hooks}) => {
+
+ // Itterate over the hooks on the fields and look at it with a switch
+ // block to check for misconfigured plugins.
+ Object.keys(hooks).forEach((hook) => {
+ switch (hook) {
+ case 'before':
+ debug(`adding before hook to resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
+
+ if (typeof hooks.before !== 'function') {
+ throw new Error(`expected ${hook} hook on resolver ${typeName}.${fieldName} from plugin '${plugin.name}' to be a function, it was a '${typeof hooks[hook]}'`);
+ }
+
+ acc.before.push(hooks.before);
+ break;
+ case 'after':
+ debug(`adding after hook to resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
+
+ if (typeof hooks.after !== 'function') {
+ throw new Error(`expected ${hook} hook on resolver ${typeName}.${fieldName} from plugin '${plugin.name}' to be a function, it was a '${typeof hooks[hook]}'`);
+ }
+
+ acc.after.unshift(hooks.after);
+ break;
+ default:
+ throw new Error(`invalid hook '${hook}' on resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
+ }
+ });
+
+ return acc;
+ }, {
+ before: [],
+ after: []
+ });
+
+ // If we have no hooks to add here, don't try to modify anything.
+ if (before.length === 0 && after.length === 0) {
+ return;
+ }
+
+ // Cache the original resolve function, this emulates the beheviour found in
+ // graphql-tools: https://github.com/apollographql/graphql-tools/blob/6e9cc124b10d673448386041e6c3d058bc205a02/src/schemaGenerator.ts#L423-L425
+ let resolve = field.resolve;
+ if (typeof resolve === 'undefined') {
+ resolve = defaultResolveFn;
+ }
+
+ // Apply our async resolve function which will fire all before functions (and
+ // wait until they resolve) followed by waiting for the response and then
+ // firing their after hooks. Lastly, we respond with the result of the
+ // original resolver.
+ field.resolve = async (obj, args, context, info) => {
+
+ // Issue all before hooks before we resolve the field.
+ await Promise.all(before.map(async (before) => await before(obj, args, context, info)));
+
+ // Resolve the field.
+ let result = await resolve(obj, args, context, info);
+
+ // Insure all after hooks after we've resolved the field with the result
+ // passed in as the fifth argument.
+ return await after.reduce(async (result, after) => await after(obj, args, context, info, result), result);
+ };
+});
+
+module.exports = {
+ decorateWithHooks
+};
diff --git a/graph/loaders/index.js b/graph/loaders/index.js
index 5b1894b65..40e723918 100644
--- a/graph/loaders/index.js
+++ b/graph/loaders/index.js
@@ -1,4 +1,5 @@
const _ = require('lodash');
+const debug = require('debug')('talk:graph:loaders');
const Actions = require('./actions');
const Assets = require('./assets');
@@ -7,6 +8,27 @@ const Metrics = require('./metrics');
const Settings = require('./settings');
const Users = require('./users');
+const plugins = require('../../plugins');
+
+let loaders = [
+
+ // Load the core loaders.
+ Actions,
+ Assets,
+ Comments,
+ Metrics,
+ Settings,
+ Users,
+
+ // Load the plugin loaders from the manager.
+ ...plugins
+ .get('server', 'loaders').map(({plugin, loaders}) => {
+ debug(`added plugin '${plugin.name}'`);
+
+ return loaders;
+ })
+];
+
/**
* Creates a set of loaders based on a GraphQL context.
* @param {Object} context the context of the GraphQL request
@@ -15,14 +37,7 @@ const Users = require('./users');
module.exports = (context) => {
// We need to return an object to be accessed.
- return _.merge(...[
- Actions,
- Assets,
- Comments,
- Metrics,
- Settings,
- Users
- ].map((loaders) => {
+ return _.merge(...loaders.map((loaders) => {
// Each loader is a function which takes the context.
return loaders(context);
diff --git a/graph/mutators/index.js b/graph/mutators/index.js
index b799cf83d..e3df4aefb 100644
--- a/graph/mutators/index.js
+++ b/graph/mutators/index.js
@@ -1,17 +1,37 @@
const _ = require('lodash');
+const debug = require('debug')('talk:graph:mutators');
const Comment = require('./comment');
const Action = require('./action');
const User = require('./user');
+const plugins = require('../../plugins');
+
+let mutators = [
+
+ // Load in the core mutators.
+ Comment,
+ Action,
+ User,
+
+ // Load the plugin mutators from the manager.
+ ...plugins
+ .get('server', 'mutators').map(({plugin, mutators}) => {
+ debug(`added plugin '${plugin.name}'`);
+
+ return mutators;
+ })
+];
+
+/**
+ * Creates a set of mutators based on a GraphQL context.
+ * @param {Object} context the context of the GraphQL request
+ * @return {Object} object of mutators
+ */
module.exports = (context) => {
// We need to return an object to be accessed.
- return _.merge(...[
- Comment,
- Action,
- User,
- ].map((mutators) => {
+ return _.merge(...mutators.map((mutators) => {
// Each set of mutators is a function which takes the context.
return mutators(context);
diff --git a/graph/resolvers/index.js b/graph/resolvers/index.js
index 3bd8c571d..7e165f2db 100644
--- a/graph/resolvers/index.js
+++ b/graph/resolvers/index.js
@@ -1,3 +1,6 @@
+const _ = require('lodash');
+const debug = require('debug')('talk:graph:resolvers');
+
const ActionSummary = require('./action_summary');
const Action = require('./action');
const AssetActionSummary = require('./asset_action_summary');
@@ -17,7 +20,10 @@ const UserError = require('./user_error');
const User = require('./user');
const ValidationUserError = require('./validation_user_error');
-module.exports = {
+const plugins = require('../../plugins');
+
+// Provide the core resolvers.
+let resolvers = {
ActionSummary,
Action,
AssetActionSummary,
@@ -37,3 +43,16 @@ module.exports = {
User,
ValidationUserError,
};
+
+/**
+ * Plugin support requires that we merge in existing resolvers with our new
+ * plugin based ones. This allows plugins to extend existing resolvers as well
+ * as provide new ones.
+ */
+resolvers = plugins.get('server', 'resolvers').reduce((resolvers, {plugin}) => {
+ debug(`added plugin '${plugin.name}'`);
+
+ return _.merge(resolvers, plugin.resolvers);
+}, resolvers);
+
+module.exports = resolvers;
diff --git a/graph/schema.js b/graph/schema.js
index 2fca1a87f..c927d8c32 100644
--- a/graph/schema.js
+++ b/graph/schema.js
@@ -1,11 +1,17 @@
-const tools = require('graphql-tools');
-const maskErrors = require('graphql-errors').maskErrors;
+const {makeExecutableSchema} = require('graphql-tools');
+const {maskErrors} = require('graphql-errors');
+const {decorateWithHooks} = require('./hooks');
+const plugins = require('../plugins');
const resolvers = require('./resolvers');
const typeDefs = require('./typeDefs');
-const schema = tools.makeExecutableSchema({typeDefs, resolvers});
+const schema = makeExecutableSchema({typeDefs, resolvers});
+// Plugin to the schema level resolvers to provide an before/after hook.
+decorateWithHooks(schema, plugins.get('server', 'hooks'));
+
+// If we are in production mode, don't show server errors to the front end.
if (process.env.NODE_ENV === 'production') {
// Mask errors that are thrown if we are in a production environment.
diff --git a/graph/typeDefs.js b/graph/typeDefs.js
index 7ac982269..5b07d0f2d 100644
--- a/graph/typeDefs.js
+++ b/graph/typeDefs.js
@@ -4,8 +4,26 @@
const fs = require('fs');
const path = require('path');
+const {mergeStrings} = require('gql-merge');
+const debug = require('debug')('talk:graph:typeDefs');
+const plugins = require('../plugins');
-// Load the typeDefs from the graphql file.
-const typeDefs = fs.readFileSync(path.join(__dirname, 'typeDefs.graphql'), 'utf8');
+/**
+ * Plugin support requires us to merge the type definitions from the loaded
+ * graphql tags, this gives us the ability to extend any portion of the
+ * available graph.
+ */
+const typeDefs = mergeStrings([
+
+ // Load the core graph definitions from the filesystem.
+ fs.readFileSync(path.join(__dirname, 'typeDefs.graphql'), 'utf8'),
+
+ // Load the plugin definitions from the manager.
+ ...plugins.get('server', 'typeDefs').map(({plugin, typeDefs}) => {
+ debug(`added plugin '${plugin.name}'`);
+
+ return typeDefs;
+ })
+]);
module.exports = typeDefs;
diff --git a/package.json b/package.json
index 72b2e9b19..ec94e1db3 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
"env-rewrite": "^1.0.2",
"express": "^4.14.0",
"express-session": "^1.14.2",
+ "gql-merge": "^0.0.4",
"graphql": "^0.8.2",
"graphql-errors": "^2.1.0",
"graphql-server-express": "^0.5.0",
diff --git a/plugins.js b/plugins.js
new file mode 100644
index 000000000..7713e0b8f
--- /dev/null
+++ b/plugins.js
@@ -0,0 +1,81 @@
+const fs = require('fs');
+const path = require('path');
+
+let plugins = {};
+
+// Try to parse the plugins.json file, logging out an error if the plugins.json
+// file isn't loaded, but continuing. Else, like a parsing error, throw it and
+// crash the program.
+try {
+ plugins = JSON.parse(fs.readFileSync(path.join(__dirname, 'plugins.json'), 'utf8'));
+} catch (err) {
+ if (err.code === 'ENOENT') {
+ console.error('plugins.json not found, plugins will not be active');
+ } else {
+ throw err;
+ }
+}
+
+/**
+ * Stores a reference to a section for a section of Plugins.
+ */
+class PluginSection {
+ constructor(plugin_names) {
+ this.plugins = plugin_names.map((plugin_name) => {
+ let plugin = require(`./plugins/${plugin_name}`);
+
+ // Ensure we have a default plugin name, but allow the name to be
+ // overrided by the plugin.
+ plugin.name = plugin.name || plugin_name;
+
+ return plugin;
+ });
+ }
+
+ /**
+ * This itterates over the section to provide all plugin hooks that are
+ * available.
+ */
+ hook(hook) {
+ return this.plugins
+ .filter((plugin) => hook in plugin)
+ .map((plugin) => ({plugin, [hook]: plugin[hook]}));
+ }
+}
+
+const NullPluginSection = new PluginSection([]);
+
+/**
+ * Stores references to all the plugins available on the application.
+ */
+class PluginManager {
+ constructor(plugins) {
+ this.sections = {};
+
+ for (let section in plugins) {
+ this.sections[section] = new PluginSection(plugins[section]);
+ }
+ }
+
+ /**
+ * Utility function which combines the Plugins.section and PluginSection.hook
+ * calls.
+ */
+ get(section, hook) {
+ return this.section(section).hook(hook);
+ }
+
+ /**
+ * Returns the named section if it exists, otherwise it returns an empty
+ * plugin section.
+ */
+ section(section) {
+ if (section in this.sections) {
+ return this.sections[section];
+ }
+
+ return NullPluginSection;
+ }
+}
+
+module.exports = new PluginManager(plugins);
diff --git a/yarn.lock b/yarn.lock
index 32f0a4c2f..fdf36c0e3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1133,7 +1133,7 @@ bluebird@3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.3.4.tgz#f780fe43e1a7a6510f67abd7d0d79533a40ddde6"
-bluebird@3.4.6:
+bluebird@3.4.6, bluebird@^3.4.6:
version "3.4.6"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f"
@@ -3230,7 +3230,7 @@ glob@7.0.5, glob@7.0.x:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
+glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
dependencies:
@@ -3302,6 +3302,29 @@ got@^3.2.0:
read-all-stream "^3.0.0"
timed-out "^2.0.0"
+gql-format@^0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/gql-format/-/gql-format-0.0.4.tgz#8237de7647de37f00aba2d0073abf6087e2da119"
+ dependencies:
+ commander "^2.9.0"
+ graphql "^0.7.2"
+
+gql-merge@^0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/gql-merge/-/gql-merge-0.0.4.tgz#1cb1d4cc8bb8768172cf08a45c5a4fbd0ecedc9f"
+ dependencies:
+ commander "^2.9.0"
+ gql-format "^0.0.4"
+ gql-utils "^0.0.2"
+ graphql "^0.7.2"
+
+gql-utils@^0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/gql-utils/-/gql-utils-0.0.2.tgz#962b3c1b34bf965a45d2564a93d3072921f61e86"
+ dependencies:
+ bluebird "^3.4.6"
+ glob "^7.1.1"
+
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -3369,6 +3392,12 @@ graphql-tools@^0.9.0:
optionalDependencies:
"@types/graphql" "^0.8.5"
+graphql@^0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.7.2.tgz#cc894a32823399b8a0cb012b9e9ecad35cd00f72"
+ dependencies:
+ iterall "1.0.2"
+
graphql@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.8.2.tgz#eb1bb524b38104bbf2c9157f9abc67db2feba7d2"
From 7bf1510a8b50ad6121ded754c0546dbbc1eaca63 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Wed, 15 Mar 2017 09:21:24 -0600
Subject: [PATCH 26/56] Changed before/after to pre/post
---
graph/hooks.js | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/graph/hooks.js b/graph/hooks.js
index 152e02b9b..17d46ff63 100644
--- a/graph/hooks.js
+++ b/graph/hooks.js
@@ -23,7 +23,7 @@ const defaultResolveFn = (source, args, context, {fieldName}) => {
};
/**
- * Decorates the schema with before and after hooks as provided by the Plugin
+ * Decorates the schema with pre and post hooks as provided by the Plugin
* Manager.
* @param {GraphQLSchema} schema the schema to decorate
* @param {Array} hooks hooks to apply to the schema
@@ -31,10 +31,10 @@ const defaultResolveFn = (source, args, context, {fieldName}) => {
*/
const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeName, fieldName) => {
- // Pull out the before/after hooks from the available hooks.
+ // Pull out the pre/post hooks from the available hooks.
const {
- before,
- after
+ pre,
+ post
} = hooks
// Only grab hooks that are associated with thie field and typeName.
@@ -43,7 +43,7 @@ const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeNa
// Grab the hooks we need.
.map(({plugin, hooks}) => ({plugin, hooks: hooks[typeName][fieldName]}))
- // Combine the before/after hooks from each plugin into an array we can
+ // Combine the pre/post hooks from each plugin into an array we can
// execute.
.reduce((acc, {plugin, hooks}) => {
@@ -51,23 +51,23 @@ const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeNa
// block to check for misconfigured plugins.
Object.keys(hooks).forEach((hook) => {
switch (hook) {
- case 'before':
- debug(`adding before hook to resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
+ case 'pre':
+ debug(`adding pre hook to resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
- if (typeof hooks.before !== 'function') {
+ if (typeof hooks.pre !== 'function') {
throw new Error(`expected ${hook} hook on resolver ${typeName}.${fieldName} from plugin '${plugin.name}' to be a function, it was a '${typeof hooks[hook]}'`);
}
- acc.before.push(hooks.before);
+ acc.pre.push(hooks.pre);
break;
- case 'after':
- debug(`adding after hook to resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
+ case 'post':
+ debug(`adding post hook to resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
- if (typeof hooks.after !== 'function') {
+ if (typeof hooks.post !== 'function') {
throw new Error(`expected ${hook} hook on resolver ${typeName}.${fieldName} from plugin '${plugin.name}' to be a function, it was a '${typeof hooks[hook]}'`);
}
- acc.after.unshift(hooks.after);
+ acc.post.unshift(hooks.post);
break;
default:
throw new Error(`invalid hook '${hook}' on resolver ${typeName}.${fieldName} from plugin '${plugin.name}'`);
@@ -76,12 +76,12 @@ const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeNa
return acc;
}, {
- before: [],
- after: []
+ pre: [],
+ post: []
});
// If we have no hooks to add here, don't try to modify anything.
- if (before.length === 0 && after.length === 0) {
+ if (pre.length === 0 && post.length === 0) {
return;
}
@@ -92,21 +92,21 @@ const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeNa
resolve = defaultResolveFn;
}
- // Apply our async resolve function which will fire all before functions (and
+ // Apply our async resolve function which will fire all pre functions (and
// wait until they resolve) followed by waiting for the response and then
- // firing their after hooks. Lastly, we respond with the result of the
+ // firing their post hooks. Lastly, we respond with the result of the
// original resolver.
field.resolve = async (obj, args, context, info) => {
- // Issue all before hooks before we resolve the field.
- await Promise.all(before.map(async (before) => await before(obj, args, context, info)));
+ // Issue all pre hooks before we resolve the field.
+ await Promise.all(pre.map(async (pre) => await pre(obj, args, context, info)));
// Resolve the field.
let result = await resolve(obj, args, context, info);
- // Insure all after hooks after we've resolved the field with the result
+ // Insure all post hooks after we've resolved the field with the result
// passed in as the fifth argument.
- return await after.reduce(async (result, after) => await after(obj, args, context, info, result), result);
+ return await post.reduce(async (result, post) => await post(obj, args, context, info, result), result);
};
});
From 86f0ed80ea465ecade1dd7efda772db1484e9f24 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Wed, 15 Mar 2017 16:52:04 -0600
Subject: [PATCH 27/56] Added docs and added new context based plugins.
---
PLUGINS.md | 261 +++++++++++++++++++++++++++++++++++++++++++++++
graph/context.js | 29 ++++++
graph/hooks.js | 23 ++++-
3 files changed, 310 insertions(+), 3 deletions(-)
create mode 100644 PLUGINS.md
diff --git a/PLUGINS.md b/PLUGINS.md
new file mode 100644
index 000000000..ca3d47ce7
--- /dev/null
+++ b/PLUGINS.md
@@ -0,0 +1,261 @@
+# Talk Plugins
+
+Plugins for Talk can take various forms, currently we are only supporting server
+side plugins.
+
+## Plugin Registration: `plugins.json`
+
+All plugins must be registered in the root file `plugins.json`.
+
+The format for this file is thus:
+
+```js
+{
+ "server": [
+ "people"
+ ]
+}
+```
+
+Where we have a `server` key with an array of plugins that match the folder
+name in the `plugins/` folder. For example, the above `plugins.json` would
+require a plugin from `plugins/people`, which must provide a `index.js` file
+that returns an object that matches the Plugin Specification.
+
+## Server Plugins
+
+### Specification
+
+Each plugin should export a single object with all hooks available on it.
+
+_**Note: You will have access to the whole core and other plugin's typeDefs,
+context, loaders, mutators, resolvers, hooks. This is intentional, as it
+encourages composing plugins to merge functionality, like a Slack plugin which
+provides a Slack notify context function as well as having the loader for
+comments.**_
+
+The following are the hooks available:
+
+#### Field: `typeDefs`
+
+```graphql
+enum COLOUR {
+ RED
+ BLUE
+}
+
+type Person {
+ name: String!
+ colour: COLOUR!
+}
+
+type RootMutation {
+ createPerson(name: String!): Person
+}
+
+type RootQuery {
+ people: [Person!]
+}
+```
+
+Thanks to [gql-merge](https://www.npmjs.com/package/gql-merge) the contents of
+`typeDefs` should be a string that will be _merged_ with the existing type
+definitions. `enum`'s will be appended to, types will be appended, and new types
+will be added.
+
+#### Field: `context`
+
+```js
+{
+ Slack: (context) => ({
+ notify: (message) => {
+ // return a promise after we're done sending notifications.
+ }
+ })
+}
+```
+
+Any property provided here will be added to the context parameter available
+inside all resolvers, loaders, mutators, and of course, other context based
+plugins.
+
+The top level item must accept a context for the request which it should use to
+configure the context plugin before it would be mounted at `context.plugins`.
+This plugin above would mount at: `context.plugins.Slack`, or, if you're using
+[object destructuring](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), `{plugins: {Slack}}`.
+
+#### Field: `loaders`
+
+```js
+(context) => ({
+ People: {
+ load: () => db.people.find({user: context.user})
+ }
+})
+```
+
+Loaders should be provided as a function which returns a map which is used in
+the resolvers function. These must return a promise or a value.
+
+#### Field: `mutators`
+
+```js
+(context) => ({
+ People: {
+ create: (name) => {
+ return db.people.insert({user: context.user, name});
+ }
+ }
+})
+```
+
+Mutators should be provided as a function which returns a map which is used in
+the resolvers function. These must return a promise or a value.
+
+#### Field: `resolvers`
+
+```js
+{
+ Person: {
+ name(obj, args, context) {
+ return obj.name;
+ },
+ colour(obj, args, context) {
+ // Bill likes the colour red, everyone else likes blue.
+ return obj.name === 'bill' ? 'RED' : 'BLUE';
+ }
+ },
+ RootQuery: {
+ people(obj, args, {loaders: {People}}) {
+ return People.load();
+ }
+ },
+ RootMutation: {
+ createPerson(obj, {name}, {mutators: {People}}) {
+ return People.create(name);
+ }
+ }
+}
+```
+
+Should return a resolver map as described in the
+[Apollo Docs](http://dev.apollodata.com/tools/graphql-tools/resolvers.html#Resolver-map).
+
+This will merge with the existing resolvers in core and from previous plugins.
+
+#### Field: `hooks`
+
+```js
+{
+ RootMutation: {
+ createPerson: {
+ post(obj, args, {plugins: {Slack}}, person) {
+ if (!person) {
+ return person;
+ }
+
+ await Slack.notify(`A new person just was created with name ${person.name}`);
+
+ return person;
+ }
+ }
+ }
+}
+```
+
+Hooks here are pretty special, for each resolver field, you can specify a
+pre/post hook that will execute pre and post field resolution.
+
+If your post function accepts four parameters, then it can modify the field
+result. It is *required* that the function resolves a promise (or returns) with
+the modified value or simply the original if you didn't modify it.
+
+### Full Example
+
+Contents of `plugins.json`:
+
+```json
+{
+ "server": [
+ "people"
+ ]
+}
+```
+
+Located in `plugins/people/index.js`:
+
+```js
+module.exports = {
+ typeDefs: `
+ enum COLOUR {
+ RED
+ BLUE
+ }
+
+ type Person {
+ name: String!
+ colour: COLOUR!
+ }
+
+ type RootQuery {
+ people: [Person!]
+ }
+ `,
+ context: {
+ Slack: () => ({
+ notify: (message) => {
+ // return a promise after we're done sending notifications.
+ }
+ })
+ },
+ loaders: ({user}) => ({
+ People: {
+ load: () => db.people.find({user})
+ }
+ }),
+ mutators: ({user}) => ({
+ People: {
+ create: (name) => {
+ return db.people.insert({user, name});
+ }
+ }
+ }),
+ resolvers: {
+ Person: {
+ name(obj, args, context) {
+ return obj.name;
+ },
+ colour(obj, args, context) {
+ // Bill likes the colour red, everyone else likes blue.
+ return obj.name === 'bill' ? 'RED' : 'BLUE';
+ }
+ },
+ RootQuery: {
+ people(obj, args, {loaders: {People}}) {
+ return People.load();
+ }
+ },
+ RootMutation: {
+ createPerson(obj, {name}, {mutators: {People}}) {
+ return People.create(name);
+ }
+ }
+ },
+ hooks: {
+ RootMutation: {
+ createPerson: {
+ post(obj, args, {plugins: {Slack}}, person) {
+ if (!person) {
+ return person;
+ }
+
+ await Slack.notify(`A new person just was created with name ${person.name}`);
+
+ return person;
+ }
+ }
+ }
+ }
+};
+
+```
diff --git a/graph/context.js b/graph/context.js
index ce8f5eb83..432ab2081 100644
--- a/graph/context.js
+++ b/graph/context.js
@@ -1,6 +1,32 @@
const loaders = require('./loaders');
const mutators = require('./mutators');
+const plugins = require('../plugins');
+const debug = require('debug')('talk:graph:context');
+
+/**
+ * Contains the array of plugins that provide context to the server, these top
+ * level functions all need the context reference.
+ * @type {Array}
+ */
+const contextPlugins = plugins.get('server', 'context').map(({plugin, context}) => {
+ debug(`added plugin '${plugin.name}'`);
+ return {context};
+});
+
+/**
+ * This should itterate over the passed in plugins and load them all with the
+ * current graph context.
+ * @return {Object} the saturated plugins object
+ */
+const decorateContextPlugins = (context, contextPlugins) => contextPlugins.reduce((acc, plugin) => {
+ Object.keys(plugin.context).forEach((service) => {
+ acc[service] = plugin.context[service](context);
+ });
+
+ return acc;
+}, {});
+
/**
* Stores the request context.
*/
@@ -17,6 +43,9 @@ class Context {
// Create the mutators.
this.mutators = mutators(this);
+
+ // Decorate the plugin context.
+ this.plugins = decorateContextPlugins(this, contextPlugins);
}
}
diff --git a/graph/hooks.js b/graph/hooks.js
index 17d46ff63..3fe1306cf 100644
--- a/graph/hooks.js
+++ b/graph/hooks.js
@@ -99,14 +99,31 @@ const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeNa
field.resolve = async (obj, args, context, info) => {
// Issue all pre hooks before we resolve the field.
- await Promise.all(pre.map(async (pre) => await pre(obj, args, context, info)));
+ await Promise.all(pre.map((pre) => pre(obj, args, context, info)));
// Resolve the field.
- let result = await resolve(obj, args, context, info);
+ let result = resolve(obj, args, context, info);
// Insure all post hooks after we've resolved the field with the result
// passed in as the fifth argument.
- return await post.reduce(async (result, post) => await post(obj, args, context, info, result), result);
+ return await post.reduce(async (result, post) => {
+
+ // Wait for the accumulator to resolve before we continue.
+ result = await result;
+
+ // Check to see if this post function accepts a result, if it does, we
+ // expect that it modifies the result, otherwise, just fire the post hook,
+ // wait till it's done, then move onto the next hook.
+ if (post.length === 5) {
+ return await post(obj, args, context, info, result);
+ }
+
+ // Wait for the post hook to finish.
+ await post(obj, args, context, info);
+
+ // Return the result, which we already awaited for before.
+ return result;
+ }, result);
};
});
From 32af28bfdc425be6d3ce0de841d8fa135884baa5 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Wed, 15 Mar 2017 16:54:07 -0600
Subject: [PATCH 28/56] Fixed typo
---
PLUGINS.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/PLUGINS.md b/PLUGINS.md
index ca3d47ce7..1f8569029 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -197,6 +197,10 @@ module.exports = {
colour: COLOUR!
}
+ type RootMutation {
+ createPerson(name: String!): Person
+ }
+
type RootQuery {
people: [Person!]
}
From a7c946f9af800b550fe2f87b544e6dd08ccdb865 Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Thu, 16 Mar 2017 12:31:25 -0600
Subject: [PATCH 29/56] Fixed bug in docs
---
PLUGINS.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/PLUGINS.md b/PLUGINS.md
index 1f8569029..eeb033d5a 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -149,7 +149,7 @@ This will merge with the existing resolvers in core and from previous plugins.
{
RootMutation: {
createPerson: {
- post(obj, args, {plugins: {Slack}}, person) {
+ post: async (obj, args, {plugins: {Slack}}, person) {
if (!person) {
return person;
}
@@ -248,7 +248,7 @@ module.exports = {
hooks: {
RootMutation: {
createPerson: {
- post(obj, args, {plugins: {Slack}}, person) {
+ post: async (obj, args, {plugins: {Slack}}, person) {
if (!person) {
return person;
}
From ae4bae6cd2baae18737038d7eccf903a44c60d8c Mon Sep 17 00:00:00 2001
From: David Jay
Date: Thu, 9 Mar 2017 18:06:44 -0500
Subject: [PATCH 30/56] Scrolling to keep selected comments in view.
---
.../containers/ModerationQueue/ModerationContainer.js | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index e53162992..70fe0cdd5 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -3,6 +3,7 @@ import {connect} from 'react-redux';
import {compose} from 'react-apollo';
import key from 'keymaster';
import isEqual from 'lodash/isEqual';
+import styles from './components/styles.css';
import {modQueueQuery} from '../../graphql/queries';
import {banUser, setCommentStatus} from '../../graphql/mutations';
@@ -90,6 +91,15 @@ class ModerationContainer extends Component {
key.unbind('t');
}
+ componentDidUpdate(_, prevState) {
+
+ // If paging through using keybaord shortcuts, scroll the page to keep the selected
+ // comment in view.
+ if (prevState.selectedIndex !== this.state.selectedIndex) {
+ document.querySelector(`.${styles.selected}`).scrollIntoViewIfNeeded();
+ }
+ }
+
componentWillReceiveProps(nextProps) {
const {updateAssets} = this.props;
if(!isEqual(nextProps.data.assets, this.props.data.assets)) {
From 28b24ebb09854d50fea0b4b1357163c937cd427a Mon Sep 17 00:00:00 2001
From: David Erwin
Date: Thu, 16 Mar 2017 14:32:59 -0400
Subject: [PATCH 31/56] Fix bug in example
---
PLUGINS.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/PLUGINS.md b/PLUGINS.md
index eeb033d5a..20ba789be 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -248,7 +248,7 @@ module.exports = {
hooks: {
RootMutation: {
createPerson: {
- post: async (obj, args, {plugins: {Slack}}, person) {
+ post: async (obj, args, {plugins: {Slack}}, person) => {
if (!person) {
return person;
}
From 46e8d30e22af4e27adb978bff7fb08d321225b25 Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Mon, 13 Mar 2017 11:13:28 -0600
Subject: [PATCH 32/56] scrollIntoView will work in more browsers
---
.../src/containers/ModerationQueue/ModerationContainer.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index 70fe0cdd5..e254a75d6 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -96,7 +96,9 @@ class ModerationContainer extends Component {
// If paging through using keybaord shortcuts, scroll the page to keep the selected
// comment in view.
if (prevState.selectedIndex !== this.state.selectedIndex) {
- document.querySelector(`.${styles.selected}`).scrollIntoViewIfNeeded();
+
+ // the 'smooth' flag only works in FF as of March 2017
+ document.querySelector(`.${styles.selected}`).scrollIntoView({behavior: 'smooth'});
}
}
From c8a0ded4adae656afa1ed6d32406492ec7a5a951 Mon Sep 17 00:00:00 2001
From: Riley Davis
Date: Tue, 14 Mar 2017 13:08:51 -0600
Subject: [PATCH 33/56] try to just add recaptcha manually
use react-recaptcha
remove recaptcha file
---
client/coral-admin/src/actions/auth.js | 15 ++++++++++---
.../coral-admin/src/components/AdminLogin.js | 22 ++++++++++++++++++-
client/coral-admin/src/constants/auth.js | 1 +
.../src/containers/LayoutContainer.js | 4 +++-
client/coral-admin/src/reducers/auth.js | 7 ++++++
package.json | 1 +
views/admin.ejs | 1 +
webpack.config.js | 3 +++
yarn.lock | 4 ++++
9 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js
index 389e8b4d2..3e6d81ae1 100644
--- a/client/coral-admin/src/actions/auth.js
+++ b/client/coral-admin/src/actions/auth.js
@@ -2,15 +2,24 @@ import * as actions from '../constants/auth';
import coralApi from 'coral-framework/helpers/response';
// Log In.
-export const handleLogin = (email, password) => dispatch => {
+export const handleLogin = (email, password, recaptchaResponse) => dispatch => {
dispatch({type: actions.LOGIN_REQUEST});
- return coralApi('/auth/local', {method: 'POST', body: {email, password}})
+ const params = {method: 'POST', body: {email, password}};
+ if (recaptchaResponse) {
+ params.headers = {'X-Recaptcha-Response': recaptchaResponse};
+ }
+ return coralApi('/auth/local', params)
.then(result => {
const isAdmin = !!result.user.roles.filter(i => i === 'ADMIN').length;
dispatch(checkLoginSuccess(result.user, isAdmin));
})
.catch(error => {
- dispatch({type: actions.LOGIN_FAILURE, message: error.translation_key});
+
+ if (error.translation_key === 'LOGIN_MAXIMUM_EXCEEDED') {
+ dispatch({type: actions.LOGIN_MAXIMUM_EXCEEDED, message: error.translation_key});
+ } else {
+ dispatch({type: actions.LOGIN_FAILURE, message: error.translation_key});
+ }
});
};
diff --git a/client/coral-admin/src/components/AdminLogin.js b/client/coral-admin/src/components/AdminLogin.js
index 7c7d4b00f..11e840eb7 100644
--- a/client/coral-admin/src/components/AdminLogin.js
+++ b/client/coral-admin/src/components/AdminLogin.js
@@ -4,6 +4,7 @@ import styles from './NotFound.css';
import {Button, TextField, Alert, Success} from 'coral-ui';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
+import Recaptcha from 'react-recaptcha';
const lang = new I18n(translations);
class AdminLogin extends React.Component {
@@ -18,13 +19,22 @@ class AdminLogin extends React.Component {
this.props.handleLogin(this.state.email, this.state.password);
}
+ onRecaptchaLoad = () => {
+
+ // do something?
+ }
+
+ onRecaptchaVerify = (recaptchaResponse) => {
+ this.props.handleLogin(this.state.email, this.state.password, recaptchaResponse);
+ }
+
handleRequestPassword = e => {
e.preventDefault();
this.props.requestPasswordReset(this.state.email);
}
render () {
- const {errorMessage} = this.props;
+ const {errorMessage, loginMaxExceeded} = this.props;
const signInForm = (
+ {
+ loginMaxExceeded &&
+
+ }
);
const requestPasswordForm = (
@@ -84,6 +103,7 @@ class AdminLogin extends React.Component {
}
AdminLogin.propTypes = {
+ loginMaxExceeded: PropTypes.bool.isRequired,
handleLogin: PropTypes.func.isRequired,
passwordRequestSuccess: PropTypes.string,
loginError: PropTypes.string
diff --git a/client/coral-admin/src/constants/auth.js b/client/coral-admin/src/constants/auth.js
index 3c47bfc45..93cd61544 100644
--- a/client/coral-admin/src/constants/auth.js
+++ b/client/coral-admin/src/constants/auth.js
@@ -11,6 +11,7 @@ export const LOGOUT_FAILURE = 'LOGOUT_FAILURE';
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAILURE = 'LOGIN_FAILURE';
+export const LOGIN_MAXIMUM_EXCEEDED = 'LOGIN_MAXIMUM_EXCEEDED';
export const FETCH_FORGOT_PASSWORD_REQUEST = 'FETCH_FORGOT_PASSWORD_REQUEST';
export const FETCH_FORGOT_PASSWORD_SUCCESS = 'FETCH_FORGOT_PASSWORD_SUCCESS';
diff --git a/client/coral-admin/src/containers/LayoutContainer.js b/client/coral-admin/src/containers/LayoutContainer.js
index d122cd23a..d2493a548 100644
--- a/client/coral-admin/src/containers/LayoutContainer.js
+++ b/client/coral-admin/src/containers/LayoutContainer.js
@@ -16,12 +16,14 @@ class LayoutContainer extends Component {
loggedIn,
loadingUser,
loginError,
+ loginMaxExceeded,
passwordRequestSuccess
} = this.props.auth;
const {handleLogout} = this.props;
if (loadingUser) { return ; }
if (!isAdmin) {
return ({
const mapDispatchToProps = dispatch => ({
checkLogin: () => dispatch(checkLogin()),
- handleLogin: (username, password) => dispatch(handleLogin(username, password)),
+ handleLogin: (username, password, recaptchaResponse) => dispatch(handleLogin(username, password, recaptchaResponse)),
requestPasswordReset: email => dispatch(requestPasswordReset(email)),
handleLogout: () => dispatch(logout())
});
diff --git a/client/coral-admin/src/reducers/auth.js b/client/coral-admin/src/reducers/auth.js
index b52efdb85..a7054ddfa 100644
--- a/client/coral-admin/src/reducers/auth.js
+++ b/client/coral-admin/src/reducers/auth.js
@@ -6,6 +6,7 @@ const initialState = Map({
user: null,
isAdmin: false,
loginError: null,
+ loginMaxExceeded: false,
passwordRequestSuccess: null
});
@@ -29,12 +30,18 @@ export default function auth (state = initialState, action) {
return initialState;
case actions.LOGIN_REQUEST:
return state.set('loginError', null);
+ case actions.LOGIN_SUCCESS:
+ return state.set('loginMaxExceeded', false).set('loginError', null);
case actions.LOGIN_FAILURE:
return state.set('loginError', action.message);
case actions.FETCH_FORGOT_PASSWORD_REQUEST:
return state.set('passwordRequestSuccess', null);
case actions.FETCH_FORGOT_PASSWORD_SUCCESS:
return state.set('passwordRequestSuccess', 'If you have a registered account, a password reset link was sent to that email.');
+ case actions.LOGIN_MAXIMUM_EXCEEDED:
+ return state
+ .set('loginMaxExceeded', true)
+ .set('loginError', action.message);
default :
return state;
}
diff --git a/package.json b/package.json
index 7da9305f8..36899a04b 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
"passport-facebook": "^2.1.1",
"passport-local": "^1.0.0",
"react-apollo": "^0.10.0",
+ "react-recaptcha": "^2.2.6",
"redis": "^2.6.5",
"uuid": "^2.0.3"
},
diff --git a/views/admin.ejs b/views/admin.ejs
index dafd5e3cc..d4d635dcc 100644
--- a/views/admin.ejs
+++ b/views/admin.ejs
@@ -85,6 +85,7 @@
+