From 31dea32ced07f34b743eb15515e28ba8aea5ae66 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 8 May 2019 23:46:53 +0800 Subject: [PATCH 01/12] - Breaking change: Stop supporting older behavior against layers of arity one or two (will now drop one with no `done`) - Breaking change: We now catch errors within layers so as to support more intuitive passing of errors (when synchronous) - Breaking change: No longer throws when login is missing a callback (as may use as Promise) - Breaking change: Only return from `serializeUser`, `deserializeUser` (and `transformAuthInfo`) when giving a synchronous serialized result or a Promise (do not do `return done()`) - Enhancement: Promises for `serializeUser`, `deserializeUser`, `transformAuthInfo` (including allowing throwing `new Error('pass')` to pass); avoid need for `done` - Enhancement: Allow `serializeUser` or `deserializeUser` to return non-`undefined` value for sync behavior - Refactoring: Return Promise from `req.logIn` and from authenticate middleware's `strategy.success` - Testing: Add tests with `req.logIn` and `SessionStrategy` returning without `done` (synchronously or with Promise) - Docs: Show example of a `LocalStrategy` using async/await - Docs: Avoid showing connect `cookieParser` with `session`; use current `expressSession` over `connect.session` - Linting: jsdoc - Git: Ignore .github/docs/jsdoc - npm/Docs: Add jsdoc with script, along with static server and open-cli to open - npm: Add `test-one` script; remove self from contributors --- .eslintignore | 1 + .eslintrc.js | 14 +- .gitignore | 3 +- README.md | 3 +- docs/jsdoc-config.js | 32 + lib/authenticator.js | 333 +++- lib/errors/authenticationerror.js | 7 +- lib/framework/connect.js | 17 +- lib/http/request.js | 68 +- lib/index.js | 44 +- lib/middleware/authenticate.js | 231 ++- lib/middleware/initialize.js | 41 +- lib/sessionmanager.js | 81 +- lib/strategies/session.js | 66 +- package-lock.json | 1773 ++++++++++++----- package.json | 11 +- templates/package.json.j2 | 7 + test/authenticator.middleware.test.js | 12 +- test/authenticator.promise.test.js | 1013 ++++++++++ test/authenticator.sync.test.js | 1011 ++++++++++ test/authenticator.test.js | 96 +- test/http/request.test.js | 76 +- test/middleware/authenticate.redirect.test.js | 3 +- .../authenticate.success.flash.test.js | 61 +- .../authenticate.success.info.test.js | 21 +- .../authenticate.success.message.test.js | 13 +- .../authenticate.success.multi.test.js | 7 +- test/middleware/authenticate.success.test.js | 29 +- test/middleware/authenticate.test.js | 4 +- test/strategies/session.test.js | 20 +- 30 files changed, 4143 insertions(+), 955 deletions(-) create mode 100644 docs/jsdoc-config.js create mode 100644 test/authenticator.promise.test.js create mode 100644 test/authenticator.sync.test.js diff --git a/.eslintignore b/.eslintignore index 4e3ef646..43c87dab 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,3 +2,4 @@ coverage/ node_modules/ var/ +docs/jsdoc diff --git a/.eslintrc.js b/.eslintrc.js index 77d0cdee..752921c1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,6 +8,14 @@ module.exports = { // Override ash-nazg's current preference for ESM 'plugin:node/recommended-script' ], + settings: { + polyfills: [ + // Needing these for some reason to avoid an error + "Promise", + "Promise.reject", + "Promise.resolve" + ] + }, overrides: [ { files: ['test/**'], @@ -60,9 +68,11 @@ module.exports = { 'no-underscore-dangle': 0, 'no-param-reassign': 0, - // Disable until implementing promises and Node version supporting + // Disable until https://github.com/eslint/eslint/issues/11899 may be addressed + 'require-atomic-updates': 0, + + // Disable as middleware approach requires some callbacks 'promise/prefer-await-to-callbacks': 0, - 'promise/prefer-await-to-then': 0, // Disable until ready to tackle 'require-jsdoc': 0, diff --git a/.gitignore b/.gitignore index 5eb228cf..96e42462 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ reports/ -docs/ +docs/jsdoc +.github/docs/jsdoc var/ diff --git a/README.md b/README.md index 2aaf6ea5..34e4aeba 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Passport-Next/Passport +# Passport-Next/Passport Status: [![NPM version](https://img.shields.io/npm/v/@passport-next/passport.svg)](https://www.npmjs.com/package/@passport-next/passport) @@ -44,4 +44,3 @@ We support all [node versions](https://github.com/nodejs/Release) supported by t ## Contributing Please see [CONTRIBUTING.md](https://github.com/passport-next/passport/blob/master/CONTRIBUTING.md) - diff --git a/docs/jsdoc-config.js b/docs/jsdoc-config.js new file mode 100644 index 00000000..ff215d33 --- /dev/null +++ b/docs/jsdoc-config.js @@ -0,0 +1,32 @@ +'use strict'; + +module.exports = { + recurseDepth: 10, + source: { + exclude: [ + 'node_modules', + 'dist', + 'var', + 'coverage', + 'test' + ] + // excludePattern: '' + }, + sourceType: 'module', + tags: { + allowUnknownTags: false + }, + templates: { + cleverLinks: true, + monospaceLinks: false /* , + default: { + layoutFile: 'docs/layout.tmpl' + } */ + }, + opts: { + recurse: true, + verbose: true, + destination: 'docs/jsdoc' + // tutorials: 'docs/tutorials' + } +}; diff --git a/lib/authenticator.js b/lib/authenticator.js index cf0b3720..3d40856b 100644 --- a/lib/authenticator.js +++ b/lib/authenticator.js @@ -1,20 +1,28 @@ +/* eslint-disable promise/prefer-await-to-then */ +/* eslint no-unused-expressions: ["error", {allowShortCircuit: true}] */ 'use strict'; /** - * Module dependencies. + * @file Module dependencies. */ const SessionStrategy = require('./strategies/session'); const SessionManager = require('./sessionmanager'); const connect = require('./framework/connect'); +const isThenable = (obj) => { + return obj && typeof obj.then === 'function'; +}; /** * The `Authenticator` constructor. - * * @public */ class Authenticator { + /** + * Sets up initial framework (via {@link GetConnectExpress}) and with + * {@link SessionStrategy} and {@link SessionManager}. + */ constructor() { this._key = 'passport'; this._strategies = {}; @@ -28,7 +36,8 @@ class Authenticator { } /** - * Initialize authenticator. + * Initialize authenticator. Sets framework internally with return of + * {@link GetConnectExpress} though this can be overridden. * @returns {void} * @protected */ @@ -39,7 +48,7 @@ class Authenticator { } /** - * Utilize the given `strategy` with optional `name`, overridding the strategy's + * Utilize the given `strategy` with optional `name`, overriding the strategy's * default name. * * @example @@ -48,8 +57,11 @@ class Authenticator { * * passport.use('api', new http.BasicStrategy(...args)); * - * @param {string|Strategy} name - * @param {Strategy} strategy + * @param {string|Strategy} [name] + * @param {Strategy} strategy The framework supplied by default will augment an + * instance made from this strategy instance or prototype with the methods, + * `success`, `fail`, `redirect`, `pass`, and `error`. See the source of + * {@link authenticate}. * @returns {Authenticator} for chaining * @public */ @@ -103,7 +115,7 @@ class Authenticator { * * passport.framework(require('hapi-passport')()); * - * @param {PlainObject} fw + * @param {ConnectExpress} fw * @returns {Authenticator} for chaining * @public */ @@ -113,7 +125,7 @@ class Authenticator { } /** - * @typedef {Object} AuthenticatorInitializeOptions + * @typedef {AuthenticateOptions} AuthenticatorInitializeOptions * @property {string} [userProperty="user"] Property to set on `req` upon login */ @@ -130,7 +142,7 @@ class Authenticator { * app.use(passport.initialize({ userProperty: 'currentUser' })); * * @param {AuthenticatorInitializeOptions} options - * @returns {GenericCallback} middleware + * @returns {InitializeMiddleware} middleware * @public */ initialize(options) { @@ -167,9 +179,9 @@ class Authenticator { * }); * * @param {string} strategy - * @param {PlainObject} options - * @param {GenericCallback} callback - * @returns {GenericCallback} middleware + * @param {AuthenticateOptions} options + * @param {AuthenticateCallback} callback + * @returns {AuthenticateMiddleware} middleware * @public */ authenticate(strategy, options, callback) { @@ -192,9 +204,9 @@ class Authenticator { * passport.authorize('twitter-authz', { failureRedirect: '/account' }); * * @param {string} strategy - * @param {PlainObject} options - * @param {GenericCallback} callback - * @returns {GenericCallback} middleware + * @param {AuthenticateOptions} options + * @param {AuthenticateCallback} callback + * @returns {AuthenticateMiddleware} middleware * @public */ authorize(strategy, options, callback) { @@ -225,13 +237,12 @@ class Authenticator { * * @example * - * app.use(connect.cookieParser()); - * app.use(connect.session({ secret: 'keyboard cat' })); + * app.use(expressSession({ secret: 'keyboard cat' })); * app.use(passport.initialize()); * app.use(passport.session()); * - * @param {PlainObject} options - * @returns {GenericCallback} middleware + * @param {AuthenticateOptions} options + * @returns {AuthenticateMiddleware} middleware * @public */ session(options) { @@ -239,20 +250,40 @@ class Authenticator { } /** - * Sets a custom SessionManager + * Sets a custom SessionManager. * * @example * * passport.sessionManager = new CustomSessionManager(); * * @public + * @param {SessionManager} mgr + * @returns {Authenticator} */ - sessionManager(mgr) { this._sm = mgr; return this; } + /** + * @typedef {PlainObject} User + */ + + /** + * @callback SerializeUserDoneCallback + * @param {null|Error} err + * @param {0|string} serializedUser + * @returns {void} + */ + + /** + * @callback SerializeUserMiddleware + * @param {Request} req + * @param {User} user + * @param {SerializeUserDoneCallback} serialized + * @returns {void|"pass"|string|Error|Promise} + */ + /** * Registers a function used to serialize user objects into the session. * @@ -263,9 +294,11 @@ class Authenticator { * }); * * @public + * @param {SerializeUserMiddleware} fn + * @param {Request} req + * @param {SerializeUserDoneCallback} [done] + * @returns {void|"pass"|string|Error|Promise} */ - - // eslint-disable-next-line consistent-return serializeUser(fn, req, done) { if (typeof fn === 'function') { return this._serializers.push(fn); @@ -282,39 +315,94 @@ class Authenticator { } const stack = this._serializers; - // eslint-disable-next-line consistent-return - (function pass(i, err, obj) { + + // Todo: Refactor to use promises exclusively + // eslint-disable-next-line require-await + return (async function pass(i, err, serializedUser) { // serializers use 'pass' as an error to skip processing - if (err === 'pass') { + if (err === 'pass' || (err && err.message === 'pass')) { err = undefined; } // an error or serialized object was obtained, done - if (err || obj || obj === 0) { return done(err, obj); } + if (err || serializedUser || serializedUser === 0) { + done && done(err, serializedUser); + if (!done && err) { + throw err; + } + return err || serializedUser; + } const layer = stack[i]; if (!layer) { - return done(new Error('Failed to serialize user into session')); + const error = new Error('Failed to serialize user into session'); + done && done(error); + if (!done && error) { + throw error; + } + return error; } - - // eslint-disable-next-line jsdoc/require-jsdoc + let serializedRet, res; + /** + * + * @param {Error} [e] + * @param {void|"pass"|string} [o] + * @returns {void|"pass"|string|Error|Promise} + */ function serialized(e, o) { - pass(i + 1, e, o); + serializedRet = pass(i + 1, e, o); + if (res) { + res(serializedRet); + } + return serializedRet; } try { - const arity = layer.length; - if (arity === 3) { - layer(req, user, serialized); - } else { - layer(user, serialized); + let ret; + try { + ret = layer(req, user, serialized); + } catch (serializeError) { + return serialized(serializeError); + } + if (isThenable(ret)) { + return ret.then((serializedObject) => { + return serialized(null, serializedObject); + }).catch((serializeError) => { + return serialized(serializeError); + }); + } + if (ret !== undefined) { + return serialized(null, ret); } } catch (e) { - return done(e); + return serialized(e); } + // eslint-disable-next-line promise/avoid-new + return new Promise((resolve) => { + if (serializedRet) { + resolve(serializedRet); + return; + } + res = resolve; + }); }(0)); } + /** + * @callback DeserializeUserDoneCallback + * @param {null|Error} err + * @param {User} user + * @returns {void} + */ + + /** + * @callback DeserializeUserMiddleware + * @param {Request} req + * @param {string} obj + * @param {DeserializeUserDoneCallback} deserialized + * @returns {void|null|false|"pass"|Error|Promise} + */ + /** * Registers a function used to deserialize user objects out of the session. * @@ -327,9 +415,11 @@ class Authenticator { * }); * * @public + * @param {DeserializeUserMiddleware} fn + * @param {Request} req + * @param {DeserializeUserDoneCallback} [done] + * @returns {Promise} */ - - // eslint-disable-next-line consistent-return deserializeUser(fn, req, done) { if (typeof fn === 'function') { return this._deserializers.push(fn); @@ -346,42 +436,104 @@ class Authenticator { } const stack = this._deserializers; - // eslint-disable-next-line consistent-return - (function pass(i, err, user) { + + // Todo: Refactor to use promises exclusively + // eslint-disable-next-line require-await + return (async function pass(i, err, user) { // deserializers use 'pass' as an error to skip processing - if (err === 'pass') { + if (err === 'pass' || (err && err.message === 'pass')) { err = undefined; } // an error or deserialized user was obtained, done - if (err || user) { return done(err, user); } + if (err || user) { + done && done(err, user); + if (!done && err) { + throw err; + } + return err || user; + } // a valid user existed when establishing the session, but that user has // since been removed - if (user === null || user === false) { return done(null, false); } + if (user === null || user === false) { + done && done(null, false); + return false; + } const layer = stack[i]; if (!layer) { - return done(new Error('Failed to deserialize user out of session')); + const error = new Error('Failed to deserialize user out of session'); + done && done(error); + if (!done && error) { + throw error; + } + return error; } - - // eslint-disable-next-line jsdoc/require-jsdoc + let deserializedRet, res; + /** + * + * @param {Error} [e] + * @param {User|void|"pass"|false} [u] + * @returns {User|void|null|false|Error|Promise} + */ function deserialized(e, u) { - pass(i + 1, e, u); + deserializedRet = pass(i + 1, e, u); + if (res) { + res(deserializedRet); + } + return deserializedRet; } try { - const arity = layer.length; - if (arity === 3) { - layer(req, obj, deserialized); - } else { - layer(obj, deserialized); + let ret; + try { + ret = layer(req, obj, deserialized); + } catch (deserializeError) { + return deserialized(deserializeError); + } + if (isThenable(ret)) { + return ret.then((u) => { + return deserialized(null, u); + }).catch((deserializeError) => { + return deserialized(deserializeError); + }); + } + if (ret !== undefined) { + return deserialized(null, ret); } } catch (e) { - return done(e); + return deserialized(e); } + // eslint-disable-next-line promise/avoid-new + return new Promise((resolve) => { + if (deserializedRet) { + resolve(deserializedRet); + return; + } + res = resolve; + }); }(0)); } + /** + * @typedef {PlainObject} AuthInfo + */ + + /** + * @callback TransformAuthDoneCallback + * @param {null|Error} err + * @param {AuthInfo} obj + * @returns {void} + */ + + /** + * @callback TransformAuthMiddleware + * @param {Request} req + * @param {AuthInfo} tinfo + * @param {TransformAuthDoneCallback} transformed + * @returns {void|"pass"|AuthInfo|Error|Promise} + */ + /** * Registers a function used to transform auth info. * @@ -419,9 +571,11 @@ class Authenticator { * }); * * @public + * @param {TransformAuthMiddleware} fn + * @param {Request} req + * @param {TransformAuthDoneCallback} [done] + * @returns {Promise} */ - - // eslint-disable-next-line consistent-return transformAuthInfo(fn, req, done) { if (typeof fn === 'function') { return this._infoTransformers.push(fn); @@ -438,42 +592,75 @@ class Authenticator { } const stack = this._infoTransformers; - // eslint-disable-next-line consistent-return - (function pass(i, err, tinfo) { + + // Todo: Refactor to use promises exclusively + // eslint-disable-next-line require-await + return (async function pass(i, err, tinfo) { // transformers use 'pass' as an error to skip processing - if (err === 'pass') { + if (err === 'pass' || (err && err.message === 'pass')) { err = undefined; } // an error or transformed info was obtained, done - if (err || tinfo) { return done(err, tinfo); } + if (err || tinfo) { + done && done(err, tinfo); + if (!done && err) { + throw err; + } + return err || tinfo; + } const layer = stack[i]; if (!layer) { // if no transformers are registered (or they all pass), the default // behavior is to use the un-transformed info as-is - return done(null, info); + done && done(null, info); + return info; } - // eslint-disable-next-line jsdoc/require-jsdoc + let transformedRet, res; + /** + * + * @param {Error} [e] + * @param {AuthInfo|void|"pass"} [t] + * @returns {AuthInfo|void|Promise} + */ function transformed(e, t) { - pass(i + 1, e, t); + transformedRet = pass(i + 1, e, t); + if (res) { + res(transformedRet); + } + return transformedRet; } try { - const arity = layer.length; - if (arity === 1) { - // sync - const t = layer(info); - transformed(null, t); - } else if (arity === 3) { - layer(req, info, transformed); - } else { - layer(info, transformed); + let ret; + try { + ret = layer(req, info, transformed); + } catch (transformError) { + return transformed(transformError); + } + if (isThenable(ret)) { + return ret.then((t) => { + return transformed(null, t); + }).catch((transformError) => { + return transformed(transformError); + }); + } + if (ret !== undefined) { + return transformed(null, ret); } } catch (e) { - return done(e); + return transformed(e); } + // eslint-disable-next-line promise/avoid-new + return new Promise((resolve) => { + if (transformedRet) { + resolve(transformedRet); + return; + } + res = resolve; + }); }(0)); } diff --git a/lib/errors/authenticationerror.js b/lib/errors/authenticationerror.js index e8daf169..11b521f3 100644 --- a/lib/errors/authenticationerror.js +++ b/lib/errors/authenticationerror.js @@ -2,10 +2,15 @@ /** * The `AuthenticationError` error. - * + * @class AuthenticationError * @private */ class AuthenticationError extends Error { + /** + * + * @param {string} message + * @param {Integer} status + */ constructor(message, status) { super(message); Error.captureStackTrace(this, AuthenticationError); diff --git a/lib/framework/connect.js b/lib/framework/connect.js index 7a34ae1d..ed08f7d0 100644 --- a/lib/framework/connect.js +++ b/lib/framework/connect.js @@ -1,24 +1,31 @@ 'use strict'; /** - * Module dependencies. + * @file Module dependencies. */ const initialize = require('../middleware/initialize'); const authenticate = require('../middleware/authenticate'); +/** +* @typedef {PlainObject} ConnectExpress +* @property {initialize} initialize +* @property {authenticate} authenticate +* @property {authenticate} [authorize] +*/ + +/* eslint-disable no-multi-assign, func-names */ /** * Framework support for Connect/Express. * * This module provides support for using Passport with Express. It exposes * middleware that conform to the `fn(req, res, next)` signature. - * - * @returns {Object} + * @callback GetConnectExpress + * @returns {ConnectExpress} * @protected */ - -// eslint-disable-next-line no-multi-assign, func-names exports = module.exports = function () { + /* eslint-enable no-multi-assign, func-names */ return { initialize, authenticate diff --git a/lib/http/request.js b/lib/http/request.js index 973b3a01..e3f0a556 100644 --- a/lib/http/request.js +++ b/lib/http/request.js @@ -1,37 +1,46 @@ /** - * Module dependencies. + * @file Module dependencies. */ 'use strict'; -// const http = require('http') -// , req = http.IncomingMessage.prototype; - /* eslint-disable no-multi-assign */ +/** + * @namespace {PlainObject} HttpRequest + */ const req = exports = module.exports = {}; /* eslint-enable no-multi-assign */ /** -* @typedef {Object} LogInOptions -* @property {boolean} [session] Save login state in session, defaults to _true_ +* @callback LoginDoneCallback +* @param {Error} [error] +*/ + +/** +* @typedef {PlainObject} LogInOptions +* @property {boolean} [session=true] Save login state in session. */ /** * Initiate a login session for `user`. * - * + * @function HttpRequest#logIn * @example * * req.logIn(user, { session: false }); * - * req.logIn(user, (err) => { - * if (err) { throw err; } - * // session saved - * }); + * (async () => { + * try { + * await req.logIn(user); + * } catch (err) { + * throw err; + * } + * // session saved + * })(); * * @param {User} user * @param {LogInOptions} options - * @param {GenericCallback} done - * @returns {void} + * @param {LoginDoneCallback} done + * @returns {Promise} * @public */ req.logIn = function logIn(user, options, done) { @@ -50,23 +59,34 @@ req.logIn = function logIn(user, options, done) { this[property] = user; if (session) { if (!this._passport) { throw new Error('passport.initialize() middleware not in use'); } - if (typeof done !== 'function') { throw new TypeError('req#login requires a callback function'); } - // eslint-disable-next-line consistent-return - this._passport.instance._sm.logIn(this, user, (err) => { - if (err) { this[property] = null; return done(err); } - done(); - }); - } else { - // eslint-disable-next-line no-unused-expressions - done && done(); + // We don't use async above in order to be able to throw early there + return (async () => { + try { + // `this._passport.instance` (and `_sm`) set by `initialize` + await this._passport.instance._sm.logIn(this, user); + // eslint-disable-next-line no-unused-expressions + done && done(); + } catch (err) { + this[property] = null; + if (done) { + done(err); + } else { + throw err; + } + } + })(); + } else if (done) { + done(); } + return Promise.resolve(); }; req.login = req.logIn; /** * Terminate an existing login session. + * @function HttpRequest#logOut * @param {GenericCallback} done * @returns {void} * @public @@ -87,7 +107,7 @@ req.logout = req.logOut; /** * Test if request is authenticated. - * + * @function HttpRequest#isAuthenticated * @returns {boolean} * @public */ @@ -102,7 +122,7 @@ req.isAuthenticated = function isAuthenticated() { /** * Test if request is unauthenticated. - * + * @function HttpRequest#isUnauthenticated * @returns {boolean} * @public */ diff --git a/lib/index.js b/lib/index.js index 14415cc2..8ee9d4b0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,13 +1,47 @@ +/* eslint-disable no-multi-assign */ /** * Module dependencies. */ -/* eslint-disable no-multi-assign */ 'use strict'; const Passport = require('./authenticator'); const SessionStrategy = require('./strategies/session'); +/** + * Middleware function passed `req`, `res`, and `next` + * @external ConnectMiddleware + * @see https://github.com/senchalabs/connect#appusefn +*/ + +/** +* @external HttpIncomingMessage +* @see https://nodejs.org/docs/latest/api/http.html#http_class_http_incomingmessage +*/ + +/** +* @external HttpServerResponse +* @see https://nodejs.org/docs/latest/api/http.html#http_class_http_serverresponse +*/ + +/** +* @typedef {external:HttpIncomingMessage} Request +*/ + +/** +* @typedef {external:HttpServerResponse} Response +*/ + +/** + * This middleware conforms to Connect/Express middleware by + * the arguments it accepts. + * @see Conforms to {@link external:ConnectMiddleware} + * @callback ConnectMiddleware + * @param {Request} req + * @param {Response} res + * @param {Function} next + * @returns {void} +*/ /** * Export default singleton. @@ -16,14 +50,10 @@ const SessionStrategy = require('./strategies/session'); */ exports = module.exports = new Passport(); -/** - * Expose constructors. - */ +// Expose constructors. exports.Passport = exports.Authenticator = Passport; exports.Strategy = require('@passport-next/passport-strategy'); -/** - * Expose strategies. - */ +// Expose strategies. exports.strategies = {}; exports.strategies.SessionStrategy = SessionStrategy; diff --git a/lib/middleware/authenticate.js b/lib/middleware/authenticate.js index 5ceb5c8b..dd9b7233 100644 --- a/lib/middleware/authenticate.js +++ b/lib/middleware/authenticate.js @@ -2,12 +2,61 @@ 'use strict'; /** - * Module dependencies. + * @file Module dependencies. */ const http = require('http'); const AuthenticationError = require('../errors/authenticationerror'); +/** +* @callback AuthenticateMiddleware +* @type {ConnectMiddleware} +*/ + +/** +* @callback AuthenticateCallback +* @param {Error|null} error +* @param {false|User} user Set to the authenticated user on a successful +* authentication attempt, or `false` otherwise. +* @param {any} [info] Contains additional details provided by the strategy's verify +* callback - this could be information about a successful authentication or a +* challenge message for a failed authentication. +* @param {any} [status] Passed when authentication fails - this could +* be an HTTP response code for a remote authentication failure or similar. +* @returns {void} +* @example +* app.get('/protected', function (req, res, next) { +* passport.authenticate('local', function (err, user, info, status) { +* if (err) { next(err); return; } +* if (!user) { res.redirect('/signin'); return; } +* res.redirect('/account'); +* })(req, res, next); +* }); +*/ + +/** + * @typedef {PlainObject} AuthenticateOptions + * @property {string} [successRedirect] After successful login, redirect to given URL + * @property {boolean|string} [successMessage] True to store success message in + * `req.session.messages`, or a string to use as override message for success. + * @property {boolean|string} [successFlash] True to flash success messages or a string + * to use as a flash message for success (overrides any from the strategy itself). + * @property {string} [failureRedirect] After failed login, redirect to given URL + * @property {boolean|string} [failureMessage] True to store failure message in + * `req.session.messages`, or a string to use as override + * message for failure. + * @property {boolean|string} [failureFlash] True to flash failure messages or a string to + * use as a flash message for failures (overrides any from the strategy itself). + * @property {boolean} [failWithError] Passes on an {@link AuthenticationError} + * @property {string} [assignProperty] Assign the object provided by the verify callback + * to given property + * @property {string} [successReturnToOrRedirect] Redirect URL; overridden if + * `req.session.returnTo` is truthy + * @property {boolean} [authInfo=true] Set to `false` to disable setting of `autoInfo` + * on `req` through `transformAuthInfo` +*/ + +// Todo: Reenable after this may be merged https://github.com/gajus/eslint-plugin-jsdoc/pull/270 /** * Authenticates requests. * @@ -17,44 +66,17 @@ const AuthenticationError = require('../errors/authenticationerror'); * established by default. If authentication fails, an unauthorized response * will be sent. * - * Options: - * - `session` Save login state in session, defaults to _true_ - * - `successRedirect` After successful login, redirect to given URL - * - `successMessage` True to store success message in - * req.session.messages, or a string to use as override - * message for success. - * - `successFlash` True to flash success messages or a string to use as a flash - * message for success (overrides any from the strategy itself). - * - `failureRedirect` After failed login, redirect to given URL - * - `failureMessage` True to store failure message in - * req.session.messages, or a string to use as override - * message for failure. - * - `failureFlash` True to flash failure messages or a string to use as a flash - * message for failures (overrides any from the strategy itself). - * - `assignProperty` Assign the object provided by the verify callback to given property - * * An optional `callback` can be supplied to allow the application to override - * the default manner in which authentication attempts are handled. The - * callback has the following signature, where `user` will be set to the - * authenticated user on a successful authentication attempt, or `false` - * otherwise. An optional `info` argument will be passed, containing additional - * details provided by the strategy's verify callback - this could be information about - * a successful authentication or a challenge message for a failed authentication. - * An optional `status` argument will be passed when authentication fails - this could - * be a HTTP response code for a remote authentication failure or similar. - * - * app.get('/protected', function(req, res, next) { - * passport.authenticate('local', function(err, user, info, status) { - * if (err) { return next(err) } - * if (!user) { return res.redirect('/signin') } - * res.redirect('/account'); - * })(req, res, next); - * }); + * the default manner in which authentication attempts are handled. * * Note that if a callback is supplied, it becomes the application's * responsibility to log-in the user, establish a session, and otherwise perform * the desired operations. * + * Note that its redirecting behavior relies on `res.redirect` (available in + * Express but not Connect). + * + * @callback authenticate * @example * * passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }); @@ -64,10 +86,10 @@ const AuthenticationError = require('../errors/authenticationerror'); * passport.authenticate('twitter'); * * @param {Authenticator} passport - * @param {string|GenericArray} name - * @param {PlainObject} options - * @param {GenericCallback} callback - * @returns {GenericCallback} + * @param {string|string[]} name + * @param {AuthenticateOptions} options + * @param {AuthenticateCallback} [callback] + * @returns {AuthenticateMiddleware} * @public */ module.exports = function authenticate(passport, name, options, callback) { @@ -94,27 +116,38 @@ module.exports = function authenticate(passport, name, options, callback) { multi = false; } + // Do not refactor to return an async method as is middleware calling `next` return function authenticate(req, res, next) { // accumulator for failures from each strategy in the chain const failures = []; - // eslint-disable-next-line jsdoc/require-jsdoc + /** + * @param {string} url + * @returns {void} + * @see https://expressjs.com/en/api.html#res.redirect + */ function redirect(url) { if (req.session && req.session.save && typeof req.session.save === 'function') { - return req.session.save(() => res.redirect(url)); + req.session.save(() => res.redirect(url)); + return; } - return res.redirect(url); + res.redirect(url); } - // eslint-disable-next-line consistent-return, jsdoc/require-jsdoc + /** + * + * @returns {void} + */ function allFailed() { if (callback) { if (!multi) { - return callback(null, false, failures[0].challenge, failures[0].status); + callback(null, false, failures[0].challenge, failures[0].status); + return; } const challenges = failures.map(f => f.challenge); const statuses = failures.map(f => f.status); - return callback(null, false, challenges, statuses); + callback(null, false, challenges, statuses); + return; } // Strategies are ordered by priority. For the purpose of flashing a @@ -148,7 +181,8 @@ module.exports = function authenticate(passport, name, options, callback) { } } if (options.failureRedirect) { - return redirect(options.failureRedirect); + redirect(options.failureRedirect); + return; } // When failure handling is not delegated to the application, the default @@ -170,25 +204,37 @@ module.exports = function authenticate(passport, name, options, callback) { res.setHeader('WWW-Authenticate', rchallenge); } if (options.failWithError) { - return next(new AuthenticationError(http.STATUS_CODES[res.statusCode], rstatus)); + next(new AuthenticationError(http.STATUS_CODES[res.statusCode], rstatus)); + return; } res.end(http.STATUS_CODES[res.statusCode]); } - // eslint-disable-next-line consistent-return (function attempt(i) { const layer = name[i]; // If no more strategies exist in the chain, authentication has failed. - if (!layer) { return allFailed(); } + if (!layer) { + allFailed(); + return; + } // Get the strategy, which will be used as prototype from which to create // a new instance. Action functions will then be bound to the strategy // within the context of the HTTP request/response pair. const prototype = passport._strategy(layer); - if (!prototype) { return next(new Error(`Unknown authentication strategy "${layer}"`)); } + if (!prototype) { + next(new Error(`Unknown authentication strategy "${layer}"`)); + return; + } const strategy = Object.create(prototype); + /** + * @typedef {PlainObject} SuccessInfo + * @property {string} [type="success"] + * @property {string} [message] + */ + // ----- BEGIN STRATEGY AUGMENTATION ----- // Augment the new strategy instance with action functions. These action @@ -207,15 +253,15 @@ module.exports = function authenticate(passport, name, options, callback) { * useful for third-party authentication strategies to pass profile * details. * - * @param {Object} user - * @param {Object} info + * @param {User} user + * @param {SuccessInfo} [info] * @public + * @returns {void} */ - - // eslint-disable-next-line consistent-return - strategy.success = function success(user, info) { + strategy.success = async function success(user, info) { if (callback) { - return callback(null, user, info); + callback(null, user, info); + return; } info = info || {}; @@ -248,40 +294,51 @@ module.exports = function authenticate(passport, name, options, callback) { } if (options.assignProperty) { req[options.assignProperty] = user; - return next(); + next(); + return; } - // eslint-disable-next-line consistent-return - req.logIn(user, options, (err) => { - if (err) { return next(err); } - - // eslint-disable-next-line consistent-return, jsdoc/require-jsdoc - function complete() { - if (options.successReturnToOrRedirect) { - let url = options.successReturnToOrRedirect; - if (req.session && req.session.returnTo) { - url = req.session.returnTo; - delete req.session.returnTo; - } - return redirect(url); - } - if (options.successRedirect) { - return redirect(options.successRedirect); + try { + await req.logIn(user, options); + } catch (err) { + next(err); + return; + } + + /** + * + * @returns {void} + */ + function complete() { + if (options.successReturnToOrRedirect) { + let url = options.successReturnToOrRedirect; + if (req.session && req.session.returnTo) { + url = req.session.returnTo; + delete req.session.returnTo; } - next(); + redirect(url); + return; } + if (options.successRedirect) { + redirect(options.successRedirect); + return; + } + next(); + } - if (options.authInfo !== false) { - // eslint-disable-next-line consistent-return - passport.transformAuthInfo(info, req, (err, tinfo) => { - if (err) { return next(err); } - req.authInfo = tinfo; - complete(); - }); - } else { + if (options.authInfo !== false) { + try { + const tinfo = await passport.transformAuthInfo(info, req); + req.authInfo = tinfo; complete(); + } catch (err) { + next(err); + // eslint-disable-next-line no-useless-return + return; } - }); + } else { + complete(); + } }; /** @@ -290,8 +347,8 @@ module.exports = function authenticate(passport, name, options, callback) { * * Strategies should call this function to fail an authentication attempt. * - * @param {string} challenge - * @param {number} status + * @param {string} [challenge] + * @param {number} [status] * @returns {void} * @public */ @@ -314,7 +371,7 @@ module.exports = function authenticate(passport, name, options, callback) { * user agent) to a third-party website for authentication. * * @param {string} url - * @param {number} status + * @param {number} [status=302] * @returns {void} * @public */ @@ -359,12 +416,12 @@ module.exports = function authenticate(passport, name, options, callback) { * * @param {Error} err * @public + * @returns {void} */ - - // eslint-disable-next-line consistent-return strategy.error = function error(err) { if (callback) { - return callback(err); + callback(err); + return; } next(err); diff --git a/lib/middleware/initialize.js b/lib/middleware/initialize.js index 1e44e2f5..d7f0d109 100644 --- a/lib/middleware/initialize.js +++ b/lib/middleware/initialize.js @@ -1,5 +1,12 @@ 'use strict'; +const IncomingMessageExt = require('../http/request'); + +/** +* @callback InitializeMiddleware +* @type {ConnectMiddleware} +*/ + /** * Passport initialization. * @@ -21,30 +28,38 @@ * entirely stateless (not using sessions), this middleware is not necessary, * but its use will not have any adverse impact. * + * Sets the following on `request` (first four from {@link HttpRequest}): + * 1. `logIn` (and `login`) + * 2. `logOut` (and `logout`) + * 3. `isAuthenticated` + * 4. `isUnauthenticated` + * 5. `_passport` (with `instance` property set to `passport` and + * `session` property set to `req.session[passport._key]`, as potentially + * set by {@link SessionManager#logIn}). + * + * As per #5, `request` may also have `session` set later. + * + * @callback initialize * @example * - * app.use(connect.cookieParser()); - * app.use(connect.session({ secret: 'keyboard cat' })); + * app.use(expressSession({ secret: 'keyboard cat' })); * app.use(passport.initialize()); * app.use(passport.session()); * - * passport.serializeUser(function (user, done) { - * done(null, user.id); + * passport.serializeUser(function (user) { + * return user.id; * }); * - * passport.deserializeUser(function (id, done) { - * User.findById(id, function (err, user) { - * done(err, user); - * }); + * passport.deserializeUser(async function (id) { + * const user = await User.findById(id); + * return user; * }); - * - * @returns {GenericCallback} + * @param {Authenticator} passport + * @returns {InitializeMiddleware} * @public */ - -const IncomingMessageExt = require('../http/request'); - module.exports = function initialize(passport) { + // Do not refactor to return an async method as is middleware calling `next` /* eslint-disable-next-line no-shadow */ return function initialize(req, res, next) { req._passport = {}; diff --git a/lib/sessionmanager.js b/lib/sessionmanager.js index acf588ae..2328d26f 100644 --- a/lib/sessionmanager.js +++ b/lib/sessionmanager.js @@ -1,6 +1,23 @@ 'use strict'; +/** +* @typedef {PlainObject} SessionManagerOptions +* @property {string} [key="passport"] +*/ + +/** +* @callback SessionManagerSerializeUser +* @param {User} user +* @param {Request} req +* @param {SerializeUserDoneCallback} done +*/ + class SessionManager { + /** + * + * @param {SessionManagerOptions} [options] + * @param {SessionManagerSerializeUser} serializeUser + */ constructor(options, serializeUser) { if (typeof options === 'function') { serializeUser = options; @@ -12,26 +29,58 @@ class SessionManager { this._serializeUser = serializeUser; } - logIn(req, user, cb) { - // eslint-disable-next-line consistent-return - this._serializeUser(user, req, (err, obj) => { - if (err) { - return cb(err); - } - if (!req._passport.session) { - req._passport.session = {}; - } - req._passport.session.user = obj; - if (!req.session) { - req.session = {}; + /** + * @callback LogInCallback + * @param {Error} [err] + */ + + /** + * Will set: + * 1. `request._passport.session` object if not (with `.user` object as subproperty) + * 2. `request.session` (with `_key` subproperty set to `request._passport.session`). + * @param {Request} req + * @param {User} user + * @param {LogInCallback} [cb] + * @returns {Promise} + */ + async logIn(req, user, cb) { + let obj; + try { + obj = await this._serializeUser(user, req); + } catch (err) { + // eslint-disable-next-line no-unused-expressions, callback-return + cb && cb(err); + if (!cb) { + throw err; } - req.session[this._key] = req._passport.session; - cb(); - }); + return; + } + if (!req._passport.session) { + req._passport.session = {}; + } + req._passport.session.user = obj; + if (!req.session) { + req.session = {}; + } + req.session[this._key] = req._passport.session; + // eslint-disable-next-line no-unused-expressions + cb && cb(); } - // eslint-disable-next-line class-methods-use-this + /** + * @callback LogoutCallback + * @returns {void} + */ + + /* eslint-disable class-methods-use-this */ + /** + * + * @param {Request} req + * @param {LogoutCallback} [cb] + * @returns {void} + */ logOut(req, cb) { + /* eslint-enable class-methods-use-this */ if (req._passport && req._passport.session) { delete req._passport.session.user; } diff --git a/lib/strategies/session.js b/lib/strategies/session.js index 51b612fc..7f17c972 100644 --- a/lib/strategies/session.js +++ b/lib/strategies/session.js @@ -1,16 +1,33 @@ 'use strict'; /** - * Module dependencies. + * @file Module dependencies. */ const Strategy = require('@passport-next/passport-strategy'); +/** + * Not presently in use + * @typedef {PlainObject} SessionStrategyOptions +*/ + +/** +* @callback SessionStrategyDeserializeUser +* @param {string} user +* @param {Request} req +* @returns {User} +*/ + /** * The `SessionStrategy` constructor. * * @public */ class SessionStrategy extends Strategy { + /** + * + * @param {SessionStrategyOptions} [options] + * @param {SessionStrategyDeserializeUser} deserializeUser + */ constructor(options, deserializeUser) { if (typeof options === 'function') { deserializeUser = options; @@ -23,6 +40,12 @@ class SessionStrategy extends Strategy { this._deserializeUser = deserializeUser; } + /** + * Not currently in use. + * @typedef {PlainObject} SessionStrategyAuthenticateOptions + */ + + /* eslint-disable no-unused-vars */ /** * Authenticate request based on the current session state. * @@ -32,14 +55,16 @@ class SessionStrategy extends Strategy { * * This strategy is registered automatically by Passport. * - * @param {Object} req - * @param {Object} options + * @param {Request} req + * @param {SessionStrategyAuthenticateOptions} options * @protected + * @returns {Promise} May return value of `this.error()` */ - - // eslint-disable-next-line consistent-return, no-unused-vars - authenticate(req, options) { - if (!req._passport) { return this.error(new Error('passport.initialize() middleware not in use')); } + async authenticate(req, options) { + /* eslint-enable no-unused-vars */ + if (!req._passport) { + return this.error(new Error('passport.initialize() middleware not in use')); + } options = options || {}; let su; @@ -49,21 +74,24 @@ class SessionStrategy extends Strategy { } if (su || su === 0) { - // eslint-disable-next-line consistent-return - this._deserializeUser(su, req, (err, user) => { - if (err) { return this.error(err); } - if (!user) { - delete req._passport.session.user; - } else { - // TODO: Remove instance access - const property = req._passport.instance._userProperty || 'user'; - req[property] = user; - } - this.pass(); - }); + let user; + try { + user = await this._deserializeUser(su, req); + } catch (err) { + return this.error(err); + } + if (!user) { + delete req._passport.session.user; + } else { + // TODO: Remove instance access (set by `initialize`) + const property = req._passport.instance._userProperty || 'user'; + req[property] = user; + } + this.pass(); } else { this.pass(); } + return undefined; } } diff --git a/package-lock.json b/package-lock.json index c9dbde53..738b7081 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,23 +5,23 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { "@babel/highlight": "^7.0.0" } }, "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.0.tgz", + "integrity": "sha512-1TTVrt7J9rcG5PMjvO7VEG3FrEoEJNHxumRq66GemPmzboLWtIjjcJgk8rokuAS7IiRSpgVSu5Vb9lc99iJkOA==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.5.0", "jsesc": "^2.5.1", - "lodash": "^4.17.13", + "lodash": "^4.17.11", "source-map": "^0.5.0", "trim-right": "^1.0.1" } @@ -56,9 +56,9 @@ } }, "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", "dev": true, "requires": { "chalk": "^2.0.0", @@ -67,9 +67,9 @@ } }, "@babel/parser": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", "dev": true }, "@babel/runtime": { @@ -82,57 +82,58 @@ } }, "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" - }, - "dependencies": { - "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==", - "dev": true - } + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.0.tgz", + "integrity": "sha512-SnA9aLbyOCcnnbQEGwdfBggnc142h/rbqqsXcaATj2hZcegCl903pUD/lfpsNBlBSuWow/YDfRyJuWi2EPR5cg==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.5.0", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.5.0", + "@babel/types": "^7.5.0", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.13" + "lodash": "^4.17.11" }, "dependencies": { "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-I5nW8AhGpOXGCCNYGc+p7ExQIBxRFnS2fd/d862bNOKvmoEPjYPcfIjsfdy0ujagYOIYPczKgD9l3FsgTkAzKA==", "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } } } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.0.tgz", + "integrity": "sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ==", "dev": true, "requires": { "esutils": "^2.0.2", - "lodash": "^4.17.13", + "lodash": "^4.17.11", "to-fast-properties": "^2.0.0" } }, @@ -247,6 +248,14 @@ "requires": { "lodash.unescape": "4.0.1", "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } } }, "a-sync-waterfall": { @@ -256,15 +265,15 @@ "dev": true }, "acorn": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", - "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", "dev": true }, "acorn-jsx": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", - "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", "dev": true }, "ajv": { @@ -374,6 +383,12 @@ "dev": true, "optional": true }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, "array-includes": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", @@ -391,6 +406,12 @@ "dev": true, "optional": true }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -557,6 +578,12 @@ "dev": true, "optional": true }, + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -643,6 +670,24 @@ "make-dir": "^2.0.0", "package-hash": "^3.0.0", "write-file-atomic": "^2.4.2" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } } }, "callsites": { @@ -657,10 +702,29 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } + }, "caniuse-db": { - "version": "1.0.30000992", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000992.tgz", - "integrity": "sha512-9AZRaPuAGg7Dh3iiO5/i8APo9UjVEeyArfW1ZTvYpg0H/Eo4VrAe3ggqOaUY9mhyfxT3CLeAZgEKqo+1nRc0MA==", + "version": "1.0.30000994", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000994.tgz", + "integrity": "sha512-7KjfAAhO0qJOs92z8lMWkcRA2ig7Ewv5SQSAy+dik8MFQCDSua+j4RbPFnGrXuOSFe/3RhmGr+68DxKZrbJQGg==", "dev": true }, "caniuse-lite": { @@ -675,6 +739,23 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, + "catharsis": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", + "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + }, + "dependencies": { + "lodash": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", + "dev": true + } + } + }, "chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", @@ -731,9 +812,9 @@ "dev": true }, "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", "dev": true, "optional": true, "requires": { @@ -749,31 +830,6 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.1" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } } }, "class-utils": { @@ -834,17 +890,6 @@ "string-width": "^2.1.1", "strip-ansi": "^4.0.0", "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } } }, "code-point-at": { @@ -885,6 +930,12 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "colors": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -939,14 +990,6 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } } }, "copy-descriptor": { @@ -974,6 +1017,14 @@ "log-driver": "^1.2.7", "minimist": "^1.2.0", "request": "^2.86.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "cp-file": { @@ -989,6 +1040,16 @@ "safe-buffer": "^5.0.1" }, "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -1010,6 +1071,15 @@ "which": "^1.2.9" } }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1020,12 +1090,20 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { "ms": "^2.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } } }, "decamelize": { @@ -1034,6 +1112,24 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -1141,27 +1237,13 @@ } }, "dom-serializer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", - "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", "dev": true, "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", - "dev": true - }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", - "dev": true - } + "domelementtype": "^1.3.0", + "entities": "^1.1.1" } }, "domelementtype": { @@ -1200,9 +1282,9 @@ } }, "electron-to-chromium": { - "version": "1.3.254", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.254.tgz", - "integrity": "sha512-7I5/OkgR6JKy6RFLJeru0kc0RMmmMu1UnkHBKInFKRrg1/4EQKIqOaUqITSww/SZ1LqWwp1qc/LLoIGy449eYw==", + "version": "1.3.255", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.255.tgz", + "integrity": "sha512-SZ6NlaNw3h4WR5kA1BK8XltdJCax02P+lW+z78RYoLDqmpyYuDQ5bS+/O6MCJ/j761qoZIFox2qYYt+UwqGA5w==", "dev": true }, "emoji-regex": { @@ -1236,21 +1318,16 @@ } }, "es-abstract": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", - "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.1.1", "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", - "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.0.0", - "string.prototype.trimright": "^2.0.0" + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" } }, "es-to-primitive": { @@ -1327,6 +1404,27 @@ "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", "dev": true }, + "acorn-jsx": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, "eslint-scope": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", @@ -1337,6 +1435,21 @@ "estraverse": "^4.1.1" } }, + "eslint-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, "espree": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.1.tgz", @@ -1348,17 +1461,47 @@ "eslint-visitor-keys": "^1.1.0" } }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true } } }, @@ -1469,6 +1612,17 @@ "requires": { "eslint-utils": "^1.4.2", "regexpp": "^2.0.1" + }, + "dependencies": { + "eslint-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + } } }, "eslint-plugin-eslint-comments": { @@ -1539,6 +1693,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } } } }, @@ -1554,6 +1717,23 @@ "lodash": "^4.17.15", "object.entries-ponyfill": "^1.0.1", "regextras": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } } }, "eslint-plugin-markdown": { @@ -1603,6 +1783,15 @@ "regexpp": "^3.0.0" } }, + "eslint-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + }, "regexpp": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", @@ -1693,9 +1882,9 @@ } }, "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, "espree": { @@ -1734,15 +1923,15 @@ } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "dev": true }, "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, "execa": { @@ -1974,6 +2163,12 @@ "flat-cache": "^2.0.1" } }, + "file-type": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-11.1.0.tgz", + "integrity": "sha512-rM0UO7Qm9K7TWTtA6AShI/t7H5BPjDeGVDaNyg9BjHAj3PysKy7+8C8D137R88jnR3rFJZQB/tFgydl5sN5m7g==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2029,10 +2224,20 @@ "path-exists": "^3.0.0" } }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -2053,6 +2258,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -2080,14 +2291,6 @@ "dev": true, "requires": { "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", - "dev": true - } } }, "flat-cache": { @@ -2182,25 +2385,29 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, "requires": { @@ -2210,13 +2417,15 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, "requires": { @@ -2226,37 +2435,43 @@ }, "chownr": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "4.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "optional": true, "requires": { @@ -2265,25 +2480,29 @@ }, "deep-extend": { "version": "0.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { @@ -2292,13 +2511,15 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -2314,7 +2535,8 @@ }, "glob": { "version": "7.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, "requires": { @@ -2328,13 +2550,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "bundled": true, + "resolved": false, + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, "requires": { @@ -2343,7 +2567,8 @@ }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { @@ -2352,7 +2577,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, "requires": { @@ -2362,19 +2588,22 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, "requires": { @@ -2383,13 +2612,15 @@ }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, "requires": { @@ -2398,13 +2629,15 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, "requires": { @@ -2414,7 +2647,8 @@ }, "minizlib": { "version": "1.2.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, "requires": { @@ -2423,7 +2657,8 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, "requires": { @@ -2432,13 +2667,15 @@ }, "ms": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true, "optional": true }, "needle": { "version": "2.3.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "dev": true, "optional": true, "requires": { @@ -2449,7 +2686,8 @@ }, "node-pre-gyp": { "version": "0.12.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "dev": true, "optional": true, "requires": { @@ -2467,7 +2705,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -2477,13 +2716,15 @@ }, "npm-bundled": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "dev": true, "optional": true, "requires": { @@ -2493,7 +2734,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -2505,19 +2747,22 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, "requires": { @@ -2526,19 +2771,22 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -2548,19 +2796,22 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, "requires": { @@ -2572,7 +2823,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -2580,7 +2832,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, "requires": { @@ -2595,7 +2848,8 @@ }, "rimraf": { "version": "2.6.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, "requires": { @@ -2604,43 +2858,50 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.7.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, "requires": { @@ -2651,7 +2912,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, "requires": { @@ -2660,7 +2922,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, "requires": { @@ -2669,13 +2932,15 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, "requires": { @@ -2690,13 +2955,15 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, "requires": { @@ -2705,13 +2972,15 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true, "optional": true } @@ -2747,6 +3016,12 @@ "integrity": "sha1-mYR1wXhEVobQsyJG2l3428++jqM=", "dev": true }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -2787,12 +3062,26 @@ } }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, + "optional": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } }, "globals": { @@ -2802,9 +3091,9 @@ "dev": true }, "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, "growl": { @@ -2814,9 +3103,9 @@ "dev": true }, "handlebars": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.2.0.tgz", - "integrity": "sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -2893,6 +3182,13 @@ "kind-of": "^4.0.0" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -2921,9 +3217,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", - "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", "dev": true }, "htmlparser2": { @@ -2938,6 +3234,19 @@ "entities": "^1.1.1", "inherits": "^2.0.1", "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "http-signature": { @@ -2961,9 +3270,9 @@ } }, "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.1.tgz", + "integrity": "sha512-DWjnQIFLenVrwyRCKZT+7a7/U4Cqgar4WG8V++K3hw+lrW1hc/SIwdiGmtxKCVACmHULTuGeBbHJmbwW7/sAvA==", "dev": true }, "import-fresh": { @@ -2988,6 +3297,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2999,9 +3314,9 @@ } }, "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "inquirer": { @@ -3023,6 +3338,29 @@ "string-width": "^2.1.0", "strip-ansi": "^5.1.0", "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "invert-kv": { @@ -3041,6 +3379,13 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -3054,15 +3399,15 @@ } }, "is-alphabetical": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.3.tgz", - "integrity": "sha512-eEMa6MKpHFzw38eKm56iNNi6GJ7lf6aLLio7Kr23sJPAECscgRtZvOBYybejWDQ2bM949Y++61PY+udzj5QMLA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.2.tgz", + "integrity": "sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg==", "dev": true }, "is-alphanumerical": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.3.tgz", - "integrity": "sha512-A1IGAPO5AW9vSh7omxIlOGwIqEvpW/TA+DksVOPM5ODuxKlZS09+TEM1E3275lJqO2oJ38vDpeAL3DCIiHE6eA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz", + "integrity": "sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg==", "dev": true, "requires": { "is-alphabetical": "^1.0.0", @@ -3086,9 +3431,9 @@ } }, "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", "dev": true }, "is-callable": { @@ -3107,6 +3452,13 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -3126,9 +3478,9 @@ "dev": true }, "is-decimal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.3.tgz", - "integrity": "sha512-bvLSwoDg2q6Gf+E2LEPiklHZxxiSi3XAh4Mav65mKqTfCO1HM3uBs24TjEH8iJX3bbDdLXKJXBTmGzuTUuAEjQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", + "integrity": "sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg==", "dev": true }, "is-descriptor": { @@ -3191,9 +3543,9 @@ } }, "is-hexadecimal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz", - "integrity": "sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz", + "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==", "dev": true }, "is-js-type": { @@ -3215,6 +3567,13 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -3300,9 +3659,9 @@ "dev": true }, "is-whitespace-character": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz", - "integrity": "sha512-SNPgMLz9JzPccD3nPctcj8sZlX9DAMJSKH8bP7Z6bohCwuNgX8xbWr1eTAYXX9Vpi/aSn8Y1akL9WgM3t43YNQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", + "integrity": "sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ==", "dev": true }, "is-windows": { @@ -3313,9 +3672,15 @@ "optional": true }, "is-word-character": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.3.tgz", - "integrity": "sha512-0wfcrFgOOOBdgRNT9H33xe6Zi6yhX/uoc4U8NBZGeQQB0ctU1dnlNTyL9JM2646bHDTpsDm1Brb3VPoCIMrd/A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.2.tgz", + "integrity": "sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, "isarray": { @@ -3374,9 +3739,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", "dev": true } } @@ -3392,6 +3757,22 @@ "supports-color": "^6.1.0" }, "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -3416,6 +3797,31 @@ "source-map": "^0.6.1" }, "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3455,12 +3861,57 @@ "esprima": "^4.0.0" } }, + "js2xmlparser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.0.tgz", + "integrity": "sha512-WuNgdZOXVmBk5kUPMcTcVUpbGRzLfNkv7+7APq7WiDihpXVKrgxo6wwRpRl9OQeEBgKCVk9mR7RbzrnNWC8oBw==", + "dev": true, + "requires": { + "xmlcreate": "^2.0.0" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsdoc": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.3.tgz", + "integrity": "sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==", + "dev": true, + "requires": { + "@babel/parser": "^7.4.4", + "bluebird": "^3.5.4", + "catharsis": "^0.8.11", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.0", + "klaw": "^3.0.0", + "markdown-it": "^8.4.2", + "markdown-it-anchor": "^5.0.2", + "marked": "^0.7.0", + "mkdirp": "^0.5.1", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.0.1", + "taffydb": "2.6.2", + "underscore": "~1.9.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + } + } + }, "jsdoctypeparser": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-5.0.1.tgz", @@ -3522,6 +3973,15 @@ "dev": true, "optional": true }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, "lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", @@ -3547,6 +4007,15 @@ "type-check": "~0.3.2" } }, + "linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -3656,6 +4125,16 @@ "chalk": "^2.0.1" } }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -3673,25 +4152,18 @@ } }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "semver": "^6.0.0" }, "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", + "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==", "dev": true } } @@ -3712,6 +4184,12 @@ "dev": true, "optional": true }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -3728,6 +4206,31 @@ "integrity": "sha512-XUi5HJhhV5R74k8/0H2oCbCiYf/u4cO/rX8tnGkRvrqhsr5BRNU6Mg0yt/8UIx1iIS8220BNJsDb7XnILhLepw==", "dev": true }, + "markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "markdown-it-anchor": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.4.tgz", + "integrity": "sha512-n8zCGjxA3T+Mx1pG8HEgbJbkB8JFUuRkeTZQuIM8iPY6oQ8sWOPRZJDFC9a/pNg2QkHEjjGkhBEl/RSyzaDZ3A==", + "dev": true + }, + "marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "dev": true + }, "mdn-browser-compat-data": { "version": "0.0.84", "resolved": "https://registry.npmjs.org/mdn-browser-compat-data/-/mdn-browser-compat-data-0.0.84.tgz", @@ -3737,6 +4240,12 @@ "extend": "3.0.2" } }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, "mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -3756,6 +4265,98 @@ } } }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, "merge-source-map": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", @@ -3795,6 +4396,12 @@ "to-regex": "^3.0.2" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -3826,11 +4433,21 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -3856,25 +4473,17 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } } }, "mocha": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", - "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", + "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", "dev": true, "requires": { "ansi-colors": "3.2.3", @@ -3902,15 +4511,6 @@ "yargs-unparser": "1.5.0" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -3951,9 +4551,9 @@ "dev": true }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -3974,12 +4574,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, "supports-color": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", @@ -3992,9 +4586,9 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "mute-stream": { @@ -4062,14 +4656,6 @@ "requires": { "object.getownpropertydescriptors": "^2.0.3", "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } } }, "node-releases": { @@ -4081,6 +4667,17 @@ "semver": "^5.3.0" } }, + "node-static": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.11.tgz", + "integrity": "sha512-zfWC/gICcqb74D9ndyvxZWaI1jzcoHmf4UTHWQchBNuNMxdBLJMDiUgZ1tjGLEIe/BMhj2DxKD8HOuc2062pDQ==", + "dev": true, + "requires": { + "colors": ">=0.6.0", + "mime": "^1.2.9", + "optimist": ">=0.3.4" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -4278,10 +4875,20 @@ "path-exists": "^3.0.0" } }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -4301,6 +4908,12 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true } } }, @@ -4344,6 +4957,13 @@ "is-descriptor": "^0.1.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4356,16 +4976,10 @@ } } }, - "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", - "dev": true - }, "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", "dev": true }, "object-visit": { @@ -4446,6 +5060,28 @@ "mimic-fn": "^1.0.0" } }, + "open": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.3.0.tgz", + "integrity": "sha512-6AHdrJxPvAXIowO/aIaeHZ8CeMdDf7qCyRNq8NwJpinmCdXhz+NZR7ie1Too94lpciCDsG+qHGO9Mt0svA4OqA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "open-cli": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-5.0.0.tgz", + "integrity": "sha512-Y2KQDS6NqNtk+PSXzSgwH41vTDMRndwFgVWsfgMhXv7lNe1cImLCe19Vo8oKwMsL7WeNsGmmbX7Ml74Ydj61Cg==", + "dev": true, + "requires": { + "file-type": "^11.0.0", + "get-stdin": "^7.0.0", + "meow": "^5.0.0", + "open": "^6.3.0", + "temp-write": "^4.0.0" + } + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -4456,12 +5092,6 @@ "wordwrap": "~0.0.2" }, "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", @@ -4571,9 +5201,9 @@ } }, "parse-entities": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", - "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.1.tgz", + "integrity": "sha512-NBWYLQm1KSoDKk7GAHyioLTvCZ5QjdH/ASBBQTD3iLiAWJXS5bg1jEWI8nIJ+vgVvsceBVBcDGRWSo0KVQBvvg==", "dev": true, "requires": { "character-entities": "^1.0.0", @@ -4696,9 +5326,9 @@ } }, "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, @@ -4748,6 +5378,12 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -4770,14 +5406,19 @@ } }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -4790,41 +5431,16 @@ "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" } }, "regenerator-runtime": { @@ -4972,6 +5588,23 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "requizzle": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + }, + "dependencies": { + "lodash": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", + "dev": true + } + } + }, "reserved-words": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", @@ -4979,9 +5612,9 @@ "dev": true }, "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", + "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -5045,9 +5678,9 @@ } }, "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safe-regex": { @@ -5066,9 +5699,9 @@ "dev": true }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, "set-blocking": { @@ -5256,6 +5889,13 @@ "kind-of": "^3.2.0" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -5296,9 +5936,9 @@ "optional": true }, "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", + "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", "dev": true, "requires": { "foreground-child": "^1.5.6", @@ -5336,9 +5976,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", "dev": true }, "split-string": { @@ -5375,9 +6015,9 @@ } }, "state-toggle": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.2.tgz", - "integrity": "sha512-8LpelPGR0qQM4PnfLiplOQNJcIN1/r2Gy0xKB2zKnIW2YzPMt2sR4I/+gtPjhN7Svh9kw+zqEg2SFwpBO9iNiw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.1.tgz", + "integrity": "sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og==", "dev": true }, "static-extend": { @@ -5411,63 +6051,24 @@ "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" } }, "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "ansi-regex": "^3.0.0" } }, "strip-bom": { @@ -5482,16 +6083,22 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5509,6 +6116,18 @@ "string-width": "^3.0.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -5519,6 +6138,48 @@ "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true + }, + "temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", + "dev": true + }, + "temp-write": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/temp-write/-/temp-write-4.0.0.tgz", + "integrity": "sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "is-stream": "^2.0.0", + "make-dir": "^3.0.0", + "temp-dir": "^1.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true } } }, @@ -5566,9 +6227,9 @@ } }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -5645,7 +6306,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -5674,6 +6335,13 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -5746,6 +6414,12 @@ "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", "dev": true }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -5753,27 +6427,27 @@ "dev": true }, "trim-trailing-lines": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.2.tgz", - "integrity": "sha512-MUjYItdrqqj2zpcHFTkMa9WAv4JHTI6gnRQGPFLrt5L9a6tRMiDnIqYl8JBvu2d2Tc3lWJKQwlGCp0K8AvCM+Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz", + "integrity": "sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg==", "dev": true }, "trough": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.4.tgz", - "integrity": "sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.3.tgz", + "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==", "dev": true }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.0.tgz", + "integrity": "sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -5810,9 +6484,15 @@ "dev": true }, "typescript": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", - "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", + "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", + "dev": true + }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true }, "uglify-js": { @@ -5835,10 +6515,16 @@ } } }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", + "dev": true + }, "unherit": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.2.tgz", - "integrity": "sha512-W3tMnpaMG7ZY6xe/moK04U9fBhi6wEiCYHUW5Mop/wQHf12+79EQGwxYejNdhEz2mkqkBlGwm7pxmgBKMVUj0w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", + "integrity": "sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -5870,18 +6556,43 @@ "get-value": "^2.0.6", "is-extendable": "^0.1.1", "set-value": "^2.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + } + } } }, "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.2.tgz", + "integrity": "sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw==", "dev": true }, "unist-util-remove-position": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.3.tgz", - "integrity": "sha512-CtszTlOjP2sBGYc2zcKA/CvNdTdEs3ozbiJ63IPBxh8iZg42SCCb8m04f8z2+V1aSk5a7BxbZKEdoDjadmBkWA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz", + "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==", "dev": true, "requires": { "unist-util-visit": "^1.1.0" @@ -5894,21 +6605,21 @@ "dev": true }, "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.0.tgz", + "integrity": "sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw==", "dev": true, "requires": { "unist-util-visit-parents": "^2.0.0" } }, "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz", + "integrity": "sha512-6B0UTiMfdWql4cQ03gDTCSns+64Zkfo2OCbK31Ov0uMizEz+CJeAp0cgZVb5Fhmcd7Bct2iRNywejT0orpbqUA==", "dev": true, "requires": { - "unist-util-is": "^3.0.0" + "unist-util-is": "^2.1.2" } }, "unset-value": { @@ -5956,9 +6667,9 @@ } }, "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", "dev": true, "optional": true }, @@ -5992,9 +6703,9 @@ "dev": true }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, "v8-compile-cache": { @@ -6034,12 +6745,20 @@ "replace-ext": "1.0.0", "unist-util-stringify-position": "^1.0.0", "vfile-message": "^1.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + } } }, "vfile-location": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.5.tgz", - "integrity": "sha512-Pa1ey0OzYBkLPxPZI3d9E+S4BmvfVwNAAXrrqGbwTVXWaX2p9kM1zZ+n35UtVM06shmWKH4RPRN8KI80qE3wNQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.4.tgz", + "integrity": "sha512-KRL5uXQPoUKu+NGvQVL4XLORw45W62v4U4gxJ3vRlDfI9QsT4ZN1PNXn/zQpKUulqGDpYuT0XDfp5q9O87/y/w==", "dev": true }, "vfile-message": { @@ -6063,6 +6782,17 @@ "espree": "^4.1.0", "esquery": "^1.0.1", "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } } }, "which": { @@ -6180,10 +6910,16 @@ "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", "dev": true }, + "xmlcreate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.1.tgz", + "integrity": "sha512-MjGsXhKG8YjTKrDCXseFo3ClbMGvUD4en29H2Cev1dv4P/chlpw6KdYmlCWDkhosBVKRDjM836+3e3pm1cBNJA==", + "dev": true + }, "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, "y18n": { @@ -6217,6 +6953,12 @@ "yargs-parser": "^13.0.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -6237,9 +6979,9 @@ } }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -6270,6 +7012,15 @@ "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } } } }, @@ -6320,9 +7071,9 @@ } }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" diff --git a/package.json b/package.json index dbe3cca1..9629bad9 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,7 @@ "url": "https://github.com/orgs/passport-next/people" }, "homepage": "https://github.com/passport-next/passport", - "contributors": [ - "Brett Zamir" - ], + "contributors": [], "bugs": { "url": "https://github.com/passport-next/passport/issues" }, @@ -38,9 +36,12 @@ "eslint-plugin-sonarjs": "^0.4.0", "eslint-plugin-standard": "^4.0.1", "eslint-plugin-unicorn": "^10.0.0", + "jsdoc": "^3.6.2", "mocha": "6.x.x", + "node-static": "^0.7.11", "nunjucks": "^3.2.0", "nyc": "^14.1.1", + "open-cli": "^5.0.0", "typescript": "^3.6.2" }, "engines": { @@ -60,11 +61,15 @@ "url": "https://github.com/passport-next/passport" }, "scripts": { + "start": "static -p 8003", + "build-docs": "rm -rf docs/jsdoc/*;jsdoc --pedantic -c docs/jsdoc-config.js lib", + "open-docs": "open-cli http://localhost:8003/docs/jsdoc/ && npm start", "templates": "node ./templates/gen.js", "init-new-project": "node ./templates/gen --init", "lint": "eslint --max-warnings 0 --ext js,md .", "lintfix": "eslint --ext js,md . --fix", "testonly": "mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js", + "test-one": "mocha --reporter spec --require test/bootstrap/node", "test": "nyc --report-dir=var/coverage --reporter=lcov --reporter=text npm run testonly", "coveralls": "nyc report --report-dir=var/coverage --reporter=text-lcov | coveralls" } diff --git a/templates/package.json.j2 b/templates/package.json.j2 index 5183b039..1c65898d 100644 --- a/templates/package.json.j2 +++ b/templates/package.json.j2 @@ -35,9 +35,12 @@ "eslint-plugin-sonarjs": "^0.4.0", "eslint-plugin-standard": "^4.0.1", "eslint-plugin-unicorn": "^10.0.0", + "jsdoc": "^3.6.2", "mocha": "6.x.x", + "node-static": "^0.7.11", "nunjucks": "^3.2.0", "nyc": "^14.1.1", + "open-cli": "^5.0.0" "typescript": "^3.6.2" }, "engines": { @@ -57,11 +60,15 @@ "url": "{{github}}" }, "scripts": { + "start": "static -p 8003", + "build-docs": "rm -rf docs/jsdoc/*;jsdoc --pedantic -c docs/jsdoc-config.js lib", + "open-docs": "open-cli http://localhost:8003/docs/jsdoc/ && npm start", "templates": "node ./templates/gen.js", "init-new-project": "node ./templates/gen --init", "lint": "eslint --max-warnings 0 --ext js,md .", "lintfix": "eslint --ext js,md . --fix", "testonly": "mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js", + "test-one": "mocha --reporter spec --require test/bootstrap/node", "test": "nyc --report-dir=var/coverage --reporter=lcov --reporter=text npm run testonly", "coveralls": "nyc report --report-dir=var/coverage --reporter=text-lcov | coveralls" } diff --git a/test/authenticator.middleware.test.js b/test/authenticator.middleware.test.js index 519ca5db..3a0b4bbf 100644 --- a/test/authenticator.middleware.test.js +++ b/test/authenticator.middleware.test.js @@ -1,5 +1,3 @@ -/* eslint-disable no-shadow */ - 'use strict'; const chai = require('chai'); @@ -128,9 +126,8 @@ describe('Authenticator', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -184,9 +181,8 @@ describe('Authenticator', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -227,8 +223,8 @@ describe('Authenticator', () => { describe('handling a request', () => { const passport = new Authenticator(); - passport.deserializeUser((user, done) => { - done(null, { id: user }); + passport.deserializeUser((req, user) => { + return { id: user }; }); let request; diff --git a/test/authenticator.promise.test.js b/test/authenticator.promise.test.js new file mode 100644 index 00000000..8a94141f --- /dev/null +++ b/test/authenticator.promise.test.js @@ -0,0 +1,1013 @@ +'use strict'; + +const Authenticator = require('../lib/authenticator'); + + +describe('Authenticator (Promise return)', () => { + describe('#sessionManager', () => { + it('should set custom session manager', () => { + const passport = new Authenticator(); + const sessionManager = {}; + passport.sessionManager(sessionManager); + + expect(passport._sm).to.equal(sessionManager); + }); + }); + + describe('#use', () => { + describe('with instance name', () => { + class Strategy { + constructor() { + this.name = 'default'; + } + + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use(new Strategy()); + + it('should register strategy', () => { + expect(authenticator._strategies.default).to.be.an('object'); + }); + }); + + describe('with registered name', () => { + class Strategy { + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use('foo', new Strategy()); + + it('should register strategy', () => { + expect(authenticator._strategies.foo).to.be.an('object'); + }); + }); + + describe('with registered name overriding instance name', () => { + class Strategy { + constructor() { + this.name = 'default'; + } + + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use('bar', new Strategy()); + + it('should register strategy', () => { + expect(authenticator._strategies.bar).to.be.an('object'); + // eslint-disable-next-line no-unused-expressions + expect(authenticator._strategies.default).to.be.undefined; + }); + }); + + it('should throw if lacking a name', () => { + class Strategy { + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + expect(() => { + const authenticator = new Authenticator(); + authenticator.use(new Strategy()); + }).to.throw(Error, 'Authentication strategies must have a name'); + }); + }); + + + describe('#unuse', () => { + class Strategy { + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use('one', new Strategy()); + authenticator.use('two', new Strategy()); + + expect(authenticator._strategies.one).to.be.an('object'); + expect(authenticator._strategies.two).to.be.an('object'); + + authenticator.unuse('one'); + + it('should unregister strategy', () => { + // eslint-disable-next-line no-unused-expressions + expect(authenticator._strategies.one).to.be.undefined; + expect(authenticator._strategies.two).to.be.an('object'); + }); + }); + + + describe('#serializeUser', () => { + describe('without serializers', () => { + const authenticator = new Authenticator(); + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to serialize user into session'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((req, user) => { + return Promise.resolve(user.id); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('1'); + }); + }); + + describe('with one serializer that serializes to 0', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + return Promise.resolve(0); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal(0); + }); + }); + + describe('with one serializer that serializes to false', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + return Promise.resolve(false); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to serialize user into session'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer that serializes to null', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + return Promise.resolve(null); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to serialize user into session'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer that encounters an error', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + return Promise.reject(new Error('something went wrong')); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went wrong'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer that throws an exception', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser(() => { + return Promise.reject(new Error('something went horribly wrong')); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went horribly wrong'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with three serializers, the first of which passes and the second of which serializes', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser(() => { + throw new Error('pass'); + }); + authenticator.serializeUser((/* req, user */) => { + return Promise.resolve('two'); + }); + authenticator.serializeUser((/* req, user */) => { + return Promise.resolve('three'); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('two'); + }); + }); + + describe('with three serializers, the first of which passes and the second of which does not serialize by no argument', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.serializeUser((req, user, done) => { + done(null); + }); + authenticator.serializeUser((/* req, user */) => { + return Promise.resolve('three'); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('three'); + }); + }); + + describe('with three serializers, the first of which passes and the second of which does not serialize by undefined', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.serializeUser((req, user, done) => { + done(null, undefined); + }); + authenticator.serializeUser((/* req, user */) => { + return Promise.resolve('three'); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('three'); + }); + }); + + describe('with one serializer that takes request as argument', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((req, user) => { + if (req.url !== '/foo') { + return Promise.reject(new Error('incorrect req argument')); + } + return Promise.resolve(user.id); + }); + + let error; + let obj; + + before((done) => { + const req = { url: '/foo' }; + + authenticator.serializeUser({ id: '1', username: 'jared' }, req, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('1'); + }); + }); + }); + + + describe('#deserializeUser', () => { + describe('without deserializers', () => { + const authenticator = new Authenticator(); + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to deserialize user out of session'); + }); + + it('should not deserialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.undefined; + }); + }); + + describe('with one deserializer', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((req, obj) => { + return Promise.resolve(obj.username); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('jared'); + }); + }); + + describe('with one deserializer that deserializes to false', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve(false); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with one deserializer that deserializes to null', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve(null); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with one deserializer that encounters an error', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.reject(new Error('something went wrong')); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went wrong'); + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.undefined; + }); + }); + + describe('with one deserializer that throws an exception', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser(() => { + return Promise.reject(new Error('something went horribly wrong')); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went horribly wrong'); + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.undefined; + }); + }); + + describe('with three deserializers, the first of which passes and the second of which deserializes', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve('two'); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve('three'); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('two'); + }); + }); + + describe('with three deserializers, the first of which passes and the second of which does not deserialize by no argument', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((req, obj, done) => { + done(null); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve('three'); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('three'); + }); + }); + + describe('with three deserializers, the first of which passes and the second of which does not deserialize by undefined', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((req, obj, done) => { + done(null, undefined); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve('three'); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('three'); + }); + }); + + describe('with three deserializers, the first of which passes and the second of which invalidates session by false', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve(false); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve('three'); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with three deserializers, the first of which passes and the second of which invalidates session by null', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve(null); + }); + authenticator.deserializeUser((/* req, obj */) => { + return Promise.resolve('three'); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with one deserializer that takes request as argument', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((req, obj) => { + if (req.url !== '/foo') { + return Promise.reject(new Error('incorrect req argument')); + } + return Promise.resolve(obj.username); + }); + + let error; + let user; + + before((done) => { + const req = { url: '/foo' }; + + authenticator.deserializeUser({ id: '1', username: 'jared' }, req, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('jared'); + }); + }); + }); + + + describe('#transformAuthInfo', () => { + describe('without transforms', () => { + const authenticator = new Authenticator(); + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.scope).to.equal('write'); + }); + }); + + describe('with one transform', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((req, info) => { + return Promise.resolve({ clientId: info.clientId, client: { name: 'Foo' } }); + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Foo'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + + describe('with one transform that encounters an error', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((/* req, info */) => { + return Promise.reject(new Error('something went wrong')); + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went wrong'); + }); + + it('should not transform info', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one transform that throws an exception', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo(() => { + return Promise.reject(new Error('something went horribly wrong')); + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went horribly wrong'); + }); + + it('should not transform info', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one sync transform', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((req, info) => { + return Promise.resolve({ clientId: info.clientId, client: { name: 'Foo' } }); + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Foo'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + + describe('with three transform, the first of which passes and the second of which transforms', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.transformAuthInfo((req, info) => { + return Promise.resolve({ clientId: info.clientId, client: { name: 'Two' } }); + }); + authenticator.transformAuthInfo((req, info) => { + return Promise.resolve({ clientId: info.clientId, client: { name: 'Three' } }); + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Two'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + + describe('with one transform that takes request as argument', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((req, info) => { + if (req.url !== '/foo') { + return Promise.reject(new Error('incorrect req argument')); + } + return Promise.resolve({ clientId: info.clientId, client: { name: 'Foo' } }); + }); + + let error; + let obj; + + before((done) => { + const req = { url: '/foo' }; + + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, req, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Foo'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + }); +}); diff --git a/test/authenticator.sync.test.js b/test/authenticator.sync.test.js new file mode 100644 index 00000000..90a01f77 --- /dev/null +++ b/test/authenticator.sync.test.js @@ -0,0 +1,1011 @@ +'use strict'; + +const Authenticator = require('../lib/authenticator'); + + +describe('Authenticator (Sync return)', () => { + describe('#sessionManager', () => { + it('should set custom session manager', () => { + const passport = new Authenticator(); + const sessionManager = {}; + passport.sessionManager(sessionManager); + + expect(passport._sm).to.equal(sessionManager); + }); + }); + + describe('#use', () => { + describe('with instance name', () => { + class Strategy { + constructor() { + this.name = 'default'; + } + + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use(new Strategy()); + + it('should register strategy', () => { + expect(authenticator._strategies.default).to.be.an('object'); + }); + }); + + describe('with registered name', () => { + class Strategy { + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use('foo', new Strategy()); + + it('should register strategy', () => { + expect(authenticator._strategies.foo).to.be.an('object'); + }); + }); + + describe('with registered name overriding instance name', () => { + class Strategy { + constructor() { + this.name = 'default'; + } + + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use('bar', new Strategy()); + + it('should register strategy', () => { + expect(authenticator._strategies.bar).to.be.an('object'); + // eslint-disable-next-line no-unused-expressions + expect(authenticator._strategies.default).to.be.undefined; + }); + }); + + it('should throw if lacking a name', () => { + class Strategy { + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + expect(() => { + const authenticator = new Authenticator(); + authenticator.use(new Strategy()); + }).to.throw(Error, 'Authentication strategies must have a name'); + }); + }); + + + describe('#unuse', () => { + class Strategy { + // eslint-disable-next-line class-methods-use-this + authenticate() {} + } + + const authenticator = new Authenticator(); + authenticator.use('one', new Strategy()); + authenticator.use('two', new Strategy()); + + expect(authenticator._strategies.one).to.be.an('object'); + expect(authenticator._strategies.two).to.be.an('object'); + + authenticator.unuse('one'); + + it('should unregister strategy', () => { + // eslint-disable-next-line no-unused-expressions + expect(authenticator._strategies.one).to.be.undefined; + expect(authenticator._strategies.two).to.be.an('object'); + }); + }); + + + describe('#serializeUser', () => { + describe('without serializers', () => { + const authenticator = new Authenticator(); + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to serialize user into session'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((req, user) => { + return user.id; + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('1'); + }); + }); + + describe('with one serializer that serializes to 0', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + return 0; + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal(0); + }); + }); + + describe('with one serializer that serializes to false', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + return false; + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to serialize user into session'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer that serializes to null', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + return null; + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to serialize user into session'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer that encounters an error', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, user */) => { + throw new Error('something went wrong'); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went wrong'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one serializer that throws an exception', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser(() => { + throw new Error('something went horribly wrong'); + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went horribly wrong'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with three serializers, the first of which passes and the second of which serializes', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.serializeUser((/* req, user */) => { + return 'two'; + }); + authenticator.serializeUser((/* req, user */) => { + return 'three'; + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('two'); + }); + }); + + describe('with three serializers, the first of which passes and the second of which does not serialize by no argument', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.serializeUser((req, user, done) => { + done(null); + }); + authenticator.serializeUser((/* req, user */) => { + return 'three'; + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('three'); + }); + }); + + describe('with three serializers, the first of which passes and the second of which does not serialize by undefined', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.serializeUser((req, user, done) => { + done(null, undefined); + }); + authenticator.serializeUser((/* req, user */) => { + return 'three'; + }); + + let error; + let obj; + + before((done) => { + authenticator.serializeUser({ id: '1', username: 'jared' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('three'); + }); + }); + + describe('with one serializer that takes request as argument', () => { + const authenticator = new Authenticator(); + authenticator.serializeUser((req, user) => { + if (req.url !== '/foo') { + throw new Error('incorrect req argument'); + } + return user.id; + }); + + let error; + let obj; + + before((done) => { + const req = { url: '/foo' }; + + authenticator.serializeUser({ id: '1', username: 'jared' }, req, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should serialize user', () => { + expect(obj).to.equal('1'); + }); + }); + }); + + + describe('#deserializeUser', () => { + describe('without deserializers', () => { + const authenticator = new Authenticator(); + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('Failed to deserialize user out of session'); + }); + + it('should not deserialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.undefined; + }); + }); + + describe('with one deserializer', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((req, obj) => { + return obj.username; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('jared'); + }); + }); + + describe('with one deserializer that deserializes to false', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + return false; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with one deserializer that deserializes to null', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + return null; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with one deserializer that encounters an error', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('something went wrong'); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went wrong'); + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.undefined; + }); + }); + + describe('with one deserializer that throws an exception', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser(() => { + throw new Error('something went horribly wrong'); + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went horribly wrong'); + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.undefined; + }); + }); + + describe('with three deserializers, the first of which passes and the second of which deserializes', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((/* req, obj */) => { + return 'two'; + }); + authenticator.deserializeUser((/* req, obj */) => { + return 'three'; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('two'); + }); + }); + + describe('with three deserializers, the first of which passes and the second of which does not deserialize by no argument', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((req, obj, done) => { + done(null); + }); + authenticator.deserializeUser((/* req, obj */) => { + return 'three'; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('three'); + }); + }); + + describe('with three deserializers, the first of which passes and the second of which does not deserialize by undefined', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((req, obj, done) => { + done(null, undefined); + }); + authenticator.deserializeUser((/* req, obj */) => { + return 'three'; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('three'); + }); + }); + + describe('with three deserializers, the first of which passes and the second of which invalidates session by false', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((/* req, obj */) => { + return false; + }); + authenticator.deserializeUser((/* req, obj */) => { + return 'three'; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with three deserializers, the first of which passes and the second of which invalidates session by null', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.deserializeUser((/* req, obj */) => { + return null; + }); + authenticator.deserializeUser((/* req, obj */) => { + return 'three'; + }); + + let error; + let user; + + before((done) => { + authenticator.deserializeUser({ id: '1', username: 'jared' }, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should invalidate session', () => { + // eslint-disable-next-line no-unused-expressions + expect(user).to.be.false; + }); + }); + + describe('with one deserializer that takes request as argument', () => { + const authenticator = new Authenticator(); + authenticator.deserializeUser((req, obj) => { + if (req.url !== '/foo') { + throw new Error('incorrect req argument'); + } + return obj.username; + }); + + let error; + let user; + + before((done) => { + const req = { url: '/foo' }; + + authenticator.deserializeUser({ id: '1', username: 'jared' }, req, (err, u) => { + error = err; + user = u; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should deserialize user', () => { + expect(user).to.equal('jared'); + }); + }); + }); + + + describe('#transformAuthInfo', () => { + describe('without transforms', () => { + const authenticator = new Authenticator(); + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.scope).to.equal('write'); + }); + }); + + describe('with one transform', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((req, info) => { + return { clientId: info.clientId, client: { name: 'Foo' } }; + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Foo'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + + describe('with one transform that encounters an error', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((/* req, info */) => { + throw new Error('something went wrong'); + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went wrong'); + }); + + it('should not transform info', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one transform that throws an exception', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo(() => { + throw new Error('something went horribly wrong'); + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should error', () => { + expect(error).to.be.an.instanceOf(Error); + expect(error.message).to.equal('something went horribly wrong'); + }); + + it('should not transform info', () => { + // eslint-disable-next-line no-unused-expressions + expect(obj).to.be.undefined; + }); + }); + + describe('with one sync transform', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((req, info) => ({ clientId: info.clientId, client: { name: 'Foo' } })); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Foo'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + + describe('with three transform, the first of which passes and the second of which transforms', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((/* req, obj */) => { + throw new Error('pass'); + }); + authenticator.transformAuthInfo((req, info) => { + return { clientId: info.clientId, client: { name: 'Two' } }; + }); + authenticator.transformAuthInfo((req, info) => { + return { clientId: info.clientId, client: { name: 'Three' } }; + }); + + let error; + let obj; + + before((done) => { + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Two'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + + describe('with one transform that takes request as argument', () => { + const authenticator = new Authenticator(); + authenticator.transformAuthInfo((req, info) => { + if (req.url !== '/foo') { + throw new Error('incorrect req argument'); + } + return { clientId: info.clientId, client: { name: 'Foo' } }; + }); + + let error; + let obj; + + before((done) => { + const req = { url: '/foo' }; + + authenticator.transformAuthInfo({ clientId: '1', scope: 'write' }, req, (err, o) => { + error = err; + obj = o; + done(); + }); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should not transform info', () => { + expect(Object.keys(obj)).to.have.length(2); + expect(obj.clientId).to.equal('1'); + expect(obj.client.name).to.equal('Foo'); + // eslint-disable-next-line no-unused-expressions + expect(obj.scope).to.be.undefined; + }); + }); + }); +}); diff --git a/test/authenticator.test.js b/test/authenticator.test.js index 15a27bfc..95f001ac 100644 --- a/test/authenticator.test.js +++ b/test/authenticator.test.js @@ -47,7 +47,7 @@ describe('Authenticator', () => { }); }); - describe('with registered name overridding instance name', () => { + describe('with registered name overriding instance name', () => { class Strategy { constructor() { this.name = 'default'; @@ -131,7 +131,7 @@ describe('Authenticator', () => { describe('with one serializer', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, user.id); }); @@ -158,7 +158,7 @@ describe('Authenticator', () => { describe('with one serializer that serializes to 0', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, 0); }); @@ -185,7 +185,7 @@ describe('Authenticator', () => { describe('with one serializer that serializes to false', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, false); }); @@ -213,7 +213,7 @@ describe('Authenticator', () => { describe('with one serializer that serializes to null', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, null); }); @@ -241,7 +241,7 @@ describe('Authenticator', () => { describe('with one serializer that serializes to undefined', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, undefined); }); @@ -269,7 +269,7 @@ describe('Authenticator', () => { describe('with one serializer that encounters an error', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(new Error('something went wrong')); }); @@ -325,13 +325,13 @@ describe('Authenticator', () => { describe('with three serializers, the first of which passes and the second of which serializes', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done('pass'); }); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, 'two'); }); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, 'three'); }); @@ -358,13 +358,13 @@ describe('Authenticator', () => { describe('with three serializers, the first of which passes and the second of which does not serialize by no argument', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done('pass'); }); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null); }); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, 'three'); }); @@ -391,13 +391,13 @@ describe('Authenticator', () => { describe('with three serializers, the first of which passes and the second of which does not serialize by undefined', () => { const authenticator = new Authenticator(); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done('pass'); }); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, undefined); }); - authenticator.serializeUser((user, done) => { + authenticator.serializeUser((req, user, done) => { done(null, 'three'); }); @@ -425,8 +425,8 @@ describe('Authenticator', () => { describe('with one serializer that takes request as argument', () => { const authenticator = new Authenticator(); authenticator.serializeUser((req, user, done) => { - if (req.url !== '/foo') { return done(new Error('incorrect req argument')); } - return done(null, user.id); + if (req.url !== '/foo') { done(new Error('incorrect req argument')); return; } + done(null, user.id); }); let error; @@ -481,7 +481,7 @@ describe('Authenticator', () => { describe('with one deserializer', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, obj.username); }); @@ -508,7 +508,7 @@ describe('Authenticator', () => { describe('with one deserializer that deserializes to false', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, false); }); @@ -536,7 +536,7 @@ describe('Authenticator', () => { describe('with one deserializer that deserializes to null', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, null); }); @@ -564,7 +564,7 @@ describe('Authenticator', () => { describe('with one deserializer that deserializes to undefined', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, undefined); }); @@ -592,7 +592,7 @@ describe('Authenticator', () => { describe('with one deserializer that encounters an error', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(new Error('something went wrong')); }); @@ -648,13 +648,13 @@ describe('Authenticator', () => { describe('with three deserializers, the first of which passes and the second of which deserializes', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done('pass'); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, 'two'); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, 'three'); }); @@ -681,13 +681,13 @@ describe('Authenticator', () => { describe('with three deserializers, the first of which passes and the second of which does not deserialize by no argument', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done('pass'); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, 'three'); }); @@ -714,13 +714,13 @@ describe('Authenticator', () => { describe('with three deserializers, the first of which passes and the second of which does not deserialize by undefined', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done('pass'); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, undefined); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, 'three'); }); @@ -747,13 +747,13 @@ describe('Authenticator', () => { describe('with three deserializers, the first of which passes and the second of which invalidates session by false', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done('pass'); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, false); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, 'three'); }); @@ -781,13 +781,13 @@ describe('Authenticator', () => { describe('with three deserializers, the first of which passes and the second of which invalidates session by null', () => { const authenticator = new Authenticator(); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done('pass'); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, null); }); - authenticator.deserializeUser((obj, done) => { + authenticator.deserializeUser((req, obj, done) => { done(null, 'three'); }); @@ -816,8 +816,8 @@ describe('Authenticator', () => { describe('with one deserializer that takes request as argument', () => { const authenticator = new Authenticator(); authenticator.deserializeUser((req, obj, done) => { - if (req.url !== '/foo') { return done(new Error('incorrect req argument')); } - return done(null, obj.username); + if (req.url !== '/foo') { done(new Error('incorrect req argument')); return; } + done(null, obj.username); }); let error; @@ -873,7 +873,7 @@ describe('Authenticator', () => { describe('with one transform', () => { const authenticator = new Authenticator(); - authenticator.transformAuthInfo((info, done) => { + authenticator.transformAuthInfo((req, info, done) => { done(null, { clientId: info.clientId, client: { name: 'Foo' } }); }); @@ -904,7 +904,7 @@ describe('Authenticator', () => { describe('with one transform that encounters an error', () => { const authenticator = new Authenticator(); - authenticator.transformAuthInfo((info, done) => { + authenticator.transformAuthInfo((req, info, done) => { done(new Error('something went wrong')); }); @@ -960,7 +960,7 @@ describe('Authenticator', () => { describe('with one sync transform', () => { const authenticator = new Authenticator(); - authenticator.transformAuthInfo(info => ({ clientId: info.clientId, client: { name: 'Foo' } })); + authenticator.transformAuthInfo((req, info) => ({ clientId: info.clientId, client: { name: 'Foo' } })); let error; let obj; @@ -989,13 +989,13 @@ describe('Authenticator', () => { describe('with three transform, the first of which passes and the second of which transforms', () => { const authenticator = new Authenticator(); - authenticator.transformAuthInfo((info, done) => { + authenticator.transformAuthInfo((req, info, done) => { done('pass'); }); - authenticator.transformAuthInfo((info, done) => { + authenticator.transformAuthInfo((req, info, done) => { done(null, { clientId: info.clientId, client: { name: 'Two' } }); }); - authenticator.transformAuthInfo((info, done) => { + authenticator.transformAuthInfo((req, info, done) => { done(null, { clientId: info.clientId, client: { name: 'Three' } }); }); @@ -1027,8 +1027,8 @@ describe('Authenticator', () => { describe('with one transform that takes request as argument', () => { const authenticator = new Authenticator(); authenticator.transformAuthInfo((req, info, done) => { - if (req.url !== '/foo') { return done(new Error('incorrect req argument')); } - return done(null, { clientId: info.clientId, client: { name: 'Foo' } }); + if (req.url !== '/foo') { done(new Error('incorrect req argument')); return; } + done(null, { clientId: info.clientId, client: { name: 'Foo' } }); }); let error; diff --git a/test/http/request.test.js b/test/http/request.test.js index 547fd70e..028e1742 100644 --- a/test/http/request.test.js +++ b/test/http/request.test.js @@ -51,13 +51,14 @@ describe('http.ServerRequest', () => { req._passport.session = {}; let error; - before((done) => { + before(async () => { const user = { id: '1', username: 'root' }; - req.login(user, { session: false }, (err) => { + try { + await req.login(user, { session: false }); + } catch (err) { error = err; - done(); - }); + } }); it('should not error', () => { @@ -90,13 +91,14 @@ describe('http.ServerRequest', () => { passport._userProperty = 'currentUser'; let error; - before((done) => { + before(async () => { const user = { id: '1', username: 'root' }; - req.login(user, { session: false }, (err) => { + try { + await req.login(user, { session: false }); + } catch (err) { error = err; - done(); - }); + } }); it('should not error', () => { @@ -128,12 +130,12 @@ describe('http.ServerRequest', () => { }); }); - describe('not establishing a session and invoked without a callback', () => { + describe('not establishing a session and invoked without a callback', async () => { const { req } = setupPassport(); req._passport.session = {}; const user = { id: '1', username: 'root' }; - req.login(user, { session: false }); + await req.login(user, { session: false }); it('should be authenticated', () => { // eslint-disable-next-line no-unused-expressions @@ -158,13 +160,14 @@ describe('http.ServerRequest', () => { const { req } = setupPassport(); let error; - before((done) => { + before(async () => { const user = { id: '1', username: 'root' }; - req.login(user, { session: false }, (err) => { + try { + await req.login(user, { session: false }); + } catch (err) { error = err; - done(); - }); + } }); it('should not error', () => { @@ -188,18 +191,19 @@ describe('http.ServerRequest', () => { describe('establishing a session', () => { const { req, passport } = setupPassport(); - passport.serializeUser((user, done) => { + passport.serializeUser((rq, user, done) => { done(null, user.id); }); let error; - before((done) => { + before(async () => { const user = { id: '1', username: 'root' }; - req.login(user, (err) => { + try { + await req.login(user); + } catch (err) { error = err; - done(); - }); + } }); it('should not error', () => { @@ -227,20 +231,21 @@ describe('http.ServerRequest', () => { describe('establishing a session and setting custom user property', () => { const { req, passport } = setupPassport(); - passport.serializeUser((user, done) => { + passport.serializeUser((rq, user, done) => { done(null, user.id); }); passport._userProperty = 'currentUser'; let error; - before((done) => { + before(async () => { const user = { id: '1', username: 'root' }; - req.login(user, (err) => { + try { + await req.login(user); + } catch (err) { error = err; - done(); - }); + } }); it('should not error', () => { @@ -274,19 +279,20 @@ describe('http.ServerRequest', () => { describe('encountering an error when serializing to session', () => { const { req, passport } = setupPassport(); req._passport.session = {}; - passport.serializeUser((user, done) => { + passport.serializeUser((rq, user, done) => { done(new Error('something went wrong')); }); let error; - before((done) => { + before(async () => { const user = { id: '1', username: 'root' }; - req.login(user, (err) => { + try { + await req.login(user); + } catch (err) { error = err; - done(); - }); + } }); it('should error', () => { @@ -314,16 +320,16 @@ describe('http.ServerRequest', () => { describe('establishing a session, but not passing a callback argument', () => { const { req, passport } = setupPassport(); - passport.serializeUser((user, done) => { - done(null, user.id); + passport.serializeUser(() => { + return Promise.resolve(user.id); }); const user = { id: '1', username: 'root' }; - it('should throw an exception', () => { - expect(() => { - req.login(user); - }).to.throw(Error, 'req#login requires a callback function'); + it('should not throw an exception', () => { + expect(async () => { + await req.login(user); + }).to.not.throw(Error, 'req#login no longer requires a callback function'); }); }); }); diff --git a/test/middleware/authenticate.redirect.test.js b/test/middleware/authenticate.redirect.test.js index 72300793..ac14093f 100644 --- a/test/middleware/authenticate.redirect.test.js +++ b/test/middleware/authenticate.redirect.test.js @@ -72,9 +72,8 @@ describe('middleware/authenticate', () => { done(); }; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { diff --git a/test/middleware/authenticate.success.flash.test.js b/test/middleware/authenticate.success.flash.test.js index fc71e3a3..d482b9a8 100644 --- a/test/middleware/authenticate.success.flash.test.js +++ b/test/middleware/authenticate.success.flash.test.js @@ -1,4 +1,3 @@ -/* eslint-disable no-shadow */ 'use strict'; const chai = require('chai'); @@ -31,9 +30,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -86,9 +84,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -141,9 +138,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -196,9 +192,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -251,9 +246,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -309,9 +303,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -364,9 +357,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -419,9 +411,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -474,9 +465,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -529,9 +519,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -587,9 +576,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -642,9 +630,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -697,9 +684,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -752,9 +738,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -807,9 +792,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -865,9 +849,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -920,9 +903,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -975,9 +957,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -1030,9 +1011,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; @@ -1085,9 +1065,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; req.flash = function flash(type, msg) { this.message = { type, msg }; diff --git a/test/middleware/authenticate.success.info.test.js b/test/middleware/authenticate.success.info.test.js index f4e00da8..548e0cba 100644 --- a/test/middleware/authenticate.success.info.test.js +++ b/test/middleware/authenticate.success.info.test.js @@ -1,4 +1,3 @@ -/* eslint-disable no-shadow */ 'use strict'; const chai = require('chai'); @@ -26,9 +25,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -67,8 +65,8 @@ describe('middleware/authenticate', () => { const passport = new Passport(); passport.use('success', new Strategy()); - passport.transformAuthInfo((info, done) => { - done(null, { clientId: info.clientId, client: { name: 'Foo' }, scope: info.scope }); + passport.transformAuthInfo((req, info) => { + return { clientId: info.clientId, client: { name: 'Foo' }, scope: info.scope }; }); let request; @@ -79,9 +77,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -121,8 +118,8 @@ describe('middleware/authenticate', () => { const passport = new Passport(); passport.use('success', new Strategy()); - passport.transformAuthInfo((info, done) => { - done(new Error('something went wrong')); + passport.transformAuthInfo((/* req, info */) => { + throw new Error('something went wrong'); }); let request; @@ -133,9 +130,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -181,9 +177,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { diff --git a/test/middleware/authenticate.success.message.test.js b/test/middleware/authenticate.success.message.test.js index b88c3391..759dac57 100644 --- a/test/middleware/authenticate.success.message.test.js +++ b/test/middleware/authenticate.success.message.test.js @@ -1,4 +1,3 @@ -/* eslint-disable no-shadow */ 'use strict'; const chai = require('chai'); @@ -30,9 +29,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { @@ -83,9 +81,8 @@ describe('middleware/authenticate', () => { req.session = {}; req.session.messages = ['I exist!']; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { @@ -136,9 +133,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { @@ -188,9 +184,8 @@ describe('middleware/authenticate', () => { request = req; req.session = {}; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { diff --git a/test/middleware/authenticate.success.multi.test.js b/test/middleware/authenticate.success.multi.test.js index 7f8f1484..68024304 100644 --- a/test/middleware/authenticate.success.multi.test.js +++ b/test/middleware/authenticate.success.multi.test.js @@ -1,4 +1,3 @@ -/* eslint-disable no-shadow */ 'use strict'; const chai = require('chai'); @@ -32,9 +31,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -80,9 +78,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { diff --git a/test/middleware/authenticate.success.test.js b/test/middleware/authenticate.success.test.js index becfb4b7..85134b68 100644 --- a/test/middleware/authenticate.success.test.js +++ b/test/middleware/authenticate.success.test.js @@ -1,4 +1,3 @@ -/* eslint-disable no-shadow */ 'use strict'; const chai = require('chai'); @@ -26,9 +25,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -74,9 +72,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { @@ -130,11 +127,12 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - // eslint-disable-next-line consistent-return - req.logIn = function logIn(user, options, done) { - if (options.scope !== 'email') { return done(new Error('invalid options')); } + req.logIn = function logIn(user, options) { + if (options.scope !== 'email') { + return Promise.reject(new Error('invalid options')); + } this.user = user; - done(); + return Promise.resolve(); }; }) .next((err) => { @@ -181,9 +179,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { @@ -230,9 +227,8 @@ describe('middleware/authenticate', () => { request = req; req.session = { returnTo: 'http://www.example.com/return' }; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { @@ -283,9 +279,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .end((res) => { @@ -331,8 +326,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { - done(new Error('something went wrong')); + req.logIn = function logIn() { + return Promise.reject(new Error('something went wrong')); }; }) .next((err) => { diff --git a/test/middleware/authenticate.test.js b/test/middleware/authenticate.test.js index fcf82039..dd2eb5e3 100644 --- a/test/middleware/authenticate.test.js +++ b/test/middleware/authenticate.test.js @@ -1,4 +1,3 @@ -/* eslint-disable no-shadow */ 'use strict'; const chai = require('chai'); @@ -22,9 +21,8 @@ describe('middleware/authenticate', () => { .req((req) => { request = req; - req.logIn = function logIn(user, options, done) { + req.logIn = function logIn(user) { this.user = user; - done(); }; }) .next((err) => { diff --git a/test/strategies/session.test.js b/test/strategies/session.test.js index 81ecdf3e..a5a8d898 100644 --- a/test/strategies/session.test.js +++ b/test/strategies/session.test.js @@ -42,8 +42,8 @@ describe('SessionStrategy', () => { }); describe('handling a request with a login session', () => { - const strategy = new SessionStrategy((user, req, done) => { - done(null, { id: user }); + const strategy = new SessionStrategy((user) => { + return { id: user }; }); let request; @@ -83,8 +83,8 @@ describe('SessionStrategy', () => { }); describe('handling a request with a login session serialized to 0', () => { - const strategy = new SessionStrategy((user, req, done) => { - done(null, { id: user }); + const strategy = new SessionStrategy((user) => { + return { id: user }; }); let request; @@ -124,8 +124,8 @@ describe('SessionStrategy', () => { }); describe('handling a request with a login session that has been invalidated', () => { - const strategy = new SessionStrategy((user, req, done) => { - done(null, false); + const strategy = new SessionStrategy((/* user, req */) => { + return false; }); let request; @@ -166,8 +166,8 @@ describe('SessionStrategy', () => { }); describe('handling a request with a login session and setting custom user property', () => { - const strategy = new SessionStrategy((user, req, done) => { - done(null, { id: user }); + const strategy = new SessionStrategy((user) => { + return { id: user }; }); let request; @@ -208,8 +208,8 @@ describe('SessionStrategy', () => { }); describe('handling a request with a login session that encounters an error when deserializing', () => { - const strategy = new SessionStrategy((user, req, done) => { - done(new Error('something went wrong')); + const strategy = new SessionStrategy(() => { + throw new Error('something went wrong'); }); let request; From fb9d5b089caee53c968d36e2378354e235459fbe Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 26 Jun 2019 08:01:49 +0800 Subject: [PATCH 02/12] - Linting (ESLint): Avoid "useless" catch error; adhere to stricter jsdoc context checking - Minor fixes for template files for gitignore/package.json/README --- .eslintrc.js | 4 ++-- .gitignore | 1 - lib/http/request.js | 1 + lib/index.js | 6 +++++- lib/sessionmanager.js | 2 ++ lib/strategies/session.js | 1 + templates/.gitignore.j2 | 2 +- templates/README.md.j2 | 3 +-- 8 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 752921c1..a794411d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, extends: [ '@passport-next/eslint-config-passport-next/sauron-node.js', - // Override ash-nazg's current preference for ESM + // Override eslint-config-passport-next's current preference for ESM 'plugin:node/recommended-script' ], settings: { @@ -77,7 +77,7 @@ module.exports = { // Disable until ready to tackle 'require-jsdoc': 0, - // Disable current preferences of ash-nazg + // Disable current preferences of eslint-config-passport-next 'import/no-commonjs': 0, 'node/exports-style': 0, diff --git a/.gitignore b/.gitignore index 96e42462..000c70b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ reports/ docs/jsdoc -.github/docs/jsdoc var/ diff --git a/lib/http/request.js b/lib/http/request.js index e3f0a556..42efd408 100644 --- a/lib/http/request.js +++ b/lib/http/request.js @@ -32,6 +32,7 @@ const req = exports = module.exports = {}; * try { * await req.logIn(user); * } catch (err) { + * console.error(err); * throw err; * } * // session saved diff --git a/lib/index.js b/lib/index.js index 8ee9d4b0..3689ab9c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -32,6 +32,10 @@ const SessionStrategy = require('./strategies/session'); * @typedef {external:HttpServerResponse} Response */ +/** +* @external ConnectNextCallback +*/ + /** * This middleware conforms to Connect/Express middleware by * the arguments it accepts. @@ -39,7 +43,7 @@ const SessionStrategy = require('./strategies/session'); * @callback ConnectMiddleware * @param {Request} req * @param {Response} res - * @param {Function} next + * @param {ConnectNextCallback} next * @returns {void} */ diff --git a/lib/sessionmanager.js b/lib/sessionmanager.js index 2328d26f..873417e1 100644 --- a/lib/sessionmanager.js +++ b/lib/sessionmanager.js @@ -55,6 +55,7 @@ class SessionManager { } return; } + /* eslint-disable require-atomic-updates */ if (!req._passport.session) { req._passport.session = {}; } @@ -63,6 +64,7 @@ class SessionManager { req.session = {}; } req.session[this._key] = req._passport.session; + /* eslint-enable require-atomic-updates */ // eslint-disable-next-line no-unused-expressions cb && cb(); } diff --git a/lib/strategies/session.js b/lib/strategies/session.js index 7f17c972..1530b6fa 100644 --- a/lib/strategies/session.js +++ b/lib/strategies/session.js @@ -85,6 +85,7 @@ class SessionStrategy extends Strategy { } else { // TODO: Remove instance access (set by `initialize`) const property = req._passport.instance._userProperty || 'user'; + // eslint-disable-next-line require-atomic-updates req[property] = user; } this.pass(); diff --git a/templates/.gitignore.j2 b/templates/.gitignore.j2 index 369fae62..0f335429 100644 --- a/templates/.gitignore.j2 +++ b/templates/.gitignore.j2 @@ -1,5 +1,5 @@ reports/ -docs/ +docs/jsdoc var/ {% if npmModule == "@passport-next/skel" %} diff --git a/templates/README.md.j2 b/templates/README.md.j2 index d0175afa..4d333ff2 100644 --- a/templates/README.md.j2 +++ b/templates/README.md.j2 @@ -1,4 +1,4 @@ -# {{projectName}} +# {{projectName}} {% include "./includes/status.j2" %} ## About @@ -19,4 +19,3 @@ $ npm install {{npmModule}} ## Contributing Please see [CONTRIBUTING.md]({{github}}/blob/master/CONTRIBUTING.md) - From 2215b0b477df011bcab756bae5742b293c8d2607 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Sat, 13 Jul 2019 08:41:33 -0700 Subject: [PATCH 03/12] - Linting: example whitespace - Docs: more precise jsdoc - Update sessionmanager test per Promises API - Fix template --- .eslintignore | 1 - .gitignore | 1 - lib/http/request.js | 22 +++++++++++----------- lib/sessionmanager.js | 2 -- lib/strategies/session.js | 1 - templates/.gitignore.j2 | 1 - templates/package.json.j2 | 2 +- test/sessionmanager.test.js | 15 +++++++-------- 8 files changed, 19 insertions(+), 26 deletions(-) diff --git a/.eslintignore b/.eslintignore index 43c87dab..b2edd167 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,4 @@ .git -coverage/ node_modules/ var/ docs/jsdoc diff --git a/.gitignore b/.gitignore index 000c70b8..2215e94f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -reports/ docs/jsdoc var/ diff --git a/lib/http/request.js b/lib/http/request.js index 42efd408..23a06ac2 100644 --- a/lib/http/request.js +++ b/lib/http/request.js @@ -26,17 +26,17 @@ const req = exports = module.exports = {}; * @function HttpRequest#logIn * @example * - * req.logIn(user, { session: false }); + * req.logIn(user, { session: false }); * - * (async () => { - * try { - * await req.logIn(user); - * } catch (err) { - * console.error(err); - * throw err; - * } - * // session saved - * })(); + * (async () => { + * try { + * await req.logIn(user); + * } catch (err) { + * console.error(err); + * throw err; + * } + * // session saved + * })(); * * @param {User} user * @param {LogInOptions} options @@ -88,7 +88,7 @@ req.login = req.logIn; /** * Terminate an existing login session. * @function HttpRequest#logOut - * @param {GenericCallback} done + * @param {LogoutCallback} done * @returns {void} * @public */ diff --git a/lib/sessionmanager.js b/lib/sessionmanager.js index 873417e1..2328d26f 100644 --- a/lib/sessionmanager.js +++ b/lib/sessionmanager.js @@ -55,7 +55,6 @@ class SessionManager { } return; } - /* eslint-disable require-atomic-updates */ if (!req._passport.session) { req._passport.session = {}; } @@ -64,7 +63,6 @@ class SessionManager { req.session = {}; } req.session[this._key] = req._passport.session; - /* eslint-enable require-atomic-updates */ // eslint-disable-next-line no-unused-expressions cb && cb(); } diff --git a/lib/strategies/session.js b/lib/strategies/session.js index 1530b6fa..7f17c972 100644 --- a/lib/strategies/session.js +++ b/lib/strategies/session.js @@ -85,7 +85,6 @@ class SessionStrategy extends Strategy { } else { // TODO: Remove instance access (set by `initialize`) const property = req._passport.instance._userProperty || 'user'; - // eslint-disable-next-line require-atomic-updates req[property] = user; } this.pass(); diff --git a/templates/.gitignore.j2 b/templates/.gitignore.j2 index 0f335429..55c9559b 100644 --- a/templates/.gitignore.j2 +++ b/templates/.gitignore.j2 @@ -1,4 +1,3 @@ -reports/ docs/jsdoc var/ diff --git a/templates/package.json.j2 b/templates/package.json.j2 index 1c65898d..3562da64 100644 --- a/templates/package.json.j2 +++ b/templates/package.json.j2 @@ -40,7 +40,7 @@ "node-static": "^0.7.11", "nunjucks": "^3.2.0", "nyc": "^14.1.1", - "open-cli": "^5.0.0" + "open-cli": "^5.0.0", "typescript": "^3.6.2" }, "engines": { diff --git a/test/sessionmanager.test.js b/test/sessionmanager.test.js index 2b39ea2e..78921033 100644 --- a/test/sessionmanager.test.js +++ b/test/sessionmanager.test.js @@ -20,8 +20,8 @@ describe('SessionManager', () => { }); }); describe('#logIn', () => { - const func = (user, req, cb) => { - cb(null, JSON.stringify(user)); + const func = (user /* , req */) => { + return JSON.stringify(user); }; const sessionManager = new SessionManager(func); const user = { @@ -38,8 +38,8 @@ describe('SessionManager', () => { }); }); describe('#logOut', () => { - const func = (user, req, cb) => { - cb(null, JSON.stringify(user)); + const func = (user /* , req */) => { + return JSON.stringify(user); }; const sessionManager = new SessionManager(func); const user = { @@ -48,10 +48,9 @@ describe('SessionManager', () => { const req = { _passport: {} }; - before((done) => { - sessionManager.logIn(req, user, () => { - sessionManager.logOut(req, done); - }); + before(async () => { + await sessionManager.logIn(req, user); + return sessionManager.logOut(req); }); it('deletes the session', () => { // eslint-disable-next-line no-unused-expressions From f763a0e02db254e964266f0075c390864e199ffc Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 11 Sep 2019 18:59:40 +0800 Subject: [PATCH 04/12] - npm: Update devDeps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9629bad9..4c963a11 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,13 @@ "eslint-plugin-sonarjs": "^0.4.0", "eslint-plugin-standard": "^4.0.1", "eslint-plugin-unicorn": "^10.0.0", - "jsdoc": "^3.6.2", + "jsdoc": "^3.6.3", "mocha": "6.x.x", "node-static": "^0.7.11", "nunjucks": "^3.2.0", "nyc": "^14.1.1", "open-cli": "^5.0.0", - "typescript": "^3.6.2" + "typescript": "^3.6.3" }, "engines": { "node": ">=8.0.0" From 4b99cc50efa71cb3f74b33c539d67f4cfae40c90 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 11 Sep 2019 19:38:28 +0800 Subject: [PATCH 05/12] - Docs: Indicate usage of test-one script --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd1c9398..2420f6bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,6 +22,12 @@ Ensure that the test suite passes by executing: $ npm test ``` +You can also run a single test file: + +```bash +$ npm run test-one -- test/sessionmanager.test.js +``` + Ensure that lint passes ```bash $ npm run lint From 6505c9474048e0f528c43b0605c3338290618540 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 11 Sep 2019 19:45:32 +0800 Subject: [PATCH 06/12] - Refactoring: Avoid `let` in unneeded higher scope - Testing: Rename strategy name to distinguish from `fail` method - Testing: Expand coverage (incomplete) --- lib/middleware/authenticate.js | 5 +- test/http/request.test.js | 92 +++++++++++ .../authenticate.fail.message.test.js | 153 +++++++++++++++++- .../authenticate.success.message.test.js | 102 ++++++++++++ test/sessionmanager.test.js | 83 ++++++++++ test/strategies/session.test.js | 29 ++++ 6 files changed, 459 insertions(+), 5 deletions(-) diff --git a/lib/middleware/authenticate.js b/lib/middleware/authenticate.js index dd9b7233..bdb75e71 100644 --- a/lib/middleware/authenticate.js +++ b/lib/middleware/authenticate.js @@ -265,7 +265,6 @@ module.exports = function authenticate(passport, name, options, callback) { } info = info || {}; - let msg; if (options.successFlash) { let flash = options.successFlash; @@ -277,13 +276,13 @@ module.exports = function authenticate(passport, name, options, callback) { } const type = flash.type || info.type || 'success'; - msg = flash.message || info.message || info; + const msg = flash.message || info.message || info; if (typeof msg === 'string') { req.flash(type, msg); } } if (options.successMessage) { - msg = options.successMessage; + let msg = options.successMessage; if (typeof msg === 'boolean') { msg = info.message || info; } diff --git a/test/http/request.test.js b/test/http/request.test.js index 028e1742..427778a0 100644 --- a/test/http/request.test.js +++ b/test/http/request.test.js @@ -85,6 +85,53 @@ describe('http.ServerRequest', () => { }); }); + describe('not establishing a session (with done callback)', () => { + const { req } = setupPassport(); + req._passport.session = {}; + let error; + let callbackRan; + + before(async () => { + const user = { id: '1', username: 'root' }; + + try { + await req.login(user, { session: false }, () => { + callbackRan = true; + }); + } catch (err) { + error = err; + } + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.undefined; + }); + + it('should be authenticated', () => { + // eslint-disable-next-line no-unused-expressions + expect(req.isAuthenticated()).to.be.true; + // eslint-disable-next-line no-unused-expressions + expect(req.isUnauthenticated()).to.be.false; + }); + + it('should set user', () => { + expect(req.user).to.be.an('object'); + expect(req.user.id).to.equal('1'); + expect(req.user.username).to.equal('root'); + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(req._passport.session.user).to.be.undefined; + }); + + it('should run callback', () => { + // eslint-disable-next-line no-unused-expressions + expect(callbackRan).to.be.true; + }); + }); + describe('not establishing a session and setting custom user property', () => { const { req, passport } = setupPassport(); req._passport.session = {}; @@ -318,6 +365,51 @@ describe('http.ServerRequest', () => { }); }); + describe('encountering an error when serializing to session with callback', () => { + const { req, passport } = setupPassport(); + req._passport.session = {}; + passport.serializeUser((rq, user, done) => { + done(new Error('something went wrong')); + }); + + let callbackError; + + before((done) => { + const user = { id: '1', username: 'root' }; + + try { + req.login(user, (err) => { + callbackError = err; + done(); + }); + } catch (err) { + done(err); + } + }); + + it('should error', () => { + expect(callbackError).to.be.an.instanceOf(Error); + expect(callbackError.message).to.equal('something went wrong'); + }); + + it('should not be authenticated', () => { + // eslint-disable-next-line no-unused-expressions + expect(req.isAuthenticated()).to.be.false; + // eslint-disable-next-line no-unused-expressions + expect(req.isUnauthenticated()).to.be.true; + }); + + it('should not set user', () => { + // eslint-disable-next-line no-unused-expressions + expect(req.user).to.be.null; + }); + + it('should not serialize user', () => { + // eslint-disable-next-line no-unused-expressions + expect(req._passport.session.user).to.be.undefined; + }); + }); + describe('establishing a session, but not passing a callback argument', () => { const { req, passport } = setupPassport(); passport.serializeUser(() => { diff --git a/test/middleware/authenticate.fail.message.test.js b/test/middleware/authenticate.fail.message.test.js index a2b1d3cc..8b5102f8 100644 --- a/test/middleware/authenticate.fail.message.test.js +++ b/test/middleware/authenticate.fail.message.test.js @@ -7,20 +7,74 @@ const { Passport } = require('../..'); describe('middleware/authenticate', () => { describe('fail with message set by route', () => { + let ranAuthentication = false; class Strategy { authenticate() { + ranAuthentication = true; this.fail({ message: 'Invalid password' }); } } const passport = new Passport(); - passport.use('fail', new Strategy()); + passport.use('failure', new Strategy()); let request; let response; before((done) => { - chai.connect.use('express', authenticate(passport, 'fail', { + chai.connect.use('express', authenticate(passport, 'failure', { + failureMessage: 'Wrong credentials', + failureRedirect: 'http://www.example.com/login' + })) + .req((req) => { + request = req; + req.session = {}; + }) + .end((res) => { + response = res; + done(); + }) + .dispatch(); + }); + + it('should not set user', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.user).to.be.undefined; + }); + + it('should add message to session', () => { + expect(request.session.messages).to.have.length(1); + expect(request.session.messages[0]).to.equal('Wrong credentials'); + }); + + it('should redirect', () => { + expect(response.statusCode).to.equal(302); + expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); + }); + + it('should run authentication', () => { + // eslint-disable-next-line no-unused-expressions + expect(ranAuthentication).to.be.true; + }); + }); + + describe('fail with message set by route but with bad strategy name', () => { + let ranAuthentication = false; + class Strategy { + authenticate() { + ranAuthentication = true; + this.fail({ message: 'Invalid password' }); + } + } + + const passport = new Passport(); + passport.use('failure', new Strategy()); + + let request; + let response; + + before((done) => { + chai.connect.use('express', authenticate(passport, /* Bad strategy name */ null, { failureMessage: 'Wrong credentials', failureRedirect: 'http://www.example.com/login' })) @@ -49,6 +103,11 @@ describe('middleware/authenticate', () => { expect(response.statusCode).to.equal(302); expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); }); + + it('should not run authentication', () => { + // eslint-disable-next-line no-unused-expressions + expect(ranAuthentication).to.be.false; + }); }); describe('fail with message set by route that is added to messages', () => { @@ -187,4 +246,94 @@ describe('middleware/authenticate', () => { expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); }); }); + + describe('fail with message set by strategy with extra info (but a boolean failureMessage without a message property)', () => { + class Strategy { + authenticate() { + this.fail({ scope: 'read' }); + } + } + + const passport = new Passport(); + passport.use('fail', new Strategy()); + + let request; + let response; + + before((done) => { + chai.connect.use('express', authenticate(passport, 'fail', { + failureMessage: true, + failureRedirect: 'http://www.example.com/login' + })) + .req((req) => { + request = req; + req.session = {}; + }) + .end((res) => { + response = res; + done(); + }) + .dispatch(); + }); + + it('should not set user', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.user).to.be.undefined; + }); + + it('should not add message to session', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.session.messages).to.be.undefined; + }); + + it('should redirect', () => { + expect(response.statusCode).to.equal(302); + expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); + }); + }); + + describe('fail with message set by strategy with extra info (and non-string failure message info)', () => { + class Strategy { + authenticate() { + this.fail({ message: 'Invalid password', scope: 'read' }); + } + } + + const passport = new Passport(); + passport.use('fail', new Strategy()); + + let request; + let response; + + before((done) => { + chai.connect.use('express', authenticate(passport, 'fail', { + failureMessage: { nonBooleanOrString: true }, + failureRedirect: 'http://www.example.com/login' + })) + .req((req) => { + request = req; + req.session = {}; + }) + .end((res) => { + response = res; + done(); + }) + .dispatch(); + }); + + it('should not set user', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.user).to.be.undefined; + }); + + it('should not add message to session', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.session.messages).to.be.undefined; + }); + + it('should redirect', () => { + expect(response.statusCode).to.equal(302); + expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); + }); + }); }); diff --git a/test/middleware/authenticate.success.message.test.js b/test/middleware/authenticate.success.message.test.js index 759dac57..07bd45b6 100644 --- a/test/middleware/authenticate.success.message.test.js +++ b/test/middleware/authenticate.success.message.test.js @@ -211,4 +211,106 @@ describe('middleware/authenticate', () => { expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); }); }); + + describe('success with message set by strategy with extra info (but a boolean successMessage without a message property)', () => { + class Strategy { + authenticate() { + const user = { id: '1', username: 'jaredhanson' }; + this.success(user, { scope: 'read' }); + } + } + + const passport = new Passport(); + passport.use('success', new Strategy()); + + let request; + let response; + + before((done) => { + chai.connect.use('express', authenticate(passport, 'success', { + successMessage: true, + successRedirect: 'http://www.example.com/account' + })) + .req((req) => { + request = req; + req.session = {}; + + req.logIn = function logIn(user) { + this.user = user; + }; + }) + .end((res) => { + response = res; + done(); + }) + .dispatch(); + }); + + it('should set user', () => { + expect(request.user).to.be.an('object'); + expect(request.user.id).to.equal('1'); + expect(request.user.username).to.equal('jaredhanson'); + }); + + it('should not add message to session', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.session.messages).to.be.undefined; + }); + + it('should redirect', () => { + expect(response.statusCode).to.equal(302); + expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); + }); + }); + + describe('success with message set by strategy with extra info (and non-string success message info)', () => { + class Strategy { + authenticate() { + const user = { id: '1', username: 'jaredhanson' }; + this.success(user, { message: 'Welcome!', scope: 'read' }); + } + } + + const passport = new Passport(); + passport.use('success', new Strategy()); + + let request; + let response; + + before((done) => { + chai.connect.use('express', authenticate(passport, 'success', { + successMessage: { nonBooleanOrString: true }, + successRedirect: 'http://www.example.com/account' + })) + .req((req) => { + request = req; + req.session = {}; + + req.logIn = function logIn(user) { + this.user = user; + }; + }) + .end((res) => { + response = res; + done(); + }) + .dispatch(); + }); + + it('should set user', () => { + expect(request.user).to.be.an('object'); + expect(request.user.id).to.equal('1'); + expect(request.user.username).to.equal('jaredhanson'); + }); + + it('should not add message to session', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.session.messages).to.be.undefined; + }); + + it('should redirect', () => { + expect(response.statusCode).to.equal(302); + expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); + }); + }); }); diff --git a/test/sessionmanager.test.js b/test/sessionmanager.test.js index 78921033..abc03b8b 100644 --- a/test/sessionmanager.test.js +++ b/test/sessionmanager.test.js @@ -37,6 +37,65 @@ describe('SessionManager', () => { expect(req.session.passport.user).to.equal('{"username":"dummy"}'); }); }); + describe('#logIn (with pre-provided private passport session)', () => { + const func = (user /* , req */) => { + return JSON.stringify(user); + }; + const sessionManager = new SessionManager(func); + const user = { + username: 'dummy' + }; + const req = { + _passport: { + session: {} + } + }; + before((done) => { + sessionManager.logIn(req, user, done); + }); + it('serializes user', () => { + expect(req.session.passport.user).to.equal('{"username":"dummy"}'); + }); + }); + describe('#logIn (with pre-provided session)', () => { + const func = (user /* , req */) => { + return JSON.stringify(user); + }; + const sessionManager = new SessionManager(func); + const user = { + username: 'dummy' + }; + const req = { + _passport: { + }, + session: {} + }; + before((done) => { + sessionManager.logIn(req, user, done); + }); + it('serializes user', () => { + expect(req.session.passport.user).to.equal('{"username":"dummy"}'); + }); + }); + describe('#logIn (erring with callback)', () => { + const func = (/* user, req */) => { + throw new Error('Bad serializer'); + }; + const sessionManager = new SessionManager(func); + const user = { + username: 'dummy' + }; + const req = { + _passport: {} + }; + it('does not throw with callback and bad serializer', (done) => { + expect(() => { + sessionManager.logIn(req, user, () => { + setTimeout(() => done()); + }); + }).to.not.throw(); + }); + }); describe('#logOut', () => { const func = (user /* , req */) => { return JSON.stringify(user); @@ -57,4 +116,28 @@ describe('SessionManager', () => { expect(req.session.passport.user).to.undefined; }); }); + + describe('#logOut (with callback)', () => { + const func = (user /* , req */) => { + return JSON.stringify(user); + }; + const sessionManager = new SessionManager(func); + const user = { + username: 'dummy' + }; + const req = { + _passport: {} + }; + before(async () => { + await sessionManager.logIn(req, user); + // eslint-disable-next-line promise/avoid-new + return new Promise((resolve) => { + sessionManager.logOut(req, resolve); + }); + }); + it('deletes the session', () => { + // eslint-disable-next-line no-unused-expressions + expect(req.session.passport.user).to.undefined; + }); + }); }); diff --git a/test/strategies/session.test.js b/test/strategies/session.test.js index a5a8d898..52593b95 100644 --- a/test/strategies/session.test.js +++ b/test/strategies/session.test.js @@ -41,6 +41,35 @@ describe('SessionStrategy', () => { }); }); + describe('handling a request without a login session object', () => { + let request; + let pass = false; + + before((done) => { + chai.passport.use(strategy) + .pass(() => { + pass = true; + done(); + }) + .req((req) => { + request = req; + + req._passport = {}; + }) + .authenticate(); + }); + + it('should pass', () => { + // eslint-disable-next-line no-unused-expressions + expect(pass).to.be.true; + }); + + it('should not set user on request', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.user).to.be.undefined; + }); + }); + describe('handling a request with a login session', () => { const strategy = new SessionStrategy((user) => { return { id: user }; From 445a6735ceb02355b1c9b639f9b3e37d931726ad Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 12 Sep 2019 12:12:52 +0800 Subject: [PATCH 07/12] - Fix: Avoid erring upon missing failure with callback and `authenticate` failure --- lib/middleware/authenticate.js | 2 +- .../authenticate.fail.multi.test.js | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/lib/middleware/authenticate.js b/lib/middleware/authenticate.js index bdb75e71..b4fcb996 100644 --- a/lib/middleware/authenticate.js +++ b/lib/middleware/authenticate.js @@ -140,7 +140,7 @@ module.exports = function authenticate(passport, name, options, callback) { */ function allFailed() { if (callback) { - if (!multi) { + if (!multi && failures[0]) { callback(null, false, failures[0].challenge, failures[0].status); return; } diff --git a/test/middleware/authenticate.fail.multi.test.js b/test/middleware/authenticate.fail.multi.test.js index 2c139bc2..3b317705 100644 --- a/test/middleware/authenticate.fail.multi.test.js +++ b/test/middleware/authenticate.fail.multi.test.js @@ -398,4 +398,66 @@ describe('middleware/authenticate', () => { expect(request.user).to.be.undefined; }); }); + + describe('without a valid strategy name, which fails with unauthorized status, and invoking callback', () => { + class BasicStrategy { + authenticate() { + this.fail('BASIC challenge'); + } + } + + const passport = new Passport(); + passport.use('basic', new BasicStrategy()); + + let request; + let error; + let user; + let challenge; + let status; + + before((done) => { + function callback(e, u, c, s) { + error = e; + user = u; + challenge = c; + status = s; + done(); + } + + chai.connect.use(authenticate( + passport, + // Bad strategy name + null, + callback + )) + .req((req) => { + request = req; + }) + .dispatch(); + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.null; + }); + + it('should pass false to callback', () => { + expect(user).to.equal(false); + }); + + it('should not pass challenges to callback', () => { + expect(challenge).to.be.an('array'); + expect(challenge).to.have.length(0); + }); + + it('should not pass statuses to callback', () => { + expect(status).to.be.an('array'); + expect(status).to.have.length(0); + }); + + it('should not set user on request', () => { + // eslint-disable-next-line no-unused-expressions + expect(request.user).to.be.undefined; + }); + }); }); From 357727a9f91d86a4e75f7fc112a9026cf49e64fc Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 12 Sep 2019 12:31:49 +0800 Subject: [PATCH 08/12] - Testing: Delete `_passport` object when simulating without `initialize` (as it is added there) --- test/http/request.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/http/request.test.js b/test/http/request.test.js index 427778a0..bd7bb872 100644 --- a/test/http/request.test.js +++ b/test/http/request.test.js @@ -483,6 +483,8 @@ describe('http.ServerRequest', () => { describe('existing session, without passport.initialize() middleware', () => { const { req } = setupPassport(); + delete req._passport; + req.user = { id: '1', username: 'root' }; req.logout(); From c220b5bf6470f6fb3dc79b3488091a6d0d144fc5 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 12 Sep 2019 12:32:23 +0800 Subject: [PATCH 09/12] - Fix: Check for `instance` property (as in check earlier in the same method) --- lib/http/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http/request.js b/lib/http/request.js index 23a06ac2..18aae843 100644 --- a/lib/http/request.js +++ b/lib/http/request.js @@ -99,7 +99,7 @@ req.logOut = function logOut(done) { } this[property] = null; - if (this._passport) { + if (this._passport && this._passport.instance) { this._passport.instance._sm.logOut(this, done); } }; From ee8ac43abba5d8e84cd0e75183fbd3a954edc55b Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 12 Sep 2019 12:39:19 +0800 Subject: [PATCH 10/12] - Testing: Expand coverage (still incomplete) --- test/http/request.test.js | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/http/request.test.js b/test/http/request.test.js index bd7bb872..b7ba6800 100644 --- a/test/http/request.test.js +++ b/test/http/request.test.js @@ -276,6 +276,52 @@ describe('http.ServerRequest', () => { }); }); + describe('establishing a session with a done callback', () => { + const { req, passport } = setupPassport(); + passport.serializeUser((rq, user, done) => { + done(null, user.id); + }); + let error; + + before(async () => { + const user = { id: '1', username: 'root' }; + + let invokedDone = false; + try { + await req.login(user, function doneCallback() { + invokedDone = true; + }); + if (!invokedDone) { + throw new Error('Did not invoke `done` callback'); + } + } catch (err) { + error = err; + } + }); + + it('should not error', () => { + // eslint-disable-next-line no-unused-expressions + expect(error).to.be.undefined; + }); + + it('should be authenticated', () => { + // eslint-disable-next-line no-unused-expressions + expect(req.isAuthenticated()).to.be.true; + // eslint-disable-next-line no-unused-expressions + expect(req.isUnauthenticated()).to.be.false; + }); + + it('should set user', () => { + expect(req.user).to.be.an('object'); + expect(req.user.id).to.equal('1'); + expect(req.user.username).to.equal('root'); + }); + + it('should serialize user', () => { + expect(req._passport.session.user).to.equal('1'); + }); + }); + describe('establishing a session and setting custom user property', () => { const { req, passport } = setupPassport(); passport.serializeUser((rq, user, done) => { @@ -501,6 +547,27 @@ describe('http.ServerRequest', () => { expect(req.user).to.be.null; }); }); + + describe('existing session, without passport.initialize() middleware but with an `instance` without a `_userProperty`', () => { + const { req } = setupPassport(); + delete req._passport.instance._userProperty; + + req.user = { id: '1', username: 'root' }; + + req.logout(); + + it('should not be authenticated', () => { + // eslint-disable-next-line no-unused-expressions + expect(req.isAuthenticated()).to.be.false; + // eslint-disable-next-line no-unused-expressions + expect(req.isUnauthenticated()).to.be.true; + }); + + it('should clear user', () => { + // eslint-disable-next-line no-unused-expressions + expect(req.user).to.be.null; + }); + }); }); From 504f7b1aa515783571c3761c0d5677a298735e98 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 12 Sep 2019 12:41:54 +0800 Subject: [PATCH 11/12] - Testing: For login test now, delete `_passport` object when simulating without `initialize` (as it is added there) --- test/http/request.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/http/request.test.js b/test/http/request.test.js index b7ba6800..197a0bd7 100644 --- a/test/http/request.test.js +++ b/test/http/request.test.js @@ -205,6 +205,7 @@ describe('http.ServerRequest', () => { describe('not establishing a session, without passport.initialize() middleware', () => { const { req } = setupPassport(); + delete req._passport.instance._userProperty; let error; before(async () => { From 3026221a7f4de9dbb7f500fdb80d5044da921ca0 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 12 Sep 2019 19:31:31 +0800 Subject: [PATCH 12/12] - Fix: Avoid unfulfilled Promise condition (and simplify Promise returns) --- lib/authenticator.js | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/lib/authenticator.js b/lib/authenticator.js index 3d40856b..25db2037 100644 --- a/lib/authenticator.js +++ b/lib/authenticator.js @@ -342,7 +342,7 @@ class Authenticator { return error; } - let serializedRet, res; + let serializedRet; /** * * @param {Error} [e] @@ -351,9 +351,6 @@ class Authenticator { */ function serialized(e, o) { serializedRet = pass(i + 1, e, o); - if (res) { - res(serializedRet); - } return serializedRet; } @@ -374,17 +371,10 @@ class Authenticator { if (ret !== undefined) { return serialized(null, ret); } + return serializedRet; } catch (e) { return serialized(e); } - // eslint-disable-next-line promise/avoid-new - return new Promise((resolve) => { - if (serializedRet) { - resolve(serializedRet); - return; - } - res = resolve; - }); }(0)); } @@ -469,7 +459,7 @@ class Authenticator { return error; } - let deserializedRet, res; + let deserializedRet; /** * * @param {Error} [e] @@ -478,9 +468,6 @@ class Authenticator { */ function deserialized(e, u) { deserializedRet = pass(i + 1, e, u); - if (res) { - res(deserializedRet); - } return deserializedRet; } @@ -501,17 +488,10 @@ class Authenticator { if (ret !== undefined) { return deserialized(null, ret); } + return deserializedRet; } catch (e) { return deserialized(e); } - // eslint-disable-next-line promise/avoid-new - return new Promise((resolve) => { - if (deserializedRet) { - resolve(deserializedRet); - return; - } - res = resolve; - }); }(0)); } @@ -618,7 +598,7 @@ class Authenticator { } - let transformedRet, res; + let transformedRet; /** * * @param {Error} [e] @@ -627,9 +607,6 @@ class Authenticator { */ function transformed(e, t) { transformedRet = pass(i + 1, e, t); - if (res) { - res(transformedRet); - } return transformedRet; } @@ -650,17 +627,10 @@ class Authenticator { if (ret !== undefined) { return transformed(null, ret); } + return transformedRet; } catch (e) { return transformed(e); } - // eslint-disable-next-line promise/avoid-new - return new Promise((resolve) => { - if (transformedRet) { - resolve(transformedRet); - return; - } - res = resolve; - }); }(0)); }