From 9af1f0d04f84355fa5a1b78377eb9bcac5f0ba7b Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 10:04:46 -0400 Subject: [PATCH 01/29] Names --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4bf1f55..313ed5d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # assignment_mad_lib_api Serving up the madness with a Mad Lib API! + +Ed and Will From 3609d9a8fc8fa2fee800eb156b0f069b4f4328e1 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Fri, 18 Aug 2017 11:26:42 -0400 Subject: [PATCH 02/29] init --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4bf1f55..f3f8b23 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # assignment_mad_lib_api Serving up the madness with a Mad Lib API! + +Will and Ed \ No newline at end of file From f8503a0662c21cdc4b2bc628d42dd292c13ba20c Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 11:32:43 -0400 Subject: [PATCH 03/29] boilerplate --- app.js | 100 +++++++++++++++++++++++++++ config/mongoUrl.js | 6 ++ config/mongoose.json | 13 ++++ helpers/AuthHelper.js | 5 ++ helpers/FlashHelper.js | 15 ++++ helpers/index.js | 5 ++ models/User.js | 32 +++++++++ models/index.js | 5 ++ mongo.js | 8 +++ repl.js | 18 +++++ routes/index.js | 64 +++++++++++++++++ strategies/local.js | 15 ++++ views/index.handlebars | 21 ++++++ views/layouts/application.handlebars | 25 +++++++ views/login.handlebars | 24 +++++++ views/register.handlebars | 31 +++++++++ views/shared/_flash.handlebars | 11 +++ views/shared/_nav.handlebars | 24 +++++++ 18 files changed, 422 insertions(+) create mode 100644 app.js create mode 100644 config/mongoUrl.js create mode 100644 config/mongoose.json create mode 100644 helpers/AuthHelper.js create mode 100644 helpers/FlashHelper.js create mode 100644 helpers/index.js create mode 100644 models/User.js create mode 100644 models/index.js create mode 100644 mongo.js create mode 100644 repl.js create mode 100644 routes/index.js create mode 100644 strategies/local.js create mode 100644 views/index.handlebars create mode 100644 views/layouts/application.handlebars create mode 100644 views/login.handlebars create mode 100644 views/register.handlebars create mode 100644 views/shared/_flash.handlebars create mode 100644 views/shared/_nav.handlebars diff --git a/app.js b/app.js new file mode 100644 index 0000000..8ce1bce --- /dev/null +++ b/app.js @@ -0,0 +1,100 @@ +const express = require("express"); +const app = express(); +const h = require("./helpers"); +const { User } = require("./models"); + +// .env +if (process.env.NODE_ENV !== "production") { + require("dotenv").config(); +} + +// Templates +const expressHandlebars = require("express-handlebars"); +const hbs = expressHandlebars.create({ + partialsDir: "views/", + defaultLayout: "application", + helpers: require("./helpers") +}); +app.engine("handlebars", hbs.engine); +app.set("view engine", "handlebars"); + +// Static Files +app.use(express.static(`${__dirname}/public`)); + +// Post Data +const bodyParser = require("body-parser"); +app.use(bodyParser.urlencoded({ extended: true })); + +// Session +const expressSession = require("express-session"); +app.use( + expressSession({ + resave: false, + saveUninitialized: true, + secret: "asdf;werxcklj;jxcvui3qksf;" + }) +); + +// Flash +const flash = require("express-flash-messages"); +app.use(flash()); + +// Log Request Info +const morgan = require("morgan"); +const morganToolkit = require("morgan-toolkit")(morgan); +app.use(morganToolkit()); + +// Connect to Mongoose +const mongoose = require("mongoose"); +app.use((req, res, next) => { + if (mongoose.connection.readyState) next(); + else require("./mongo")().then(() => next()); +}); + +// Authentication Middleware +const passport = require("passport"); + +app.use(passport.initialize()); +app.use(passport.session()); + +passport.serializeUser(function(user, done) { + done(null, user._id); +}); + +passport.deserializeUser(function(userId, done) { + User.findById(userId, (err, user) => done(err, user)); +}); + +passport.use("local", require("./strategies/local")); + +// Add populated user and referral URL to locals +app.use(async (req, res, next) => { + if (req.user) { + const protocol = req.protocol; + const host = req.get("host"); + const shortId = req.user.shortId; + res.locals.referralUrl = `${protocol}://${host}/${shortId}`; + res.locals.user = await req.user.populateChildren(); + res.locals.pyramid = []; + for (let i = 0; i < Object.keys(req.user.pyramid).length; i++) { + res.locals.pyramid.push(req.user.pyramid[i]); + } + } + next(); +}); + +// Routes +app.use("/", require("./routes")(passport)); + +// Set up port/host +const port = process.env.PORT || process.argv[2] || 3000; +const host = "localhost"; +let args = process.env.NODE_ENV === "production" ? [port] : [port, host]; + +// helpful log when the server starts +args.push(() => { + console.log(`Listening: http://${host}:${port}`); +}); + +// Use apply to pass the args to listen +app.listen.apply(app, args); diff --git a/config/mongoUrl.js b/config/mongoUrl.js new file mode 100644 index 0000000..b6f03e0 --- /dev/null +++ b/config/mongoUrl.js @@ -0,0 +1,6 @@ +const env = process.env.NODE_ENV || "development"; +const config = require("./mongoose.json")[env]; +module.exports = + process.env.NODE_ENV === "production" + ? process.env[config.use_env_variable] + : `mongodb://${config.host}/${config.database}`; diff --git a/config/mongoose.json b/config/mongoose.json new file mode 100644 index 0000000..08c6a68 --- /dev/null +++ b/config/mongoose.json @@ -0,0 +1,13 @@ +{ + "development": { + "database": "mad_lib_development", + "host": "localhost" + }, + "test": { + "database": "mad_lib_test", + "host": "localhost" + }, + "production": { + "use_env_variable": "MONGO_URL" + } +} diff --git a/helpers/AuthHelper.js b/helpers/AuthHelper.js new file mode 100644 index 0000000..0c134b3 --- /dev/null +++ b/helpers/AuthHelper.js @@ -0,0 +1,5 @@ +module.exports = { + loginPath: () => "/login", + logoutPath: () => "/logout", + registerPath: () => "/register" +}; diff --git a/helpers/FlashHelper.js b/helpers/FlashHelper.js new file mode 100644 index 0000000..b020546 --- /dev/null +++ b/helpers/FlashHelper.js @@ -0,0 +1,15 @@ +module.exports = { + bootstrapAlertClassFor: key => { + return ( + { + error: "danger", + alert: "danger", + notice: "info" + }[key] || key + ); + }, + missingFlashRedirect: (req, res, path, item) => { + req.flash("alert", `Sorry, that ${item} does not exist`); + res.redirect(path); + } +}; diff --git a/helpers/index.js b/helpers/index.js new file mode 100644 index 0000000..4efd3b2 --- /dev/null +++ b/helpers/index.js @@ -0,0 +1,5 @@ +const LoadHelpers = require("load-helpers"); +const helperLoader = new LoadHelpers(); +const helpers = helperLoader.load("helpers/*Helper.js").cache; + +module.exports = helpers; diff --git a/models/User.js b/models/User.js new file mode 100644 index 0000000..b5fe7da --- /dev/null +++ b/models/User.js @@ -0,0 +1,32 @@ +const mongoose = require("mongoose"); +const uniqueValidator = require("mongoose-unique-validator"); +const bcrypt = require("bcrypt"); +const Schema = mongoose.Schema; + +const UserSchema = new Schema( + { + fname: { type: String, required: true }, + lname: { type: String, required: true }, + email: { type: String, required: true, unique: true }, + passwordHash: { type: String, required: true } + }, + { + timestamps: true + } +); + +UserSchema.plugin(uniqueValidator); + +// Instance Methods +UserSchema.methods.validPassword = function(password) { + return bcrypt.compareSync(password, this.passwordHash); +}; + +// Virtual Properties +UserSchema.virtual("password").set(function(value) { + this.passwordHash = bcrypt.hashSync(value, 8); +}); + +const User = mongoose.model("User", UserSchema); + +module.exports = User; diff --git a/models/index.js b/models/index.js new file mode 100644 index 0000000..54b926a --- /dev/null +++ b/models/index.js @@ -0,0 +1,5 @@ +const mongoose = require("mongoose"); +mongoose.Promise = require("bluebird"); +module.exports = { + User: require("./User") +}; diff --git a/mongo.js b/mongo.js new file mode 100644 index 0000000..e402209 --- /dev/null +++ b/mongo.js @@ -0,0 +1,8 @@ +const mongoose = require("mongoose"); +module.exports = () => { + mongoose.Promise = require("bluebird"); + return mongoose.connect(require("./config/mongoUrl"), { + useMongoClient: true + }); +}; + diff --git a/repl.js b/repl.js new file mode 100644 index 0000000..df1827c --- /dev/null +++ b/repl.js @@ -0,0 +1,18 @@ +// repl.js +let repl = require("repl").start({}); +const mongoose = require("mongoose"); +const models = require("./models"); + +// connect +require("./mongo")().then(() => { + // Set `models` global + repl.context.models = models; + + // model globals + Object.keys(models).forEach(modelName => { + repl.context[modelName] = mongoose.model(modelName); + }); + + // logger + repl.context.lg = data => console.log(data); +}); diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..fe2ac19 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,64 @@ +const express = require("express"); +const router = express.Router(); +const h = require("../helpers"); +const { User } = require("../models"); + +// Authentication Middleware +const ensureAuthenticated = (req, res, next) => { + if (req.isAuthenticated()) next(); + else res.redirect(h.loginPath()); +}; + +// Route Handlers +function authenticate(passport) { + //main page + router.get("/", ensureAuthenticated, (req, res) => { + res.render("index"); + }); + + //login view + router.get("/login", (req, res) => { + User.find().then(r => { + if (!r.length) res.redirect(h.registerPath()); + else res.render("login"); + }); + }); + + //login handler + router.post( + "/login", + passport.authenticate("local", { + successRedirect: "/", + failureRedirect: h.loginPath(), + failureFlash: true + }) + ); + + //register view + router.get("/register", (req, res) => { + res.render("register"); + }); + + //register handler + router.post("/register", (req, res, next) => { + const { username, password } = req.body; + User.create({ username, password }) + .then(user => { + req.login(user, err => { + if (err) next(err); + else res.redirect("/"); + }); + }) + .catch(e => res.status(500).end(e)); + }); + + //logout handler + router.get("/logout", function(req, res) { + req.logout(); + res.redirect("/"); + }); + + return router; +} + +module.exports = authenticate; diff --git a/strategies/local.js b/strategies/local.js new file mode 100644 index 0000000..250ea09 --- /dev/null +++ b/strategies/local.js @@ -0,0 +1,15 @@ +const LocalStrategy = require("passport-local").Strategy; +const { User } = require("../models"); + +const localOpts = { usernameField: "email" }; +const localHandler = (email, password, done) => { + User.findOne({ email }) + .then(user => { + if (!user || !user.validPassword(password)) { + done(null, false, { message: "Invalid username/password" }); + } else done(null, user); + }) + .catch(e => done(e)); +}; + +module.exports = new LocalStrategy(localOpts, localHandler); diff --git a/views/index.handlebars b/views/index.handlebars new file mode 100644 index 0000000..8a83cdd --- /dev/null +++ b/views/index.handlebars @@ -0,0 +1,21 @@ +
+ +
+
+

Welcome {{user.username}}

+
+
+
+
First Name
+
{{user.fname}}
+
Last Name
+
{{user.lname}}
+
email
+
{{user.email}}
+
Token
+
{{user.token}}
+
+
+
+ +
diff --git a/views/layouts/application.handlebars b/views/layouts/application.handlebars new file mode 100644 index 0000000..ab4bc1d --- /dev/null +++ b/views/layouts/application.handlebars @@ -0,0 +1,25 @@ + + + + + + + + Mad Libs + + + + + + + + + {{> shared/_nav }} +
+ {{> shared/_flash }} {{{ body }}} +
+ + + diff --git a/views/login.handlebars b/views/login.handlebars new file mode 100644 index 0000000..ae66c5a --- /dev/null +++ b/views/login.handlebars @@ -0,0 +1,24 @@ +
+
+
Login
+
+ +
+
+ + +
+
+ + +
+ +
+ +
+ + +
+
diff --git a/views/register.handlebars b/views/register.handlebars new file mode 100644 index 0000000..afe9929 --- /dev/null +++ b/views/register.handlebars @@ -0,0 +1,31 @@ +
+
+
Sign Up
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
+ + +
+
diff --git a/views/shared/_flash.handlebars b/views/shared/_flash.handlebars new file mode 100644 index 0000000..e814a82 --- /dev/null +++ b/views/shared/_flash.handlebars @@ -0,0 +1,11 @@ +
+
+ {{#each getMessages as |messages key| }} {{#each messages as |message| }} + + {{/each }} {{/each }} +
+
diff --git a/views/shared/_nav.handlebars b/views/shared/_nav.handlebars new file mode 100644 index 0000000..d519be3 --- /dev/null +++ b/views/shared/_nav.handlebars @@ -0,0 +1,24 @@ + From fbfa4d093c517f0843bc4dee8de32da745d8a813 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 11:51:54 -0400 Subject: [PATCH 04/29] getting running --- .gitignore | 1 + README.md | 4 - app.js | 16 +- package-lock.json | 2770 +++++++++++++++++++++++++++++++++++++ package.json | 47 + routes/index.js | 2 +- views/login.handlebars | 2 +- views/register.handlebars | 3 +- 8 files changed, 2829 insertions(+), 16 deletions(-) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/README.md b/README.md index 58aa6c1..313ed5d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ # assignment_mad_lib_api Serving up the madness with a Mad Lib API! -<<<<<<< HEAD Ed and Will -======= -Will and Ed ->>>>>>> 3609d9a8fc8fa2fee800eb156b0f069b4f4328e1 diff --git a/app.js b/app.js index 8ce1bce..66c060e 100644 --- a/app.js +++ b/app.js @@ -18,6 +18,12 @@ const hbs = expressHandlebars.create({ app.engine("handlebars", hbs.engine); app.set("view engine", "handlebars"); +// Method Overriding +const methodOverride = require("method-override"); +const getPostSupport = require("express-method-override-get-post-support"); +app.use(methodOverride(getPostSupport.callback, getPostSupport.options)); + + // Static Files app.use(express.static(`${__dirname}/public`)); @@ -70,15 +76,7 @@ passport.use("local", require("./strategies/local")); // Add populated user and referral URL to locals app.use(async (req, res, next) => { if (req.user) { - const protocol = req.protocol; - const host = req.get("host"); - const shortId = req.user.shortId; - res.locals.referralUrl = `${protocol}://${host}/${shortId}`; - res.locals.user = await req.user.populateChildren(); - res.locals.pyramid = []; - for (let i = 0; i < Object.keys(req.user.pyramid).length; i++) { - res.locals.pyramid.push(req.user.pyramid[i]); - } + res.locals.user = req.user; } next(); }); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6e312ff --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2770 @@ +{ + "name": "assignment_mad_lib_api", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=" + }, + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "requires": { + "mime-types": "2.1.16", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "requires": { + "color-convert": "1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "apparatus": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/apparatus/-/apparatus-0.0.9.tgz", + "integrity": "sha1-N9zSWDStC2UQdllikduCPusZCL0=", + "requires": { + "sylvester": "0.0.21" + } + }, + "aproba": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz", + "integrity": "sha512-ZpYajIfO0j2cOFTO955KUMIKNmj6zhX8kVztMAxFsDaMwz+9Z9SV0uou2pC9HJqcfpffOsjnbrDMvkNy+9RXPw==" + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "articles": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/articles/-/articles-0.2.1.tgz", + "integrity": "sha1-wOEBAxuDPb0L3HegrgZQLYIaRSA=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "async-array-reduce": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/async-array-reduce/-/async-array-reduce-0.2.1.tgz", + "integrity": "sha1-yL4BCitc0A3qlsgRFgNGk9/dgtE=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "basic-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", + "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=" + }, + "bcrypt": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-1.0.2.tgz", + "integrity": "sha1-0F/F0iMXPg4o7DgcDwDMJf+vJzY=", + "requires": { + "bindings": "1.2.1", + "nan": "2.5.0", + "node-pre-gyp": "0.6.32" + } + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "bindings": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "2.0.3" + } + }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" + }, + "body-parser": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.17.2.tgz", + "integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=", + "requires": { + "bytes": "2.4.0", + "content-type": "1.0.2", + "debug": "2.6.7", + "depd": "1.1.1", + "http-errors": "1.6.2", + "iconv-lite": "0.4.15", + "on-finished": "2.3.0", + "qs": "6.4.0", + "raw-body": "2.2.0", + "type-is": "1.6.15" + }, + "dependencies": { + "debug": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "bson": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", + "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "bytes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "optional": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "optional": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", + "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.2.1" + } + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + }, + "cli-highlight": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-1.1.4.tgz", + "integrity": "sha1-5FWQwU+xjhOGXjiZ6CTFWSzCKSY=", + "requires": { + "chalk": "1.1.3", + "he": "1.1.1", + "highlight.js": "9.12.0", + "mz": "2.6.0", + "yargs": "4.8.1" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "lodash.assign": "4.2.0", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "window-size": "0.2.0", + "y18n": "3.2.1", + "yargs-parser": "2.4.1" + } + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "optional": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "optional": true + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" + }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "crc": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", + "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=" + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dotenv": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz", + "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es6-promise": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "etag": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", + "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "expand-tilde": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", + "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "requires": { + "os-homedir": "1.0.2" + } + }, + "express": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", + "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", + "requires": { + "accepts": "1.3.3", + "array-flatten": "1.1.1", + "content-disposition": "0.5.2", + "content-type": "1.0.2", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.8", + "depd": "1.1.1", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.0", + "finalhandler": "1.0.4", + "fresh": "0.5.0", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.1", + "path-to-regexp": "0.1.7", + "proxy-addr": "1.1.5", + "qs": "6.5.0", + "range-parser": "1.2.0", + "send": "0.15.4", + "serve-static": "1.12.4", + "setprototypeof": "1.0.3", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.0", + "vary": "1.1.1" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", + "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==" + } + } + }, + "express-flash-messages": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/express-flash-messages/-/express-flash-messages-0.1.1.tgz", + "integrity": "sha1-QCDOUXjDuzZjlN6QZiILwnBb1vI=" + }, + "express-handlebars": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.0.0.tgz", + "integrity": "sha1-gKBwu4GbCeSvLKbQeA91zgXnXC8=", + "requires": { + "glob": "6.0.4", + "graceful-fs": "4.1.11", + "handlebars": "4.0.10", + "object.assign": "4.0.4", + "promise": "7.3.1" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, + "express-method-override-get-post-support": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/express-method-override-get-post-support/-/express-method-override-get-post-support-0.0.7.tgz", + "integrity": "sha512-aHO/iYL9GV4FoRZACHkhOYfHXVljwu5LbGMOuq8++6K2JpG6437oZt64w1k3Ff6oPq1/0AtsGYnZwyivKUi67A==" + }, + "express-session": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.5.tgz", + "integrity": "sha512-BBVy6E/XqjB507wqe5T+7Ia2N/gtur/dT/fKmvGGKQqUrzI4dcBPGJgV4t2ciX7FoxZPhZcKTDTmb4+5nCyQOw==", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "crc": "3.4.4", + "debug": "2.6.8", + "depd": "1.1.1", + "on-headers": "1.0.1", + "parseurl": "1.3.1", + "uid-safe": "2.1.5", + "utils-merge": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "faker": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", + "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=" + }, + "fileset": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.1.8.tgz", + "integrity": "sha1-UGuRqTluqn4y+0KoQHfHoMc2t0E=", + "requires": { + "glob": "3.2.11", + "minimatch": "0.4.0" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2.0.3", + "minimatch": "0.3.0" + }, + "dependencies": { + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "minimatch": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz", + "integrity": "sha1-vSx9Bg0sjI/Xzefx8u0tWycP2xs=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "finalhandler": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz", + "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==", + "requires": { + "debug": "2.6.8", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.1", + "statuses": "1.3.1", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.16" + } + }, + "forwarded": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=" + }, + "fresh": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", + "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=" + }, + "fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.5.4" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "function-bind": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "1.1.2", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "gaze": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.3.4.tgz", + "integrity": "sha1-X5S92gr+U7xxCWm81vKCVI1gwnk=", + "requires": { + "fileset": "0.1.8", + "minimatch": "0.2.14" + }, + "dependencies": { + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "global-modules": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", + "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", + "requires": { + "global-prefix": "0.1.5", + "is-windows": "0.2.0" + } + }, + "global-prefix": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", + "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", + "requires": { + "homedir-polyfill": "1.0.1", + "ini": "1.3.4", + "is-windows": "0.2.0", + "which": "1.3.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "growl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" + }, + "handlebars": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", + "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "has-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/has-glob/-/has-glob-0.1.1.tgz", + "integrity": "sha1-omHEwqbGZ+DHe3AKfyl8Oe86pYk=", + "requires": { + "is-glob": "2.0.1" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "highlight.js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", + "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=" + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "requires": { + "parse-passwd": "1.0.0" + } + }, + "hooks-fixed": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.0.tgz", + "integrity": "sha1-oB2JTVKsf2WZu7H2PfycQR33DLo=" + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "iconv-lite": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ipaddr.js": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", + "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "3.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=" + }, + "is-windows": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jasmine": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.7.0.tgz", + "integrity": "sha1-XPC7TllLRgC7QjVWA2YhKsWuobI=", + "requires": { + "exit": "0.1.2", + "glob": "7.1.2", + "jasmine-core": "2.7.0" + } + }, + "jasmine-core": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.7.0.tgz", + "integrity": "sha1-UP+MT5LY71wLLBuEbdJj7YUVIJE=" + }, + "jasmine-growl-reporter": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/jasmine-growl-reporter/-/jasmine-growl-reporter-0.0.3.tgz", + "integrity": "sha1-uHrlUeNZ0orVIXdl6u9sB7dj9sg=", + "requires": { + "growl": "1.7.0" + } + }, + "jasmine-node": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/jasmine-node/-/jasmine-node-1.14.5.tgz", + "integrity": "sha1-GOg5e4VpJO53ADZmw3MbWupQw50=", + "requires": { + "coffee-script": "1.12.7", + "gaze": "0.3.4", + "jasmine-growl-reporter": "0.0.3", + "jasmine-reporters": "1.0.2", + "mkdirp": "0.3.5", + "requirejs": "2.3.4", + "underscore": "1.8.3", + "walkdir": "0.0.11" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + } + } + }, + "jasmine-reporters": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-1.0.2.tgz", + "integrity": "sha1-q2E+1Zd9x0h+hbPBL2qOqNsq3jE=", + "requires": { + "mkdirp": "0.3.5" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "kareem": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", + "integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg=" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "optional": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "1.0.0" + } + }, + "load-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/load-helpers/-/load-helpers-1.0.1.tgz", + "integrity": "sha1-ZoEbwF/PBwSO9w9lRzC0Ip5qrLM=", + "requires": { + "component-emitter": "1.2.1", + "extend-shallow": "2.0.1", + "is-glob": "3.1.0", + "is-valid-glob": "0.3.0", + "kind-of": "3.2.2", + "matched": "0.4.4", + "resolve-dir": "0.1.1", + "set-value": "0.4.3" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "matched": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/matched/-/matched-0.4.4.tgz", + "integrity": "sha1-Vte36xgDPwz5vFLrIJD6x9weifo=", + "requires": { + "arr-union": "3.1.0", + "async-array-reduce": "0.2.1", + "extend-shallow": "2.0.1", + "fs-exists-sync": "0.1.0", + "glob": "7.1.2", + "has-glob": "0.1.1", + "is-valid-glob": "0.3.0", + "lazy-cache": "2.0.2", + "resolve-dir": "0.1.1" + }, + "dependencies": { + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "requires": { + "set-getter": "0.1.0" + } + } + } + }, + "md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "1.1.5" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "method-override": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-2.3.9.tgz", + "integrity": "sha1-vRUfLONM8Bp2ykAKuVwBKxAtj3E=", + "requires": { + "debug": "2.6.8", + "methods": "1.1.2", + "parseurl": "1.3.1", + "vary": "1.1.1" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + }, + "mime-db": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", + "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=" + }, + "mime-types": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", + "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", + "requires": { + "mime-db": "1.29.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.1.tgz", + "integrity": "sha512-u1aUllxPJUI07cOqzR7reGmQxmCqlH88uIIsf6XZFEWgw7gXKpJdR+5R9Y3KEDmWYkdIz9wXZs3C0jOPxejk/Q==", + "requires": { + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz", + "integrity": "sha1-1cGr93vhVGGZUuJTM27Mq5sqMvU=", + "requires": { + "minipass": "2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mongodb": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.31.tgz", + "integrity": "sha1-GUBEXGYeGSF7s7+CRdmFSq71SNs=", + "requires": { + "es6-promise": "3.2.1", + "mongodb-core": "2.1.15", + "readable-stream": "2.2.7" + }, + "dependencies": { + "readable-stream": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + } + } + }, + "mongodb-core": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.15.tgz", + "integrity": "sha1-hB9TuH//9MdFgYnDXIroJ+EWl2Q=", + "requires": { + "bson": "1.0.4", + "require_optional": "1.0.1" + } + }, + "mongoose": { + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.11.7.tgz", + "integrity": "sha512-tRYcThB+qx8yzVdG7rY8mlBUrF5CEJADl4Vlp3MMI9vVpgyFtOJfC2/IZiGmNFNThJjNIESBUxgoFrCRVDcLCw==", + "requires": { + "async": "2.1.4", + "bson": "1.0.4", + "hooks-fixed": "2.0.0", + "kareem": "1.5.0", + "mongodb": "2.2.31", + "mpath": "0.3.0", + "mpromise": "0.5.5", + "mquery": "2.3.1", + "ms": "2.0.0", + "muri": "1.2.2", + "regexp-clone": "0.0.1", + "sliced": "1.0.1" + }, + "dependencies": { + "async": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", + "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", + "requires": { + "lodash": "4.17.4" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "mongoose-unique-validator": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/mongoose-unique-validator/-/mongoose-unique-validator-1.0.5.tgz", + "integrity": "sha1-WRhy+v4IMJOfC3VrZVQkPJMWOqA=", + "requires": { + "lodash.foreach": "4.5.0", + "lodash.get": "4.4.2" + } + }, + "mongose": { + "version": "0.0.2-security", + "resolved": "https://registry.npmjs.org/mongose/-/mongose-0.0.2-security.tgz", + "integrity": "sha512-XJUBQHhC/12+hWtrcB1Ww+gkxSzbxg4VdjpNlBQGvFoyPm1bErhA+3n/IkWbGCkavFB1OSycpvpCRphPsZXgLw==" + }, + "morgan": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.8.2.tgz", + "integrity": "sha1-eErHc05KRTqcbm6GgKkyknXItoc=", + "requires": { + "basic-auth": "1.1.0", + "debug": "2.6.8", + "depd": "1.1.1", + "on-finished": "2.3.0", + "on-headers": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "morgan-toolkit": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/morgan-toolkit/-/morgan-toolkit-1.0.2.tgz", + "integrity": "sha512-tFhfamRSNrnGlaVfY384lFyeHZl8Y4IwSt2219+6YEuVeEJKVBlAQHT/NSAF5hlHrloI0G578zRy0Hxv4Iv10Q==", + "requires": { + "chalk": "2.1.0", + "cli-highlight": "1.1.4" + } + }, + "mpath": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz", + "integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q=" + }, + "mpromise": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz", + "integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY=" + }, + "mquery": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.1.tgz", + "integrity": "sha1-mrNnSXFIAP8LtTpoHOS8TV8HyHs=", + "requires": { + "bluebird": "2.10.2", + "debug": "2.6.8", + "regexp-clone": "0.0.1", + "sliced": "0.0.5" + }, + "dependencies": { + "bluebird": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz", + "integrity": "sha1-AkpVFylTCIV/FPkfEQb8O1VfRGs=" + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "sliced": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", + "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=" + } + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "muri": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/muri/-/muri-1.2.2.tgz", + "integrity": "sha1-YxmBMmUNsIoEzHnM0A3Tia/SYxw=" + }, + "mz": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.6.0.tgz", + "integrity": "sha1-yLhSHZWN8KTydoAl22nHGe5O8c4=", + "requires": { + "any-promise": "1.3.0", + "object-assign": "4.1.1", + "thenify-all": "1.6.0" + } + }, + "nan": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.0.tgz", + "integrity": "sha1-qo8eNFMdgH6eJ3VbI0tKbsDBUqg=" + }, + "natural": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/natural/-/natural-0.1.29.tgz", + "integrity": "sha1-WaN6bNhtVekEtlbTudpD/S3J4Eo=", + "requires": { + "apparatus": "0.0.9", + "sylvester": "0.0.21", + "underscore": "1.8.3" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "node-pre-gyp": { + "version": "0.6.32", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz", + "integrity": "sha1-/EUrN25zGbPSVfXzSFPvb9j+H9U=", + "requires": { + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "rc": "1.1.7", + "request": "2.81.0", + "rimraf": "2.5.4", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.3.0" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1.1.0" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.3.0", + "validate-npm-package-license": "3.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" + }, + "object.assign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", + "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", + "requires": { + "define-properties": "1.1.2", + "function-bind": "1.1.0", + "object-keys": "1.0.11" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.3" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "1.3.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=" + }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.0.0", + "pause": "0.0.1" + } + }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.0.0" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "2.0.4" + } + }, + "prng-well1024a": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prng-well1024a/-/prng-well1024a-1.0.1.tgz", + "integrity": "sha1-Bejtkj5OorP3ivXulPBWtNXPqyQ=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "2.0.6" + } + }, + "proxy-addr": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", + "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", + "requires": { + "forwarded": "0.1.0", + "ipaddr.js": "1.4.0" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, + "randy": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/randy/-/randy-1.5.1.tgz", + "integrity": "sha1-59wIag7Li+99ZzVmQs0qM/liRlw=", + "requires": { + "prng-well1024a": "1.0.1" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz", + "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=", + "requires": { + "bytes": "2.4.0", + "iconv-lite": "0.4.15", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "integrity": "sha1-xepWS7B6/5/TpbMukGwdOmWUD+o=", + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "regexp-clone": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", + "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.16", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "2.0.0", + "semver": "5.3.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "requirejs": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.4.tgz", + "integrity": "sha512-qPD5gRrj6kGQ0ZySAJgqbArkaDqPMbQIT0zSZDkIv1mfA17w/tR2Faq5l2qqRf2CdQx6FWln9nUrgd2UDzb19A==" + }, + "resolve-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", + "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", + "requires": { + "expand-tilde": "1.2.2", + "global-modules": "0.2.3" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "optional": true, + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "send": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", + "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", + "requires": { + "debug": "2.6.8", + "depd": "1.1.1", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.0", + "fresh": "0.5.0", + "http-errors": "1.6.2", + "mime": "1.3.4", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "sentencer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/sentencer/-/sentencer-0.1.5.tgz", + "integrity": "sha1-HDURWBoAsiNhJ/+yH94X9PSGWeI=", + "requires": { + "articles": "0.2.1", + "lodash": "2.4.2", + "natural": "0.1.29", + "randy": "1.5.1" + } + }, + "serve-static": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", + "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.1", + "send": "0.15.4" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "requires": { + "to-object-path": "0.3.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": "1.0.1" + } + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "0.2.1" + } + }, + "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=" + }, + "supports-color": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.1.tgz", + "integrity": "sha512-qxzYsob3yv6U+xMzPrv170y8AwGP7i74g+pbixCfD6rgso8BscLT2qXIuz6TpOaiJZ3mFgT5O9lyT9nMU4LfaA==", + "requires": { + "has-flag": "2.0.0" + } + }, + "sylvester": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.21.tgz", + "integrity": "sha1-KYexzivS84sNzio0OIiEv6RADqc=" + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.3.0.tgz", + "integrity": "sha1-MJMYFkGPVa/E0hd1r91nIM7kXa4=", + "requires": { + "debug": "2.2.0", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.3.3", + "readable-stream": "2.1.5", + "rimraf": "2.5.4", + "tar": "2.2.1", + "uid-number": "0.0.6" + }, + "dependencies": { + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "requires": { + "wrappy": "1.0.2" + } + }, + "readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "requires": { + "any-promise": "1.3.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "requires": { + "thenify": "3.3.0" + } + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "3.2.2" + } + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.16" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "optional": true, + "requires": { + "source-map": "0.5.6", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "optional": true + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" + }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "1.0.0" + } + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "vary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "walkdir": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz", + "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=" + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "requires": { + "string-width": "1.0.2" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "optional": true + }, + "wordnet-db": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/wordnet-db/-/wordnet-db-3.1.6.tgz", + "integrity": "sha1-75kaOOmGq5HhsDai+ZF5jBNiD5g=", + "requires": { + "tar": "3.2.0" + }, + "dependencies": { + "tar": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-3.2.0.tgz", + "integrity": "sha512-YRHTZzumkYuLx/lXsWZVowi9WrYbOQvxbEDtuFjq+LPCtLLGleWLkL/pdCnTwZMA92+Vg3UwVRBjpVQNcg6H3A==", + "requires": { + "chownr": "1.0.1", + "minipass": "2.2.1", + "minizlib": "1.0.3", + "mkdirp": "0.5.1", + "yallist": "3.0.2" + } + } + } + }, + "wordpos": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wordpos/-/wordpos-1.1.5.tgz", + "integrity": "sha1-FzXIjAhGbwUpc1jij3JUEiQMzvc=", + "requires": { + "commander": "2.11.0", + "underscore": "1.8.3", + "wordnet-db": "3.1.6" + } + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "optional": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "3.0.0", + "lodash.assign": "4.2.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..edc1548 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "assignment_mad_lib_api", + "version": "1.0.0", + "description": "Serving up the madness with a Mad Lib API!", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/thebopshoobop/assignment_mad_lib_api.git" + }, + "author": "Ed and Will", + "license": "ISC", + "bugs": { + "url": "https://github.com/thebopshoobop/assignment_mad_lib_api/issues" + }, + "homepage": "https://github.com/thebopshoobop/assignment_mad_lib_api#readme", + "dependencies": { + "bcrypt": "^1.0.2", + "bluebird": "^3.5.0", + "body-parser": "^1.17.2", + "dotenv": "^4.0.0", + "express": "^4.15.4", + "express-flash-messages": "^0.1.1", + "express-handlebars": "^3.0.0", + "express-method-override-get-post-support": "0.0.7", + "express-session": "^1.15.5", + "faker": "^4.1.0", + "handlebars": "^4.0.10", + "jasmine": "^2.7.0", + "jasmine-node": "^1.14.5", + "load-helpers": "^1.0.1", + "md5": "^2.2.1", + "method-override": "^2.3.9", + "mongoose": "^4.11.7", + "mongoose-unique-validator": "^1.0.5", + "mongose": "0.0.2-security", + "morgan": "^1.8.2", + "morgan-toolkit": "^1.0.2", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "sentencer": "^0.1.5", + "uuid": "^3.1.0", + "wordpos": "^1.1.5" + } +} diff --git a/routes/index.js b/routes/index.js index fe2ac19..0af1e2c 100644 --- a/routes/index.js +++ b/routes/index.js @@ -49,7 +49,7 @@ function authenticate(passport) { else res.redirect("/"); }); }) - .catch(e => res.status(500).end(e)); + .catch(e => res.status(500).end(e.stack)); }); //logout handler diff --git a/views/login.handlebars b/views/login.handlebars index ae66c5a..887ce75 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -9,7 +9,7 @@
- +
diff --git a/views/register.handlebars b/views/register.handlebars index afe9929..0fd6f04 100644 --- a/views/register.handlebars +++ b/views/register.handlebars @@ -17,7 +17,8 @@
- + +
From 8d3c920773b0396187b1d4880562e325d6b1c753 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Fri, 18 Aug 2017 12:00:42 -0400 Subject: [PATCH 05/29] passport-local working --- routes/index.js | 12 +++++++++--- views/register.handlebars | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/routes/index.js b/routes/index.js index 0af1e2c..0a3058c 100644 --- a/routes/index.js +++ b/routes/index.js @@ -2,6 +2,7 @@ const express = require("express"); const router = express.Router(); const h = require("../helpers"); const { User } = require("../models"); +const faker = require('faker'); // Authentication Middleware const ensureAuthenticated = (req, res, next) => { @@ -36,13 +37,18 @@ function authenticate(passport) { //register view router.get("/register", (req, res) => { - res.render("register"); + const fake = { + fname: faker.name.firstName(), + lname: faker.name.lastName(), + email: faker.internet.email() + } + res.render("register", {fake}); }); //register handler router.post("/register", (req, res, next) => { - const { username, password } = req.body; - User.create({ username, password }) + const { fname, lname, email, password } = req.body; + User.create({ fname, lname, email, password }) .then(user => { req.login(user, err => { if (err) next(err); diff --git a/views/register.handlebars b/views/register.handlebars index 0fd6f04..568437a 100644 --- a/views/register.handlebars +++ b/views/register.handlebars @@ -18,7 +18,7 @@
- +
From a77b56fd4a783fcf96fca275ab371c6c194cd4b6 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Fri, 18 Aug 2017 12:17:26 -0400 Subject: [PATCH 06/29] token --- app.js | 3 +++ models/User.js | 12 +++++++++++- package-lock.json | 26 +++++++++++++++++++++++--- package.json | 3 +++ strategies/bearer.js | 14 ++++++++++++++ 5 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 strategies/bearer.js diff --git a/app.js b/app.js index 66c060e..09d5870 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,7 @@ const app = express(); const h = require("./helpers"); const { User } = require("./models"); + // .env if (process.env.NODE_ENV !== "production") { require("dotenv").config(); @@ -73,6 +74,8 @@ passport.deserializeUser(function(userId, done) { passport.use("local", require("./strategies/local")); +passport.use("bearer", require("./strategies/bearer")); + // Add populated user and referral URL to locals app.use(async (req, res, next) => { if (req.user) { diff --git a/models/User.js b/models/User.js index b5fe7da..ea5dd05 100644 --- a/models/User.js +++ b/models/User.js @@ -1,6 +1,8 @@ const mongoose = require("mongoose"); const uniqueValidator = require("mongoose-unique-validator"); const bcrypt = require("bcrypt"); +const md5 = require("md5"); +const uuid = require("uuid"); const Schema = mongoose.Schema; const UserSchema = new Schema( @@ -8,7 +10,8 @@ const UserSchema = new Schema( fname: { type: String, required: true }, lname: { type: String, required: true }, email: { type: String, required: true, unique: true }, - passwordHash: { type: String, required: true } + passwordHash: { type: String, required: true }, + token: { type: String, unique: true } }, { timestamps: true @@ -27,6 +30,13 @@ UserSchema.virtual("password").set(function(value) { this.passwordHash = bcrypt.hashSync(value, 8); }); + +UserSchema.pre('save', function(next) { + this.token = md5(`${ this.email }${ uuid() }`); + next(); +}); + + const User = mongoose.model("User", UserSchema); module.exports = User; diff --git a/package-lock.json b/package-lock.json index 6e312ff..d64133a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -213,6 +213,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" } } }, @@ -1934,6 +1939,14 @@ "pause": "0.0.1" } }, + "passport-http-bearer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz", + "integrity": "sha1-FHRp6jZp4qhMYWfvmdu3fh8AmKg=", + "requires": { + "passport-strategy": "1.0.0" + } + }, "passport-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", @@ -2036,9 +2049,9 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", + "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==" }, "random-bytes": { "version": "1.0.0", @@ -2156,6 +2169,13 @@ "tough-cookie": "2.3.2", "tunnel-agent": "0.6.0", "uuid": "3.1.0" + }, + "dependencies": { + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + } } }, "require_optional": { diff --git a/package.json b/package.json index edc1548..ee90a83 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,10 @@ "morgan": "^1.8.2", "morgan-toolkit": "^1.0.2", "passport": "^0.4.0", + "passport-http-bearer": "^1.0.1", "passport-local": "^1.0.0", + "qs": "^6.5.0", + "request": "^2.81.0", "sentencer": "^0.1.5", "uuid": "^3.1.0", "wordpos": "^1.1.5" diff --git a/strategies/bearer.js b/strategies/bearer.js new file mode 100644 index 0000000..78daa4a --- /dev/null +++ b/strategies/bearer.js @@ -0,0 +1,14 @@ +const BearerStrategy = require("passport-http-bearer").Strategy; +const { User } = require("../models"); + +const localHandler = (token, done)=>{ + User.findOne({token}).then(user=>{ + done(null, user || false) + }) + .catch(err => { + done(null, false) + }) +} + + +module.exports = new BearerStrategy(localHandler); \ No newline at end of file From ce3ef981545b09e7632ea9988b60209a57d5c5a6 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 12:29:03 -0400 Subject: [PATCH 07/29] password required on register --- views/register.handlebars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/register.handlebars b/views/register.handlebars index 568437a..1926a87 100644 --- a/views/register.handlebars +++ b/views/register.handlebars @@ -18,7 +18,7 @@
- +
From 113a93e4683d735fa6430e59ebd7ccb34fb9cd01 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 12:45:12 -0400 Subject: [PATCH 08/29] testing boilerplate --- .env | 1 + app.js | 20 +++++++----- spec/appSpec.js | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 .env create mode 100644 spec/appSpec.js diff --git a/.env b/.env new file mode 100644 index 0000000..c0d6652 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +NODE_ENV=development diff --git a/app.js b/app.js index 09d5870..1dea60c 100644 --- a/app.js +++ b/app.js @@ -3,7 +3,6 @@ const app = express(); const h = require("./helpers"); const { User } = require("./models"); - // .env if (process.env.NODE_ENV !== "production") { require("dotenv").config(); @@ -24,7 +23,6 @@ const methodOverride = require("method-override"); const getPostSupport = require("express-method-override-get-post-support"); app.use(methodOverride(getPostSupport.callback, getPostSupport.options)); - // Static Files app.use(express.static(`${__dirname}/public`)); @@ -46,10 +44,12 @@ app.use( const flash = require("express-flash-messages"); app.use(flash()); -// Log Request Info -const morgan = require("morgan"); -const morganToolkit = require("morgan-toolkit")(morgan); -app.use(morganToolkit()); +// Log Request Info in Development +if (process.env.NODE_ENV === "development") { + const morgan = require("morgan"); + const morganToolkit = require("morgan-toolkit")(morgan); + app.use(morganToolkit()); +} // Connect to Mongoose const mongoose = require("mongoose"); @@ -97,5 +97,9 @@ args.push(() => { console.log(`Listening: http://${host}:${port}`); }); -// Use apply to pass the args to listen -app.listen.apply(app, args); +// Use apply to pass the args to listen if we're run directly +if (require.main === module) { + app.listen.apply(app, args); +} + +module.exports = app; diff --git a/spec/appSpec.js b/spec/appSpec.js new file mode 100644 index 0000000..05292db --- /dev/null +++ b/spec/appSpec.js @@ -0,0 +1,81 @@ +const app = require("../app"); +const request = require("request"); +const mongoose = require("mongoose"); +const User = mongoose.model("User"); +const qs = require("qs"); + +describe("App", () => { + const baseUrl = "http://localhost:8888"; + const apiUrl = baseUrl + "/api/v1/"; + let server; + let user; + const apiUrlFor = (type, params) => { + params = params ? `&${qs.stringify(params)}` : ""; + return `${apiUrl}${type}?access_token=${user.token}${params}`; + }; + const j = str => JSON.parse(str); + + beforeAll(done => { + server = app.listen(8888, () => { + done(); + }); + }); + + beforeEach(done => { + User.create({ + fname: "Foo", + lname: "Bar", + email: "foobar@gmail", + password: "password" + }) + .then(result => { + user = result; + done(); + }) + .catch(e => console.error(e)); + }); + + afterAll(done => { + server.close(); + server = null; + done(); + }); + + // ---------------------------------------- + // App + // ---------------------------------------- + it("renders the home page", done => { + request.get(baseUrl, (err, res, body) => { + expect(res.statusCode).toBe(200); + expect(body).toMatch(/api/i); + done(); + }); + }); + + // ---------------------------------------- + // Furious Spinoffs API + // ---------------------------------------- + it("returns an array with the given number of titles", done => { + request.get( + apiUrlFor("furious_spinoffs", { count: 10 }), + (err, res, body) => { + let result = j(body); + expect(result.length).toEqual(10); + done(); + } + ); + }); + + it("does not allow requests without an access_token", done => { + request.get(apiUrl, (err, res, body) => { + // Note, this SHOULD have a status code of 401 + // however something is not working right with + // the Passport HTTP Bearer package in setting + // the correct status code + // See Github issue: + // https://github.com/jaredhanson/passport-http-bearer/issues/11 + expect(res.statusCode).toBe(404); + done(); + }); + }); +}); From 431fc6704a7e69ed90487f1e092b30f2cdd77fb3 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Fri, 18 Aug 2017 13:12:39 -0400 Subject: [PATCH 09/29] promisetest --- package-lock.json | 31 +++++++++++++++++++++++++++++++ package.json | 1 + spec/appSpec.js | 25 +++++++++++++++++++++---- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d64133a..e6554d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2178,6 +2178,32 @@ } } }, + "request-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.1.tgz", + "integrity": "sha1-fuxWyJMXqCLL/qmbA5zlQ8LhX2c=", + "requires": { + "bluebird": "3.5.0", + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.2" + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "requires": { + "lodash": "4.17.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + } + } + }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -2405,6 +2431,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", diff --git a/package.json b/package.json index ee90a83..3e4489f 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "passport-local": "^1.0.0", "qs": "^6.5.0", "request": "^2.81.0", + "request-promise": "^4.2.1", "sentencer": "^0.1.5", "uuid": "^3.1.0", "wordpos": "^1.1.5" diff --git a/spec/appSpec.js b/spec/appSpec.js index 05292db..cf7db87 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -1,5 +1,5 @@ const app = require("../app"); -const request = require("request"); +const request = require("request-promise"); const mongoose = require("mongoose"); const User = mongoose.model("User"); const qs = require("qs"); @@ -15,6 +15,10 @@ describe("App", () => { }; const j = str => JSON.parse(str); + const requestResponse = (uri)=>{ + return {uri, resolveWithFullResponse: true} + } + beforeAll(done => { server = app.listen(8888, () => { done(); @@ -44,12 +48,25 @@ describe("App", () => { // ---------------------------------------- // App // ---------------------------------------- + + + it("renders the home page", done => { - request.get(baseUrl, (err, res, body) => { + // request.get(baseUrl, (err, res, body) => { + // expect(res.statusCode).toBe(200); + // expect(body).toMatch(/api/i); + // done(); + // }) + request(requestResponse(baseUrl)).then(res=>{ expect(res.statusCode).toBe(200); - expect(body).toMatch(/api/i); + expect(res).toMatch(/api/i); done(); - }); + }) + .catch(err=>{ + console.error(err); + expect(false); + done(); + }) }); // ---------------------------------------- From f4276d1559b101c4be903f269c16a2f759325251 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 13:36:49 -0400 Subject: [PATCH 10/29] listing route tests --- package.json | 6 +- spec/appSpec.js | 120 ++++++++++++++++++++++++++----------- spec/helpers/specHelper.js | 18 ++++++ spec/support/jasmine.json | 7 +++ 4 files changed, 116 insertions(+), 35 deletions(-) create mode 100644 spec/helpers/specHelper.js create mode 100644 spec/support/jasmine.json diff --git a/package.json b/package.json index 3e4489f..ac7fc9e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,11 @@ "description": "Serving up the madness with a Mad Lib API!", "main": "app.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "npm run whichnodemon && nodemon --exec npm run jasmine || npm run jasmine", + "start": "npm run whichnodemon && nodemon app.js || node app.js", + "whichnodemon": "which nodemon > /dev/null", + "console": "node repl.js", + "jasmine": "./node_modules/jasmine/bin/jasmine.js" }, "repository": { "type": "git", diff --git a/spec/appSpec.js b/spec/appSpec.js index cf7db87..8ab8d82 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -1,5 +1,5 @@ const app = require("../app"); -const request = require("request-promise"); +const request = require("promise"); const mongoose = require("mongoose"); const User = mongoose.model("User"); const qs = require("qs"); @@ -15,10 +15,6 @@ describe("App", () => { }; const j = str => JSON.parse(str); - const requestResponse = (uri)=>{ - return {uri, resolveWithFullResponse: true} - } - beforeAll(done => { server = app.listen(8888, () => { done(); @@ -39,6 +35,10 @@ describe("App", () => { .catch(e => console.error(e)); }); + afterEach(done => { + User.remove({}).then(() => done()).catch(e => console.error(e)); + }); + afterAll(done => { server.close(); server = null; @@ -49,38 +49,13 @@ describe("App", () => { // App // ---------------------------------------- - - it("renders the home page", done => { - // request.get(baseUrl, (err, res, body) => { - // expect(res.statusCode).toBe(200); - // expect(body).toMatch(/api/i); - // done(); - // }) - request(requestResponse(baseUrl)).then(res=>{ + request.get(baseUrl, (err, res, body) => { expect(res.statusCode).toBe(200); - expect(res).toMatch(/api/i); - done(); - }) - .catch(err=>{ - console.error(err); - expect(false); - done(); - }) - }); - // ---------------------------------------- - // Furious Spinoffs API - // ---------------------------------------- - it("returns an array with the given number of titles", done => { - request.get( - apiUrlFor("furious_spinoffs", { count: 10 }), - (err, res, body) => { - let result = j(body); - expect(result.length).toEqual(10); - done(); - } - ); + expect(body).toMatch(/api/i); + done(); + }); }); it("does not allow requests without an access_token", done => { @@ -95,4 +70,81 @@ describe("App", () => { done(); }); }); + + // ---------------------------------------- + // List API + // ---------------------------------------- + describe("List APIs", () => { + it("returns an array with the default number of nouns", done => { + request.get(apiUrlFor("nouns"), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(10); + done(); + }); + }); + + it("returns an array with the specified number of nouns", done => { + request.get(apiUrlFor("nouns", { count: 22 }), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(22); + done(); + }); + }); + + it("returns an array with the default number of verbs", done => { + request.get(apiUrlFor("verbs"), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(10); + done(); + }); + }); + + it("returns an array with the specified number of verbs", done => { + request.get(apiUrlFor("verbs", { count: 22 }), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(22); + done(); + }); + }); + + it("returns an array with the default number of adverbs", done => { + request.get(apiUrlFor("adverbs"), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(10); + done(); + }); + }); + + it("returns an array with the specified number of adverbs", done => { + request.get(apiUrlFor("adverbs", { count: 22 }), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(22); + done(); + }); + }); + + it("returns an array with the default number of adjectives", done => { + request.get(apiUrlFor("adjectives"), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(10); + done(); + }); + }); + + it("returns an array with the specified number of adjectives", done => { + request.get(apiUrlFor("adjectives", { count: 22 }), (err, res, body) => { + let result = j(body); + + expect(result.length).toEqual(22); + done(); + }); + }); + }); }); diff --git a/spec/helpers/specHelper.js b/spec/helpers/specHelper.js new file mode 100644 index 0000000..b505158 --- /dev/null +++ b/spec/helpers/specHelper.js @@ -0,0 +1,18 @@ +const mongoose = require("mongoose"); + +// Set test environment +process.env.NODE_ENV = "test"; + +beforeAll(done => { + if (mongoose.connection.readyState) { + done(); + } else { + require("./../../mongo")().then(() => done()); + } +}); + +afterEach(done => { + require("./../../seeds/clean")() + .then(() => done()) + .catch(e => console.error(e.stack)); +}); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..94ac6eb --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,7 @@ +{ + "spec_dir": "spec", + "spec_files": ["**/*[sS]pec.js"], + "helpers": ["helpers/**/*.js"], + "stopSpecOnExpectationFailure": false, + "random": false +} From b3f06fc46307b8d791e179484ed93aefc79351d4 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 13:37:51 -0400 Subject: [PATCH 11/29] save specHelper --- spec/helpers/specHelper.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spec/helpers/specHelper.js b/spec/helpers/specHelper.js index b505158..ab2960c 100644 --- a/spec/helpers/specHelper.js +++ b/spec/helpers/specHelper.js @@ -10,9 +10,3 @@ beforeAll(done => { require("./../../mongo")().then(() => done()); } }); - -afterEach(done => { - require("./../../seeds/clean")() - .then(() => done()) - .catch(e => console.error(e.stack)); -}); From 984816722350e784f45cf812111bbd2cb7105359 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Fri, 18 Aug 2017 13:59:20 -0400 Subject: [PATCH 12/29] passed first test --- app.js | 1 + routes/apiV1.js | 15 +++++++++++++++ spec/appSpec.js | 4 ++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 routes/apiV1.js diff --git a/app.js b/app.js index 1dea60c..92eb7fb 100644 --- a/app.js +++ b/app.js @@ -86,6 +86,7 @@ app.use(async (req, res, next) => { // Routes app.use("/", require("./routes")(passport)); +app.use("/api/v1", require("./routes/apiV1")(passport)); // Set up port/host const port = process.env.PORT || process.argv[2] || 3000; diff --git a/routes/apiV1.js b/routes/apiV1.js new file mode 100644 index 0000000..40976be --- /dev/null +++ b/routes/apiV1.js @@ -0,0 +1,15 @@ +const express = require("express"); +const router = express.Router(); +const h = require("../helpers"); +const { User } = require("../models"); + + +function api(passport) { + + router.get('/', (req, res) => { + res.json({api: "api"}) + }) + return router; +} + +module.exports = api \ No newline at end of file diff --git a/spec/appSpec.js b/spec/appSpec.js index 8ab8d82..1ff9a1e 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -1,5 +1,5 @@ const app = require("../app"); -const request = require("promise"); +const request = require("request"); const mongoose = require("mongoose"); const User = mongoose.model("User"); const qs = require("qs"); @@ -53,7 +53,7 @@ describe("App", () => { request.get(baseUrl, (err, res, body) => { expect(res.statusCode).toBe(200); - expect(body).toMatch(/api/i); + expect(body).toMatch(/Mad Libs/); done(); }); }); From eab611aac139dba6d28c9370c89fd3ada665345d Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 15:56:27 -0400 Subject: [PATCH 13/29] check token middleware, 404 handler --- app.js | 6 +++++- helpers/APIHelper.js | 7 +++++++ routes/apiV1.js | 15 +++++---------- spec/appSpec.js | 8 +------- 4 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 helpers/APIHelper.js diff --git a/app.js b/app.js index 92eb7fb..ca7d3a2 100644 --- a/app.js +++ b/app.js @@ -86,7 +86,11 @@ app.use(async (req, res, next) => { // Routes app.use("/", require("./routes")(passport)); -app.use("/api/v1", require("./routes/apiV1")(passport)); +app.use( + "/api/v1", + passport.authenticate("bearer", { session: false }), + require("./routes/apiV1") +); // Set up port/host const port = process.env.PORT || process.argv[2] || 3000; diff --git a/helpers/APIHelper.js b/helpers/APIHelper.js new file mode 100644 index 0000000..a8acc6a --- /dev/null +++ b/helpers/APIHelper.js @@ -0,0 +1,7 @@ +module.exports = { + APIPath: v => `/api/v${v}`, + nounsPath: v => `/api/v${v}/nouns`, + verbsPath: v => `/api/v${v}/verbs`, + adverbsPath: v => `/api/v${v}/adverbs`, + adjectivesPath: v => `/api/v${v}/adjectives` +}; diff --git a/routes/apiV1.js b/routes/apiV1.js index 40976be..3904f93 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -1,15 +1,10 @@ const express = require("express"); const router = express.Router(); -const h = require("../helpers"); -const { User } = require("../models"); +router.get("/", (req, res) => res.json({ name: "Mad Lib API" })); -function api(passport) { - - router.get('/', (req, res) => { - res.json({api: "api"}) - }) - return router; -} +router.get("*", (req, res) => { + res.json({ error: "404 Not Found" }); +}); -module.exports = api \ No newline at end of file +module.exports = router; diff --git a/spec/appSpec.js b/spec/appSpec.js index 1ff9a1e..7887cdb 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -60,13 +60,7 @@ describe("App", () => { it("does not allow requests without an access_token", done => { request.get(apiUrl, (err, res, body) => { - // Note, this SHOULD have a status code of 401 - // however something is not working right with - // the Passport HTTP Bearer package in setting - // the correct status code - // See Github issue: - // https://github.com/jaredhanson/passport-http-bearer/issues/11 - expect(res.statusCode).toBe(404); + expect(res.statusCode).toBe(401); done(); }); }); From 9490570c33e72a60de0c255b4b9c4f1a349a44dc Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 16:12:05 -0400 Subject: [PATCH 14/29] passing nouns --- routes/apiV1.js | 18 ++++++++++++++++-- spec/appSpec.js | 12 ++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index 3904f93..ab897f0 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -1,10 +1,24 @@ -const express = require("express"); -const router = express.Router(); +const router = require("express").Router(); +const WordPOS = require("wordpos"); +const wp = new WordPOS(); router.get("/", (req, res) => res.json({ name: "Mad Lib API" })); +router.get("/nouns", (req, res, next) => { + const count = req.query.count || 10; + wp + .randNoun({ count: count }) + .then(words => res.json(words)) + .catch(e => next(e)); +}); + router.get("*", (req, res) => { res.json({ error: "404 Not Found" }); }); +router.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ error: err.stack }); +}); + module.exports = router; diff --git a/spec/appSpec.js b/spec/appSpec.js index 7887cdb..9d8b0df 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -87,7 +87,7 @@ describe("App", () => { }); }); - it("returns an array with the default number of verbs", done => { + xit("returns an array with the default number of verbs", done => { request.get(apiUrlFor("verbs"), (err, res, body) => { let result = j(body); @@ -96,7 +96,7 @@ describe("App", () => { }); }); - it("returns an array with the specified number of verbs", done => { + xit("returns an array with the specified number of verbs", done => { request.get(apiUrlFor("verbs", { count: 22 }), (err, res, body) => { let result = j(body); @@ -105,7 +105,7 @@ describe("App", () => { }); }); - it("returns an array with the default number of adverbs", done => { + xit("returns an array with the default number of adverbs", done => { request.get(apiUrlFor("adverbs"), (err, res, body) => { let result = j(body); @@ -114,7 +114,7 @@ describe("App", () => { }); }); - it("returns an array with the specified number of adverbs", done => { + xit("returns an array with the specified number of adverbs", done => { request.get(apiUrlFor("adverbs", { count: 22 }), (err, res, body) => { let result = j(body); @@ -123,7 +123,7 @@ describe("App", () => { }); }); - it("returns an array with the default number of adjectives", done => { + xit("returns an array with the default number of adjectives", done => { request.get(apiUrlFor("adjectives"), (err, res, body) => { let result = j(body); @@ -132,7 +132,7 @@ describe("App", () => { }); }); - it("returns an array with the specified number of adjectives", done => { + xit("returns an array with the specified number of adjectives", done => { request.get(apiUrlFor("adjectives", { count: 22 }), (err, res, body) => { let result = j(body); From 051a814dcbe418e3716acbf5aed00a92f6e0b125 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 16:13:30 -0400 Subject: [PATCH 15/29] passing verbs --- routes/apiV1.js | 10 ++++++---- spec/appSpec.js | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index ab897f0..b0f96bc 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -6,10 +6,12 @@ router.get("/", (req, res) => res.json({ name: "Mad Lib API" })); router.get("/nouns", (req, res, next) => { const count = req.query.count || 10; - wp - .randNoun({ count: count }) - .then(words => res.json(words)) - .catch(e => next(e)); + wp.randNoun({ count }).then(words => res.json(words)).catch(e => next(e)); +}); + +router.get("/verbs", (req, res, next) => { + const count = req.query.count || 10; + wp.randVerb({ count }).then(words => res.json(words)).catch(e => next(e)); }); router.get("*", (req, res) => { diff --git a/spec/appSpec.js b/spec/appSpec.js index 9d8b0df..f6ad346 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -87,7 +87,7 @@ describe("App", () => { }); }); - xit("returns an array with the default number of verbs", done => { + it("returns an array with the default number of verbs", done => { request.get(apiUrlFor("verbs"), (err, res, body) => { let result = j(body); @@ -96,7 +96,7 @@ describe("App", () => { }); }); - xit("returns an array with the specified number of verbs", done => { + it("returns an array with the specified number of verbs", done => { request.get(apiUrlFor("verbs", { count: 22 }), (err, res, body) => { let result = j(body); From 9f1c49b560d8d059762eb06bd05497db4798e5a9 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 16:14:39 -0400 Subject: [PATCH 16/29] passing adverbs --- routes/apiV1.js | 5 +++++ spec/appSpec.js | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index b0f96bc..37dbfcd 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -14,6 +14,11 @@ router.get("/verbs", (req, res, next) => { wp.randVerb({ count }).then(words => res.json(words)).catch(e => next(e)); }); +router.get("/adverbs", (req, res, next) => { + const count = req.query.count || 10; + wp.randAdverb({ count }).then(words => res.json(words)).catch(e => next(e)); +}); + router.get("*", (req, res) => { res.json({ error: "404 Not Found" }); }); diff --git a/spec/appSpec.js b/spec/appSpec.js index f6ad346..fa9324a 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -105,7 +105,7 @@ describe("App", () => { }); }); - xit("returns an array with the default number of adverbs", done => { + it("returns an array with the default number of adverbs", done => { request.get(apiUrlFor("adverbs"), (err, res, body) => { let result = j(body); @@ -114,7 +114,7 @@ describe("App", () => { }); }); - xit("returns an array with the specified number of adverbs", done => { + it("returns an array with the specified number of adverbs", done => { request.get(apiUrlFor("adverbs", { count: 22 }), (err, res, body) => { let result = j(body); From 24fca0184cd1c44ce1761ff3663769a6cd926f0e Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 16:15:48 -0400 Subject: [PATCH 17/29] passing adjectives --- routes/apiV1.js | 8 ++++++++ spec/appSpec.js | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index 37dbfcd..71f1d1b 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -19,6 +19,14 @@ router.get("/adverbs", (req, res, next) => { wp.randAdverb({ count }).then(words => res.json(words)).catch(e => next(e)); }); +router.get("/adjectives", (req, res, next) => { + const count = req.query.count || 10; + wp + .randAdjective({ count }) + .then(words => res.json(words)) + .catch(e => next(e)); +}); + router.get("*", (req, res) => { res.json({ error: "404 Not Found" }); }); diff --git a/spec/appSpec.js b/spec/appSpec.js index fa9324a..7887cdb 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -123,7 +123,7 @@ describe("App", () => { }); }); - xit("returns an array with the default number of adjectives", done => { + it("returns an array with the default number of adjectives", done => { request.get(apiUrlFor("adjectives"), (err, res, body) => { let result = j(body); @@ -132,7 +132,7 @@ describe("App", () => { }); }); - xit("returns an array with the specified number of adjectives", done => { + it("returns an array with the specified number of adjectives", done => { request.get(apiUrlFor("adjectives", { count: 22 }), (err, res, body) => { let result = j(body); From d58a027d7d2dc2ce82b3ed0600c025d11f71acc6 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 16:23:07 -0400 Subject: [PATCH 18/29] to fancy by half refactor --- routes/apiV1.js | 55 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index 71f1d1b..832fc12 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -4,28 +4,45 @@ const wp = new WordPOS(); router.get("/", (req, res) => res.json({ name: "Mad Lib API" })); -router.get("/nouns", (req, res, next) => { +const typeMap = { + nouns: wp.randNoun, + verbs: wp.randVerb, + adverbs: wp.randAdverb, + adjectives: wp.randAdjective +}; + +router.get("/:type", (req, res, next) => { const count = req.query.count || 10; - wp.randNoun({ count }).then(words => res.json(words)).catch(e => next(e)); + const typeFunc = typeMap[req.params.type]; + if (typeFunc) { + typeFunc({ count }).then(words => res.json(words)).catch(e => next(e)); + } else { + next(); + } }); -router.get("/verbs", (req, res, next) => { - const count = req.query.count || 10; - wp.randVerb({ count }).then(words => res.json(words)).catch(e => next(e)); -}); - -router.get("/adverbs", (req, res, next) => { - const count = req.query.count || 10; - wp.randAdverb({ count }).then(words => res.json(words)).catch(e => next(e)); -}); - -router.get("/adjectives", (req, res, next) => { - const count = req.query.count || 10; - wp - .randAdjective({ count }) - .then(words => res.json(words)) - .catch(e => next(e)); -}); +// router.get("/nouns", (req, res, next) => { +// const count = req.query.count || 10; +// wp.randNoun({ count }).then(words => res.json(words)).catch(e => next(e)); +// }); + +// router.get("/verbs", (req, res, next) => { +// const count = req.query.count || 10; +// wp.randVerb({ count }).then(words => res.json(words)).catch(e => next(e)); +// }); + +// router.get("/adverbs", (req, res, next) => { +// const count = req.query.count || 10; +// wp.randAdverb({ count }).then(words => res.json(words)).catch(e => next(e)); +// }); + +// router.get("/adjectives", (req, res, next) => { +// const count = req.query.count || 10; +// wp +// .randAdjective({ count }) +// .then(words => res.json(words)) +// .catch(e => next(e)); +// }); router.get("*", (req, res) => { res.json({ error: "404 Not Found" }); From 50a5e9335763c1fba2d3b5a912bf6d267282ab42 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Fri, 18 Aug 2017 16:52:52 -0400 Subject: [PATCH 19/29] it returns a sentence --- routes/apiV1.js | 42 +++++++++++++++--------------------------- spec/appSpec.js | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index 832fc12..ef3092f 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -5,44 +5,32 @@ const wp = new WordPOS(); router.get("/", (req, res) => res.json({ name: "Mad Lib API" })); const typeMap = { - nouns: wp.randNoun, - verbs: wp.randVerb, - adverbs: wp.randAdverb, - adjectives: wp.randAdjective + nouns: "randNoun", + verbs: "randVerb", + adverbs: "randAdverb", + adjectives: "randAdjective" }; router.get("/:type", (req, res, next) => { const count = req.query.count || 10; const typeFunc = typeMap[req.params.type]; if (typeFunc) { - typeFunc({ count }).then(words => res.json(words)).catch(e => next(e)); + wp[typeFunc]({ count }).then(words => res.json(words)).catch(e => next(e)); } else { next(); } }); -// router.get("/nouns", (req, res, next) => { -// const count = req.query.count || 10; -// wp.randNoun({ count }).then(words => res.json(words)).catch(e => next(e)); -// }); - -// router.get("/verbs", (req, res, next) => { -// const count = req.query.count || 10; -// wp.randVerb({ count }).then(words => res.json(words)).catch(e => next(e)); -// }); - -// router.get("/adverbs", (req, res, next) => { -// const count = req.query.count || 10; -// wp.randAdverb({ count }).then(words => res.json(words)).catch(e => next(e)); -// }); - -// router.get("/adjectives", (req, res, next) => { -// const count = req.query.count || 10; -// wp -// .randAdjective({ count }) -// .then(words => res.json(words)) -// .catch(e => next(e)); -// }); +router.post("/sentences", (req, res) => { + const {words, template} = req.body; + if (!words || !template) { + res.status(400).json({ error: "words list and template sentence are both required" }); + } else { + res.end("sentence here") + } + +}) + router.get("*", (req, res) => { res.json({ error: "404 Not Found" }); diff --git a/spec/appSpec.js b/spec/appSpec.js index 7887cdb..b68a756 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -140,5 +140,23 @@ describe("App", () => { done(); }); }); + }) + + // ---------------------------------------- + // Sentence API + // ---------------------------------------- + describe("Sentence API", () => { + it("requires both a list of words and a template sentence.", done => { + request.post(apiUrlFor("sentences"), (err, res, body) => { + expect(res.statusCode).toBe(400); + done(); + }); + }); + + it("returns a sentence", done=>{ + + }) + + }); }); From 34872cde5fb0a8e2b10c984759a08f141a7dbc6e Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 17:11:49 -0400 Subject: [PATCH 20/29] sentences require valid inputs --- routes/apiV1.js | 22 ++++++++++++++-------- spec/appSpec.js | 19 +++++++++++++------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index ef3092f..90f8e75 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -21,16 +21,22 @@ router.get("/:type", (req, res, next) => { } }); -router.post("/sentences", (req, res) => { - const {words, template} = req.body; +const checkSentenceInputs = (req, res, next) => { + const { words, template } = req.body; if (!words || !template) { - res.status(400).json({ error: "words list and template sentence are both required" }); - } else { - res.end("sentence here") - } - -}) + res + .status(400) + .json({ error: "words list and template sentence are both required" }); + } else if (!Array.isArray(words) || typeof template !== "string") { + res + .status(400) + .json({ error: "words must be an array and template must be a string" }); + } else next(); +}; +router.post("/sentences", checkSentenceInputs, (req, res) => { + res.end("sentence here"); +}); router.get("*", (req, res) => { res.json({ error: "404 Not Found" }); diff --git a/spec/appSpec.js b/spec/appSpec.js index b68a756..bc54603 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -140,7 +140,7 @@ describe("App", () => { done(); }); }); - }) + }); // ---------------------------------------- // Sentence API @@ -153,10 +153,17 @@ describe("App", () => { }); }); - it("returns a sentence", done=>{ - - }) - - + it("requires that words be an array of strings and template is a string", done => { + const formData = { + form: { + words: "this is not an array", + template: ["this", "is", "not", "a", "string"] + } + }; + request.post(apiUrlFor("sentences"), formData, (err, res, body) => { + expect(res.statusCode).toBe(400); + done(); + }); + }); }); }); From a257a9ec81e0df8b16824e1cfb51fd9f1bc735a7 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 17:19:30 -0400 Subject: [PATCH 21/29] senteces must return a string --- routes/apiV1.js | 2 +- spec/appSpec.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index 90f8e75..4bdefbb 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -35,7 +35,7 @@ const checkSentenceInputs = (req, res, next) => { }; router.post("/sentences", checkSentenceInputs, (req, res) => { - res.end("sentence here"); + res.json({ sentence: "sentence here" }); }); router.get("*", (req, res) => { diff --git a/spec/appSpec.js b/spec/appSpec.js index bc54603..1209e00 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -165,5 +165,20 @@ describe("App", () => { done(); }); }); + + it("returns a sentence", done => { + const formData = { + form: { + words: ["this", "is", "an", "array"], + template: "this is a string" + } + }; + request.post(apiUrlFor("sentences"), formData, (err, res, body) => { + let result = j(body); + + expect(typeof result.sentence).toEqual("string"); + done(); + }); + }); }); }); From c52d5ca2f5806ea1532a8f64730806ad8a92665d Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Fri, 18 Aug 2017 18:15:55 -0400 Subject: [PATCH 22/29] returns sentence --- routes/apiV1.js | 40 ++++++++++++++++++++++++++++++++++++++-- spec/appSpec.js | 15 +++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/routes/apiV1.js b/routes/apiV1.js index 4bdefbb..5a8af93 100644 --- a/routes/apiV1.js +++ b/routes/apiV1.js @@ -1,6 +1,7 @@ const router = require("express").Router(); const WordPOS = require("wordpos"); const wp = new WordPOS(); +var Sentencer = require('sentencer'); router.get("/", (req, res) => res.json({ name: "Mad Lib API" })); @@ -34,8 +35,43 @@ const checkSentenceInputs = (req, res, next) => { } else next(); }; -router.post("/sentences", checkSentenceInputs, (req, res) => { - res.json({ sentence: "sentence here" }); +router.post("/sentences", checkSentenceInputs, async (req, res, next) => { + try { + const {nouns, verbs, adjectives, adverbs} = await wp.getPOS(req.body.words) + + const getRandomArrayMember = (array)=> { + return array[Math.floor(Math.random()*array.length)]; + } + + const verb = ()=> { + return getRandomArrayMember(verbs); + } + + const adverb = ()=> { + return getRandomArrayMember(adverbs); + } + + + console.log("Nouns is: ", nouns); + console.log("Verbs is: ", verbs); + console.log("Adjectives is: ", adjectives); + console.log("Adverbs is: ", adverbs); + + Sentencer.configure({ + nounList: nouns, + adjectiveList: adjectives, + actions: { + verb: verb, + adverb: adverb + } + }) + const sentence = Sentencer.make(req.body.template); + console.log("Sentence is: ", sentence); + res.json({sentence}) + } + catch (err) { + next(err); + } }); router.get("*", (req, res) => { diff --git a/spec/appSpec.js b/spec/appSpec.js index 1209e00..3cb8038 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -166,19 +166,26 @@ describe("App", () => { }); }); - it("returns a sentence", done => { + it("returns the correct sentence for the given input.", done => { const formData = { form: { - words: ["this", "is", "an", "array"], - template: "this is a string" + words: ["Warty", "zebra", "extend", "furiously"], + template: "{{adjective}} {{noun}} {{verb}} {{adverb}}." } }; request.post(apiUrlFor("sentences"), formData, (err, res, body) => { let result = j(body); - expect(typeof result.sentence).toEqual("string"); + expect(result.sentence).toEqual("Warty zebra extend furiously."); done(); }); }); + + + + + + + }); }); From 5a900a7ce40350d3c5e457bcb9a84ab06486024d Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 18:41:18 -0400 Subject: [PATCH 23/29] Simple client --- client.js | 39 +++++++++++++++++++++++++++++++++ spec/appSpec.js | 7 ------ views/client/display.handlebars | 2 ++ views/client/index.handlebars | 24 ++++++++++++++++++++ 4 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 client.js create mode 100644 views/client/display.handlebars create mode 100644 views/client/index.handlebars diff --git a/client.js b/client.js new file mode 100644 index 0000000..b145e90 --- /dev/null +++ b/client.js @@ -0,0 +1,39 @@ +const express = require("express"); +const app = express(); +const request = require("request"); + +// Post Data +const bodyParser = require("body-parser"); +app.use(bodyParser.urlencoded({ extended: true })); + +// Handlebars +const expressHandlebars = require("express-handlebars"); +const hbs = expressHandlebars.create({ + partialsDir: "views/", + defaultLayout: "application" +}); +app.engine("handlebars", hbs.engine); +app.set("view engine", "handlebars"); + +// Logging +const morgan = require("morgan"); +const morganToolkit = require("morgan-toolkit")(morgan); +app.use(morganToolkit()); + +// Route Handlers +app.get("/", (req, res) => { + res.render("client/index"); +}); + +app.post("/", (req, res) => { + let { token, template, words } = req.body; + words = words.split(" "); + const url = `http://localhost:3000/api/v1/sentences?access_token=${token}`; + const formData = { form: { words, template } }; + request.post(url, formData, (err, r, body) => { + const sentence = JSON.parse(body).sentence; + res.render("client/display", { sentence }); + }); +}); + +app.listen(3030, () => console.log("Up and running!")); diff --git a/spec/appSpec.js b/spec/appSpec.js index 3cb8038..f8b622c 100644 --- a/spec/appSpec.js +++ b/spec/appSpec.js @@ -180,12 +180,5 @@ describe("App", () => { done(); }); }); - - - - - - - }); }); diff --git a/views/client/display.handlebars b/views/client/display.handlebars new file mode 100644 index 0000000..b0c7f8b --- /dev/null +++ b/views/client/display.handlebars @@ -0,0 +1,2 @@ +

Your Sentence

+

{{ sentence }}

diff --git a/views/client/index.handlebars b/views/client/index.handlebars new file mode 100644 index 0000000..ed2e8cc --- /dev/null +++ b/views/client/index.handlebars @@ -0,0 +1,24 @@ +
+
+
Build Sentence
+
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+
From 3d5209c27ce782a5cdd4a00ad2323c60b9182863 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 19:04:40 -0400 Subject: [PATCH 24/29] almost client --- client.js | 35 +++++++++++++++++++++++++++- views/client/index.handlebars | 13 ++--------- views/layouts/application.handlebars | 1 - 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/client.js b/client.js index b145e90..bf3795d 100644 --- a/client.js +++ b/client.js @@ -1,6 +1,7 @@ const express = require("express"); const app = express(); const request = require("request"); +const rp = require("request-promise"); // Post Data const bodyParser = require("body-parser"); @@ -21,11 +22,43 @@ const morganToolkit = require("morgan-toolkit")(morgan); app.use(morganToolkit()); // Route Handlers + app.get("/", (req, res) => { res.render("client/index"); }); -app.post("/", (req, res) => { +app.post("/prepare", async (req, res) => { + const token = req.body.token; + try { + const promises = [ + rp.get(`http://localhost:3000/api/v1/nouns?access_token=${token}`), + rp.get(`http://localhost:3000/api/v1/verbs?access_token=${token}`), + rp.get(`http://localhost:3000/api/v1/adverbs?access_token=${token}`), + rp.get(`http://localhost:3000/api/v1/adjectives?access_token=${token}`) + ]; + let [nouns, verbs, adverbs, adjectives] = await Promise.all(promises); + [nouns, verbs, adverbs, adjectives] = [ + nouns, + verbs, + adverbs, + adjectives + ].map(type => { + JSON.parse(type); + }); + const words = [ + { name: "Nouns", words: nouns }, + { name: "Verbs", words: verbs }, + { name: "Adverbs", words: adverbs }, + { name: "Adjectives", words: adjectives } + ]; + console.log(words); + res.render("client/build", { words, token }); + } catch (error) { + res.end(error.stack); + } +}); + +app.post("/build", (req, res) => { let { token, template, words } = req.body; words = words.split(" "); const url = `http://localhost:3000/api/v1/sentences?access_token=${token}`; diff --git a/views/client/index.handlebars b/views/client/index.handlebars index ed2e8cc..5f4230e 100644 --- a/views/client/index.handlebars +++ b/views/client/index.handlebars @@ -1,21 +1,12 @@
-
Build Sentence
+
Enter Your Token
- -
+
-
- - -
-
- - -
diff --git a/views/layouts/application.handlebars b/views/layouts/application.handlebars index ab4bc1d..eaf7383 100644 --- a/views/layouts/application.handlebars +++ b/views/layouts/application.handlebars @@ -9,7 +9,6 @@ - From 5c47f06c92e749d09833ae971b757f4ece5e4c59 Mon Sep 17 00:00:00 2001 From: Will Timpson Date: Fri, 18 Aug 2017 20:08:33 -0400 Subject: [PATCH 25/29] fully clientified --- client.js | 56 ++++++++++++++++------------------- views/client/build.handlebars | 38 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 31 deletions(-) create mode 100644 views/client/build.handlebars diff --git a/client.js b/client.js index bf3795d..dd4718c 100644 --- a/client.js +++ b/client.js @@ -19,51 +19,45 @@ app.set("view engine", "handlebars"); // Logging const morgan = require("morgan"); const morganToolkit = require("morgan-toolkit")(morgan); +const Promise = require("bluebird"); app.use(morganToolkit()); -// Route Handlers +const urlFor = (type, token) => { + const apiUrl = "http://localhost:3000/api/v1/"; + return `${apiUrl}${type}?access_token=${token}`; +}; +// Route Handlers app.get("/", (req, res) => { res.render("client/index"); }); -app.post("/prepare", async (req, res) => { +app.post("/prepare", (req, res) => { const token = req.body.token; - try { - const promises = [ - rp.get(`http://localhost:3000/api/v1/nouns?access_token=${token}`), - rp.get(`http://localhost:3000/api/v1/verbs?access_token=${token}`), - rp.get(`http://localhost:3000/api/v1/adverbs?access_token=${token}`), - rp.get(`http://localhost:3000/api/v1/adjectives?access_token=${token}`) - ]; - let [nouns, verbs, adverbs, adjectives] = await Promise.all(promises); - [nouns, verbs, adverbs, adjectives] = [ - nouns, - verbs, - adverbs, - adjectives - ].map(type => { - JSON.parse(type); - }); - const words = [ - { name: "Nouns", words: nouns }, - { name: "Verbs", words: verbs }, - { name: "Adverbs", words: adverbs }, - { name: "Adjectives", words: adjectives } - ]; - console.log(words); - res.render("client/build", { words, token }); - } catch (error) { - res.end(error.stack); - } + const promises = [ + rp.get(urlFor("nouns", token)), + rp.get(urlFor("verbs", token)), + rp.get(urlFor("adverbs", token)), + rp.get(urlFor("adjectives", token)) + ]; + Promise.all(promises) + .spread((nouns, verbs, adverbs, adjectives) => { + const words = [ + { name: "Nouns", words: JSON.parse(nouns) }, + { name: "Verbs", words: JSON.parse(verbs) }, + { name: "Adverbs", words: JSON.parse(adverbs) }, + { name: "Adjectives", words: JSON.parse(adjectives) } + ]; + res.render("client/build", { words, token }); + }) + .catch(e => res.end(e.stack)); }); app.post("/build", (req, res) => { let { token, template, words } = req.body; words = words.split(" "); - const url = `http://localhost:3000/api/v1/sentences?access_token=${token}`; const formData = { form: { words, template } }; - request.post(url, formData, (err, r, body) => { + request.post(urlFor("sentences", token), formData, (err, r, body) => { const sentence = JSON.parse(body).sentence; res.render("client/display", { sentence }); }); diff --git a/views/client/build.handlebars b/views/client/build.handlebars new file mode 100644 index 0000000..f6e44de --- /dev/null +++ b/views/client/build.handlebars @@ -0,0 +1,38 @@ +{{#each words as |type|}} +
+
+
+ {{type.name}} +
+
+
    + {{#each type.words as |word|}} +
  • {{word}}
  • + {{/each}} +
+
+
+
+{{/each}} + +
+
+
Build Sentence
+
+ +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
From 358be3b8f0b8aa08f88c35e9ff8ca9cc617861b3 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Mon, 12 Feb 2018 16:52:29 -0500 Subject: [PATCH 26/29] update readme --- README.md | 15 ++++++++++++--- madLibReadme1.gif | Bin 0 -> 53322 bytes 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 madLibReadme1.gif diff --git a/README.md b/README.md index 313ed5d..8c3a9a9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,13 @@ -# assignment_mad_lib_api -Serving up the madness with a Mad Lib API! +# Mad Lib API +A RESTful API that supplies different parts of speech for fun fill-in-the-blank word games. -Ed and Will +![Alt text](/madLibReadme1.gif?raw=true "Demo") + +This API enables users to register with name, unique e-mail address, and password to receive an API token that can be used to request lists of random nouns, verbs, adjectives and adverbs. + +Download and install dependencies, then type 'npm start' to open the application. Type 'npm test' to run tests. + +#Key Technologies +Promises, Sessions, Cookies, Logging, Authentication with Passport, Testing with Jasmine, MongoDB, Mongoose ORM, Express, Node.js + +by Will Timpson and Ed Triplett diff --git a/madLibReadme1.gif b/madLibReadme1.gif new file mode 100644 index 0000000000000000000000000000000000000000..24fab3b63a3a54018df9939210f5925e82f377bc GIT binary patch literal 53322 zcmbTd1yGyY+pnGAPSD~6cP&tyV8tm=916juKyh~qPH?BV7I$bV?oixmp~Z@{cyUhl z-skxtZ1$zllnJal%8G3md zdwP2ENks5TCJU*@3#g=t8pQ}{=Lj2>NLarY^k~*LOjfZBS9_PL?vi0-6`^aLW$YYo z;GQApS}x<+tYcMdzmf16xE}gKCYWNs*^u!9yef}J?5S} z;!rf{P&DsWIOS2bU|sh!SxO{QO*lnWB*#cB$5Oo1RwCS3FYT>fxcA!#Z?80O>nuO7 z3NO8gfcCs#)2K+VOqhLkq<3zLM@hJ8YlLhYOs6c`vMkcOG}Wvs#kVQOtUlJYJ5+{l^GSyInAwQnUTdg=`Gn|RcT!n zg)xiW{E*4CEV(w36XEv*%;ZEaCyBT+T8F)b?@)uXvh zgXwKcsok4pHAAH>!^NF*ZFK{mD*Kw-Mk_vl|J>J_+<#CtH2I-#?ZfO!^TLu z&kl|)tj&*&EKJQW&Wx_Fua7P7^{t=Gt?ti#J6YUW9NGD^ws*92c)fOVuzGR7KQ*>L zKen^FaI!LTvO0bAedT(8@Z#sv!|CMh`Qjd899`^Q?eCr6ogM9+{yM$6+Ix6-Kz#Wz zo&XP+REqTlyn5#k%r>oM6hfOl~QBLXbS7=D&x_{ z((w#li?Lj#rn1Q#VYh?z(Wde*?;&AW)XL2jGsTK2>~F@JE9c5Jik0(}TdEeS^_%QB z##*YE>Y)8G)GDnXR+?<*s@{yZ)~vNT{}{_tX{+7n@;*P<7;mfl_Bj|Cn?|+0eycx< zh{I%}z2V0&oIxdDwWD!oJoEY6uM-_jdtVA})w)s<#-=SLW*XXZN?@FHt zx;bvnc4@~bspe#9Kb25)q+-o;7R6F94soVnElh>5M}Ti%=;3xS+q2T*~ z!ty=d!r!^P(1-8>%`Jd5gj%9EqNA{~1Z)3GVXV;Mho*Q^?cs#H@KYe~;{?ShYdE2$ zTHT|6mrC;^#vJl z&i$P?BIUgqG=$f1t`DQW=sNJFZDVPJ8IeB0m< zCU@(ATotR*fq$LVglAJUgWcuW@4k~gq&pOVHL*(02323EnGVt851ZqM()jaBhw@jr z__F;0al@=>Sb08WI_c-n%o}#a7RV5hpL~&gw%+RgAnDr_!koUAKj;7FX_9jO8)3OS zeh9YvB?SD;GM8t2v3I>K!Rex@AUv1u{mP>%Wuw%^uVm-7=Xc^Kji@S)-egMwDeoam z700gxYOlHOj+(7jTtmu~VREBofn6eXq3AVV_&g3DJ;tdX)@`x{erp|~SA!i+a_Ig% zKU^z*Qr<>yn~jr`?BK7cJ0BmA;XB?h4_b{(hJTl2`-!roEc`=6%7kND|L*Q{UkknZ zBl>MRW1ove0ERLlz9P4CGWTm>-*j)bYML7hwN<-Ec--Yb5LQ$ps1jDD%ZwY=8z z7}MnZHs{lsAWPuGp%W6vayF>7zD!jg>P$jebJ;P5{p-7|g+?KU+JIwgbB?u`r|H(#jGH6Lf~4q|nXkaVGk?gP zyoSGHtgmkybhHu{OGHC~0Du?}V0{=m$l-*}hYSL{4T)gp6zUku!?9W61^CM|0Mt*= zUD*22kNaG(HLsn#UmU^%9n4S|4q%j3Aa*v45{hRS!AO#3$R1?<7-RYnfahk#mD98C zN8TZn*^_nsUXqXJ5jZxN;7#4sFmwN&--1YRfQCOEV0A~0vX}^9}^KbO)mHc>azBPoM)a5=O7JsuR2> z$Y!p7P>*dw04FMzcVkd+uwp~1tiL4LD}$#Y`IpN`N5b=5ZU%f=EikfKMHq2TPqz%& z2<^Nv9le52mtpWZWWN_6ioG!9^-|TEJl;b3u7masD?j-258C?95KfdW8luRkT`E<(S5B$-8i7MGAU69S+DlaPrF z5v*aIMj@B|NJ8rvzN2^L&hX#%eG>7B)JZt&su%yr^G@WAd4f^)xHz{^hU3(`U+H;G zi+kdx*wboa7X`(o`vvlyGvh_>lR^PM&3#0zO7Sl1G&UOa>O}SW#x8699uA@SUCYDj zSB*Iv)rQaBtx_jnwpf%Nk9c%N?sS9N=K|P0gRVCyQ@pyb0)DICbiF~vzb*wyu0~|I zf5&#c9;7Ti{q6bD3>>68rjlQq@dvi^{CCQvRQ7q;slSFizHg6p{zd6$%!7c=+xfYY zOYw+Lhva^@D|hl&qJy7~nT+l>en&K&ZK7#?F2wV3DE>;~Gb|)(>96X#{^xU06m4yQ zBg-}&;H%?^IIlBGm;-RIAUp60^E=+dM`xjVBtR1Tx(g2s$^dX-R(w`OX&V&oyY9;) zWimwzxW^9HZiI`xn8r2L031Rwi9tX@4c3KV2V`fiGcU(#QhOI69Fk1{0f@uH8JU?G zz_p3%1_BZUzq617;_(6v&|Y_z2eI@9O?YZQ@&<5U186#>O8uLZ(V`Q`jU#u)0z)8LQ|OV{uzJ>OeF*g=h}_r z-o>XPeM1BnFtY`nKcVCNW&!My+RQ>Q4?V-r6|kQO0Vo>G=L)Eu>48KAP)Iv0K>ykO zUeJ|ZRH86w7L2xpX7#%~#%USbiR?|#%P<~ibNR)X-!B09%jT|RQ6eA=;huNwgRpW8 z!h4p;>dc5LW)Ni*H5z){Z#^VRfuL$3e26vD6~tkH4>yx6{xeyeHpISEUrDeebX@q^ zq&`T0KeBo^5@QqdV>|y069An7w8{$7-j5%#M#(9FYAh!1*hW3TQCU6Fu&hyEEho74 z2Kfqu20;Kkk)UgHd=}C-SP-g*`lR!b#L+8yEKDmB@;3xM(MQ2aIm^*b`)~>$N3%0H z2=kf7^-F=L(m25m0#T9I5*;aIrzvF^sg881M~bNqW~n@-sfy$Tsw-NrR#F|7{Iq=B zwGGm&3DP)9)AX}I##vgXD`{wShTrRPt$osMv(oK5(j8XPoo>>d$unFmD0_#(m)Nk;U&gC&A1?f^e_=nE)(9nh|I*5Ov_$_7;?Le6;PZ~ zR{Dy4)+i`vB{MHN3!fl6OeEXuD$7G5%OWJ(xGWpXEZh8NwisHD$!NCbA5bf~NtX|( zaV4!e%j}a#dOJC3uN~+D?5{A0J94epKk7Y*NjiLEPOqOw0F6z(%tblP(HF@pU|_7eM0 z;N>2^>q(KaC`e7YSW>=3KAS`#MmzunQ~~38fD0etrADizr-CJ=2Bn64MMJ?QC}x-o zA}NnOW%T=H7Y1e4x1M`S*{|)?l6;gA`x|X#HCGbghxzgkm*sPedH`@mm0U&TlU2oR zctvz)g;#dAyP+DKqOxM3qHL#P#=SDrUO8LTFc(WL|F*J(xT-*~>Wg+&8M|@?dwv>L z#UlXFY*<}`rJUGN-lC)3;9Jp@T~Sk2opoUl*zb5`EYT7k7QJXVpN|XqI(rnjayy5wkbI{R!dY} z{^(gnZd9&OSVw`{D9F}GCf|5$+ejMGSYFXcJlLqY-$;nsbj#8Nl5d)RvT4GLXzD6& z!WnF8+H1l>ZLVZ##*l9=uxUn%Xr}h_Qug!dX~P|EAs8F>ABX9yaropi1(HbPl0ex} zP6K*aL53=UG)5$q`UG=(aMYymORpeZqgLJ11XSRAdT-=yI1grf@DAY{^l70svUr(& zeA1{8ijff0%@8naC?jh-b5y7ZW}CHGgn}Zde?R!B#|)qB&1-o0=3dyWe3bmla3D1N zQ$@J?a`=2(q5@`z7F`=LIdHQ#zGo!jaWf)oKV_j|F~wpFAP4|Px(nn5~LWQeiFdWi#3HBcdnQC zXCw^MS@NMh=HyR25DZ6QOS~p)eV+-(UV+n#_r22WTe3!70$8$<1A0IyR7zCmP$x67 zNQN6$rcsfl^yIve=_ofws~+Gc{-?x^~$_>lVDkaJv% z>jz@T`wEX-Vz2c|pEzPa)v5r0;-HUJA@|-YMk8WoHCs=eBXR2^3HKv#s?lW5j2|&0 zGSVLiIEaAXefpy1H=p;1*3oTU}ry|^jXq+)^-Y#(=A z6KLUu29OAVl?tDH@*;bt%}{U=9ykb-XrQiuLqmD7c%A~Li5$MneVo0ypF~T_Cw`7k07dl= zE-~GN6X(r})Xd>c&Yh3}k7congAxDdcuPlEh#dw20QuH=o-A)(`O}Q5S_i>F`_(2Y z{WX#;2>7vQ!MSwSw8qdZ-pJ`;QJj3KMR3U-*T|F0$UAT8S<-my!cw54k>7E-iNvz= z@p!(^a@3opnE3MSm1VJq<cXVk5|`=c&&N>$hW-tz(xTlPWtB&U-Z~Ke-$a<5iBS zUE3elh)%z@%6(&g_U-IA^*Zlc9JRkvizx9aF5Vpe(2o24B!R)(PF+QUs{-heb39t$ zCPi=1PaE*CKEcQ)<}Gh50r!?PTSs$y2=#Rcop31685#i?Kv<8FsD%+#zm+29K~uYQ zsAB$G7VD|EqpW&hKny?$dZT+1zex5Y$?BWf%Ym6Rlk;aEX9Pc5+v1bLk*sFJwW0X9 zyxU*yTA$ah;S$)RgD}qwF~1~5bJ-;%^3UQIgy|;iP+kK7y7XYGz7t*V)jkQtQ`mX?)V*h*-WteFpokZUw$kyQEIJW{Za)QZ2sC)v z?V4O2ZXlj)bf2u6ooJVUVvG<3-8imWQM1U*lQ#hB>*b z4UeP7q^kv$$Bp@*&$(5bzYc=q^+P}H9GmF>mi%$bt$+S&>+)CbVfVp})70J9{fB(L zKipfu#Q8m^Ujs@jSkm|OigNFf;E8;94Tu545Q`r{z;-}nRkI;tC-{+h%cV)`zEhJOdf?a{?isZEA{!) zuRVNUT&4#gDi;3lkKd8EF0e+va}WW;G-ic0=zigRu_{78rvkPG1Eo!7>GzRbG%jW| zW_g_3R-nk!;00M{JoAF!18Em}#mnJMQ#n&ACJCUqHpfXEwd2zj0qBn#50JxDh5!9G zdoKuL@PHE2sFS7`62J}KGfQY8u|4YQ=Ghzw zC_IhH6ptrJ3yjg2HlSWOP{NIPg=Oa$8wfb_<5}c|U?CrfTL;`>wQtc^+;=1b?Xy*4 zdA>1J;ZoaMZnA+4uy7oo;18x0KsXsNn~WQUeFn*8o;03n5stJ6cuWihehS8Ao5nZM z2)YUe>S&y!`c8vsxeqWwLB`kTHgz&QL4nqc#){LXQas4Cz76J_ecYjo^tpYlNt@52 z7a8l_^k=t~QrUEBEmvlDR5JOjrb-OvcGYs3+j@yJ=Js9{NJro?8P4x(mMEohTdiK$ ze8~_qFsJO-Kw2ZAPTpDCp`;fRR$}A2Kv1~Q zn5|%#S`d|D9ub$QS70^!Dkj;KonuIH`On+y(;c_`N&YMU`W8>LKx2j{=|tcrsSJ=V@mgp?+R(&5NkvtpxFbw~&}Y-j%4)D*#w5|B_XYwDTK6-N5|Rp?%rzJ>*U5QOq%n){ zDS(H2@j9`W=rlOi4}3&!uH!nR+9`^HUT-|tJeJ;gsK`8P_B-+UW8(iNXQ|Np z{PK@!FmfbrSpcfgHIxbas@yc3((~FZ%E)|~HiDt`+I)dHpUfgb>?(6Tn&Z*iG8s18 zZkhU8mwXhi=XqoGUOm#sI{PhNja81@;tE}s*VT=*aW14MfME<==F_bww08TY4MhpqRn;GH3Vw^xFD_ioc! zI05c%5-bny^Dg`n9*cWMpFEZ=q<(s=yp@XgTyvA6_S*1!w&ArIvM1sF9Y)dZy*(%? z>9hORu-j)pFHq9=Krg%7_b8xS((hN>-lJdUCv+*_)8QBIx6i&PNCsT4wPgnU34loj z-s0weeq8-M9Q5ROx%b}V>ETr{m~!(GNgsk@eh`egy^chF4Pg)gg?glHqHvA8$1(vq zlFvh*(6{=f@m+Dl=@{vPq4fApQL+)=>z}>7J&kuwh1>I&(0zn{hXJ-VU~NjcLP#2+ z6oC%m(u_q~%vUff!afjagpNlyB_2>v5Jg4#ow&wUJQJ)LgR&w6Kp~39W86jC_Ksp= z1pzT_V`=OZCr}(*YVZM(6ts#+(qBOeL99RG2qCD_38x{3%XEOA%;9IG1xSJ@Jt%tq zv<%c)B@8cJ)NnB%@~*-_G!!V{XgZ8p3NFijEsG5l4o1sfjsOVv(t1rZitRQ4fCa@V zqBsB|LYZ+xjpX!L|C0WyWfkZcV=~h8IFc(_oVi>N@uT++b65Mw+u%attC4Nu`=`_4 zH>y8iMH6N`%?6=9mS=pcI1pYqSuj&nKUuaMOlqWH1WJFDaavM-*{C>9Wj{`xb#2LE zLmrIT{GG0NVhh)GE76x13fLI21XMv_%D44^6mljW_=pOX^$_gi2bP}Q6;1frVLr-9 zBo@na70leiv`2U(nR~mul{*ARCQ}MT;>E}|a38YYaw19Lb2tQT89I`xBxCbO%!MWq7UZ^4=EHEoH6ti=q(<$|)|4&NxyRS>Z{4)wc4|k>jbp0M=X6F!G ze<%uzelNDQa~SnK6hlG3kEqN!g3H*<>>#6?=)6u;{N4-%(;r}9c8ON=HzzLFALOuh ziG6c#PByGR^t{X^-qGKJ;x2=QB7vv>HWts&Sb9X3*)=KN-;$B#$zW8)+BGHb-jY?p zVC;37Yg&!J6{n5CxX}x1!yjtt9)Rmc``6iXdxAx;st0=%J-TN6w7T4jb?}(o!eSMMZ z`yBADNm5N}mSdT|QOOD3ZtDXdi}Z#VU)XK3`RxXFSV0o?6EH~K@MRGJ$BY=;88b#J zNxrC`k)@2D8cyUYoUF%K9H|GC)FURDoY=+Bw;$N9N!DGTEH(S}(HZuFYStCj!@JAp zKO$tfjGt4-wimHARQ#GIHAU=xuh8gb`hm#3*r;J&=^OrRmij=c6t$FkhA3{a$>p^M z^&#WO+r>A<9(5^8Ot%2{2zf>bAmd+bCsEB4hRJiQ_!$#rtfb&myPq zhu;Q1cT!%x$o zTf4=;v;J=nJSk6q4yA(9HiGVMho5lno){kQr057Cw3ra2k|N{_d}P(0m*$Wzm!4gN zp2-vlh65Q^+(+yWkY6#FAG4_Nsd90Qg>a2&XP}f%!IZ<;y#)8q>iBw}z?6fpdr2lT zs&4R;l0?aKr8{G=7&>~($EB&8>1a3!X)Q&;I5J=8GilIlnO$dlVRyN36t*(Q#%S(lPP+43gc$24PSow=4eRYzM9f3+Lg1z zO->#T4Aq%Fgz;q<3bob|jIUh* zw{S>)70D=BF@`u6=I}CbVkCnT1MNf+2pokwfWtX4?PCu(6+webjGNx)Db}a zsL#ktDKMa6npsTjS?+|=1f6`)J|ihBn;Dn`X3Y2&r0_1dqDU3v^#m3gC|DPSZZ}dw zDP2TJ25WKWT**|fYNo2bKvyeZGMZQ19AiY$2vf^e%$Qe5wu`TZq z_!#o}Cv?X7B?R;NGx6TbZP6>7I>HMA zVw|a)kJ()}kVll(*+nBNS8RsOe4sGIVgFPs5<%RlhJ7L8Vp6#Ihh*R z5?DAjQ#cD>@Jgd`AEu88XK)xafAY;x%W9Tl&YWt`ka%dm3ezky)4WWbDJq7ia|p+Tjf7HkDSXx{15F-T}Js%bI3(PDPgVhPY!>rV8tn-QIU9+#goNlxK(ys&qFvC5F_5e;VDBn`#Mjy; zgBXVLA)=~1#)31^9Vo;jgZdH?fzWVc=S54d#T4gA2B?mXGlmUlkrf)j%G+$HgYGRH z>bMCc1|g-mf3{-ML>NO9`*NXhJ$AUBZbax?d7#CmE-?T}R}qQW8^;C$WQjrz!9{YF z*ENv`vPDHuXz1Vj={*s|>(SUErDQIG;piTpbkct;CGVrt!k6vjLp)fKqf60g;QBCD zJ(Gu;P&M7FyVtjVn0}M`wBDgPd9rX144(j;(5MBs3Shw{Oa*EX>TE!KqG)otd|hk+ z35X6{764@-KAoI z6@>l@62fu!QVciYTGK~0HX>Q?Rm+M;EQ}-K$&FKjVTfg5-$&Q9)nUuT(TD2L z6&T9Kt~`0~uk_@7ZaLPNT!Hy{f63Z}ePP6~sgDq33!7Z%-I~2OBgi$u z-YE$5j&LMFGKI+eDxE$%ZkUsQBc=8&N#xrf*Kep1rXm)mPYl|(^6xJ{nWB7n{cuTk zqgMAsG7X@q1Ll80Qg42|B-?(NK~;xF*+I!8py+{QjlkE~-B6qH&TZq_tPgzKOwUGQ}A@yhh!LyX6v6SJql$ErU zQ@4~iv3%)dsSs$Xm|&@tZ>d~ssnTt!I%TQ$)l&VJ<*P?a4LmDN8Y?YstJjiN+Uiz1 zCRVymR(gR}`UzGB`BsLtRz}@c##2^rzFL|5vNCvTU$t4TdG@InOIvp zS=$6!+a_4smjk~&yhl!1+lZ{uP zjdy~LPri+Bt&Lx|jsKL*J3NfPl}B-e=*4Lb3;_ZGAV9E=jt=5B5=wX?ChFhQf7y`# zKK=Cc^q;4>{?qh-do=!E(`0~pz$@Su;9owaJipKzev!9)Vu648l**Jb1--?D{!uEe^j~*cew0o1jL$Q%piKR=3wQrP@ zYlyXHsIGsCy==_g6;$eofzc{pe`t z`1+^0jjG|T>baxpxwGccAI%F#jSE-JtLGmVPCC~wKX3gRuY~nfW{kDwF4TstHHR&? zM6Gqjt#o7|yvecF($SBNE8PVMX>zT%dcLoHZMi1mvA;Ohh`Ea_{ezVmv zI5;>tJc2ML$H!-f=LW~-*Jj5@=cWhd=jZ3OxZFLv z|94S7;m_{b+1dH|?ZqwP>hb#G{QC0d@!{g}@e$!uKK*Y#W#kIY z%dU!d?|T!T{ku=eKC$myJeGR>xD|WT__t4)Zn-jDIt4#QnYvf{%co@X;lGF`rtgj; zvYd55_>?-;%Fp;eRh|5l0%*cq5kBS7=m)H}Y^JE+t9tcXO9-FRt8}Q-VFPKI(v$t` z=is^`wF*zCwKo4BXb7Kj9~vI9L-SRoL8l)u0A_`E=o0CGz&Hm7jV+ESixV;;x6>(sRqJ=|N4m|sQdBzMcgMR->VPQ*O=GFHs6ASw}$n_ zmMrJg#lCz*?~#%wVBIoB<0joyb(C-445ymap|k!i*A&fC?O|JFK zlbwOnPbQ1&LL%H3ELlXj!pPClyBQ8u;k01j$IThJ(I|}=X%iIQ9$}%SXe41ssKkJVNvS`Igh;3q;f?s*@idTdaTLDQ zeU2y4Q%t2)N7M-fRdwL}6%9n=;H@M6s3DvuKCmY^fBUGg94lUZt190g@4^9ii9xAQ z%q9@SR{Yr$yF|8+uvrDQ=ssl%2|4O~u=I{IWr(E#4Ki2+{O}zD&^>YeDfQNtcU)>< ztRBDZD=8j3y!5wD35ANlko-= ziYc3c$5O);GqwQ1fw!oi>hFn$CivHzoB(2(4ev&opX}LizHu%mh#i)hb9esOrbEdd z_@!h^e9`Ay+j_(c4u0_je(nyCe@}HZ@m#9m7jCuyfU6k8SZ1i(84V7oo<#$^{g~nT zMBPJ}%BjIRYMJ49v10q9D{d30KfTX+G_g^E1TAOi9rqchpO!Gj=^mdTLxJ=Ni%|x8 zD?T5a$3G~0wqbQxpLpSW)T}4qcib-P?)U2>^4i^R;bXk}6Lg|?;>vxr-~7(T_MA3f z_sa7;TyzP3W(=%qc}^R-?)CfO&nE2mr%UDU<d%C-$1w1SfMpnpDEGdG8Hu)f7r@z60Bf$vF5`mUo6O2uOg8s1=-@gJG zmnte9mF*G|W(%i6rhAF;q?sU~sDbH!Aj{^S6p|aSq0Gb?1?C51-*C#}h*rQvpX2~Y zst|f)ofC8lMhv9-jT0pi#t}Rg)?OdJFyyUknA8+}K=9-w`gP2Ah`=(In`J+R%EXF9 zxC;gWc!u`vNpEh?J|mw@3^FS*hM1g2gta0Zl)d?bwBe-!D;6&u#XwF6e=w?)J0QZS zxsQ}Xe?*jqkWS1!oJG74lhCqBDBX2B2hHz{nWekzZJD zl2oW#-EfN}EsV-#L>e>k;@S+G-Bx7|IT&mt_>Osc~sGW$W_453aAA zfK^!MtUJt|JIj7@&2IAD8qgBWYg1q6tIL!1Wf2c!{Ni(4k$*K$AbB4BR2*MjtDzI7 zN-j8=L)gYg@G_h_6dN}5n5xB{rv~60`H2--pqPa8gYa@%f6P86ziHtE_+y_OISL%v zemw+<6g;@Q9}EQALJ02Khv^9E@Z*`{D&$c!Hbi?d)g5Fg!!3SJIJhMG;clYDcgieX zZR^xq4{1gFA#GV}B$@~)lRDx;NSFDLC{L8|&de2%_Ye5Ggod=xZRMEQ38M8uQ+AUe zYAkZWIzbvFr1+vxbMiZ#;jhCL7q=NhS8~GaB|r3$6#I7Mx|Us@<`0dmL+qQ0k*Z!#!(T9Y46Tg$p;wJK1E9AMInX zD2WLu0}5)SjTBhskI`P{-;cojTArIyExyS7vtbo@*=5Qg()@WH)ma=>9EvnI$F?nA z9$Id*nFHmaSS5NN&Z26DBe=}G%kU-Qu&MjXF|)hxp4YRM?fYD0`@kq2Ukvhk3Q1yS z2DdI-oQ1<25v?3`J``2%7f_2r%z6lA#RAP8G#kOnfXsdAauO^Kvi9ev*E_0Ark>ai z;_Q!SBO8>O+LFP8>VS=>f-@w(Z_Bj1V7#^8!nP3Z$5Fj`qW9#CWI^AGagXXCcsJ5R zKspK(KP2xYv_xUzpAK=sX^2u#{Z43lvpc7KLp!wzsNSxZ4!Ng8ZY^jg;&H@g5j&<; zpcy1-5>^rpY?x{FyC4~EkY~gRr*0DutG?9l*TEj3wWuht*;tkrDH2@};vT6&zp*FS zOol%#GjP^s4lCXAAqNw0lfxh0Mib91zTF3Wh4=P5yc0tvuP%fft&b?!pW-A>ShdvD z9FWjtq0bl4C4Hm*P4~NhlnN>@GIlbmkK5=!MP z-)NH+VmM^Di5!FxeMU$MhIj5T4QPLeh! z&Vqn(X<;>Py|z@j5*Zz`-Q*XIac;eei6%3cXWvR;B|p`@+~;YypDc;%V8WAn_YIfYdy;_bqaPRRiiKX=t#d z+YB032*yw43F!e}5sA{dAS7H;D;9+VfxV-3QUW>pXbJx&X}YGTon z74)6Owd+9{^x%pt;_*eobC_lw*ue=xwF{NS#@~0xcy0N1PUsyGz~%W12{%q*IA(z$ zkI@{`B{)31`8kpXN+=QX-E_E$4VwfM6g$V7Km>p@hyWoXx@d;+jVOaUVL$*Xe37-e zUc{OAxl{p%t-iZvvgq}u3i1p-qYdclY1&24#><^0+EpRi*(TZv7VS_TZKp?fcM@Gd z7E>e~Q=%VJCLF^%0(viu|3Mi4^dzR9EVfZNwpl;6^*W}q0(93Ca|*!gzK-o7i|Z4P z8_?EI>Vt60cHByz)*AEsFWno_McFIN5;zL98URvjg-6je`l3H~gg_+fRH? zI8X}e%XO2?`7`+rtU|8AtrM}A#83&!NC#@2;7hQf8Zk=O^QHm`jfE5BH0p@H6fnf$FeF?brxl#ZOVNOz^nY5Eo{A=?A^#MFhPXeMd=inBvaOG zAui+bx@gW`&ALU$33Qj|=psE<^l=T17zhB;W!A0i(&9*CP=K(My%~jYk&t-+sMy${ zVGKejXlPggKpd|`4lE2zEG)EWrflnYnqb2BC9>G)D5&UJ)ur#_n`AB9I=eo8>i*o* z+t)uZI5a#mIyOErIrU|FW_E6VVR31BWp!7jX6Wq2yd^sEFC^}XK2i-ZCI--b zl#mXC8(F3oD2NgUDyY|2hMN8U8i&KJ9q0~O6s^o zIO%^V=&yIY2OtOjH?Z=99nU@Jc9jR?G=o;InUJ%%7Gh@H`YBrG1)i~kX< zWV{jk-`GloyCH(Db!~Sjuu>2*T2m6aE)rsmv%tj1LX}!YgfUZU337tRkpNZreyo z`$AIp7J{u5cFiEtsQ(?WRE+K*a;R1R;FbBKCIntt`wOph{WrX_+8({umG@E$s#-|L(EI~dlp?{e{t2pvu$lf9RApBFE2!#D?)FzumFo%z z5meR8R7hb*6?I1hRXKBH@{|{f=d1Lals{IrRO^Kse~x*eNQZXXEzg=a332B&dy#)p zqI@yUwb9@mAxTACP-n^(h$QDy;e0qyftqMeR@g9)DqYs{9Re`8=3kX^wA2`vqL`{r40@6$ zg__cqz-2&ZWY>vyI}hhDQ>t8~tml+kWWih$#-!2-L#i@JK|JZU-(}NBU&3R{0$AaE z4%bYS3DrMY$ED1#1^XrTP<#5xDU)rt^;u%t8GU%rK(tQocI{*d{PV>OZU>w_reZi4 zped>0a1mw`D;Zt#?6N@{3Z$&mhQja+I+pj@_3@E502O2NJ$v)Vp9|5F#PisZcA;0V z#A2>Wd-DI3G#I-AqkQq70Y5xl*_l2=fj~seZQ0X3Nw?+_UtaIClh^f;8WA=OF~3b~ zoahdBXqwi&aA=+v6>w}>R!=={rF=7f-1arFi>vP1fnl;^zpVOK=TYlnZRdB=QfkTB z_8)kX*SpofyYH_Ke}BFmQc3k(Tj$_yTU8=D>ir?fh}(}LhU3zYCn3()ifJ0xFhKD- z$7OgX5XZHNo~0Sj&|ZyUZzS0Dg9eyvfb%?!Gv%kNOoS~6PwHlI-E|OWXJXlb1zG@P zM<#iFF(b*L!!IM^W>F`0et(XQMgw(4Lo3pcZ&@^c5imc8PD1ZCp}qdR56|_scnIy} z!^YLxSrpgrNsYHBVx z@r!5$4fEj|LlGwxhPD}uz&P1Y13uFEM2r|{uWb9#(X{W+eUF-!@;eXWD7Uc>kZ)c` zPrQOKi=T%6>Yh8m|Cw?Rnd0~?JUyE+;(sahIx)cW)4TTpvZQ`e4u8JCQx3c_e3K}t zi_+Ef_yu?7Fz{{f_Z-oV-R|nA$NOTH+54;4#_cCswLz+zv_RrDz_1nsK*bv(X;h$p z5)vN-=n26v)<>z7m&P_Z38ApeLis$~iyg=tMj5AzsTL)T;pq@=VnBy2%_@VZ3y)wf zp&NlNr;(@w!cellVLv(A_F}gGfaUd|F(X|wL_?(Qp#4y>ZLlJ52qrR8 z0nrs!AyLb9qI&K)wRSE*GFw>Az5|&O)or*x@ETtzAt~%n-x3L`d0n=I3#aQ*IQg-6 zgOs|ny*^bI9Xaa|XLp_cs!Qw(Ir@yQ`+n|3foM;vbMYuJ14=0*&OCBxR5f{9PN!GedvCG-G9+vP&rrO#s;z0+?Y|yk+-9Z#Y=Ns^x%qyS4)?r`@ zEC_l`QzkiXumrHa&!4ovs(XG<@&>afu#XsEt&FWCLZQgR3W^Dux8vI*ND2dQ(^DH@ z$kKg>!yp4s;JHjUj(tWo=?SxFl8e%8fyNn6^8t368w^Dm`edmm5pwrFt(O6#J(MqR z@L84Y#6C0=rZ*3icKb)?64z8bM+PIifX~FMo%fJP`2p{7q zcP&Q|zGcl@1_~8^D$tA+R-ofj+N9$?kJgiwdgkr zEcz#^(aMc2d>%6-HA#9H{&5Kq+uH9P z0!e@X0i_9QC;|dXKsqYD7r{bTL+?oMV(7giz4zXGGxVV z8Ec$(kMo{0#>vPxzD{z@>-WFroZbDRQ$X7~!F^9*=p|l8vq*s+72A`V!&@yqLUtR1K`LW=0PQz z0bA-XT!g0Xok-%hi6$QwdF73VO}!B*lh(7;we)8!R^GKzE8J#P@y#zCHE%HOl}#K@ zhrOchd}{)Ks58k-Go~x|O^H0hTn@T0?iI+Napj?z((wGmlRo}bfpk-~)cHvx*nXmH zy2)Lx`6*#J=goQE=U&!tk#)uYTOy57)P1|e+mbfVFP7s)oix12@1 zgi82^#V5(87?P0b@xtWJX%Z4HK_Hrdz50#i8iuDp|FP zav_YiwC4EzY77dwx@wmqzqEcj!-I#{(PcWGm!qP>vFu7Xz>>V?MBeV}>+MHHzU5RE zcq#pu5aYlw_fzv#-;v^BOq3$mQNxp2nlY$AI(or$J3+4Y-h)bwIo(AO$IwV zn*0<(ewNsWYrtT98?HjMtMn3)1FNwQJz-qN^fYpkvt#cbl11yGdxRGD(tRK@+HMHf z58JNYVvmq?Zw?fHRx@GW8InmblhXqoa~R(Xu_agZl5SwLSx{S86Z2hM?%hWaHmosT z|GR_%jV6{3=IPgq3%-FB*%#8%^kqVm(wZAF1wvRf4O!=OnFr`>svzA?NQ9=JP%xwc z@5TS#5W{9f5C{_pH2FqriloHW%tEIn9#uB_t8RMy#Cu&>CRxYAB-o5)OpO&>jO3e8 zhJmQ>Lj2w68@D6^O$X>kW(jz6Au;Dn?rEB6tp_B_njMb05ZMR8-cdwMnM5RNXVO|^ z2f6zdxd#lm&k4DQBm{@j1>2tW2gl9c4lTMy&Lke^hVqqjOKH0P*e&GgT8IxU)Q&AQ zTOl;lJoIT`sAWN@Szl=JW@tP-%!VzjS|QBcG^{Z=%%vc#bs!8j?z%w~KC@=o{Xjpl zNwGE1Fh=`+NEJey;A)&3v(gI1hK5+ZlX7#N@(W7F=xTC%EI9TEDciN-up>RgA+IhI z{#>ST!4V8lA{XauG$%uZ&oR7lVWOX)o#a=V@mJMarg(?~;OYd!F;@s?;kozOJ@{Cl z1Vm>Nh;Imi#7+V({JrKXwXJVzYp6S9U(j)eM4Gh(Rb%XYSria@phhB=hZcA^$qtDo z4M4@1)LLbtQG_Py$7f0EYc8=6G+{wMxu!f>FC{op4yR*g+GoN-=t+5Xf;MG<>1eV= zbpbkR;6ks)rD@ulcNCp=1m2xW1qJ{FO-zLbTw+Ou`J5i)v#HYSOlYH~=m6gQ7=bmm zw^cAzH2aV03*-F2`(@ZuUZ?Rf+AK;k7ei8;AKGB*M6v$9Dm4(C!=ZPVQKX8PMeC9? z6~Loz_cU8fhZ&H?vPZ=vMf6^UF=;-P)P!I)^Qi!fdL+v%t@4=Q1~1VWCP{3?#$lOO z0(&v9S(}Pg*FzR{vKs_5pm5{x>j(}R{t-4xZ2IbIcyfY1LSZ+jMyim9+!Li!Cu%r% z;{gWC%R*|c`=~rf*j|_1k<;_8aDn8M&lN^o8y(lemwW|81$E%HE4qYz#4 z>tYA_ZbNxeC8%|ED?!EL+CZzsk2X)Ti+i0ZQjaLuYxx*16e}tfNRSkJ=$5#L=H(RS z6{et6he`w~QUAnYnLsO`2^9Yi2*}wV9QJ9n^$f&e#ugdoB^B16I_$V#IoZ5*tJv|{ zIm2h!DR5ti8pv}Pv=+@AD{@jQ*R7l@a~a<9c(GjNrgA;?)n=W?sBP=p-6k)!%Eg_- zHt$zEKhJPJzJC4zfkD9`p<&?>kx|hxv2pRw6B3h>Q&Q8?GcvQXb8_?Y3kr*hOG?Yi zD=MpKkz~kP0}`rwERvWEQrGaXjSP6v(uL^h#SaV(_v<1DfvH!W+7mCQ`errfr>B>6 z-YzeWXstqq&y&tgLP$OmA_%^2Z4SOb5Dvo-B(DpQR%k2^q;WLd2#zXR2o!dHmlVJJ zh=vG3#i5Q}#zOVSX=!GT*bP{ch~S9x!dQ(t-w4uP2`g~A_ZMm;|3#NJ0Ck8nVD#TY zFn=dt{={D%2=eO*34jS0cQK)dih@=;!X7HZ{}z7%voAXTjK3H^&~m!3{p=5}0YQzS z=7VP-r7_Y2Q!m!WwomK~pV-@a*&DgrKLHWVU)h)BUu5QYoZRq1u@H$oKef1DeK||kD~sk71N%T|46Zb5f(7Nl52Xa!b#lU zSSiv(ImJrH$Ky$q;}ejp{K>3j1lU&v+^G-I&ke9I4z#Zh!GbxJnt#cujAXm~N~jdO zfTGP)Ecd<2WvOc-LsdT=x42+MgcSe66i2giM zzCB&HJ>9ai*t)yacJQvXr>Eynf@ElT988e(Prn`LYv0GW_9nJY=05$H`1U76a{LBNZcLBvtjr&O>O9#VIN4eFl_2>#eX_Ukhbgao zI{di3xBlfPm>}6YK0W#J{^V%&>}2a#g5>+rx6`j*f1HBxk+a|N5wMK@fA9fAFrNPm z!LSBf{Jm6+>Gx8xaC7@#OU16ZAQfwj2V>aom9zyrfJ?=Q^}5nT?y-%ga@wpc(cfi% zm3iSQ!9`IGj`3Xb2C8D%q8Va1O5((&m2p>9wnpY)qO?+g#hV!4 zaE+plC*RkAL2d-v;JRE9&#)XI@koK-+or6!KOr$c-+qnkx*s9(iPOH4K)x>_g_E@o zQ)eM9^EnZxy%vJR%D0c|E)b%rg3IAV5LomIRV$$$x-F6rgC?kq1w=h;LN1a#@SULC zAW^VqhUkeuW5a)N=IjJCeDpT&uI7z$&WU>vpGg_?s{=I6PVfOo4!|MUak0jkrRsk2 zIcs=$&S--)kF}Pjn$7g85xQ+9j<(%(*EgNb9-jq%V*YqGTc=MVR4OU)km{eLlMQU`L2L zKH*XsW}Jo~{acMfvh{2Aya=&x69_%ersVN!h}SjBlC7AMS*b^=B=_M6@lDjcb;jhZc5W?tA0+Ym0R+KgJI95swL*_rqu+i?dHwU)^dr_q4A)+ z+|ZV>$}Bp_lQvd**(a2T1$mX+S-XpsZ6qJ4%J9(dtI?okP21^$i)=D=-i-VjMPs*Ygcf+#MM^_6g1+$TcI6^z z_tnd*0Yo&1W5eT$UndAB+=R*YInml#ogDY|UNiY~^Z3^iNsl32cpjkB&F5)8YbYaV ztR;pPY~q;t$0?pm4NAD2JqTg*_<9zxz+!fCQF!)zP}!HE0NSUA(yTm>G-e|m3Efx2 z@2H97Cp^A%Z#_k**L{QN#7cNG;}vPuN*)Dk3uRMY=eMP^Csc!PPY<=kyS8L-fC3tF@|_xx zBlIEB&pK%-e1Tw#_D0r)LYW98Nexk+AN>3g@(Nv4DbfCs!riCLcyZqKy^1?aPvf=n^xXtbWb4u3oDnd5haT57&z5(rYZ_knxVtFG;LkJAs6V^6SX-OE8 zSx3LSblE{JE<7eelQB=4Bsungi144C4Ih|qRRNTMAP59PN=iygOUuZ}$jr>l$;o-` zniwA+pV-ZtOhS4$_@!?M-s2P0;S)CI6M4cX>dYtRDIjxCNWnx<>4~6{v#6${B>#=O z0{mDZfxD6%R_gpRGBQd^O84*I*V57fRas9@?}6qoN!EPi0;;m9zMQS594N_89%(u~ zlm~^`!RoQ0k&%^^l?m3%(%upL{MrI~E1dUsjG#)OfNHpaT7rmnpongwh;F*LRl2x! zuCir>vQ?~_eT24krmjP*lw*;OQ!%L4@;((Rku920Eml5xwo&yiA^DyutwzuB<|&<^ zRD<&T=OHBYmT;J=NQAa%td3|LRxHix7ML&%vsVHYI?CE2%UiX?Qz9zJGAhU^4Tnt+ zwkpL*RQaei1WVP0>NiF|%=W>8@zU~m&+1TYd%RIcvQ1mIcjxm*UCEE|iH2RtMqMek zJ^5~+cn{{g4HkL6s(R)J?(7T?kBNzS9-o?#k>QtC8IaSMlvj|D)ev6NprEfh&euJeEuc{)48#0EQGQiyE?2F{y z>fD~D;^C&^(az@C&XU(7rSD%=f^pH++1AyCUt$ktMMr*TMe#!m1N{?2qf_|lg+F3H zKiWG#27ZUY-01Y&;=;m0&(iA9>$QouYrnMr&GO5YHBkO1SJ#$S*Qd6B$^ZP<)3-lB z;eWOErf==-^7_iy+WV!StDSo*(hw!DLgiK>zcP5>m-(8ACn!A26(G>N#>jH1#g|-MRKW z0)70#h0;N6?@Jz0C-!o(S3D|B-$eN)YuA|eCpeqz%QqF#7Sb%ucMiU4c|V}NAMaWz zM14_)|0jbR2HQDVM&cfJ*H+T386kCvLyeYqC;{%0`dsyf*{k$^joMx^%l8RI@UHw# zVS(Q1b9$`_2)&%o^VE0V)9CzsxAmIXQ|y?`OK5MUHj3p=xtII!`pY{~5Zd-5VWrG6 zd2!gji^`ip0|gli!`zHUyIK7Q5F{`33CMw`iovW}CVhhIOvef8qSvy*rJESX*l z=gpUW5Nt)uzDU8%WgjRJ$2~!U@KmYRS~pcy%vlY&Wi(Adaga6 z9iw7N(0BQeAO0xts%acH@`EuPST+7Nfu?vwfE|&YcjDAjO3H0W9&}(jNpE#@r(~oBiiM##CbMa0GD1T^wdMoDy2D z)TgKnK+*gba8DGyEagzcJS#2ISc>&3oX(z%Cy|>NNq`T3PYQ_BuznW4MkmEetsK9Y z$A51MwrPfydAx#G{*@gnaj}TjgD# zBy|Xjv7L)Xa=}&X5HxTMYJ`CZF~%k7xox2Vxfh7+hmamZ`XHit*4N`EkcN_Va8}+C z0p79$Mtnc44%O2oEEVO4fJZzssv}58HTOXhgp2uN+c{#pATYBtRaunm z%$@)va@hgC2(?+Vhj*Z>m_eq5mS@6o!#Z z-7|s%ZkOR1XUvF~X01ay0AvHkHbanG3J!8FyHK_#G~jH6G@QYh1P8&5_zn>{JwA6p zmNa*nP`#m@E!)Qu4iMBYOSD>r`#z)E?AjnSYv*HKCcT)u#m*_g&7;P7Q-z1`9;msX;DXu;3ah#RzpT`4H${xJw6v^}ii(oFqN1Xd zqMn9^hL*P8BVBb+ULTrT8X6i}TU$FgI=Z{N3#i458KjFD=00{zGVsg*1rbz3Py~%r zdO_jKw7M1W)H*fLvMugTQ_Q2>NDolIK;`O8G6ZF7u*loT$LEh!Wu&D=WE3Q(7ggsc zHk2iTs#IJIev4~sYeBb}(>V_+%V=c;s30qIEudb2Pv1l1Z$Whc-@K=1=RsW<{4ENv z*Vb1SdRAB8fIsq|K79g>-2b6p^8c(0@H3p|zjG7K3xG&$fa)!X4w1B`f=VKW90uG%tpeY9v*3ON_srzu48_H7BettR?b48wp3?YCNk-BS$msAqQscV;< zAK`|1DKw4mJPwhPVdGFdWD0x5Ed^u5;SnU{NV?i8r{YdU--~w`uMzL5G9_>^ND2Go ztwM4B0LvQ!b>YD-&$Sz>W)Sx7y>?!O8^7vN_WWpJe!_Ak$(3!1f93 zof32N|CAy~K>n0s6Ml5GA{?wkuyp|m#Sr-Us}2_z7vC@be_wn5xibIrU*S7Asb6&) za7&OD;Pw^18R*AG0+A);mtwvg?aN6_C(D0Pl7$JJ+9rj|iqr8?F^ONLXN6Iq)5y<* z{+19*b`BsyVy?h^*_(OW1Y`hlaR5pr#UXW_0Jv~b7Qx>p(DJf}5c||29#XIWips4{Jjjrx5N2B@3 z5HvOSbnQS>V}4i0R!0^zH5R(}Ku3escQ!UQ{`G_G-~bjm?@eC;r2qltaN!5xqWbZ- zO=EAHCMlZuDVhW;Tlq`aq-$A+=-T}9GWt$&GVVElyo{-PvSL8_-N5okVKtAV8=v5k zEdmRTqG}9cTO6ay)#6^=i67I+T>9l}@_&&>oBA(~wd*eJM?N=&{O*ec-xmqd6OA+x zi!;5M;dDFG?bj5Yua!LLapFDn0!)npyiEeUZDXIA118Bd3~~deVS);sv|!4m%#}L2nYi|q0#7k%02C@YTq^8mMwl}$LLnY03To#A>t*4Sia4 z6TNg7qP{JcHE!pS&zEFgM#MyLdx_VVa?7~cBvIFaK9Y{b$#6B<{4{9Q=4m*FfO@Cy zM}e4elLaYwf@HD_!DgMyp6R_kZ^mm&vZtF8%=Yn3N`s<^FSX|FBQ-|b6+Vmnk8KzN zk|pC7i^3F#j}fV|j3t}JMKGPqewdY+(HqeAGtOwSG3^^x20zqs0 zlCArbx`tKR`I-jkAnq>%tcliN2004Tz8Ele3%=Gc?B)JC!ar;MbyRqx?8ZyuWNIo6 zaiVMA#$;%1zKtuem)A|)UOX6|RF}WDKXqTrW`A1S?CIW&zU$WhEH>!c!5laocQ9{W zSbnfz-MDqIXcxqgs^K(i^Zm8kM)~(QUK?i*?U$_0996IT)7l=s3udo4TnQJdI23q5 zaLMg@v9ZCAwIs8OAM0tZ+dnqa3S*U5V{>1>6nOraH{iTR-S*L^s{ZTXRf1Vt&rf$% z>}6?+^}o7)FiMS6rqQ~oLByWUT6z3sL}=&u>zF*x(=TcF_jn`Cd;l0}I6!>+&}!Lq_efiHxK z=$`q6Py%?08zcT)RYkHKEqjJ`J=qvhEC8p&1E}a;{u#87*rRbWVgN6OP$#x9hYFG9pvGA!Omx7dz%c|F9U3uIWx#kIi*r>sA`IHaQNOF6iG) zAb}1Ev^Bg4WWfXgG>$+;njEH(Pd-059(hEXAs8=*q3a}oB5&k39Ye^zqb7xWoC1He z6<#I)aOXyrFT%%Uq^+QUy_W=4_n3rgVv9!M(U)ANUIYFSROp2crY!9>0?LMk>B^OR z`TR({SS%$DG8dT5f+pk@#LP1CO;=nRl{zm`B5i1gg$s;8u}{zuJQ6sX>-$m$SKPHP zu)PPS-1_+e40+6%wmdAz&L1Mf)YoEoQ0(^l9Rm^%RkqF{hc!vuxGNg*kS+_J$N`lw zdG}x@a5{(&A!$=49qfAi?jj2+7%^AngD8rt$T_xoktHf^G4d`ZiqMz!Jr4T777v^k zmX>I?e3&+&e(s9!Ckb=K;oS}+fT#-3$)ECQe?6?7KOV*x-5JgxM?lGslqTCw?l!)^ z0v)7NK)%sfcJ*qaR%k2pyUeJM`O*p8PV#q3$MqPGp$V&>%DcW0^R-~aq4+*$hid8b zAXxS&uVe6pRM|bM1Lz_qoqk~@w4V{H{eVnlV$5`rG+=cX4UDk@{Mj>{-K5DSF$)>Kf1$YRFGZm^GfRtv0AQI9u4n!1v@w4wuWQ= zwpH2V0h=~dRxukkK_Ma_Uj+C{x4K7B!B3^?wZ9A5{Mrr+*GYdXPPUkRHSj|DDr=K;myy50Z)hPV3!MTEUi9H*4t+uK0uP z9rH%P_ST?i&93Pi*xrIp@o#dU{$I)cf57&CJmnv94>FH`h4-M#{F{IL8@B&f>-mS= z{~{u}`M!%>G}2{lC)szu)EmruF}Z>cK`8w8A~F*FYpQy0rNR)q|WK zY+Cg<5B?{By>0J5AIJZ9E^cWa z3vws^$BSDkK~KdFuhBMGzpeRC7q@Iu$@D6UiGmEexoZDh+}b`QTg3Re!v9>{N_;>m z{^#NrnWJLg?~7Y}0<`DJeqY?;jlI+)`s?D>!$uk*v0oRrF21TrZ*AOpd(9OJ_O`-` z@A|;r_BN%+*Nw4UQm5*$mH>A+*7pWVNx{JX+)Jq})bjDh zyC4*iHp+uVzw=SZN|j{R)vFboqogcpyCY$|FA=%nQz&zpAT-$9niB45qWIg9GI*I#mfX+iJLCLRRyXt4<9OS_$4`}b7}sBq<7}m1Z`(oq zQgBR;>#xUgQ>$xHqaF+4e1-m8e?5+agAC0>WwYh^qiGW%G~Z^dcG~|j$WZ!(y|C%$ z_1tBS8&7DoWgxv>NTJWW-6(nPy`J+tj6^g#=HnrJOXOagf`#TW0o2LihQE4S@S&Q! z@$;8q4l_g9zC!EwdpT$;A6d4(Z=b)ul7Ro|ZJoxFvDvbtLM$nc=X2@&M*r$<%lBvW zwQNQ>G`MXXC;iuvvQrO#_qNZ-0>D=H;+OfmwP0`S+DhiSgiAS#5PXA!SJb~z`Fc|L z)|s@TX~jRv<}f3e=eqlHg!oId)i|>^2OLjmZGWUESzQ;Meg2I6Xz7{NEwU?NNM_<2 z_s(*ZmtWxcz^+7vOqs_m(e@}U+elRz4!eoPLC&4`8@(Wak91`qAw-iyo?~kRsM;Mn zzVA4|o(a%X`t$8`!nna9yez^0&S2Q{QOaBBK=+jv8gSgO}q> z!PIM);Q@NAKQh#Zgq2?k3PX4CmIVe8F-kCYb3j;cq5}-(AVj7dm(C?fif*}Yd)o{> zp5DaaWp;BcT?ZgHj16$4H<^*JV1Remu1%_^6S$X^1wE6%k=X+{0BVkh1L^>bj(`ld zEJ^$+s7t6g+Hz8zkT7b)pSTajU^(_~stx61xY`M1BrVXoU)9(%p1oYhtB5>7N&}wJ7k_Flwg1$m(j9}5T=|? zpS$GS@x}M!&>@5ib&uEU{(om6HRfO|vHuJ5(lUxY}Edp9~cz{}>XTgCNDoVA=Fpn+1ml>*3?;TQ}+e(B4 zFem^;12k&u>7FdG;3K!G09+?hZrA4u0iM%RkK0ImReRjo8FquNki}5KulJ%?+!L2RTd!#wj%8_bzDfu?fG7?%Ak7lf}spYn3akc3ex0(i2D;zu%_}`Jc zHZn2j3}WS8f7G3A;mf7x%v{X$y~u4qOaYJO%-*_6!qM7bxrgQb#;x+%(WG;y1Ktko~r4(=;>i~4QwqH z%?*EDJ$-7d1jZ3P?3A9_>zf#xSld|`Ie54^S-87`+qgmm)dG2C5`;8UgtYT+nZ(~T zExu_{bHk&F*K>&X>5RU#+i~;vLK|FyVoZVxOham|PsvP~iC zg~7(TA&w=9o;AV7;Bv0oc(eLM=awwb?l`sXBy;fO=|HY|UyjpMq1jBa^IV;4bW{|$ zjVmCvBqX&dAvGri zuePGGzP_lju^}YAJD{*9qH4jGNmqL5j{a@{dOniCXvyn9St+;!kd~&X7Xt8Z_v1nkebZWbJ{-AbvrD5_z zP1V71Aww|!usC%1Q~X`!=ZcBpx2 zqIIn+ZgVhiWw7@1M8U_I=5O<5`>(-^rY%E#Lqmg8o#O+8zh_9NN2g~e2Igkx2In{W zU+>N=tbrFz7dID%mbd#ij)wP67T#sP5Qa*J}@&DJ0rXt0tp8uCGn*JL*eA(9gtG`mtRBLb9{}*=H9d>s4PDk6n z#2V+`ySdg>g7Dil*1RV1X*5Ij|Vxu0QxXAQ^ILO1b zF6?xjn@!m!N6#LScXGUyFAcC-(S1kp^&q4mfPDjB9eMA3&!_lK0^;{qiEJQzRCL|u zv4wA>t0pLRTCLUCy+IMrs3G*yL%2)1q| ze^|}^!4h#JO+5uC$dxB4li)tqCgqzx){LUqZt=MLbMkY_=mOJz-eu8N#1UCZwZiEZ*N@(xXzBgtv!GAj`OmdD34GN=JD9*vEWZfLRUXSw9+m1E=kjX3@9VgA)aU#Tj5m@zd^NTq;47^Fyv2$4} zR(oK{>;;L3*&|FS(a+!q6{fvXNMnQn;sq{1<;t!rO(xM^? z`>i1~sC*&0LY!Zozv^5TzwcJc+>2t$E_)7hHjmaX`QUk_beaL&yDr&9p?9v7mOF7i ztxW?H7LgXi9mX&ED8nMf`NqJD-8Eh|sa>>HPd1a|a=;JXuW3UTZT{x%;RxLB69iHnGJ*bY3C^NRYGB>DuP?I!TW-2s1|Iz~- z6eTOy<>FeHNI9r0QZ2XC8eW)bX(-QoT5fHoytr_-aL~{+T5fApy7=bkpb<}2VHc#l z6usB*yT3{tURoEOtppz!oeGt~{_1;6N5plP#^GOht9AKGnM=3wn=dKf+cpJu+$M+L z9JG9I-)-OWc(45SXyJRup~+{jufuOokG{VEu7C1|sw@*Q9O8E_Vti>wmSJK|%_QDc z;0CUDB#nYyG%u?Hg+|`>8#Z;L$-%8xH)kp04tqG%szb|-S7wghGpqzx3wLQm(nfKW zf0^qOzWptNfmZXSz+66jmYtgw-91B#j;70OZ&w5+cvJ-b-~gi4m`*3KND~ zqW5lZ9#aSjy8|!H9Y_Tq`bqn{$5E3w$4ypm81v6VQ@8n&x*1LRj=Pt(c)`1$U2F3Me14nie zueiQ_Wo9_uRM;u6)krlZY|A2&#KOCY(19e=ZG$j3{|qAajWALi@?g#he)E*VReyy@ zRj?VT+O2{iaL-*UAh!g!AmkERgLBB_OxL|gx{ddjkl28LGG3e`q4(}XwU!lHp2z;A ziNlbzqr&0sAXQReT&2S&z4(;E-nAK}Is;s~>22ovwtK5PRBVt42hcR(C^1Plb)J#2 z;J+CI)+`kK6lr|2+G^j^68KI7Aw`61i$ao@#4eV_yTWz4{7qvWR%nZ=W! z`nQS>^;#!j_W#z|?8!S9k{>MR;-Xzw#HVnVwaqC4o}B%h%f`j3p{a_d^XF0Kpmn=gB-~6 z!w3QA9n9#3{RqaOa_pLe63_THjqhOsR5b%Qk^*!$wK;l8V+zhn?P2B=r zrvuEJ0$z@SOFL**`ZD(_|8K(FVs*+iHVT1_C3BbbiWfK5#LSB{0>Ka|x)oRqJy|Z3?wj z2<+SsF*32A3Nq7U55TRN{kTW*1RGX17sfUhl1vn~je*l(AdKqP@un6@1r}-R7PTCf zd2%j=36`ZZmX$PCwVGCq30AEM)~5A7XLw}D200hrLS~maAey8f?b9(A2_8O8+7J0Q zW{f~Vq}`ks`<&mb8!rp`sL9!0fJt^LaxLp5^uaa z89TtRduO{T#t#;ixF0f)fynJ*=4MHOIr4K__5&Nv^o{lxXHC%T4zFj)+55>~^*dZY zbA@8q9r?{1@6hpy%o^)SSbo}oJ%IZ$CdUu0$0QJi?JpC%0sifF&fNvh<8#in2p2)e%&h+!#H@VOT2b*ypxEjiil38n==fLNEPJX zR(DqobZ2OEf3V@c1$YpkJsw+l6bU7i&U#eRdYU(5aO|-8F=G`J2~yqJ7VqNxBU#=h zF}By_J@(0AG=+`}5yd&jcD&ciLWgm7U^3BfCg>>%2e72&z(44|%~r_{z!X^ zRUQ^H<}0UT^8lVQl5ChmuR{X@@WeuN-T(*gk`@j=;6Cn_irmDtp8a_GR3za{ z-3Myt;}_xx9+$~Rk&tl!AEHev(G>1zK(LBpa6au=#PAid&iK5skR8Q=ur=p~t%@)p z;Ff-(?IAYjCq8E^b?73yX)B-P|7p-)Em#ww7{#lQrD_UwMS1_6&tw}iI3s@a*e!?K z!}sb!4(38`;(iXFE;uEs&F_&b+LHV1dG5`lTnSyR+ZXa=bn~nP^5jzTltd*JTJqG6 z@-P#5>Z18docWXcWN&scw-Pk>Q6w!G%~K%6oSG~iAg@l?2z% zv7Y8b*At6L&d}sKp$Gz$pX~z*u>^!8IIh#8Wxt@?A_!^IWQv?2cEA9)mdS7!2(=wa z{2ZJX16pj00%jhi%eTDrOqjX;>%R}5>=DLUwTBIMMr*0A|*$| z4A|5C#+=9OTyNnm=kd^2%Pw+u_<58N4jYCqJi1uoM{5x+-4qA4BT$ut;pA~#I+|lB$cI7FuXMQF4R40h^zB5` zxM*ThoH>QLjlIgQxA|OwgjZa`s zD$LVmU7vww@4^W1q+%Zs@yl@*eYH$SwO+baAt@y#^>H_}9NlN^eh@#wSH)2=5ZqK} zxxfx9l&|jQ)Lg&-JUQ@ZnoJoNkPfNh&cnuEdyET~J*i{U6hg>&?Z`>-N*LOwHvq@& z#uP(*60r3?PmP(zlY^%A{xEIN65Ui?M|Qy3)s+B) zB>S$Gs0av5*v&K|!|^Sai-2{QruQe0%Z?&_Cd00qBw2ZX- zNin%we;-qQ4Nq_d@2qiPVXID*tH*NZOayLd+U!W|1~p>}K|#kSVG&#j)9dXqdM#f( zTZ;CRD&U@-V|HctE$dQSK3GIGx3-#1w`LIgATe!3D$V_+X^B^XB)*5BCm;_Q^Tx;r4Ik4z;PXpr8uIM5NF}PeafDwhn zXyJSDq|A)nZ$H7pNZrcnx;{5Hon^u9+nHUsn`Tg!_SULfJFL4fAmif?U#iXSEe71J z(gtC@9vd;U5Ly$#WwK#(>e(4~Ah>K`Y@OU1o%zio^RPH`STUApH{XQTLLk;87e~>H z25M3LtV<@g5T)%0@>(`tJEquG&mzPtZQSg3{5)LXS+!H2)6z>pu=NV|S{v^* zMnV=$+&)JBGzh!|LPZutd25{JX(08}@e5>u^zGx9wsL@zab~jIi?=4&+TE@^)!|_? zzA;mP`9dzUu62wiPd=L~%f?&MFjEMq-ner}4!kE9ddT!eV)EQ7g^lRsCv{eZTfA>k zBw{vKPNSy`O{N5;rlf9#sGpGEy(M{D^~UuRilzsL z&9HA_ufC2)D!0tcM7ga|s4N;QN8^aRl8N-0WP@j^Cty0e>Dm$$ytBsKt`x8jqxMza z!Bl z@=mB+*KEGllp=a&&a^QSDN6u=YN^;8` z_L&L}-nZmiCnwA5uni%I5uI8A>m}1Gj0#UWii=CpahVmzMhoebqhy)Me8MIV>iUz^Yrx2mt7cP*6>u#*X3D4 zpCcF5$6GR&Cf4iCHm(6r(_4IJ?O=F|qqfCaPCy*+X5J|FW8`!EXdJjO)X)L=Hf9PW zlE?pil6v29?V%Y)3*dcsu=l!!=I*AqGTiY_{Km2i98G*+PWn~*Q_U&GDjACtdew;n z%&dR9X~twbW^$d@PoU6NguyoOEzCf3g}kbXxAnEAyz_?j!l^{72kDU^%2Ea6@o~|P z-OEo4@Q%9tBlV0T5S_Ko4bqSb!C6n{MJ<+77Ga54C zS&6=mXQzx)RcwGHDGeoI@Q;nal^4&x5k_~;?LE%9aOvy=6K?nIEm)Z-xwU+h50FHF zsDsXrzwvz(La5T&XbmBYhoTcu>hIMdaiEk;@sdDys^Ay_L_qW#;XLlaseTXR7^yhXBdP1mqOqb^N zj0VCTSA+HEzpS-s!0TFPv4B{(=+!>^5{FWNf;$w$%>2r+^_j*FmE4(Per{@ufIup~ zN&<+MCYRL5DROFr=aF;doM`c!XZNzN!Hxv(3tA#+2$2XmGZ$uGq;Oybr89!%TjIcj zw!jZe`Q?#=g6#SS7Wz3$rqacU!iJlOHb*)I;FjEVpS7md>kb=n+WMbl z87hjXC~n5V!ruo*RuMnB*{Za-u!pnoWQA~VQ(>1(5hk?4J)}}!;B_#0Bq!f$k zvTJVL9QasJc7C3M#fVLkf&!AHAg?xe8p4uC{iGBk{iw*0@b+1lXlQQ5p=Ef9dI{rU zHcRz_A}ly^L3<@P8IvN1!>%M{=Sm|SB$UHL-w~+_uz!&!tU9&exR~FOrB+fj5&xh> zM%2ctR^mb4-euup-SHm$ct6ux6GNadU; zd?mU9iGFA#cGJu6Q~g}1Xn5WIRbu~g^D`eK(wJidiROO$Nz91wjcCjVPdqgxSD3=briR zKX-2ZQ#GM#x-5$-v8rqDZ>{})&tBjAj_E>_Qa8vdyGyO}oM=$))!eVdl>jH({_+(Q z=%p4bekuto24@;4AGA?-EWmvdi8ov7T*B8}nD>zLt(t8TnLF#U=PJVLLTR@DK?xqL zBGcjC_dS32wvSJKyLSYwig~|)68}Fjx$5wQaRa0IBA6XoB(jL8*ku1u7 zdt`GFMV+v!FYCE~>_uC4NrU3fkGm2VFYa!#Q=p?t-l2zf6Kbv_gPcMsvG^Od;g-AW zvwTd&E;R!W+%EQBKl$GI@#)E(!=2~;9!I~wDA%9tX`yl0tRN(TC>Y0tz@eTM4&y!w zuB07KSz?7t)Po{&(8kcOf{CWm`?82jco=3jYl0hra#Us7k<)a`gckd9H2vC9g2dKD z&Vlj_>)O$htA&qUXXTN2Ix+HWHsq4upUSjX9CYH% zR&6da;wWB=(n+voW2NjeRuU@HNp#U)rk&eY66@DVBJ_RF@HtRfYQ4eCZ^oACXkQs+ z7?m6bIzN*HsdNxiq{iFXv9499C@blvr7y6saR;etIHbkgpRwbV{xx*bnpc|e-H z$w!;upkGYC=A9@H=|6y^oqT+y2XKRR=<9kV5##Bw_v@zb^UV5 zHD|5c2h&4bR7Iejo=#%0ehG&`rMmqsz5E0H3MGRoz0z9-wZR584hGd`YqyM^9T?O{ z8PwRayO@jy8#b32)VkQan7utPZ0R?s^C@+)_#AB1v2O4vWXb&#)fD?rKdE zV%*DN*l_k*__y)Npps!@da0}Z)ew^r2g9b^HCIRJ-zF1LhRwz7Zq7GCOlQgrA6MJE zxmf%*o$WV#(p2i^<`!bMux|LYea+3|_HVN#JfoH#c6YDD5c5?Iqt;=2cc1*<=9@}J zZPTUhezhSMpB#+Z=hxf=p8dAijxy?a&+ZX48e+LyX7uc%y+_E~-b1X_}qLe=QU%Oe=9#|sfD4hou~0vW^wy+Lj(2p)g2L~=C^KM z8pu~WhSg$L09vy=61OHnMfCZ2m9tg6=_DX3W=1&*rLnStmOBLYynYd5(SxKaL_@l+ zb{d98n7?E`2^_j{$2+6keC>*3(6Gg^ch-RUx?p+GsM{T%ybbdW$&H}#+s8fy_!gV; zoWYZccYI4YEwJ@#)Iu=wIr9`g3{oq&!Fi?1OYA&dBw=gw5>rvnV9=kq85s{evBRUnu- zMOQ88Qn~sTnJyF-88!pYVEVvJQlC?YJM2UMPAudWv1RfTf__hdn>sQ24hcgh4v-Gq zGf(ze-G}uQ;e3Kfs_VlQ%rq%LWFJedJAxPwR~)~qnPAx4u}Lsv+b6!++t+htv=CJLQXA8FKFj&E;R=#{qNM3{!x!vj8u{Bc`J_ zRiaxW#@BF&--Rl^uYXL`%NKw1YJn#OBHSfIRSsc;7tu|b2FGJXSvR2K;CPr7dMj6c z!c%|o5_)k@~BcA82D8)*W@%^M0|d*@a*JZJp=~125|qm{=nPzAMV-c z{~+i9);w{Va`2?JtlAvd}QPU(dxeREkNN* zhPoMLM#Mphp2kblk>Dx_Q{?hX8Ju`eW`5)J;cUh$I@^pCM66Qmv8aee!PuSlL)Mb_PB%m4EodpL&j+`ojyHKXuL;-z;Pc zpaW>m)(^ON1cej@L~ja61cF0ye=Qzm6=87koaR%Sx`GpL;Gi40V1#uZ3hS1D{(1_b zO}3bHn}`B~1gu%^2)Jy(f&Tg^hx&NO?hM_j(>z9S<+!}9a}AHoIIqg5XR(T40!c*0 z!1?+qpZW?q*@27a@$mbmg&%G6d*J$+>nz-xuLF$;PK$Ts#d%Q4=)13j%RjajM%LEe z9q<0(F}Lykc5L;}bB_;jxKnQKE`O zF>#T?*Apbz#pLb*nW>Sum*WK>kc)C6ia>EzBpz=(6ATsesG+em9G{95LVQ(`jsQw> zt*-uANj4lp%|-~h#t*?^79V}qFoC@gE`?92h$K(xSnQ0(zk*>v$Y1)9G7b$4cL-Uif(k7TM(|k6!xWjOv_H)vkcz2yq1WcPm|db0FUcXKxavPd zAZdQe5m4}VgT?|fp>~($N>WEC1RZvft*Dq})yUx4mdWA);#|c`%)%vtld7^e%0<4* z7Do`!DL>{Fc%xr#{7`bd%3}D5+x)pv0S|Zuc~-$CNeWQ^*~|Z@jr{27=={RN+(3)Y zD=wc5tL)xcPQkJqVu4>KDmW$VA3LjJA75h(2_$`#@Zgbu?yo!9mVlB8%mUJvhDK=T zU!@2KKm|mrBhN8Fz&f(Sqz7P9&GJTX z4y_IJeGngTURLL=_^+CZEp7q3@zcjo|Ei|Ep5;I4XyLSu zCZD97ml9Y+i*H`^E_@wZ+#Xu_`DYQ$oHyYwKTeD2_}2&c$MB__g)~Hat|BGyydeaDi6|NdzNrE089{0bfPq@CoAJa)@^k&}4}q zsFc_#D`go)Qw3<{S-+OaLdYVyh@lBfn1MpYD`a%Ab)qu2z9a;WF%5YGA{R=4A6ltR zRcv#arA7{H!j0g$ZC0Ff33KM2`WsjUHbGyR)1;frmw7-V8394bQ@1u@bx|d0Nxkc$ z$||BN+LHQ;68bk}%;jawZ%FFvNSo_`ty*71$W&C=f$yq=n6Q_GNT3ARt%VI$z&{e^ zDk5h3QckL({_3KB#-eVwq=RjxPkXt(yoNbS-9qK20m{TcMa4-|!Ab8%pq8SSfl82( zN{I1IZ#5k+1N|V-Yt{6ox1N=StdBa%?UtN%kd~ddu62k(sK2IPu&#FEP2*^Dvm{H4 z^qUsx){aS*x1x1j;!RyLZu-U=-O8|X&$jl zSmd6mWWJeHtd-<_d+`i6sRy=Fd2Z50=2FGB(k1RPm3Goq9x~Md*OPq}!a{T&y31w< zDi_|CFT0~sb4R5y7=^)T#3dVq-#5P>tC=3Bm!6`PlWGuKa5J;eJh#~JVWnkcsawSL)Q_rBG9<(YL) z?=9@8`&f_NP~WZLQMdka&z_fF!&9Ck^WM|Fwo|>%uZLV_d+px#JIxI{&X2mhn(~-= z<1#C-`|3s{w_Z&Pe?!r-m09+LncP>d$4TcffWqDUnpe4Y9d zQS{XxipqACBGjO3)*7~By?Dn`J zDwp3=0me3S=~B-Dd17j)xvu^dD6yxt5EN#u8F#&2Fdp!%;l`4yTNFYiKf-hOaZU{w$q8so%2;6l@`OHo2XjBorRfst@wNGR_#%Hu+w zMCkPa&k!*vfvSU(Ux|RIlMe|lrjyn&&X>hxkp{;+Er|)L|J}Kw_#jpjb`QDskpq|S zi6+cD#;Xfpj~N=H;pFMsN<%43EQ^|FZ1q#h8B1dE+_K8oOtw^P?R4J3Q=+mz`O2Zh z6>tN`sJW=vH3SrnAYp*WC+a8wFUylzr?Osc=AxYf-=@w-ZKQ%unH3U69GgXeOQh)R zWtHNH;=>~L#96Td3W*vSSLooV@F%M|zhUG~U*xDV(s}6db(T<8(SF$@7vd!&2868t78W)Kzd)Oz_n@B(^`Fyee#QOma2Tv-n>32n!RuB&V>N>CGH z`}I6CxzrZWug}-Z2#wi>>x5+FM+3fkV?de0s9L#B z1sfW($-CnL^P9&b#X^6#64_DSXt5l)qQ-I-ej5KcGnOz_l^>cZ?p1 zBKjmw8p6}#1ftj&*C-FJ5bng;gy9lz5;)NHbcpOeK>kW#eBfzCF*ALcXn{bB8VQA* z#M+>^3b70yE4qVga5}Qc3;pdSF#rr!2FWNi=L=-o5ZagYUnP^xj~OdSh*4mtU=HR( zs_p6*Bwe$#2I4F57XA6Sk7Spl_1-*$Nez+`XtUg`w8h7*$Uwtn*kQlG17QpNWKuLX zP$Ctg96swVB8#p_1)V-1MHT{E8i10brXfy93`tEi&z(a+I3dR>sVppJXZ&t4Kk|Qxe0^d!76eFSu?4*=2^xoo!F_Oga;dk2D z{7DxZ;ctCO#>6Za3$-~*t(t07%?rcx(RnZ`td7vCHTjVpb9tE#Tty!PO#|Mz^3atS z#Z`ON>u4)pdqXh2EWi*sOQiF_lfQ_Lx^nn{Ri~Py~W*nmTXzjE32!4lx&>t)jc zIP26EXd1E%N+fRHz$I1Aa21iK=H*hOG~}zCc~(iGUxDOA8ih5TtZb79^wE*O;>S^I z?6HTGpmO^K352A_?b9b-_uvf^54k_i{V+=|JXZ*#Dk*-d0CC^tjf;?7)+K@#;0SYV zl%&!15z3(An0{V_K#f^wgrJ*zhva|&e1-8d`h`phTD647J*cJ!cX3mp44LQluz~?# zt$^S`_Id6F>XtdiQ}FLo;p#Yu;Vq6RhzKSGSUxsG@!Czk^R5|lEkR^RbZ=UB4nY_` z-}VTdvxdNCSZK&S0Y9_>{!QNk{R=eM5cVZbE@9-wG6X3|ZQ!M01!rTtSj94T-6t(gEpobu7N9TiK z>H~V>7~{!bmn3^3`2ze!&3SL!b+!2?U&+*PWn7`8-KO4hrog4bfm3Y=43n-le6hZF zp*g3x$Ki(p28TCz)~ASdmAo*=$K7_|3MmIbwS+&BX)}^VyOYRX;z*Jp?pIZ)90um+ zOtka9(0@fiI>pD%IIBV|>@G_dHv696)WqLb$tT0t^}VmV$-q^5LFx8o4~)WXN3h0Ff3zAC=3jWt}pw}|fO$J6eMW7|=Zp;w^K zNrWkmx8uwXS1elzZbWo{OY#6Gp8pCg-39X=r9cjTs)MgwxpFFl1tkQ96wVZ|=D7kE z*FDv~G8Vd^_SF*>Jk`Eu!Z+wl_?j6=gL?PwS*QT0ZsnYA$T{nSx)xNlx*_1-hE^6R zNATn128!1+RcgIcEk?C6&9F04t|4Eqrs!rf&tzr&kb6sy zV`r~(+pznKyc=B)w5Rel2TJv)OZ8?d4Q5Nu^kTi)P>0oOhh^`8Q{Sk^i&2lhanJ78 z-lK1Qru%GPkGRbB+t2qq&yU=ipKyIU=RP&-wJ_^Fz2v>P?zcAL{1sGzBlhp7UDjva zLH)P%+G}OO=kwyJ`1?NTu=mF8RQWCXtgfGGzK^S*=JP+-e0v`P_E$YY&6l75pt|_f z2MomJ%}tMZufB^%-3lIOXM2v@WlMr%LS$5kcN<-Tj>q+?_HNFCn_o+*x}Zsi zy{Vk{W)=GAdD#k7l0tUZyTB2diLi@MD!wk5En}E5%adg%7Pd$Yh$5p%bL52(mU{vO z?DP;%0?ymm6fBFl%oL0~Py&snxJw*Xj)1ya_0iCj!Ll>A_s|Mz3HzC8G&n7gSd}c_ zDOIkt6c`~{BIA!zHQ|H_-2@&H6mZdNDUyo~MXasJL~ZVpIYr_lryIxhAe1GJE5F%9 z4dBQ!=r89UDu9CCg8)5Ss2JZXJSu$=p3uluzE{AooDELOlQe-i1wnfoZ4RG;8A^@l ztlzqvO(HA#xR%5!K%ka<;+#d-@(S60E?> zmw8Y7uaK00keZ~vmIm)tJs|-u|Qlc#YA^2J;scfnAecZ-?yW z$8If8xq;p1)11fhtk>S6$JZtAuOIw(R{i!jPWP0ckNRIP;<`J_|3JfM0oI>3=0QKc zvsSWmzK_HkLfzC7+oerbSsk0b!twA~-Zj}!xl-WP4*X+qyL28|O6@`C;&xUIqrV=< zJ);{AltxrxcP7IpKiW$pp_h1>p-gvd(EhA2UcPJiv={Ft=o6E1(BrVN+$FGA35LRM z`w~bN3X1W=AvBzfP|`R(88Jb7VLU>f6iVEyI!gQkdVr7wK1m86MR%{Dh=^nxfiyLg z4pivmd@_<^;*ydggEH(FN)Wf@q;6QNuW;~Q;~iU8l@b*c64l<^fksA)U&0|UowMmv z{u{49y*p$e96h}|XOBiP0U>oMM>Sba4)E@P_eCpM&)dn#H_;@)S~AZ;Jj++%Ua>`I zl4{LU$H{!ng)x`KiCgc-9p1nC<2r*^c=vSI9No7x^DS;ovv5Vq&j`&lGGTAstom)&VDKs;>Jn7xD_(dCbhBikL&6$ z#@-@!3Q5gYMAmb(r;|h2^gMn)Bg3Jy@xOb81wwes^-dIl&ut5P1ybtU_+0n^l7kT! zb|KW(fCFjIiU+3&)w^b|^`8PTz zWAQgS_n)FUgHtpY1fn@3vHua81NDoRvnD8Ew4Am7eL4rCIq)s$zo2v8Xno%VLvW#- zGdllVYwr@f;9^@4KgXU4pTCp3b4(XzC6Qn)^)E2psSf%lQU?kmunRPWpd!%fAgl|! zXNJkKj7u?1i@lK@_fOQW$l#pXC6?O$2WppE?~vNyl=s9rztsh7B}L(?wHTG!c=dmV zcul#w?Rlphucp}S6yMcYw3nW;yZ|E;@n=~Axpm6 zK0Dy+4a@AZ zzC`s*+IL;6U;52CKiM04UF#BlFc(la*2McUEpaG;wl&LbxO0Z*js49!ON%E*Qf)rY zmQJ?*Zyg_l_Du|11_apOxE;Z3fjfb;p<*K@5Rp;WBAgaaU#dWSl8`tldRTC2V+pp3N}3l_9beVeqR$dBUtj-Wtu?(s~|2^zr=FVvsHkU-cQ z7@}A5!m+m2HOLag0Z^SOi6q>cV6}IoBt{jmF~PRc_DgKj+1wB*nsjlq%M>VfQK(7s z<=t8ufc~o$rL4a=Z7eB_M1zu)%KMd1U)l#NW-R|VX2gwh?;=Vp$$PY5SH4}2vvrQj zs}oEVP&n8$n4WGJT$%Sx0kGFldBOFmUf{kQ)_hl?V1V`l9MyfVF=rcAA_FB43*ST+ zmNM$XyK0&qDr^s^Q)++A3*XD5L@}7X@_7M(eomL9ObfQB1SVT%;4sK6(vTKHr40?^ z9sORZZd*d$mCrvZ7>2mk=Pe8HYc#G=@pR1K-WF;D*&S$jcmtSeiD*!7u|r_t6AtaQ zR7$=k3F`9RQ!bZyOK~8`uEexC-km|{wTQ%*As&K@GG?-5LY$mF(<18H^7+h8x>{5F zi0Zvp`fC&@@^Lr|ioeB)7LR{#znk&m3`;mxlA>Oj3>P}l1@}orhq?F{o^Bf&xpB zs$Ezr%-KE@nOE(!rNRt>-_e9|6HluFcM@6;SSb>u1KtMUBYXhC*Mh5zT}O1uFr^6r zEGsP>jR~~K!VV6&j^MVgX1p%`Ik&s;GLagGVW)WqvOqo#D_837O5fP&**iY0jwtdy zX;9qyP$-yp1ESk$W!X5J&W_8@Ckf3{$HhY?1{bgZq-d@C1qNig*lLE)-@EUk--{hE zqDUFxSf&|2{7hK1v96 zepO%}51n-|pXbo7K3_8Y$Jb;uRqO>Aioj#R4L_Q0i7L;D4XRcUxtD_CT!?H7&bX55 zqq^irXp~IISEpWtWDaY_%4bf<0TWpO^o1S--**TQN(sZuFiOVhWb+l!=_AonuHTXs z@H3NBhSjlhT9_(1=80*M_lFU|k!Z%g_)>g6r#LlBUG3Mha+y>@qSx@2JV+SWnmf>H z290U9?Ip^h?uv}AYWM`x@GVeNegA`FkOfL$w)Strpa3TC%i2XkcwuA(t&B#<~$T_W*O zOf0{$VAvf)WoVbr(A75v$re&JG&KdBMLg42G@Yy?$~{45SD-6K6HY<&lDP!jHfRLe zLUSuj9F)|GEiGNV?zbkZs0PPP(dsd#Ai(D!maoc`y82m8iTVo(vXJ$ixUw zckd@%ib}y)Wxl}o#LCv&xQ21)(qF_)OPMKNJ7*y|8A4E+f36k1Os?;P~@ z>@8Yuu_by4yjQ*7ia4`VfoOMRjTd7ZJ9z>d6IgA=yK26j##OVVUBQ~pS;r@*Czb>a zeT?LwnHOT6u*EB^85oRP=Ht(=R)0=M%lS^RUe`0a*+p?fO50b5vjBBz!f(j8Tv?Z6 zVW=)B@lhzzR3xxnNZ&#@pI7s-raJZ(;dt$N?E}T=ZLy zopg~ltG%ZonX09C`f_2v%;5e>ZdTr-ybX1-vF~N@=dFFbH#j7+G{{WnQ?!XhQW&Mj zaT#wekU6^Wxgu3_T3Xj+`womG^gU9Gq8Ui>P0d7x%9~7L<+$5j789k1l z5JG006KiXuBZGXP;gu4={W|4>Ai>Q~PPRDoELrYUZ>M}AA&E}a%Dns3XsV3psg?)v zg%@{WTO-!I&q@9LI3}PD(T^@gJ!oqAc0dutrNsR~?5DX(A7cI(*KSgAqlw(<$g zJCcy69qM}SeowjI@i{;3Fu|d`ji0y33)y#~;-w5ic3j#iOL!hF@Ffmk;4;m4OTXyY zw0cFkaX}JsUnNPG@Ul@&jzu$hi#+TeW;;6ZkGk>@)Hr_fGFt+pH_u&SL ztZgD%bGL~8Xj4V2Z7OqYw@mVAOFOx3rd)Hc%IxTa@p#*-r(=6{Ax9sr$l7NIG=DVa z9({7sYJc;3?8lS#qtBkn?F$>4KilSyz66f9zxz4%v-9`SS2S72628`cH~sN;j8@0W z#qs@q$>VP+$sMblTEAYH9e>Xn?^qWd|1}nJyhAw@wyCUjFqM0}TcP#rgVFfGtM=o) z`s8Py9JPMGnLqx~GXCsK!1(WXzu)Z@kaccHYaOo8pX?86b$-trKU|kQ`8AQ;xm&Jv z^ug@pV0OIo$J6nnFCiyj^6~SN{Q<4x@3|+3n_ABgUXLIDXg@jHPJVv4p>+b<3mor{ zKR^CCescU9v>Bp7L&eZAV>DhM8lHtFY(W#xqDlAB2%0bou`nv*unU1O9ZkCgs z08p7hKNK906N@l0ickWb0LmlO2O|tNBDCO>wrjIE5^1pDY<3D~BH)#D$2ch*dX={h1Pz4Ubb$jsc^n zbuON7T+iZ1x}bKDCE($|J=Jr*hX*&7u>YHacN-f2n9yu%|8qjKx9@yHb7b@%6PmMg zZ{`=?zFS;cUU|Q|w!X2s_2J{E&tJa&i`m&W+X*zJ$B&RrOn(3=ivdWO9#qc)Kz}TK z2)XZW7XPYB8ghsqK~TW1mbha0!1Wh7hgdeR#VdwrLC(>%d;bBvpfInbth`u_xD=?b&R40csxN9%dfdqAMV3B_QVfSEz}Ewjmg5BCO&sq3LG{Z=kCxS!6GWJn24);{;qh#H(RD5$zgHK#z4c)TL0;A1?v*dgW6@p4MgG!tN zV!-$lZ(p=(RD(9U91KCR0CP~{>dfO#qfneM*};)*Ej4A$4P~v3MeR-1 zo$b|)O&yJG9qmnx&!0VMZ)=Yz>rHAH%xLIIYZ}UZJeb`wTHG{P($ZVlHeB1@Tir2S z_-v}JqqnuE1x!+@=$?AqHT-yZsH1PB?ZsH*i$B6tz$BIKikPnI^#1zz;l`x?=JdYi z{NcwLBW)SOZ8;MixxFpbBhShQpVjm}Yv_H}b{eeG**^KaxUaXdxBK~v?xq)g&xVJe zj*fs)E7endO;ZEy(<6_kM%!m5p3Y2kVEg*8{UZZo*q#?-V*}W+7v00dBi$p@qhm9J z)8jpp(<9R}Q=`4pW1};Z-P6;f(=#*w7G1cz=HA6xaW$PUd?|x^H(>Z(mnrYbJ*lnn z+`+It?+-jJU+;H@?YNU)q4BMdyIUu6b-LAONv8NDYrVtKbFn6j*?{n{pO9I)5MfQY z8u0PL%P<}@3JrQ6*SUwNF4DYTkJhgS!G?0EHy;_8rDe|s0Vwd1?-G$q+>3|emsJM5 zX?HmthOYM}PO2h_mtH*g%M+KEjpqOa!@|S_J-5E5e0YvaL|#Xgvt1YFdh2jkx$p-G zW?JK?OHa^%7OKtY&2kOaME=G7;>cI*2u9eLRn<+-LsjJEF(~@wiQkRDy|vztB9wJ3 z+7gZ*J~F8A#6Q?3&fy$48Nq)c}KV=ZC;-$ouYAb6x*00`{n z@nZ!y0aS$4f2*=DIwI#{W{K~kHnEGWp z5?8CW>qi=Vy*N*g(lv=MnND?4rW;DUz-fW6YQl1lPkM|iP9ktnOiah}Dv*<-rfA51 zMpqB20jc+z1&n`C$npU^(F9`zGlSWA?__&$q%ZVp!VHP+;&1^#HVzn0v4-7rIy8&{ z0>uz0iW5~Fth4b2X6?&MY4yt$rT##l5Wv8yE%KXLt>k_SABSf`^`ULdU1(}gTzn6q zzH1dry+GJK*amdF8}_x46n6QOSUA_Q)nPu>(+LoXED{v-s!qUD5@>T9t$V}~3;jtj zi1BxPYvbM;*cAj~D+xyJqb57;EEhH!&HIjfqODH_iOT@w@uV;SNd9K%wIteFpr3gCQzCV)X0c6=t} z$zo^*fzYt5qKC3TA1jWPjVz!t`<{$W@^BdqSR6L(zOsn_o;=yTU7URVZA;L|8jfS9 zCJZ1>CBUPkV`Or~J6!emBSnXm-kX3CZ@Do>(Mg952dDu8$M9erb4K@lGGvFTydT{V zk=-4BEGJ484_LDKco8~!Kt`)mP>RCTtU?7G$U%f{SZn8%gI%0at6C6Z%0q?5;cO=sPHng)L5UvW z3tMr4)L5I0F0}+^iu=GegdB~P?@;MU&&g1O^Qj^u>LK)cSbQ1naEi_XTUwkb<#5+{ zkNl6M(cUjllJ)FZXxyjQ#K=qI1xmDx*340oZ--(Xeh}tBxraD<$-~TNzlI~Q-EBpR z69S%IbZb)Ob?+Qh{OpQoLuoND+0-V@k#UAw9W1ak<4k^F$L&)~uOv4l4A&`~UUC!% z&_%|zxy4w)1WK;2AW>JzolVOtiMahQW9kWTs{F)zNDd39GTx#2YghCyCCDmYTx)!Q zBdcM2ZLm44V2U=mzTmphHiqNdH>Sz=inM8%iJHk>rg*NZsNiT^|C*(E3*y-)YHZa< zK!VTrH@M7SB;dJzkIOy;=icw$)*#tlVu+@W=}6)ifQr{DM=9U{LS94!?83=7s@k$e zf%FO|!1!VZ{Z$_>S;WVOp)~ue$rqSHkM$ zsL8kr=%59$MpjE65DF}pJ_ORr9|dVnxt8#N-r|5;xTPot*9$wbc!FolbZtg~pDlw? zzOjcSFST0>486bEn22PZ*A{+~4kbdw^g&heiQ$x&Is>VgU+w3jG`eV6K1E6CP{1Wk zQCUDl5wCOo)S!YILOx--ZgM3)LP`jyio`v83Z$^sP6=&n;eyhhN!L5`R_~5vMe+%A zXbWR;0XzkDKB@Ic)DzhaSe~8#5EMmbj9Gnkgfmd|25{^^rif<_h(DK?Eg(tUY?~== zxiAzf=N;}GvtNA4(QB=gsAwzs5W}>`2d|P(OxI7(d0CDpTxoG5MfY{it6Iz0njL{# a5wjAn+rBhc{w?? Date: Mon, 12 Feb 2018 16:54:37 -0500 Subject: [PATCH 27/29] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c3a9a9..184fe9d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This API enables users to register with name, unique e-mail address, and passwor Download and install dependencies, then type 'npm start' to open the application. Type 'npm test' to run tests. -#Key Technologies +# Key Technologies Promises, Sessions, Cookies, Logging, Authentication with Passport, Testing with Jasmine, MongoDB, Mongoose ORM, Express, Node.js by Will Timpson and Ed Triplett From cdbfe622cbe8e0dc362d27aa39acc9f6d9413bab Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Tue, 13 Feb 2018 22:41:18 -0500 Subject: [PATCH 28/29] readme edit --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 184fe9d..78416e6 100644 --- a/README.md +++ b/README.md @@ -9,5 +9,3 @@ Download and install dependencies, then type 'npm start' to open the application # Key Technologies Promises, Sessions, Cookies, Logging, Authentication with Passport, Testing with Jasmine, MongoDB, Mongoose ORM, Express, Node.js - -by Will Timpson and Ed Triplett From 04775d89c487008eefc68eb5c0e5f389fb5d4e77 Mon Sep 17 00:00:00 2001 From: EdTriplett Date: Tue, 13 Feb 2018 23:43:51 -0500 Subject: [PATCH 29/29] readme edits --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78416e6..a1529d2 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ This API enables users to register with name, unique e-mail address, and passwor Download and install dependencies, then type 'npm start' to open the application. Type 'npm test' to run tests. # Key Technologies -Promises, Sessions, Cookies, Logging, Authentication with Passport, Testing with Jasmine, MongoDB, Mongoose ORM, Express, Node.js +Promises, Sessions, Cookies, Logging, Authentication with Passport, Testing with Jasmine, MongoDB database, Mongoose ORM, Express server, Node.js