} returns Promise if no callback passed
+ */
+Collection.prototype.findOneAndUpdate = function(filter, update, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ // Basic validation
+ if (filter == null || typeof filter !== 'object')
+ throw toError('filter parameter must be an object');
+ if (update == null || typeof update !== 'object')
+ throw toError('update parameter must be an object');
+
+ const err = checkForAtomicOperators(update);
+ if (err) {
+ if (typeof callback === 'function') return callback(err);
+ return this.s.promiseLibrary.reject(err);
+ }
+
+ return executeOperation(this.s.topology, findOneAndUpdate, [
+ this,
+ filter,
+ update,
+ options,
+ callback
+ ]);
+};
+
+/**
+ * Find and update a document.
+ * @method
+ * @param {object} query Query object to locate the object to modify.
+ * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
+ * @param {object} doc The fields/vals to be updated.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {boolean} [options.remove=false] Set to true to remove the object before returning.
+ * @param {boolean} [options.upsert=false] Perform an upsert operation.
+ * @param {boolean} [options.new=false] Set to true if you want to return the modified object rather than the original. Ignored for remove.
+ * @param {object} [options.projection] Object containing the field projection for the result returned from the operation.
+ * @param {object} [options.fields] **Deprecated** Use `options.projection` instead
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Array} [options.arrayFilters] optional list of array filters referenced in filtered positional operators
+ * @param {Collection~findAndModifyCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead
+ */
+Collection.prototype.findAndModify = deprecate(function(query, sort, doc, options, callback) {
+ const args = Array.prototype.slice.call(arguments, 1);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ sort = args.length ? args.shift() || [] : [];
+ doc = args.length ? args.shift() : null;
+ options = args.length ? args.shift() || {} : {};
+
+ // Clone options
+ options = Object.assign({}, options);
+ // Force read preference primary
+ options.readPreference = ReadPreference.PRIMARY;
+
+ return executeOperation(this.s.topology, findAndModify, [
+ this,
+ query,
+ sort,
+ doc,
+ options,
+ callback
+ ]);
+}, 'collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead.');
+
+/**
+ * Find and remove a document.
+ * @method
+ * @param {object} query Query object to locate the object to modify.
+ * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Collection~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated use findOneAndDelete instead
+ */
+Collection.prototype.findAndRemove = deprecate(function(query, sort, options, callback) {
+ const args = Array.prototype.slice.call(arguments, 1);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ sort = args.length ? args.shift() || [] : [];
+ options = args.length ? args.shift() || {} : {};
+
+ return executeOperation(this.s.topology, findAndRemove, [this, query, sort, options, callback]);
+}, 'collection.findAndRemove is deprecated. Use findOneAndDelete instead.');
+
+/**
+ * Execute an aggregation framework pipeline against the collection, needs MongoDB >= 2.2
+ * @method
+ * @param {object} [pipeline=[]] Array containing all the aggregation framework commands for the execution.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {object} [options.cursor] Return the query as cursor, on 2.6 > it returns as a real cursor on pre 2.6 it returns as an emulated cursor.
+ * @param {number} [options.cursor.batchSize] The batchSize for the cursor
+ * @param {boolean} [options.explain=false] Explain returns the aggregation execution plan (requires mongodb 2.6 >).
+ * @param {boolean} [options.allowDiskUse=false] allowDiskUse lets the server know if it can use disk to store temporary results for the aggregation (requires mongodb 2.6 >).
+ * @param {number} [options.maxTimeMS] maxTimeMS specifies a cumulative time limit in milliseconds for processing operations on the cursor. MongoDB interrupts the operation at the earliest following interrupt point.
+ * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
+ * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
+ * @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
+ * @param {string} [options.comment] Add a comment to an aggregation command
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Collection~aggregationCallback} callback The command result callback
+ * @return {(null|AggregationCursor)}
+ */
+Collection.prototype.aggregate = function(pipeline, options, callback) {
+ if (Array.isArray(pipeline)) {
+ // Set up callback if one is provided
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ // If we have no options or callback we are doing
+ // a cursor based aggregation
+ if (options == null && callback == null) {
+ options = {};
+ }
+ } else {
+ // Aggregation pipeline passed as arguments on the method
+ const args = Array.prototype.slice.call(arguments, 0);
+ // Get the callback
+ callback = args.pop();
+ // Get the possible options object
+ const opts = args[args.length - 1];
+ // If it contains any of the admissible options pop it of the args
+ options =
+ opts &&
+ (opts.readPreference ||
+ opts.explain ||
+ opts.cursor ||
+ opts.out ||
+ opts.maxTimeMS ||
+ opts.hint ||
+ opts.allowDiskUse)
+ ? args.pop()
+ : {};
+ // Left over arguments is the pipeline
+ pipeline = args;
+ }
+
+ // Ignore readConcern option
+ let ignoreReadConcern = false;
+
+ // Build the command
+ const command = { aggregate: this.s.name, pipeline: pipeline };
+
+ // If out was specified
+ if (typeof options.out === 'string') {
+ pipeline.push({ $out: options.out });
+ // Ignore read concern
+ ignoreReadConcern = true;
+ } else if (pipeline.length > 0 && pipeline[pipeline.length - 1]['$out']) {
+ ignoreReadConcern = true;
+ }
+
+ // Decorate command with writeConcern if out has been specified
+ if (
+ pipeline.length > 0 &&
+ pipeline[pipeline.length - 1]['$out'] &&
+ this.s.topology.capabilities().commandsTakeWriteConcern
+ ) {
+ applyWriteConcern(command, { db: this.s.db, collection: this }, options);
+ }
+
+ // Have we specified collation
+ try {
+ decorateWithCollation(command, this, options);
+ } catch (err) {
+ if (typeof callback === 'function') return callback(err, null);
+ throw err;
+ }
+
+ // If we have bypassDocumentValidation set
+ if (options.bypassDocumentValidation === true) {
+ command.bypassDocumentValidation = options.bypassDocumentValidation;
+ }
+
+ // Do we have a readConcern specified
+ if (!ignoreReadConcern) {
+ decorateWithReadConcern(command, this, options);
+ }
+
+ // If we have allowDiskUse defined
+ if (options.allowDiskUse) command.allowDiskUse = options.allowDiskUse;
+ if (typeof options.maxTimeMS === 'number') command.maxTimeMS = options.maxTimeMS;
+
+ // If we are giving a hint
+ if (options.hint) command.hint = options.hint;
+
+ options = Object.assign({}, options);
+ // Ensure we have the right read preference inheritance
+ options.readPreference = resolveReadPreference(options, { db: this.s.db, collection: this });
+
+ // If explain has been specified add it
+ if (options.explain) {
+ if (command.readConcern || command.writeConcern) {
+ throw toError('"explain" cannot be used on an aggregate call with readConcern/writeConcern');
+ }
+ command.explain = options.explain;
+ }
+
+ if (typeof options.comment === 'string') command.comment = options.comment;
+
+ // Validate that cursor options is valid
+ if (options.cursor != null && typeof options.cursor !== 'object') {
+ throw toError('cursor options must be an object');
+ }
+
+ options.cursor = options.cursor || {};
+ if (options.batchSize) options.cursor.batchSize = options.batchSize;
+ command.cursor = options.cursor;
+
+ // promiseLibrary
+ options.promiseLibrary = this.s.promiseLibrary;
+
+ // Set the AggregationCursor constructor
+ options.cursorFactory = AggregationCursor;
+ if (typeof callback !== 'function') {
+ if (!this.s.topology.capabilities()) {
+ throw new MongoError('cannot connect to server');
+ }
+
+ // Allow disk usage command
+ if (typeof options.allowDiskUse === 'boolean') command.allowDiskUse = options.allowDiskUse;
+ if (typeof options.maxTimeMS === 'number') command.maxTimeMS = options.maxTimeMS;
+
+ // Execute the cursor
+ return this.s.topology.cursor(this.s.namespace, command, options);
+ }
+
+ return handleCallback(callback, null, this.s.topology.cursor(this.s.namespace, command, options));
+};
+
+/**
+ * Create a new Change Stream, watching for new changes (insertions, updates, replacements, deletions, and invalidations) in this collection.
+ * @method
+ * @since 3.0.0
+ * @param {Array} [pipeline] An array of {@link https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.
+ * @param {object} [options] Optional settings
+ * @param {string} [options.fullDocument='default'] Allowed values: ‘default’, ‘updateLookup’. When set to ‘updateLookup’, the change stream will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
+ * @param {object} [options.resumeAfter] Specifies the logical starting point for the new change stream. This should be the _id field from a previously returned change stream document.
+ * @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query
+ * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
+ * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
+ * @param {ReadPreference} [options.readPreference] The read preference. Defaults to the read preference of the database or collection. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
+ * @param {Timestamp} [options.startAtClusterTime] receive change events that occur after the specified timestamp
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @return {ChangeStream} a ChangeStream instance.
+ */
+Collection.prototype.watch = function(pipeline, options) {
+ pipeline = pipeline || [];
+ options = options || {};
+
+ // Allow optionally not specifying a pipeline
+ if (!Array.isArray(pipeline)) {
+ options = pipeline;
+ pipeline = [];
+ }
+
+ return new ChangeStream(this, pipeline, options);
+};
+
+/**
+ * The callback format for results
+ * @callback Collection~parallelCollectionScanCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {Cursor[]} cursors A list of cursors returned allowing for parallel reading of collection.
+ */
+
+/**
+ * Return N number of parallel cursors for a collection allowing parallel reading of entire collection. There are
+ * no ordering guarantees for returned results.
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {number} [options.batchSize] Set the batchSize for the getMoreCommand when iterating over the query results.
+ * @param {number} [options.numCursors=1] The maximum number of parallel command cursors to return (the number of returned cursors will be in the range 1:numCursors)
+ * @param {boolean} [options.raw=false] Return all BSON documents as Raw Buffer documents.
+ * @param {Collection~parallelCollectionScanCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Collection.prototype.parallelCollectionScan = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = { numCursors: 1 });
+ // Set number of cursors to 1
+ options.numCursors = options.numCursors || 1;
+ options.batchSize = options.batchSize || 1000;
+
+ options = Object.assign({}, options);
+ // Ensure we have the right read preference inheritance
+ options.readPreference = resolveReadPreference(options, { db: this.s.db, collection: this });
+
+ // Add a promiseLibrary
+ options.promiseLibrary = this.s.promiseLibrary;
+
+ if (options.session) {
+ options.session = undefined;
+ }
+
+ return executeOperation(this.s.topology, parallelCollectionScan, [this, options, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * Execute a geo search using a geo haystack index on a collection.
+ *
+ * @method
+ * @param {number} x Point to search on the x axis, ensure the indexes are ordered in the same order.
+ * @param {number} y Point to search on the y axis, ensure the indexes are ordered in the same order.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {number} [options.maxDistance] Include results up to maxDistance from the point.
+ * @param {object} [options.search] Filter the results by a query.
+ * @param {number} [options.limit=false] Max number of results to return.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Collection~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Collection.prototype.geoHaystackSearch = function(x, y, options, callback) {
+ const args = Array.prototype.slice.call(arguments, 2);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ options = args.length ? args.shift() || {} : {};
+
+ return executeOperation(this.s.topology, geoHaystackSearch, [this, x, y, options, callback]);
+};
+
+/**
+ * Run a group command across a collection
+ *
+ * @method
+ * @param {(object|array|function|code)} keys An object, array or function expressing the keys to group by.
+ * @param {object} condition An optional condition that must be true for a row to be considered.
+ * @param {object} initial Initial value of the aggregation counter object.
+ * @param {(function|Code)} reduce The reduce function aggregates (reduces) the objects iterated
+ * @param {(function|Code)} finalize An optional function to be run on each item in the result set just before the item is returned.
+ * @param {boolean} command Specify if you wish to run using the internal group command or using eval, default is true.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Collection~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated MongoDB 3.6 or higher no longer supports the group command. We recommend rewriting using the aggregation framework.
+ */
+Collection.prototype.group = deprecate(function(
+ keys,
+ condition,
+ initial,
+ reduce,
+ finalize,
+ command,
+ options,
+ callback
+) {
+ const args = Array.prototype.slice.call(arguments, 3);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ reduce = args.length ? args.shift() : null;
+ finalize = args.length ? args.shift() : null;
+ command = args.length ? args.shift() : null;
+ options = args.length ? args.shift() || {} : {};
+
+ // Make sure we are backward compatible
+ if (!(typeof finalize === 'function')) {
+ command = finalize;
+ finalize = null;
+ }
+
+ if (
+ !Array.isArray(keys) &&
+ keys instanceof Object &&
+ typeof keys !== 'function' &&
+ !(keys._bsontype === 'Code')
+ ) {
+ keys = Object.keys(keys);
+ }
+
+ if (typeof reduce === 'function') {
+ reduce = reduce.toString();
+ }
+
+ if (typeof finalize === 'function') {
+ finalize = finalize.toString();
+ }
+
+ // Set up the command as default
+ command = command == null ? true : command;
+
+ return executeOperation(this.s.topology, group, [
+ this,
+ keys,
+ condition,
+ initial,
+ reduce,
+ finalize,
+ command,
+ options,
+ callback
+ ]);
+},
+'MongoDB 3.6 or higher no longer supports the group command. We recommend rewriting using the aggregation framework.');
+
+/**
+ * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
+ *
+ * @method
+ * @param {(function|string)} map The mapping function.
+ * @param {(function|string)} reduce The reduce function.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {object} [options.out] Sets the output target for the map reduce job. *{inline:1} | {replace:'collectionName'} | {merge:'collectionName'} | {reduce:'collectionName'}*
+ * @param {object} [options.query] Query filter object.
+ * @param {object} [options.sort] Sorts the input objects using this key. Useful for optimization, like sorting by the emit key for fewer reduces.
+ * @param {number} [options.limit] Number of objects to return from collection.
+ * @param {boolean} [options.keeptemp=false] Keep temporary data.
+ * @param {(function|string)} [options.finalize] Finalize function.
+ * @param {object} [options.scope] Can pass in variables that can be access from map/reduce/finalize.
+ * @param {boolean} [options.jsMode=false] It is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X.
+ * @param {boolean} [options.verbose=false] Provide statistics on job execution time.
+ * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Collection~resultCallback} [callback] The command result callback
+ * @throws {MongoError}
+ * @return {Promise} returns Promise if no callback passed
+ */
+Collection.prototype.mapReduce = function(map, reduce, options, callback) {
+ if ('function' === typeof options) (callback = options), (options = {});
+ // Out must allways be defined (make sure we don't break weirdly on pre 1.8+ servers)
+ if (null == options.out) {
+ throw new Error(
+ 'the out option parameter must be defined, see mongodb docs for possible values'
+ );
+ }
+
+ if ('function' === typeof map) {
+ map = map.toString();
+ }
+
+ if ('function' === typeof reduce) {
+ reduce = reduce.toString();
+ }
+
+ if ('function' === typeof options.finalize) {
+ options.finalize = options.finalize.toString();
+ }
+
+ return executeOperation(this.s.topology, mapReduce, [this, map, reduce, options, callback]);
+};
+
+/**
+ * Initiate an Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order.
+ *
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @return {UnorderedBulkOperation}
+ */
+Collection.prototype.initializeUnorderedBulkOp = function(options) {
+ options = options || {};
+ options.promiseLibrary = this.s.promiseLibrary;
+ return unordered(this.s.topology, this, options);
+};
+
+/**
+ * Initiate an In order bulk write operation. Operations will be serially executed in the order they are added, creating a new operation for each switch in types.
+ *
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {OrderedBulkOperation} callback The command result callback
+ * @return {null}
+ */
+Collection.prototype.initializeOrderedBulkOp = function(options) {
+ options = options || {};
+ options.promiseLibrary = this.s.promiseLibrary;
+ return ordered(this.s.topology, this, options);
+};
+
+/**
+ * Return the db logger
+ * @method
+ * @return {Logger} return the db logger
+ * @ignore
+ */
+Collection.prototype.getLogger = function() {
+ return this.s.db.s.logger;
+};
+
+module.exports = Collection;
diff --git a/node_modules/mongodb/lib/command_cursor.js b/node_modules/mongodb/lib/command_cursor.js
new file mode 100644
index 0000000..50afb6f
--- /dev/null
+++ b/node_modules/mongodb/lib/command_cursor.js
@@ -0,0 +1,334 @@
+'use strict';
+
+const inherits = require('util').inherits;
+const ReadPreference = require('mongodb-core').ReadPreference;
+const MongoError = require('mongodb-core').MongoError;
+const Readable = require('stream').Readable;
+const CoreCursor = require('./cursor');
+
+/**
+ * @fileOverview The **CommandCursor** class is an internal class that embodies a
+ * generalized cursor based on a MongoDB command allowing for iteration over the
+ * results returned. It supports one by one document iteration, conversion to an
+ * array or can be iterated as a Node 0.10.X or higher stream
+ *
+ * **CommandCursor Cannot directly be instantiated**
+ * @example
+ * const MongoClient = require('mongodb').MongoClient;
+ * const test = require('assert');
+ * // Connection url
+ * const url = 'mongodb://localhost:27017';
+ * // Database Name
+ * const dbName = 'test';
+ * // Connect using MongoClient
+ * MongoClient.connect(url, function(err, client) {
+ * // Create a collection we want to drop later
+ * const col = client.db(dbName).collection('listCollectionsExample1');
+ * // Insert a bunch of documents
+ * col.insert([{a:1, b:1}
+ * , {a:2, b:2}, {a:3, b:3}
+ * , {a:4, b:4}], {w:1}, function(err, result) {
+ * test.equal(null, err);
+ * // List the database collections available
+ * db.listCollections().toArray(function(err, items) {
+ * test.equal(null, err);
+ * client.close();
+ * });
+ * });
+ * });
+ */
+
+/**
+ * Namespace provided by the browser.
+ * @external Readable
+ */
+
+/**
+ * Creates a new Command Cursor instance (INTERNAL TYPE, do not instantiate directly)
+ * @class CommandCursor
+ * @extends external:Readable
+ * @fires CommandCursor#data
+ * @fires CommandCursor#end
+ * @fires CommandCursor#close
+ * @fires CommandCursor#readable
+ * @return {CommandCursor} an CommandCursor instance.
+ */
+var CommandCursor = function(bson, ns, cmd, options, topology, topologyOptions) {
+ CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
+ var state = CommandCursor.INIT;
+ var streamOptions = {};
+
+ // MaxTimeMS
+ var maxTimeMS = null;
+
+ // Get the promiseLibrary
+ var promiseLibrary = options.promiseLibrary || Promise;
+
+ // Set up
+ Readable.call(this, { objectMode: true });
+
+ // Internal state
+ this.s = {
+ // MaxTimeMS
+ maxTimeMS: maxTimeMS,
+ // State
+ state: state,
+ // Stream options
+ streamOptions: streamOptions,
+ // BSON
+ bson: bson,
+ // Namespace
+ ns: ns,
+ // Command
+ cmd: cmd,
+ // Options
+ options: options,
+ // Topology
+ topology: topology,
+ // Topology Options
+ topologyOptions: topologyOptions,
+ // Promise library
+ promiseLibrary: promiseLibrary,
+ // Optional ClientSession
+ session: options.session
+ };
+};
+
+/**
+ * CommandCursor stream data event, fired for each document in the cursor.
+ *
+ * @event CommandCursor#data
+ * @type {object}
+ */
+
+/**
+ * CommandCursor stream end event
+ *
+ * @event CommandCursor#end
+ * @type {null}
+ */
+
+/**
+ * CommandCursor stream close event
+ *
+ * @event CommandCursor#close
+ * @type {null}
+ */
+
+/**
+ * CommandCursor stream readable event
+ *
+ * @event CommandCursor#readable
+ * @type {null}
+ */
+
+// Inherit from Readable
+inherits(CommandCursor, Readable);
+
+// Set the methods to inherit from prototype
+var methodsToInherit = [
+ '_next',
+ 'next',
+ 'hasNext',
+ 'each',
+ 'forEach',
+ 'toArray',
+ 'rewind',
+ 'bufferedCount',
+ 'readBufferedDocuments',
+ 'close',
+ 'isClosed',
+ 'kill',
+ 'setCursorBatchSize',
+ '_find',
+ '_getmore',
+ '_killcursor',
+ 'isDead',
+ 'explain',
+ 'isNotified',
+ 'isKilled',
+ '_endSession',
+ '_initImplicitSession'
+];
+
+// Only inherit the types we need
+for (var i = 0; i < methodsToInherit.length; i++) {
+ CommandCursor.prototype[methodsToInherit[i]] = CoreCursor.prototype[methodsToInherit[i]];
+}
+
+/**
+ * Set the ReadPreference for the cursor.
+ * @method
+ * @param {(string|ReadPreference)} readPreference The new read preference for the cursor.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+CommandCursor.prototype.setReadPreference = function(readPreference) {
+ if (this.s.state === CommandCursor.CLOSED || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ if (this.s.state !== CommandCursor.INIT) {
+ throw MongoError.create({
+ message: 'cannot change cursor readPreference after cursor has been accessed',
+ driver: true
+ });
+ }
+
+ if (readPreference instanceof ReadPreference) {
+ this.s.options.readPreference = readPreference;
+ } else if (typeof readPreference === 'string') {
+ this.s.options.readPreference = new ReadPreference(readPreference);
+ } else {
+ throw new TypeError('Invalid read preference: ' + readPreference);
+ }
+
+ return this;
+};
+
+/**
+ * Set the batch size for the cursor.
+ * @method
+ * @param {number} value The batchSize for the cursor.
+ * @throws {MongoError}
+ * @return {CommandCursor}
+ */
+CommandCursor.prototype.batchSize = function(value) {
+ if (this.s.state === CommandCursor.CLOSED || this.isDead())
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ if (typeof value !== 'number')
+ throw MongoError.create({ message: 'batchSize requires an integer', driver: true });
+ if (this.s.cmd.cursor) this.s.cmd.cursor.batchSize = value;
+ this.setCursorBatchSize(value);
+ return this;
+};
+
+/**
+ * Add a maxTimeMS stage to the aggregation pipeline
+ * @method
+ * @param {number} value The state maxTimeMS value.
+ * @return {CommandCursor}
+ */
+CommandCursor.prototype.maxTimeMS = function(value) {
+ if (this.s.topology.lastIsMaster().minWireVersion > 2) {
+ this.s.cmd.maxTimeMS = value;
+ }
+ return this;
+};
+
+/**
+ * Return the cursor logger
+ * @method
+ * @return {Logger} return the cursor logger
+ * @ignore
+ */
+CommandCursor.prototype.getLogger = function() {
+ return this.logger;
+};
+
+CommandCursor.prototype.get = CommandCursor.prototype.toArray;
+
+/**
+ * Get the next available document from the cursor, returns null if no more documents are available.
+ * @function CommandCursor.prototype.next
+ * @param {CommandCursor~resultCallback} [callback] The result callback.
+ * @throws {MongoError}
+ * @return {Promise} returns Promise if no callback passed
+ */
+
+/**
+ * Check if there is any document still available in the cursor
+ * @function CommandCursor.prototype.hasNext
+ * @param {CommandCursor~resultCallback} [callback] The result callback.
+ * @throws {MongoError}
+ * @return {Promise} returns Promise if no callback passed
+ */
+
+/**
+ * The callback format for results
+ * @callback CommandCursor~toArrayResultCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {object[]} documents All the documents the satisfy the cursor.
+ */
+
+/**
+ * Returns an array of documents. The caller is responsible for making sure that there
+ * is enough memory to store the results. Note that the array only contain partial
+ * results when this cursor had been previouly accessed.
+ * @method CommandCursor.prototype.toArray
+ * @param {CommandCursor~toArrayResultCallback} [callback] The result callback.
+ * @throws {MongoError}
+ * @return {Promise} returns Promise if no callback passed
+ */
+
+/**
+ * The callback format for results
+ * @callback CommandCursor~resultCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {(object|null)} result The result object if the command was executed successfully.
+ */
+
+/**
+ * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
+ * not all of the elements will be iterated if this cursor had been previouly accessed.
+ * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike
+ * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements
+ * at any given time if batch size is specified. Otherwise, the caller is responsible
+ * for making sure that the entire result can fit the memory.
+ * @method CommandCursor.prototype.each
+ * @param {CommandCursor~resultCallback} callback The result callback.
+ * @throws {MongoError}
+ * @return {null}
+ */
+
+/**
+ * Close the cursor, sending a KillCursor command and emitting close.
+ * @method CommandCursor.prototype.close
+ * @param {CommandCursor~resultCallback} [callback] The result callback.
+ * @return {Promise} returns Promise if no callback passed
+ */
+
+/**
+ * Is the cursor closed
+ * @method CommandCursor.prototype.isClosed
+ * @return {boolean}
+ */
+
+/**
+ * Clone the cursor
+ * @function CommandCursor.prototype.clone
+ * @return {CommandCursor}
+ */
+
+/**
+ * Resets the cursor
+ * @function CommandCursor.prototype.rewind
+ * @return {CommandCursor}
+ */
+
+/**
+ * The callback format for the forEach iterator method
+ * @callback CommandCursor~iteratorCallback
+ * @param {Object} doc An emitted document for the iterator
+ */
+
+/**
+ * The callback error format for the forEach iterator method
+ * @callback CommandCursor~endCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ */
+
+/*
+ * Iterates over all the documents for this cursor using the iterator, callback pattern.
+ * @method CommandCursor.prototype.forEach
+ * @param {CommandCursor~iteratorCallback} iterator The iteration callback.
+ * @param {CommandCursor~endCallback} callback The end callback.
+ * @throws {MongoError}
+ * @return {null}
+ */
+
+CommandCursor.INIT = 0;
+CommandCursor.OPEN = 1;
+CommandCursor.CLOSED = 2;
+
+module.exports = CommandCursor;
diff --git a/node_modules/mongodb/lib/constants.js b/node_modules/mongodb/lib/constants.js
new file mode 100644
index 0000000..d6cc68a
--- /dev/null
+++ b/node_modules/mongodb/lib/constants.js
@@ -0,0 +1,10 @@
+'use strict';
+
+module.exports = {
+ SYSTEM_NAMESPACE_COLLECTION: 'system.namespaces',
+ SYSTEM_INDEX_COLLECTION: 'system.indexes',
+ SYSTEM_PROFILE_COLLECTION: 'system.profile',
+ SYSTEM_USER_COLLECTION: 'system.users',
+ SYSTEM_COMMAND_COLLECTION: '$cmd',
+ SYSTEM_JS_COLLECTION: 'system.js'
+};
diff --git a/node_modules/mongodb/lib/cursor.js b/node_modules/mongodb/lib/cursor.js
new file mode 100644
index 0000000..4a0b815
--- /dev/null
+++ b/node_modules/mongodb/lib/cursor.js
@@ -0,0 +1,1152 @@
+'use strict';
+
+const Transform = require('stream').Transform;
+const PassThrough = require('stream').PassThrough;
+const inherits = require('util').inherits;
+const deprecate = require('util').deprecate;
+const handleCallback = require('./utils').handleCallback;
+const ReadPreference = require('mongodb-core').ReadPreference;
+const MongoError = require('mongodb-core').MongoError;
+const Readable = require('stream').Readable;
+const CoreCursor = require('mongodb-core').Cursor;
+const Map = require('mongodb-core').BSON.Map;
+const executeOperation = require('./utils').executeOperation;
+
+const count = require('./operations/cursor_ops').count;
+const each = require('./operations/cursor_ops').each;
+const hasNext = require('./operations/cursor_ops').hasNext;
+const next = require('./operations/cursor_ops').next;
+const toArray = require('./operations/cursor_ops').toArray;
+
+/**
+ * @fileOverview The **Cursor** class is an internal class that embodies a cursor on MongoDB
+ * allowing for iteration over the results returned from the underlying query. It supports
+ * one by one document iteration, conversion to an array or can be iterated as a Node 4.X
+ * or higher stream
+ *
+ * **CURSORS Cannot directly be instantiated**
+ * @example
+ * const MongoClient = require('mongodb').MongoClient;
+ * const test = require('assert');
+ * // Connection url
+ * const url = 'mongodb://localhost:27017';
+ * // Database Name
+ * const dbName = 'test';
+ * // Connect using MongoClient
+ * MongoClient.connect(url, function(err, client) {
+ * // Create a collection we want to drop later
+ * const col = client.db(dbName).collection('createIndexExample1');
+ * // Insert a bunch of documents
+ * col.insert([{a:1, b:1}
+ * , {a:2, b:2}, {a:3, b:3}
+ * , {a:4, b:4}], {w:1}, function(err, result) {
+ * test.equal(null, err);
+ * // Show that duplicate records got dropped
+ * col.find({}).toArray(function(err, items) {
+ * test.equal(null, err);
+ * test.equal(4, items.length);
+ * client.close();
+ * });
+ * });
+ * });
+ */
+
+/**
+ * Namespace provided by the mongodb-core and node.js
+ * @external CoreCursor
+ * @external Readable
+ */
+
+// Flags allowed for cursor
+const flags = ['tailable', 'oplogReplay', 'noCursorTimeout', 'awaitData', 'exhaust', 'partial'];
+const fields = ['numberOfRetries', 'tailableRetryInterval'];
+
+/**
+ * Creates a new Cursor instance (INTERNAL TYPE, do not instantiate directly)
+ * @class Cursor
+ * @extends external:CoreCursor
+ * @extends external:Readable
+ * @property {string} sortValue Cursor query sort setting.
+ * @property {boolean} timeout Is Cursor able to time out.
+ * @property {ReadPreference} readPreference Get cursor ReadPreference.
+ * @fires Cursor#data
+ * @fires Cursor#end
+ * @fires Cursor#close
+ * @fires Cursor#readable
+ * @return {Cursor} a Cursor instance.
+ * @example
+ * Cursor cursor options.
+ *
+ * collection.find({}).project({a:1}) // Create a projection of field a
+ * collection.find({}).skip(1).limit(10) // Skip 1 and limit 10
+ * collection.find({}).batchSize(5) // Set batchSize on cursor to 5
+ * collection.find({}).filter({a:1}) // Set query on the cursor
+ * collection.find({}).comment('add a comment') // Add a comment to the query, allowing to correlate queries
+ * collection.find({}).addCursorFlag('tailable', true) // Set cursor as tailable
+ * collection.find({}).addCursorFlag('oplogReplay', true) // Set cursor as oplogReplay
+ * collection.find({}).addCursorFlag('noCursorTimeout', true) // Set cursor as noCursorTimeout
+ * collection.find({}).addCursorFlag('awaitData', true) // Set cursor as awaitData
+ * collection.find({}).addCursorFlag('partial', true) // Set cursor as partial
+ * collection.find({}).addQueryModifier('$orderby', {a:1}) // Set $orderby {a:1}
+ * collection.find({}).max(10) // Set the cursor max
+ * collection.find({}).maxTimeMS(1000) // Set the cursor maxTimeMS
+ * collection.find({}).min(100) // Set the cursor min
+ * collection.find({}).returnKey(true) // Set the cursor returnKey
+ * collection.find({}).setReadPreference(ReadPreference.PRIMARY) // Set the cursor readPreference
+ * collection.find({}).showRecordId(true) // Set the cursor showRecordId
+ * collection.find({}).sort([['a', 1]]) // Sets the sort order of the cursor query
+ * collection.find({}).hint('a_1') // Set the cursor hint
+ *
+ * All options are chainable, so one can do the following.
+ *
+ * collection.find({}).maxTimeMS(1000).maxScan(100).skip(1).toArray(..)
+ */
+function Cursor(bson, ns, cmd, options, topology, topologyOptions) {
+ CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
+ const state = Cursor.INIT;
+ const streamOptions = {};
+
+ // Tailable cursor options
+ const numberOfRetries = options.numberOfRetries || 5;
+ const tailableRetryInterval = options.tailableRetryInterval || 500;
+ const currentNumberOfRetries = numberOfRetries;
+
+ // Get the promiseLibrary
+ const promiseLibrary = options.promiseLibrary || Promise;
+
+ // Set up
+ Readable.call(this, { objectMode: true });
+
+ // Internal cursor state
+ this.s = {
+ // Tailable cursor options
+ numberOfRetries: numberOfRetries,
+ tailableRetryInterval: tailableRetryInterval,
+ currentNumberOfRetries: currentNumberOfRetries,
+ // State
+ state: state,
+ // Stream options
+ streamOptions: streamOptions,
+ // BSON
+ bson: bson,
+ // Namespace
+ ns: ns,
+ // Command
+ cmd: cmd,
+ // Options
+ options: options,
+ // Topology
+ topology: topology,
+ // Topology options
+ topologyOptions: topologyOptions,
+ // Promise library
+ promiseLibrary: promiseLibrary,
+ // Current doc
+ currentDoc: null,
+ // explicitlyIgnoreSession
+ explicitlyIgnoreSession: options.explicitlyIgnoreSession
+ };
+
+ // Optional ClientSession
+ if (!options.explicitlyIgnoreSession && options.session) {
+ this.s.session = options.session;
+ }
+
+ // Translate correctly
+ if (this.s.options.noCursorTimeout === true) {
+ this.addCursorFlag('noCursorTimeout', true);
+ }
+
+ // Set the sort value
+ this.sortValue = this.s.cmd.sort;
+
+ // Get the batchSize
+ const batchSize =
+ cmd.cursor && cmd.cursor.batchSize
+ ? cmd.cursor && cmd.cursor.batchSize
+ : options.cursor && options.cursor.batchSize
+ ? options.cursor.batchSize
+ : 1000;
+
+ // Set the batchSize
+ this.setCursorBatchSize(batchSize);
+}
+
+/**
+ * Cursor stream data event, fired for each document in the cursor.
+ *
+ * @event Cursor#data
+ * @type {object}
+ */
+
+/**
+ * Cursor stream end event
+ *
+ * @event Cursor#end
+ * @type {null}
+ */
+
+/**
+ * Cursor stream close event
+ *
+ * @event Cursor#close
+ * @type {null}
+ */
+
+/**
+ * Cursor stream readable event
+ *
+ * @event Cursor#readable
+ * @type {null}
+ */
+
+// Inherit from Readable
+inherits(Cursor, Readable);
+
+// Map core cursor _next method so we can apply mapping
+Cursor.prototype._next = function() {
+ if (this._initImplicitSession) {
+ this._initImplicitSession();
+ }
+ return CoreCursor.prototype.next.apply(this, arguments);
+};
+
+for (let name in CoreCursor.prototype) {
+ Cursor.prototype[name] = CoreCursor.prototype[name];
+}
+
+Cursor.prototype._initImplicitSession = function() {
+ if (!this.s.explicitlyIgnoreSession && !this.s.session && this.s.topology.hasSessionSupport()) {
+ this.s.session = this.s.topology.startSession({ owner: this });
+ this.cursorState.session = this.s.session;
+ }
+};
+
+Cursor.prototype._endSession = function() {
+ const didCloseCursor = CoreCursor.prototype._endSession.apply(this, arguments);
+ if (didCloseCursor) {
+ this.s.session = undefined;
+ }
+};
+
+/**
+ * Check if there is any document still available in the cursor
+ * @method
+ * @param {Cursor~resultCallback} [callback] The result callback.
+ * @throws {MongoError}
+ * @return {Promise} returns Promise if no callback passed
+ */
+Cursor.prototype.hasNext = function(callback) {
+ return executeOperation(this.s.topology, hasNext, [this, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * Get the next available document from the cursor, returns null if no more documents are available.
+ * @method
+ * @param {Cursor~resultCallback} [callback] The result callback.
+ * @throws {MongoError}
+ * @return {Promise} returns Promise if no callback passed
+ */
+Cursor.prototype.next = function(callback) {
+ return executeOperation(this.s.topology, next, [this, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * Set the cursor query
+ * @method
+ * @param {object} filter The filter object used for the cursor.
+ * @return {Cursor}
+ */
+Cursor.prototype.filter = function(filter) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.query = filter;
+ return this;
+};
+
+/**
+ * Set the cursor maxScan
+ * @method
+ * @param {object} maxScan Constrains the query to only scan the specified number of documents when fulfilling the query
+ * @deprecated as of MongoDB 4.0
+ * @return {Cursor}
+ */
+Cursor.prototype.maxScan = deprecate(function(maxScan) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.maxScan = maxScan;
+ return this;
+}, 'Cursor.maxScan is deprecated, and will be removed in a later version');
+
+/**
+ * Set the cursor hint
+ * @method
+ * @param {object} hint If specified, then the query system will only consider plans using the hinted index.
+ * @return {Cursor}
+ */
+Cursor.prototype.hint = function(hint) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.hint = hint;
+ return this;
+};
+
+/**
+ * Set the cursor min
+ * @method
+ * @param {object} min Specify a $min value to specify the inclusive lower bound for a specific index in order to constrain the results of find(). The $min specifies the lower bound for all keys of a specific index in order.
+ * @return {Cursor}
+ */
+Cursor.prototype.min = function(min) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead())
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ this.s.cmd.min = min;
+ return this;
+};
+
+/**
+ * Set the cursor max
+ * @method
+ * @param {object} max Specify a $max value to specify the exclusive upper bound for a specific index in order to constrain the results of find(). The $max specifies the upper bound for all keys of a specific index in order.
+ * @return {Cursor}
+ */
+Cursor.prototype.max = function(max) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.max = max;
+ return this;
+};
+
+/**
+ * Set the cursor returnKey. If set to true, modifies the cursor to only return the index field or fields for the results of the query, rather than documents. If set to true and the query does not use an index to perform the read operation, the returned documents will not contain any fields.
+ * @method
+ * @param {bool} returnKey the returnKey value.
+ * @return {Cursor}
+ */
+Cursor.prototype.returnKey = function(value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.returnKey = value;
+ return this;
+};
+
+/**
+ * Set the cursor showRecordId
+ * @method
+ * @param {object} showRecordId The $showDiskLoc option has now been deprecated and replaced with the showRecordId field. $showDiskLoc will still be accepted for OP_QUERY stye find.
+ * @return {Cursor}
+ */
+Cursor.prototype.showRecordId = function(value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.showDiskLoc = value;
+ return this;
+};
+
+/**
+ * Set the cursor snapshot
+ * @method
+ * @param {object} snapshot The $snapshot operator prevents the cursor from returning a document more than once because an intervening write operation results in a move of the document.
+ * @deprecated as of MongoDB 4.0
+ * @return {Cursor}
+ */
+Cursor.prototype.snapshot = deprecate(function(value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.snapshot = value;
+ return this;
+}, 'Cursor Snapshot is deprecated, and will be removed in a later version');
+
+/**
+ * Set a node.js specific cursor option
+ * @method
+ * @param {string} field The cursor option to set ['numberOfRetries', 'tailableRetryInterval'].
+ * @param {object} value The field value.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.setCursorOption = function(field, value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ if (fields.indexOf(field) === -1) {
+ throw MongoError.create({
+ message: `option ${field} is not a supported option ${fields}`,
+ driver: true
+ });
+ }
+
+ this.s[field] = value;
+ if (field === 'numberOfRetries') this.s.currentNumberOfRetries = value;
+ return this;
+};
+
+/**
+ * Add a cursor flag to the cursor
+ * @method
+ * @param {string} flag The flag to set, must be one of following ['tailable', 'oplogReplay', 'noCursorTimeout', 'awaitData', 'partial'].
+ * @param {boolean} value The flag boolean value.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.addCursorFlag = function(flag, value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ if (flags.indexOf(flag) === -1) {
+ throw MongoError.create({
+ message: `flag ${flag} is not a supported flag ${flags}`,
+ driver: true
+ });
+ }
+
+ if (typeof value !== 'boolean') {
+ throw MongoError.create({ message: `flag ${flag} must be a boolean value`, driver: true });
+ }
+
+ this.s.cmd[flag] = value;
+ return this;
+};
+
+/**
+ * Add a query modifier to the cursor query
+ * @method
+ * @param {string} name The query modifier (must start with $, such as $orderby etc)
+ * @param {string|boolean|number} value The modifier value.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.addQueryModifier = function(name, value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ if (name[0] !== '$') {
+ throw MongoError.create({ message: `${name} is not a valid query modifier`, driver: true });
+ }
+
+ // Strip of the $
+ const field = name.substr(1);
+ // Set on the command
+ this.s.cmd[field] = value;
+ // Deal with the special case for sort
+ if (field === 'orderby') this.s.cmd.sort = this.s.cmd[field];
+ return this;
+};
+
+/**
+ * Add a comment to the cursor query allowing for tracking the comment in the log.
+ * @method
+ * @param {string} value The comment attached to this query.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.comment = function(value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.comment = value;
+ return this;
+};
+
+/**
+ * Set a maxAwaitTimeMS on a tailing cursor query to allow to customize the timeout value for the option awaitData (Only supported on MongoDB 3.2 or higher, ignored otherwise)
+ * @method
+ * @param {number} value Number of milliseconds to wait before aborting the tailed query.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.maxAwaitTimeMS = function(value) {
+ if (typeof value !== 'number') {
+ throw MongoError.create({ message: 'maxAwaitTimeMS must be a number', driver: true });
+ }
+
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.maxAwaitTimeMS = value;
+ return this;
+};
+
+/**
+ * Set a maxTimeMS on the cursor query, allowing for hard timeout limits on queries (Only supported on MongoDB 2.6 or higher)
+ * @method
+ * @param {number} value Number of milliseconds to wait before aborting the query.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.maxTimeMS = function(value) {
+ if (typeof value !== 'number') {
+ throw MongoError.create({ message: 'maxTimeMS must be a number', driver: true });
+ }
+
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.maxTimeMS = value;
+ return this;
+};
+
+Cursor.prototype.maxTimeMs = Cursor.prototype.maxTimeMS;
+
+/**
+ * Sets a field projection for the query.
+ * @method
+ * @param {object} value The field projection object.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.project = function(value) {
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ this.s.cmd.fields = value;
+ return this;
+};
+
+/**
+ * Sets the sort order of the cursor query.
+ * @method
+ * @param {(string|array|object)} keyOrList The key or keys set for the sort.
+ * @param {number} [direction] The direction of the sorting (1 or -1).
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.sort = function(keyOrList, direction) {
+ if (this.s.options.tailable) {
+ throw MongoError.create({ message: "Tailable cursor doesn't support sorting", driver: true });
+ }
+
+ if (this.s.state === Cursor.CLOSED || this.s.state === Cursor.OPEN || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ let order = keyOrList;
+
+ // We have an array of arrays, we need to preserve the order of the sort
+ // so we will us a Map
+ if (Array.isArray(order) && Array.isArray(order[0])) {
+ order = new Map(
+ order.map(x => {
+ const value = [x[0], null];
+ if (x[1] === 'asc') {
+ value[1] = 1;
+ } else if (x[1] === 'desc') {
+ value[1] = -1;
+ } else if (x[1] === 1 || x[1] === -1 || x[1].$meta) {
+ value[1] = x[1];
+ } else {
+ throw new MongoError(
+ "Illegal sort clause, must be of the form [['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
+ );
+ }
+
+ return value;
+ })
+ );
+ }
+
+ if (direction != null) {
+ order = [[keyOrList, direction]];
+ }
+
+ this.s.cmd.sort = order;
+ this.sortValue = order;
+ return this;
+};
+
+/**
+ * Set the batch size for the cursor.
+ * @method
+ * @param {number} value The batchSize for the cursor.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.batchSize = function(value) {
+ if (this.s.options.tailable) {
+ throw MongoError.create({ message: "Tailable cursor doesn't support batchSize", driver: true });
+ }
+
+ if (this.s.state === Cursor.CLOSED || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ if (typeof value !== 'number') {
+ throw MongoError.create({ message: 'batchSize requires an integer', driver: true });
+ }
+
+ this.s.cmd.batchSize = value;
+ this.setCursorBatchSize(value);
+ return this;
+};
+
+/**
+ * Set the collation options for the cursor.
+ * @method
+ * @param {object} value The cursor collation options (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.collation = function(value) {
+ this.s.cmd.collation = value;
+ return this;
+};
+
+/**
+ * Set the limit for the cursor.
+ * @method
+ * @param {number} value The limit for the cursor query.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.limit = function(value) {
+ if (this.s.options.tailable) {
+ throw MongoError.create({ message: "Tailable cursor doesn't support limit", driver: true });
+ }
+
+ if (this.s.state === Cursor.OPEN || this.s.state === Cursor.CLOSED || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ if (typeof value !== 'number') {
+ throw MongoError.create({ message: 'limit requires an integer', driver: true });
+ }
+
+ this.s.cmd.limit = value;
+ // this.cursorLimit = value;
+ this.setCursorLimit(value);
+ return this;
+};
+
+/**
+ * Set the skip for the cursor.
+ * @method
+ * @param {number} value The skip for the cursor query.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.skip = function(value) {
+ if (this.s.options.tailable) {
+ throw MongoError.create({ message: "Tailable cursor doesn't support skip", driver: true });
+ }
+
+ if (this.s.state === Cursor.OPEN || this.s.state === Cursor.CLOSED || this.isDead()) {
+ throw MongoError.create({ message: 'Cursor is closed', driver: true });
+ }
+
+ if (typeof value !== 'number') {
+ throw MongoError.create({ message: 'skip requires an integer', driver: true });
+ }
+
+ this.s.cmd.skip = value;
+ this.setCursorSkip(value);
+ return this;
+};
+
+/**
+ * The callback format for results
+ * @callback Cursor~resultCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {(object|null|boolean)} result The result object if the command was executed successfully.
+ */
+
+/**
+ * Clone the cursor
+ * @function external:CoreCursor#clone
+ * @return {Cursor}
+ */
+
+/**
+ * Resets the cursor
+ * @function external:CoreCursor#rewind
+ * @return {null}
+ */
+
+/**
+ * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
+ * not all of the elements will be iterated if this cursor had been previouly accessed.
+ * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike
+ * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements
+ * at any given time if batch size is specified. Otherwise, the caller is responsible
+ * for making sure that the entire result can fit the memory.
+ * @method
+ * @deprecated
+ * @param {Cursor~resultCallback} callback The result callback.
+ * @throws {MongoError}
+ * @return {null}
+ */
+Cursor.prototype.each = deprecate(function(callback) {
+ // Rewind cursor state
+ this.rewind();
+ // Set current cursor to INIT
+ this.s.state = Cursor.INIT;
+ // Run the query
+ each(this, callback);
+}, 'Cursor.each is deprecated. Use Cursor.forEach instead.');
+
+/**
+ * The callback format for the forEach iterator method
+ * @callback Cursor~iteratorCallback
+ * @param {Object} doc An emitted document for the iterator
+ */
+
+/**
+ * The callback error format for the forEach iterator method
+ * @callback Cursor~endCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ */
+
+/**
+ * Iterates over all the documents for this cursor using the iterator, callback pattern.
+ * @method
+ * @param {Cursor~iteratorCallback} iterator The iteration callback.
+ * @param {Cursor~endCallback} callback The end callback.
+ * @throws {MongoError}
+ * @return {Promise} if no callback supplied
+ */
+Cursor.prototype.forEach = function(iterator, callback) {
+ // Rewind cursor state
+ this.rewind();
+
+ // Set current cursor to INIT
+ this.s.state = Cursor.INIT;
+
+ if (typeof callback === 'function') {
+ each(this, (err, doc) => {
+ if (err) {
+ callback(err);
+ return false;
+ }
+ if (doc != null) {
+ iterator(doc);
+ return true;
+ }
+ if (doc == null && callback) {
+ const internalCallback = callback;
+ callback = null;
+ internalCallback(null);
+ return false;
+ }
+ });
+ } else {
+ return new this.s.promiseLibrary((fulfill, reject) => {
+ each(this, (err, doc) => {
+ if (err) {
+ reject(err);
+ return false;
+ } else if (doc == null) {
+ fulfill(null);
+ return false;
+ } else {
+ iterator(doc);
+ return true;
+ }
+ });
+ });
+ }
+};
+
+/**
+ * Set the ReadPreference for the cursor.
+ * @method
+ * @param {(string|ReadPreference)} readPreference The new read preference for the cursor.
+ * @throws {MongoError}
+ * @return {Cursor}
+ */
+Cursor.prototype.setReadPreference = function(readPreference) {
+ if (this.s.state !== Cursor.INIT) {
+ throw MongoError.create({
+ message: 'cannot change cursor readPreference after cursor has been accessed',
+ driver: true
+ });
+ }
+
+ if (readPreference instanceof ReadPreference) {
+ this.s.options.readPreference = readPreference;
+ } else if (typeof readPreference === 'string') {
+ this.s.options.readPreference = new ReadPreference(readPreference);
+ } else {
+ throw new TypeError('Invalid read preference: ' + readPreference);
+ }
+
+ return this;
+};
+
+/**
+ * The callback format for results
+ * @callback Cursor~toArrayResultCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {object[]} documents All the documents the satisfy the cursor.
+ */
+
+/**
+ * Returns an array of documents. The caller is responsible for making sure that there
+ * is enough memory to store the results. Note that the array only contains partial
+ * results when this cursor had been previouly accessed. In that case,
+ * cursor.rewind() can be used to reset the cursor.
+ * @method
+ * @param {Cursor~toArrayResultCallback} [callback] The result callback.
+ * @throws {MongoError}
+ * @return {Promise} returns Promise if no callback passed
+ */
+Cursor.prototype.toArray = function(callback) {
+ if (this.s.options.tailable) {
+ throw MongoError.create({
+ message: 'Tailable cursor cannot be converted to array',
+ driver: true
+ });
+ }
+
+ return executeOperation(this.s.topology, toArray, [this, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * The callback format for results
+ * @callback Cursor~countResultCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {number} count The count of documents.
+ */
+
+/**
+ * Get the count of documents for this cursor
+ * @method
+ * @param {boolean} [applySkipLimit=true] Should the count command apply limit and skip settings on the cursor or in the passed in options.
+ * @param {object} [options] Optional settings.
+ * @param {number} [options.skip] The number of documents to skip.
+ * @param {number} [options.limit] The maximum amounts to count before aborting.
+ * @param {number} [options.maxTimeMS] Number of miliseconds to wait before aborting the query.
+ * @param {string} [options.hint] An index name hint for the query.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {Cursor~countResultCallback} [callback] The result callback.
+ * @return {Promise} returns Promise if no callback passed
+ */
+Cursor.prototype.count = function(applySkipLimit, opts, callback) {
+ if (this.s.cmd.query == null)
+ throw MongoError.create({ message: 'count can only be used with find command', driver: true });
+ if (typeof opts === 'function') (callback = opts), (opts = {});
+ opts = opts || {};
+
+ if (typeof applySkipLimit === 'function') {
+ callback = applySkipLimit;
+ applySkipLimit = true;
+ }
+
+ if (this.s.session) {
+ opts = Object.assign({}, opts, { session: this.s.session });
+ }
+
+ return executeOperation(this.s.topology, count, [this, applySkipLimit, opts, callback], {
+ skipSessions: !!this.s.session
+ });
+};
+
+/**
+ * Close the cursor, sending a KillCursor command and emitting close.
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.skipKillCursors] Bypass calling killCursors when closing the cursor.
+ * @param {Cursor~resultCallback} [callback] The result callback.
+ * @return {Promise} returns Promise if no callback passed
+ */
+Cursor.prototype.close = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = Object.assign({}, { skipKillCursors: false }, options);
+
+ this.s.state = Cursor.CLOSED;
+ if (!options.skipKillCursors) {
+ // Kill the cursor
+ this.kill();
+ }
+
+ const completeClose = () => {
+ // Emit the close event for the cursor
+ this.emit('close');
+
+ // Callback if provided
+ if (typeof callback === 'function') {
+ return handleCallback(callback, null, this);
+ }
+
+ // Return a Promise
+ return new this.s.promiseLibrary(resolve => {
+ resolve();
+ });
+ };
+
+ if (this.s.session) {
+ if (typeof callback === 'function') {
+ return this._endSession(() => completeClose());
+ }
+
+ return new this.s.promiseLibrary(resolve => {
+ this._endSession(() => completeClose().then(resolve));
+ });
+ }
+
+ return completeClose();
+};
+
+/**
+ * Map all documents using the provided function
+ * @method
+ * @param {function} [transform] The mapping transformation method.
+ * @return {Cursor}
+ */
+Cursor.prototype.map = function(transform) {
+ if (this.cursorState.transforms && this.cursorState.transforms.doc) {
+ const oldTransform = this.cursorState.transforms.doc;
+ this.cursorState.transforms.doc = doc => {
+ return transform(oldTransform(doc));
+ };
+ } else {
+ this.cursorState.transforms = { doc: transform };
+ }
+ return this;
+};
+
+/**
+ * Is the cursor closed
+ * @method
+ * @return {boolean}
+ */
+Cursor.prototype.isClosed = function() {
+ return this.isDead();
+};
+
+Cursor.prototype.destroy = function(err) {
+ if (err) this.emit('error', err);
+ this.pause();
+ this.close();
+};
+
+/**
+ * Return a modified Readable stream including a possible transform method.
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {function} [options.transform] A transformation method applied to each document emitted by the stream.
+ * @return {Cursor}
+ * TODO: replace this method with transformStream in next major release
+ */
+Cursor.prototype.stream = function(options) {
+ this.s.streamOptions = options || {};
+ return this;
+};
+
+/**
+ * Return a modified Readable stream that applies a given transform function, if supplied. If none supplied,
+ * returns a stream of unmodified docs.
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {function} [options.transform] A transformation method applied to each document emitted by the stream.
+ * @return {stream}
+ */
+Cursor.prototype.transformStream = function(options) {
+ const streamOptions = options || {};
+ if (typeof streamOptions.transform === 'function') {
+ const stream = new Transform({
+ objectMode: true,
+ transform: function(chunk, encoding, callback) {
+ this.push(streamOptions.transform(chunk));
+ callback();
+ }
+ });
+
+ return this.pipe(stream);
+ }
+ return this.pipe(new PassThrough({ objectMode: true }));
+};
+
+/**
+ * Execute the explain for the cursor
+ * @method
+ * @param {Cursor~resultCallback} [callback] The result callback.
+ * @return {Promise} returns Promise if no callback passed
+ */
+Cursor.prototype.explain = function(callback) {
+ this.s.cmd.explain = true;
+
+ // Do we have a readConcern
+ if (this.s.cmd.readConcern) {
+ delete this.s.cmd['readConcern'];
+ }
+
+ return executeOperation(this.s.topology, this._next.bind(this), [callback], {
+ skipSessions: true
+ });
+};
+
+Cursor.prototype._read = function() {
+ if (this.s.state === Cursor.CLOSED || this.isDead()) {
+ return this.push(null);
+ }
+
+ // Get the next item
+ this.next((err, result) => {
+ if (err) {
+ if (this.listeners('error') && this.listeners('error').length > 0) {
+ this.emit('error', err);
+ }
+ if (!this.isDead()) this.close();
+
+ // Emit end event
+ this.emit('end');
+ return this.emit('finish');
+ }
+
+ // If we provided a transformation method
+ if (typeof this.s.streamOptions.transform === 'function' && result != null) {
+ return this.push(this.s.streamOptions.transform(result));
+ }
+
+ // If we provided a map function
+ if (
+ this.cursorState.transforms &&
+ typeof this.cursorState.transforms.doc === 'function' &&
+ result != null
+ ) {
+ return this.push(this.cursorState.transforms.doc(result));
+ }
+
+ // Return the result
+ this.push(result);
+
+ if (result === null && this.isDead()) {
+ this.once('end', () => {
+ this.close();
+ this.emit('finish');
+ });
+ }
+ });
+};
+
+/**
+ * Return the cursor logger
+ * @method
+ * @return {Logger} return the cursor logger
+ * @ignore
+ */
+Cursor.prototype.getLogger = function() {
+ return this.logger;
+};
+
+Object.defineProperty(Cursor.prototype, 'readPreference', {
+ enumerable: true,
+ get: function() {
+ if (!this || !this.s) {
+ return null;
+ }
+
+ return this.s.options.readPreference;
+ }
+});
+
+Object.defineProperty(Cursor.prototype, 'namespace', {
+ enumerable: true,
+ get: function() {
+ if (!this || !this.s) {
+ return null;
+ }
+
+ // TODO: refactor this logic into core
+ const ns = this.s.ns || '';
+ const firstDot = ns.indexOf('.');
+ if (firstDot < 0) {
+ return {
+ database: this.s.ns,
+ collection: ''
+ };
+ }
+ return {
+ database: ns.substr(0, firstDot),
+ collection: ns.substr(firstDot + 1)
+ };
+ }
+});
+
+/**
+ * The read() method pulls some data out of the internal buffer and returns it. If there is no data available, then it will return null.
+ * @function external:Readable#read
+ * @param {number} size Optional argument to specify how much data to read.
+ * @return {(String | Buffer | null)}
+ */
+
+/**
+ * Call this function to cause the stream to return strings of the specified encoding instead of Buffer objects.
+ * @function external:Readable#setEncoding
+ * @param {string} encoding The encoding to use.
+ * @return {null}
+ */
+
+/**
+ * This method will cause the readable stream to resume emitting data events.
+ * @function external:Readable#resume
+ * @return {null}
+ */
+
+/**
+ * This method will cause a stream in flowing-mode to stop emitting data events. Any data that becomes available will remain in the internal buffer.
+ * @function external:Readable#pause
+ * @return {null}
+ */
+
+/**
+ * This method pulls all the data out of a readable stream, and writes it to the supplied destination, automatically managing the flow so that the destination is not overwhelmed by a fast readable stream.
+ * @function external:Readable#pipe
+ * @param {Writable} destination The destination for writing data
+ * @param {object} [options] Pipe options
+ * @return {null}
+ */
+
+/**
+ * This method will remove the hooks set up for a previous pipe() call.
+ * @function external:Readable#unpipe
+ * @param {Writable} [destination] The destination for writing data
+ * @return {null}
+ */
+
+/**
+ * This is useful in certain cases where a stream is being consumed by a parser, which needs to "un-consume" some data that it has optimistically pulled out of the source, so that the stream can be passed on to some other party.
+ * @function external:Readable#unshift
+ * @param {(Buffer|string)} chunk Chunk of data to unshift onto the read queue.
+ * @return {null}
+ */
+
+/**
+ * Versions of Node prior to v0.10 had streams that did not implement the entire Streams API as it is today. (See "Compatibility" below for more information.)
+ * @function external:Readable#wrap
+ * @param {Stream} stream An "old style" readable stream.
+ * @return {null}
+ */
+
+Cursor.INIT = 0;
+Cursor.OPEN = 1;
+Cursor.CLOSED = 2;
+Cursor.GET_MORE = 3;
+
+module.exports = Cursor;
diff --git a/node_modules/mongodb/lib/db.js b/node_modules/mongodb/lib/db.js
new file mode 100644
index 0000000..b3257f3
--- /dev/null
+++ b/node_modules/mongodb/lib/db.js
@@ -0,0 +1,985 @@
+'use strict';
+
+const EventEmitter = require('events').EventEmitter;
+const inherits = require('util').inherits;
+const getSingleProperty = require('./utils').getSingleProperty;
+const CommandCursor = require('./command_cursor');
+const handleCallback = require('./utils').handleCallback;
+const filterOptions = require('./utils').filterOptions;
+const toError = require('./utils').toError;
+const ReadPreference = require('mongodb-core').ReadPreference;
+const MongoError = require('mongodb-core').MongoError;
+const ObjectID = require('mongodb-core').ObjectID;
+const Logger = require('mongodb-core').Logger;
+const Collection = require('./collection');
+const mergeOptionsAndWriteConcern = require('./utils').mergeOptionsAndWriteConcern;
+const executeOperation = require('./utils').executeOperation;
+const applyWriteConcern = require('./utils').applyWriteConcern;
+const resolveReadPreference = require('./utils').resolveReadPreference;
+const ChangeStream = require('./change_stream');
+const deprecate = require('util').deprecate;
+const deprecateOptions = require('./utils').deprecateOptions;
+const CONSTANTS = require('./constants');
+
+// Operations
+const addUser = require('./operations/db_ops').addUser;
+const collections = require('./operations/db_ops').collections;
+const createCollection = require('./operations/db_ops').createCollection;
+const createIndex = require('./operations/db_ops').createIndex;
+const createListener = require('./operations/db_ops').createListener;
+const dropCollection = require('./operations/db_ops').dropCollection;
+const dropDatabase = require('./operations/db_ops').dropDatabase;
+const ensureIndex = require('./operations/db_ops').ensureIndex;
+const evaluate = require('./operations/db_ops').evaluate;
+const executeCommand = require('./operations/db_ops').executeCommand;
+const executeDbAdminCommand = require('./operations/db_ops').executeDbAdminCommand;
+const indexInformation = require('./operations/db_ops').indexInformation;
+const listCollectionsTransforms = require('./operations/db_ops').listCollectionsTransforms;
+const profilingInfo = require('./operations/db_ops').profilingInfo;
+const profilingLevel = require('./operations/db_ops').profilingLevel;
+const removeUser = require('./operations/db_ops').removeUser;
+const setProfilingLevel = require('./operations/db_ops').setProfilingLevel;
+const validateDatabaseName = require('./operations/db_ops').validateDatabaseName;
+
+/**
+ * @fileOverview The **Db** class is a class that represents a MongoDB Database.
+ *
+ * @example
+ * const MongoClient = require('mongodb').MongoClient;
+ * // Connection url
+ * const url = 'mongodb://localhost:27017';
+ * // Database Name
+ * const dbName = 'test';
+ * // Connect using MongoClient
+ * MongoClient.connect(url, function(err, client) {
+ * // Select the database by name
+ * const testDb = client.db(dbName);
+ * client.close();
+ * });
+ */
+
+// Allowed parameters
+const legalOptionNames = [
+ 'w',
+ 'wtimeout',
+ 'fsync',
+ 'j',
+ 'readPreference',
+ 'readPreferenceTags',
+ 'native_parser',
+ 'forceServerObjectId',
+ 'pkFactory',
+ 'serializeFunctions',
+ 'raw',
+ 'bufferMaxEntries',
+ 'authSource',
+ 'ignoreUndefined',
+ 'promoteLongs',
+ 'promiseLibrary',
+ 'readConcern',
+ 'retryMiliSeconds',
+ 'numberOfRetries',
+ 'parentDb',
+ 'noListener',
+ 'loggerLevel',
+ 'logger',
+ 'promoteBuffers',
+ 'promoteLongs',
+ 'promoteValues',
+ 'compression',
+ 'retryWrites'
+];
+
+/**
+ * Creates a new Db instance
+ * @class
+ * @param {string} databaseName The name of the database this instance represents.
+ * @param {(Server|ReplSet|Mongos)} topology The server topology for the database.
+ * @param {object} [options] Optional settings.
+ * @param {string} [options.authSource] If the database authentication is dependent on another databaseName.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
+ * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
+ * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
+ * @param {number} [options.bufferMaxEntries=-1] Sets a cap on how many operations the driver will buffer up before giving up on getting a working connection, default is -1 which is unlimited.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {object} [options.pkFactory] A primary key factory object for generation of custom _id keys.
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {object} [options.readConcern] Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
+ * @param {object} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
+ * @property {(Server|ReplSet|Mongos)} serverConfig Get the current db topology.
+ * @property {number} bufferMaxEntries Current bufferMaxEntries value for the database
+ * @property {string} databaseName The name of the database this instance represents.
+ * @property {object} options The options associated with the db instance.
+ * @property {boolean} native_parser The current value of the parameter native_parser.
+ * @property {boolean} slaveOk The current slaveOk value for the db instance.
+ * @property {object} writeConcern The current write concern values.
+ * @property {object} topology Access the topology object (single server, replicaset or mongos).
+ * @fires Db#close
+ * @fires Db#reconnect
+ * @fires Db#error
+ * @fires Db#timeout
+ * @fires Db#parseError
+ * @fires Db#fullsetup
+ * @return {Db} a Db instance.
+ */
+function Db(databaseName, topology, options) {
+ options = options || {};
+ if (!(this instanceof Db)) return new Db(databaseName, topology, options);
+ EventEmitter.call(this);
+
+ // Get the promiseLibrary
+ const promiseLibrary = options.promiseLibrary || Promise;
+
+ // Filter the options
+ options = filterOptions(options, legalOptionNames);
+
+ // Ensure we put the promiseLib in the options
+ options.promiseLibrary = promiseLibrary;
+
+ // Internal state of the db object
+ this.s = {
+ // Database name
+ databaseName: databaseName,
+ // DbCache
+ dbCache: {},
+ // Children db's
+ children: [],
+ // Topology
+ topology: topology,
+ // Options
+ options: options,
+ // Logger instance
+ logger: Logger('Db', options),
+ // Get the bson parser
+ bson: topology ? topology.bson : null,
+ // Unpack read preference
+ readPreference: options.readPreference,
+ // Set buffermaxEntries
+ bufferMaxEntries: typeof options.bufferMaxEntries === 'number' ? options.bufferMaxEntries : -1,
+ // Parent db (if chained)
+ parentDb: options.parentDb || null,
+ // Set up the primary key factory or fallback to ObjectID
+ pkFactory: options.pkFactory || ObjectID,
+ // Get native parser
+ nativeParser: options.nativeParser || options.native_parser,
+ // Promise library
+ promiseLibrary: promiseLibrary,
+ // No listener
+ noListener: typeof options.noListener === 'boolean' ? options.noListener : false,
+ // ReadConcern
+ readConcern: options.readConcern
+ };
+
+ // Ensure we have a valid db name
+ validateDatabaseName(this.s.databaseName);
+
+ // Add a read Only property
+ getSingleProperty(this, 'serverConfig', this.s.topology);
+ getSingleProperty(this, 'bufferMaxEntries', this.s.bufferMaxEntries);
+ getSingleProperty(this, 'databaseName', this.s.databaseName);
+
+ // This is a child db, do not register any listeners
+ if (options.parentDb) return;
+ if (this.s.noListener) return;
+
+ // Add listeners
+ topology.on('error', createListener(this, 'error', this));
+ topology.on('timeout', createListener(this, 'timeout', this));
+ topology.on('close', createListener(this, 'close', this));
+ topology.on('parseError', createListener(this, 'parseError', this));
+ topology.once('open', createListener(this, 'open', this));
+ topology.once('fullsetup', createListener(this, 'fullsetup', this));
+ topology.once('all', createListener(this, 'all', this));
+ topology.on('reconnect', createListener(this, 'reconnect', this));
+}
+
+inherits(Db, EventEmitter);
+
+// Topology
+Object.defineProperty(Db.prototype, 'topology', {
+ enumerable: true,
+ get: function() {
+ return this.s.topology;
+ }
+});
+
+// Options
+Object.defineProperty(Db.prototype, 'options', {
+ enumerable: true,
+ get: function() {
+ return this.s.options;
+ }
+});
+
+// slaveOk specified
+Object.defineProperty(Db.prototype, 'slaveOk', {
+ enumerable: true,
+ get: function() {
+ if (
+ this.s.options.readPreference != null &&
+ (this.s.options.readPreference !== 'primary' ||
+ this.s.options.readPreference.mode !== 'primary')
+ ) {
+ return true;
+ }
+ return false;
+ }
+});
+
+// get the write Concern
+Object.defineProperty(Db.prototype, 'writeConcern', {
+ enumerable: true,
+ get: function() {
+ const ops = {};
+ if (this.s.options.w != null) ops.w = this.s.options.w;
+ if (this.s.options.j != null) ops.j = this.s.options.j;
+ if (this.s.options.fsync != null) ops.fsync = this.s.options.fsync;
+ if (this.s.options.wtimeout != null) ops.wtimeout = this.s.options.wtimeout;
+ return ops;
+ }
+});
+
+/**
+ * Execute a command
+ * @method
+ * @param {object} command The command hash
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.command = function(command, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = Object.assign({}, options);
+
+ return executeOperation(this.s.topology, executeCommand, [this, command, options, callback]);
+};
+
+/**
+ * Return the Admin db instance
+ * @method
+ * @return {Admin} return the new Admin db instance
+ */
+Db.prototype.admin = function() {
+ const Admin = require('./admin');
+
+ return new Admin(this, this.s.topology, this.s.promiseLibrary);
+};
+
+/**
+ * The callback format for the collection method, must be used if strict is specified
+ * @callback Db~collectionResultCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {Collection} collection The collection instance.
+ */
+
+const collectionKeys = [
+ 'pkFactory',
+ 'readPreference',
+ 'serializeFunctions',
+ 'strict',
+ 'readConcern',
+ 'ignoreUndefined',
+ 'promoteValues',
+ 'promoteBuffers',
+ 'promoteLongs'
+];
+
+/**
+ * Fetch a specific collection (containing the actual collection information). If the application does not use strict mode you
+ * can use it without a callback in the following way: `const collection = db.collection('mycollection');`
+ *
+ * @method
+ * @param {string} name the collection name we wish to access.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
+ * @param {object} [options.pkFactory] A primary key factory object for generation of custom _id keys.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
+ * @param {boolean} [options.strict=false] Returns an error if the collection does not exist
+ * @param {object} [options.readConcern] Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
+ * @param {object} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
+ * @param {Db~collectionResultCallback} [callback] The collection result callback
+ * @return {Collection} return the new Collection instance if not in strict mode
+ */
+Db.prototype.collection = function(name, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+ options = Object.assign({}, options);
+
+ // Set the promise library
+ options.promiseLibrary = this.s.promiseLibrary;
+
+ // If we have not set a collection level readConcern set the db level one
+ options.readConcern = options.readConcern || this.s.readConcern;
+
+ // Do we have ignoreUndefined set
+ if (this.s.options.ignoreUndefined) {
+ options.ignoreUndefined = this.s.options.ignoreUndefined;
+ }
+
+ // Merge in all needed options and ensure correct writeConcern merging from db level
+ options = mergeOptionsAndWriteConcern(options, this.s.options, collectionKeys, true);
+
+ // Execute
+ if (options == null || !options.strict) {
+ try {
+ const collection = new Collection(
+ this,
+ this.s.topology,
+ this.s.databaseName,
+ name,
+ this.s.pkFactory,
+ options
+ );
+ if (callback) callback(null, collection);
+ return collection;
+ } catch (err) {
+ if (err instanceof MongoError && callback) return callback(err);
+ throw err;
+ }
+ }
+
+ // Strict mode
+ if (typeof callback !== 'function') {
+ throw toError(`A callback is required in strict mode. While getting collection ${name}`);
+ }
+
+ // Did the user destroy the topology
+ if (this.serverConfig && this.serverConfig.isDestroyed()) {
+ return callback(new MongoError('topology was destroyed'));
+ }
+
+ const listCollectionOptions = Object.assign({}, options, { nameOnly: true });
+
+ // Strict mode
+ this.listCollections({ name: name }, listCollectionOptions).toArray((err, collections) => {
+ if (err != null) return handleCallback(callback, err, null);
+ if (collections.length === 0)
+ return handleCallback(
+ callback,
+ toError(`Collection ${name} does not exist. Currently in strict mode.`),
+ null
+ );
+
+ try {
+ return handleCallback(
+ callback,
+ null,
+ new Collection(this, this.s.topology, this.s.databaseName, name, this.s.pkFactory, options)
+ );
+ } catch (err) {
+ return handleCallback(callback, err, null);
+ }
+ });
+};
+
+/**
+ * Create a new collection on a server with the specified options. Use this to create capped collections.
+ * More information about command options available at https://docs.mongodb.com/manual/reference/command/create/
+ *
+ * @method
+ * @param {string} name the collection name we wish to access.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
+ * @param {object} [options.pkFactory] A primary key factory object for generation of custom _id keys.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
+ * @param {boolean} [options.strict=false] Returns an error if the collection does not exist
+ * @param {boolean} [options.capped=false] Create a capped collection.
+ * @param {boolean} [options.autoIndexId=true] DEPRECATED: Create an index on the _id field of the document, True by default on MongoDB 2.6 - 3.0
+ * @param {number} [options.size] The size of the capped collection in bytes.
+ * @param {number} [options.max] The maximum number of documents in the capped collection.
+ * @param {number} [options.flags] Optional. Available for the MMAPv1 storage engine only to set the usePowerOf2Sizes and the noPadding flag.
+ * @param {object} [options.storageEngine] Allows users to specify configuration to the storage engine on a per-collection basis when creating a collection on MongoDB 3.0 or higher.
+ * @param {object} [options.validator] Allows users to specify validation rules or expressions for the collection. For more information, see Document Validation on MongoDB 3.2 or higher.
+ * @param {string} [options.validationLevel] Determines how strictly MongoDB applies the validation rules to existing documents during an update on MongoDB 3.2 or higher.
+ * @param {string} [options.validationAction] Determines whether to error on invalid documents or just warn about the violations but allow invalid documents to be inserted on MongoDB 3.2 or higher.
+ * @param {object} [options.indexOptionDefaults] Allows users to specify a default configuration for indexes when creating a collection on MongoDB 3.2 or higher.
+ * @param {string} [options.viewOn] The name of the source collection or view from which to create the view. The name is not the full namespace of the collection or view; i.e. does not include the database name and implies the same database as the view to create on MongoDB 3.4 or higher.
+ * @param {array} [options.pipeline] An array that consists of the aggregation pipeline stage. create creates the view by applying the specified pipeline to the viewOn collection or view on MongoDB 3.4 or higher.
+ * @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~collectionResultCallback} [callback] The results callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.createCollection = deprecateOptions(
+ {
+ name: 'Db.createCollection',
+ deprecatedOptions: ['autoIndexId'],
+ optionsIndex: 1
+ },
+ function(name, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+ options.promiseLibrary = options.promiseLibrary || this.s.promiseLibrary;
+
+ return executeOperation(this.s.topology, createCollection, [this, name, options, callback]);
+ }
+);
+
+/**
+ * Get all the db statistics.
+ *
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {number} [options.scale] Divide the returned sizes by scale value.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The collection result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.stats = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+ // Build command object
+ const commandObject = { dbStats: true };
+ // Check if we have the scale value
+ if (options['scale'] != null) commandObject['scale'] = options['scale'];
+
+ // If we have a readPreference set
+ if (options.readPreference == null && this.s.readPreference) {
+ options.readPreference = this.s.readPreference;
+ }
+
+ // Execute the command
+ return this.command(commandObject, options, callback);
+};
+
+/**
+ * Get the list of all collection information for the specified db.
+ *
+ * @method
+ * @param {object} [filter={}] Query to filter collections by
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.nameOnly=false] Since 4.0: If true, will only return the collection name in the response, and will omit additional info
+ * @param {number} [options.batchSize] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @return {CommandCursor}
+ */
+Db.prototype.listCollections = function(filter, options) {
+ filter = filter || {};
+ options = options || {};
+
+ // Shallow clone the object
+ options = Object.assign({}, options);
+ // Set the promise library
+ options.promiseLibrary = this.s.promiseLibrary;
+
+ // Ensure valid readPreference
+ options.readPreference = resolveReadPreference(options, {
+ db: this,
+ default: ReadPreference.primary
+ });
+
+ // Cursor options
+ let cursor = options.batchSize ? { batchSize: options.batchSize } : {};
+
+ // We have a list collections command
+ if (this.serverConfig.capabilities().hasListCollectionsCommand) {
+ const nameOnly = typeof options.nameOnly === 'boolean' ? options.nameOnly : false;
+ // Build the command
+ const command = { listCollections: true, filter, cursor, nameOnly };
+ // Set the AggregationCursor constructor
+ options.cursorFactory = CommandCursor;
+ // Create the cursor
+ cursor = this.s.topology.cursor(`${this.s.databaseName}.$cmd`, command, options);
+ // Do we have a readPreference, apply it
+ if (options.readPreference) {
+ cursor.setReadPreference(options.readPreference);
+ }
+ // Return the cursor
+ return cursor;
+ }
+
+ // We cannot use the listCollectionsCommand
+ if (!this.serverConfig.capabilities().hasListCollectionsCommand) {
+ // If we have legacy mode and have not provided a full db name filter it
+ if (
+ typeof filter.name === 'string' &&
+ !new RegExp('^' + this.databaseName + '\\.').test(filter.name)
+ ) {
+ filter = Object.assign({}, filter);
+ filter.name = `${this.s.databaseName}.${filter.name}`;
+ }
+ }
+
+ // No filter, filter by current database
+ if (filter == null) {
+ filter.name = `/${this.s.databaseName}/`;
+ }
+
+ // Rewrite the filter to use $and to filter out indexes
+ if (filter.name) {
+ filter = { $and: [{ name: filter.name }, { name: /^((?!\$).)*$/ }] };
+ } else {
+ filter = { name: /^((?!\$).)*$/ };
+ }
+
+ // Return options
+ const _options = { transforms: listCollectionsTransforms(this.s.databaseName) };
+ // Get the cursor
+ cursor = this.collection(CONSTANTS.SYSTEM_NAMESPACE_COLLECTION).find(filter, _options);
+ // Do we have a readPreference, apply it
+ if (options.readPreference) cursor.setReadPreference(options.readPreference);
+ // Set the passed in batch size if one was provided
+ if (options.batchSize) cursor = cursor.batchSize(options.batchSize);
+ // We have a fallback mode using legacy systems collections
+ return cursor;
+};
+
+/**
+ * Evaluate JavaScript on the server
+ *
+ * @method
+ * @param {Code} code JavaScript to execute on server.
+ * @param {(object|array)} parameters The parameters for the call.
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.nolock=false] Tell MongoDB not to block on the evaulation of the javascript.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The results callback
+ * @deprecated Eval is deprecated on MongoDB 3.2 and forward
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.eval = deprecate(function(code, parameters, options, callback) {
+ const args = Array.prototype.slice.call(arguments, 1);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ parameters = args.length ? args.shift() : parameters;
+ options = args.length ? args.shift() || {} : {};
+
+ return executeOperation(this.s.topology, evaluate, [this, code, parameters, options, callback]);
+}, 'Db.eval is deprecated as of MongoDB version 3.2');
+
+/**
+ * Rename a collection.
+ *
+ * @method
+ * @param {string} fromCollection Name of current collection to rename.
+ * @param {string} toCollection New name of of the collection.
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.dropTarget=false] Drop the target name collection if it previously exists.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~collectionResultCallback} [callback] The results callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.renameCollection = function(fromCollection, toCollection, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+ // Add return new collection
+ options.new_collection = true;
+
+ const collection = this.collection(fromCollection);
+ return executeOperation(this.s.topology, collection.rename.bind(collection), [
+ toCollection,
+ options,
+ callback
+ ]);
+};
+
+/**
+ * Drop a collection from the database, removing it permanently. New accesses will create a new collection.
+ *
+ * @method
+ * @param {string} name Name of collection to drop
+ * @param {Object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The results callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.dropCollection = function(name, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ // Command to execute
+ const cmd = { drop: name };
+
+ // Decorate with write concern
+ applyWriteConcern(cmd, { db: this }, options);
+
+ // options
+ const opts = Object.assign({}, this.s.options, { readPreference: ReadPreference.PRIMARY });
+ if (options.session) opts.session = options.session;
+
+ return executeOperation(this.s.topology, dropCollection, [this, cmd, opts, callback]);
+};
+
+/**
+ * Drop a database, removing it permanently from the server.
+ *
+ * @method
+ * @param {Object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The results callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.dropDatabase = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+ // Drop database command
+ const cmd = { dropDatabase: 1 };
+
+ // Decorate with write concern
+ applyWriteConcern(cmd, { db: this }, options);
+
+ // Ensure primary only
+ const finalOptions = Object.assign({}, this.s.options, {
+ readPreference: ReadPreference.PRIMARY
+ });
+
+ if (options.session) {
+ finalOptions.session = options.session;
+ }
+
+ return executeOperation(this.s.topology, dropDatabase, [this, cmd, finalOptions, callback]);
+};
+
+/**
+ * Fetch all collections for the current db.
+ *
+ * @method
+ * @param {Object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~collectionsResultCallback} [callback] The results callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.collections = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, collections, [this, options, callback]);
+};
+
+/**
+ * Runs a command on the database as admin.
+ * @method
+ * @param {object} command The command hash
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+ options.readPreference = resolveReadPreference(options);
+
+ return executeOperation(this.s.topology, executeDbAdminCommand, [
+ this,
+ selector,
+ options,
+ callback
+ ]);
+};
+
+/**
+ * Creates an index on the db and collection.
+ * @method
+ * @param {string} name Name of the collection to create the index on.
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {boolean} [options.unique=false] Creates an unique index.
+ * @param {boolean} [options.sparse=false] Creates a sparse index.
+ * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
+ * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
+ * @param {number} [options.min] For geospatial indexes set the lower bound for the co-ordinates.
+ * @param {number} [options.max] For geospatial indexes set the high bound for the co-ordinates.
+ * @param {number} [options.v] Specify the format version of the indexes.
+ * @param {number} [options.expireAfterSeconds] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
+ * @param {number} [options.name] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
+ * @param {object} [options.partialFilterExpression] Creates a partial index based on the given filter object (MongoDB 3.2 or higher)
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.createIndex = function(name, fieldOrSpec, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options ? Object.assign({}, options) : {};
+
+ return executeOperation(this.s.topology, createIndex, [
+ this,
+ name,
+ fieldOrSpec,
+ options,
+ callback
+ ]);
+};
+
+/**
+ * Ensures that an index exists, if it does not it creates it
+ * @method
+ * @deprecated since version 2.0
+ * @param {string} name The index name
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {boolean} [options.unique=false] Creates an unique index.
+ * @param {boolean} [options.sparse=false] Creates a sparse index.
+ * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
+ * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
+ * @param {number} [options.min] For geospatial indexes set the lower bound for the co-ordinates.
+ * @param {number} [options.max] For geospatial indexes set the high bound for the co-ordinates.
+ * @param {number} [options.v] Specify the format version of the indexes.
+ * @param {number} [options.expireAfterSeconds] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
+ * @param {number} [options.name] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.ensureIndex = deprecate(function(name, fieldOrSpec, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, ensureIndex, [
+ this,
+ name,
+ fieldOrSpec,
+ options,
+ callback
+ ]);
+}, 'Db.ensureIndex is deprecated as of MongoDB version 3.0 / driver version 2.0');
+
+Db.prototype.addChild = function(db) {
+ if (this.s.parentDb) return this.s.parentDb.addChild(db);
+ this.s.children.push(db);
+};
+
+/**
+ * Add a user to the database.
+ * @method
+ * @param {string} username The username.
+ * @param {string} password The password.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {object} [options.customData] Custom data associated with the user (only Mongodb 2.6 or higher)
+ * @param {object[]} [options.roles] Roles associated with the created user (only Mongodb 2.6 or higher)
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.addUser = function(username, password, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, addUser, [this, username, password, options, callback]);
+};
+
+/**
+ * Remove a user from a database
+ * @method
+ * @param {string} username The username.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.removeUser = function(username, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, removeUser, [this, username, options, callback]);
+};
+
+/**
+ * Set the current profiling level of MongoDB
+ *
+ * @param {string} level The new profiling level (off, slow_only, all).
+ * @param {Object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback.
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.setProfilingLevel = function(level, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, setProfilingLevel, [this, level, options, callback]);
+};
+
+/**
+ * Retrive the current profiling information for MongoDB
+ *
+ * @param {Object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Query the system.profile collection directly.
+ */
+Db.prototype.profilingInfo = deprecate(function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, profilingInfo, [this, options, callback]);
+}, 'Db.profilingInfo is deprecated. Query the system.profile collection directly.');
+
+/**
+ * Retrieve the current profiling Level for MongoDB
+ *
+ * @param {Object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.profilingLevel = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, profilingLevel, [this, options, callback]);
+};
+
+/**
+ * Retrieves this collections index info.
+ * @method
+ * @param {string} name The name of the collection.
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.full=false] Returns the full raw index information.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+Db.prototype.indexInformation = function(name, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.s.topology, indexInformation, [this, name, options, callback]);
+};
+
+/**
+ * Unref all sockets
+ * @method
+ */
+Db.prototype.unref = function() {
+ this.s.topology.unref();
+};
+
+/**
+ * Create a new Change Stream, watching for new changes (insertions, updates, replacements, deletions, and invalidations) in this database. Will ignore all changes to system collections.
+ * @method
+ * @since 3.1.0
+ * @param {Array} [pipeline] An array of {@link https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.
+ * @param {object} [options] Optional settings
+ * @param {string} [options.fullDocument='default'] Allowed values: ‘default’, ‘updateLookup’. When set to ‘updateLookup’, the change stream will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
+ * @param {object} [options.resumeAfter] Specifies the logical starting point for the new change stream. This should be the _id field from a previously returned change stream document.
+ * @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query
+ * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
+ * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
+ * @param {ReadPreference} [options.readPreference] The read preference. Defaults to the read preference of the database. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
+ * @param {Timestamp} [options.startAtClusterTime] receive change events that occur after the specified timestamp
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @return {ChangeStream} a ChangeStream instance.
+ */
+Db.prototype.watch = function(pipeline, options) {
+ pipeline = pipeline || [];
+ options = options || {};
+
+ // Allow optionally not specifying a pipeline
+ if (!Array.isArray(pipeline)) {
+ options = pipeline;
+ pipeline = [];
+ }
+
+ return new ChangeStream(this, pipeline, options);
+};
+
+/**
+ * Return the db logger
+ * @method
+ * @return {Logger} return the db logger
+ * @ignore
+ */
+Db.prototype.getLogger = function() {
+ return this.s.logger;
+};
+
+/**
+ * Db close event
+ *
+ * Emitted after a socket closed against a single server or mongos proxy.
+ *
+ * @event Db#close
+ * @type {MongoError}
+ */
+
+/**
+ * Db reconnect event
+ *
+ * * Server: Emitted when the driver has reconnected and re-authenticated.
+ * * ReplicaSet: N/A
+ * * Mongos: Emitted when the driver reconnects and re-authenticates successfully against a Mongos.
+ *
+ * @event Db#reconnect
+ * @type {object}
+ */
+
+/**
+ * Db error event
+ *
+ * Emitted after an error occurred against a single server or mongos proxy.
+ *
+ * @event Db#error
+ * @type {MongoError}
+ */
+
+/**
+ * Db timeout event
+ *
+ * Emitted after a socket timeout occurred against a single server or mongos proxy.
+ *
+ * @event Db#timeout
+ * @type {MongoError}
+ */
+
+/**
+ * Db parseError event
+ *
+ * The parseError event is emitted if the driver detects illegal or corrupt BSON being received from the server.
+ *
+ * @event Db#parseError
+ * @type {MongoError}
+ */
+
+/**
+ * Db fullsetup event, emitted when all servers in the topology have been connected to at start up time.
+ *
+ * * Server: Emitted when the driver has connected to the single server and has authenticated.
+ * * ReplSet: Emitted after the driver has attempted to connect to all replicaset members.
+ * * Mongos: Emitted after the driver has attempted to connect to all mongos proxies.
+ *
+ * @event Db#fullsetup
+ * @type {Db}
+ */
+
+// Constants
+Db.SYSTEM_NAMESPACE_COLLECTION = CONSTANTS.SYSTEM_NAMESPACE_COLLECTION;
+Db.SYSTEM_INDEX_COLLECTION = CONSTANTS.SYSTEM_INDEX_COLLECTION;
+Db.SYSTEM_PROFILE_COLLECTION = CONSTANTS.SYSTEM_PROFILE_COLLECTION;
+Db.SYSTEM_USER_COLLECTION = CONSTANTS.SYSTEM_USER_COLLECTION;
+Db.SYSTEM_COMMAND_COLLECTION = CONSTANTS.SYSTEM_COMMAND_COLLECTION;
+Db.SYSTEM_JS_COLLECTION = CONSTANTS.SYSTEM_JS_COLLECTION;
+
+module.exports = Db;
diff --git a/node_modules/mongodb/lib/error.js b/node_modules/mongodb/lib/error.js
new file mode 100644
index 0000000..03b555a
--- /dev/null
+++ b/node_modules/mongodb/lib/error.js
@@ -0,0 +1,43 @@
+'use strict';
+
+const MongoNetworkError = require('mongodb-core').MongoNetworkError;
+const mongoErrorContextSymbol = require('mongodb-core').mongoErrorContextSymbol;
+
+const GET_MORE_NON_RESUMABLE_CODES = new Set([
+ 136, // CappedPositionLost
+ 237, // CursorKilled
+ 11601 // Interrupted
+]);
+
+// From spec@https://github.com/mongodb/specifications/blob/35e466ddf25059cb30e4113de71cdebd3754657f/source/change-streams.rst#resumable-error:
+//
+// An error is considered resumable if it meets any of the following criteria:
+// - any error encountered which is not a server error (e.g. a timeout error or network error)
+// - any server error response from a getMore command excluding those containing the following error codes
+// - Interrupted: 11601
+// - CappedPositionLost: 136
+// - CursorKilled: 237
+// - a server error response with an error message containing the substring "not master" or "node is recovering"
+//
+// An error on an aggregate command is not a resumable error. Only errors on a getMore command may be considered resumable errors.
+
+function isGetMoreError(error) {
+ if (error[mongoErrorContextSymbol]) {
+ return error[mongoErrorContextSymbol].isGetMore;
+ }
+}
+
+function isResumableError(error) {
+ if (!isGetMoreError(error)) {
+ return false;
+ }
+
+ return !!(
+ error instanceof MongoNetworkError ||
+ !GET_MORE_NON_RESUMABLE_CODES.has(error.code) ||
+ error.message.match(/not master/) ||
+ error.message.match(/node is recovering/)
+ );
+}
+
+module.exports = { GET_MORE_NON_RESUMABLE_CODES, isResumableError };
diff --git a/node_modules/mongodb/lib/gridfs-stream/download.js b/node_modules/mongodb/lib/gridfs-stream/download.js
new file mode 100644
index 0000000..9012602
--- /dev/null
+++ b/node_modules/mongodb/lib/gridfs-stream/download.js
@@ -0,0 +1,415 @@
+'use strict';
+
+var stream = require('stream'),
+ util = require('util');
+
+module.exports = GridFSBucketReadStream;
+
+/**
+ * A readable stream that enables you to read buffers from GridFS.
+ *
+ * Do not instantiate this class directly. Use `openDownloadStream()` instead.
+ *
+ * @class
+ * @param {Collection} chunks Handle for chunks collection
+ * @param {Collection} files Handle for files collection
+ * @param {Object} readPreference The read preference to use
+ * @param {Object} filter The query to use to find the file document
+ * @param {Object} [options] Optional settings.
+ * @param {Number} [options.sort] Optional sort for the file find query
+ * @param {Number} [options.skip] Optional skip for the file find query
+ * @param {Number} [options.start] Optional 0-based offset in bytes to start streaming from
+ * @param {Number} [options.end] Optional 0-based offset in bytes to stop streaming before
+ * @fires GridFSBucketReadStream#error
+ * @fires GridFSBucketReadStream#file
+ * @return {GridFSBucketReadStream} a GridFSBucketReadStream instance.
+ */
+
+function GridFSBucketReadStream(chunks, files, readPreference, filter, options) {
+ this.s = {
+ bytesRead: 0,
+ chunks: chunks,
+ cursor: null,
+ expected: 0,
+ files: files,
+ filter: filter,
+ init: false,
+ expectedEnd: 0,
+ file: null,
+ options: options,
+ readPreference: readPreference
+ };
+
+ stream.Readable.call(this);
+}
+
+util.inherits(GridFSBucketReadStream, stream.Readable);
+
+/**
+ * An error occurred
+ *
+ * @event GridFSBucketReadStream#error
+ * @type {Error}
+ */
+
+/**
+ * Fires when the stream loaded the file document corresponding to the
+ * provided id.
+ *
+ * @event GridFSBucketReadStream#file
+ * @type {object}
+ */
+
+/**
+ * Emitted when a chunk of data is available to be consumed.
+ *
+ * @event GridFSBucketReadStream#data
+ * @type {object}
+ */
+
+/**
+ * Fired when the stream is exhausted (no more data events).
+ *
+ * @event GridFSBucketReadStream#end
+ * @type {object}
+ */
+
+/**
+ * Fired when the stream is exhausted and the underlying cursor is killed
+ *
+ * @event GridFSBucketReadStream#close
+ * @type {object}
+ */
+
+/**
+ * Reads from the cursor and pushes to the stream.
+ * @method
+ */
+
+GridFSBucketReadStream.prototype._read = function() {
+ var _this = this;
+ if (this.destroyed) {
+ return;
+ }
+
+ waitForFile(_this, function() {
+ doRead(_this);
+ });
+};
+
+/**
+ * Sets the 0-based offset in bytes to start streaming from. Throws
+ * an error if this stream has entered flowing mode
+ * (e.g. if you've already called `on('data')`)
+ * @method
+ * @param {Number} start Offset in bytes to start reading at
+ * @return {GridFSBucketReadStream}
+ */
+
+GridFSBucketReadStream.prototype.start = function(start) {
+ throwIfInitialized(this);
+ this.s.options.start = start;
+ return this;
+};
+
+/**
+ * Sets the 0-based offset in bytes to start streaming from. Throws
+ * an error if this stream has entered flowing mode
+ * (e.g. if you've already called `on('data')`)
+ * @method
+ * @param {Number} end Offset in bytes to stop reading at
+ * @return {GridFSBucketReadStream}
+ */
+
+GridFSBucketReadStream.prototype.end = function(end) {
+ throwIfInitialized(this);
+ this.s.options.end = end;
+ return this;
+};
+
+/**
+ * Marks this stream as aborted (will never push another `data` event)
+ * and kills the underlying cursor. Will emit the 'end' event, and then
+ * the 'close' event once the cursor is successfully killed.
+ *
+ * @method
+ * @param {GridFSBucket~errorCallback} [callback] called when the cursor is successfully closed or an error occurred.
+ * @fires GridFSBucketWriteStream#close
+ * @fires GridFSBucketWriteStream#end
+ */
+
+GridFSBucketReadStream.prototype.abort = function(callback) {
+ var _this = this;
+ this.push(null);
+ this.destroyed = true;
+ if (this.s.cursor) {
+ this.s.cursor.close(function(error) {
+ _this.emit('close');
+ callback && callback(error);
+ });
+ } else {
+ if (!this.s.init) {
+ // If not initialized, fire close event because we will never
+ // get a cursor
+ _this.emit('close');
+ }
+ callback && callback();
+ }
+};
+
+/**
+ * @ignore
+ */
+
+function throwIfInitialized(self) {
+ if (self.s.init) {
+ throw new Error('You cannot change options after the stream has entered' + 'flowing mode!');
+ }
+}
+
+/**
+ * @ignore
+ */
+
+function doRead(_this) {
+ if (_this.destroyed) {
+ return;
+ }
+
+ _this.s.cursor.next(function(error, doc) {
+ if (_this.destroyed) {
+ return;
+ }
+ if (error) {
+ return __handleError(_this, error);
+ }
+ if (!doc) {
+ _this.push(null);
+ return _this.s.cursor.close(function(error) {
+ if (error) {
+ return __handleError(_this, error);
+ }
+ _this.emit('close');
+ });
+ }
+
+ var bytesRemaining = _this.s.file.length - _this.s.bytesRead;
+ var expectedN = _this.s.expected++;
+ var expectedLength = Math.min(_this.s.file.chunkSize, bytesRemaining);
+
+ if (doc.n > expectedN) {
+ var errmsg = 'ChunkIsMissing: Got unexpected n: ' + doc.n + ', expected: ' + expectedN;
+ return __handleError(_this, new Error(errmsg));
+ }
+
+ if (doc.n < expectedN) {
+ errmsg = 'ExtraChunk: Got unexpected n: ' + doc.n + ', expected: ' + expectedN;
+ return __handleError(_this, new Error(errmsg));
+ }
+
+ var buf = Buffer.isBuffer(doc.data) ? doc.data : doc.data.buffer;
+
+ if (buf.length !== expectedLength) {
+ if (bytesRemaining <= 0) {
+ errmsg = 'ExtraChunk: Got unexpected n: ' + doc.n;
+ return __handleError(_this, new Error(errmsg));
+ }
+
+ errmsg =
+ 'ChunkIsWrongSize: Got unexpected length: ' + buf.length + ', expected: ' + expectedLength;
+ return __handleError(_this, new Error(errmsg));
+ }
+
+ _this.s.bytesRead += buf.length;
+
+ if (buf.length === 0) {
+ return _this.push(null);
+ }
+
+ var sliceStart = null;
+ var sliceEnd = null;
+
+ if (_this.s.bytesToSkip != null) {
+ sliceStart = _this.s.bytesToSkip;
+ _this.s.bytesToSkip = 0;
+ }
+
+ if (expectedN === _this.s.expectedEnd && _this.s.bytesToTrim != null) {
+ sliceEnd = _this.s.bytesToTrim;
+ }
+
+ // If the remaining amount of data left is < chunkSize read the right amount of data
+ if (_this.s.options.end && _this.s.options.end - _this.s.bytesToSkip < buf.length) {
+ sliceEnd = _this.s.options.end - _this.s.bytesToSkip;
+ }
+
+ if (sliceStart != null || sliceEnd != null) {
+ buf = buf.slice(sliceStart || 0, sliceEnd || buf.length);
+ }
+
+ _this.push(buf);
+ });
+}
+
+/**
+ * @ignore
+ */
+
+function init(self) {
+ var findOneOptions = {};
+ if (self.s.readPreference) {
+ findOneOptions.readPreference = self.s.readPreference;
+ }
+ if (self.s.options && self.s.options.sort) {
+ findOneOptions.sort = self.s.options.sort;
+ }
+ if (self.s.options && self.s.options.skip) {
+ findOneOptions.skip = self.s.options.skip;
+ }
+
+ self.s.files.findOne(self.s.filter, findOneOptions, function(error, doc) {
+ if (error) {
+ return __handleError(self, error);
+ }
+ if (!doc) {
+ var identifier = self.s.filter._id ? self.s.filter._id.toString() : self.s.filter.filename;
+ var errmsg = 'FileNotFound: file ' + identifier + ' was not found';
+ var err = new Error(errmsg);
+ err.code = 'ENOENT';
+ return __handleError(self, err);
+ }
+
+ // If document is empty, kill the stream immediately and don't
+ // execute any reads
+ if (doc.length <= 0) {
+ self.push(null);
+ return;
+ }
+
+ if (self.destroyed) {
+ // If user destroys the stream before we have a cursor, wait
+ // until the query is done to say we're 'closed' because we can't
+ // cancel a query.
+ self.emit('close');
+ return;
+ }
+
+ self.s.bytesToSkip = handleStartOption(self, doc, self.s.options);
+
+ var filter = { files_id: doc._id };
+
+ // Currently (MongoDB 3.4.4) skip function does not support the index,
+ // it needs to retrieve all the documents first and then skip them. (CS-25811)
+ // As work around we use $gte on the "n" field.
+ if (self.s.options && self.s.options.start != null) {
+ var skip = Math.floor(self.s.options.start / doc.chunkSize);
+ if (skip > 0) {
+ filter['n'] = { $gte: skip };
+ }
+ }
+ self.s.cursor = self.s.chunks.find(filter).sort({ n: 1 });
+
+ if (self.s.readPreference) {
+ self.s.cursor.setReadPreference(self.s.readPreference);
+ }
+
+ self.s.expectedEnd = Math.ceil(doc.length / doc.chunkSize);
+ self.s.file = doc;
+ self.s.bytesToTrim = handleEndOption(self, doc, self.s.cursor, self.s.options);
+ self.emit('file', doc);
+ });
+}
+
+/**
+ * @ignore
+ */
+
+function waitForFile(_this, callback) {
+ if (_this.s.file) {
+ return callback();
+ }
+
+ if (!_this.s.init) {
+ init(_this);
+ _this.s.init = true;
+ }
+
+ _this.once('file', function() {
+ callback();
+ });
+}
+
+/**
+ * @ignore
+ */
+
+function handleStartOption(stream, doc, options) {
+ if (options && options.start != null) {
+ if (options.start > doc.length) {
+ throw new Error(
+ 'Stream start (' +
+ options.start +
+ ') must not be ' +
+ 'more than the length of the file (' +
+ doc.length +
+ ')'
+ );
+ }
+ if (options.start < 0) {
+ throw new Error('Stream start (' + options.start + ') must not be ' + 'negative');
+ }
+ if (options.end != null && options.end < options.start) {
+ throw new Error(
+ 'Stream start (' +
+ options.start +
+ ') must not be ' +
+ 'greater than stream end (' +
+ options.end +
+ ')'
+ );
+ }
+
+ stream.s.bytesRead = Math.floor(options.start / doc.chunkSize) * doc.chunkSize;
+ stream.s.expected = Math.floor(options.start / doc.chunkSize);
+
+ return options.start - stream.s.bytesRead;
+ }
+}
+
+/**
+ * @ignore
+ */
+
+function handleEndOption(stream, doc, cursor, options) {
+ if (options && options.end != null) {
+ if (options.end > doc.length) {
+ throw new Error(
+ 'Stream end (' +
+ options.end +
+ ') must not be ' +
+ 'more than the length of the file (' +
+ doc.length +
+ ')'
+ );
+ }
+ if (options.start < 0) {
+ throw new Error('Stream end (' + options.end + ') must not be ' + 'negative');
+ }
+
+ var start = options.start != null ? Math.floor(options.start / doc.chunkSize) : 0;
+
+ cursor.limit(Math.ceil(options.end / doc.chunkSize) - start);
+
+ stream.s.expectedEnd = Math.ceil(options.end / doc.chunkSize);
+
+ return Math.ceil(options.end / doc.chunkSize) * doc.chunkSize - options.end;
+ }
+}
+
+/**
+ * @ignore
+ */
+
+function __handleError(_this, error) {
+ _this.emit('error', error);
+}
diff --git a/node_modules/mongodb/lib/gridfs-stream/index.js b/node_modules/mongodb/lib/gridfs-stream/index.js
new file mode 100644
index 0000000..9d64a84
--- /dev/null
+++ b/node_modules/mongodb/lib/gridfs-stream/index.js
@@ -0,0 +1,358 @@
+'use strict';
+
+var Emitter = require('events').EventEmitter;
+var GridFSBucketReadStream = require('./download');
+var GridFSBucketWriteStream = require('./upload');
+var shallowClone = require('../utils').shallowClone;
+var toError = require('../utils').toError;
+var util = require('util');
+var executeOperation = require('../utils').executeOperation;
+
+var DEFAULT_GRIDFS_BUCKET_OPTIONS = {
+ bucketName: 'fs',
+ chunkSizeBytes: 255 * 1024
+};
+
+module.exports = GridFSBucket;
+
+/**
+ * Constructor for a streaming GridFS interface
+ * @class
+ * @param {Db} db A db handle
+ * @param {object} [options] Optional settings.
+ * @param {string} [options.bucketName="fs"] The 'files' and 'chunks' collections will be prefixed with the bucket name followed by a dot.
+ * @param {number} [options.chunkSizeBytes=255 * 1024] Number of bytes stored in each chunk. Defaults to 255KB
+ * @param {object} [options.writeConcern] Optional write concern to be passed to write operations, for instance `{ w: 1 }`
+ * @param {object} [options.readPreference] Optional read preference to be passed to read operations
+ * @fires GridFSBucketWriteStream#index
+ * @return {GridFSBucket}
+ */
+
+function GridFSBucket(db, options) {
+ Emitter.apply(this);
+ this.setMaxListeners(0);
+
+ if (options && typeof options === 'object') {
+ options = shallowClone(options);
+ var keys = Object.keys(DEFAULT_GRIDFS_BUCKET_OPTIONS);
+ for (var i = 0; i < keys.length; ++i) {
+ if (!options[keys[i]]) {
+ options[keys[i]] = DEFAULT_GRIDFS_BUCKET_OPTIONS[keys[i]];
+ }
+ }
+ } else {
+ options = DEFAULT_GRIDFS_BUCKET_OPTIONS;
+ }
+
+ this.s = {
+ db: db,
+ options: options,
+ _chunksCollection: db.collection(options.bucketName + '.chunks'),
+ _filesCollection: db.collection(options.bucketName + '.files'),
+ checkedIndexes: false,
+ calledOpenUploadStream: false,
+ promiseLibrary: db.s.promiseLibrary || Promise
+ };
+}
+
+util.inherits(GridFSBucket, Emitter);
+
+/**
+ * When the first call to openUploadStream is made, the upload stream will
+ * check to see if it needs to create the proper indexes on the chunks and
+ * files collections. This event is fired either when 1) it determines that
+ * no index creation is necessary, 2) when it successfully creates the
+ * necessary indexes.
+ *
+ * @event GridFSBucket#index
+ * @type {Error}
+ */
+
+/**
+ * Returns a writable stream (GridFSBucketWriteStream) for writing
+ * buffers to GridFS. The stream's 'id' property contains the resulting
+ * file's id.
+ * @method
+ * @param {string} filename The value of the 'filename' key in the files doc
+ * @param {object} [options] Optional settings.
+ * @param {number} [options.chunkSizeBytes] Optional overwrite this bucket's chunkSizeBytes for this file
+ * @param {object} [options.metadata] Optional object to store in the file document's `metadata` field
+ * @param {string} [options.contentType] Optional string to store in the file document's `contentType` field
+ * @param {array} [options.aliases] Optional array of strings to store in the file document's `aliases` field
+ * @param {boolean} [options.disableMD5=false] If true, disables adding an md5 field to file data
+ * @return {GridFSBucketWriteStream}
+ */
+
+GridFSBucket.prototype.openUploadStream = function(filename, options) {
+ if (options) {
+ options = shallowClone(options);
+ } else {
+ options = {};
+ }
+ if (!options.chunkSizeBytes) {
+ options.chunkSizeBytes = this.s.options.chunkSizeBytes;
+ }
+ return new GridFSBucketWriteStream(this, filename, options);
+};
+
+/**
+ * Returns a writable stream (GridFSBucketWriteStream) for writing
+ * buffers to GridFS for a custom file id. The stream's 'id' property contains the resulting
+ * file's id.
+ * @method
+ * @param {string|number|object} id A custom id used to identify the file
+ * @param {string} filename The value of the 'filename' key in the files doc
+ * @param {object} [options] Optional settings.
+ * @param {number} [options.chunkSizeBytes] Optional overwrite this bucket's chunkSizeBytes for this file
+ * @param {object} [options.metadata] Optional object to store in the file document's `metadata` field
+ * @param {string} [options.contentType] Optional string to store in the file document's `contentType` field
+ * @param {array} [options.aliases] Optional array of strings to store in the file document's `aliases` field
+ * @param {boolean} [options.disableMD5=false] If true, disables adding an md5 field to file data
+ * @return {GridFSBucketWriteStream}
+ */
+
+GridFSBucket.prototype.openUploadStreamWithId = function(id, filename, options) {
+ if (options) {
+ options = shallowClone(options);
+ } else {
+ options = {};
+ }
+
+ if (!options.chunkSizeBytes) {
+ options.chunkSizeBytes = this.s.options.chunkSizeBytes;
+ }
+
+ options.id = id;
+
+ return new GridFSBucketWriteStream(this, filename, options);
+};
+
+/**
+ * Returns a readable stream (GridFSBucketReadStream) for streaming file
+ * data from GridFS.
+ * @method
+ * @param {ObjectId} id The id of the file doc
+ * @param {Object} [options] Optional settings.
+ * @param {Number} [options.start] Optional 0-based offset in bytes to start streaming from
+ * @param {Number} [options.end] Optional 0-based offset in bytes to stop streaming before
+ * @return {GridFSBucketReadStream}
+ */
+
+GridFSBucket.prototype.openDownloadStream = function(id, options) {
+ var filter = { _id: id };
+ options = {
+ start: options && options.start,
+ end: options && options.end
+ };
+
+ return new GridFSBucketReadStream(
+ this.s._chunksCollection,
+ this.s._filesCollection,
+ this.s.options.readPreference,
+ filter,
+ options
+ );
+};
+
+/**
+ * Deletes a file with the given id
+ * @method
+ * @param {ObjectId} id The id of the file doc
+ * @param {GridFSBucket~errorCallback} [callback]
+ */
+
+GridFSBucket.prototype.delete = function(id, callback) {
+ return executeOperation(this.s.db.s.topology, _delete, [this, id, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * @ignore
+ */
+
+function _delete(_this, id, callback) {
+ _this.s._filesCollection.deleteOne({ _id: id }, function(error, res) {
+ if (error) {
+ return callback(error);
+ }
+
+ _this.s._chunksCollection.deleteMany({ files_id: id }, function(error) {
+ if (error) {
+ return callback(error);
+ }
+
+ // Delete orphaned chunks before returning FileNotFound
+ if (!res.result.n) {
+ var errmsg = 'FileNotFound: no file with id ' + id + ' found';
+ return callback(new Error(errmsg));
+ }
+
+ callback();
+ });
+ });
+}
+
+/**
+ * Convenience wrapper around find on the files collection
+ * @method
+ * @param {Object} filter
+ * @param {Object} [options] Optional settings for cursor
+ * @param {number} [options.batchSize] Optional batch size for cursor
+ * @param {number} [options.limit] Optional limit for cursor
+ * @param {number} [options.maxTimeMS] Optional maxTimeMS for cursor
+ * @param {boolean} [options.noCursorTimeout] Optionally set cursor's `noCursorTimeout` flag
+ * @param {number} [options.skip] Optional skip for cursor
+ * @param {object} [options.sort] Optional sort for cursor
+ * @return {Cursor}
+ */
+
+GridFSBucket.prototype.find = function(filter, options) {
+ filter = filter || {};
+ options = options || {};
+
+ var cursor = this.s._filesCollection.find(filter);
+
+ if (options.batchSize != null) {
+ cursor.batchSize(options.batchSize);
+ }
+ if (options.limit != null) {
+ cursor.limit(options.limit);
+ }
+ if (options.maxTimeMS != null) {
+ cursor.maxTimeMS(options.maxTimeMS);
+ }
+ if (options.noCursorTimeout != null) {
+ cursor.addCursorFlag('noCursorTimeout', options.noCursorTimeout);
+ }
+ if (options.skip != null) {
+ cursor.skip(options.skip);
+ }
+ if (options.sort != null) {
+ cursor.sort(options.sort);
+ }
+
+ return cursor;
+};
+
+/**
+ * Returns a readable stream (GridFSBucketReadStream) for streaming the
+ * file with the given name from GridFS. If there are multiple files with
+ * the same name, this will stream the most recent file with the given name
+ * (as determined by the `uploadDate` field). You can set the `revision`
+ * option to change this behavior.
+ * @method
+ * @param {String} filename The name of the file to stream
+ * @param {Object} [options] Optional settings
+ * @param {number} [options.revision=-1] The revision number relative to the oldest file with the given filename. 0 gets you the oldest file, 1 gets you the 2nd oldest, -1 gets you the newest.
+ * @param {Number} [options.start] Optional 0-based offset in bytes to start streaming from
+ * @param {Number} [options.end] Optional 0-based offset in bytes to stop streaming before
+ * @return {GridFSBucketReadStream}
+ */
+
+GridFSBucket.prototype.openDownloadStreamByName = function(filename, options) {
+ var sort = { uploadDate: -1 };
+ var skip = null;
+ if (options && options.revision != null) {
+ if (options.revision >= 0) {
+ sort = { uploadDate: 1 };
+ skip = options.revision;
+ } else {
+ skip = -options.revision - 1;
+ }
+ }
+
+ var filter = { filename: filename };
+ options = {
+ sort: sort,
+ skip: skip,
+ start: options && options.start,
+ end: options && options.end
+ };
+ return new GridFSBucketReadStream(
+ this.s._chunksCollection,
+ this.s._filesCollection,
+ this.s.options.readPreference,
+ filter,
+ options
+ );
+};
+
+/**
+ * Renames the file with the given _id to the given string
+ * @method
+ * @param {ObjectId} id the id of the file to rename
+ * @param {String} filename new name for the file
+ * @param {GridFSBucket~errorCallback} [callback]
+ */
+
+GridFSBucket.prototype.rename = function(id, filename, callback) {
+ return executeOperation(this.s.db.s.topology, _rename, [this, id, filename, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * @ignore
+ */
+
+function _rename(_this, id, filename, callback) {
+ var filter = { _id: id };
+ var update = { $set: { filename: filename } };
+ _this.s._filesCollection.updateOne(filter, update, function(error, res) {
+ if (error) {
+ return callback(error);
+ }
+ if (!res.result.n) {
+ return callback(toError('File with id ' + id + ' not found'));
+ }
+ callback();
+ });
+}
+
+/**
+ * Removes this bucket's files collection, followed by its chunks collection.
+ * @method
+ * @param {GridFSBucket~errorCallback} [callback]
+ */
+
+GridFSBucket.prototype.drop = function(callback) {
+ return executeOperation(this.s.db.s.topology, _drop, [this, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * Return the db logger
+ * @method
+ * @return {Logger} return the db logger
+ * @ignore
+ */
+GridFSBucket.prototype.getLogger = function() {
+ return this.s.db.s.logger;
+};
+
+/**
+ * @ignore
+ */
+
+function _drop(_this, callback) {
+ _this.s._filesCollection.drop(function(error) {
+ if (error) {
+ return callback(error);
+ }
+ _this.s._chunksCollection.drop(function(error) {
+ if (error) {
+ return callback(error);
+ }
+
+ return callback();
+ });
+ });
+}
+
+/**
+ * Callback format for all GridFSBucket methods that can accept a callback.
+ * @callback GridFSBucket~errorCallback
+ * @param {MongoError} error An error instance representing any errors that occurred
+ */
diff --git a/node_modules/mongodb/lib/gridfs-stream/upload.js b/node_modules/mongodb/lib/gridfs-stream/upload.js
new file mode 100644
index 0000000..adbba41
--- /dev/null
+++ b/node_modules/mongodb/lib/gridfs-stream/upload.js
@@ -0,0 +1,538 @@
+'use strict';
+
+var core = require('mongodb-core');
+var crypto = require('crypto');
+var stream = require('stream');
+var util = require('util');
+var Buffer = require('safe-buffer').Buffer;
+
+var ERROR_NAMESPACE_NOT_FOUND = 26;
+
+module.exports = GridFSBucketWriteStream;
+
+/**
+ * A writable stream that enables you to write buffers to GridFS.
+ *
+ * Do not instantiate this class directly. Use `openUploadStream()` instead.
+ *
+ * @class
+ * @param {GridFSBucket} bucket Handle for this stream's corresponding bucket
+ * @param {string} filename The value of the 'filename' key in the files doc
+ * @param {object} [options] Optional settings.
+ * @param {string|number|object} [options.id] Custom file id for the GridFS file.
+ * @param {number} [options.chunkSizeBytes] The chunk size to use, in bytes
+ * @param {number} [options.w] The write concern
+ * @param {number} [options.wtimeout] The write concern timeout
+ * @param {number} [options.j] The journal write concern
+ * @param {boolean} [options.disableMD5=false] If true, disables adding an md5 field to file data
+ * @fires GridFSBucketWriteStream#error
+ * @fires GridFSBucketWriteStream#finish
+ * @return {GridFSBucketWriteStream} a GridFSBucketWriteStream instance.
+ */
+
+function GridFSBucketWriteStream(bucket, filename, options) {
+ options = options || {};
+ this.bucket = bucket;
+ this.chunks = bucket.s._chunksCollection;
+ this.filename = filename;
+ this.files = bucket.s._filesCollection;
+ this.options = options;
+ // Signals the write is all done
+ this.done = false;
+
+ this.id = options.id ? options.id : core.BSON.ObjectId();
+ this.chunkSizeBytes = this.options.chunkSizeBytes;
+ this.bufToStore = Buffer.alloc(this.chunkSizeBytes);
+ this.length = 0;
+ this.md5 = !options.disableMD5 && crypto.createHash('md5');
+ this.n = 0;
+ this.pos = 0;
+ this.state = {
+ streamEnd: false,
+ outstandingRequests: 0,
+ errored: false,
+ aborted: false,
+ promiseLibrary: this.bucket.s.promiseLibrary
+ };
+
+ if (!this.bucket.s.calledOpenUploadStream) {
+ this.bucket.s.calledOpenUploadStream = true;
+
+ var _this = this;
+ checkIndexes(this, function() {
+ _this.bucket.s.checkedIndexes = true;
+ _this.bucket.emit('index');
+ });
+ }
+}
+
+util.inherits(GridFSBucketWriteStream, stream.Writable);
+
+/**
+ * An error occurred
+ *
+ * @event GridFSBucketWriteStream#error
+ * @type {Error}
+ */
+
+/**
+ * `end()` was called and the write stream successfully wrote the file
+ * metadata and all the chunks to MongoDB.
+ *
+ * @event GridFSBucketWriteStream#finish
+ * @type {object}
+ */
+
+/**
+ * Write a buffer to the stream.
+ *
+ * @method
+ * @param {Buffer} chunk Buffer to write
+ * @param {String} encoding Optional encoding for the buffer
+ * @param {Function} callback Function to call when the chunk was added to the buffer, or if the entire chunk was persisted to MongoDB if this chunk caused a flush.
+ * @return {Boolean} False if this write required flushing a chunk to MongoDB. True otherwise.
+ */
+
+GridFSBucketWriteStream.prototype.write = function(chunk, encoding, callback) {
+ var _this = this;
+ return waitForIndexes(this, function() {
+ return doWrite(_this, chunk, encoding, callback);
+ });
+};
+
+/**
+ * Places this write stream into an aborted state (all future writes fail)
+ * and deletes all chunks that have already been written.
+ *
+ * @method
+ * @param {GridFSBucket~errorCallback} callback called when chunks are successfully removed or error occurred
+ * @return {Promise} if no callback specified
+ */
+
+GridFSBucketWriteStream.prototype.abort = function(callback) {
+ if (this.state.streamEnd) {
+ var error = new Error('Cannot abort a stream that has already completed');
+ if (typeof callback === 'function') {
+ return callback(error);
+ }
+ return this.state.promiseLibrary.reject(error);
+ }
+ if (this.state.aborted) {
+ error = new Error('Cannot call abort() on a stream twice');
+ if (typeof callback === 'function') {
+ return callback(error);
+ }
+ return this.state.promiseLibrary.reject(error);
+ }
+ this.state.aborted = true;
+ this.chunks.deleteMany({ files_id: this.id }, function(error) {
+ if (typeof callback === 'function') callback(error);
+ });
+};
+
+/**
+ * Tells the stream that no more data will be coming in. The stream will
+ * persist the remaining data to MongoDB, write the files document, and
+ * then emit a 'finish' event.
+ *
+ * @method
+ * @param {Buffer} chunk Buffer to write
+ * @param {String} encoding Optional encoding for the buffer
+ * @param {Function} callback Function to call when all files and chunks have been persisted to MongoDB
+ */
+
+GridFSBucketWriteStream.prototype.end = function(chunk, encoding, callback) {
+ var _this = this;
+ if (typeof chunk === 'function') {
+ (callback = chunk), (chunk = null), (encoding = null);
+ } else if (typeof encoding === 'function') {
+ (callback = encoding), (encoding = null);
+ }
+
+ if (checkAborted(this, callback)) {
+ return;
+ }
+ this.state.streamEnd = true;
+
+ if (callback) {
+ this.once('finish', function(result) {
+ callback(null, result);
+ });
+ }
+
+ if (!chunk) {
+ waitForIndexes(this, function() {
+ writeRemnant(_this);
+ });
+ return;
+ }
+
+ this.write(chunk, encoding, function() {
+ writeRemnant(_this);
+ });
+};
+
+/**
+ * @ignore
+ */
+
+function __handleError(_this, error, callback) {
+ if (_this.state.errored) {
+ return;
+ }
+ _this.state.errored = true;
+ if (callback) {
+ return callback(error);
+ }
+ _this.emit('error', error);
+}
+
+/**
+ * @ignore
+ */
+
+function createChunkDoc(filesId, n, data) {
+ return {
+ _id: core.BSON.ObjectId(),
+ files_id: filesId,
+ n: n,
+ data: data
+ };
+}
+
+/**
+ * @ignore
+ */
+
+function checkChunksIndex(_this, callback) {
+ _this.chunks.listIndexes().toArray(function(error, indexes) {
+ if (error) {
+ // Collection doesn't exist so create index
+ if (error.code === ERROR_NAMESPACE_NOT_FOUND) {
+ var index = { files_id: 1, n: 1 };
+ _this.chunks.createIndex(index, { background: false, unique: true }, function(error) {
+ if (error) {
+ return callback(error);
+ }
+
+ callback();
+ });
+ return;
+ }
+ return callback(error);
+ }
+
+ var hasChunksIndex = false;
+ indexes.forEach(function(index) {
+ if (index.key) {
+ var keys = Object.keys(index.key);
+ if (keys.length === 2 && index.key.files_id === 1 && index.key.n === 1) {
+ hasChunksIndex = true;
+ }
+ }
+ });
+
+ if (hasChunksIndex) {
+ callback();
+ } else {
+ index = { files_id: 1, n: 1 };
+ var indexOptions = getWriteOptions(_this);
+
+ indexOptions.background = false;
+ indexOptions.unique = true;
+
+ _this.chunks.createIndex(index, indexOptions, function(error) {
+ if (error) {
+ return callback(error);
+ }
+
+ callback();
+ });
+ }
+ });
+}
+
+/**
+ * @ignore
+ */
+
+function checkDone(_this, callback) {
+ if (_this.done) return true;
+ if (_this.state.streamEnd && _this.state.outstandingRequests === 0 && !_this.state.errored) {
+ // Set done so we dont' trigger duplicate createFilesDoc
+ _this.done = true;
+ // Create a new files doc
+ var filesDoc = createFilesDoc(
+ _this.id,
+ _this.length,
+ _this.chunkSizeBytes,
+ _this.md5 && _this.md5.digest('hex'),
+ _this.filename,
+ _this.options.contentType,
+ _this.options.aliases,
+ _this.options.metadata
+ );
+
+ if (checkAborted(_this, callback)) {
+ return false;
+ }
+
+ _this.files.insertOne(filesDoc, getWriteOptions(_this), function(error) {
+ if (error) {
+ return __handleError(_this, error, callback);
+ }
+ _this.emit('finish', filesDoc);
+ });
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * @ignore
+ */
+
+function checkIndexes(_this, callback) {
+ _this.files.findOne({}, { _id: 1 }, function(error, doc) {
+ if (error) {
+ return callback(error);
+ }
+ if (doc) {
+ return callback();
+ }
+
+ _this.files.listIndexes().toArray(function(error, indexes) {
+ if (error) {
+ // Collection doesn't exist so create index
+ if (error.code === ERROR_NAMESPACE_NOT_FOUND) {
+ var index = { filename: 1, uploadDate: 1 };
+ _this.files.createIndex(index, { background: false }, function(error) {
+ if (error) {
+ return callback(error);
+ }
+
+ checkChunksIndex(_this, callback);
+ });
+ return;
+ }
+ return callback(error);
+ }
+
+ var hasFileIndex = false;
+ indexes.forEach(function(index) {
+ var keys = Object.keys(index.key);
+ if (keys.length === 2 && index.key.filename === 1 && index.key.uploadDate === 1) {
+ hasFileIndex = true;
+ }
+ });
+
+ if (hasFileIndex) {
+ checkChunksIndex(_this, callback);
+ } else {
+ index = { filename: 1, uploadDate: 1 };
+
+ var indexOptions = getWriteOptions(_this);
+
+ indexOptions.background = false;
+
+ _this.files.createIndex(index, indexOptions, function(error) {
+ if (error) {
+ return callback(error);
+ }
+
+ checkChunksIndex(_this, callback);
+ });
+ }
+ });
+ });
+}
+
+/**
+ * @ignore
+ */
+
+function createFilesDoc(_id, length, chunkSize, md5, filename, contentType, aliases, metadata) {
+ var ret = {
+ _id: _id,
+ length: length,
+ chunkSize: chunkSize,
+ uploadDate: new Date(),
+ filename: filename
+ };
+
+ if (md5) {
+ ret.md5 = md5;
+ }
+
+ if (contentType) {
+ ret.contentType = contentType;
+ }
+
+ if (aliases) {
+ ret.aliases = aliases;
+ }
+
+ if (metadata) {
+ ret.metadata = metadata;
+ }
+
+ return ret;
+}
+
+/**
+ * @ignore
+ */
+
+function doWrite(_this, chunk, encoding, callback) {
+ if (checkAborted(_this, callback)) {
+ return false;
+ }
+
+ var inputBuf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
+
+ _this.length += inputBuf.length;
+
+ // Input is small enough to fit in our buffer
+ if (_this.pos + inputBuf.length < _this.chunkSizeBytes) {
+ inputBuf.copy(_this.bufToStore, _this.pos);
+ _this.pos += inputBuf.length;
+
+ callback && callback();
+
+ // Note that we reverse the typical semantics of write's return value
+ // to be compatible with node's `.pipe()` function.
+ // True means client can keep writing.
+ return true;
+ }
+
+ // Otherwise, buffer is too big for current chunk, so we need to flush
+ // to MongoDB.
+ var inputBufRemaining = inputBuf.length;
+ var spaceRemaining = _this.chunkSizeBytes - _this.pos;
+ var numToCopy = Math.min(spaceRemaining, inputBuf.length);
+ var outstandingRequests = 0;
+ while (inputBufRemaining > 0) {
+ var inputBufPos = inputBuf.length - inputBufRemaining;
+ inputBuf.copy(_this.bufToStore, _this.pos, inputBufPos, inputBufPos + numToCopy);
+ _this.pos += numToCopy;
+ spaceRemaining -= numToCopy;
+ if (spaceRemaining === 0) {
+ if (_this.md5) {
+ _this.md5.update(_this.bufToStore);
+ }
+ var doc = createChunkDoc(_this.id, _this.n, _this.bufToStore);
+ ++_this.state.outstandingRequests;
+ ++outstandingRequests;
+
+ if (checkAborted(_this, callback)) {
+ return false;
+ }
+
+ _this.chunks.insertOne(doc, getWriteOptions(_this), function(error) {
+ if (error) {
+ return __handleError(_this, error);
+ }
+ --_this.state.outstandingRequests;
+ --outstandingRequests;
+
+ if (!outstandingRequests) {
+ _this.emit('drain', doc);
+ callback && callback();
+ checkDone(_this);
+ }
+ });
+
+ spaceRemaining = _this.chunkSizeBytes;
+ _this.pos = 0;
+ ++_this.n;
+ }
+ inputBufRemaining -= numToCopy;
+ numToCopy = Math.min(spaceRemaining, inputBufRemaining);
+ }
+
+ // Note that we reverse the typical semantics of write's return value
+ // to be compatible with node's `.pipe()` function.
+ // False means the client should wait for the 'drain' event.
+ return false;
+}
+
+/**
+ * @ignore
+ */
+
+function getWriteOptions(_this) {
+ var obj = {};
+ if (_this.options.writeConcern) {
+ obj.w = _this.options.writeConcern.w;
+ obj.wtimeout = _this.options.writeConcern.wtimeout;
+ obj.j = _this.options.writeConcern.j;
+ }
+ return obj;
+}
+
+/**
+ * @ignore
+ */
+
+function waitForIndexes(_this, callback) {
+ if (_this.bucket.s.checkedIndexes) {
+ return callback(false);
+ }
+
+ _this.bucket.once('index', function() {
+ callback(true);
+ });
+
+ return true;
+}
+
+/**
+ * @ignore
+ */
+
+function writeRemnant(_this, callback) {
+ // Buffer is empty, so don't bother to insert
+ if (_this.pos === 0) {
+ return checkDone(_this, callback);
+ }
+
+ ++_this.state.outstandingRequests;
+
+ // Create a new buffer to make sure the buffer isn't bigger than it needs
+ // to be.
+ var remnant = Buffer.alloc(_this.pos);
+ _this.bufToStore.copy(remnant, 0, 0, _this.pos);
+ if (_this.md5) {
+ _this.md5.update(remnant);
+ }
+ var doc = createChunkDoc(_this.id, _this.n, remnant);
+
+ // If the stream was aborted, do not write remnant
+ if (checkAborted(_this, callback)) {
+ return false;
+ }
+
+ _this.chunks.insertOne(doc, getWriteOptions(_this), function(error) {
+ if (error) {
+ return __handleError(_this, error);
+ }
+ --_this.state.outstandingRequests;
+ checkDone(_this);
+ });
+}
+
+/**
+ * @ignore
+ */
+
+function checkAborted(_this, callback) {
+ if (_this.state.aborted) {
+ if (typeof callback === 'function') {
+ callback(new Error('this stream has been aborted'));
+ }
+ return true;
+ }
+ return false;
+}
diff --git a/node_modules/mongodb/lib/gridfs/chunk.js b/node_modules/mongodb/lib/gridfs/chunk.js
new file mode 100644
index 0000000..c29bba0
--- /dev/null
+++ b/node_modules/mongodb/lib/gridfs/chunk.js
@@ -0,0 +1,236 @@
+'use strict';
+
+var Binary = require('mongodb-core').BSON.Binary,
+ ObjectID = require('mongodb-core').BSON.ObjectID;
+
+var Buffer = require('safe-buffer').Buffer;
+
+/**
+ * Class for representing a single chunk in GridFS.
+ *
+ * @class
+ *
+ * @param file {GridStore} The {@link GridStore} object holding this chunk.
+ * @param mongoObject {object} The mongo object representation of this chunk.
+ *
+ * @throws Error when the type of data field for {@link mongoObject} is not
+ * supported. Currently supported types for data field are instances of
+ * {@link String}, {@link Array}, {@link Binary} and {@link Binary}
+ * from the bson module
+ *
+ * @see Chunk#buildMongoObject
+ */
+var Chunk = function(file, mongoObject, writeConcern) {
+ if (!(this instanceof Chunk)) return new Chunk(file, mongoObject);
+
+ this.file = file;
+ var mongoObjectFinal = mongoObject == null ? {} : mongoObject;
+ this.writeConcern = writeConcern || { w: 1 };
+ this.objectId = mongoObjectFinal._id == null ? new ObjectID() : mongoObjectFinal._id;
+ this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n;
+ this.data = new Binary();
+
+ if (typeof mongoObjectFinal.data === 'string') {
+ var buffer = Buffer.alloc(mongoObjectFinal.data.length);
+ buffer.write(mongoObjectFinal.data, 0, mongoObjectFinal.data.length, 'binary');
+ this.data = new Binary(buffer);
+ } else if (Array.isArray(mongoObjectFinal.data)) {
+ buffer = Buffer.alloc(mongoObjectFinal.data.length);
+ var data = mongoObjectFinal.data.join('');
+ buffer.write(data, 0, data.length, 'binary');
+ this.data = new Binary(buffer);
+ } else if (mongoObjectFinal.data && mongoObjectFinal.data._bsontype === 'Binary') {
+ this.data = mongoObjectFinal.data;
+ } else if (!Buffer.isBuffer(mongoObjectFinal.data) && !(mongoObjectFinal.data == null)) {
+ throw Error('Illegal chunk format');
+ }
+
+ // Update position
+ this.internalPosition = 0;
+};
+
+/**
+ * Writes a data to this object and advance the read/write head.
+ *
+ * @param data {string} the data to write
+ * @param callback {function(*, GridStore)} This will be called after executing
+ * this method. The first parameter will contain null and the second one
+ * will contain a reference to this object.
+ */
+Chunk.prototype.write = function(data, callback) {
+ this.data.write(data, this.internalPosition, data.length, 'binary');
+ this.internalPosition = this.data.length();
+ if (callback != null) return callback(null, this);
+ return this;
+};
+
+/**
+ * Reads data and advances the read/write head.
+ *
+ * @param length {number} The length of data to read.
+ *
+ * @return {string} The data read if the given length will not exceed the end of
+ * the chunk. Returns an empty String otherwise.
+ */
+Chunk.prototype.read = function(length) {
+ // Default to full read if no index defined
+ length = length == null || length === 0 ? this.length() : length;
+
+ if (this.length() - this.internalPosition + 1 >= length) {
+ var data = this.data.read(this.internalPosition, length);
+ this.internalPosition = this.internalPosition + length;
+ return data;
+ } else {
+ return '';
+ }
+};
+
+Chunk.prototype.readSlice = function(length) {
+ if (this.length() - this.internalPosition >= length) {
+ var data = null;
+ if (this.data.buffer != null) {
+ //Pure BSON
+ data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length);
+ } else {
+ //Native BSON
+ data = Buffer.alloc(length);
+ length = this.data.readInto(data, this.internalPosition);
+ }
+ this.internalPosition = this.internalPosition + length;
+ return data;
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Checks if the read/write head is at the end.
+ *
+ * @return {boolean} Whether the read/write head has reached the end of this
+ * chunk.
+ */
+Chunk.prototype.eof = function() {
+ return this.internalPosition === this.length() ? true : false;
+};
+
+/**
+ * Reads one character from the data of this chunk and advances the read/write
+ * head.
+ *
+ * @return {string} a single character data read if the the read/write head is
+ * not at the end of the chunk. Returns an empty String otherwise.
+ */
+Chunk.prototype.getc = function() {
+ return this.read(1);
+};
+
+/**
+ * Clears the contents of the data in this chunk and resets the read/write head
+ * to the initial position.
+ */
+Chunk.prototype.rewind = function() {
+ this.internalPosition = 0;
+ this.data = new Binary();
+};
+
+/**
+ * Saves this chunk to the database. Also overwrites existing entries having the
+ * same id as this chunk.
+ *
+ * @param callback {function(*, GridStore)} This will be called after executing
+ * this method. The first parameter will contain null and the second one
+ * will contain a reference to this object.
+ */
+Chunk.prototype.save = function(options, callback) {
+ var self = this;
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ self.file.chunkCollection(function(err, collection) {
+ if (err) return callback(err);
+
+ // Merge the options
+ var writeOptions = { upsert: true };
+ for (var name in options) writeOptions[name] = options[name];
+ for (name in self.writeConcern) writeOptions[name] = self.writeConcern[name];
+
+ if (self.data.length() > 0) {
+ self.buildMongoObject(function(mongoObject) {
+ var options = { forceServerObjectId: true };
+ for (var name in self.writeConcern) {
+ options[name] = self.writeConcern[name];
+ }
+
+ collection.replaceOne({ _id: self.objectId }, mongoObject, writeOptions, function(err) {
+ callback(err, self);
+ });
+ });
+ } else {
+ callback(null, self);
+ }
+ // });
+ });
+};
+
+/**
+ * Creates a mongoDB object representation of this chunk.
+ *
+ * @param callback {function(Object)} This will be called after executing this
+ * method. The object will be passed to the first parameter and will have
+ * the structure:
+ *
+ *
+ * {
+ * '_id' : , // {number} id for this chunk
+ * 'files_id' : , // {number} foreign key to the file collection
+ * 'n' : , // {number} chunk number
+ * 'data' : , // {bson#Binary} the chunk data itself
+ * }
+ *
+ *
+ * @see MongoDB GridFS Chunk Object Structure
+ */
+Chunk.prototype.buildMongoObject = function(callback) {
+ var mongoObject = {
+ files_id: this.file.fileId,
+ n: this.chunkNumber,
+ data: this.data
+ };
+ // If we are saving using a specific ObjectId
+ if (this.objectId != null) mongoObject._id = this.objectId;
+
+ callback(mongoObject);
+};
+
+/**
+ * @return {number} the length of the data
+ */
+Chunk.prototype.length = function() {
+ return this.data.length();
+};
+
+/**
+ * The position of the read/write head
+ * @name position
+ * @lends Chunk#
+ * @field
+ */
+Object.defineProperty(Chunk.prototype, 'position', {
+ enumerable: true,
+ get: function() {
+ return this.internalPosition;
+ },
+ set: function(value) {
+ this.internalPosition = value;
+ }
+});
+
+/**
+ * The default chunk size
+ * @constant
+ */
+Chunk.DEFAULT_CHUNK_SIZE = 1024 * 255;
+
+module.exports = Chunk;
diff --git a/node_modules/mongodb/lib/gridfs/grid_store.js b/node_modules/mongodb/lib/gridfs/grid_store.js
new file mode 100644
index 0000000..4b5eb5e
--- /dev/null
+++ b/node_modules/mongodb/lib/gridfs/grid_store.js
@@ -0,0 +1,1907 @@
+'use strict';
+
+/**
+ * @fileOverview GridFS is a tool for MongoDB to store files to the database.
+ * Because of the restrictions of the object size the database can hold, a
+ * facility to split a file into several chunks is needed. The {@link GridStore}
+ * class offers a simplified api to interact with files while managing the
+ * chunks of split files behind the scenes. More information about GridFS can be
+ * found here.
+ *
+ * @example
+ * const MongoClient = require('mongodb').MongoClient;
+ * const GridStore = require('mongodb').GridStore;
+ * const ObjectID = require('mongodb').ObjectID;
+ * const test = require('assert');
+ * // Connection url
+ * const url = 'mongodb://localhost:27017';
+ * // Database Name
+ * const dbName = 'test';
+ * // Connect using MongoClient
+ * MongoClient.connect(url, function(err, client) {
+ * const db = client.db(dbName);
+ * const gridStore = new GridStore(db, null, "w");
+ * gridStore.open(function(err, gridStore) {
+ * gridStore.write("hello world!", function(err, gridStore) {
+ * gridStore.close(function(err, result) {
+ * // Let's read the file using object Id
+ * GridStore.read(db, result._id, function(err, data) {
+ * test.equal('hello world!', data);
+ * client.close();
+ * test.done();
+ * });
+ * });
+ * });
+ * });
+ * });
+ */
+const Chunk = require('./chunk');
+const ObjectID = require('mongodb-core').BSON.ObjectID;
+const ReadPreference = require('mongodb-core').ReadPreference;
+const Buffer = require('safe-buffer').Buffer;
+const fs = require('fs');
+const f = require('util').format;
+const util = require('util');
+const MongoError = require('mongodb-core').MongoError;
+const inherits = util.inherits;
+const Duplex = require('stream').Duplex;
+const shallowClone = require('../utils').shallowClone;
+const executeOperation = require('../utils').executeOperation;
+const deprecate = require('util').deprecate;
+
+var REFERENCE_BY_FILENAME = 0,
+ REFERENCE_BY_ID = 1;
+
+const deprecationFn = deprecate(() => {},
+'GridStore is deprecated, and will be removed in a future version. Please use GridFSBucket instead');
+
+/**
+ * Namespace provided by the mongodb-core and node.js
+ * @external Duplex
+ */
+
+/**
+ * Create a new GridStore instance
+ *
+ * Modes
+ * - **"r"** - read only. This is the default mode.
+ * - **"w"** - write in truncate mode. Existing data will be overwriten.
+ *
+ * @class
+ * @param {Db} db A database instance to interact with.
+ * @param {object} [id] optional unique id for this file
+ * @param {string} [filename] optional filename for this file, no unique constrain on the field
+ * @param {string} mode set the mode for this file.
+ * @param {object} [options] Optional settings.
+ * @param {(number|string)} [options.w] The write concern.
+ * @param {number} [options.wtimeout] The write concern timeout.
+ * @param {boolean} [options.j=false] Specify a journal write concern.
+ * @param {boolean} [options.fsync=false] Specify a file sync write concern.
+ * @param {string} [options.root] Root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
+ * @param {string} [options.content_type] MIME type of the file. Defaults to **{GridStore.DEFAULT_CONTENT_TYPE}**.
+ * @param {number} [options.chunk_size=261120] Size for the chunk. Defaults to **{Chunk.DEFAULT_CHUNK_SIZE}**.
+ * @param {object} [options.metadata] Arbitrary data the user wants to store.
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @property {number} chunkSize Get the gridstore chunk size.
+ * @property {number} md5 The md5 checksum for this file.
+ * @property {number} chunkNumber The current chunk number the gridstore has materialized into memory
+ * @return {GridStore} a GridStore instance.
+ * @deprecated Use GridFSBucket API instead
+ */
+var GridStore = function GridStore(db, id, filename, mode, options) {
+ deprecationFn();
+ if (!(this instanceof GridStore)) return new GridStore(db, id, filename, mode, options);
+ this.db = db;
+
+ // Handle options
+ if (typeof options === 'undefined') options = {};
+ // Handle mode
+ if (typeof mode === 'undefined') {
+ mode = filename;
+ filename = undefined;
+ } else if (typeof mode === 'object') {
+ options = mode;
+ mode = filename;
+ filename = undefined;
+ }
+
+ if (id && id._bsontype === 'ObjectID') {
+ this.referenceBy = REFERENCE_BY_ID;
+ this.fileId = id;
+ this.filename = filename;
+ } else if (typeof filename === 'undefined') {
+ this.referenceBy = REFERENCE_BY_FILENAME;
+ this.filename = id;
+ if (mode.indexOf('w') != null) {
+ this.fileId = new ObjectID();
+ }
+ } else {
+ this.referenceBy = REFERENCE_BY_ID;
+ this.fileId = id;
+ this.filename = filename;
+ }
+
+ // Set up the rest
+ this.mode = mode == null ? 'r' : mode;
+ this.options = options || {};
+
+ // Opened
+ this.isOpen = false;
+
+ // Set the root if overridden
+ this.root =
+ this.options['root'] == null ? GridStore.DEFAULT_ROOT_COLLECTION : this.options['root'];
+ this.position = 0;
+ this.readPreference =
+ this.options.readPreference || db.options.readPreference || ReadPreference.primary;
+ this.writeConcern = _getWriteConcern(db, this.options);
+ // Set default chunk size
+ this.internalChunkSize =
+ this.options['chunkSize'] == null ? Chunk.DEFAULT_CHUNK_SIZE : this.options['chunkSize'];
+
+ // Get the promiseLibrary
+ var promiseLibrary = this.options.promiseLibrary || Promise;
+
+ // Set the promiseLibrary
+ this.promiseLibrary = promiseLibrary;
+
+ Object.defineProperty(this, 'chunkSize', {
+ enumerable: true,
+ get: function() {
+ return this.internalChunkSize;
+ },
+ set: function(value) {
+ if (!(this.mode[0] === 'w' && this.position === 0 && this.uploadDate == null)) {
+ this.internalChunkSize = this.internalChunkSize;
+ } else {
+ this.internalChunkSize = value;
+ }
+ }
+ });
+
+ Object.defineProperty(this, 'md5', {
+ enumerable: true,
+ get: function() {
+ return this.internalMd5;
+ }
+ });
+
+ Object.defineProperty(this, 'chunkNumber', {
+ enumerable: true,
+ get: function() {
+ return this.currentChunk && this.currentChunk.chunkNumber
+ ? this.currentChunk.chunkNumber
+ : null;
+ }
+ });
+};
+
+/**
+ * The callback format for the Gridstore.open method
+ * @callback GridStore~openCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {GridStore} gridStore The GridStore instance if the open method was successful.
+ */
+
+/**
+ * Opens the file from the database and initialize this object. Also creates a
+ * new one if file does not exist.
+ *
+ * @method
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~openCallback} [callback] this will be called after executing this method
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.open = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ if (this.mode !== 'w' && this.mode !== 'w+' && this.mode !== 'r') {
+ throw MongoError.create({ message: 'Illegal mode ' + this.mode, driver: true });
+ }
+
+ return executeOperation(this.db.s.topology, open, [this, options, callback], {
+ skipSessions: true
+ });
+};
+
+var open = function(self, options, callback) {
+ // Get the write concern
+ var writeConcern = _getWriteConcern(self.db, self.options);
+
+ // If we are writing we need to ensure we have the right indexes for md5's
+ if (self.mode === 'w' || self.mode === 'w+') {
+ // Get files collection
+ var collection = self.collection();
+ // Put index on filename
+ collection.ensureIndex([['filename', 1]], writeConcern, function() {
+ // Get chunk collection
+ var chunkCollection = self.chunkCollection();
+ // Make an unique index for compatibility with mongo-cxx-driver:legacy
+ var chunkIndexOptions = shallowClone(writeConcern);
+ chunkIndexOptions.unique = true;
+ // Ensure index on chunk collection
+ chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], chunkIndexOptions, function() {
+ // Open the connection
+ _open(self, writeConcern, function(err, r) {
+ if (err) return callback(err);
+ self.isOpen = true;
+ callback(err, r);
+ });
+ });
+ });
+ } else {
+ // Open the gridstore
+ _open(self, writeConcern, function(err, r) {
+ if (err) return callback(err);
+ self.isOpen = true;
+ callback(err, r);
+ });
+ }
+};
+
+/**
+ * Verify if the file is at EOF.
+ *
+ * @method
+ * @return {boolean} true if the read/write head is at the end of this file.
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.eof = function() {
+ return this.position === this.length ? true : false;
+};
+
+/**
+ * The callback result format.
+ * @callback GridStore~resultCallback
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {object} result The result from the callback.
+ */
+
+/**
+ * Retrieves a single character from this file.
+ *
+ * @method
+ * @param {GridStore~resultCallback} [callback] this gets called after this method is executed. Passes null to the first parameter and the character read to the second or null to the second if the read/write head is at the end of the file.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.getc = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.db.s.topology, getc, [this, options, callback], {
+ skipSessions: true
+ });
+};
+
+var getc = function(self, options, callback) {
+ if (self.eof()) {
+ callback(null, null);
+ } else if (self.currentChunk.eof()) {
+ nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) {
+ self.currentChunk = chunk;
+ self.position = self.position + 1;
+ callback(err, self.currentChunk.getc());
+ });
+ } else {
+ self.position = self.position + 1;
+ callback(null, self.currentChunk.getc());
+ }
+};
+
+/**
+ * Writes a string to the file with a newline character appended at the end if
+ * the given string does not have one.
+ *
+ * @method
+ * @param {string} string the string to write.
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.puts = function(string, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ var finalString = string.match(/\n$/) == null ? string + '\n' : string;
+ return executeOperation(
+ this.db.s.topology,
+ this.write.bind(this),
+ [finalString, options, callback],
+ { skipSessions: true }
+ );
+};
+
+/**
+ * Return a modified Readable stream including a possible transform method.
+ *
+ * @method
+ * @return {GridStoreStream}
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.stream = function() {
+ return new GridStoreStream(this);
+};
+
+/**
+ * Writes some data. This method will work properly only if initialized with mode "w" or "w+".
+ *
+ * @method
+ * @param {(string|Buffer)} data the data to write.
+ * @param {boolean} [close] closes this file after writing if set to true.
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.write = function write(data, close, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(
+ this.db.s.topology,
+ _writeNormal,
+ [this, data, close, options, callback],
+ { skipSessions: true }
+ );
+};
+
+/**
+ * Handles the destroy part of a stream
+ *
+ * @method
+ * @result {null}
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.destroy = function destroy() {
+ // close and do not emit any more events. queued data is not sent.
+ if (!this.writable) return;
+ this.readable = false;
+ if (this.writable) {
+ this.writable = false;
+ this._q.length = 0;
+ this.emit('close');
+ }
+};
+
+/**
+ * Stores a file from the file system to the GridFS database.
+ *
+ * @method
+ * @param {(string|Buffer|FileHandle)} file the file to store.
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.writeFile = function(file, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.db.s.topology, writeFile, [this, file, options, callback], {
+ skipSessions: true
+ });
+};
+
+var writeFile = function(self, file, options, callback) {
+ if (typeof file === 'string') {
+ fs.open(file, 'r', function(err, fd) {
+ if (err) return callback(err);
+ self.writeFile(fd, callback);
+ });
+ return;
+ }
+
+ self.open(function(err, self) {
+ if (err) return callback(err, self);
+
+ fs.fstat(file, function(err, stats) {
+ if (err) return callback(err, self);
+
+ var offset = 0;
+ var index = 0;
+
+ // Write a chunk
+ var writeChunk = function() {
+ // Allocate the buffer
+ var _buffer = Buffer.alloc(self.chunkSize);
+ // Read the file
+ fs.read(file, _buffer, 0, _buffer.length, offset, function(err, bytesRead, data) {
+ if (err) return callback(err, self);
+
+ offset = offset + bytesRead;
+
+ // Create a new chunk for the data
+ var chunk = new Chunk(self, { n: index++ }, self.writeConcern);
+ chunk.write(data.slice(0, bytesRead), function(err, chunk) {
+ if (err) return callback(err, self);
+
+ chunk.save({}, function(err) {
+ if (err) return callback(err, self);
+
+ self.position = self.position + bytesRead;
+
+ // Point to current chunk
+ self.currentChunk = chunk;
+
+ if (offset >= stats.size) {
+ fs.close(file, function(err) {
+ if (err) return callback(err);
+
+ self.close(function(err) {
+ if (err) return callback(err, self);
+ return callback(null, self);
+ });
+ });
+ } else {
+ return process.nextTick(writeChunk);
+ }
+ });
+ });
+ });
+ };
+
+ // Process the first write
+ process.nextTick(writeChunk);
+ });
+ });
+};
+
+/**
+ * Saves this file to the database. This will overwrite the old entry if it
+ * already exists. This will work properly only if mode was initialized to
+ * "w" or "w+".
+ *
+ * @method
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.close = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.db.s.topology, close, [this, options, callback], {
+ skipSessions: true
+ });
+};
+
+var close = function(self, options, callback) {
+ if (self.mode[0] === 'w') {
+ // Set up options
+ options = Object.assign({}, self.writeConcern, options);
+
+ if (self.currentChunk != null && self.currentChunk.position > 0) {
+ self.currentChunk.save({}, function(err) {
+ if (err && typeof callback === 'function') return callback(err);
+
+ self.collection(function(err, files) {
+ if (err && typeof callback === 'function') return callback(err);
+
+ // Build the mongo object
+ if (self.uploadDate != null) {
+ buildMongoObject(self, function(err, mongoObject) {
+ if (err) {
+ if (typeof callback === 'function') return callback(err);
+ else throw err;
+ }
+
+ files.save(mongoObject, options, function(err) {
+ if (typeof callback === 'function') callback(err, mongoObject);
+ });
+ });
+ } else {
+ self.uploadDate = new Date();
+ buildMongoObject(self, function(err, mongoObject) {
+ if (err) {
+ if (typeof callback === 'function') return callback(err);
+ else throw err;
+ }
+
+ files.save(mongoObject, options, function(err) {
+ if (typeof callback === 'function') callback(err, mongoObject);
+ });
+ });
+ }
+ });
+ });
+ } else {
+ self.collection(function(err, files) {
+ if (err && typeof callback === 'function') return callback(err);
+
+ self.uploadDate = new Date();
+ buildMongoObject(self, function(err, mongoObject) {
+ if (err) {
+ if (typeof callback === 'function') return callback(err);
+ else throw err;
+ }
+
+ files.save(mongoObject, options, function(err) {
+ if (typeof callback === 'function') callback(err, mongoObject);
+ });
+ });
+ });
+ }
+ } else if (self.mode[0] === 'r') {
+ if (typeof callback === 'function') callback(null, null);
+ } else {
+ if (typeof callback === 'function')
+ callback(MongoError.create({ message: f('Illegal mode %s', self.mode), driver: true }));
+ }
+};
+
+/**
+ * The collection callback format.
+ * @callback GridStore~collectionCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {Collection} collection The collection from the command execution.
+ */
+
+/**
+ * Retrieve this file's chunks collection.
+ *
+ * @method
+ * @param {GridStore~collectionCallback} callback the command callback.
+ * @return {Collection}
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.chunkCollection = function(callback) {
+ if (typeof callback === 'function') return this.db.collection(this.root + '.chunks', callback);
+ return this.db.collection(this.root + '.chunks');
+};
+
+/**
+ * Deletes all the chunks of this file in the database.
+ *
+ * @method
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.unlink = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.db.s.topology, unlink, [this, options, callback], {
+ skipSessions: true
+ });
+};
+
+var unlink = function(self, options, callback) {
+ deleteChunks(self, function(err) {
+ if (err !== null) {
+ err.message = 'at deleteChunks: ' + err.message;
+ return callback(err);
+ }
+
+ self.collection(function(err, collection) {
+ if (err !== null) {
+ err.message = 'at collection: ' + err.message;
+ return callback(err);
+ }
+
+ collection.remove({ _id: self.fileId }, self.writeConcern, function(err) {
+ callback(err, self);
+ });
+ });
+ });
+};
+
+/**
+ * Retrieves the file collection associated with this object.
+ *
+ * @method
+ * @param {GridStore~collectionCallback} callback the command callback.
+ * @return {Collection}
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.collection = function(callback) {
+ if (typeof callback === 'function') this.db.collection(this.root + '.files', callback);
+ return this.db.collection(this.root + '.files');
+};
+
+/**
+ * The readlines callback format.
+ * @callback GridStore~readlinesCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {string[]} strings The array of strings returned.
+ */
+
+/**
+ * Read the entire file as a list of strings splitting by the provided separator.
+ *
+ * @method
+ * @param {string} [separator] The character to be recognized as the newline separator.
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~readlinesCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.readlines = function(separator, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ separator = args.length ? args.shift() : '\n';
+ separator = separator || '\n';
+ options = args.length ? args.shift() : {};
+
+ return executeOperation(this.db.s.topology, readlines, [this, separator, options, callback], {
+ skipSessions: true
+ });
+};
+
+var readlines = function(self, separator, options, callback) {
+ self.read(function(err, data) {
+ if (err) return callback(err);
+
+ var items = data.toString().split(separator);
+ items = items.length > 0 ? items.splice(0, items.length - 1) : [];
+ for (var i = 0; i < items.length; i++) {
+ items[i] = items[i] + separator;
+ }
+
+ callback(null, items);
+ });
+};
+
+/**
+ * Deletes all the chunks of this file in the database if mode was set to "w" or
+ * "w+" and resets the read/write head to the initial position.
+ *
+ * @method
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.rewind = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ return executeOperation(this.db.s.topology, rewind, [this, options, callback], {
+ skipSessions: true
+ });
+};
+
+var rewind = function(self, options, callback) {
+ if (self.currentChunk.chunkNumber !== 0) {
+ if (self.mode[0] === 'w') {
+ deleteChunks(self, function(err) {
+ if (err) return callback(err);
+ self.currentChunk = new Chunk(self, { n: 0 }, self.writeConcern);
+ self.position = 0;
+ callback(null, self);
+ });
+ } else {
+ self.currentChunk(0, function(err, chunk) {
+ if (err) return callback(err);
+ self.currentChunk = chunk;
+ self.currentChunk.rewind();
+ self.position = 0;
+ callback(null, self);
+ });
+ }
+ } else {
+ self.currentChunk.rewind();
+ self.position = 0;
+ callback(null, self);
+ }
+};
+
+/**
+ * The read callback format.
+ * @callback GridStore~readCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {Buffer} data The data read from the GridStore object
+ */
+
+/**
+ * Retrieves the contents of this file and advances the read/write head. Works with Buffers only.
+ *
+ * There are 3 signatures for this method:
+ *
+ * (callback)
+ * (length, callback)
+ * (length, buffer, callback)
+ *
+ * @method
+ * @param {number} [length] the number of characters to read. Reads all the characters from the read/write head to the EOF if not specified.
+ * @param {(string|Buffer)} [buffer] a string to hold temporary data. This is used for storing the string data read so far when recursively calling this method.
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~readCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.read = function(length, buffer, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ length = args.length ? args.shift() : null;
+ buffer = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : {};
+
+ return executeOperation(this.db.s.topology, read, [this, length, buffer, options, callback], {
+ skipSessions: true
+ });
+};
+
+var read = function(self, length, buffer, options, callback) {
+ // The data is a c-terminated string and thus the length - 1
+ var finalLength = length == null ? self.length - self.position : length;
+ var finalBuffer = buffer == null ? Buffer.alloc(finalLength) : buffer;
+ // Add a index to buffer to keep track of writing position or apply current index
+ finalBuffer._index = buffer != null && buffer._index != null ? buffer._index : 0;
+
+ if (self.currentChunk.length() - self.currentChunk.position + finalBuffer._index >= finalLength) {
+ var slice = self.currentChunk.readSlice(finalLength - finalBuffer._index);
+ // Copy content to final buffer
+ slice.copy(finalBuffer, finalBuffer._index);
+ // Update internal position
+ self.position = self.position + finalBuffer.length;
+ // Check if we don't have a file at all
+ if (finalLength === 0 && finalBuffer.length === 0)
+ return callback(MongoError.create({ message: 'File does not exist', driver: true }), null);
+ // Else return data
+ return callback(null, finalBuffer);
+ }
+
+ // Read the next chunk
+ slice = self.currentChunk.readSlice(self.currentChunk.length() - self.currentChunk.position);
+ // Copy content to final buffer
+ slice.copy(finalBuffer, finalBuffer._index);
+ // Update index position
+ finalBuffer._index += slice.length;
+
+ // Load next chunk and read more
+ nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) {
+ if (err) return callback(err);
+
+ if (chunk.length() > 0) {
+ self.currentChunk = chunk;
+ self.read(length, finalBuffer, callback);
+ } else {
+ if (finalBuffer._index > 0) {
+ callback(null, finalBuffer);
+ } else {
+ callback(
+ MongoError.create({
+ message: 'no chunks found for file, possibly corrupt',
+ driver: true
+ }),
+ null
+ );
+ }
+ }
+ });
+};
+
+/**
+ * The tell callback format.
+ * @callback GridStore~tellCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {number} position The current read position in the GridStore.
+ */
+
+/**
+ * Retrieves the position of the read/write head of this file.
+ *
+ * @method
+ * @param {number} [length] the number of characters to read. Reads all the characters from the read/write head to the EOF if not specified.
+ * @param {(string|Buffer)} [buffer] a string to hold temporary data. This is used for storing the string data read so far when recursively calling this method.
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~tellCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.tell = function(callback) {
+ var self = this;
+ // We provided a callback leg
+ if (typeof callback === 'function') return callback(null, this.position);
+ // Return promise
+ return new self.promiseLibrary(function(resolve) {
+ resolve(self.position);
+ });
+};
+
+/**
+ * The tell callback format.
+ * @callback GridStore~gridStoreCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {GridStore} gridStore The gridStore.
+ */
+
+/**
+ * Moves the read/write head to a new location.
+ *
+ * There are 3 signatures for this method
+ *
+ * Seek Location Modes
+ * - **GridStore.IO_SEEK_SET**, **(default)** set the position from the start of the file.
+ * - **GridStore.IO_SEEK_CUR**, set the position from the current position in the file.
+ * - **GridStore.IO_SEEK_END**, set the position from the end of the file.
+ *
+ * @method
+ * @param {number} [position] the position to seek to
+ * @param {number} [seekLocation] seek mode. Use one of the Seek Location modes.
+ * @param {object} [options] Optional settings
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~gridStoreCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.prototype.seek = function(position, seekLocation, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ seekLocation = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : {};
+
+ return executeOperation(
+ this.db.s.topology,
+ seek,
+ [this, position, seekLocation, options, callback],
+ { skipSessions: true }
+ );
+};
+
+var seek = function(self, position, seekLocation, options, callback) {
+ // Seek only supports read mode
+ if (self.mode !== 'r') {
+ return callback(
+ MongoError.create({ message: 'seek is only supported for mode r', driver: true })
+ );
+ }
+
+ var seekLocationFinal = seekLocation == null ? GridStore.IO_SEEK_SET : seekLocation;
+ var finalPosition = position;
+ var targetPosition = 0;
+
+ // Calculate the position
+ if (seekLocationFinal === GridStore.IO_SEEK_CUR) {
+ targetPosition = self.position + finalPosition;
+ } else if (seekLocationFinal === GridStore.IO_SEEK_END) {
+ targetPosition = self.length + finalPosition;
+ } else {
+ targetPosition = finalPosition;
+ }
+
+ // Get the chunk
+ var newChunkNumber = Math.floor(targetPosition / self.chunkSize);
+ var seekChunk = function() {
+ nthChunk(self, newChunkNumber, function(err, chunk) {
+ if (err) return callback(err, null);
+ if (chunk == null) return callback(new Error('no chunk found'));
+
+ // Set the current chunk
+ self.currentChunk = chunk;
+ self.position = targetPosition;
+ self.currentChunk.position = self.position % self.chunkSize;
+ callback(err, self);
+ });
+ };
+
+ seekChunk();
+};
+
+/**
+ * @ignore
+ */
+var _open = function(self, options, callback) {
+ var collection = self.collection();
+ // Create the query
+ var query =
+ self.referenceBy === REFERENCE_BY_ID ? { _id: self.fileId } : { filename: self.filename };
+ query = null == self.fileId && self.filename == null ? null : query;
+ options.readPreference = self.readPreference;
+
+ // Fetch the chunks
+ if (query != null) {
+ collection.findOne(query, options, function(err, doc) {
+ if (err) {
+ return error(err);
+ }
+
+ // Check if the collection for the files exists otherwise prepare the new one
+ if (doc != null) {
+ self.fileId = doc._id;
+ // Prefer a new filename over the existing one if this is a write
+ self.filename =
+ self.mode === 'r' || self.filename === undefined ? doc.filename : self.filename;
+ self.contentType = doc.contentType;
+ self.internalChunkSize = doc.chunkSize;
+ self.uploadDate = doc.uploadDate;
+ self.aliases = doc.aliases;
+ self.length = doc.length;
+ self.metadata = doc.metadata;
+ self.internalMd5 = doc.md5;
+ } else if (self.mode !== 'r') {
+ self.fileId = self.fileId == null ? new ObjectID() : self.fileId;
+ self.contentType = GridStore.DEFAULT_CONTENT_TYPE;
+ self.internalChunkSize =
+ self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
+ self.length = 0;
+ } else {
+ self.length = 0;
+ var txtId = self.fileId._bsontype === 'ObjectID' ? self.fileId.toHexString() : self.fileId;
+ return error(
+ MongoError.create({
+ message: f(
+ 'file with id %s not opened for writing',
+ self.referenceBy === REFERENCE_BY_ID ? txtId : self.filename
+ ),
+ driver: true
+ }),
+ self
+ );
+ }
+
+ // Process the mode of the object
+ if (self.mode === 'r') {
+ nthChunk(self, 0, options, function(err, chunk) {
+ if (err) return error(err);
+ self.currentChunk = chunk;
+ self.position = 0;
+ callback(null, self);
+ });
+ } else if (self.mode === 'w' && doc) {
+ // Delete any existing chunks
+ deleteChunks(self, options, function(err) {
+ if (err) return error(err);
+ self.currentChunk = new Chunk(self, { n: 0 }, self.writeConcern);
+ self.contentType =
+ self.options['content_type'] == null ? self.contentType : self.options['content_type'];
+ self.internalChunkSize =
+ self.options['chunk_size'] == null
+ ? self.internalChunkSize
+ : self.options['chunk_size'];
+ self.metadata =
+ self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
+ self.position = 0;
+ callback(null, self);
+ });
+ } else if (self.mode === 'w') {
+ self.currentChunk = new Chunk(self, { n: 0 }, self.writeConcern);
+ self.contentType =
+ self.options['content_type'] == null ? self.contentType : self.options['content_type'];
+ self.internalChunkSize =
+ self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
+ self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
+ self.position = 0;
+ callback(null, self);
+ } else if (self.mode === 'w+') {
+ nthChunk(self, lastChunkNumber(self), options, function(err, chunk) {
+ if (err) return error(err);
+ // Set the current chunk
+ self.currentChunk = chunk == null ? new Chunk(self, { n: 0 }, self.writeConcern) : chunk;
+ self.currentChunk.position = self.currentChunk.data.length();
+ self.metadata =
+ self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
+ self.position = self.length;
+ callback(null, self);
+ });
+ }
+ });
+ } else {
+ // Write only mode
+ self.fileId = null == self.fileId ? new ObjectID() : self.fileId;
+ self.contentType = GridStore.DEFAULT_CONTENT_TYPE;
+ self.internalChunkSize =
+ self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
+ self.length = 0;
+
+ // No file exists set up write mode
+ if (self.mode === 'w') {
+ // Delete any existing chunks
+ deleteChunks(self, options, function(err) {
+ if (err) return error(err);
+ self.currentChunk = new Chunk(self, { n: 0 }, self.writeConcern);
+ self.contentType =
+ self.options['content_type'] == null ? self.contentType : self.options['content_type'];
+ self.internalChunkSize =
+ self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
+ self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
+ self.position = 0;
+ callback(null, self);
+ });
+ } else if (self.mode === 'w+') {
+ nthChunk(self, lastChunkNumber(self), options, function(err, chunk) {
+ if (err) return error(err);
+ // Set the current chunk
+ self.currentChunk = chunk == null ? new Chunk(self, { n: 0 }, self.writeConcern) : chunk;
+ self.currentChunk.position = self.currentChunk.data.length();
+ self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.aliases = self.options['aliases'] == null ? self.aliases : self.options['aliases'];
+ self.position = self.length;
+ callback(null, self);
+ });
+ }
+ }
+
+ // only pass error to callback once
+ function error(err) {
+ if (error.err) return;
+ callback((error.err = err));
+ }
+};
+
+/**
+ * @ignore
+ */
+var writeBuffer = function(self, buffer, close, callback) {
+ if (typeof close === 'function') {
+ callback = close;
+ close = null;
+ }
+ var finalClose = typeof close === 'boolean' ? close : false;
+
+ if (self.mode !== 'w') {
+ callback(
+ MongoError.create({
+ message: f(
+ 'file with id %s not opened for writing',
+ self.referenceBy === REFERENCE_BY_ID ? self.referenceBy : self.filename
+ ),
+ driver: true
+ }),
+ null
+ );
+ } else {
+ if (self.currentChunk.position + buffer.length >= self.chunkSize) {
+ // Write out the current Chunk and then keep writing until we have less data left than a chunkSize left
+ // to a new chunk (recursively)
+ var previousChunkNumber = self.currentChunk.chunkNumber;
+ var leftOverDataSize = self.chunkSize - self.currentChunk.position;
+ var firstChunkData = buffer.slice(0, leftOverDataSize);
+ var leftOverData = buffer.slice(leftOverDataSize);
+ // A list of chunks to write out
+ var chunksToWrite = [self.currentChunk.write(firstChunkData)];
+ // If we have more data left than the chunk size let's keep writing new chunks
+ while (leftOverData.length >= self.chunkSize) {
+ // Create a new chunk and write to it
+ var newChunk = new Chunk(self, { n: previousChunkNumber + 1 }, self.writeConcern);
+ firstChunkData = leftOverData.slice(0, self.chunkSize);
+ leftOverData = leftOverData.slice(self.chunkSize);
+ // Update chunk number
+ previousChunkNumber = previousChunkNumber + 1;
+ // Write data
+ newChunk.write(firstChunkData);
+ // Push chunk to save list
+ chunksToWrite.push(newChunk);
+ }
+
+ // Set current chunk with remaining data
+ self.currentChunk = new Chunk(self, { n: previousChunkNumber + 1 }, self.writeConcern);
+ // If we have left over data write it
+ if (leftOverData.length > 0) self.currentChunk.write(leftOverData);
+
+ // Update the position for the gridstore
+ self.position = self.position + buffer.length;
+ // Total number of chunks to write
+ var numberOfChunksToWrite = chunksToWrite.length;
+
+ for (var i = 0; i < chunksToWrite.length; i++) {
+ chunksToWrite[i].save({}, function(err) {
+ if (err) return callback(err);
+
+ numberOfChunksToWrite = numberOfChunksToWrite - 1;
+
+ if (numberOfChunksToWrite <= 0) {
+ // We care closing the file before returning
+ if (finalClose) {
+ return self.close(function(err) {
+ callback(err, self);
+ });
+ }
+
+ // Return normally
+ return callback(null, self);
+ }
+ });
+ }
+ } else {
+ // Update the position for the gridstore
+ self.position = self.position + buffer.length;
+ // We have less data than the chunk size just write it and callback
+ self.currentChunk.write(buffer);
+ // We care closing the file before returning
+ if (finalClose) {
+ return self.close(function(err) {
+ callback(err, self);
+ });
+ }
+ // Return normally
+ return callback(null, self);
+ }
+ }
+};
+
+/**
+ * Creates a mongoDB object representation of this object.
+ *
+ *
+ * {
+ * '_id' : , // {number} id for this file
+ * 'filename' : , // {string} name for this file
+ * 'contentType' : , // {string} mime type for this file
+ * 'length' : , // {number} size of this file?
+ * 'chunksize' : , // {number} chunk size used by this file
+ * 'uploadDate' : , // {Date}
+ * 'aliases' : , // {array of string}
+ * 'metadata' : , // {string}
+ * }
+ *
+ *
+ * @ignore
+ */
+var buildMongoObject = function(self, callback) {
+ // Calcuate the length
+ var mongoObject = {
+ _id: self.fileId,
+ filename: self.filename,
+ contentType: self.contentType,
+ length: self.position ? self.position : 0,
+ chunkSize: self.chunkSize,
+ uploadDate: self.uploadDate,
+ aliases: self.aliases,
+ metadata: self.metadata
+ };
+
+ var md5Command = { filemd5: self.fileId, root: self.root };
+ self.db.command(md5Command, function(err, results) {
+ if (err) return callback(err);
+
+ mongoObject.md5 = results.md5;
+ callback(null, mongoObject);
+ });
+};
+
+/**
+ * Gets the nth chunk of this file.
+ * @ignore
+ */
+var nthChunk = function(self, chunkNumber, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ options = options || self.writeConcern;
+ options.readPreference = self.readPreference;
+ // Get the nth chunk
+ self
+ .chunkCollection()
+ .findOne({ files_id: self.fileId, n: chunkNumber }, options, function(err, chunk) {
+ if (err) return callback(err);
+
+ var finalChunk = chunk == null ? {} : chunk;
+ callback(null, new Chunk(self, finalChunk, self.writeConcern));
+ });
+};
+
+/**
+ * @ignore
+ */
+var lastChunkNumber = function(self) {
+ return Math.floor((self.length ? self.length - 1 : 0) / self.chunkSize);
+};
+
+/**
+ * Deletes all the chunks of this file in the database.
+ *
+ * @ignore
+ */
+var deleteChunks = function(self, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ options = options || self.writeConcern;
+
+ if (self.fileId != null) {
+ self.chunkCollection().remove({ files_id: self.fileId }, options, function(err) {
+ if (err) return callback(err, false);
+ callback(null, true);
+ });
+ } else {
+ callback(null, true);
+ }
+};
+
+/**
+ * The collection to be used for holding the files and chunks collection.
+ *
+ * @classconstant DEFAULT_ROOT_COLLECTION
+ */
+GridStore.DEFAULT_ROOT_COLLECTION = 'fs';
+
+/**
+ * Default file mime type
+ *
+ * @classconstant DEFAULT_CONTENT_TYPE
+ */
+GridStore.DEFAULT_CONTENT_TYPE = 'binary/octet-stream';
+
+/**
+ * Seek mode where the given length is absolute.
+ *
+ * @classconstant IO_SEEK_SET
+ */
+GridStore.IO_SEEK_SET = 0;
+
+/**
+ * Seek mode where the given length is an offset to the current read/write head.
+ *
+ * @classconstant IO_SEEK_CUR
+ */
+GridStore.IO_SEEK_CUR = 1;
+
+/**
+ * Seek mode where the given length is an offset to the end of the file.
+ *
+ * @classconstant IO_SEEK_END
+ */
+GridStore.IO_SEEK_END = 2;
+
+/**
+ * Checks if a file exists in the database.
+ *
+ * @method
+ * @static
+ * @param {Db} db the database to query.
+ * @param {string} name The name of the file to look for.
+ * @param {string} [rootCollection] The root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] result from exists.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.exist = function(db, fileIdObject, rootCollection, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ rootCollection = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : {};
+ options = options || {};
+
+ return executeOperation(
+ db.s.topology,
+ exists,
+ [db, fileIdObject, rootCollection, options, callback],
+ { skipSessions: true }
+ );
+};
+
+var exists = function(db, fileIdObject, rootCollection, options, callback) {
+ // Establish read preference
+ var readPreference = options.readPreference || ReadPreference.PRIMARY;
+ // Fetch collection
+ var rootCollectionFinal =
+ rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;
+ db.collection(rootCollectionFinal + '.files', function(err, collection) {
+ if (err) return callback(err);
+
+ // Build query
+ var query =
+ typeof fileIdObject === 'string' ||
+ Object.prototype.toString.call(fileIdObject) === '[object RegExp]'
+ ? { filename: fileIdObject }
+ : { _id: fileIdObject }; // Attempt to locate file
+
+ // We have a specific query
+ if (
+ fileIdObject != null &&
+ typeof fileIdObject === 'object' &&
+ Object.prototype.toString.call(fileIdObject) !== '[object RegExp]'
+ ) {
+ query = fileIdObject;
+ }
+
+ // Check if the entry exists
+ collection.findOne(query, { readPreference: readPreference }, function(err, item) {
+ if (err) return callback(err);
+ callback(null, item == null ? false : true);
+ });
+ });
+};
+
+/**
+ * Gets the list of files stored in the GridFS.
+ *
+ * @method
+ * @static
+ * @param {Db} db the database to query.
+ * @param {string} [rootCollection] The root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] result from exists.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.list = function(db, rootCollection, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ rootCollection = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : {};
+ options = options || {};
+
+ return executeOperation(db.s.topology, list, [db, rootCollection, options, callback], {
+ skipSessions: true
+ });
+};
+
+var list = function(db, rootCollection, options, callback) {
+ // Ensure we have correct values
+ if (rootCollection != null && typeof rootCollection === 'object') {
+ options = rootCollection;
+ rootCollection = null;
+ }
+
+ // Establish read preference
+ var readPreference = options.readPreference || ReadPreference.primary;
+ // Check if we are returning by id not filename
+ var byId = options['id'] != null ? options['id'] : false;
+ // Fetch item
+ var rootCollectionFinal =
+ rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;
+ var items = [];
+ db.collection(rootCollectionFinal + '.files', function(err, collection) {
+ if (err) return callback(err);
+
+ collection.find({}, { readPreference: readPreference }, function(err, cursor) {
+ if (err) return callback(err);
+
+ cursor.each(function(err, item) {
+ if (item != null) {
+ items.push(byId ? item._id : item.filename);
+ } else {
+ callback(err, items);
+ }
+ });
+ });
+ });
+};
+
+/**
+ * Reads the contents of a file.
+ *
+ * This method has the following signatures
+ *
+ * (db, name, callback)
+ * (db, name, length, callback)
+ * (db, name, length, offset, callback)
+ * (db, name, length, offset, options, callback)
+ *
+ * @method
+ * @static
+ * @param {Db} db the database to query.
+ * @param {string} name The name of the file.
+ * @param {number} [length] The size of data to read.
+ * @param {number} [offset] The offset from the head of the file of which to start reading from.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~readCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.read = function(db, name, length, offset, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ length = args.length ? args.shift() : null;
+ offset = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : null;
+ options = options || {};
+
+ return executeOperation(
+ db.s.topology,
+ readStatic,
+ [db, name, length, offset, options, callback],
+ { skipSessions: true }
+ );
+};
+
+var readStatic = function(db, name, length, offset, options, callback) {
+ new GridStore(db, name, 'r', options).open(function(err, gridStore) {
+ if (err) return callback(err);
+ // Make sure we are not reading out of bounds
+ if (offset && offset >= gridStore.length)
+ return callback('offset larger than size of file', null);
+ if (length && length > gridStore.length)
+ return callback('length is larger than the size of the file', null);
+ if (offset && length && offset + length > gridStore.length)
+ return callback('offset and length is larger than the size of the file', null);
+
+ if (offset != null) {
+ gridStore.seek(offset, function(err, gridStore) {
+ if (err) return callback(err);
+ gridStore.read(length, callback);
+ });
+ } else {
+ gridStore.read(length, callback);
+ }
+ });
+};
+
+/**
+ * Read the entire file as a list of strings splitting by the provided separator.
+ *
+ * @method
+ * @static
+ * @param {Db} db the database to query.
+ * @param {(String|object)} name the name of the file.
+ * @param {string} [separator] The character to be recognized as the newline separator.
+ * @param {object} [options] Optional settings.
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~readlinesCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.readlines = function(db, name, separator, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ separator = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : null;
+ options = options || {};
+
+ return executeOperation(
+ db.s.topology,
+ readlinesStatic,
+ [db, name, separator, options, callback],
+ { skipSessions: true }
+ );
+};
+
+var readlinesStatic = function(db, name, separator, options, callback) {
+ var finalSeperator = separator == null ? '\n' : separator;
+ new GridStore(db, name, 'r', options).open(function(err, gridStore) {
+ if (err) return callback(err);
+ gridStore.readlines(finalSeperator, callback);
+ });
+};
+
+/**
+ * Deletes the chunks and metadata information of a file from GridFS.
+ *
+ * @method
+ * @static
+ * @param {Db} db The database to query.
+ * @param {(string|array)} names The name/names of the files to delete.
+ * @param {object} [options] Optional settings.
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @param {GridStore~resultCallback} [callback] the command callback.
+ * @return {Promise} returns Promise if no callback passed
+ * @deprecated Use GridFSBucket API instead
+ */
+GridStore.unlink = function(db, names, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ options = args.length ? args.shift() : {};
+ options = options || {};
+
+ return executeOperation(db.s.topology, unlinkStatic, [this, db, names, options, callback], {
+ skipSessions: true
+ });
+};
+
+var unlinkStatic = function(self, db, names, options, callback) {
+ // Get the write concern
+ var writeConcern = _getWriteConcern(db, options);
+
+ // List of names
+ if (names.constructor === Array) {
+ var tc = 0;
+ for (var i = 0; i < names.length; i++) {
+ ++tc;
+ GridStore.unlink(db, names[i], options, function() {
+ if (--tc === 0) {
+ callback(null, self);
+ }
+ });
+ }
+ } else {
+ new GridStore(db, names, 'w', options).open(function(err, gridStore) {
+ if (err) return callback(err);
+ deleteChunks(gridStore, function(err) {
+ if (err) return callback(err);
+ gridStore.collection(function(err, collection) {
+ if (err) return callback(err);
+ collection.remove({ _id: gridStore.fileId }, writeConcern, function(err) {
+ callback(err, self);
+ });
+ });
+ });
+ });
+ }
+};
+
+/**
+ * @ignore
+ */
+var _writeNormal = function(self, data, close, options, callback) {
+ // If we have a buffer write it using the writeBuffer method
+ if (Buffer.isBuffer(data)) {
+ return writeBuffer(self, data, close, callback);
+ } else {
+ return writeBuffer(self, Buffer.from(data, 'binary'), close, callback);
+ }
+};
+
+/**
+ * @ignore
+ */
+var _setWriteConcernHash = function(options) {
+ var finalOptions = {};
+ if (options.w != null) finalOptions.w = options.w;
+ if (options.journal === true) finalOptions.j = options.journal;
+ if (options.j === true) finalOptions.j = options.j;
+ if (options.fsync === true) finalOptions.fsync = options.fsync;
+ if (options.wtimeout != null) finalOptions.wtimeout = options.wtimeout;
+ return finalOptions;
+};
+
+/**
+ * @ignore
+ */
+var _getWriteConcern = function(self, options) {
+ // Final options
+ var finalOptions = { w: 1 };
+ options = options || {};
+
+ // Local options verification
+ if (
+ options.w != null ||
+ typeof options.j === 'boolean' ||
+ typeof options.journal === 'boolean' ||
+ typeof options.fsync === 'boolean'
+ ) {
+ finalOptions = _setWriteConcernHash(options);
+ } else if (options.safe != null && typeof options.safe === 'object') {
+ finalOptions = _setWriteConcernHash(options.safe);
+ } else if (typeof options.safe === 'boolean') {
+ finalOptions = { w: options.safe ? 1 : 0 };
+ } else if (
+ self.options.w != null ||
+ typeof self.options.j === 'boolean' ||
+ typeof self.options.journal === 'boolean' ||
+ typeof self.options.fsync === 'boolean'
+ ) {
+ finalOptions = _setWriteConcernHash(self.options);
+ } else if (
+ self.safe &&
+ (self.safe.w != null ||
+ typeof self.safe.j === 'boolean' ||
+ typeof self.safe.journal === 'boolean' ||
+ typeof self.safe.fsync === 'boolean')
+ ) {
+ finalOptions = _setWriteConcernHash(self.safe);
+ } else if (typeof self.safe === 'boolean') {
+ finalOptions = { w: self.safe ? 1 : 0 };
+ }
+
+ // Ensure we don't have an invalid combination of write concerns
+ if (
+ finalOptions.w < 1 &&
+ (finalOptions.journal === true || finalOptions.j === true || finalOptions.fsync === true)
+ )
+ throw MongoError.create({
+ message: 'No acknowledgement using w < 1 cannot be combined with journal:true or fsync:true',
+ driver: true
+ });
+
+ // Return the options
+ return finalOptions;
+};
+
+/**
+ * Create a new GridStoreStream instance (INTERNAL TYPE, do not instantiate directly)
+ *
+ * @class
+ * @extends external:Duplex
+ * @return {GridStoreStream} a GridStoreStream instance.
+ * @deprecated Use GridFSBucket API instead
+ */
+var GridStoreStream = function(gs) {
+ // Initialize the duplex stream
+ Duplex.call(this);
+
+ // Get the gridstore
+ this.gs = gs;
+
+ // End called
+ this.endCalled = false;
+
+ // If we have a seek
+ this.totalBytesToRead = this.gs.length - this.gs.position;
+ this.seekPosition = this.gs.position;
+};
+
+//
+// Inherit duplex
+inherits(GridStoreStream, Duplex);
+
+GridStoreStream.prototype._pipe = GridStoreStream.prototype.pipe;
+
+// Set up override
+GridStoreStream.prototype.pipe = function(destination) {
+ var self = this;
+
+ // Only open gridstore if not already open
+ if (!self.gs.isOpen) {
+ self.gs.open(function(err) {
+ if (err) return self.emit('error', err);
+ self.totalBytesToRead = self.gs.length - self.gs.position;
+ self._pipe.apply(self, [destination]);
+ });
+ } else {
+ self.totalBytesToRead = self.gs.length - self.gs.position;
+ self._pipe.apply(self, [destination]);
+ }
+
+ return destination;
+};
+
+// Called by stream
+GridStoreStream.prototype._read = function() {
+ var self = this;
+
+ var read = function() {
+ // Read data
+ self.gs.read(length, function(err, buffer) {
+ if (err && !self.endCalled) return self.emit('error', err);
+
+ // Stream is closed
+ if (self.endCalled || buffer == null) return self.push(null);
+ // Remove bytes read
+ if (buffer.length <= self.totalBytesToRead) {
+ self.totalBytesToRead = self.totalBytesToRead - buffer.length;
+ self.push(buffer);
+ } else if (buffer.length > self.totalBytesToRead) {
+ self.totalBytesToRead = self.totalBytesToRead - buffer._index;
+ self.push(buffer.slice(0, buffer._index));
+ }
+
+ // Finished reading
+ if (self.totalBytesToRead <= 0) {
+ self.endCalled = true;
+ }
+ });
+ };
+
+ // Set read length
+ var length =
+ self.gs.length < self.gs.chunkSize ? self.gs.length - self.seekPosition : self.gs.chunkSize;
+ if (!self.gs.isOpen) {
+ self.gs.open(function(err) {
+ self.totalBytesToRead = self.gs.length - self.gs.position;
+ if (err) return self.emit('error', err);
+ read();
+ });
+ } else {
+ read();
+ }
+};
+
+GridStoreStream.prototype.destroy = function() {
+ this.pause();
+ this.endCalled = true;
+ this.gs.close();
+ this.emit('end');
+};
+
+GridStoreStream.prototype.write = function(chunk) {
+ var self = this;
+ if (self.endCalled)
+ return self.emit(
+ 'error',
+ MongoError.create({ message: 'attempting to write to stream after end called', driver: true })
+ );
+ // Do we have to open the gridstore
+ if (!self.gs.isOpen) {
+ self.gs.open(function() {
+ self.gs.isOpen = true;
+ self.gs.write(chunk, function() {
+ process.nextTick(function() {
+ self.emit('drain');
+ });
+ });
+ });
+ return false;
+ } else {
+ self.gs.write(chunk, function() {
+ self.emit('drain');
+ });
+ return true;
+ }
+};
+
+GridStoreStream.prototype.end = function(chunk, encoding, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ chunk = args.length ? args.shift() : null;
+ encoding = args.length ? args.shift() : null;
+ self.endCalled = true;
+
+ if (chunk) {
+ self.gs.write(chunk, function() {
+ self.gs.close(function() {
+ if (typeof callback === 'function') callback();
+ self.emit('end');
+ });
+ });
+ }
+
+ self.gs.close(function() {
+ if (typeof callback === 'function') callback();
+ self.emit('end');
+ });
+};
+
+/**
+ * The read() method pulls some data out of the internal buffer and returns it. If there is no data available, then it will return null.
+ * @function external:Duplex#read
+ * @param {number} size Optional argument to specify how much data to read.
+ * @return {(String | Buffer | null)}
+ */
+
+/**
+ * Call this function to cause the stream to return strings of the specified encoding instead of Buffer objects.
+ * @function external:Duplex#setEncoding
+ * @param {string} encoding The encoding to use.
+ * @return {null}
+ */
+
+/**
+ * This method will cause the readable stream to resume emitting data events.
+ * @function external:Duplex#resume
+ * @return {null}
+ */
+
+/**
+ * This method will cause a stream in flowing-mode to stop emitting data events. Any data that becomes available will remain in the internal buffer.
+ * @function external:Duplex#pause
+ * @return {null}
+ */
+
+/**
+ * This method pulls all the data out of a readable stream, and writes it to the supplied destination, automatically managing the flow so that the destination is not overwhelmed by a fast readable stream.
+ * @function external:Duplex#pipe
+ * @param {Writable} destination The destination for writing data
+ * @param {object} [options] Pipe options
+ * @return {null}
+ */
+
+/**
+ * This method will remove the hooks set up for a previous pipe() call.
+ * @function external:Duplex#unpipe
+ * @param {Writable} [destination] The destination for writing data
+ * @return {null}
+ */
+
+/**
+ * This is useful in certain cases where a stream is being consumed by a parser, which needs to "un-consume" some data that it has optimistically pulled out of the source, so that the stream can be passed on to some other party.
+ * @function external:Duplex#unshift
+ * @param {(Buffer|string)} chunk Chunk of data to unshift onto the read queue.
+ * @return {null}
+ */
+
+/**
+ * Versions of Node prior to v0.10 had streams that did not implement the entire Streams API as it is today. (See "Compatibility" below for more information.)
+ * @function external:Duplex#wrap
+ * @param {Stream} stream An "old style" readable stream.
+ * @return {null}
+ */
+
+/**
+ * This method writes some data to the underlying system, and calls the supplied callback once the data has been fully handled.
+ * @function external:Duplex#write
+ * @param {(string|Buffer)} chunk The data to write
+ * @param {string} encoding The encoding, if chunk is a String
+ * @param {function} callback Callback for when this chunk of data is flushed
+ * @return {boolean}
+ */
+
+/**
+ * Call this method when no more data will be written to the stream. If supplied, the callback is attached as a listener on the finish event.
+ * @function external:Duplex#end
+ * @param {(string|Buffer)} chunk The data to write
+ * @param {string} encoding The encoding, if chunk is a String
+ * @param {function} callback Callback for when this chunk of data is flushed
+ * @return {null}
+ */
+
+/**
+ * GridStoreStream stream data event, fired for each document in the cursor.
+ *
+ * @event GridStoreStream#data
+ * @type {object}
+ */
+
+/**
+ * GridStoreStream stream end event
+ *
+ * @event GridStoreStream#end
+ * @type {null}
+ */
+
+/**
+ * GridStoreStream stream close event
+ *
+ * @event GridStoreStream#close
+ * @type {null}
+ */
+
+/**
+ * GridStoreStream stream readable event
+ *
+ * @event GridStoreStream#readable
+ * @type {null}
+ */
+
+/**
+ * GridStoreStream stream drain event
+ *
+ * @event GridStoreStream#drain
+ * @type {null}
+ */
+
+/**
+ * GridStoreStream stream finish event
+ *
+ * @event GridStoreStream#finish
+ * @type {null}
+ */
+
+/**
+ * GridStoreStream stream pipe event
+ *
+ * @event GridStoreStream#pipe
+ * @type {null}
+ */
+
+/**
+ * GridStoreStream stream unpipe event
+ *
+ * @event GridStoreStream#unpipe
+ * @type {null}
+ */
+
+/**
+ * GridStoreStream stream error event
+ *
+ * @event GridStoreStream#error
+ * @type {null}
+ */
+
+/**
+ * @ignore
+ */
+module.exports = GridStore;
diff --git a/node_modules/mongodb/lib/mongo_client.js b/node_modules/mongodb/lib/mongo_client.js
new file mode 100644
index 0000000..e6e9381
--- /dev/null
+++ b/node_modules/mongodb/lib/mongo_client.js
@@ -0,0 +1,472 @@
+'use strict';
+
+const ChangeStream = require('./change_stream');
+const Db = require('./db');
+const EventEmitter = require('events').EventEmitter;
+const executeOperation = require('./utils').executeOperation;
+const handleCallback = require('./utils').handleCallback;
+const inherits = require('util').inherits;
+const MongoError = require('mongodb-core').MongoError;
+
+// Operations
+const connectOp = require('./operations/mongo_client_ops').connectOp;
+const logout = require('./operations/mongo_client_ops').logout;
+const validOptions = require('./operations/mongo_client_ops').validOptions;
+
+/**
+ * @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
+ *
+ * @example
+ * // Connect using a MongoClient instance
+ * const MongoClient = require('mongodb').MongoClient;
+ * const test = require('assert');
+ * // Connection url
+ * const url = 'mongodb://localhost:27017';
+ * // Database Name
+ * const dbName = 'test';
+ * // Connect using MongoClient
+ * const mongoClient = new MongoClient(url);
+ * mongoClient.connect(function(err, client) {
+ * const db = client.db(dbName);
+ * client.close();
+ * });
+ *
+ * @example
+ * // Connect using the MongoClient.connect static method
+ * const MongoClient = require('mongodb').MongoClient;
+ * const test = require('assert');
+ * // Connection url
+ * const url = 'mongodb://localhost:27017';
+ * // Database Name
+ * const dbName = 'test';
+ * // Connect using MongoClient
+ * MongoClient.connect(url, function(err, client) {
+ * const db = client.db(dbName);
+ * client.close();
+ * });
+ */
+
+/**
+ * Creates a new MongoClient instance
+ * @class
+ * @param {string} url The connection URI string
+ * @param {object} [options] Optional settings
+ * @param {number} [options.poolSize=5] The maximum size of the individual server pool
+ * @param {boolean} [options.ssl=false] Enable SSL connection.
+ * @param {boolean} [options.sslValidate=true] Validate mongod server certificate against Certificate Authority
+ * @param {buffer} [options.sslCA=undefined] SSL Certificate store binary buffer
+ * @param {buffer} [options.sslCert=undefined] SSL Certificate binary buffer
+ * @param {buffer} [options.sslKey=undefined] SSL Key file binary buffer
+ * @param {string} [options.sslPass=undefined] SSL Certificate pass phrase
+ * @param {buffer} [options.sslCRL=undefined] SSL Certificate revocation list binary buffer
+ * @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
+ * @param {boolean} [options.noDelay=true] TCP Connection no delay
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
+ * @param {number} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
+ * @param {number} [options.connectTimeoutMS=30000] TCP Connection timeout setting
+ * @param {number} [options.family] Version of IP stack. Can be 4, 6 or null (default).
+ * If null, will attempt to connect with IPv6, and will fall back to IPv4 on failure
+ * @param {number} [options.socketTimeoutMS=360000] TCP Socket timeout setting
+ * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
+ * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
+ * @param {boolean} [options.ha=true] Control if high availability monitoring runs for Replicaset or Mongos proxies
+ * @param {number} [options.haInterval=10000] The High availability period for replicaset inquiry
+ * @param {string} [options.replicaSet=undefined] The Replicaset set name
+ * @param {number} [options.secondaryAcceptableLatencyMS=15] Cutoff latency point in MS for Replicaset member selection
+ * @param {number} [options.acceptableLatencyMS=15] Cutoff latency point in MS for Mongos proxies selection
+ * @param {boolean} [options.connectWithNoPrimary=false] Sets if the driver should connect even if no primary is available
+ * @param {string} [options.authSource=undefined] Define the database to authenticate against
+ * @param {(number|string)} [options.w] The write concern
+ * @param {number} [options.wtimeout] The write concern timeout
+ * @param {boolean} [options.j=false] Specify a journal write concern
+ * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver
+ * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers
+ * @param {number} [options.bufferMaxEntries=-1] Sets a cap on how many operations the driver will buffer up before giving up on getting a working connection, default is -1 which is unlimited
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST)
+ * @param {object} [options.pkFactory] A primary key factory object for generation of custom _id keys
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {object} [options.readConcern] Specify a read concern for the collection (only MongoDB 3.2 or higher supported)
+ * @param {string} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
+ * @param {number} [options.maxStalenessSeconds=undefined] The max staleness to secondary reads (values under 10 seconds cannot be guaranteed)
+ * @param {string} [options.loggerLevel=undefined] The logging level (error/warn/info/debug)
+ * @param {object} [options.logger=undefined] Custom logger object
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers
+ * @param {boolean} [options.promoteLongs=true] Promotes long values to number if they fit inside the 53 bits resolution
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function
+ * @param {object} [options.validateOptions=false] Validate MongoClient passed in options for correctness
+ * @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections
+ * @param {string} [options.auth.user=undefined] The username for auth
+ * @param {string} [options.auth.password=undefined] The password for auth
+ * @param {string} [options.authMechanism=undefined] Mechanism for authentication: MDEFAULT, GSSAPI, PLAIN, MONGODB-X509, or SCRAM-SHA-1
+ * @param {object} [options.compression] Type of compression to use: snappy or zlib
+ * @param {boolean} [options.fsync=false] Specify a file sync write concern
+ * @param {array} [options.readPreferenceTags] Read preference tags
+ * @param {number} [options.numberOfRetries=5] The number of retries for a tailable cursor
+ * @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances
+ * @param {boolean} [options.monitorCommands=false] Enable command monitoring for this client
+ * @param {number} [options.minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
+ * @param {boolean} [options.useNewUrlParser=false] Determines whether or not to use the new url parser. Enables the new, spec-compliant, url parser shipped in the core driver. This url parser fixes a number of problems with the original parser, and aims to outright replace that parser in the near future.
+ * @param {MongoClient~connectCallback} [callback] The command result callback
+ * @return {MongoClient} a MongoClient instance
+ */
+function MongoClient(url, options) {
+ if (!(this instanceof MongoClient)) return new MongoClient(url, options);
+ // Set up event emitter
+ EventEmitter.call(this);
+
+ // The internal state
+ this.s = {
+ url: url,
+ options: options || {},
+ promiseLibrary: null,
+ dbCache: {},
+ sessions: []
+ };
+
+ // Get the promiseLibrary
+ const promiseLibrary = this.s.options.promiseLibrary || Promise;
+
+ // Add the promise to the internal state
+ this.s.promiseLibrary = promiseLibrary;
+}
+
+/**
+ * @ignore
+ */
+inherits(MongoClient, EventEmitter);
+
+/**
+ * The callback format for results
+ * @callback MongoClient~connectCallback
+ * @param {MongoError} error An error instance representing the error during the execution.
+ * @param {MongoClient} client The connected client.
+ */
+
+/**
+ * Connect to MongoDB using a url as documented at
+ *
+ * docs.mongodb.org/manual/reference/connection-string/
+ *
+ * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
+ *
+ * @method
+ * @param {MongoClient~connectCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+MongoClient.prototype.connect = function(callback) {
+ // Validate options object
+ const err = validOptions(this.s.options);
+
+ if (typeof callback === 'string') {
+ throw new TypeError('`connect` only accepts a callback');
+ }
+
+ return executeOperation(this, connectOp, [this, err, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * Logout user from server, fire off on all connections and remove all auth info
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {string} [options.dbName] Logout against different database than current.
+ * @param {Db~resultCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+MongoClient.prototype.logout = function(options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ // Establish the correct database name
+ const dbName = this.s.options.authSource ? this.s.options.authSource : this.s.options.dbName;
+
+ return executeOperation(this, logout, [this, dbName, callback], {
+ skipSessions: true
+ });
+};
+
+/**
+ * Close the db and its underlying connections
+ * @method
+ * @param {boolean} force Force close, emitting no events
+ * @param {Db~noResultCallback} [callback] The result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+MongoClient.prototype.close = function(force, callback) {
+ if (typeof force === 'function') (callback = force), (force = false);
+
+ // Close the topology connection
+ if (this.topology) {
+ this.topology.close(force);
+ }
+ // Emit close event
+ this.emit('close', this);
+
+ // Fire close event on any cached db instances
+ for (const name in this.s.dbCache) {
+ this.s.dbCache[name].emit('close');
+ }
+
+ // Remove listeners after emit
+ this.removeAllListeners('close');
+
+ // Callback after next event loop tick
+ if (typeof callback === 'function')
+ return process.nextTick(() => {
+ handleCallback(callback, null);
+ });
+
+ // Return dummy promise
+ return new this.s.promiseLibrary(resolve => {
+ resolve();
+ });
+};
+
+/**
+ * Create a new Db instance sharing the current socket connections. Be aware that the new db instances are
+ * related in a parent-child relationship to the original instance so that events are correctly emitted on child
+ * db instances. Child db instances are cached so performing db('db1') twice will return the same instance.
+ * You can control these behaviors with the options noListener and returnNonCachedInstance.
+ *
+ * @method
+ * @param {string} [dbName] The name of the database we want to use. If not provided, use database name from connection string.
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.noListener=false] Do not make the db an event listener to the original connection.
+ * @param {boolean} [options.returnNonCachedInstance=false] Control if you want to return a cached instance or have a new one created
+ * @return {Db}
+ */
+MongoClient.prototype.db = function(dbName, options) {
+ options = options || {};
+
+ // Default to db from connection string if not provided
+ if (!dbName) {
+ dbName = this.s.options.dbName;
+ }
+
+ // Copy the options and add out internal override of the not shared flag
+ const finalOptions = Object.assign({}, this.s.options, options);
+
+ // Do we have the db in the cache already
+ if (this.s.dbCache[dbName] && finalOptions.returnNonCachedInstance !== true) {
+ return this.s.dbCache[dbName];
+ }
+
+ // Add promiseLibrary
+ finalOptions.promiseLibrary = this.s.promiseLibrary;
+
+ // If no topology throw an error message
+ if (!this.topology) {
+ throw new MongoError('MongoClient must be connected before calling MongoClient.prototype.db');
+ }
+
+ // Return the db object
+ const db = new Db(dbName, this.topology, finalOptions);
+
+ // Add the db to the cache
+ this.s.dbCache[dbName] = db;
+ // Return the database
+ return db;
+};
+
+/**
+ * Check if MongoClient is connected
+ *
+ * @method
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.noListener=false] Do not make the db an event listener to the original connection.
+ * @param {boolean} [options.returnNonCachedInstance=false] Control if you want to return a cached instance or have a new one created
+ * @return {boolean}
+ */
+MongoClient.prototype.isConnected = function(options) {
+ options = options || {};
+
+ if (!this.topology) return false;
+ return this.topology.isConnected(options);
+};
+
+/**
+ * Connect to MongoDB using a url as documented at
+ *
+ * docs.mongodb.org/manual/reference/connection-string/
+ *
+ * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
+ *
+ * @method
+ * @static
+ * @param {string} url The connection URI string
+ * @param {object} [options] Optional settings
+ * @param {number} [options.poolSize=5] The maximum size of the individual server pool
+ * @param {boolean} [options.ssl=false] Enable SSL connection.
+ * @param {boolean} [options.sslValidate=true] Validate mongod server certificate against Certificate Authority
+ * @param {buffer} [options.sslCA=undefined] SSL Certificate store binary buffer
+ * @param {buffer} [options.sslCert=undefined] SSL Certificate binary buffer
+ * @param {buffer} [options.sslKey=undefined] SSL Key file binary buffer
+ * @param {string} [options.sslPass=undefined] SSL Certificate pass phrase
+ * @param {buffer} [options.sslCRL=undefined] SSL Certificate revocation list binary buffer
+ * @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
+ * @param {boolean} [options.noDelay=true] TCP Connection no delay
+ * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
+ * @param {boolean} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
+ * @param {number} [options.connectTimeoutMS=30000] TCP Connection timeout setting
+ * @param {number} [options.family] Version of IP stack. Can be 4, 6 or null (default).
+ * If null, will attempt to connect with IPv6, and will fall back to IPv4 on failure
+ * @param {number} [options.socketTimeoutMS=360000] TCP Socket timeout setting
+ * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
+ * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
+ * @param {boolean} [options.ha=true] Control if high availability monitoring runs for Replicaset or Mongos proxies
+ * @param {number} [options.haInterval=10000] The High availability period for replicaset inquiry
+ * @param {string} [options.replicaSet=undefined] The Replicaset set name
+ * @param {number} [options.secondaryAcceptableLatencyMS=15] Cutoff latency point in MS for Replicaset member selection
+ * @param {number} [options.acceptableLatencyMS=15] Cutoff latency point in MS for Mongos proxies selection
+ * @param {boolean} [options.connectWithNoPrimary=false] Sets if the driver should connect even if no primary is available
+ * @param {string} [options.authSource=undefined] Define the database to authenticate against
+ * @param {(number|string)} [options.w] The write concern
+ * @param {number} [options.wtimeout] The write concern timeout
+ * @param {boolean} [options.j=false] Specify a journal write concern
+ * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver
+ * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
+ * @param {boolean} [options.raw=false] Return document results as raw BSON buffers
+ * @param {number} [options.bufferMaxEntries=-1] Sets a cap on how many operations the driver will buffer up before giving up on getting a working connection, default is -1 which is unlimited
+ * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST)
+ * @param {object} [options.pkFactory] A primary key factory object for generation of custom _id keys
+ * @param {object} [options.promiseLibrary] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
+ * @param {object} [options.readConcern] Specify a read concern for the collection (only MongoDB 3.2 or higher supported)
+ * @param {string} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
+ * @param {number} [options.maxStalenessSeconds=undefined] The max staleness to secondary reads (values under 10 seconds cannot be guaranteed)
+ * @param {string} [options.loggerLevel=undefined] The logging level (error/warn/info/debug)
+ * @param {object} [options.logger=undefined] Custom logger object
+ * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types
+ * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers
+ * @param {boolean} [options.promoteLongs=true] Promotes long values to number if they fit inside the 53 bits resolution
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function
+ * @param {object} [options.validateOptions=false] Validate MongoClient passed in options for correctness
+ * @param {string} [options.appname=undefined] The name of the application that created this MongoClient instance. MongoDB 3.4 and newer will print this value in the server log upon establishing each connection. It is also recorded in the slow query log and profile collections
+ * @param {string} [options.auth.user=undefined] The username for auth
+ * @param {string} [options.auth.password=undefined] The password for auth
+ * @param {string} [options.authMechanism=undefined] Mechanism for authentication: MDEFAULT, GSSAPI, PLAIN, MONGODB-X509, or SCRAM-SHA-1
+ * @param {object} [options.compression] Type of compression to use: snappy or zlib
+ * @param {boolean} [options.fsync=false] Specify a file sync write concern
+ * @param {array} [options.readPreferenceTags] Read preference tags
+ * @param {number} [options.numberOfRetries=5] The number of retries for a tailable cursor
+ * @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances
+ * @param {number} [options.minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
+ * @param {MongoClient~connectCallback} [callback] The command result callback
+ * @return {Promise} returns Promise if no callback passed
+ */
+MongoClient.connect = function(url, options, callback) {
+ const args = Array.prototype.slice.call(arguments, 1);
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
+ options = args.length ? args.shift() : null;
+ options = options || {};
+
+ // Create client
+ const mongoClient = new MongoClient(url, options);
+ // Execute the connect method
+ return mongoClient.connect(callback);
+};
+
+/**
+ * Starts a new session on the server
+ *
+ * @param {SessionOptions} [options] optional settings for a driver session
+ * @return {ClientSession} the newly established session
+ */
+MongoClient.prototype.startSession = function(options) {
+ options = Object.assign({ explicit: true }, options);
+ if (!this.topology) {
+ throw new MongoError('Must connect to a server before calling this method');
+ }
+
+ if (!this.topology.hasSessionSupport()) {
+ throw new MongoError('Current topology does not support sessions');
+ }
+
+ return this.topology.startSession(options, this.s.options);
+};
+
+/**
+ * Runs a given operation with an implicitly created session. The lifetime of the session
+ * will be handled without the need for user interaction.
+ *
+ * NOTE: presently the operation MUST return a Promise (either explicit or implicity as an async function)
+ *
+ * @param {Object} [options] Optional settings to be appled to implicitly created session
+ * @param {Function} operation An operation to execute with an implicitly created session. The signature of this MUST be `(session) => {}`
+ * @return {Promise}
+ */
+MongoClient.prototype.withSession = function(options, operation) {
+ if (typeof options === 'function') (operation = options), (options = undefined);
+ const session = this.startSession(options);
+
+ let cleanupHandler = (err, result, opts) => {
+ // prevent multiple calls to cleanupHandler
+ cleanupHandler = () => {
+ throw new ReferenceError('cleanupHandler was called too many times');
+ };
+
+ opts = Object.assign({ throw: true }, opts);
+ session.endSession();
+
+ if (err) {
+ if (opts.throw) throw err;
+ return Promise.reject(err);
+ }
+ };
+
+ try {
+ const result = operation(session);
+ return Promise.resolve(result)
+ .then(result => cleanupHandler(null, result))
+ .catch(err => cleanupHandler(err, null, { throw: true }));
+ } catch (err) {
+ return cleanupHandler(err, null, { throw: false });
+ }
+};
+/**
+ * Create a new Change Stream, watching for new changes (insertions, updates, replacements, deletions, and invalidations) in this cluster. Will ignore all changes to system collections, as well as the local, admin,
+ * and config databases.
+ * @method
+ * @since 3.1.0
+ * @param {Array} [pipeline] An array of {@link https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.
+ * @param {object} [options] Optional settings
+ * @param {string} [options.fullDocument='default'] Allowed values: ‘default’, ‘updateLookup’. When set to ‘updateLookup’, the change stream will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
+ * @param {object} [options.resumeAfter] Specifies the logical starting point for the new change stream. This should be the _id field from a previously returned change stream document.
+ * @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query
+ * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
+ * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
+ * @param {ReadPreference} [options.readPreference] The read preference. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
+ * @param {Timestamp} [options.startAtClusterTime] receive change events that occur after the specified timestamp
+ * @param {ClientSession} [options.session] optional session to use for this operation
+ * @return {ChangeStream} a ChangeStream instance.
+ */
+MongoClient.prototype.watch = function(pipeline, options) {
+ pipeline = pipeline || [];
+ options = options || {};
+
+ // Allow optionally not specifying a pipeline
+ if (!Array.isArray(pipeline)) {
+ options = pipeline;
+ pipeline = [];
+ }
+
+ return new ChangeStream(this, pipeline, options);
+};
+
+/**
+ * Return the mongo client logger
+ * @method
+ * @return {Logger} return the mongo client logger
+ * @ignore
+ */
+MongoClient.prototype.getLogger = function() {
+ return this.s.options.logger;
+};
+
+module.exports = MongoClient;
diff --git a/node_modules/mongodb/lib/operations/admin_ops.js b/node_modules/mongodb/lib/operations/admin_ops.js
new file mode 100644
index 0000000..b08071c
--- /dev/null
+++ b/node_modules/mongodb/lib/operations/admin_ops.js
@@ -0,0 +1,62 @@
+'use strict';
+
+const executeCommand = require('./db_ops').executeCommand;
+const executeDbAdminCommand = require('./db_ops').executeDbAdminCommand;
+
+/**
+ * Get ReplicaSet status
+ *
+ * @param {Admin} a collection instance.
+ * @param {Object} [options] Optional settings. See Admin.prototype.replSetGetStatus for a list of options.
+ * @param {Admin~resultCallback} [callback] The command result callback.
+ */
+function replSetGetStatus(admin, options, callback) {
+ executeDbAdminCommand(admin.s.db, { replSetGetStatus: 1 }, options, callback);
+}
+
+/**
+ * Retrieve this db's server status.
+ *
+ * @param {Admin} a collection instance.
+ * @param {Object} [options] Optional settings. See Admin.prototype.serverStatus for a list of options.
+ * @param {Admin~resultCallback} [callback] The command result callback
+ */
+function serverStatus(admin, options, callback) {
+ executeDbAdminCommand(admin.s.db, { serverStatus: 1 }, options, callback);
+}
+
+/**
+ * Validate an existing collection
+ *
+ * @param {Admin} a collection instance.
+ * @param {string} collectionName The name of the collection to validate.
+ * @param {Object} [options] Optional settings. See Admin.prototype.validateCollection for a list of options.
+ * @param {Admin~resultCallback} [callback] The command result callback.
+ */
+function validateCollection(admin, collectionName, options, callback) {
+ const command = { validate: collectionName };
+ const keys = Object.keys(options);
+
+ // Decorate command with extra options
+ for (let i = 0; i < keys.length; i++) {
+ if (options.hasOwnProperty(keys[i]) && keys[i] !== 'session') {
+ command[keys[i]] = options[keys[i]];
+ }
+ }
+
+ executeCommand(admin.s.db, command, options, (err, doc) => {
+ if (err != null) return callback(err, null);
+
+ if (doc.ok === 0) return callback(new Error('Error with validate command'), null);
+ if (doc.result != null && doc.result.constructor !== String)
+ return callback(new Error('Error with validation data'), null);
+ if (doc.result != null && doc.result.match(/exception|corrupt/) != null)
+ return callback(new Error('Error: invalid collection ' + collectionName), null);
+ if (doc.valid != null && !doc.valid)
+ return callback(new Error('Error: invalid collection ' + collectionName), null);
+
+ return callback(null, doc);
+ });
+}
+
+module.exports = { replSetGetStatus, serverStatus, validateCollection };
diff --git a/node_modules/mongodb/lib/operations/collection_ops.js b/node_modules/mongodb/lib/operations/collection_ops.js
new file mode 100644
index 0000000..e828584
--- /dev/null
+++ b/node_modules/mongodb/lib/operations/collection_ops.js
@@ -0,0 +1,1479 @@
+'use strict';
+
+const applyWriteConcern = require('../utils').applyWriteConcern;
+const applyRetryableWrites = require('../utils').applyRetryableWrites;
+const checkCollectionName = require('../utils').checkCollectionName;
+const Code = require('mongodb-core').BSON.Code;
+const createIndexDb = require('./db_ops').createIndex;
+const decorateCommand = require('../utils').decorateCommand;
+const decorateWithCollation = require('../utils').decorateWithCollation;
+const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
+const ensureIndexDb = require('./db_ops').ensureIndex;
+const evaluate = require('./db_ops').evaluate;
+const executeCommand = require('./db_ops').executeCommand;
+const executeDbAdminCommand = require('./db_ops').executeDbAdminCommand;
+const formattedOrderClause = require('../utils').formattedOrderClause;
+const resolveReadPreference = require('../utils').resolveReadPreference;
+const handleCallback = require('../utils').handleCallback;
+const indexInformationDb = require('./db_ops').indexInformation;
+const isObject = require('../utils').isObject;
+const Long = require('mongodb-core').BSON.Long;
+const MongoError = require('mongodb-core').MongoError;
+const ReadPreference = require('mongodb-core').ReadPreference;
+const toError = require('../utils').toError;
+
+/**
+ * Group function helper
+ * @ignore
+ */
+// var groupFunction = function () {
+// var c = db[ns].find(condition);
+// var map = new Map();
+// var reduce_function = reduce;
+//
+// while (c.hasNext()) {
+// var obj = c.next();
+// var key = {};
+//
+// for (var i = 0, len = keys.length; i < len; ++i) {
+// var k = keys[i];
+// key[k] = obj[k];
+// }
+//
+// var aggObj = map.get(key);
+//
+// if (aggObj == null) {
+// var newObj = Object.extend({}, key);
+// aggObj = Object.extend(newObj, initial);
+// map.put(key, aggObj);
+// }
+//
+// reduce_function(obj, aggObj);
+// }
+//
+// return { "result": map.values() };
+// }.toString();
+const groupFunction =
+ 'function () {\nvar c = db[ns].find(condition);\nvar map = new Map();\nvar reduce_function = reduce;\n\nwhile (c.hasNext()) {\nvar obj = c.next();\nvar key = {};\n\nfor (var i = 0, len = keys.length; i < len; ++i) {\nvar k = keys[i];\nkey[k] = obj[k];\n}\n\nvar aggObj = map.get(key);\n\nif (aggObj == null) {\nvar newObj = Object.extend({}, key);\naggObj = Object.extend(newObj, initial);\nmap.put(key, aggObj);\n}\n\nreduce_function(obj, aggObj);\n}\n\nreturn { "result": map.values() };\n}';
+
+/**
+ * Perform a bulkWrite operation. See Collection.prototype.bulkWrite for more information.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object[]} operations Bulk operations to perform.
+ * @param {object} [options] Optional settings. See Collection.prototype.bulkWrite for a list of options.
+ * @param {Collection~bulkWriteOpCallback} [callback] The command result callback
+ */
+function bulkWrite(coll, operations, options, callback) {
+ // Add ignoreUndfined
+ if (coll.s.options.ignoreUndefined) {
+ options = Object.assign({}, options);
+ options.ignoreUndefined = coll.s.options.ignoreUndefined;
+ }
+
+ // Create the bulk operation
+ const bulk =
+ options.ordered === true || options.ordered == null
+ ? coll.initializeOrderedBulkOp(options)
+ : coll.initializeUnorderedBulkOp(options);
+
+ // Do we have a collation
+ let collation = false;
+
+ // for each op go through and add to the bulk
+ try {
+ for (let i = 0; i < operations.length; i++) {
+ // Get the operation type
+ const key = Object.keys(operations[i])[0];
+ // Check if we have a collation
+ if (operations[i][key].collation) {
+ collation = true;
+ }
+
+ // Pass to the raw bulk
+ bulk.raw(operations[i]);
+ }
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ // Final options for retryable writes and write concern
+ let finalOptions = Object.assign({}, options);
+ finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
+ finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
+
+ const writeCon = finalOptions.writeConcern ? finalOptions.writeConcern : {};
+ const capabilities = coll.s.topology.capabilities();
+
+ // Did the user pass in a collation, check if our write server supports it
+ if (collation && capabilities && !capabilities.commandsTakeCollation) {
+ return callback(new MongoError('server/primary/mongos does not support collation'));
+ }
+
+ // Execute the bulk
+ bulk.execute(writeCon, finalOptions, (err, r) => {
+ // We have connection level error
+ if (!r && err) {
+ return callback(err, null);
+ }
+
+ r.insertedCount = r.nInserted;
+ r.matchedCount = r.nMatched;
+ r.modifiedCount = r.nModified || 0;
+ r.deletedCount = r.nRemoved;
+ r.upsertedCount = r.getUpsertedIds().length;
+ r.upsertedIds = {};
+ r.insertedIds = {};
+
+ // Update the n
+ r.n = r.insertedCount;
+
+ // Inserted documents
+ const inserted = r.getInsertedIds();
+ // Map inserted ids
+ for (let i = 0; i < inserted.length; i++) {
+ r.insertedIds[inserted[i].index] = inserted[i]._id;
+ }
+
+ // Upserted documents
+ const upserted = r.getUpsertedIds();
+ // Map upserted ids
+ for (let i = 0; i < upserted.length; i++) {
+ r.upsertedIds[upserted[i].index] = upserted[i]._id;
+ }
+
+ // Return the results
+ callback(null, r);
+ });
+}
+
+// Check the update operation to ensure it has atomic operators.
+function checkForAtomicOperators(update) {
+ const keys = Object.keys(update);
+
+ // same errors as the server would give for update doc lacking atomic operators
+ if (keys.length === 0) {
+ return toError('The update operation document must contain at least one atomic operator.');
+ }
+
+ if (keys[0][0] !== '$') {
+ return toError('the update operation document must contain atomic operators.');
+ }
+}
+
+/**
+ * Count the number of documents in the collection that match the query.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} query The query for the count.
+ * @param {object} [options] Optional settings. See Collection.prototype.count for a list of options.
+ * @param {Collection~countCallback} [callback] The command result callback
+ */
+function count(coll, query, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = Object.assign({}, options);
+ options.collectionName = coll.s.name;
+
+ options.readPreference = resolveReadPreference(options, {
+ db: coll.s.db,
+ collection: coll
+ });
+
+ let cmd;
+ try {
+ cmd = buildCountCommand(coll, query, options);
+ } catch (err) {
+ return callback(err);
+ }
+
+ executeCommand(coll.s.db, cmd, options, (err, result) => {
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, null, result.n);
+ });
+}
+
+function countDocuments(coll, query, options, callback) {
+ const skip = options.skip;
+ const limit = options.limit;
+ options = Object.assign({}, options);
+
+ const pipeline = [{ $match: query }];
+
+ // Add skip and limit if defined
+ if (typeof skip === 'number') {
+ pipeline.push({ $skip: skip });
+ }
+
+ if (typeof limit === 'number') {
+ pipeline.push({ $limit: limit });
+ }
+
+ pipeline.push({ $group: { _id: null, n: { $sum: 1 } } });
+
+ delete options.limit;
+ delete options.skip;
+
+ coll.aggregate(pipeline, options).toArray((err, docs) => {
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, null, docs.length ? docs[0].n : 0);
+ });
+}
+
+/**
+ * Build the count command.
+ *
+ * @method
+ * @param {collectionOrCursor} an instance of a collection or cursor
+ * @param {object} query The query for the count.
+ * @param {object} [options] Optional settings. See Collection.prototype.count and Cursor.prototype.count for a list of options.
+ */
+function buildCountCommand(collectionOrCursor, query, options) {
+ const skip = options.skip;
+ const limit = options.limit;
+ let hint = options.hint;
+ const maxTimeMS = options.maxTimeMS;
+ query = query || {};
+
+ // Final query
+ const cmd = {
+ count: options.collectionName,
+ query: query
+ };
+
+ // check if collectionOrCursor is a cursor by using cursor.s.numberOfRetries
+ if (collectionOrCursor.s.numberOfRetries) {
+ if (collectionOrCursor.s.options.hint) {
+ hint = collectionOrCursor.s.options.hint;
+ } else if (collectionOrCursor.s.cmd.hint) {
+ hint = collectionOrCursor.s.cmd.hint;
+ }
+ decorateWithCollation(cmd, collectionOrCursor, collectionOrCursor.s.cmd);
+ } else {
+ decorateWithCollation(cmd, collectionOrCursor, options);
+ }
+
+ // Add limit, skip and maxTimeMS if defined
+ if (typeof skip === 'number') cmd.skip = skip;
+ if (typeof limit === 'number') cmd.limit = limit;
+ if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
+ if (hint) cmd.hint = hint;
+
+ // Do we have a readConcern specified
+ decorateWithReadConcern(cmd, collectionOrCursor);
+
+ return cmd;
+}
+
+/**
+ * Create an index on the db and collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {object} [options] Optional settings. See Collection.prototype.createIndex for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function createIndex(coll, fieldOrSpec, options, callback) {
+ createIndexDb(coll.s.db, coll.s.name, fieldOrSpec, options, callback);
+}
+
+/**
+ * Create multiple indexes in the collection. This method is only supported for
+ * MongoDB 2.6 or higher. Earlier version of MongoDB will throw a command not supported
+ * error. Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {array} indexSpecs An array of index specifications to be created
+ * @param {Object} [options] Optional settings. See Collection.prototype.createIndexes for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function createIndexes(coll, indexSpecs, options, callback) {
+ const capabilities = coll.s.topology.capabilities();
+
+ // Ensure we generate the correct name if the parameter is not set
+ for (let i = 0; i < indexSpecs.length; i++) {
+ if (indexSpecs[i].name == null) {
+ const keys = [];
+
+ // Did the user pass in a collation, check if our write server supports it
+ if (indexSpecs[i].collation && capabilities && !capabilities.commandsTakeCollation) {
+ return callback(new MongoError('server/primary/mongos does not support collation'));
+ }
+
+ for (let name in indexSpecs[i].key) {
+ keys.push(`${name}_${indexSpecs[i].key[name]}`);
+ }
+
+ // Set the name
+ indexSpecs[i].name = keys.join('_');
+ }
+ }
+
+ options = Object.assign({}, options, { readPreference: ReadPreference.PRIMARY });
+
+ // Execute the index
+ executeCommand(
+ coll.s.db,
+ {
+ createIndexes: coll.s.name,
+ indexes: indexSpecs
+ },
+ options,
+ callback
+ );
+}
+
+function deleteCallback(err, r, callback) {
+ if (callback == null) return;
+ if (err && callback) return callback(err);
+ if (r == null) return callback(null, { result: { ok: 1 } });
+ r.deletedCount = r.result.n;
+ if (callback) callback(null, r);
+}
+
+/**
+ * Delete multiple documents from the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter The Filter used to select the documents to remove
+ * @param {object} [options] Optional settings. See Collection.prototype.deleteMany for a list of options.
+ * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
+ */
+function deleteMany(coll, filter, options, callback) {
+ options.single = false;
+
+ removeDocuments(coll, filter, options, (err, r) => deleteCallback(err, r, callback));
+}
+
+/**
+ * Delete a single document from the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter The Filter used to select the document to remove
+ * @param {object} [options] Optional settings. See Collection.prototype.deleteOne for a list of options.
+ * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
+ */
+function deleteOne(coll, filter, options, callback) {
+ options.single = true;
+ removeDocuments(coll, filter, options, (err, r) => deleteCallback(err, r, callback));
+}
+
+/**
+ * Return a list of distinct values for the given key across a collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {string} key Field of the document to find distinct values for.
+ * @param {object} query The query for filtering the set of documents to which we apply the distinct filter.
+ * @param {object} [options] Optional settings. See Collection.prototype.distinct for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function distinct(coll, key, query, options, callback) {
+ // maxTimeMS option
+ const maxTimeMS = options.maxTimeMS;
+
+ // Distinct command
+ const cmd = {
+ distinct: coll.s.name,
+ key: key,
+ query: query
+ };
+
+ options = Object.assign({}, options);
+ // Ensure we have the right read preference inheritance
+ options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
+
+ // Add maxTimeMS if defined
+ if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
+
+ // Do we have a readConcern specified
+ decorateWithReadConcern(cmd, coll, options);
+
+ // Have we specified collation
+ try {
+ decorateWithCollation(cmd, coll, options);
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ // Execute the command
+ executeCommand(coll.s.db, cmd, options, (err, result) => {
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, null, result.values);
+ });
+}
+
+/**
+ * Drop an index from this collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {string} indexName Name of the index to drop.
+ * @param {object} [options] Optional settings. See Collection.prototype.dropIndex for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function dropIndex(coll, indexName, options, callback) {
+ // Delete index command
+ const cmd = { dropIndexes: coll.s.name, index: indexName };
+
+ // Decorate command with writeConcern if supported
+ applyWriteConcern(cmd, { db: coll.s.db, collection: coll }, options);
+
+ // Execute command
+ executeCommand(coll.s.db, cmd, options, (err, result) => {
+ if (typeof callback !== 'function') return;
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, result);
+ });
+}
+
+/**
+ * Drop all indexes from this collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {Object} [options] Optional settings. See Collection.prototype.dropIndexes for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function dropIndexes(coll, options, callback) {
+ dropIndex(coll, '*', options, err => {
+ if (err) return handleCallback(callback, err, false);
+ handleCallback(callback, null, true);
+ });
+}
+
+/**
+ * Ensure that an index exists. If the index does not exist, this function creates it.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {object} [options] Optional settings. See Collection.prototype.ensureIndex for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function ensureIndex(coll, fieldOrSpec, options, callback) {
+ ensureIndexDb(coll.s.db, coll.s.name, fieldOrSpec, options, callback);
+}
+
+/**
+ * Find and update a document.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} query Query object to locate the object to modify.
+ * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
+ * @param {object} doc The fields/vals to be updated.
+ * @param {object} [options] Optional settings. See Collection.prototype.findAndModify for a list of options.
+ * @param {Collection~findAndModifyCallback} [callback] The command result callback
+ * @deprecated use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead
+ */
+function findAndModify(coll, query, sort, doc, options, callback) {
+ // Create findAndModify command object
+ const queryObject = {
+ findAndModify: coll.s.name,
+ query: query
+ };
+
+ sort = formattedOrderClause(sort);
+ if (sort) {
+ queryObject.sort = sort;
+ }
+
+ queryObject.new = options.new ? true : false;
+ queryObject.remove = options.remove ? true : false;
+ queryObject.upsert = options.upsert ? true : false;
+
+ const projection = options.projection || options.fields;
+
+ if (projection) {
+ queryObject.fields = projection;
+ }
+
+ if (options.arrayFilters) {
+ queryObject.arrayFilters = options.arrayFilters;
+ delete options.arrayFilters;
+ }
+
+ if (doc && !options.remove) {
+ queryObject.update = doc;
+ }
+
+ if (options.maxTimeMS) queryObject.maxTimeMS = options.maxTimeMS;
+
+ // Either use override on the function, or go back to default on either the collection
+ // level or db
+ options.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
+
+ // No check on the documents
+ options.checkKeys = false;
+
+ // Final options for retryable writes and write concern
+ let finalOptions = Object.assign({}, options);
+ finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
+ finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
+
+ // Decorate the findAndModify command with the write Concern
+ if (finalOptions.writeConcern) {
+ queryObject.writeConcern = finalOptions.writeConcern;
+ }
+
+ // Have we specified bypassDocumentValidation
+ if (finalOptions.bypassDocumentValidation === true) {
+ queryObject.bypassDocumentValidation = finalOptions.bypassDocumentValidation;
+ }
+
+ finalOptions.readPreference = ReadPreference.primary;
+
+ // Have we specified collation
+ try {
+ decorateWithCollation(queryObject, coll, finalOptions);
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ // Execute the command
+ executeCommand(coll.s.db, queryObject, finalOptions, (err, result) => {
+ if (err) return handleCallback(callback, err, null);
+
+ return handleCallback(callback, null, result);
+ });
+}
+
+/**
+ * Find and remove a document.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} query Query object to locate the object to modify.
+ * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
+ * @param {object} [options] Optional settings. See Collection.prototype.findAndRemove for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ * @deprecated use findOneAndDelete instead
+ */
+function findAndRemove(coll, query, sort, options, callback) {
+ // Add the remove option
+ options.remove = true;
+ // Execute the callback
+ findAndModify(coll, query, sort, null, options, callback);
+}
+
+/**
+ * Fetch the first document that matches the query.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} query Query for find Operation
+ * @param {object} [options] Optional settings. See Collection.prototype.findOne for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function findOne(coll, query, options, callback) {
+ const cursor = coll
+ .find(query, options)
+ .limit(-1)
+ .batchSize(1);
+
+ // Return the item
+ cursor.next((err, item) => {
+ if (err != null) return handleCallback(callback, toError(err), null);
+ handleCallback(callback, null, item);
+ });
+}
+
+/**
+ * Find a document and delete it in one atomic operation. This requires a write lock for the duration of the operation.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter Document selection filter.
+ * @param {object} [options] Optional settings. See Collection.prototype.findOneAndDelete for a list of options.
+ * @param {Collection~findAndModifyCallback} [callback] The collection result callback
+ */
+function findOneAndDelete(coll, filter, options, callback) {
+ // Final options
+ const finalOptions = Object.assign({}, options);
+ finalOptions.fields = options.projection;
+ finalOptions.remove = true;
+ // Execute find and Modify
+ findAndModify(coll, filter, options.sort, null, finalOptions, callback);
+}
+
+/**
+ * Find a document and replace it in one atomic operation. This requires a write lock for the duration of the operation.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter Document selection filter.
+ * @param {object} replacement Document replacing the matching document.
+ * @param {object} [options] Optional settings. See Collection.prototype.findOneAndReplace for a list of options.
+ * @param {Collection~findAndModifyCallback} [callback] The collection result callback
+ */
+function findOneAndReplace(coll, filter, replacement, options, callback) {
+ // Final options
+ const finalOptions = Object.assign({}, options);
+ finalOptions.fields = options.projection;
+ finalOptions.update = true;
+ finalOptions.new = options.returnOriginal !== void 0 ? !options.returnOriginal : false;
+ finalOptions.upsert = options.upsert !== void 0 ? !!options.upsert : false;
+
+ // Execute findAndModify
+ findAndModify(coll, filter, options.sort, replacement, finalOptions, callback);
+}
+
+/**
+ * Find a document and update it in one atomic operation. This requires a write lock for the duration of the operation.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter Document selection filter.
+ * @param {object} update Update operations to be performed on the document
+ * @param {object} [options] Optional settings. See Collection.prototype.findOneAndUpdate for a list of options.
+ * @param {Collection~findAndModifyCallback} [callback] The collection result callback
+ */
+function findOneAndUpdate(coll, filter, update, options, callback) {
+ // Final options
+ const finalOptions = Object.assign({}, options);
+ finalOptions.fields = options.projection;
+ finalOptions.update = true;
+ finalOptions.new = typeof options.returnOriginal === 'boolean' ? !options.returnOriginal : false;
+ finalOptions.upsert = typeof options.upsert === 'boolean' ? options.upsert : false;
+
+ // Execute findAndModify
+ findAndModify(coll, filter, options.sort, update, finalOptions, callback);
+}
+
+/**
+ * Execute a geo search using a geo haystack index on a collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {number} x Point to search on the x axis, ensure the indexes are ordered in the same order.
+ * @param {number} y Point to search on the y axis, ensure the indexes are ordered in the same order.
+ * @param {object} [options] Optional settings. See Collection.prototype.geoHaystackSearch for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function geoHaystackSearch(coll, x, y, options, callback) {
+ // Build command object
+ let commandObject = {
+ geoSearch: coll.s.name,
+ near: [x, y]
+ };
+
+ // Remove read preference from hash if it exists
+ commandObject = decorateCommand(commandObject, options, ['readPreference', 'session']);
+
+ options = Object.assign({}, options);
+ // Ensure we have the right read preference inheritance
+ options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
+
+ // Do we have a readConcern specified
+ decorateWithReadConcern(commandObject, coll, options);
+
+ // Execute the command
+ executeCommand(coll.s.db, commandObject, options, (err, res) => {
+ if (err) return handleCallback(callback, err);
+ if (res.err || res.errmsg) handleCallback(callback, toError(res));
+ // should we only be returning res.results here? Not sure if the user
+ // should see the other return information
+ handleCallback(callback, null, res);
+ });
+}
+
+/**
+ * Run a group command across a collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {(object|array|function|code)} keys An object, array or function expressing the keys to group by.
+ * @param {object} condition An optional condition that must be true for a row to be considered.
+ * @param {object} initial Initial value of the aggregation counter object.
+ * @param {(function|Code)} reduce The reduce function aggregates (reduces) the objects iterated
+ * @param {(function|Code)} finalize An optional function to be run on each item in the result set just before the item is returned.
+ * @param {boolean} command Specify if you wish to run using the internal group command or using eval, default is true.
+ * @param {object} [options] Optional settings. See Collection.prototype.group for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ * @deprecated MongoDB 3.6 or higher will no longer support the group command. We recommend rewriting using the aggregation framework.
+ */
+function group(coll, keys, condition, initial, reduce, finalize, command, options, callback) {
+ // Execute using the command
+ if (command) {
+ const reduceFunction = reduce && reduce._bsontype === 'Code' ? reduce : new Code(reduce);
+
+ const selector = {
+ group: {
+ ns: coll.s.name,
+ $reduce: reduceFunction,
+ cond: condition,
+ initial: initial,
+ out: 'inline'
+ }
+ };
+
+ // if finalize is defined
+ if (finalize != null) selector.group['finalize'] = finalize;
+ // Set up group selector
+ if ('function' === typeof keys || (keys && keys._bsontype === 'Code')) {
+ selector.group.$keyf = keys && keys._bsontype === 'Code' ? keys : new Code(keys);
+ } else {
+ const hash = {};
+ keys.forEach(key => {
+ hash[key] = 1;
+ });
+ selector.group.key = hash;
+ }
+
+ options = Object.assign({}, options);
+ // Ensure we have the right read preference inheritance
+ options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
+
+ // Do we have a readConcern specified
+ decorateWithReadConcern(selector, coll, options);
+
+ // Have we specified collation
+ try {
+ decorateWithCollation(selector, coll, options);
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ // Execute command
+ executeCommand(coll.s.db, selector, options, (err, result) => {
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, result.retval);
+ });
+ } else {
+ // Create execution scope
+ const scope = reduce != null && reduce._bsontype === 'Code' ? reduce.scope : {};
+
+ scope.ns = coll.s.name;
+ scope.keys = keys;
+ scope.condition = condition;
+ scope.initial = initial;
+
+ // Pass in the function text to execute within mongodb.
+ const groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';');
+
+ evaluate(coll.s.db, new Code(groupfn, scope), null, options, (err, results) => {
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, results.result || results);
+ });
+ }
+}
+
+/**
+ * Retrieve all the indexes on the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {Object} [options] Optional settings. See Collection.prototype.indexes for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function indexes(coll, options, callback) {
+ options = Object.assign({}, { full: true }, options);
+ indexInformationDb(coll.s.db, coll.s.name, options, callback);
+}
+
+/**
+ * Check if one or more indexes exist on the collection. This fails on the first index that doesn't exist.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {(string|array)} indexes One or more index names to check.
+ * @param {Object} [options] Optional settings. See Collection.prototype.indexExists for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function indexExists(coll, indexes, options, callback) {
+ indexInformation(coll, options, (err, indexInformation) => {
+ // If we have an error return
+ if (err != null) return handleCallback(callback, err, null);
+ // Let's check for the index names
+ if (!Array.isArray(indexes))
+ return handleCallback(callback, null, indexInformation[indexes] != null);
+ // Check in list of indexes
+ for (let i = 0; i < indexes.length; i++) {
+ if (indexInformation[indexes[i]] == null) {
+ return handleCallback(callback, null, false);
+ }
+ }
+
+ // All keys found return true
+ return handleCallback(callback, null, true);
+ });
+}
+
+/**
+ * Retrieve this collection's index info.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} [options] Optional settings. See Collection.prototype.indexInformation for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function indexInformation(coll, options, callback) {
+ indexInformationDb(coll.s.db, coll.s.name, options, callback);
+}
+
+function insertDocuments(coll, docs, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+ // Ensure we are operating on an array op docs
+ docs = Array.isArray(docs) ? docs : [docs];
+
+ // Final options for retryable writes and write concern
+ let finalOptions = Object.assign({}, options);
+ finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
+ finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
+
+ // If keep going set unordered
+ if (finalOptions.keepGoing === true) finalOptions.ordered = false;
+ finalOptions.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
+
+ docs = prepareDocs(coll, docs, options);
+
+ // File inserts
+ coll.s.topology.insert(coll.s.namespace, docs, finalOptions, (err, result) => {
+ if (callback == null) return;
+ if (err) return handleCallback(callback, err);
+ if (result == null) return handleCallback(callback, null, null);
+ if (result.result.code) return handleCallback(callback, toError(result.result));
+ if (result.result.writeErrors)
+ return handleCallback(callback, toError(result.result.writeErrors[0]));
+ // Add docs to the list
+ result.ops = docs;
+ // Return the results
+ handleCallback(callback, null, result);
+ });
+}
+
+/**
+ * Insert a single document into the collection. See Collection.prototype.insertOne for more information.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} doc Document to insert.
+ * @param {object} [options] Optional settings. See Collection.prototype.insertOne for a list of options.
+ * @param {Collection~insertOneWriteOpCallback} [callback] The command result callback
+ */
+function insertOne(coll, doc, options, callback) {
+ if (Array.isArray(doc)) {
+ return callback(
+ MongoError.create({ message: 'doc parameter must be an object', driver: true })
+ );
+ }
+
+ insertDocuments(coll, [doc], options, (err, r) => {
+ if (callback == null) return;
+ if (err && callback) return callback(err);
+ // Workaround for pre 2.6 servers
+ if (r == null) return callback(null, { result: { ok: 1 } });
+ // Add values to top level to ensure crud spec compatibility
+ r.insertedCount = r.result.n;
+ r.insertedId = doc._id;
+ if (callback) callback(null, r);
+ });
+}
+
+/**
+ * Determine whether the collection is a capped collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {Object} [options] Optional settings. See Collection.prototype.isCapped for a list of options.
+ * @param {Collection~resultCallback} [callback] The results callback
+ */
+function isCapped(coll, options, callback) {
+ optionsOp(coll, options, (err, document) => {
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, null, !!(document && document.capped));
+ });
+}
+
+/**
+ * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {(function|string)} map The mapping function.
+ * @param {(function|string)} reduce The reduce function.
+ * @param {object} [options] Optional settings. See Collection.prototype.mapReduce for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function mapReduce(coll, map, reduce, options, callback) {
+ const mapCommandHash = {
+ mapreduce: coll.s.name,
+ map: map,
+ reduce: reduce
+ };
+
+ // Exclusion list
+ const exclusionList = ['readPreference', 'session', 'bypassDocumentValidation'];
+
+ // Add any other options passed in
+ for (let n in options) {
+ if ('scope' === n) {
+ mapCommandHash[n] = processScope(options[n]);
+ } else {
+ // Only include if not in exclusion list
+ if (exclusionList.indexOf(n) === -1) {
+ mapCommandHash[n] = options[n];
+ }
+ }
+ }
+
+ options = Object.assign({}, options);
+
+ // Ensure we have the right read preference inheritance
+ options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
+
+ // If we have a read preference and inline is not set as output fail hard
+ if (
+ options.readPreference !== false &&
+ options.readPreference !== 'primary' &&
+ options['out'] &&
+ (options['out'].inline !== 1 && options['out'] !== 'inline')
+ ) {
+ // Force readPreference to primary
+ options.readPreference = 'primary';
+ // Decorate command with writeConcern if supported
+ applyWriteConcern(mapCommandHash, { db: coll.s.db, collection: coll }, options);
+ } else {
+ decorateWithReadConcern(mapCommandHash, coll, options);
+ }
+
+ // Is bypassDocumentValidation specified
+ if (options.bypassDocumentValidation === true) {
+ mapCommandHash.bypassDocumentValidation = options.bypassDocumentValidation;
+ }
+
+ // Have we specified collation
+ try {
+ decorateWithCollation(mapCommandHash, coll, options);
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ // Execute command
+ executeCommand(coll.s.db, mapCommandHash, options, (err, result) => {
+ if (err) return handleCallback(callback, err);
+ // Check if we have an error
+ if (1 !== result.ok || result.err || result.errmsg) {
+ return handleCallback(callback, toError(result));
+ }
+
+ // Create statistics value
+ const stats = {};
+ if (result.timeMillis) stats['processtime'] = result.timeMillis;
+ if (result.counts) stats['counts'] = result.counts;
+ if (result.timing) stats['timing'] = result.timing;
+
+ // invoked with inline?
+ if (result.results) {
+ // If we wish for no verbosity
+ if (options['verbose'] == null || !options['verbose']) {
+ return handleCallback(callback, null, result.results);
+ }
+
+ return handleCallback(callback, null, { results: result.results, stats: stats });
+ }
+
+ // The returned collection
+ let collection = null;
+
+ // If we have an object it's a different db
+ if (result.result != null && typeof result.result === 'object') {
+ const doc = result.result;
+ // Return a collection from another db
+ const Db = require('../db');
+ collection = new Db(doc.db, coll.s.db.s.topology, coll.s.db.s.options).collection(
+ doc.collection
+ );
+ } else {
+ // Create a collection object that wraps the result collection
+ collection = coll.s.db.collection(result.result);
+ }
+
+ // If we wish for no verbosity
+ if (options['verbose'] == null || !options['verbose']) {
+ return handleCallback(callback, err, collection);
+ }
+
+ // Return stats as third set of values
+ handleCallback(callback, err, { collection: collection, stats: stats });
+ });
+}
+
+/**
+ * Return the options of the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {Object} [options] Optional settings. See Collection.prototype.options for a list of options.
+ * @param {Collection~resultCallback} [callback] The results callback
+ */
+function optionsOp(coll, opts, callback) {
+ coll.s.db.listCollections({ name: coll.s.name }, opts).toArray((err, collections) => {
+ if (err) return handleCallback(callback, err);
+ if (collections.length === 0) {
+ return handleCallback(
+ callback,
+ MongoError.create({ message: `collection ${coll.s.namespace} not found`, driver: true })
+ );
+ }
+
+ handleCallback(callback, err, collections[0].options || null);
+ });
+}
+
+/**
+ * Return N parallel cursors for a collection to allow parallel reading of the entire collection. There are
+ * no ordering guarantees for returned results.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} [options] Optional settings. See Collection.prototype.parallelCollectionScan for a list of options.
+ * @param {Collection~parallelCollectionScanCallback} [callback] The command result callback
+ */
+function parallelCollectionScan(coll, options, callback) {
+ // Create command object
+ const commandObject = {
+ parallelCollectionScan: coll.s.name,
+ numCursors: options.numCursors
+ };
+
+ // Do we have a readConcern specified
+ decorateWithReadConcern(commandObject, coll, options);
+
+ // Store the raw value
+ const raw = options.raw;
+ delete options['raw'];
+
+ // Execute the command
+ executeCommand(coll.s.db, commandObject, options, (err, result) => {
+ if (err) return handleCallback(callback, err, null);
+ if (result == null)
+ return handleCallback(
+ callback,
+ new Error('no result returned for parallelCollectionScan'),
+ null
+ );
+
+ options = Object.assign({ explicitlyIgnoreSession: true }, options);
+
+ const cursors = [];
+ // Add the raw back to the option
+ if (raw) options.raw = raw;
+ // Create command cursors for each item
+ for (let i = 0; i < result.cursors.length; i++) {
+ const rawId = result.cursors[i].cursor.id;
+ // Convert cursorId to Long if needed
+ const cursorId = typeof rawId === 'number' ? Long.fromNumber(rawId) : rawId;
+ // Add a command cursor
+ cursors.push(coll.s.topology.cursor(coll.s.namespace, cursorId, options));
+ }
+
+ handleCallback(callback, null, cursors);
+ });
+}
+
+// modifies documents before being inserted or updated
+function prepareDocs(coll, docs, options) {
+ const forceServerObjectId =
+ typeof options.forceServerObjectId === 'boolean'
+ ? options.forceServerObjectId
+ : coll.s.db.options.forceServerObjectId;
+
+ // no need to modify the docs if server sets the ObjectId
+ if (forceServerObjectId === true) {
+ return docs;
+ }
+
+ return docs.map(doc => {
+ if (forceServerObjectId !== true && doc._id == null) {
+ doc._id = coll.s.pkFactory.createPk();
+ }
+
+ return doc;
+ });
+}
+
+/**
+ * Functions that are passed as scope args must
+ * be converted to Code instances.
+ * @ignore
+ */
+function processScope(scope) {
+ if (!isObject(scope) || scope._bsontype === 'ObjectID') {
+ return scope;
+ }
+
+ const keys = Object.keys(scope);
+ let key;
+ const new_scope = {};
+
+ for (let i = keys.length - 1; i >= 0; i--) {
+ key = keys[i];
+ if ('function' === typeof scope[key]) {
+ new_scope[key] = new Code(String(scope[key]));
+ } else {
+ new_scope[key] = processScope(scope[key]);
+ }
+ }
+
+ return new_scope;
+}
+
+/**
+ * Reindex all indexes on the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {Object} [options] Optional settings. See Collection.prototype.reIndex for a list of options.
+ * @param {Collection~resultCallback} [callback] The command result callback
+ */
+function reIndex(coll, options, callback) {
+ // Reindex
+ const cmd = { reIndex: coll.s.name };
+
+ // Execute the command
+ executeCommand(coll.s.db, cmd, options, (err, result) => {
+ if (callback == null) return;
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, result.ok ? true : false);
+ });
+}
+
+function removeDocuments(coll, selector, options, callback) {
+ if (typeof options === 'function') {
+ (callback = options), (options = {});
+ } else if (typeof selector === 'function') {
+ callback = selector;
+ options = {};
+ selector = {};
+ }
+
+ // Create an empty options object if the provided one is null
+ options = options || {};
+
+ // Final options for retryable writes and write concern
+ let finalOptions = Object.assign({}, options);
+ finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
+ finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
+
+ // If selector is null set empty
+ if (selector == null) selector = {};
+
+ // Build the op
+ const op = { q: selector, limit: 0 };
+ if (options.single) {
+ op.limit = 1;
+ } else if (finalOptions.retryWrites) {
+ finalOptions.retryWrites = false;
+ }
+
+ // Have we specified collation
+ try {
+ decorateWithCollation(finalOptions, coll, options);
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ // Execute the remove
+ coll.s.topology.remove(coll.s.namespace, [op], finalOptions, (err, result) => {
+ if (callback == null) return;
+ if (err) return handleCallback(callback, err, null);
+ if (result == null) return handleCallback(callback, null, null);
+ if (result.result.code) return handleCallback(callback, toError(result.result));
+ if (result.result.writeErrors)
+ return handleCallback(callback, toError(result.result.writeErrors[0]));
+ // Return the results
+ handleCallback(callback, null, result);
+ });
+}
+
+/**
+ * Rename the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {string} newName New name of of the collection.
+ * @param {object} [options] Optional settings. See Collection.prototype.rename for a list of options.
+ * @param {Collection~collectionResultCallback} [callback] The results callback
+ */
+function rename(coll, newName, options, callback) {
+ const Collection = require('../collection');
+ // Check the collection name
+ checkCollectionName(newName);
+ // Build the command
+ const renameCollection = `${coll.s.dbName}.${coll.s.name}`;
+ const toCollection = `${coll.s.dbName}.${newName}`;
+ const dropTarget = typeof options.dropTarget === 'boolean' ? options.dropTarget : false;
+ const cmd = { renameCollection: renameCollection, to: toCollection, dropTarget: dropTarget };
+
+ // Decorate command with writeConcern if supported
+ applyWriteConcern(cmd, { db: coll.s.db, collection: coll }, options);
+
+ // Execute against admin
+ executeDbAdminCommand(coll.s.db.admin().s.db, cmd, options, (err, doc) => {
+ if (err) return handleCallback(callback, err, null);
+ // We have an error
+ if (doc.errmsg) return handleCallback(callback, toError(doc), null);
+ try {
+ return handleCallback(
+ callback,
+ null,
+ new Collection(
+ coll.s.db,
+ coll.s.topology,
+ coll.s.dbName,
+ newName,
+ coll.s.pkFactory,
+ coll.s.options
+ )
+ );
+ } catch (err) {
+ return handleCallback(callback, toError(err), null);
+ }
+ });
+}
+
+/**
+ * Replace a document in the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter The Filter used to select the document to update
+ * @param {object} doc The Document that replaces the matching document
+ * @param {object} [options] Optional settings. See Collection.prototype.replaceOne for a list of options.
+ * @param {Collection~updateWriteOpCallback} [callback] The command result callback
+ */
+function replaceOne(coll, filter, doc, options, callback) {
+ // Set single document update
+ options.multi = false;
+
+ // Execute update
+ updateDocuments(coll, filter, doc, options, (err, r) => {
+ if (callback == null) return;
+ if (err && callback) return callback(err);
+ if (r == null) return callback(null, { result: { ok: 1 } });
+
+ r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
+ r.upsertedId =
+ Array.isArray(r.result.upserted) && r.result.upserted.length > 0
+ ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
+ : null;
+ r.upsertedCount =
+ Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
+ r.matchedCount =
+ Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
+ r.ops = [doc];
+ if (callback) callback(null, r);
+ });
+}
+
+/**
+ * Save a document.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} doc Document to save
+ * @param {object} [options] Optional settings. See Collection.prototype.save for a list of options.
+ * @param {Collection~writeOpCallback} [callback] The command result callback
+ * @deprecated use insertOne, insertMany, updateOne or updateMany
+ */
+function save(coll, doc, options, callback) {
+ // Get the write concern options
+ const finalOptions = applyWriteConcern(
+ Object.assign({}, options),
+ { db: coll.s.db, collection: coll },
+ options
+ );
+ // Establish if we need to perform an insert or update
+ if (doc._id != null) {
+ finalOptions.upsert = true;
+ return updateDocuments(coll, { _id: doc._id }, doc, finalOptions, callback);
+ }
+
+ // Insert the document
+ insertDocuments(coll, [doc], finalOptions, (err, result) => {
+ if (callback == null) return;
+ if (doc == null) return handleCallback(callback, null, null);
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, result);
+ });
+}
+
+/**
+ * Get all the collection statistics.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} [options] Optional settings. See Collection.prototype.stats for a list of options.
+ * @param {Collection~resultCallback} [callback] The collection result callback
+ */
+function stats(coll, options, callback) {
+ // Build command object
+ const commandObject = {
+ collStats: coll.s.name
+ };
+
+ // Check if we have the scale value
+ if (options['scale'] != null) commandObject['scale'] = options['scale'];
+
+ options = Object.assign({}, options);
+ // Ensure we have the right read preference inheritance
+ options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
+
+ // Execute the command
+ executeCommand(coll.s.db, commandObject, options, callback);
+}
+
+function updateCallback(err, r, callback) {
+ if (callback == null) return;
+ if (err) return callback(err);
+ if (r == null) return callback(null, { result: { ok: 1 } });
+ r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
+ r.upsertedId =
+ Array.isArray(r.result.upserted) && r.result.upserted.length > 0
+ ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
+ : null;
+ r.upsertedCount =
+ Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
+ r.matchedCount =
+ Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
+ callback(null, r);
+}
+
+function updateDocuments(coll, selector, document, options, callback) {
+ if ('function' === typeof options) (callback = options), (options = null);
+ if (options == null) options = {};
+ if (!('function' === typeof callback)) callback = null;
+
+ // If we are not providing a selector or document throw
+ if (selector == null || typeof selector !== 'object')
+ return callback(toError('selector must be a valid JavaScript object'));
+ if (document == null || typeof document !== 'object')
+ return callback(toError('document must be a valid JavaScript object'));
+
+ // Final options for retryable writes and write concern
+ let finalOptions = Object.assign({}, options);
+ finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
+ finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
+
+ // Do we return the actual result document
+ // Either use override on the function, or go back to default on either the collection
+ // level or db
+ finalOptions.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
+
+ // Execute the operation
+ const op = { q: selector, u: document };
+ op.upsert = options.upsert !== void 0 ? !!options.upsert : false;
+ op.multi = options.multi !== void 0 ? !!options.multi : false;
+
+ if (finalOptions.arrayFilters) {
+ op.arrayFilters = finalOptions.arrayFilters;
+ delete finalOptions.arrayFilters;
+ }
+
+ if (finalOptions.retryWrites && op.multi) {
+ finalOptions.retryWrites = false;
+ }
+
+ // Have we specified collation
+ try {
+ decorateWithCollation(finalOptions, coll, options);
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ // Update options
+ coll.s.topology.update(coll.s.namespace, [op], finalOptions, (err, result) => {
+ if (callback == null) return;
+ if (err) return handleCallback(callback, err, null);
+ if (result == null) return handleCallback(callback, null, null);
+ if (result.result.code) return handleCallback(callback, toError(result.result));
+ if (result.result.writeErrors)
+ return handleCallback(callback, toError(result.result.writeErrors[0]));
+ // Return the results
+ handleCallback(callback, null, result);
+ });
+}
+
+/**
+ * Update multiple documents in the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter The Filter used to select the documents to update
+ * @param {object} update The update operations to be applied to the document
+ * @param {object} [options] Optional settings. See Collection.prototype.updateMany for a list of options.
+ * @param {Collection~updateWriteOpCallback} [callback] The command result callback
+ */
+function updateMany(coll, filter, update, options, callback) {
+ // Set single document update
+ options.multi = true;
+ // Execute update
+ updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
+}
+
+/**
+ * Update a single document in the collection.
+ *
+ * @method
+ * @param {Collection} a Collection instance.
+ * @param {object} filter The Filter used to select the document to update
+ * @param {object} update The update operations to be applied to the document
+ * @param {object} [options] Optional settings. See Collection.prototype.updateOne for a list of options.
+ * @param {Collection~updateWriteOpCallback} [callback] The command result callback
+ */
+function updateOne(coll, filter, update, options, callback) {
+ // Set single document update
+ options.multi = false;
+ // Execute update
+ updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
+}
+
+module.exports = {
+ bulkWrite,
+ checkForAtomicOperators,
+ count,
+ countDocuments,
+ buildCountCommand,
+ createIndex,
+ createIndexes,
+ deleteMany,
+ deleteOne,
+ distinct,
+ dropIndex,
+ dropIndexes,
+ ensureIndex,
+ findAndModify,
+ findAndRemove,
+ findOne,
+ findOneAndDelete,
+ findOneAndReplace,
+ findOneAndUpdate,
+ geoHaystackSearch,
+ group,
+ indexes,
+ indexExists,
+ indexInformation,
+ insertOne,
+ isCapped,
+ mapReduce,
+ optionsOp,
+ parallelCollectionScan,
+ prepareDocs,
+ reIndex,
+ removeDocuments,
+ rename,
+ replaceOne,
+ save,
+ stats,
+ updateDocuments,
+ updateMany,
+ updateOne
+};
diff --git a/node_modules/mongodb/lib/operations/cursor_ops.js b/node_modules/mongodb/lib/operations/cursor_ops.js
new file mode 100644
index 0000000..5c0c093
--- /dev/null
+++ b/node_modules/mongodb/lib/operations/cursor_ops.js
@@ -0,0 +1,242 @@
+'use strict';
+
+const buildCountCommand = require('./collection_ops').buildCountCommand;
+const formattedOrderClause = require('../utils').formattedOrderClause;
+const handleCallback = require('../utils').handleCallback;
+const MongoError = require('mongodb-core').MongoError;
+const push = Array.prototype.push;
+
+/**
+ * Get the count of documents for this cursor.
+ *
+ * @method
+ * @param {Cursor} cursor The Cursor instance on which to count.
+ * @param {boolean} [applySkipLimit=true] Specifies whether the count command apply limit and skip settings should be applied on the cursor or in the provided options.
+ * @param {object} [options] Optional settings. See Cursor.prototype.count for a list of options.
+ * @param {Cursor~countResultCallback} [callback] The result callback.
+ */
+function count(cursor, applySkipLimit, opts, callback) {
+ if (applySkipLimit) {
+ if (typeof cursor.cursorSkip() === 'number') opts.skip = cursor.cursorSkip();
+ if (typeof cursor.cursorLimit() === 'number') opts.limit = cursor.cursorLimit();
+ }
+
+ // Ensure we have the right read preference inheritance
+ if (opts.readPreference) {
+ cursor.setReadPreference(opts.readPreference);
+ }
+
+ if (
+ typeof opts.maxTimeMS !== 'number' &&
+ cursor.s.cmd &&
+ typeof cursor.s.cmd.maxTimeMS === 'number'
+ ) {
+ opts.maxTimeMS = cursor.s.cmd.maxTimeMS;
+ }
+
+ let options = {};
+ options.skip = opts.skip;
+ options.limit = opts.limit;
+ options.hint = opts.hint;
+ options.maxTimeMS = opts.maxTimeMS;
+
+ // Command
+ const delimiter = cursor.s.ns.indexOf('.');
+ options.collectionName = cursor.s.ns.substr(delimiter + 1);
+
+ let command;
+ try {
+ command = buildCountCommand(cursor, cursor.s.cmd.query, options);
+ } catch (err) {
+ return callback(err);
+ }
+
+ // Set cursor server to the same as the topology
+ cursor.server = cursor.topology.s.coreTopology;
+
+ // Execute the command
+ cursor.s.topology.command(
+ `${cursor.s.ns.substr(0, delimiter)}.$cmd`,
+ command,
+ cursor.s.options,
+ (err, result) => {
+ callback(err, result ? result.result.n : null);
+ }
+ );
+}
+
+/**
+ * Iterates over all the documents for this cursor. See Cursor.prototype.each for more information.
+ *
+ * @method
+ * @deprecated
+ * @param {Cursor} cursor The Cursor instance on which to run.
+ * @param {Cursor~resultCallback} callback The result callback.
+ */
+function each(cursor, callback) {
+ const Cursor = require('../cursor');
+
+ if (!callback) throw MongoError.create({ message: 'callback is mandatory', driver: true });
+ if (cursor.isNotified()) return;
+ if (cursor.s.state === Cursor.CLOSED || cursor.isDead()) {
+ return handleCallback(
+ callback,
+ MongoError.create({ message: 'Cursor is closed', driver: true })
+ );
+ }
+
+ if (cursor.s.state === Cursor.INIT) cursor.s.state = Cursor.OPEN;
+
+ // Define function to avoid global scope escape
+ let fn = null;
+ // Trampoline all the entries
+ if (cursor.bufferedCount() > 0) {
+ while ((fn = loop(cursor, callback))) fn(cursor, callback);
+ each(cursor, callback);
+ } else {
+ cursor.next((err, item) => {
+ if (err) return handleCallback(callback, err);
+ if (item == null) {
+ return cursor.close({ skipKillCursors: true }, () => handleCallback(callback, null, null));
+ }
+
+ if (handleCallback(callback, null, item) === false) return;
+ each(cursor, callback);
+ });
+ }
+}
+
+/**
+ * Check if there is any document still available in the cursor.
+ *
+ * @method
+ * @param {Cursor} cursor The Cursor instance on which to run.
+ * @param {Cursor~resultCallback} [callback] The result callback.
+ */
+function hasNext(cursor, callback) {
+ const Cursor = require('../cursor');
+
+ if (cursor.s.currentDoc) {
+ return callback(null, true);
+ }
+
+ if (cursor.isNotified()) {
+ return callback(null, false);
+ }
+
+ nextObject(cursor, (err, doc) => {
+ if (err) return callback(err, null);
+ if (cursor.s.state === Cursor.CLOSED || cursor.isDead()) return callback(null, false);
+ if (!doc) return callback(null, false);
+ cursor.s.currentDoc = doc;
+ callback(null, true);
+ });
+}
+
+// Trampoline emptying the number of retrieved items
+// without incurring a nextTick operation
+function loop(cursor, callback) {
+ // No more items we are done
+ if (cursor.bufferedCount() === 0) return;
+ // Get the next document
+ cursor._next(callback);
+ // Loop
+ return loop;
+}
+
+/**
+ * Get the next available document from the cursor. Returns null if no more documents are available.
+ *
+ * @method
+ * @param {Cursor} cursor The Cursor instance from which to get the next document.
+ * @param {Cursor~resultCallback} [callback] The result callback.
+ */
+function next(cursor, callback) {
+ // Return the currentDoc if someone called hasNext first
+ if (cursor.s.currentDoc) {
+ const doc = cursor.s.currentDoc;
+ cursor.s.currentDoc = null;
+ return callback(null, doc);
+ }
+
+ // Return the next object
+ nextObject(cursor, callback);
+}
+
+// Get the next available document from the cursor, returns null if no more documents are available.
+function nextObject(cursor, callback) {
+ const Cursor = require('../cursor');
+
+ if (cursor.s.state === Cursor.CLOSED || (cursor.isDead && cursor.isDead()))
+ return handleCallback(
+ callback,
+ MongoError.create({ message: 'Cursor is closed', driver: true })
+ );
+ if (cursor.s.state === Cursor.INIT && cursor.s.cmd.sort) {
+ try {
+ cursor.s.cmd.sort = formattedOrderClause(cursor.s.cmd.sort);
+ } catch (err) {
+ return handleCallback(callback, err);
+ }
+ }
+
+ // Get the next object
+ cursor._next((err, doc) => {
+ cursor.s.state = Cursor.OPEN;
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, null, doc);
+ });
+}
+
+/**
+ * Returns an array of documents. See Cursor.prototype.toArray for more information.
+ *
+ * @method
+ * @param {Cursor} cursor The Cursor instance from which to get the next document.
+ * @param {Cursor~toArrayResultCallback} [callback] The result callback.
+ */
+function toArray(cursor, callback) {
+ const Cursor = require('../cursor');
+
+ const items = [];
+
+ // Reset cursor
+ cursor.rewind();
+ cursor.s.state = Cursor.INIT;
+
+ // Fetch all the documents
+ const fetchDocs = () => {
+ cursor._next((err, doc) => {
+ if (err) {
+ return cursor._endSession
+ ? cursor._endSession(() => handleCallback(callback, err))
+ : handleCallback(callback, err);
+ }
+ if (doc == null) {
+ return cursor.close({ skipKillCursors: true }, () => handleCallback(callback, null, items));
+ }
+
+ // Add doc to items
+ items.push(doc);
+
+ // Get all buffered objects
+ if (cursor.bufferedCount() > 0) {
+ let docs = cursor.readBufferedDocuments(cursor.bufferedCount());
+
+ // Transform the doc if transform method added
+ if (cursor.s.transforms && typeof cursor.s.transforms.doc === 'function') {
+ docs = docs.map(cursor.s.transforms.doc);
+ }
+
+ push.apply(items, docs);
+ }
+
+ // Attempt a fetch
+ fetchDocs();
+ });
+ };
+
+ fetchDocs();
+}
+
+module.exports = { count, each, hasNext, next, toArray };
diff --git a/node_modules/mongodb/lib/operations/db_ops.js b/node_modules/mongodb/lib/operations/db_ops.js
new file mode 100644
index 0000000..3795f8b
--- /dev/null
+++ b/node_modules/mongodb/lib/operations/db_ops.js
@@ -0,0 +1,986 @@
+'use strict';
+
+const applyWriteConcern = require('../utils').applyWriteConcern;
+const Code = require('mongodb-core').BSON.Code;
+const resolveReadPreference = require('../utils').resolveReadPreference;
+const crypto = require('crypto');
+const debugOptions = require('../utils').debugOptions;
+const handleCallback = require('../utils').handleCallback;
+const MongoError = require('mongodb-core').MongoError;
+const parseIndexOptions = require('../utils').parseIndexOptions;
+const ReadPreference = require('mongodb-core').ReadPreference;
+const toError = require('../utils').toError;
+const CONSTANTS = require('../constants');
+
+const count = require('./collection_ops').count;
+const findOne = require('./collection_ops').findOne;
+const remove = require('./collection_ops').remove;
+const updateOne = require('./collection_ops').updateOne;
+
+const debugFields = [
+ 'authSource',
+ 'w',
+ 'wtimeout',
+ 'j',
+ 'native_parser',
+ 'forceServerObjectId',
+ 'serializeFunctions',
+ 'raw',
+ 'promoteLongs',
+ 'promoteValues',
+ 'promoteBuffers',
+ 'bufferMaxEntries',
+ 'numberOfRetries',
+ 'retryMiliSeconds',
+ 'readPreference',
+ 'pkFactory',
+ 'parentDb',
+ 'promiseLibrary',
+ 'noListener'
+];
+
+// Filter out any write concern options
+const illegalCommandFields = [
+ 'w',
+ 'wtimeout',
+ 'j',
+ 'fsync',
+ 'autoIndexId',
+ 'strict',
+ 'serializeFunctions',
+ 'pkFactory',
+ 'raw',
+ 'readPreference',
+ 'session'
+];
+
+/**
+ * Add a user to the database.
+ * @method
+ * @param {Db} db The Db instance on which to add a user.
+ * @param {string} username The username.
+ * @param {string} password The password.
+ * @param {object} [options] Optional settings. See Db.prototype.addUser for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function addUser(db, username, password, options, callback) {
+ const Db = require('../db');
+
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed())
+ return callback(new MongoError('topology was destroyed'));
+ // Attempt to execute auth command
+ executeAuthCreateUserCommand(db, username, password, options, (err, r) => {
+ // We need to perform the backward compatible insert operation
+ if (err && err.code === -5000) {
+ const finalOptions = applyWriteConcern(Object.assign({}, options), { db }, options);
+
+ // Use node md5 generator
+ const md5 = crypto.createHash('md5');
+ // Generate keys used for authentication
+ md5.update(username + ':mongo:' + password);
+ const userPassword = md5.digest('hex');
+
+ // If we have another db set
+ const db = options.dbName ? new Db(options.dbName, db.s.topology, db.s.options) : db;
+
+ // Fetch a user collection
+ const collection = db.collection(CONSTANTS.SYSTEM_USER_COLLECTION);
+
+ // Check if we are inserting the first user
+ count(collection, {}, finalOptions, (err, count) => {
+ // We got an error (f.ex not authorized)
+ if (err != null) return handleCallback(callback, err, null);
+ // Check if the user exists and update i
+ const findOptions = Object.assign({ projection: { dbName: 1 } }, finalOptions);
+ collection.find({ user: username }, findOptions).toArray(err => {
+ // We got an error (f.ex not authorized)
+ if (err != null) return handleCallback(callback, err, null);
+ // Add command keys
+ finalOptions.upsert = true;
+
+ // We have a user, let's update the password or upsert if not
+ updateOne(
+ collection,
+ { user: username },
+ { $set: { user: username, pwd: userPassword } },
+ finalOptions,
+ err => {
+ if (count === 0 && err)
+ return handleCallback(callback, null, [{ user: username, pwd: userPassword }]);
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, [{ user: username, pwd: userPassword }]);
+ }
+ );
+ });
+ });
+
+ return;
+ }
+
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, err, r);
+ });
+}
+
+/**
+ * Fetch all collections for the current db.
+ *
+ * @method
+ * @param {Db} db The Db instance on which to fetch collections.
+ * @param {object} [options] Optional settings. See Db.prototype.collections for a list of options.
+ * @param {Db~collectionsResultCallback} [callback] The results callback
+ */
+function collections(db, options, callback) {
+ const Collection = require('../collection');
+
+ options = Object.assign({}, options, { nameOnly: true });
+ // Let's get the collection names
+ db.listCollections({}, options).toArray((err, documents) => {
+ if (err != null) return handleCallback(callback, err, null);
+ // Filter collections removing any illegal ones
+ documents = documents.filter(doc => {
+ return doc.name.indexOf('$') === -1;
+ });
+
+ // Return the collection objects
+ handleCallback(
+ callback,
+ null,
+ documents.map(d => {
+ return new Collection(
+ db,
+ db.s.topology,
+ db.s.databaseName,
+ d.name,
+ db.s.pkFactory,
+ db.s.options
+ );
+ })
+ );
+ });
+}
+
+/**
+ * Create a new collection on a server with the specified options. Use this to create capped collections.
+ * More information about command options available at https://docs.mongodb.com/manual/reference/command/create/
+ *
+ * @method
+ * @param {Db} db The Db instance on which to create the collection.
+ * @param {string} name The collection name to create.
+ * @param {object} [options] Optional settings. See Db.prototype.createCollection for a list of options.
+ * @param {Db~collectionResultCallback} [callback] The results callback
+ */
+function createCollection(db, name, options, callback) {
+ const Collection = require('../collection');
+
+ // Get the write concern options
+ const finalOptions = applyWriteConcern(Object.assign({}, options), { db }, options);
+
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed()) {
+ return callback(new MongoError('topology was destroyed'));
+ }
+
+ const listCollectionOptions = Object.assign({}, finalOptions, { nameOnly: true });
+
+ // Check if we have the name
+ db
+ .listCollections({ name }, listCollectionOptions)
+ .setReadPreference(ReadPreference.PRIMARY)
+ .toArray((err, collections) => {
+ if (err != null) return handleCallback(callback, err, null);
+ if (collections.length > 0 && finalOptions.strict) {
+ return handleCallback(
+ callback,
+ MongoError.create({
+ message: `Collection ${name} already exists. Currently in strict mode.`,
+ driver: true
+ }),
+ null
+ );
+ } else if (collections.length > 0) {
+ try {
+ return handleCallback(
+ callback,
+ null,
+ new Collection(db, db.s.topology, db.s.databaseName, name, db.s.pkFactory, options)
+ );
+ } catch (err) {
+ return handleCallback(callback, err);
+ }
+ }
+
+ // Create collection command
+ const cmd = { create: name };
+
+ // Decorate command with writeConcern if supported
+ applyWriteConcern(cmd, { db }, options);
+
+ // Add all optional parameters
+ for (let n in options) {
+ if (
+ options[n] != null &&
+ typeof options[n] !== 'function' &&
+ illegalCommandFields.indexOf(n) === -1
+ ) {
+ cmd[n] = options[n];
+ }
+ }
+
+ // Force a primary read Preference
+ finalOptions.readPreference = ReadPreference.PRIMARY;
+ // Execute command
+ executeCommand(db, cmd, finalOptions, err => {
+ if (err) return handleCallback(callback, err);
+ handleCallback(
+ callback,
+ null,
+ new Collection(db, db.s.topology, db.s.databaseName, name, db.s.pkFactory, options)
+ );
+ });
+ });
+}
+
+/**
+ * Creates an index on the db and collection.
+ * @method
+ * @param {Db} db The Db instance on which to create an index.
+ * @param {string} name Name of the collection to create the index on.
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {object} [options] Optional settings. See Db.prototype.createIndex for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function createIndex(db, name, fieldOrSpec, options, callback) {
+ // Get the write concern options
+ let finalOptions = Object.assign({}, { readPreference: ReadPreference.PRIMARY }, options);
+ finalOptions = applyWriteConcern(finalOptions, { db }, options);
+
+ // Ensure we have a callback
+ if (finalOptions.writeConcern && typeof callback !== 'function') {
+ throw MongoError.create({
+ message: 'Cannot use a writeConcern without a provided callback',
+ driver: true
+ });
+ }
+
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed())
+ return callback(new MongoError('topology was destroyed'));
+
+ // Attempt to run using createIndexes command
+ createIndexUsingCreateIndexes(db, name, fieldOrSpec, finalOptions, (err, result) => {
+ if (err == null) return handleCallback(callback, err, result);
+
+ /**
+ * The following errors mean that the server recognized `createIndex` as a command so we don't need to fallback to an insert:
+ * 67 = 'CannotCreateIndex' (malformed index options)
+ * 85 = 'IndexOptionsConflict' (index already exists with different options)
+ * 86 = 'IndexKeySpecsConflict' (index already exists with the same name)
+ * 11000 = 'DuplicateKey' (couldn't build unique index because of dupes)
+ * 11600 = 'InterruptedAtShutdown' (interrupted at shutdown)
+ * 197 = 'InvalidIndexSpecificationOption' (`_id` with `background: true`)
+ */
+ if (
+ err.code === 67 ||
+ err.code === 11000 ||
+ err.code === 85 ||
+ err.code === 86 ||
+ err.code === 11600 ||
+ err.code === 197
+ ) {
+ return handleCallback(callback, err, result);
+ }
+
+ // Create command
+ const doc = createCreateIndexCommand(db, name, fieldOrSpec, options);
+ // Set no key checking
+ finalOptions.checkKeys = false;
+ // Insert document
+ db.s.topology.insert(
+ `${db.s.databaseName}.${CONSTANTS.SYSTEM_INDEX_COLLECTION}`,
+ doc,
+ finalOptions,
+ (err, result) => {
+ if (callback == null) return;
+ if (err) return handleCallback(callback, err);
+ if (result == null) return handleCallback(callback, null, null);
+ if (result.result.writeErrors)
+ return handleCallback(callback, MongoError.create(result.result.writeErrors[0]), null);
+ handleCallback(callback, null, doc.name);
+ }
+ );
+ });
+}
+
+// Add listeners to topology
+function createListener(db, e, object) {
+ function listener(err) {
+ if (object.listeners(e).length > 0) {
+ object.emit(e, err, db);
+
+ // Emit on all associated db's if available
+ for (let i = 0; i < db.s.children.length; i++) {
+ db.s.children[i].emit(e, err, db.s.children[i]);
+ }
+ }
+ }
+ return listener;
+}
+
+/**
+ * Drop a collection from the database, removing it permanently. New accesses will create a new collection.
+ *
+ * @method
+ * @param {Db} db The Db instance on which to drop the collection.
+ * @param {string} name Name of collection to drop
+ * @param {Object} [options] Optional settings. See Db.prototype.dropCollection for a list of options.
+ * @param {Db~resultCallback} [callback] The results callback
+ */
+function dropCollection(db, name, options, callback) {
+ executeCommand(db, name, options, (err, result) => {
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed()) {
+ return callback(new MongoError('topology was destroyed'));
+ }
+
+ if (err) return handleCallback(callback, err);
+ if (result.ok) return handleCallback(callback, null, true);
+ handleCallback(callback, null, false);
+ });
+}
+
+/**
+ * Drop a database, removing it permanently from the server.
+ *
+ * @method
+ * @param {Db} db The Db instance to drop.
+ * @param {Object} cmd The command document.
+ * @param {Object} [options] Optional settings. See Db.prototype.dropDatabase for a list of options.
+ * @param {Db~resultCallback} [callback] The results callback
+ */
+function dropDatabase(db, cmd, options, callback) {
+ executeCommand(db, cmd, options, (err, result) => {
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed()) {
+ return callback(new MongoError('topology was destroyed'));
+ }
+
+ if (callback == null) return;
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, result.ok ? true : false);
+ });
+}
+
+/**
+ * Ensures that an index exists. If it does not, creates it.
+ *
+ * @method
+ * @param {Db} db The Db instance on which to ensure the index.
+ * @param {string} name The index name
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {object} [options] Optional settings. See Db.prototype.ensureIndex for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function ensureIndex(db, name, fieldOrSpec, options, callback) {
+ // Get the write concern options
+ const finalOptions = applyWriteConcern({}, { db }, options);
+ // Create command
+ const selector = createCreateIndexCommand(db, name, fieldOrSpec, options);
+ const index_name = selector.name;
+
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed())
+ return callback(new MongoError('topology was destroyed'));
+
+ // Merge primary readPreference
+ finalOptions.readPreference = ReadPreference.PRIMARY;
+
+ // Check if the index already exists
+ indexInformation(db, name, finalOptions, (err, indexInformation) => {
+ if (err != null && err.code !== 26) return handleCallback(callback, err, null);
+ // If the index does not exist, create it
+ if (indexInformation == null || !indexInformation[index_name]) {
+ createIndex(db, name, fieldOrSpec, options, callback);
+ } else {
+ if (typeof callback === 'function') return handleCallback(callback, null, index_name);
+ }
+ });
+}
+
+/**
+ * Evaluate JavaScript on the server
+ *
+ * @method
+ * @param {Db} db The Db instance.
+ * @param {Code} code JavaScript to execute on server.
+ * @param {(object|array)} parameters The parameters for the call.
+ * @param {object} [options] Optional settings. See Db.prototype.eval for a list of options.
+ * @param {Db~resultCallback} [callback] The results callback
+ * @deprecated Eval is deprecated on MongoDB 3.2 and forward
+ */
+function evaluate(db, code, parameters, options, callback) {
+ let finalCode = code;
+ let finalParameters = [];
+
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed())
+ return callback(new MongoError('topology was destroyed'));
+
+ // If not a code object translate to one
+ if (!(finalCode && finalCode._bsontype === 'Code')) finalCode = new Code(finalCode);
+ // Ensure the parameters are correct
+ if (parameters != null && !Array.isArray(parameters) && typeof parameters !== 'function') {
+ finalParameters = [parameters];
+ } else if (parameters != null && Array.isArray(parameters) && typeof parameters !== 'function') {
+ finalParameters = parameters;
+ }
+
+ // Create execution selector
+ let cmd = { $eval: finalCode, args: finalParameters };
+ // Check if the nolock parameter is passed in
+ if (options['nolock']) {
+ cmd['nolock'] = options['nolock'];
+ }
+
+ // Set primary read preference
+ options.readPreference = new ReadPreference(ReadPreference.PRIMARY);
+
+ // Execute the command
+ executeCommand(db, cmd, options, (err, result) => {
+ if (err) return handleCallback(callback, err, null);
+ if (result && result.ok === 1) return handleCallback(callback, null, result.retval);
+ if (result)
+ return handleCallback(
+ callback,
+ MongoError.create({ message: `eval failed: ${result.errmsg}`, driver: true }),
+ null
+ );
+ handleCallback(callback, err, result);
+ });
+}
+
+/**
+ * Execute a command
+ *
+ * @method
+ * @param {Db} db The Db instance on which to execute the command.
+ * @param {object} command The command hash
+ * @param {object} [options] Optional settings. See Db.prototype.command for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function executeCommand(db, command, options, callback) {
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed())
+ return callback(new MongoError('topology was destroyed'));
+ // Get the db name we are executing against
+ const dbName = options.dbName || options.authdb || db.s.databaseName;
+
+ // Convert the readPreference if its not a write
+ options.readPreference = resolveReadPreference(options, { db, default: ReadPreference.primary });
+
+ // Debug information
+ if (db.s.logger.isDebug())
+ db.s.logger.debug(
+ `executing command ${JSON.stringify(
+ command
+ )} against ${dbName}.$cmd with options [${JSON.stringify(
+ debugOptions(debugFields, options)
+ )}]`
+ );
+
+ // Execute command
+ db.s.topology.command(`${dbName}.$cmd`, command, options, (err, result) => {
+ if (err) return handleCallback(callback, err);
+ if (options.full) return handleCallback(callback, null, result);
+ handleCallback(callback, null, result.result);
+ });
+}
+
+/**
+ * Runs a command on the database as admin.
+ *
+ * @method
+ * @param {Db} db The Db instance on which to execute the command.
+ * @param {object} command The command hash
+ * @param {object} [options] Optional settings. See Db.prototype.executeDbAdminCommand for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function executeDbAdminCommand(db, command, options, callback) {
+ db.s.topology.command('admin.$cmd', command, options, (err, result) => {
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed()) {
+ return callback(new MongoError('topology was destroyed'));
+ }
+
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, null, result.result);
+ });
+}
+
+/**
+ * Retrieves this collections index info.
+ *
+ * @method
+ * @param {Db} db The Db instance on which to retrieve the index info.
+ * @param {string} name The name of the collection.
+ * @param {object} [options] Optional settings. See Db.prototype.indexInformation for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function indexInformation(db, name, options, callback) {
+ // If we specified full information
+ const full = options['full'] == null ? false : options['full'];
+
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed())
+ return callback(new MongoError('topology was destroyed'));
+ // Process all the results from the index command and collection
+ function processResults(indexes) {
+ // Contains all the information
+ let info = {};
+ // Process all the indexes
+ for (let i = 0; i < indexes.length; i++) {
+ const index = indexes[i];
+ // Let's unpack the object
+ info[index.name] = [];
+ for (let name in index.key) {
+ info[index.name].push([name, index.key[name]]);
+ }
+ }
+
+ return info;
+ }
+
+ // Get the list of indexes of the specified collection
+ db
+ .collection(name)
+ .listIndexes(options)
+ .toArray((err, indexes) => {
+ if (err) return callback(toError(err));
+ if (!Array.isArray(indexes)) return handleCallback(callback, null, []);
+ if (full) return handleCallback(callback, null, indexes);
+ handleCallback(callback, null, processResults(indexes));
+ });
+}
+
+// Transformation methods for cursor results
+function listCollectionsTransforms(databaseName) {
+ const matching = `${databaseName}.`;
+
+ return {
+ doc: doc => {
+ const index = doc.name.indexOf(matching);
+ // Remove database name if available
+ if (doc.name && index === 0) {
+ doc.name = doc.name.substr(index + matching.length);
+ }
+
+ return doc;
+ }
+ };
+}
+
+/**
+ * Retrive the current profiling information for MongoDB
+ *
+ * @method
+ * @param {Db} db The Db instance on which to retrieve the profiling info.
+ * @param {Object} [options] Optional settings. See Db.protoype.profilingInfo for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback.
+ * @deprecated Query the system.profile collection directly.
+ */
+function profilingInfo(db, options, callback) {
+ try {
+ db
+ .collection('system.profile')
+ .find({}, options)
+ .toArray(callback);
+ } catch (err) {
+ return callback(err, null);
+ }
+}
+
+/**
+ * Retrieve the current profiling level for MongoDB
+ *
+ * @method
+ * @param {Db} db The Db instance on which to retrieve the profiling level.
+ * @param {Object} [options] Optional settings. See Db.prototype.profilingLevel for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function profilingLevel(db, options, callback) {
+ executeCommand(db, { profile: -1 }, options, (err, doc) => {
+ if (err == null && doc.ok === 1) {
+ const was = doc.was;
+ if (was === 0) return callback(null, 'off');
+ if (was === 1) return callback(null, 'slow_only');
+ if (was === 2) return callback(null, 'all');
+ return callback(new Error('Error: illegal profiling level value ' + was), null);
+ } else {
+ err != null ? callback(err, null) : callback(new Error('Error with profile command'), null);
+ }
+ });
+}
+
+/**
+ * Remove a user from a database
+ *
+ * @method
+ * @param {Db} db The Db instance on which to remove the user.
+ * @param {string} username The username.
+ * @param {object} [options] Optional settings. See Db.prototype.removeUser for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function removeUser(db, username, options, callback) {
+ const Db = require('../db');
+
+ // Attempt to execute command
+ executeAuthRemoveUserCommand(db, username, options, (err, result) => {
+ if (err && err.code === -5000) {
+ const finalOptions = applyWriteConcern(Object.assign({}, options), { db }, options);
+ // If we have another db set
+ const db = options.dbName ? new Db(options.dbName, db.s.topology, db.s.options) : db;
+
+ // Fetch a user collection
+ const collection = db.collection(CONSTANTS.SYSTEM_USER_COLLECTION);
+
+ // Locate the user
+ findOne(collection, { user: username }, finalOptions, (err, user) => {
+ if (user == null) return handleCallback(callback, err, false);
+ remove(collection, { user: username }, finalOptions, err => {
+ handleCallback(callback, err, true);
+ });
+ });
+
+ return;
+ }
+
+ if (err) return handleCallback(callback, err);
+ handleCallback(callback, err, result);
+ });
+}
+
+/**
+ * Set the current profiling level of MongoDB
+ *
+ * @method
+ * @param {Db} db The Db instance on which to execute the command.
+ * @param {string} level The new profiling level (off, slow_only, all).
+ * @param {Object} [options] Optional settings. See Db.prototype.setProfilingLevel for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback.
+ */
+function setProfilingLevel(db, level, options, callback) {
+ const command = {};
+ let profile = 0;
+
+ if (level === 'off') {
+ profile = 0;
+ } else if (level === 'slow_only') {
+ profile = 1;
+ } else if (level === 'all') {
+ profile = 2;
+ } else {
+ return callback(new Error('Error: illegal profiling level value ' + level));
+ }
+
+ // Set up the profile number
+ command['profile'] = profile;
+
+ executeCommand(db, command, options, (err, doc) => {
+ if (err == null && doc.ok === 1) return callback(null, level);
+ return err != null
+ ? callback(err, null)
+ : callback(new Error('Error with profile command'), null);
+ });
+}
+
+// Validate the database name
+function validateDatabaseName(databaseName) {
+ if (typeof databaseName !== 'string')
+ throw MongoError.create({ message: 'database name must be a string', driver: true });
+ if (databaseName.length === 0)
+ throw MongoError.create({ message: 'database name cannot be the empty string', driver: true });
+ if (databaseName === '$external') return;
+
+ const invalidChars = [' ', '.', '$', '/', '\\'];
+ for (let i = 0; i < invalidChars.length; i++) {
+ if (databaseName.indexOf(invalidChars[i]) !== -1)
+ throw MongoError.create({
+ message: "database names cannot contain the character '" + invalidChars[i] + "'",
+ driver: true
+ });
+ }
+}
+
+/**
+ * Create the command object for Db.prototype.createIndex.
+ *
+ * @param {Db} db The Db instance on which to create the command.
+ * @param {string} name Name of the collection to create the index on.
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {Object} [options] Optional settings. See Db.prototype.createIndex for a list of options.
+ * @return {Object} The insert command object.
+ */
+function createCreateIndexCommand(db, name, fieldOrSpec, options) {
+ const indexParameters = parseIndexOptions(fieldOrSpec);
+ const fieldHash = indexParameters.fieldHash;
+
+ // Generate the index name
+ const indexName = typeof options.name === 'string' ? options.name : indexParameters.name;
+ const selector = {
+ ns: db.databaseName + '.' + name,
+ key: fieldHash,
+ name: indexName
+ };
+
+ // Ensure we have a correct finalUnique
+ const finalUnique = options == null || 'object' === typeof options ? false : options;
+ // Set up options
+ options = options == null || typeof options === 'boolean' ? {} : options;
+
+ // Add all the options
+ const keysToOmit = Object.keys(selector);
+ for (let optionName in options) {
+ if (keysToOmit.indexOf(optionName) === -1) {
+ selector[optionName] = options[optionName];
+ }
+ }
+
+ if (selector['unique'] == null) selector['unique'] = finalUnique;
+
+ // Remove any write concern operations
+ const removeKeys = ['w', 'wtimeout', 'j', 'fsync', 'readPreference', 'session'];
+ for (let i = 0; i < removeKeys.length; i++) {
+ delete selector[removeKeys[i]];
+ }
+
+ // Return the command creation selector
+ return selector;
+}
+
+/**
+ * Create index using the createIndexes command.
+ *
+ * @param {Db} db The Db instance on which to execute the command.
+ * @param {string} name Name of the collection to create the index on.
+ * @param {(string|object)} fieldOrSpec Defines the index.
+ * @param {Object} [options] Optional settings. See Db.prototype.createIndex for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback.
+ */
+function createIndexUsingCreateIndexes(db, name, fieldOrSpec, options, callback) {
+ // Build the index
+ const indexParameters = parseIndexOptions(fieldOrSpec);
+ // Generate the index name
+ const indexName = typeof options.name === 'string' ? options.name : indexParameters.name;
+ // Set up the index
+ const indexes = [{ name: indexName, key: indexParameters.fieldHash }];
+ // merge all the options
+ const keysToOmit = Object.keys(indexes[0]).concat([
+ 'writeConcern',
+ 'w',
+ 'wtimeout',
+ 'j',
+ 'fsync',
+ 'readPreference',
+ 'session'
+ ]);
+
+ for (let optionName in options) {
+ if (keysToOmit.indexOf(optionName) === -1) {
+ indexes[0][optionName] = options[optionName];
+ }
+ }
+
+ // Get capabilities
+ const capabilities = db.s.topology.capabilities();
+
+ // Did the user pass in a collation, check if our write server supports it
+ if (indexes[0].collation && capabilities && !capabilities.commandsTakeCollation) {
+ // Create a new error
+ const error = new MongoError('server/primary/mongos does not support collation');
+ error.code = 67;
+ // Return the error
+ return callback(error);
+ }
+
+ // Create command, apply write concern to command
+ const cmd = applyWriteConcern({ createIndexes: name, indexes }, { db }, options);
+
+ // ReadPreference primary
+ options.readPreference = ReadPreference.PRIMARY;
+
+ // Build the command
+ executeCommand(db, cmd, options, (err, result) => {
+ if (err) return handleCallback(callback, err, null);
+ if (result.ok === 0) return handleCallback(callback, toError(result), null);
+ // Return the indexName for backward compatibility
+ handleCallback(callback, null, indexName);
+ });
+}
+
+/**
+ * Run the createUser command.
+ *
+ * @param {Db} db The Db instance on which to execute the command.
+ * @param {string} username The username of the user to add.
+ * @param {string} password The password of the user to add.
+ * @param {object} [options] Optional settings. See Db.prototype.addUser for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function executeAuthCreateUserCommand(db, username, password, options, callback) {
+ // Special case where there is no password ($external users)
+ if (typeof username === 'string' && password != null && typeof password === 'object') {
+ options = password;
+ password = null;
+ }
+
+ // Unpack all options
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ // Error out if we digestPassword set
+ if (options.digestPassword != null) {
+ return callback(
+ toError(
+ "The digestPassword option is not supported via add_user. Please use db.command('createUser', ...) instead for this option."
+ )
+ );
+ }
+
+ // Get additional values
+ const customData = options.customData != null ? options.customData : {};
+ let roles = Array.isArray(options.roles) ? options.roles : [];
+ const maxTimeMS = typeof options.maxTimeMS === 'number' ? options.maxTimeMS : null;
+
+ // If not roles defined print deprecated message
+ if (roles.length === 0) {
+ console.log('Creating a user without roles is deprecated in MongoDB >= 2.6');
+ }
+
+ // Get the error options
+ const commandOptions = { writeCommand: true };
+ if (options['dbName']) commandOptions.dbName = options['dbName'];
+
+ // Add maxTimeMS to options if set
+ if (maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
+
+ // Check the db name and add roles if needed
+ if (
+ (db.databaseName.toLowerCase() === 'admin' || options.dbName === 'admin') &&
+ !Array.isArray(options.roles)
+ ) {
+ roles = ['root'];
+ } else if (!Array.isArray(options.roles)) {
+ roles = ['dbOwner'];
+ }
+
+ const digestPassword = db.s.topology.lastIsMaster().maxWireVersion >= 7;
+
+ // Build the command to execute
+ let command = {
+ createUser: username,
+ customData: customData,
+ roles: roles,
+ digestPassword
+ };
+
+ // Apply write concern to command
+ command = applyWriteConcern(command, { db }, options);
+
+ let userPassword = password;
+
+ if (!digestPassword) {
+ // Use node md5 generator
+ const md5 = crypto.createHash('md5');
+ // Generate keys used for authentication
+ md5.update(username + ':mongo:' + password);
+ userPassword = md5.digest('hex');
+ }
+
+ // No password
+ if (typeof password === 'string') {
+ command.pwd = userPassword;
+ }
+
+ // Force write using primary
+ commandOptions.readPreference = ReadPreference.primary;
+
+ // Execute the command
+ executeCommand(db, command, commandOptions, (err, result) => {
+ if (err && err.ok === 0 && err.code === undefined)
+ return handleCallback(callback, { code: -5000 }, null);
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(
+ callback,
+ !result.ok ? toError(result) : null,
+ result.ok ? [{ user: username, pwd: '' }] : null
+ );
+ });
+}
+
+/**
+ * Run the dropUser command.
+ *
+ * @param {Db} db The Db instance on which to execute the command.
+ * @param {string} username The username of the user to remove.
+ * @param {object} [options] Optional settings. See Db.prototype.removeUser for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function executeAuthRemoveUserCommand(db, username, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ // Did the user destroy the topology
+ if (db.serverConfig && db.serverConfig.isDestroyed())
+ return callback(new MongoError('topology was destroyed'));
+ // Get the error options
+ const commandOptions = { writeCommand: true };
+ if (options['dbName']) commandOptions.dbName = options['dbName'];
+
+ // Get additional values
+ const maxTimeMS = typeof options.maxTimeMS === 'number' ? options.maxTimeMS : null;
+
+ // Add maxTimeMS to options if set
+ if (maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
+
+ // Build the command to execute
+ let command = {
+ dropUser: username
+ };
+
+ // Apply write concern to command
+ command = applyWriteConcern(command, { db }, options);
+
+ // Force write using primary
+ commandOptions.readPreference = ReadPreference.primary;
+
+ // Execute the command
+ executeCommand(db, command, commandOptions, (err, result) => {
+ if (err && !err.ok && err.code === undefined) return handleCallback(callback, { code: -5000 });
+ if (err) return handleCallback(callback, err, null);
+ handleCallback(callback, null, result.ok ? true : false);
+ });
+}
+
+module.exports = {
+ addUser,
+ collections,
+ createCollection,
+ createListener,
+ createIndex,
+ dropCollection,
+ dropDatabase,
+ ensureIndex,
+ evaluate,
+ executeCommand,
+ executeDbAdminCommand,
+ listCollectionsTransforms,
+ indexInformation,
+ profilingInfo,
+ profilingLevel,
+ removeUser,
+ setProfilingLevel,
+ validateDatabaseName
+};
diff --git a/node_modules/mongodb/lib/operations/mongo_client_ops.js b/node_modules/mongodb/lib/operations/mongo_client_ops.js
new file mode 100644
index 0000000..4f7da1c
--- /dev/null
+++ b/node_modules/mongodb/lib/operations/mongo_client_ops.js
@@ -0,0 +1,646 @@
+'use strict';
+
+const authenticate = require('../authenticate');
+const deprecate = require('util').deprecate;
+const Logger = require('mongodb-core').Logger;
+const MongoError = require('mongodb-core').MongoError;
+const Mongos = require('../topologies/mongos');
+const parse = require('mongodb-core').parseConnectionString;
+const ReadPreference = require('mongodb-core').ReadPreference;
+const ReplSet = require('../topologies/replset');
+const Server = require('../topologies/server');
+const ServerSessionPool = require('mongodb-core').Sessions.ServerSessionPool;
+
+const monitoringEvents = [
+ 'timeout',
+ 'close',
+ 'serverOpening',
+ 'serverDescriptionChanged',
+ 'serverHeartbeatStarted',
+ 'serverHeartbeatSucceeded',
+ 'serverHeartbeatFailed',
+ 'serverClosed',
+ 'topologyOpening',
+ 'topologyClosed',
+ 'topologyDescriptionChanged',
+ 'commandStarted',
+ 'commandSucceeded',
+ 'commandFailed',
+ 'joined',
+ 'left',
+ 'ping',
+ 'ha',
+ 'all',
+ 'fullsetup',
+ 'open'
+];
+const ignoreOptionNames = ['native_parser'];
+const legacyOptionNames = ['server', 'replset', 'replSet', 'mongos', 'db'];
+const legacyParse = deprecate(
+ require('../url_parser'),
+ 'current URL string parser is deprecated, and will be removed in a future version. ' +
+ 'To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.'
+);
+const validOptionNames = [
+ 'poolSize',
+ 'ssl',
+ 'sslValidate',
+ 'sslCA',
+ 'sslCert',
+ 'sslKey',
+ 'sslPass',
+ 'sslCRL',
+ 'autoReconnect',
+ 'noDelay',
+ 'keepAlive',
+ 'keepAliveInitialDelay',
+ 'connectTimeoutMS',
+ 'family',
+ 'socketTimeoutMS',
+ 'reconnectTries',
+ 'reconnectInterval',
+ 'ha',
+ 'haInterval',
+ 'replicaSet',
+ 'secondaryAcceptableLatencyMS',
+ 'acceptableLatencyMS',
+ 'connectWithNoPrimary',
+ 'authSource',
+ 'w',
+ 'wtimeout',
+ 'j',
+ 'forceServerObjectId',
+ 'serializeFunctions',
+ 'ignoreUndefined',
+ 'raw',
+ 'bufferMaxEntries',
+ 'readPreference',
+ 'pkFactory',
+ 'promiseLibrary',
+ 'readConcern',
+ 'maxStalenessSeconds',
+ 'loggerLevel',
+ 'logger',
+ 'promoteValues',
+ 'promoteBuffers',
+ 'promoteLongs',
+ 'domainsEnabled',
+ 'checkServerIdentity',
+ 'validateOptions',
+ 'appname',
+ 'auth',
+ 'user',
+ 'password',
+ 'authMechanism',
+ 'compression',
+ 'fsync',
+ 'readPreferenceTags',
+ 'numberOfRetries',
+ 'auto_reconnect',
+ 'minSize',
+ 'monitorCommands',
+ 'retryWrites',
+ 'useNewUrlParser'
+];
+
+function addListeners(mongoClient, topology) {
+ topology.on('authenticated', createListener(mongoClient, 'authenticated'));
+ topology.on('error', createListener(mongoClient, 'error'));
+ topology.on('timeout', createListener(mongoClient, 'timeout'));
+ topology.on('close', createListener(mongoClient, 'close'));
+ topology.on('parseError', createListener(mongoClient, 'parseError'));
+ topology.once('open', createListener(mongoClient, 'open'));
+ topology.once('fullsetup', createListener(mongoClient, 'fullsetup'));
+ topology.once('all', createListener(mongoClient, 'all'));
+ topology.on('reconnect', createListener(mongoClient, 'reconnect'));
+}
+
+function assignTopology(client, topology) {
+ client.topology = topology;
+ topology.s.sessionPool = new ServerSessionPool(topology.s.coreTopology);
+}
+
+// Clear out all events
+function clearAllEvents(topology) {
+ monitoringEvents.forEach(event => topology.removeAllListeners(event));
+}
+
+// Collect all events in order from SDAM
+function collectEvents(mongoClient, topology) {
+ const MongoClient = require('../mongo_client');
+ const collectedEvents = [];
+
+ if (mongoClient instanceof MongoClient) {
+ monitoringEvents.forEach(event => {
+ topology.on(event, (object1, object2) => {
+ if (event === 'open') {
+ collectedEvents.push({ event: event, object1: mongoClient });
+ } else {
+ collectedEvents.push({ event: event, object1: object1, object2: object2 });
+ }
+ });
+ });
+ }
+
+ return collectedEvents;
+}
+
+/**
+ * Connect to MongoDB using a url as documented at
+ *
+ * docs.mongodb.org/manual/reference/connection-string/
+ *
+ * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
+ *
+ * @method
+ * @param {MongoClient} mongoClient The MongoClient instance with which to connect.
+ * @param {string} url The connection URI string
+ * @param {object} [options] Optional settings. See MongoClient.prototype.connect for a list of options.
+ * @param {MongoClient~connectCallback} [callback] The command result callback
+ */
+function connect(mongoClient, url, options, callback) {
+ options = Object.assign({}, options);
+
+ // If callback is null throw an exception
+ if (callback == null) {
+ throw new Error('no callback function provided');
+ }
+
+ // Get a logger for MongoClient
+ const logger = Logger('MongoClient', options);
+
+ // Did we pass in a Server/ReplSet/Mongos
+ if (url instanceof Server || url instanceof ReplSet || url instanceof Mongos) {
+ return connectWithUrl(mongoClient, url, options, connectCallback);
+ }
+
+ const parseFn = options.useNewUrlParser ? parse : legacyParse;
+ const transform = options.useNewUrlParser ? transformUrlOptions : legacyTransformUrlOptions;
+
+ parseFn(url, options, (err, _object) => {
+ // Do not attempt to connect if parsing error
+ if (err) return callback(err);
+
+ // Flatten
+ const object = transform(_object);
+
+ // Parse the string
+ const _finalOptions = createUnifiedOptions(object, options);
+
+ // Check if we have connection and socket timeout set
+ if (_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
+ if (_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
+
+ if (_finalOptions.db_options && _finalOptions.db_options.auth) {
+ delete _finalOptions.db_options.auth;
+ }
+
+ // Store the merged options object
+ mongoClient.s.options = _finalOptions;
+
+ // Failure modes
+ if (object.servers.length === 0) {
+ return callback(new Error('connection string must contain at least one seed host'));
+ }
+
+ // Do we have a replicaset then skip discovery and go straight to connectivity
+ if (_finalOptions.replicaSet || _finalOptions.rs_name) {
+ return createTopology(
+ mongoClient,
+ 'replicaset',
+ _finalOptions,
+ connectHandler(mongoClient, _finalOptions, connectCallback)
+ );
+ } else if (object.servers.length > 1) {
+ return createTopology(
+ mongoClient,
+ 'mongos',
+ _finalOptions,
+ connectHandler(mongoClient, _finalOptions, connectCallback)
+ );
+ } else {
+ return createServer(
+ mongoClient,
+ _finalOptions,
+ connectHandler(mongoClient, _finalOptions, connectCallback)
+ );
+ }
+ });
+ function connectCallback(err, topology) {
+ const warningMessage = `seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name`;
+ if (err && err.message === 'no mongos proxies found in seed list') {
+ if (logger.isWarn()) {
+ logger.warn(warningMessage);
+ }
+
+ // Return a more specific error message for MongoClient.connect
+ return callback(new MongoError(warningMessage));
+ }
+
+ // Return the error and db instance
+ callback(err, topology);
+ }
+}
+
+function connectHandler(client, options, callback) {
+ return (err, topology) => {
+ if (err) {
+ return handleConnectCallback(err, topology, callback);
+ }
+
+ // No authentication just reconnect
+ if (!options.auth) {
+ return handleConnectCallback(err, topology, callback);
+ }
+
+ // Authenticate
+ authenticate(client, options.user, options.password, options, (err, success) => {
+ if (success) {
+ handleConnectCallback(null, topology, callback);
+ } else {
+ if (topology) topology.close();
+ const authError = err ? err : new Error('Could not authenticate user ' + options.auth[0]);
+ handleConnectCallback(authError, topology, callback);
+ }
+ });
+ };
+}
+
+/**
+ * Connect to MongoDB using a url as documented at
+ *
+ * docs.mongodb.org/manual/reference/connection-string/
+ *
+ * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
+ *
+ * @method
+ * @param {MongoClient} mongoClient The MongoClient instance with which to connect.
+ * @param {MongoClient~connectCallback} [callback] The command result callback
+ */
+function connectOp(mongoClient, err, callback) {
+ // Did we have a validation error
+ if (err) return callback(err);
+ // Fallback to callback based connect
+ connect(mongoClient, mongoClient.s.url, mongoClient.s.options, err => {
+ if (err) return callback(err);
+ callback(null, mongoClient);
+ });
+}
+
+function connectWithUrl(mongoClient, url, options, connectCallback) {
+ // Set the topology
+ assignTopology(mongoClient, url);
+
+ // Add listeners
+ addListeners(mongoClient, url);
+
+ // Propagate the events to the client
+ relayEvents(mongoClient, url);
+
+ let finalOptions = Object.assign({}, options);
+
+ // If we have a readPreference passed in by the db options, convert it from a string
+ if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
+ finalOptions.readPreference = new ReadPreference(
+ options.readPreference || options.read_preference
+ );
+ }
+
+ // Connect
+ return url.connect(
+ finalOptions,
+ connectHandler(mongoClient, finalOptions, (err, topology) => {
+ if (err) return connectCallback(err, topology);
+ if (finalOptions.user || finalOptions.password || finalOptions.authMechanism) {
+ return authenticate(
+ mongoClient,
+ finalOptions.user,
+ finalOptions.password,
+ finalOptions,
+ err => {
+ if (err) return connectCallback(err, topology);
+ connectCallback(err, topology);
+ }
+ );
+ }
+
+ connectCallback(err, topology);
+ })
+ );
+}
+
+function createListener(mongoClient, event) {
+ const eventSet = new Set(['all', 'fullsetup', 'open', 'reconnect']);
+ return (v1, v2) => {
+ if (eventSet.has(event)) {
+ return mongoClient.emit(event, mongoClient);
+ }
+
+ mongoClient.emit(event, v1, v2);
+ };
+}
+
+function createServer(mongoClient, options, callback) {
+ // Pass in the promise library
+ options.promiseLibrary = mongoClient.s.promiseLibrary;
+
+ // Set default options
+ const servers = translateOptions(options);
+
+ const server = servers[0];
+
+ // Propagate the events to the client
+ const collectedEvents = collectEvents(mongoClient, server);
+
+ // Connect to topology
+ server.connect(options, (err, topology) => {
+ if (err) {
+ server.close(true);
+ return callback(err);
+ }
+ // Clear out all the collected event listeners
+ clearAllEvents(server);
+
+ // Relay all the events
+ relayEvents(mongoClient, server);
+ // Add listeners
+ addListeners(mongoClient, server);
+ // Check if we are really speaking to a mongos
+ const ismaster = topology.lastIsMaster();
+
+ // Set the topology
+ assignTopology(mongoClient, topology);
+
+ // Do we actually have a mongos
+ if (ismaster && ismaster.msg === 'isdbgrid') {
+ // Destroy the current connection
+ topology.close();
+ // Create mongos connection instead
+ return createTopology(mongoClient, 'mongos', options, callback);
+ }
+
+ // Fire all the events
+ replayEvents(mongoClient, collectedEvents);
+ // Otherwise callback
+ callback(err, topology);
+ });
+}
+
+function createTopology(mongoClient, topologyType, options, callback) {
+ // Pass in the promise library
+ options.promiseLibrary = mongoClient.s.promiseLibrary;
+
+ const translationOptions = {};
+ if (topologyType === 'unified') translationOptions.createServers = false;
+
+ // Set default options
+ const servers = translateOptions(options, translationOptions);
+
+ // Create the topology
+ let topology;
+ if (topologyType === 'mongos') {
+ topology = new Mongos(servers, options);
+ } else if (topologyType === 'replicaset') {
+ topology = new ReplSet(servers, options);
+ }
+
+ // Add listeners
+ addListeners(mongoClient, topology);
+
+ // Propagate the events to the client
+ relayEvents(mongoClient, topology);
+
+ // Open the connection
+ topology.connect(options, (err, newTopology) => {
+ if (err) {
+ topology.close(true);
+ return callback(err);
+ }
+
+ assignTopology(mongoClient, newTopology);
+ callback(null, newTopology);
+ });
+}
+
+function createUnifiedOptions(finalOptions, options) {
+ const childOptions = [
+ 'mongos',
+ 'server',
+ 'db',
+ 'replset',
+ 'db_options',
+ 'server_options',
+ 'rs_options',
+ 'mongos_options'
+ ];
+ const noMerge = ['readconcern', 'compression'];
+
+ for (const name in options) {
+ if (noMerge.indexOf(name.toLowerCase()) !== -1) {
+ finalOptions[name] = options[name];
+ } else if (childOptions.indexOf(name.toLowerCase()) !== -1) {
+ finalOptions = mergeOptions(finalOptions, options[name], false);
+ } else {
+ if (
+ options[name] &&
+ typeof options[name] === 'object' &&
+ !Buffer.isBuffer(options[name]) &&
+ !Array.isArray(options[name])
+ ) {
+ finalOptions = mergeOptions(finalOptions, options[name], true);
+ } else {
+ finalOptions[name] = options[name];
+ }
+ }
+ }
+
+ return finalOptions;
+}
+
+function handleConnectCallback(err, topology, callback) {
+ return process.nextTick(() => {
+ try {
+ callback(err, topology);
+ } catch (err) {
+ if (topology) topology.close();
+ throw err;
+ }
+ });
+}
+
+function legacyTransformUrlOptions(object) {
+ return mergeOptions(createUnifiedOptions({}, object), object, false);
+}
+
+/**
+ * Logout user from server, fire off on all connections and remove all auth info.
+ *
+ * @method
+ * @param {MongoClient} mongoClient The MongoClient instance on which to logout.
+ * @param {object} [options] Optional settings. See MongoClient.prototype.logout for a list of options.
+ * @param {Db~resultCallback} [callback] The command result callback
+ */
+function logout(mongoClient, dbName, callback) {
+ mongoClient.topology.logout(dbName, err => {
+ if (err) return callback(err);
+ callback(null, true);
+ });
+}
+
+function mergeOptions(target, source, flatten) {
+ for (const name in source) {
+ if (source[name] && typeof source[name] === 'object' && flatten) {
+ target = mergeOptions(target, source[name], flatten);
+ } else {
+ target[name] = source[name];
+ }
+ }
+
+ return target;
+}
+
+function relayEvents(mongoClient, topology) {
+ const serverOrCommandEvents = [
+ 'serverOpening',
+ 'serverDescriptionChanged',
+ 'serverHeartbeatStarted',
+ 'serverHeartbeatSucceeded',
+ 'serverHeartbeatFailed',
+ 'serverClosed',
+ 'topologyOpening',
+ 'topologyClosed',
+ 'topologyDescriptionChanged',
+ 'commandStarted',
+ 'commandSucceeded',
+ 'commandFailed',
+ 'joined',
+ 'left',
+ 'ping',
+ 'ha'
+ ];
+
+ serverOrCommandEvents.forEach(event => {
+ topology.on(event, (object1, object2) => {
+ mongoClient.emit(event, object1, object2);
+ });
+ });
+}
+
+//
+// Replay any events due to single server connection switching to Mongos
+//
+function replayEvents(mongoClient, events) {
+ for (let i = 0; i < events.length; i++) {
+ mongoClient.emit(events[i].event, events[i].object1, events[i].object2);
+ }
+}
+
+const LEGACY_OPTIONS_MAP = validOptionNames.reduce((obj, name) => {
+ obj[name.toLowerCase()] = name;
+ return obj;
+}, {});
+
+function transformUrlOptions(_object) {
+ let object = Object.assign({ servers: _object.hosts }, _object.options);
+ for (let name in object) {
+ const camelCaseName = LEGACY_OPTIONS_MAP[name];
+ if (camelCaseName) {
+ object[camelCaseName] = object[name];
+ }
+ }
+ if (_object.auth) {
+ const auth = _object.auth;
+ for (let i in auth) {
+ if (auth[i]) {
+ object[i] = auth[i];
+ }
+ }
+
+ if (auth.username) {
+ object.auth = auth;
+ object.user = auth.username;
+ }
+
+ if (auth.db) {
+ object.authSource = object.authSource || auth.db;
+ }
+ }
+
+ if (_object.defaultDatabase) {
+ object.dbName = _object.defaultDatabase;
+ }
+
+ if (object.maxpoolsize) {
+ object.poolSize = object.maxpoolsize;
+ }
+
+ if (object.readconcernlevel) {
+ object.readConcern = { level: object.readconcernlevel };
+ }
+
+ if (object.wtimeoutms) {
+ object.wtimeout = object.wtimeoutms;
+ }
+
+ return object;
+}
+
+function translateOptions(options, translationOptions) {
+ translationOptions = Object.assign({}, { createServers: true }, translationOptions);
+
+ // If we have a readPreference passed in by the db options
+ if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
+ options.readPreference = new ReadPreference(options.readPreference || options.read_preference);
+ }
+
+ // Do we have readPreference tags, add them
+ if (options.readPreference && (options.readPreferenceTags || options.read_preference_tags)) {
+ options.readPreference.tags = options.readPreferenceTags || options.read_preference_tags;
+ }
+
+ // Do we have maxStalenessSeconds
+ if (options.maxStalenessSeconds) {
+ options.readPreference.maxStalenessSeconds = options.maxStalenessSeconds;
+ }
+
+ // Set the socket and connection timeouts
+ if (options.socketTimeoutMS == null) options.socketTimeoutMS = 360000;
+ if (options.connectTimeoutMS == null) options.connectTimeoutMS = 30000;
+
+ if (!translationOptions.createServers) {
+ return;
+ }
+
+ // Create server instances
+ return options.servers.map(serverObj => {
+ return serverObj.domain_socket
+ ? new Server(serverObj.domain_socket, 27017, options)
+ : new Server(serverObj.host, serverObj.port, options);
+ });
+}
+
+// Validate options object
+function validOptions(options) {
+ const _validOptions = validOptionNames.concat(legacyOptionNames);
+
+ for (const name in options) {
+ if (ignoreOptionNames.indexOf(name) !== -1) {
+ continue;
+ }
+
+ if (_validOptions.indexOf(name) === -1 && options.validateOptions) {
+ return new MongoError(`option ${name} is not supported`);
+ } else if (_validOptions.indexOf(name) === -1) {
+ console.warn(`the options [${name}] is not supported`);
+ }
+
+ if (legacyOptionNames.indexOf(name) !== -1) {
+ console.warn(
+ `the server/replset/mongos/db options are deprecated, ` +
+ `all their options are supported at the top level of the options object [${validOptionNames}]`
+ );
+ }
+ }
+}
+
+module.exports = { connectOp, logout, validOptions };
diff --git a/node_modules/mongodb/lib/topologies/mongos.js b/node_modules/mongodb/lib/topologies/mongos.js
new file mode 100644
index 0000000..dc14277
--- /dev/null
+++ b/node_modules/mongodb/lib/topologies/mongos.js
@@ -0,0 +1,452 @@
+'use strict';
+
+const TopologyBase = require('./topology_base').TopologyBase;
+const MongoError = require('mongodb-core').MongoError;
+const CMongos = require('mongodb-core').Mongos;
+const Cursor = require('../cursor');
+const Server = require('./server');
+const Store = require('./topology_base').Store;
+const MAX_JS_INT = require('../utils').MAX_JS_INT;
+const translateOptions = require('../utils').translateOptions;
+const filterOptions = require('../utils').filterOptions;
+const mergeOptions = require('../utils').mergeOptions;
+
+/**
+ * @fileOverview The **Mongos** class is a class that represents a Mongos Proxy topology and is
+ * used to construct connections.
+ *
+ * **Mongos Should not be used, use MongoClient.connect**
+ */
+
+// Allowed parameters
+var legalOptionNames = [
+ 'ha',
+ 'haInterval',
+ 'acceptableLatencyMS',
+ 'poolSize',
+ 'ssl',
+ 'checkServerIdentity',
+ 'sslValidate',
+ 'sslCA',
+ 'sslCRL',
+ 'sslCert',
+ 'ciphers',
+ 'ecdhCurve',
+ 'sslKey',
+ 'sslPass',
+ 'socketOptions',
+ 'bufferMaxEntries',
+ 'store',
+ 'auto_reconnect',
+ 'autoReconnect',
+ 'emitError',
+ 'keepAlive',
+ 'keepAliveInitialDelay',
+ 'noDelay',
+ 'connectTimeoutMS',
+ 'socketTimeoutMS',
+ 'loggerLevel',
+ 'logger',
+ 'reconnectTries',
+ 'appname',
+ 'domainsEnabled',
+ 'servername',
+ 'promoteLongs',
+ 'promoteValues',
+ 'promoteBuffers',
+ 'promiseLibrary',
+ 'monitorCommands'
+];
+
+/**
+ * Creates a new Mongos instance
+ * @class
+ * @deprecated
+ * @param {Server[]} servers A seedlist of servers participating in the replicaset.
+ * @param {object} [options] Optional settings.
+ * @param {booelan} [options.ha=true] Turn on high availability monitoring.
+ * @param {number} [options.haInterval=5000] Time between each replicaset status check.
+ * @param {number} [options.poolSize=5] Number of connections in the connection pool for each server instance, set to 5 as default for legacy reasons.
+ * @param {number} [options.acceptableLatencyMS=15] Cutoff latency point in MS for MongoS proxy selection
+ * @param {boolean} [options.ssl=false] Use ssl connection (needs to have a mongod server with ssl support)
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
+ * @param {boolean} [options.sslValidate=true] Validate mongod server certificate against ca (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {array} [options.sslCA] Array of valid certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {array} [options.sslCRL] Array of revocation certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {string} [options.ciphers] Passed directly through to tls.createSecureContext. See https://nodejs.org/dist/latest-v9.x/docs/api/tls.html#tls_tls_createsecurecontext_options for more info.
+ * @param {string} [options.ecdhCurve] Passed directly through to tls.createSecureContext. See https://nodejs.org/dist/latest-v9.x/docs/api/tls.html#tls_tls_createsecurecontext_options for more info.
+ * @param {(Buffer|string)} [options.sslCert] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {(Buffer|string)} [options.sslKey] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {(Buffer|string)} [options.sslPass] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {string} [options.servername] String containing the server name requested via TLS SNI.
+ * @param {object} [options.socketOptions] Socket options
+ * @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
+ * @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
+ * @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
+ * @param {number} [options.socketOptions.connectTimeoutMS=0] TCP Connection timeout setting
+ * @param {number} [options.socketOptions.socketTimeoutMS=0] TCP Socket timeout setting
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
+ * @param {boolean} [options.monitorCommands=false] Enable command monitoring for this topology
+ * @fires Mongos#connect
+ * @fires Mongos#ha
+ * @fires Mongos#joined
+ * @fires Mongos#left
+ * @fires Mongos#fullsetup
+ * @fires Mongos#open
+ * @fires Mongos#close
+ * @fires Mongos#error
+ * @fires Mongos#timeout
+ * @fires Mongos#parseError
+ * @fires Mongos#commandStarted
+ * @fires Mongos#commandSucceeded
+ * @fires Mongos#commandFailed
+ * @property {string} parserType the parser type used (c++ or js).
+ * @return {Mongos} a Mongos instance.
+ */
+class Mongos extends TopologyBase {
+ constructor(servers, options) {
+ super();
+
+ options = options || {};
+ var self = this;
+
+ // Filter the options
+ options = filterOptions(options, legalOptionNames);
+
+ // Ensure all the instances are Server
+ for (var i = 0; i < servers.length; i++) {
+ if (!(servers[i] instanceof Server)) {
+ throw MongoError.create({
+ message: 'all seed list instances must be of the Server type',
+ driver: true
+ });
+ }
+ }
+
+ // Stored options
+ var storeOptions = {
+ force: false,
+ bufferMaxEntries:
+ typeof options.bufferMaxEntries === 'number' ? options.bufferMaxEntries : MAX_JS_INT
+ };
+
+ // Shared global store
+ var store = options.store || new Store(self, storeOptions);
+
+ // Build seed list
+ var seedlist = servers.map(function(x) {
+ return { host: x.host, port: x.port };
+ });
+
+ // Get the reconnect option
+ var reconnect = typeof options.auto_reconnect === 'boolean' ? options.auto_reconnect : true;
+ reconnect = typeof options.autoReconnect === 'boolean' ? options.autoReconnect : reconnect;
+
+ // Clone options
+ var clonedOptions = mergeOptions(
+ {},
+ {
+ disconnectHandler: store,
+ cursorFactory: Cursor,
+ reconnect: reconnect,
+ emitError: typeof options.emitError === 'boolean' ? options.emitError : true,
+ size: typeof options.poolSize === 'number' ? options.poolSize : 5,
+ monitorCommands:
+ typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false
+ }
+ );
+
+ // Translate any SSL options and other connectivity options
+ clonedOptions = translateOptions(clonedOptions, options);
+
+ // Socket options
+ var socketOptions =
+ options.socketOptions && Object.keys(options.socketOptions).length > 0
+ ? options.socketOptions
+ : options;
+
+ // Translate all the options to the mongodb-core ones
+ clonedOptions = translateOptions(clonedOptions, socketOptions);
+
+ // Build default client information
+ clonedOptions.clientInfo = this.clientInfo;
+ // Do we have an application specific string
+ if (options.appname) {
+ clonedOptions.clientInfo.application = { name: options.appname };
+ }
+
+ // Internal state
+ this.s = {
+ // Create the Mongos
+ coreTopology: new CMongos(seedlist, clonedOptions),
+ // Server capabilities
+ sCapabilities: null,
+ // Debug turned on
+ debug: clonedOptions.debug,
+ // Store option defaults
+ storeOptions: storeOptions,
+ // Cloned options
+ clonedOptions: clonedOptions,
+ // Actual store of callbacks
+ store: store,
+ // Options
+ options: options,
+ // Server Session Pool
+ sessionPool: null,
+ // Active client sessions
+ sessions: [],
+ // Promise library
+ promiseLibrary: options.promiseLibrary || Promise
+ };
+ }
+
+ // Connect
+ connect(_options, callback) {
+ var self = this;
+ if ('function' === typeof _options) (callback = _options), (_options = {});
+ if (_options == null) _options = {};
+ if (!('function' === typeof callback)) callback = null;
+ _options = Object.assign({}, this.s.clonedOptions, _options);
+ self.s.options = _options;
+
+ // Update bufferMaxEntries
+ self.s.storeOptions.bufferMaxEntries =
+ typeof _options.bufferMaxEntries === 'number' ? _options.bufferMaxEntries : -1;
+
+ // Error handler
+ var connectErrorHandler = function() {
+ return function(err) {
+ // Remove all event handlers
+ var events = ['timeout', 'error', 'close'];
+ events.forEach(function(e) {
+ self.removeListener(e, connectErrorHandler);
+ });
+
+ self.s.coreTopology.removeListener('connect', connectErrorHandler);
+ // Force close the topology
+ self.close(true);
+
+ // Try to callback
+ try {
+ callback(err);
+ } catch (err) {
+ process.nextTick(function() {
+ throw err;
+ });
+ }
+ };
+ };
+
+ // Actual handler
+ var errorHandler = function(event) {
+ return function(err) {
+ if (event !== 'error') {
+ self.emit(event, err);
+ }
+ };
+ };
+
+ // Error handler
+ var reconnectHandler = function() {
+ self.emit('reconnect');
+ self.s.store.execute();
+ };
+
+ // relay the event
+ var relay = function(event) {
+ return function(t, server) {
+ self.emit(event, t, server);
+ };
+ };
+
+ // Connect handler
+ var connectHandler = function() {
+ // Clear out all the current handlers left over
+ var events = ['timeout', 'error', 'close', 'fullsetup'];
+ events.forEach(function(e) {
+ self.s.coreTopology.removeAllListeners(e);
+ });
+
+ // Set up listeners
+ self.s.coreTopology.on('timeout', errorHandler('timeout'));
+ self.s.coreTopology.on('error', errorHandler('error'));
+ self.s.coreTopology.on('close', errorHandler('close'));
+
+ // Set up serverConfig listeners
+ self.s.coreTopology.on('fullsetup', function() {
+ self.emit('fullsetup', self);
+ });
+
+ // Emit open event
+ self.emit('open', null, self);
+
+ // Return correctly
+ try {
+ callback(null, self);
+ } catch (err) {
+ process.nextTick(function() {
+ throw err;
+ });
+ }
+ };
+
+ // Clear out all the current handlers left over
+ var events = [
+ 'timeout',
+ 'error',
+ 'close',
+ 'serverOpening',
+ 'serverDescriptionChanged',
+ 'serverHeartbeatStarted',
+ 'serverHeartbeatSucceeded',
+ 'serverHeartbeatFailed',
+ 'serverClosed',
+ 'topologyOpening',
+ 'topologyClosed',
+ 'topologyDescriptionChanged',
+ 'commandStarted',
+ 'commandSucceeded',
+ 'commandFailed'
+ ];
+ events.forEach(function(e) {
+ self.s.coreTopology.removeAllListeners(e);
+ });
+
+ // Set up SDAM listeners
+ self.s.coreTopology.on('serverDescriptionChanged', relay('serverDescriptionChanged'));
+ self.s.coreTopology.on('serverHeartbeatStarted', relay('serverHeartbeatStarted'));
+ self.s.coreTopology.on('serverHeartbeatSucceeded', relay('serverHeartbeatSucceeded'));
+ self.s.coreTopology.on('serverHeartbeatFailed', relay('serverHeartbeatFailed'));
+ self.s.coreTopology.on('serverOpening', relay('serverOpening'));
+ self.s.coreTopology.on('serverClosed', relay('serverClosed'));
+ self.s.coreTopology.on('topologyOpening', relay('topologyOpening'));
+ self.s.coreTopology.on('topologyClosed', relay('topologyClosed'));
+ self.s.coreTopology.on('topologyDescriptionChanged', relay('topologyDescriptionChanged'));
+ self.s.coreTopology.on('commandStarted', relay('commandStarted'));
+ self.s.coreTopology.on('commandSucceeded', relay('commandSucceeded'));
+ self.s.coreTopology.on('commandFailed', relay('commandFailed'));
+
+ // Set up listeners
+ self.s.coreTopology.once('timeout', connectErrorHandler('timeout'));
+ self.s.coreTopology.once('error', connectErrorHandler('error'));
+ self.s.coreTopology.once('close', connectErrorHandler('close'));
+ self.s.coreTopology.once('connect', connectHandler);
+ // Join and leave events
+ self.s.coreTopology.on('joined', relay('joined'));
+ self.s.coreTopology.on('left', relay('left'));
+
+ // Reconnect server
+ self.s.coreTopology.on('reconnect', reconnectHandler);
+
+ // Start connection
+ self.s.coreTopology.connect(_options);
+ }
+}
+
+Object.defineProperty(Mongos.prototype, 'haInterval', {
+ enumerable: true,
+ get: function() {
+ return this.s.coreTopology.s.haInterval;
+ }
+});
+
+/**
+ * A mongos connect event, used to verify that the connection is up and running
+ *
+ * @event Mongos#connect
+ * @type {Mongos}
+ */
+
+/**
+ * The mongos high availability event
+ *
+ * @event Mongos#ha
+ * @type {function}
+ * @param {string} type The stage in the high availability event (start|end)
+ * @param {boolean} data.norepeat This is a repeating high availability process or a single execution only
+ * @param {number} data.id The id for this high availability request
+ * @param {object} data.state An object containing the information about the current replicaset
+ */
+
+/**
+ * A server member left the mongos set
+ *
+ * @event Mongos#left
+ * @type {function}
+ * @param {string} type The type of member that left (primary|secondary|arbiter)
+ * @param {Server} server The server object that left
+ */
+
+/**
+ * A server member joined the mongos set
+ *
+ * @event Mongos#joined
+ * @type {function}
+ * @param {string} type The type of member that joined (primary|secondary|arbiter)
+ * @param {Server} server The server object that joined
+ */
+
+/**
+ * Mongos fullsetup event, emitted when all proxies in the topology have been connected to.
+ *
+ * @event Mongos#fullsetup
+ * @type {Mongos}
+ */
+
+/**
+ * Mongos open event, emitted when mongos can start processing commands.
+ *
+ * @event Mongos#open
+ * @type {Mongos}
+ */
+
+/**
+ * Mongos close event
+ *
+ * @event Mongos#close
+ * @type {object}
+ */
+
+/**
+ * Mongos error event, emitted if there is an error listener.
+ *
+ * @event Mongos#error
+ * @type {MongoError}
+ */
+
+/**
+ * Mongos timeout event
+ *
+ * @event Mongos#timeout
+ * @type {object}
+ */
+
+/**
+ * Mongos parseError event
+ *
+ * @event Mongos#parseError
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command was started, if command monitoring is enabled
+ *
+ * @event Mongos#commandStarted
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command succeeded, if command monitoring is enabled
+ *
+ * @event Mongos#commandSucceeded
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command failed, if command monitoring is enabled
+ *
+ * @event Mongos#commandFailed
+ * @type {object}
+ */
+
+module.exports = Mongos;
diff --git a/node_modules/mongodb/lib/topologies/replset.js b/node_modules/mongodb/lib/topologies/replset.js
new file mode 100644
index 0000000..0a73134
--- /dev/null
+++ b/node_modules/mongodb/lib/topologies/replset.js
@@ -0,0 +1,497 @@
+'use strict';
+
+const Server = require('./server');
+const Cursor = require('../cursor');
+const MongoError = require('mongodb-core').MongoError;
+const TopologyBase = require('./topology_base').TopologyBase;
+const Store = require('./topology_base').Store;
+const CReplSet = require('mongodb-core').ReplSet;
+const MAX_JS_INT = require('../utils').MAX_JS_INT;
+const translateOptions = require('../utils').translateOptions;
+const filterOptions = require('../utils').filterOptions;
+const mergeOptions = require('../utils').mergeOptions;
+
+/**
+ * @fileOverview The **ReplSet** class is a class that represents a Replicaset topology and is
+ * used to construct connections.
+ *
+ * **ReplSet Should not be used, use MongoClient.connect**
+ */
+
+// Allowed parameters
+var legalOptionNames = [
+ 'ha',
+ 'haInterval',
+ 'replicaSet',
+ 'rs_name',
+ 'secondaryAcceptableLatencyMS',
+ 'connectWithNoPrimary',
+ 'poolSize',
+ 'ssl',
+ 'checkServerIdentity',
+ 'sslValidate',
+ 'sslCA',
+ 'sslCert',
+ 'ciphers',
+ 'ecdhCurve',
+ 'sslCRL',
+ 'sslKey',
+ 'sslPass',
+ 'socketOptions',
+ 'bufferMaxEntries',
+ 'store',
+ 'auto_reconnect',
+ 'autoReconnect',
+ 'emitError',
+ 'keepAlive',
+ 'keepAliveInitialDelay',
+ 'noDelay',
+ 'connectTimeoutMS',
+ 'socketTimeoutMS',
+ 'strategy',
+ 'debug',
+ 'family',
+ 'loggerLevel',
+ 'logger',
+ 'reconnectTries',
+ 'appname',
+ 'domainsEnabled',
+ 'servername',
+ 'promoteLongs',
+ 'promoteValues',
+ 'promoteBuffers',
+ 'maxStalenessSeconds',
+ 'promiseLibrary',
+ 'minSize',
+ 'monitorCommands'
+];
+
+/**
+ * Creates a new ReplSet instance
+ * @class
+ * @deprecated
+ * @param {Server[]} servers A seedlist of servers participating in the replicaset.
+ * @param {object} [options] Optional settings.
+ * @param {boolean} [options.ha=true] Turn on high availability monitoring.
+ * @param {number} [options.haInterval=10000] Time between each replicaset status check.
+ * @param {string} [options.replicaSet] The name of the replicaset to connect to.
+ * @param {number} [options.secondaryAcceptableLatencyMS=15] Sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms)
+ * @param {boolean} [options.connectWithNoPrimary=false] Sets if the driver should connect even if no primary is available
+ * @param {number} [options.poolSize=5] Number of connections in the connection pool for each server instance, set to 5 as default for legacy reasons.
+ * @param {boolean} [options.ssl=false] Use ssl connection (needs to have a mongod server with ssl support)
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
+ * @param {boolean} [options.sslValidate=true] Validate mongod server certificate against ca (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {array} [options.sslCA] Array of valid certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {array} [options.sslCRL] Array of revocation certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {(Buffer|string)} [options.sslCert] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher.
+ * @param {string} [options.ciphers] Passed directly through to tls.createSecureContext. See https://nodejs.org/dist/latest-v9.x/docs/api/tls.html#tls_tls_createsecurecontext_options for more info.
+ * @param {string} [options.ecdhCurve] Passed directly through to tls.createSecureContext. See https://nodejs.org/dist/latest-v9.x/docs/api/tls.html#tls_tls_createsecurecontext_options for more info.
+ * @param {(Buffer|string)} [options.sslKey] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {(Buffer|string)} [options.sslPass] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {string} [options.servername] String containing the server name requested via TLS SNI.
+ * @param {object} [options.socketOptions] Socket options
+ * @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
+ * @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
+ * @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
+ * @param {number} [options.socketOptions.connectTimeoutMS=10000] TCP Connection timeout setting
+ * @param {number} [options.socketOptions.socketTimeoutMS=0] TCP Socket timeout setting
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
+ * @param {number} [options.maxStalenessSeconds=undefined] The max staleness to secondary reads (values under 10 seconds cannot be guaranteed);
+ * @param {boolean} [options.monitorCommands=false] Enable command monitoring for this topology
+ * @fires ReplSet#connect
+ * @fires ReplSet#ha
+ * @fires ReplSet#joined
+ * @fires ReplSet#left
+ * @fires ReplSet#fullsetup
+ * @fires ReplSet#open
+ * @fires ReplSet#close
+ * @fires ReplSet#error
+ * @fires ReplSet#timeout
+ * @fires ReplSet#parseError
+ * @fires ReplSet#commandStarted
+ * @fires ReplSet#commandSucceeded
+ * @fires ReplSet#commandFailed
+ * @property {string} parserType the parser type used (c++ or js).
+ * @return {ReplSet} a ReplSet instance.
+ */
+class ReplSet extends TopologyBase {
+ constructor(servers, options) {
+ super();
+
+ options = options || {};
+ var self = this;
+
+ // Filter the options
+ options = filterOptions(options, legalOptionNames);
+
+ // Ensure all the instances are Server
+ for (var i = 0; i < servers.length; i++) {
+ if (!(servers[i] instanceof Server)) {
+ throw MongoError.create({
+ message: 'all seed list instances must be of the Server type',
+ driver: true
+ });
+ }
+ }
+
+ // Stored options
+ var storeOptions = {
+ force: false,
+ bufferMaxEntries:
+ typeof options.bufferMaxEntries === 'number' ? options.bufferMaxEntries : MAX_JS_INT
+ };
+
+ // Shared global store
+ var store = options.store || new Store(self, storeOptions);
+
+ // Build seed list
+ var seedlist = servers.map(function(x) {
+ return { host: x.host, port: x.port };
+ });
+
+ // Clone options
+ var clonedOptions = mergeOptions(
+ {},
+ {
+ disconnectHandler: store,
+ cursorFactory: Cursor,
+ reconnect: false,
+ emitError: typeof options.emitError === 'boolean' ? options.emitError : true,
+ size: typeof options.poolSize === 'number' ? options.poolSize : 5,
+ monitorCommands:
+ typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false
+ }
+ );
+
+ // Translate any SSL options and other connectivity options
+ clonedOptions = translateOptions(clonedOptions, options);
+
+ // Socket options
+ var socketOptions =
+ options.socketOptions && Object.keys(options.socketOptions).length > 0
+ ? options.socketOptions
+ : options;
+
+ // Translate all the options to the mongodb-core ones
+ clonedOptions = translateOptions(clonedOptions, socketOptions);
+
+ // Build default client information
+ clonedOptions.clientInfo = this.clientInfo;
+ // Do we have an application specific string
+ if (options.appname) {
+ clonedOptions.clientInfo.application = { name: options.appname };
+ }
+
+ // Create the ReplSet
+ var coreTopology = new CReplSet(seedlist, clonedOptions);
+
+ // Listen to reconnect event
+ coreTopology.on('reconnect', function() {
+ self.emit('reconnect');
+ store.execute();
+ });
+
+ // Internal state
+ this.s = {
+ // Replicaset
+ coreTopology: coreTopology,
+ // Server capabilities
+ sCapabilities: null,
+ // Debug tag
+ tag: options.tag,
+ // Store options
+ storeOptions: storeOptions,
+ // Cloned options
+ clonedOptions: clonedOptions,
+ // Store
+ store: store,
+ // Options
+ options: options,
+ // Server Session Pool
+ sessionPool: null,
+ // Active client sessions
+ sessions: [],
+ // Promise library
+ promiseLibrary: options.promiseLibrary || Promise
+ };
+
+ // Debug
+ if (clonedOptions.debug) {
+ // Last ismaster
+ Object.defineProperty(this, 'replset', {
+ enumerable: true,
+ get: function() {
+ return coreTopology;
+ }
+ });
+ }
+ }
+
+ // Connect method
+ connect(_options, callback) {
+ var self = this;
+ if ('function' === typeof _options) (callback = _options), (_options = {});
+ if (_options == null) _options = {};
+ if (!('function' === typeof callback)) callback = null;
+ _options = Object.assign({}, this.s.clonedOptions, _options);
+ self.s.options = _options;
+
+ // Update bufferMaxEntries
+ self.s.storeOptions.bufferMaxEntries =
+ typeof _options.bufferMaxEntries === 'number' ? _options.bufferMaxEntries : -1;
+
+ // Actual handler
+ var errorHandler = function(event) {
+ return function(err) {
+ if (event !== 'error') {
+ self.emit(event, err);
+ }
+ };
+ };
+
+ // Clear out all the current handlers left over
+ var events = [
+ 'timeout',
+ 'error',
+ 'close',
+ 'serverOpening',
+ 'serverDescriptionChanged',
+ 'serverHeartbeatStarted',
+ 'serverHeartbeatSucceeded',
+ 'serverHeartbeatFailed',
+ 'serverClosed',
+ 'topologyOpening',
+ 'topologyClosed',
+ 'topologyDescriptionChanged',
+ 'commandStarted',
+ 'commandSucceeded',
+ 'commandFailed',
+ 'joined',
+ 'left',
+ 'ping',
+ 'ha'
+ ];
+ events.forEach(function(e) {
+ self.s.coreTopology.removeAllListeners(e);
+ });
+
+ // relay the event
+ var relay = function(event) {
+ return function(t, server) {
+ self.emit(event, t, server);
+ };
+ };
+
+ // Replset events relay
+ var replsetRelay = function(event) {
+ return function(t, server) {
+ self.emit(event, t, server.lastIsMaster(), server);
+ };
+ };
+
+ // Relay ha
+ var relayHa = function(t, state) {
+ self.emit('ha', t, state);
+
+ if (t === 'start') {
+ self.emit('ha_connect', t, state);
+ } else if (t === 'end') {
+ self.emit('ha_ismaster', t, state);
+ }
+ };
+
+ // Set up serverConfig listeners
+ self.s.coreTopology.on('joined', replsetRelay('joined'));
+ self.s.coreTopology.on('left', relay('left'));
+ self.s.coreTopology.on('ping', relay('ping'));
+ self.s.coreTopology.on('ha', relayHa);
+
+ // Set up SDAM listeners
+ self.s.coreTopology.on('serverDescriptionChanged', relay('serverDescriptionChanged'));
+ self.s.coreTopology.on('serverHeartbeatStarted', relay('serverHeartbeatStarted'));
+ self.s.coreTopology.on('serverHeartbeatSucceeded', relay('serverHeartbeatSucceeded'));
+ self.s.coreTopology.on('serverHeartbeatFailed', relay('serverHeartbeatFailed'));
+ self.s.coreTopology.on('serverOpening', relay('serverOpening'));
+ self.s.coreTopology.on('serverClosed', relay('serverClosed'));
+ self.s.coreTopology.on('topologyOpening', relay('topologyOpening'));
+ self.s.coreTopology.on('topologyClosed', relay('topologyClosed'));
+ self.s.coreTopology.on('topologyDescriptionChanged', relay('topologyDescriptionChanged'));
+ self.s.coreTopology.on('commandStarted', relay('commandStarted'));
+ self.s.coreTopology.on('commandSucceeded', relay('commandSucceeded'));
+ self.s.coreTopology.on('commandFailed', relay('commandFailed'));
+
+ self.s.coreTopology.on('fullsetup', function() {
+ self.emit('fullsetup', self, self);
+ });
+
+ self.s.coreTopology.on('all', function() {
+ self.emit('all', null, self);
+ });
+
+ // Connect handler
+ var connectHandler = function() {
+ // Set up listeners
+ self.s.coreTopology.once('timeout', errorHandler('timeout'));
+ self.s.coreTopology.once('error', errorHandler('error'));
+ self.s.coreTopology.once('close', errorHandler('close'));
+
+ // Emit open event
+ self.emit('open', null, self);
+
+ // Return correctly
+ try {
+ callback(null, self);
+ } catch (err) {
+ process.nextTick(function() {
+ throw err;
+ });
+ }
+ };
+
+ // Error handler
+ var connectErrorHandler = function() {
+ return function(err) {
+ ['timeout', 'error', 'close'].forEach(function(e) {
+ self.s.coreTopology.removeListener(e, connectErrorHandler);
+ });
+
+ self.s.coreTopology.removeListener('connect', connectErrorHandler);
+ // Destroy the replset
+ self.s.coreTopology.destroy();
+
+ // Try to callback
+ try {
+ callback(err);
+ } catch (err) {
+ if (!self.s.coreTopology.isConnected())
+ process.nextTick(function() {
+ throw err;
+ });
+ }
+ };
+ };
+
+ // Set up listeners
+ self.s.coreTopology.once('timeout', connectErrorHandler('timeout'));
+ self.s.coreTopology.once('error', connectErrorHandler('error'));
+ self.s.coreTopology.once('close', connectErrorHandler('close'));
+ self.s.coreTopology.once('connect', connectHandler);
+
+ // Start connection
+ self.s.coreTopology.connect(_options);
+ }
+
+ close(forceClosed) {
+ super.close(forceClosed);
+
+ ['timeout', 'error', 'close', 'joined', 'left'].forEach(e => this.removeAllListeners(e));
+ }
+}
+
+Object.defineProperty(ReplSet.prototype, 'haInterval', {
+ enumerable: true,
+ get: function() {
+ return this.s.coreTopology.s.haInterval;
+ }
+});
+
+/**
+ * A replset connect event, used to verify that the connection is up and running
+ *
+ * @event ReplSet#connect
+ * @type {ReplSet}
+ */
+
+/**
+ * The replset high availability event
+ *
+ * @event ReplSet#ha
+ * @type {function}
+ * @param {string} type The stage in the high availability event (start|end)
+ * @param {boolean} data.norepeat This is a repeating high availability process or a single execution only
+ * @param {number} data.id The id for this high availability request
+ * @param {object} data.state An object containing the information about the current replicaset
+ */
+
+/**
+ * A server member left the replicaset
+ *
+ * @event ReplSet#left
+ * @type {function}
+ * @param {string} type The type of member that left (primary|secondary|arbiter)
+ * @param {Server} server The server object that left
+ */
+
+/**
+ * A server member joined the replicaset
+ *
+ * @event ReplSet#joined
+ * @type {function}
+ * @param {string} type The type of member that joined (primary|secondary|arbiter)
+ * @param {Server} server The server object that joined
+ */
+
+/**
+ * ReplSet open event, emitted when replicaset can start processing commands.
+ *
+ * @event ReplSet#open
+ * @type {Replset}
+ */
+
+/**
+ * ReplSet fullsetup event, emitted when all servers in the topology have been connected to.
+ *
+ * @event ReplSet#fullsetup
+ * @type {Replset}
+ */
+
+/**
+ * ReplSet close event
+ *
+ * @event ReplSet#close
+ * @type {object}
+ */
+
+/**
+ * ReplSet error event, emitted if there is an error listener.
+ *
+ * @event ReplSet#error
+ * @type {MongoError}
+ */
+
+/**
+ * ReplSet timeout event
+ *
+ * @event ReplSet#timeout
+ * @type {object}
+ */
+
+/**
+ * ReplSet parseError event
+ *
+ * @event ReplSet#parseError
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command was started, if command monitoring is enabled
+ *
+ * @event ReplSet#commandStarted
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command succeeded, if command monitoring is enabled
+ *
+ * @event ReplSet#commandSucceeded
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command failed, if command monitoring is enabled
+ *
+ * @event ReplSet#commandFailed
+ * @type {object}
+ */
+
+module.exports = ReplSet;
diff --git a/node_modules/mongodb/lib/topologies/server.js b/node_modules/mongodb/lib/topologies/server.js
new file mode 100644
index 0000000..77d8b6e
--- /dev/null
+++ b/node_modules/mongodb/lib/topologies/server.js
@@ -0,0 +1,455 @@
+'use strict';
+
+const CServer = require('mongodb-core').Server;
+const Cursor = require('../cursor');
+const TopologyBase = require('./topology_base').TopologyBase;
+const Store = require('./topology_base').Store;
+const MongoError = require('mongodb-core').MongoError;
+const MAX_JS_INT = require('../utils').MAX_JS_INT;
+const translateOptions = require('../utils').translateOptions;
+const filterOptions = require('../utils').filterOptions;
+const mergeOptions = require('../utils').mergeOptions;
+
+/**
+ * @fileOverview The **Server** class is a class that represents a single server topology and is
+ * used to construct connections.
+ *
+ * **Server Should not be used, use MongoClient.connect**
+ */
+
+// Allowed parameters
+var legalOptionNames = [
+ 'ha',
+ 'haInterval',
+ 'acceptableLatencyMS',
+ 'poolSize',
+ 'ssl',
+ 'checkServerIdentity',
+ 'sslValidate',
+ 'sslCA',
+ 'sslCRL',
+ 'sslCert',
+ 'ciphers',
+ 'ecdhCurve',
+ 'sslKey',
+ 'sslPass',
+ 'socketOptions',
+ 'bufferMaxEntries',
+ 'store',
+ 'auto_reconnect',
+ 'autoReconnect',
+ 'emitError',
+ 'keepAlive',
+ 'keepAliveInitialDelay',
+ 'noDelay',
+ 'connectTimeoutMS',
+ 'socketTimeoutMS',
+ 'family',
+ 'loggerLevel',
+ 'logger',
+ 'reconnectTries',
+ 'reconnectInterval',
+ 'monitoring',
+ 'appname',
+ 'domainsEnabled',
+ 'servername',
+ 'promoteLongs',
+ 'promoteValues',
+ 'promoteBuffers',
+ 'compression',
+ 'promiseLibrary',
+ 'monitorCommands'
+];
+
+/**
+ * Creates a new Server instance
+ * @class
+ * @deprecated
+ * @param {string} host The host for the server, can be either an IP4, IP6 or domain socket style host.
+ * @param {number} [port] The server port if IP4.
+ * @param {object} [options] Optional settings.
+ * @param {number} [options.poolSize=5] Number of connections in the connection pool for each server instance, set to 5 as default for legacy reasons.
+ * @param {boolean} [options.ssl=false] Use ssl connection (needs to have a mongod server with ssl support)
+ * @param {boolean} [options.sslValidate=true] Validate mongod server certificate against ca (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
+ * @param {array} [options.sslCA] Array of valid certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {array} [options.sslCRL] Array of revocation certificates either as Buffers or Strings (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {(Buffer|string)} [options.sslCert] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {string} [options.ciphers] Passed directly through to tls.createSecureContext. See https://nodejs.org/dist/latest-v9.x/docs/api/tls.html#tls_tls_createsecurecontext_options for more info.
+ * @param {string} [options.ecdhCurve] Passed directly through to tls.createSecureContext. See https://nodejs.org/dist/latest-v9.x/docs/api/tls.html#tls_tls_createsecurecontext_options for more info.
+ * @param {(Buffer|string)} [options.sslKey] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {(Buffer|string)} [options.sslPass] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
+ * @param {string} [options.servername] String containing the server name requested via TLS SNI.
+ * @param {object} [options.socketOptions] Socket options
+ * @param {boolean} [options.socketOptions.autoReconnect=true] Reconnect on error.
+ * @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
+ * @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
+ * @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
+ * @param {number} [options.socketOptions.connectTimeoutMS=0] TCP Connection timeout setting
+ * @param {number} [options.socketOptions.socketTimeoutMS=0] TCP Socket timeout setting
+ * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
+ * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
+ * @param {number} [options.monitoring=true] Triggers the server instance to call ismaster
+ * @param {number} [options.haInterval=10000] The interval of calling ismaster when monitoring is enabled.
+ * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
+ * @param {boolean} [options.monitorCommands=false] Enable command monitoring for this topology
+ * @fires Server#connect
+ * @fires Server#close
+ * @fires Server#error
+ * @fires Server#timeout
+ * @fires Server#parseError
+ * @fires Server#reconnect
+ * @fires Server#commandStarted
+ * @fires Server#commandSucceeded
+ * @fires Server#commandFailed
+ * @property {string} parserType the parser type used (c++ or js).
+ * @return {Server} a Server instance.
+ */
+class Server extends TopologyBase {
+ constructor(host, port, options) {
+ super();
+ var self = this;
+
+ // Filter the options
+ options = filterOptions(options, legalOptionNames);
+
+ // Promise library
+ const promiseLibrary = options.promiseLibrary;
+
+ // Stored options
+ var storeOptions = {
+ force: false,
+ bufferMaxEntries:
+ typeof options.bufferMaxEntries === 'number' ? options.bufferMaxEntries : MAX_JS_INT
+ };
+
+ // Shared global store
+ var store = options.store || new Store(self, storeOptions);
+
+ // Detect if we have a socket connection
+ if (host.indexOf('/') !== -1) {
+ if (port != null && typeof port === 'object') {
+ options = port;
+ port = null;
+ }
+ } else if (port == null) {
+ throw MongoError.create({ message: 'port must be specified', driver: true });
+ }
+
+ // Get the reconnect option
+ var reconnect = typeof options.auto_reconnect === 'boolean' ? options.auto_reconnect : true;
+ reconnect = typeof options.autoReconnect === 'boolean' ? options.autoReconnect : reconnect;
+
+ // Clone options
+ var clonedOptions = mergeOptions(
+ {},
+ {
+ host: host,
+ port: port,
+ disconnectHandler: store,
+ cursorFactory: Cursor,
+ reconnect: reconnect,
+ emitError: typeof options.emitError === 'boolean' ? options.emitError : true,
+ size: typeof options.poolSize === 'number' ? options.poolSize : 5,
+ monitorCommands:
+ typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false
+ }
+ );
+
+ // Translate any SSL options and other connectivity options
+ clonedOptions = translateOptions(clonedOptions, options);
+
+ // Socket options
+ var socketOptions =
+ options.socketOptions && Object.keys(options.socketOptions).length > 0
+ ? options.socketOptions
+ : options;
+
+ // Translate all the options to the mongodb-core ones
+ clonedOptions = translateOptions(clonedOptions, socketOptions);
+
+ // Build default client information
+ clonedOptions.clientInfo = this.clientInfo;
+ // Do we have an application specific string
+ if (options.appname) {
+ clonedOptions.clientInfo.application = { name: options.appname };
+ }
+
+ // Define the internal properties
+ this.s = {
+ // Create an instance of a server instance from mongodb-core
+ coreTopology: new CServer(clonedOptions),
+ // Server capabilities
+ sCapabilities: null,
+ // Cloned options
+ clonedOptions: clonedOptions,
+ // Reconnect
+ reconnect: clonedOptions.reconnect,
+ // Emit error
+ emitError: clonedOptions.emitError,
+ // Pool size
+ poolSize: clonedOptions.size,
+ // Store Options
+ storeOptions: storeOptions,
+ // Store
+ store: store,
+ // Host
+ host: host,
+ // Port
+ port: port,
+ // Options
+ options: options,
+ // Server Session Pool
+ sessionPool: null,
+ // Active client sessions
+ sessions: [],
+ // Promise library
+ promiseLibrary: promiseLibrary || Promise
+ };
+ }
+
+ // Connect
+ connect(_options, callback) {
+ var self = this;
+ if ('function' === typeof _options) (callback = _options), (_options = {});
+ if (_options == null) _options = this.s.clonedOptions;
+ if (!('function' === typeof callback)) callback = null;
+ _options = Object.assign({}, this.s.clonedOptions, _options);
+ self.s.options = _options;
+
+ // Update bufferMaxEntries
+ self.s.storeOptions.bufferMaxEntries =
+ typeof _options.bufferMaxEntries === 'number' ? _options.bufferMaxEntries : -1;
+
+ // Error handler
+ var connectErrorHandler = function() {
+ return function(err) {
+ // Remove all event handlers
+ var events = ['timeout', 'error', 'close'];
+ events.forEach(function(e) {
+ self.s.coreTopology.removeListener(e, connectHandlers[e]);
+ });
+
+ self.s.coreTopology.removeListener('connect', connectErrorHandler);
+
+ // Try to callback
+ try {
+ callback(err);
+ } catch (err) {
+ process.nextTick(function() {
+ throw err;
+ });
+ }
+ };
+ };
+
+ // Actual handler
+ var errorHandler = function(event) {
+ return function(err) {
+ if (event !== 'error') {
+ self.emit(event, err);
+ }
+ };
+ };
+
+ // Error handler
+ var reconnectHandler = function() {
+ self.emit('reconnect', self);
+ self.s.store.execute();
+ };
+
+ // Reconnect failed
+ var reconnectFailedHandler = function(err) {
+ self.emit('reconnectFailed', err);
+ self.s.store.flush(err);
+ };
+
+ // Destroy called on topology, perform cleanup
+ var destroyHandler = function() {
+ self.s.store.flush();
+ };
+
+ // relay the event
+ var relay = function(event) {
+ return function(t, server) {
+ self.emit(event, t, server);
+ };
+ };
+
+ // Connect handler
+ var connectHandler = function() {
+ // Clear out all the current handlers left over
+ ['timeout', 'error', 'close', 'destroy'].forEach(function(e) {
+ self.s.coreTopology.removeAllListeners(e);
+ });
+
+ // Set up listeners
+ self.s.coreTopology.on('timeout', errorHandler('timeout'));
+ self.s.coreTopology.once('error', errorHandler('error'));
+ self.s.coreTopology.on('close', errorHandler('close'));
+ // Only called on destroy
+ self.s.coreTopology.on('destroy', destroyHandler);
+
+ // Emit open event
+ self.emit('open', null, self);
+
+ // Return correctly
+ try {
+ callback(null, self);
+ } catch (err) {
+ process.nextTick(function() {
+ throw err;
+ });
+ }
+ };
+
+ // Set up listeners
+ var connectHandlers = {
+ timeout: connectErrorHandler('timeout'),
+ error: connectErrorHandler('error'),
+ close: connectErrorHandler('close')
+ };
+
+ // Clear out all the current handlers left over
+ [
+ 'timeout',
+ 'error',
+ 'close',
+ 'serverOpening',
+ 'serverDescriptionChanged',
+ 'serverHeartbeatStarted',
+ 'serverHeartbeatSucceeded',
+ 'serverHeartbeatFailed',
+ 'serverClosed',
+ 'topologyOpening',
+ 'topologyClosed',
+ 'topologyDescriptionChanged',
+ 'commandStarted',
+ 'commandSucceeded',
+ 'commandFailed'
+ ].forEach(function(e) {
+ self.s.coreTopology.removeAllListeners(e);
+ });
+
+ // Add the event handlers
+ self.s.coreTopology.once('timeout', connectHandlers.timeout);
+ self.s.coreTopology.once('error', connectHandlers.error);
+ self.s.coreTopology.once('close', connectHandlers.close);
+ self.s.coreTopology.once('connect', connectHandler);
+ // Reconnect server
+ self.s.coreTopology.on('reconnect', reconnectHandler);
+ self.s.coreTopology.on('reconnectFailed', reconnectFailedHandler);
+
+ // Set up SDAM listeners
+ self.s.coreTopology.on('serverDescriptionChanged', relay('serverDescriptionChanged'));
+ self.s.coreTopology.on('serverHeartbeatStarted', relay('serverHeartbeatStarted'));
+ self.s.coreTopology.on('serverHeartbeatSucceeded', relay('serverHeartbeatSucceeded'));
+ self.s.coreTopology.on('serverHeartbeatFailed', relay('serverHeartbeatFailed'));
+ self.s.coreTopology.on('serverOpening', relay('serverOpening'));
+ self.s.coreTopology.on('serverClosed', relay('serverClosed'));
+ self.s.coreTopology.on('topologyOpening', relay('topologyOpening'));
+ self.s.coreTopology.on('topologyClosed', relay('topologyClosed'));
+ self.s.coreTopology.on('topologyDescriptionChanged', relay('topologyDescriptionChanged'));
+ self.s.coreTopology.on('commandStarted', relay('commandStarted'));
+ self.s.coreTopology.on('commandSucceeded', relay('commandSucceeded'));
+ self.s.coreTopology.on('commandFailed', relay('commandFailed'));
+ self.s.coreTopology.on('attemptReconnect', relay('attemptReconnect'));
+ self.s.coreTopology.on('monitoring', relay('monitoring'));
+
+ // Start connection
+ self.s.coreTopology.connect(_options);
+ }
+}
+
+Object.defineProperty(Server.prototype, 'poolSize', {
+ enumerable: true,
+ get: function() {
+ return this.s.coreTopology.connections().length;
+ }
+});
+
+Object.defineProperty(Server.prototype, 'autoReconnect', {
+ enumerable: true,
+ get: function() {
+ return this.s.reconnect;
+ }
+});
+
+Object.defineProperty(Server.prototype, 'host', {
+ enumerable: true,
+ get: function() {
+ return this.s.host;
+ }
+});
+
+Object.defineProperty(Server.prototype, 'port', {
+ enumerable: true,
+ get: function() {
+ return this.s.port;
+ }
+});
+
+/**
+ * Server connect event
+ *
+ * @event Server#connect
+ * @type {object}
+ */
+
+/**
+ * Server close event
+ *
+ * @event Server#close
+ * @type {object}
+ */
+
+/**
+ * Server reconnect event
+ *
+ * @event Server#reconnect
+ * @type {object}
+ */
+
+/**
+ * Server error event
+ *
+ * @event Server#error
+ * @type {MongoError}
+ */
+
+/**
+ * Server timeout event
+ *
+ * @event Server#timeout
+ * @type {object}
+ */
+
+/**
+ * Server parseError event
+ *
+ * @event Server#parseError
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command was started, if command monitoring is enabled
+ *
+ * @event Server#commandStarted
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command succeeded, if command monitoring is enabled
+ *
+ * @event Server#commandSucceeded
+ * @type {object}
+ */
+
+/**
+ * An event emitted indicating a command failed, if command monitoring is enabled
+ *
+ * @event Server#commandFailed
+ * @type {object}
+ */
+
+module.exports = Server;
diff --git a/node_modules/mongodb/lib/topologies/topology_base.js b/node_modules/mongodb/lib/topologies/topology_base.js
new file mode 100644
index 0000000..54f848c
--- /dev/null
+++ b/node_modules/mongodb/lib/topologies/topology_base.js
@@ -0,0 +1,446 @@
+'use strict';
+
+const EventEmitter = require('events'),
+ MongoError = require('mongodb-core').MongoError,
+ f = require('util').format,
+ os = require('os'),
+ translateReadPreference = require('../utils').translateReadPreference,
+ ClientSession = require('mongodb-core').Sessions.ClientSession;
+
+// The store of ops
+var Store = function(topology, storeOptions) {
+ var self = this;
+ var storedOps = [];
+ storeOptions = storeOptions || { force: false, bufferMaxEntries: -1 };
+
+ // Internal state
+ this.s = {
+ storedOps: storedOps,
+ storeOptions: storeOptions,
+ topology: topology
+ };
+
+ Object.defineProperty(this, 'length', {
+ enumerable: true,
+ get: function() {
+ return self.s.storedOps.length;
+ }
+ });
+};
+
+Store.prototype.add = function(opType, ns, ops, options, callback) {
+ if (this.s.storeOptions.force) {
+ return callback(MongoError.create({ message: 'db closed by application', driver: true }));
+ }
+
+ if (this.s.storeOptions.bufferMaxEntries === 0) {
+ return callback(
+ MongoError.create({
+ message: f(
+ 'no connection available for operation and number of stored operation > %s',
+ this.s.storeOptions.bufferMaxEntries
+ ),
+ driver: true
+ })
+ );
+ }
+
+ if (
+ this.s.storeOptions.bufferMaxEntries > 0 &&
+ this.s.storedOps.length > this.s.storeOptions.bufferMaxEntries
+ ) {
+ while (this.s.storedOps.length > 0) {
+ var op = this.s.storedOps.shift();
+ op.c(
+ MongoError.create({
+ message: f(
+ 'no connection available for operation and number of stored operation > %s',
+ this.s.storeOptions.bufferMaxEntries
+ ),
+ driver: true
+ })
+ );
+ }
+
+ return;
+ }
+
+ this.s.storedOps.push({ t: opType, n: ns, o: ops, op: options, c: callback });
+};
+
+Store.prototype.addObjectAndMethod = function(opType, object, method, params, callback) {
+ if (this.s.storeOptions.force) {
+ return callback(MongoError.create({ message: 'db closed by application', driver: true }));
+ }
+
+ if (this.s.storeOptions.bufferMaxEntries === 0) {
+ return callback(
+ MongoError.create({
+ message: f(
+ 'no connection available for operation and number of stored operation > %s',
+ this.s.storeOptions.bufferMaxEntries
+ ),
+ driver: true
+ })
+ );
+ }
+
+ if (
+ this.s.storeOptions.bufferMaxEntries > 0 &&
+ this.s.storedOps.length > this.s.storeOptions.bufferMaxEntries
+ ) {
+ while (this.s.storedOps.length > 0) {
+ var op = this.s.storedOps.shift();
+ op.c(
+ MongoError.create({
+ message: f(
+ 'no connection available for operation and number of stored operation > %s',
+ this.s.storeOptions.bufferMaxEntries
+ ),
+ driver: true
+ })
+ );
+ }
+
+ return;
+ }
+
+ this.s.storedOps.push({ t: opType, m: method, o: object, p: params, c: callback });
+};
+
+Store.prototype.flush = function(err) {
+ while (this.s.storedOps.length > 0) {
+ this.s.storedOps
+ .shift()
+ .c(
+ err ||
+ MongoError.create({ message: f('no connection available for operation'), driver: true })
+ );
+ }
+};
+
+var primaryOptions = ['primary', 'primaryPreferred', 'nearest', 'secondaryPreferred'];
+var secondaryOptions = ['secondary', 'secondaryPreferred'];
+
+Store.prototype.execute = function(options) {
+ options = options || {};
+ // Get current ops
+ var ops = this.s.storedOps;
+ // Reset the ops
+ this.s.storedOps = [];
+
+ // Unpack options
+ var executePrimary = typeof options.executePrimary === 'boolean' ? options.executePrimary : true;
+ var executeSecondary =
+ typeof options.executeSecondary === 'boolean' ? options.executeSecondary : true;
+
+ // Execute all the stored ops
+ while (ops.length > 0) {
+ var op = ops.shift();
+
+ if (op.t === 'cursor') {
+ if (executePrimary && executeSecondary) {
+ op.o[op.m].apply(op.o, op.p);
+ } else if (
+ executePrimary &&
+ op.o.options &&
+ op.o.options.readPreference &&
+ primaryOptions.indexOf(op.o.options.readPreference.mode) !== -1
+ ) {
+ op.o[op.m].apply(op.o, op.p);
+ } else if (
+ !executePrimary &&
+ executeSecondary &&
+ op.o.options &&
+ op.o.options.readPreference &&
+ secondaryOptions.indexOf(op.o.options.readPreference.mode) !== -1
+ ) {
+ op.o[op.m].apply(op.o, op.p);
+ }
+ } else if (op.t === 'auth') {
+ this.s.topology[op.t].apply(this.s.topology, op.o);
+ } else {
+ if (executePrimary && executeSecondary) {
+ this.s.topology[op.t](op.n, op.o, op.op, op.c);
+ } else if (
+ executePrimary &&
+ op.op &&
+ op.op.readPreference &&
+ primaryOptions.indexOf(op.op.readPreference.mode) !== -1
+ ) {
+ this.s.topology[op.t](op.n, op.o, op.op, op.c);
+ } else if (
+ !executePrimary &&
+ executeSecondary &&
+ op.op &&
+ op.op.readPreference &&
+ secondaryOptions.indexOf(op.op.readPreference.mode) !== -1
+ ) {
+ this.s.topology[op.t](op.n, op.o, op.op, op.c);
+ }
+ }
+ }
+};
+
+Store.prototype.all = function() {
+ return this.s.storedOps;
+};
+
+// Server capabilities
+var ServerCapabilities = function(ismaster) {
+ var setup_get_property = function(object, name, value) {
+ Object.defineProperty(object, name, {
+ enumerable: true,
+ get: function() {
+ return value;
+ }
+ });
+ };
+
+ // Capabilities
+ var aggregationCursor = false;
+ var writeCommands = false;
+ var textSearch = false;
+ var authCommands = false;
+ var listCollections = false;
+ var listIndexes = false;
+ var maxNumberOfDocsInBatch = ismaster.maxWriteBatchSize || 1000;
+ var commandsTakeWriteConcern = false;
+ var commandsTakeCollation = false;
+
+ if (ismaster.minWireVersion >= 0) {
+ textSearch = true;
+ }
+
+ if (ismaster.maxWireVersion >= 1) {
+ aggregationCursor = true;
+ authCommands = true;
+ }
+
+ if (ismaster.maxWireVersion >= 2) {
+ writeCommands = true;
+ }
+
+ if (ismaster.maxWireVersion >= 3) {
+ listCollections = true;
+ listIndexes = true;
+ }
+
+ if (ismaster.maxWireVersion >= 5) {
+ commandsTakeWriteConcern = true;
+ commandsTakeCollation = true;
+ }
+
+ // If no min or max wire version set to 0
+ if (ismaster.minWireVersion == null) {
+ ismaster.minWireVersion = 0;
+ }
+
+ if (ismaster.maxWireVersion == null) {
+ ismaster.maxWireVersion = 0;
+ }
+
+ // Map up read only parameters
+ setup_get_property(this, 'hasAggregationCursor', aggregationCursor);
+ setup_get_property(this, 'hasWriteCommands', writeCommands);
+ setup_get_property(this, 'hasTextSearch', textSearch);
+ setup_get_property(this, 'hasAuthCommands', authCommands);
+ setup_get_property(this, 'hasListCollectionsCommand', listCollections);
+ setup_get_property(this, 'hasListIndexesCommand', listIndexes);
+ setup_get_property(this, 'minWireVersion', ismaster.minWireVersion);
+ setup_get_property(this, 'maxWireVersion', ismaster.maxWireVersion);
+ setup_get_property(this, 'maxNumberOfDocsInBatch', maxNumberOfDocsInBatch);
+ setup_get_property(this, 'commandsTakeWriteConcern', commandsTakeWriteConcern);
+ setup_get_property(this, 'commandsTakeCollation', commandsTakeCollation);
+};
+
+// Get package.json variable
+const driverVersion = require('../../package.json').version,
+ nodejsversion = f('Node.js %s, %s', process.version, os.endianness()),
+ type = os.type(),
+ name = process.platform,
+ architecture = process.arch,
+ release = os.release();
+
+class TopologyBase extends EventEmitter {
+ constructor() {
+ super();
+
+ // Build default client information
+ this.clientInfo = {
+ driver: {
+ name: 'nodejs',
+ version: driverVersion
+ },
+ os: {
+ type: type,
+ name: name,
+ architecture: architecture,
+ version: release
+ },
+ platform: nodejsversion
+ };
+
+ this.setMaxListeners(Infinity);
+ }
+
+ // Sessions related methods
+ hasSessionSupport() {
+ return this.logicalSessionTimeoutMinutes != null;
+ }
+
+ startSession(options, clientOptions) {
+ const session = new ClientSession(this, this.s.sessionPool, options, clientOptions);
+ session.once('ended', () => {
+ this.s.sessions = this.s.sessions.filter(s => !s.equals(session));
+ });
+
+ this.s.sessions.push(session);
+ return session;
+ }
+
+ endSessions(sessions, callback) {
+ return this.s.coreTopology.endSessions(sessions, callback);
+ }
+
+ // Server capabilities
+ capabilities() {
+ if (this.s.sCapabilities) return this.s.sCapabilities;
+ if (this.s.coreTopology.lastIsMaster() == null) return null;
+ this.s.sCapabilities = new ServerCapabilities(this.s.coreTopology.lastIsMaster());
+ return this.s.sCapabilities;
+ }
+
+ // Command
+ command(ns, cmd, options, callback) {
+ this.s.coreTopology.command(ns, cmd, translateReadPreference(options), callback);
+ }
+
+ // Insert
+ insert(ns, ops, options, callback) {
+ this.s.coreTopology.insert(ns, ops, options, callback);
+ }
+
+ // Update
+ update(ns, ops, options, callback) {
+ this.s.coreTopology.update(ns, ops, options, callback);
+ }
+
+ // Remove
+ remove(ns, ops, options, callback) {
+ this.s.coreTopology.remove(ns, ops, options, callback);
+ }
+
+ // IsConnected
+ isConnected(options) {
+ options = options || {};
+ options = translateReadPreference(options);
+
+ return this.s.coreTopology.isConnected(options);
+ }
+
+ // IsDestroyed
+ isDestroyed() {
+ return this.s.coreTopology.isDestroyed();
+ }
+
+ // Cursor
+ cursor(ns, cmd, options) {
+ options = options || {};
+ options = translateReadPreference(options);
+ options.disconnectHandler = this.s.store;
+ options.topology = this;
+
+ return this.s.coreTopology.cursor(ns, cmd, options);
+ }
+
+ lastIsMaster() {
+ return this.s.coreTopology.lastIsMaster();
+ }
+
+ selectServer(selector, options, callback) {
+ return this.s.coreTopology.selectServer(selector, options, callback);
+ }
+
+ /**
+ * Unref all sockets
+ * @method
+ */
+ unref() {
+ return this.s.coreTopology.unref();
+ }
+
+ auth() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ this.s.coreTopology.auth.apply(this.s.coreTopology, args);
+ }
+
+ logout() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ this.s.coreTopology.logout.apply(this.s.coreTopology, args);
+ }
+
+ /**
+ * All raw connections
+ * @method
+ * @return {array}
+ */
+ connections() {
+ return this.s.coreTopology.connections();
+ }
+
+ close(forceClosed) {
+ // If we have sessions, we want to individually move them to the session pool,
+ // and then send a single endSessions call.
+ if (this.s.sessions.length) {
+ this.s.sessions.forEach(session => session.endSession());
+ }
+
+ if (this.s.sessionPool) {
+ this.s.sessionPool.endAllPooledSessions();
+ }
+
+ this.s.coreTopology.destroy({
+ force: typeof forceClosed === 'boolean' ? forceClosed : false
+ });
+
+ // We need to wash out all stored processes
+ if (forceClosed === true) {
+ this.s.storeOptions.force = forceClosed;
+ this.s.store.flush();
+ }
+ }
+}
+
+// Properties
+Object.defineProperty(TopologyBase.prototype, 'bson', {
+ enumerable: true,
+ get: function() {
+ return this.s.coreTopology.s.bson;
+ }
+});
+
+Object.defineProperty(TopologyBase.prototype, 'parserType', {
+ enumerable: true,
+ get: function() {
+ return this.s.coreTopology.parserType;
+ }
+});
+
+Object.defineProperty(TopologyBase.prototype, 'logicalSessionTimeoutMinutes', {
+ enumerable: true,
+ get: function() {
+ return this.s.coreTopology.logicalSessionTimeoutMinutes;
+ }
+});
+
+Object.defineProperty(TopologyBase.prototype, 'type', {
+ enumerable: true,
+ get: function() {
+ return this.s.coreTopology.type;
+ }
+});
+
+exports.Store = Store;
+exports.ServerCapabilities = ServerCapabilities;
+exports.TopologyBase = TopologyBase;
diff --git a/node_modules/mongodb/lib/url_parser.js b/node_modules/mongodb/lib/url_parser.js
new file mode 100644
index 0000000..7cc0b2f
--- /dev/null
+++ b/node_modules/mongodb/lib/url_parser.js
@@ -0,0 +1,622 @@
+'use strict';
+
+const ReadPreference = require('mongodb-core').ReadPreference,
+ parser = require('url'),
+ f = require('util').format,
+ Logger = require('mongodb-core').Logger,
+ dns = require('dns');
+
+module.exports = function(url, options, callback) {
+ if (typeof options === 'function') (callback = options), (options = {});
+ options = options || {};
+
+ let result;
+ try {
+ result = parser.parse(url, true);
+ } catch (e) {
+ return callback(new Error('URL malformed, cannot be parsed'));
+ }
+
+ if (result.protocol !== 'mongodb:' && result.protocol !== 'mongodb+srv:') {
+ return callback(new Error('Invalid schema, expected `mongodb` or `mongodb+srv`'));
+ }
+
+ if (result.protocol === 'mongodb:') {
+ return parseHandler(url, options, callback);
+ }
+
+ // Otherwise parse this as an SRV record
+ if (result.hostname.split('.').length < 3) {
+ return callback(new Error('URI does not have hostname, domain name and tld'));
+ }
+
+ result.domainLength = result.hostname.split('.').length;
+
+ if (result.pathname && result.pathname.match(',')) {
+ return callback(new Error('Invalid URI, cannot contain multiple hostnames'));
+ }
+
+ if (result.port) {
+ return callback(new Error('Ports not accepted with `mongodb+srv` URIs'));
+ }
+
+ let srvAddress = `_mongodb._tcp.${result.host}`;
+ dns.resolveSrv(srvAddress, function(err, addresses) {
+ if (err) return callback(err);
+
+ if (addresses.length === 0) {
+ return callback(new Error('No addresses found at host'));
+ }
+
+ for (let i = 0; i < addresses.length; i++) {
+ if (!matchesParentDomain(addresses[i].name, result.hostname, result.domainLength)) {
+ return callback(new Error('Server record does not share hostname with parent URI'));
+ }
+ }
+
+ let base = result.auth ? `mongodb://${result.auth}@` : `mongodb://`;
+ let connectionStrings = addresses.map(function(address, i) {
+ if (i === 0) return `${base}${address.name}:${address.port}`;
+ else return `${address.name}:${address.port}`;
+ });
+
+ let connectionString = connectionStrings.join(',') + '/';
+ let connectionStringOptions = [];
+
+ // Add the default database if needed
+ if (result.path) {
+ let defaultDb = result.path.slice(1);
+ if (defaultDb.indexOf('?') !== -1) {
+ defaultDb = defaultDb.slice(0, defaultDb.indexOf('?'));
+ }
+
+ connectionString += defaultDb;
+ }
+
+ // Default to SSL true
+ if (!options.ssl && !result.search) {
+ connectionStringOptions.push('ssl=true');
+ } else if (!options.ssl && result.search && !result.search.match('ssl')) {
+ connectionStringOptions.push('ssl=true');
+ }
+
+ // Keep original uri options
+ if (result.search) {
+ connectionStringOptions.push(result.search.replace('?', ''));
+ }
+
+ dns.resolveTxt(result.host, function(err, record) {
+ if (err && err.code !== 'ENODATA') return callback(err);
+ if (err && err.code === 'ENODATA') record = null;
+
+ if (record) {
+ if (record.length > 1) {
+ return callback(new Error('Multiple text records not allowed'));
+ }
+
+ record = record[0];
+ if (record.length > 1) record = record.join('');
+ else record = record[0];
+
+ if (!record.includes('authSource') && !record.includes('replicaSet')) {
+ return callback(new Error('Text record must only set `authSource` or `replicaSet`'));
+ }
+
+ connectionStringOptions.push(record);
+ }
+
+ // Add any options to the connection string
+ if (connectionStringOptions.length) {
+ connectionString += `?${connectionStringOptions.join('&')}`;
+ }
+
+ parseHandler(connectionString, options, callback);
+ });
+ });
+};
+
+function matchesParentDomain(srvAddress, parentDomain) {
+ let regex = /^.*?\./;
+ let srv = `.${srvAddress.replace(regex, '')}`;
+ let parent = `.${parentDomain.replace(regex, '')}`;
+ if (srv.endsWith(parent)) return true;
+ else return false;
+}
+
+function parseHandler(address, options, callback) {
+ let result, err;
+ try {
+ result = parseConnectionString(address, options);
+ } catch (e) {
+ err = e;
+ }
+
+ return err ? callback(err, null) : callback(null, result);
+}
+
+function parseConnectionString(url, options) {
+ // Variables
+ let connection_part = '';
+ let auth_part = '';
+ let query_string_part = '';
+ let dbName = 'admin';
+
+ // Url parser result
+ let result = parser.parse(url, true);
+ if ((result.hostname == null || result.hostname === '') && url.indexOf('.sock') === -1) {
+ throw new Error('No hostname or hostnames provided in connection string');
+ }
+
+ if (result.port === '0') {
+ throw new Error('Invalid port (zero) with hostname');
+ }
+
+ if (!isNaN(parseInt(result.port, 10)) && parseInt(result.port, 10) > 65535) {
+ throw new Error('Invalid port (larger than 65535) with hostname');
+ }
+
+ if (
+ result.path &&
+ result.path.length > 0 &&
+ result.path[0] !== '/' &&
+ url.indexOf('.sock') === -1
+ ) {
+ throw new Error('Missing delimiting slash between hosts and options');
+ }
+
+ if (result.query) {
+ for (let name in result.query) {
+ if (name.indexOf('::') !== -1) {
+ throw new Error('Double colon in host identifier');
+ }
+
+ if (result.query[name] === '') {
+ throw new Error('Query parameter ' + name + ' is an incomplete value pair');
+ }
+ }
+ }
+
+ if (result.auth) {
+ let parts = result.auth.split(':');
+ if (url.indexOf(result.auth) !== -1 && parts.length > 2) {
+ throw new Error('Username with password containing an unescaped colon');
+ }
+
+ if (url.indexOf(result.auth) !== -1 && result.auth.indexOf('@') !== -1) {
+ throw new Error('Username containing an unescaped at-sign');
+ }
+ }
+
+ // Remove query
+ let clean = url.split('?').shift();
+
+ // Extract the list of hosts
+ let strings = clean.split(',');
+ let hosts = [];
+
+ for (let i = 0; i < strings.length; i++) {
+ let hostString = strings[i];
+
+ if (hostString.indexOf('mongodb') !== -1) {
+ if (hostString.indexOf('@') !== -1) {
+ hosts.push(hostString.split('@').pop());
+ } else {
+ hosts.push(hostString.substr('mongodb://'.length));
+ }
+ } else if (hostString.indexOf('/') !== -1) {
+ hosts.push(hostString.split('/').shift());
+ } else if (hostString.indexOf('/') === -1) {
+ hosts.push(hostString.trim());
+ }
+ }
+
+ for (let i = 0; i < hosts.length; i++) {
+ let r = parser.parse(f('mongodb://%s', hosts[i].trim()));
+ if (r.path && r.path.indexOf('.sock') !== -1) continue;
+ if (r.path && r.path.indexOf(':') !== -1) {
+ // Not connecting to a socket so check for an extra slash in the hostname.
+ // Using String#split as perf is better than match.
+ if (r.path.split('/').length > 1 && r.path.indexOf('::') === -1) {
+ throw new Error('Slash in host identifier');
+ } else {
+ throw new Error('Double colon in host identifier');
+ }
+ }
+ }
+
+ // If we have a ? mark cut the query elements off
+ if (url.indexOf('?') !== -1) {
+ query_string_part = url.substr(url.indexOf('?') + 1);
+ connection_part = url.substring('mongodb://'.length, url.indexOf('?'));
+ } else {
+ connection_part = url.substring('mongodb://'.length);
+ }
+
+ // Check if we have auth params
+ if (connection_part.indexOf('@') !== -1) {
+ auth_part = connection_part.split('@')[0];
+ connection_part = connection_part.split('@')[1];
+ }
+
+ // Check there is not more than one unescaped slash
+ if (connection_part.split('/').length > 2) {
+ throw new Error(
+ "Unsupported host '" +
+ connection_part.split('?')[0] +
+ "', hosts must be URL encoded and contain at most one unencoded slash"
+ );
+ }
+
+ // Check if the connection string has a db
+ if (connection_part.indexOf('.sock') !== -1) {
+ if (connection_part.indexOf('.sock/') !== -1) {
+ dbName = connection_part.split('.sock/')[1];
+ // Check if multiple database names provided, or just an illegal trailing backslash
+ if (dbName.indexOf('/') !== -1) {
+ if (dbName.split('/').length === 2 && dbName.split('/')[1].length === 0) {
+ throw new Error('Illegal trailing backslash after database name');
+ }
+ throw new Error('More than 1 database name in URL');
+ }
+ connection_part = connection_part.split(
+ '/',
+ connection_part.indexOf('.sock') + '.sock'.length
+ );
+ }
+ } else if (connection_part.indexOf('/') !== -1) {
+ // Check if multiple database names provided, or just an illegal trailing backslash
+ if (connection_part.split('/').length > 2) {
+ if (connection_part.split('/')[2].length === 0) {
+ throw new Error('Illegal trailing backslash after database name');
+ }
+ throw new Error('More than 1 database name in URL');
+ }
+ dbName = connection_part.split('/')[1];
+ connection_part = connection_part.split('/')[0];
+ }
+
+ // URI decode the host information
+ connection_part = decodeURIComponent(connection_part);
+
+ // Result object
+ let object = {};
+
+ // Pick apart the authentication part of the string
+ let authPart = auth_part || '';
+ let auth = authPart.split(':', 2);
+
+ // Decode the authentication URI components and verify integrity
+ let user = decodeURIComponent(auth[0]);
+ if (auth[0] !== encodeURIComponent(user)) {
+ throw new Error('Username contains an illegal unescaped character');
+ }
+ auth[0] = user;
+
+ if (auth[1]) {
+ let pass = decodeURIComponent(auth[1]);
+ if (auth[1] !== encodeURIComponent(pass)) {
+ throw new Error('Password contains an illegal unescaped character');
+ }
+ auth[1] = pass;
+ }
+
+ // Add auth to final object if we have 2 elements
+ if (auth.length === 2) object.auth = { user: auth[0], password: auth[1] };
+ // if user provided auth options, use that
+ if (options && options.auth != null) object.auth = options.auth;
+
+ // Variables used for temporary storage
+ let hostPart;
+ let urlOptions;
+ let servers;
+ let compression;
+ let serverOptions = { socketOptions: {} };
+ let dbOptions = { read_preference_tags: [] };
+ let replSetServersOptions = { socketOptions: {} };
+ let mongosOptions = { socketOptions: {} };
+ // Add server options to final object
+ object.server_options = serverOptions;
+ object.db_options = dbOptions;
+ object.rs_options = replSetServersOptions;
+ object.mongos_options = mongosOptions;
+
+ // Let's check if we are using a domain socket
+ if (url.match(/\.sock/)) {
+ // Split out the socket part
+ let domainSocket = url.substring(
+ url.indexOf('mongodb://') + 'mongodb://'.length,
+ url.lastIndexOf('.sock') + '.sock'.length
+ );
+ // Clean out any auth stuff if any
+ if (domainSocket.indexOf('@') !== -1) domainSocket = domainSocket.split('@')[1];
+ domainSocket = decodeURIComponent(domainSocket);
+ servers = [{ domain_socket: domainSocket }];
+ } else {
+ // Split up the db
+ hostPart = connection_part;
+ // Deduplicate servers
+ let deduplicatedServers = {};
+
+ // Parse all server results
+ servers = hostPart
+ .split(',')
+ .map(function(h) {
+ let _host, _port, ipv6match;
+ //check if it matches [IPv6]:port, where the port number is optional
+ if ((ipv6match = /\[([^\]]+)\](?::(.+))?/.exec(h))) {
+ _host = ipv6match[1];
+ _port = parseInt(ipv6match[2], 10) || 27017;
+ } else {
+ //otherwise assume it's IPv4, or plain hostname
+ let hostPort = h.split(':', 2);
+ _host = hostPort[0] || 'localhost';
+ _port = hostPort[1] != null ? parseInt(hostPort[1], 10) : 27017;
+ // Check for localhost?safe=true style case
+ if (_host.indexOf('?') !== -1) _host = _host.split(/\?/)[0];
+ }
+
+ // No entry returned for duplicate servr
+ if (deduplicatedServers[_host + '_' + _port]) return null;
+ deduplicatedServers[_host + '_' + _port] = 1;
+
+ // Return the mapped object
+ return { host: _host, port: _port };
+ })
+ .filter(function(x) {
+ return x != null;
+ });
+ }
+
+ // Get the db name
+ object.dbName = dbName || 'admin';
+ // Split up all the options
+ urlOptions = (query_string_part || '').split(/[&;]/);
+ // Ugh, we have to figure out which options go to which constructor manually.
+ urlOptions.forEach(function(opt) {
+ if (!opt) return;
+ var splitOpt = opt.split('='),
+ name = splitOpt[0],
+ value = splitOpt[1];
+
+ // Options implementations
+ switch (name) {
+ case 'slaveOk':
+ case 'slave_ok':
+ serverOptions.slave_ok = value === 'true';
+ dbOptions.slaveOk = value === 'true';
+ break;
+ case 'maxPoolSize':
+ case 'poolSize':
+ serverOptions.poolSize = parseInt(value, 10);
+ replSetServersOptions.poolSize = parseInt(value, 10);
+ break;
+ case 'appname':
+ object.appname = decodeURIComponent(value);
+ break;
+ case 'autoReconnect':
+ case 'auto_reconnect':
+ serverOptions.auto_reconnect = value === 'true';
+ break;
+ case 'ssl':
+ if (value === 'prefer') {
+ serverOptions.ssl = value;
+ replSetServersOptions.ssl = value;
+ mongosOptions.ssl = value;
+ break;
+ }
+ serverOptions.ssl = value === 'true';
+ replSetServersOptions.ssl = value === 'true';
+ mongosOptions.ssl = value === 'true';
+ break;
+ case 'sslValidate':
+ serverOptions.sslValidate = value === 'true';
+ replSetServersOptions.sslValidate = value === 'true';
+ mongosOptions.sslValidate = value === 'true';
+ break;
+ case 'replicaSet':
+ case 'rs_name':
+ replSetServersOptions.rs_name = value;
+ break;
+ case 'reconnectWait':
+ replSetServersOptions.reconnectWait = parseInt(value, 10);
+ break;
+ case 'retries':
+ replSetServersOptions.retries = parseInt(value, 10);
+ break;
+ case 'readSecondary':
+ case 'read_secondary':
+ replSetServersOptions.read_secondary = value === 'true';
+ break;
+ case 'fsync':
+ dbOptions.fsync = value === 'true';
+ break;
+ case 'journal':
+ dbOptions.j = value === 'true';
+ break;
+ case 'safe':
+ dbOptions.safe = value === 'true';
+ break;
+ case 'nativeParser':
+ case 'native_parser':
+ dbOptions.native_parser = value === 'true';
+ break;
+ case 'readConcernLevel':
+ dbOptions.readConcern = { level: value };
+ break;
+ case 'connectTimeoutMS':
+ serverOptions.socketOptions.connectTimeoutMS = parseInt(value, 10);
+ replSetServersOptions.socketOptions.connectTimeoutMS = parseInt(value, 10);
+ mongosOptions.socketOptions.connectTimeoutMS = parseInt(value, 10);
+ break;
+ case 'socketTimeoutMS':
+ serverOptions.socketOptions.socketTimeoutMS = parseInt(value, 10);
+ replSetServersOptions.socketOptions.socketTimeoutMS = parseInt(value, 10);
+ mongosOptions.socketOptions.socketTimeoutMS = parseInt(value, 10);
+ break;
+ case 'w':
+ dbOptions.w = parseInt(value, 10);
+ if (isNaN(dbOptions.w)) dbOptions.w = value;
+ break;
+ case 'authSource':
+ dbOptions.authSource = value;
+ break;
+ case 'gssapiServiceName':
+ dbOptions.gssapiServiceName = value;
+ break;
+ case 'authMechanism':
+ if (value === 'GSSAPI') {
+ // If no password provided decode only the principal
+ if (object.auth == null) {
+ let urlDecodeAuthPart = decodeURIComponent(authPart);
+ if (urlDecodeAuthPart.indexOf('@') === -1)
+ throw new Error('GSSAPI requires a provided principal');
+ object.auth = { user: urlDecodeAuthPart, password: null };
+ } else {
+ object.auth.user = decodeURIComponent(object.auth.user);
+ }
+ } else if (value === 'MONGODB-X509') {
+ object.auth = { user: decodeURIComponent(authPart) };
+ }
+
+ // Only support GSSAPI or MONGODB-CR for now
+ if (
+ value !== 'GSSAPI' &&
+ value !== 'MONGODB-X509' &&
+ value !== 'MONGODB-CR' &&
+ value !== 'DEFAULT' &&
+ value !== 'SCRAM-SHA-1' &&
+ value !== 'SCRAM-SHA-256' &&
+ value !== 'PLAIN'
+ )
+ throw new Error(
+ 'Only DEFAULT, GSSAPI, PLAIN, MONGODB-X509, or SCRAM-SHA-1 is supported by authMechanism'
+ );
+
+ // Authentication mechanism
+ dbOptions.authMechanism = value;
+ break;
+ case 'authMechanismProperties':
+ {
+ // Split up into key, value pairs
+ let values = value.split(',');
+ let o = {};
+ // For each value split into key, value
+ values.forEach(function(x) {
+ let v = x.split(':');
+ o[v[0]] = v[1];
+ });
+
+ // Set all authMechanismProperties
+ dbOptions.authMechanismProperties = o;
+ // Set the service name value
+ if (typeof o.SERVICE_NAME === 'string') dbOptions.gssapiServiceName = o.SERVICE_NAME;
+ if (typeof o.SERVICE_REALM === 'string') dbOptions.gssapiServiceRealm = o.SERVICE_REALM;
+ if (typeof o.CANONICALIZE_HOST_NAME === 'string')
+ dbOptions.gssapiCanonicalizeHostName =
+ o.CANONICALIZE_HOST_NAME === 'true' ? true : false;
+ }
+ break;
+ case 'wtimeoutMS':
+ dbOptions.wtimeout = parseInt(value, 10);
+ break;
+ case 'readPreference':
+ if (!ReadPreference.isValid(value))
+ throw new Error(
+ 'readPreference must be either primary/primaryPreferred/secondary/secondaryPreferred/nearest'
+ );
+ dbOptions.readPreference = value;
+ break;
+ case 'maxStalenessSeconds':
+ dbOptions.maxStalenessSeconds = parseInt(value, 10);
+ break;
+ case 'readPreferenceTags':
+ {
+ // Decode the value
+ value = decodeURIComponent(value);
+ // Contains the tag object
+ let tagObject = {};
+ if (value == null || value === '') {
+ dbOptions.read_preference_tags.push(tagObject);
+ break;
+ }
+
+ // Split up the tags
+ let tags = value.split(/,/);
+ for (let i = 0; i < tags.length; i++) {
+ let parts = tags[i].trim().split(/:/);
+ tagObject[parts[0]] = parts[1];
+ }
+
+ // Set the preferences tags
+ dbOptions.read_preference_tags.push(tagObject);
+ }
+ break;
+ case 'compressors':
+ {
+ compression = serverOptions.compression || {};
+ let compressors = value.split(',');
+ if (
+ !compressors.every(function(compressor) {
+ return compressor === 'snappy' || compressor === 'zlib';
+ })
+ ) {
+ throw new Error('Compressors must be at least one of snappy or zlib');
+ }
+
+ compression.compressors = compressors;
+ serverOptions.compression = compression;
+ }
+ break;
+ case 'zlibCompressionLevel':
+ {
+ compression = serverOptions.compression || {};
+ let zlibCompressionLevel = parseInt(value, 10);
+ if (zlibCompressionLevel < -1 || zlibCompressionLevel > 9) {
+ throw new Error('zlibCompressionLevel must be an integer between -1 and 9');
+ }
+
+ compression.zlibCompressionLevel = zlibCompressionLevel;
+ serverOptions.compression = compression;
+ }
+ break;
+ case 'retryWrites':
+ dbOptions.retryWrites = value === 'true';
+ break;
+ case 'minSize':
+ dbOptions.minSize = parseInt(value, 10);
+ break;
+ default:
+ {
+ let logger = Logger('URL Parser');
+ logger.warn(`${name} is not supported as a connection string option`);
+ }
+ break;
+ }
+ });
+
+ // No tags: should be null (not [])
+ if (dbOptions.read_preference_tags.length === 0) {
+ dbOptions.read_preference_tags = null;
+ }
+
+ // Validate if there are an invalid write concern combinations
+ if (
+ (dbOptions.w === -1 || dbOptions.w === 0) &&
+ (dbOptions.journal === true || dbOptions.fsync === true || dbOptions.safe === true)
+ )
+ throw new Error('w set to -1 or 0 cannot be combined with safe/w/journal/fsync');
+
+ // If no read preference set it to primary
+ if (!dbOptions.readPreference) {
+ dbOptions.readPreference = 'primary';
+ }
+
+ // make sure that user-provided options are applied with priority
+ dbOptions = Object.assign(dbOptions, options);
+
+ // Add servers to result
+ object.servers = servers;
+
+ // Returned parsed object
+ return object;
+}
diff --git a/node_modules/mongodb/lib/utils.js b/node_modules/mongodb/lib/utils.js
new file mode 100644
index 0000000..a96f584
--- /dev/null
+++ b/node_modules/mongodb/lib/utils.js
@@ -0,0 +1,720 @@
+'use strict';
+
+const MongoError = require('mongodb-core').MongoError;
+const ReadPreference = require('mongodb-core').ReadPreference;
+
+var shallowClone = function(obj) {
+ var copy = {};
+ for (var name in obj) copy[name] = obj[name];
+ return copy;
+};
+
+// Figure out the read preference
+var translateReadPreference = function(options) {
+ var r = null;
+ if (options.readPreference) {
+ r = options.readPreference;
+ } else {
+ return options;
+ }
+
+ if (typeof r === 'string') {
+ options.readPreference = new ReadPreference(r);
+ } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
+ const mode = r.mode || r.preference;
+ if (mode && typeof mode === 'string') {
+ options.readPreference = new ReadPreference(mode, r.tags, {
+ maxStalenessSeconds: r.maxStalenessSeconds
+ });
+ }
+ } else if (!(r instanceof ReadPreference)) {
+ throw new TypeError('Invalid read preference: ' + r);
+ }
+
+ return options;
+};
+
+// Set simple property
+var getSingleProperty = function(obj, name, value) {
+ Object.defineProperty(obj, name, {
+ enumerable: true,
+ get: function() {
+ return value;
+ }
+ });
+};
+
+var formatSortValue = (exports.formatSortValue = function(sortDirection) {
+ var value = ('' + sortDirection).toLowerCase();
+
+ switch (value) {
+ case 'ascending':
+ case 'asc':
+ case '1':
+ return 1;
+ case 'descending':
+ case 'desc':
+ case '-1':
+ return -1;
+ default:
+ throw new Error(
+ 'Illegal sort clause, must be of the form ' +
+ "[['field1', '(ascending|descending)'], " +
+ "['field2', '(ascending|descending)']]"
+ );
+ }
+});
+
+var formattedOrderClause = (exports.formattedOrderClause = function(sortValue) {
+ var orderBy = {};
+ if (sortValue == null) return null;
+ if (Array.isArray(sortValue)) {
+ if (sortValue.length === 0) {
+ return null;
+ }
+
+ for (var i = 0; i < sortValue.length; i++) {
+ if (sortValue[i].constructor === String) {
+ orderBy[sortValue[i]] = 1;
+ } else {
+ orderBy[sortValue[i][0]] = formatSortValue(sortValue[i][1]);
+ }
+ }
+ } else if (sortValue != null && typeof sortValue === 'object') {
+ orderBy = sortValue;
+ } else if (typeof sortValue === 'string') {
+ orderBy[sortValue] = 1;
+ } else {
+ throw new Error(
+ 'Illegal sort clause, must be of the form ' +
+ "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
+ );
+ }
+
+ return orderBy;
+});
+
+var checkCollectionName = function checkCollectionName(collectionName) {
+ if ('string' !== typeof collectionName) {
+ throw new MongoError('collection name must be a String');
+ }
+
+ if (!collectionName || collectionName.indexOf('..') !== -1) {
+ throw new MongoError('collection names cannot be empty');
+ }
+
+ if (
+ collectionName.indexOf('$') !== -1 &&
+ collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null
+ ) {
+ throw new MongoError("collection names must not contain '$'");
+ }
+
+ if (collectionName.match(/^\.|\.$/) != null) {
+ throw new MongoError("collection names must not start or end with '.'");
+ }
+
+ // Validate that we are not passing 0x00 in the colletion name
+ if (collectionName.indexOf('\x00') !== -1) {
+ throw new MongoError('collection names cannot contain a null character');
+ }
+};
+
+var handleCallback = function(callback, err, value1, value2) {
+ try {
+ if (callback == null) return;
+
+ if (callback) {
+ return value2 ? callback(err, value1, value2) : callback(err, value1);
+ }
+ } catch (err) {
+ process.nextTick(function() {
+ throw err;
+ });
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * Wrap a Mongo error document in an Error instance
+ * @ignore
+ * @api private
+ */
+var toError = function(error) {
+ if (error instanceof Error) return error;
+
+ var msg = error.err || error.errmsg || error.errMessage || error;
+ var e = MongoError.create({ message: msg, driver: true });
+
+ // Get all object keys
+ var keys = typeof error === 'object' ? Object.keys(error) : [];
+
+ for (var i = 0; i < keys.length; i++) {
+ try {
+ e[keys[i]] = error[keys[i]];
+ } catch (err) {
+ // continue
+ }
+ }
+
+ return e;
+};
+
+/**
+ * @ignore
+ */
+var normalizeHintField = function normalizeHintField(hint) {
+ var finalHint = null;
+
+ if (typeof hint === 'string') {
+ finalHint = hint;
+ } else if (Array.isArray(hint)) {
+ finalHint = {};
+
+ hint.forEach(function(param) {
+ finalHint[param] = 1;
+ });
+ } else if (hint != null && typeof hint === 'object') {
+ finalHint = {};
+ for (var name in hint) {
+ finalHint[name] = hint[name];
+ }
+ }
+
+ return finalHint;
+};
+
+/**
+ * Create index name based on field spec
+ *
+ * @ignore
+ * @api private
+ */
+var parseIndexOptions = function(fieldOrSpec) {
+ var fieldHash = {};
+ var indexes = [];
+ var keys;
+
+ // Get all the fields accordingly
+ if ('string' === typeof fieldOrSpec) {
+ // 'type'
+ indexes.push(fieldOrSpec + '_' + 1);
+ fieldHash[fieldOrSpec] = 1;
+ } else if (Array.isArray(fieldOrSpec)) {
+ fieldOrSpec.forEach(function(f) {
+ if ('string' === typeof f) {
+ // [{location:'2d'}, 'type']
+ indexes.push(f + '_' + 1);
+ fieldHash[f] = 1;
+ } else if (Array.isArray(f)) {
+ // [['location', '2d'],['type', 1]]
+ indexes.push(f[0] + '_' + (f[1] || 1));
+ fieldHash[f[0]] = f[1] || 1;
+ } else if (isObject(f)) {
+ // [{location:'2d'}, {type:1}]
+ keys = Object.keys(f);
+ keys.forEach(function(k) {
+ indexes.push(k + '_' + f[k]);
+ fieldHash[k] = f[k];
+ });
+ } else {
+ // undefined (ignore)
+ }
+ });
+ } else if (isObject(fieldOrSpec)) {
+ // {location:'2d', type:1}
+ keys = Object.keys(fieldOrSpec);
+ keys.forEach(function(key) {
+ indexes.push(key + '_' + fieldOrSpec[key]);
+ fieldHash[key] = fieldOrSpec[key];
+ });
+ }
+
+ return {
+ name: indexes.join('_'),
+ keys: keys,
+ fieldHash: fieldHash
+ };
+};
+
+var isObject = (exports.isObject = function(arg) {
+ return '[object Object]' === Object.prototype.toString.call(arg);
+});
+
+var debugOptions = function(debugFields, options) {
+ var finaloptions = {};
+ debugFields.forEach(function(n) {
+ finaloptions[n] = options[n];
+ });
+
+ return finaloptions;
+};
+
+var decorateCommand = function(command, options, exclude) {
+ for (var name in options) {
+ if (exclude.indexOf(name) === -1) command[name] = options[name];
+ }
+
+ return command;
+};
+
+var mergeOptions = function(target, source) {
+ for (var name in source) {
+ target[name] = source[name];
+ }
+
+ return target;
+};
+
+// Merge options with translation
+var translateOptions = function(target, source) {
+ var translations = {
+ // SSL translation options
+ sslCA: 'ca',
+ sslCRL: 'crl',
+ sslValidate: 'rejectUnauthorized',
+ sslKey: 'key',
+ sslCert: 'cert',
+ sslPass: 'passphrase',
+ // SocketTimeout translation options
+ socketTimeoutMS: 'socketTimeout',
+ connectTimeoutMS: 'connectionTimeout',
+ // Replicaset options
+ replicaSet: 'setName',
+ rs_name: 'setName',
+ secondaryAcceptableLatencyMS: 'acceptableLatency',
+ connectWithNoPrimary: 'secondaryOnlyConnectionAllowed',
+ // Mongos options
+ acceptableLatencyMS: 'localThresholdMS'
+ };
+
+ for (var name in source) {
+ if (translations[name]) {
+ target[translations[name]] = source[name];
+ } else {
+ target[name] = source[name];
+ }
+ }
+
+ return target;
+};
+
+var filterOptions = function(options, names) {
+ var filterOptions = {};
+
+ for (var name in options) {
+ if (names.indexOf(name) !== -1) filterOptions[name] = options[name];
+ }
+
+ // Filtered options
+ return filterOptions;
+};
+
+// Write concern keys
+var writeConcernKeys = ['w', 'j', 'wtimeout', 'fsync'];
+
+// Merge the write concern options
+var mergeOptionsAndWriteConcern = function(targetOptions, sourceOptions, keys, mergeWriteConcern) {
+ // Mix in any allowed options
+ for (var i = 0; i < keys.length; i++) {
+ if (!targetOptions[keys[i]] && sourceOptions[keys[i]] !== undefined) {
+ targetOptions[keys[i]] = sourceOptions[keys[i]];
+ }
+ }
+
+ // No merging of write concern
+ if (!mergeWriteConcern) return targetOptions;
+
+ // Found no write Concern options
+ var found = false;
+ for (i = 0; i < writeConcernKeys.length; i++) {
+ if (targetOptions[writeConcernKeys[i]]) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ for (i = 0; i < writeConcernKeys.length; i++) {
+ if (sourceOptions[writeConcernKeys[i]]) {
+ targetOptions[writeConcernKeys[i]] = sourceOptions[writeConcernKeys[i]];
+ }
+ }
+ }
+
+ return targetOptions;
+};
+
+/**
+ * Executes the given operation with provided arguments.
+ *
+ * This method reduces large amounts of duplication in the entire codebase by providing
+ * a single point for determining whether callbacks or promises should be used. Additionally
+ * it allows for a single point of entry to provide features such as implicit sessions, which
+ * are required by the Driver Sessions specification in the event that a ClientSession is
+ * not provided
+ *
+ * @param {object} topology The topology to execute this operation on
+ * @param {function} operation The operation to execute
+ * @param {array} args Arguments to apply the provided operation
+ * @param {object} [options] Options that modify the behavior of the method
+ * @param {function]} [options.resultMutator] Allows for the result of the operation to be changed for custom return types
+ */
+const executeOperation = (topology, operation, args, options) => {
+ if (topology == null) {
+ throw new TypeError('This method requires a valid topology instance');
+ }
+
+ if (!Array.isArray(args)) {
+ throw new TypeError('This method requires an array of arguments to apply');
+ }
+
+ options = options || {};
+ const Promise = topology.s.promiseLibrary;
+ let resultMutator = options.resultMutator;
+ let callback = args[args.length - 1];
+
+ // The driver sessions spec mandates that we implicitly create sessions for operations
+ // that are not explicitly provided with a session.
+ let session, opOptions, owner;
+ if (!options.skipSessions && topology.hasSessionSupport()) {
+ opOptions = args[args.length - 2];
+ if (opOptions == null || opOptions.session == null) {
+ owner = Symbol();
+ session = topology.startSession({ owner });
+ const optionsIndex = args.length - 2;
+ args[optionsIndex] = Object.assign({}, args[optionsIndex], { session: session });
+ } else if (opOptions.session && opOptions.session.hasEnded) {
+ throw new MongoError('Use of expired sessions is not permitted');
+ }
+ }
+
+ const makeExecuteCallback = (resolve, reject) =>
+ function executeCallback(err, result) {
+ if (session && session.owner === owner && !options.returnsCursor) {
+ session.endSession(() => {
+ delete opOptions.session;
+ if (err) return reject(err);
+ if (resultMutator) return resolve(resultMutator(result));
+ resolve(result);
+ });
+ } else {
+ if (err) return reject(err);
+ if (resultMutator) return resolve(resultMutator(result));
+ resolve(result);
+ }
+ };
+
+ // Execute using callback
+ if (typeof callback === 'function') {
+ callback = args.pop();
+ const handler = makeExecuteCallback(
+ result => callback(null, result),
+ err => callback(err, null)
+ );
+ args.push(handler);
+
+ try {
+ return operation.apply(null, args);
+ } catch (e) {
+ handler(e);
+ throw e;
+ }
+ }
+
+ // Return a Promise
+ if (args[args.length - 1] != null) {
+ throw new TypeError('final argument to `executeOperation` must be a callback');
+ }
+
+ return new Promise(function(resolve, reject) {
+ const handler = makeExecuteCallback(resolve, reject);
+ args[args.length - 1] = handler;
+
+ try {
+ return operation.apply(null, args);
+ } catch (e) {
+ handler(e);
+ }
+ });
+};
+
+/**
+ * Applies retryWrites: true to a command if retryWrites is set on the command's database.
+ *
+ * @param {object} target The target command to which we will apply retryWrites.
+ * @param {object} db The database from which we can inherit a retryWrites value.
+ */
+function applyRetryableWrites(target, db) {
+ if (db && db.s.options.retryWrites) {
+ target.retryWrites = true;
+ }
+
+ return target;
+}
+
+/**
+ * Applies a write concern to a command based on well defined inheritance rules, optionally
+ * detecting support for the write concern in the first place.
+ *
+ * @param {Object} target the target command we will be applying the write concern to
+ * @param {Object} sources sources where we can inherit default write concerns from
+ * @param {Object} [options] optional settings passed into a command for write concern overrides
+ * @returns {Object} the (now) decorated target
+ */
+function applyWriteConcern(target, sources, options) {
+ options = options || {};
+ const db = sources.db;
+ const coll = sources.collection;
+
+ if (options.session && options.session.inTransaction()) {
+ // writeConcern is not allowed within a multi-statement transaction
+ if (target.writeConcern) {
+ delete target.writeConcern;
+ }
+
+ return target;
+ }
+
+ if (options.w != null || options.j != null || options.fsync != null) {
+ const writeConcern = {};
+ if (options.w != null) writeConcern.w = options.w;
+ if (options.wtimeout != null) writeConcern.wtimeout = options.wtimeout;
+ if (options.j != null) writeConcern.j = options.j;
+ if (options.fsync != null) writeConcern.fsync = options.fsync;
+ return Object.assign(target, { writeConcern });
+ }
+
+ if (
+ coll &&
+ (coll.writeConcern.w != null || coll.writeConcern.j != null || coll.writeConcern.fsync != null)
+ ) {
+ return Object.assign(target, { writeConcern: Object.assign({}, coll.writeConcern) });
+ }
+
+ if (
+ db &&
+ (db.writeConcern.w != null || db.writeConcern.j != null || db.writeConcern.fsync != null)
+ ) {
+ return Object.assign(target, { writeConcern: Object.assign({}, db.writeConcern) });
+ }
+
+ return target;
+}
+
+/**
+ * Resolves a read preference based on well-defined inheritance rules. This method will not only
+ * determine the read preference (if there is one), but will also ensure the returned value is a
+ * properly constructed instance of `ReadPreference`.
+ *
+ * @param {Object} options The options passed into the method, potentially containing a read preference
+ * @param {Object} sources Sources from which we can inherit a read preference
+ * @returns {(ReadPreference|null)} The resolved read preference
+ */
+function resolveReadPreference(options, sources) {
+ options = options || {};
+ sources = sources || {};
+
+ const db = sources.db;
+ const coll = sources.collection;
+ const defaultReadPreference = sources.default;
+ const session = options.session;
+
+ let readPreference;
+ if (options.readPreference) {
+ readPreference = options.readPreference;
+ } else if (session && session.inTransaction() && session.transaction.options.readPreference) {
+ // The transaction’s read preference MUST override all other user configurable read preferences.
+ readPreference = session.transaction.options.readPreference;
+ } else {
+ if (coll && coll.s.readPreference) {
+ readPreference = coll.s.readPreference;
+ } else if (db && db.s.readPreference) {
+ readPreference = db.s.readPreference;
+ } else if (defaultReadPreference) {
+ readPreference = defaultReadPreference;
+ }
+ }
+
+ // do we even have a read preference?
+ if (readPreference == null) {
+ return null;
+ }
+
+ // now attempt to convert the read preference if necessary
+ if (typeof readPreference === 'string') {
+ readPreference = new ReadPreference(readPreference);
+ } else if (
+ readPreference &&
+ !(readPreference instanceof ReadPreference) &&
+ typeof readPreference === 'object'
+ ) {
+ const mode = readPreference.mode || readPreference.preference;
+ if (mode && typeof mode === 'string') {
+ readPreference = new ReadPreference(mode, readPreference.tags, {
+ maxStalenessSeconds: readPreference.maxStalenessSeconds
+ });
+ }
+ } else if (!(readPreference instanceof ReadPreference)) {
+ throw new TypeError('Invalid read preference: ' + readPreference);
+ }
+
+ return readPreference;
+}
+
+/**
+ * Checks if a given value is a Promise
+ *
+ * @param {*} maybePromise
+ * @return true if the provided value is a Promise
+ */
+function isPromiseLike(maybePromise) {
+ return maybePromise && typeof maybePromise.then === 'function';
+}
+
+/**
+ * Applies collation to a given command.
+ *
+ * @param {object} [command] the command on which to apply collation
+ * @param {(Cursor|Collection)} [target] target of command
+ * @param {object} [options] options containing collation settings
+ */
+function decorateWithCollation(command, target, options) {
+ const topology = target.s && target.s.topology;
+
+ if (!topology) {
+ throw new TypeError('parameter "target" is missing a topology');
+ }
+
+ const capabilities = target.s.topology.capabilities();
+ if (options.collation && typeof options.collation === 'object') {
+ if (capabilities && capabilities.commandsTakeCollation) {
+ command.collation = options.collation;
+ } else {
+ throw new MongoError(`server ${topology.s.coreTopology.name} does not support collation`);
+ }
+ }
+}
+
+/**
+ * Applies a read concern to a given command.
+ *
+ * @param {object} command the command on which to apply the read concern
+ * @param {Collection} coll the parent collection of the operation calling this method
+ */
+function decorateWithReadConcern(command, coll) {
+ let readConcern = Object.assign({}, command.readConcern || {});
+ if (coll.s.readConcern) {
+ Object.assign(readConcern, coll.s.readConcern);
+ }
+
+ if (Object.keys(readConcern).length > 0) {
+ Object.assign(command, { readConcern: readConcern });
+ }
+}
+
+const emitProcessWarning = msg => process.emitWarning(msg, 'DeprecationWarning');
+const emitConsoleWarning = msg => console.error(msg);
+const emitDeprecationWarning = process.emitWarning ? emitProcessWarning : emitConsoleWarning;
+
+/**
+ * Default message handler for generating deprecation warnings.
+ *
+ * @param {string} name function name
+ * @param {string} option option name
+ * @return {string} warning message
+ * @ignore
+ * @api private
+ */
+function defaultMsgHandler(name, option) {
+ return `${name} option [${option}] is deprecated and will be removed in a later version.`;
+}
+
+/**
+ * Deprecates a given function's options.
+ *
+ * @param {object} config configuration for deprecation
+ * @param {string} config.name function name
+ * @param {Array} config.deprecatedOptions options to deprecate
+ * @param {number} config.optionsIndex index of options object in function arguments array
+ * @param {function} [config.msgHandler] optional custom message handler to generate warnings
+ * @param {function} fn the target function of deprecation
+ * @return {function} modified function that warns once per deprecated option, and executes original function
+ * @ignore
+ * @api private
+ */
+function deprecateOptions(config, fn) {
+ if (process.noDeprecation === true) {
+ return fn;
+ }
+
+ const msgHandler = config.msgHandler ? config.msgHandler : defaultMsgHandler;
+
+ const optionsWarned = new Set();
+ function deprecated() {
+ const options = arguments[config.optionsIndex];
+
+ // ensure options is a valid, non-empty object, otherwise short-circuit
+ if (!isObject(options) || Object.keys(options).length === 0) {
+ return fn.apply(this, arguments);
+ }
+
+ config.deprecatedOptions.forEach(deprecatedOption => {
+ if (options.hasOwnProperty(deprecatedOption) && !optionsWarned.has(deprecatedOption)) {
+ optionsWarned.add(deprecatedOption);
+ const msg = msgHandler(config.name, deprecatedOption);
+ emitDeprecationWarning(msg);
+ if (this && this.getLogger) {
+ const logger = this.getLogger();
+ if (logger) {
+ logger.warn(msg);
+ }
+ }
+ }
+ });
+
+ return fn.apply(this, arguments);
+ }
+
+ // These lines copied from https://github.com/nodejs/node/blob/25e5ae41688676a5fd29b2e2e7602168eee4ceb5/lib/internal/util.js#L73-L80
+ // The wrapper will keep the same prototype as fn to maintain prototype chain
+ Object.setPrototypeOf(deprecated, fn);
+ if (fn.prototype) {
+ // Setting this (rather than using Object.setPrototype, as above) ensures
+ // that calling the unwrapped constructor gives an instanceof the wrapped
+ // constructor.
+ deprecated.prototype = fn.prototype;
+ }
+
+ return deprecated;
+}
+
+module.exports = {
+ filterOptions,
+ mergeOptions,
+ translateOptions,
+ shallowClone,
+ getSingleProperty,
+ checkCollectionName,
+ toError,
+ formattedOrderClause,
+ parseIndexOptions,
+ normalizeHintField,
+ handleCallback,
+ decorateCommand,
+ isObject,
+ debugOptions,
+ MAX_JS_INT: Number.MAX_SAFE_INTEGER + 1,
+ mergeOptionsAndWriteConcern,
+ translateReadPreference,
+ executeOperation,
+ applyRetryableWrites,
+ applyWriteConcern,
+ resolveReadPreference,
+ isPromiseLike,
+ decorateWithCollation,
+ decorateWithReadConcern,
+ deprecateOptions
+};
diff --git a/node_modules/mongodb/package.json b/node_modules/mongodb/package.json
new file mode 100644
index 0000000..2bd4e18
--- /dev/null
+++ b/node_modules/mongodb/package.json
@@ -0,0 +1,90 @@
+{
+ "_from": "mongodb",
+ "_id": "mongodb@3.1.10",
+ "_inBundle": false,
+ "_integrity": "sha512-Uml42GeFxhTGQVml1XQ4cD0o/rp7J2ROy0fdYUcVitoE7vFqEhKH4TYVqRDpQr/bXtCJVxJdNQC1ntRxNREkPQ==",
+ "_location": "/mongodb",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "tag",
+ "registry": true,
+ "raw": "mongodb",
+ "name": "mongodb",
+ "escapedName": "mongodb",
+ "rawSpec": "",
+ "saveSpec": null,
+ "fetchSpec": "latest"
+ },
+ "_requiredBy": [
+ "#USER",
+ "/",
+ "/mongoose"
+ ],
+ "_resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.10.tgz",
+ "_shasum": "45ad9b74ea376f4122d0881b75e5489b9e504ed7",
+ "_spec": "mongodb",
+ "_where": "D:\\Gitterra\\om",
+ "bugs": {
+ "url": "https://github.com/mongodb/node-mongodb-native/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "mongodb-core": "3.1.9",
+ "safe-buffer": "^5.1.2"
+ },
+ "deprecated": false,
+ "description": "The official MongoDB driver for Node.js",
+ "devDependencies": {
+ "bluebird": "3.5.0",
+ "bson": "^1.0.4",
+ "chai": "^4.1.1",
+ "chai-subset": "^1.6.0",
+ "co": "4.6.0",
+ "coveralls": "^2.11.6",
+ "eslint": "^4.5.0",
+ "eslint-plugin-prettier": "^2.2.0",
+ "istanbul": "^0.4.5",
+ "jsdoc": "3.5.5",
+ "lodash.camelcase": "^4.3.0",
+ "mocha-sinon": "^2.1.0",
+ "mongodb-extjson": "^2.1.1",
+ "mongodb-mock-server": "^1.0.0",
+ "mongodb-test-runner": "^1.1.18",
+ "prettier": "~1.12.0",
+ "semver": "^5.5.0",
+ "sinon": "^4.3.0",
+ "sinon-chai": "^3.2.0",
+ "standard-version": "^4.4.0",
+ "worker-farm": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "files": [
+ "index.js",
+ "lib"
+ ],
+ "homepage": "https://github.com/mongodb/node-mongodb-native",
+ "keywords": [
+ "mongodb",
+ "driver",
+ "official"
+ ],
+ "license": "Apache-2.0",
+ "main": "index.js",
+ "name": "mongodb",
+ "repository": {
+ "type": "git",
+ "url": "git+ssh://git@github.com/mongodb/node-mongodb-native.git"
+ },
+ "scripts": {
+ "bench": "node test/driverBench/",
+ "coverage": "istanbul cover mongodb-test-runner -- -t 60000 test/unit test/functional",
+ "format": "prettier --print-width 100 --tab-width 2 --single-quote --write 'test/**/*.js' 'lib/**/*.js'",
+ "generate-evergreen": "node .evergreen/generate_evergreen_tasks.js",
+ "lint": "eslint lib test",
+ "release": "standard-version -i HISTORY.md",
+ "test": "npm run lint && mongodb-test-runner -t 60000 test/unit test/functional"
+ },
+ "version": "3.1.10"
+}
diff --git a/node_modules/mongoose-legacy-pluralize/LICENSE b/node_modules/mongoose-legacy-pluralize/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/node_modules/mongoose-legacy-pluralize/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/node_modules/mongoose-legacy-pluralize/README.md b/node_modules/mongoose-legacy-pluralize/README.md
new file mode 100644
index 0000000..3296efb
--- /dev/null
+++ b/node_modules/mongoose-legacy-pluralize/README.md
@@ -0,0 +1,2 @@
+# mongoose-legacy-pluralize
+Legacy pluralization logic for mongoose
diff --git a/node_modules/mongoose-legacy-pluralize/index.js b/node_modules/mongoose-legacy-pluralize/index.js
new file mode 100644
index 0000000..1dd451d
--- /dev/null
+++ b/node_modules/mongoose-legacy-pluralize/index.js
@@ -0,0 +1,95 @@
+module.exports = pluralize;
+
+/**
+ * Pluralization rules.
+ *
+ * These rules are applied while processing the argument to `toCollectionName`.
+ *
+ * @deprecated remove in 4.x gh-1350
+ */
+
+exports.pluralization = [
+ [/(m)an$/gi, '$1en'],
+ [/(pe)rson$/gi, '$1ople'],
+ [/(child)$/gi, '$1ren'],
+ [/^(ox)$/gi, '$1en'],
+ [/(ax|test)is$/gi, '$1es'],
+ [/(octop|vir)us$/gi, '$1i'],
+ [/(alias|status)$/gi, '$1es'],
+ [/(bu)s$/gi, '$1ses'],
+ [/(buffal|tomat|potat)o$/gi, '$1oes'],
+ [/([ti])um$/gi, '$1a'],
+ [/sis$/gi, 'ses'],
+ [/(?:([^f])fe|([lr])f)$/gi, '$1$2ves'],
+ [/(hive)$/gi, '$1s'],
+ [/([^aeiouy]|qu)y$/gi, '$1ies'],
+ [/(x|ch|ss|sh)$/gi, '$1es'],
+ [/(matr|vert|ind)ix|ex$/gi, '$1ices'],
+ [/([m|l])ouse$/gi, '$1ice'],
+ [/(kn|w|l)ife$/gi, '$1ives'],
+ [/(quiz)$/gi, '$1zes'],
+ [/s$/gi, 's'],
+ [/([^a-z])$/, '$1'],
+ [/$/gi, 's']
+];
+var rules = exports.pluralization;
+
+/**
+ * Uncountable words.
+ *
+ * These words are applied while processing the argument to `toCollectionName`.
+ * @api public
+ */
+
+exports.uncountables = [
+ 'advice',
+ 'energy',
+ 'excretion',
+ 'digestion',
+ 'cooperation',
+ 'health',
+ 'justice',
+ 'labour',
+ 'machinery',
+ 'equipment',
+ 'information',
+ 'pollution',
+ 'sewage',
+ 'paper',
+ 'money',
+ 'species',
+ 'series',
+ 'rain',
+ 'rice',
+ 'fish',
+ 'sheep',
+ 'moose',
+ 'deer',
+ 'news',
+ 'expertise',
+ 'status',
+ 'media'
+];
+var uncountables = exports.uncountables;
+
+/*!
+ * Pluralize function.
+ *
+ * @author TJ Holowaychuk (extracted from _ext.js_)
+ * @param {String} string to pluralize
+ * @api private
+ */
+
+function pluralize(str) {
+ var found;
+ str = str.toLowerCase();
+ if (!~uncountables.indexOf(str)) {
+ found = rules.filter(function(rule) {
+ return str.match(rule[0]);
+ });
+ if (found[0]) {
+ return str.replace(found[0][0], found[0][1]);
+ }
+ }
+ return str;
+}
diff --git a/node_modules/mongoose-legacy-pluralize/package.json b/node_modules/mongoose-legacy-pluralize/package.json
new file mode 100644
index 0000000..6a5d043
--- /dev/null
+++ b/node_modules/mongoose-legacy-pluralize/package.json
@@ -0,0 +1,51 @@
+{
+ "_from": "mongoose-legacy-pluralize@1.0.2",
+ "_id": "mongoose-legacy-pluralize@1.0.2",
+ "_inBundle": false,
+ "_integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==",
+ "_location": "/mongoose-legacy-pluralize",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "mongoose-legacy-pluralize@1.0.2",
+ "name": "mongoose-legacy-pluralize",
+ "escapedName": "mongoose-legacy-pluralize",
+ "rawSpec": "1.0.2",
+ "saveSpec": null,
+ "fetchSpec": "1.0.2"
+ },
+ "_requiredBy": [
+ "/mongoose"
+ ],
+ "_resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
+ "_shasum": "3ba9f91fa507b5186d399fb40854bff18fb563e4",
+ "_spec": "mongoose-legacy-pluralize@1.0.2",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mongoose",
+ "author": {
+ "name": "Valeri Karpov",
+ "email": "val@karpov.io"
+ },
+ "bugs": {
+ "url": "https://github.com/vkarpov15/mongoose-legacy-pluralize/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Legacy pluralization logic for mongoose",
+ "homepage": "https://github.com/vkarpov15/mongoose-legacy-pluralize",
+ "keywords": [
+ "mongoose",
+ "mongodb"
+ ],
+ "license": "Apache-2.0",
+ "main": "index.js",
+ "name": "mongoose-legacy-pluralize",
+ "peerDependencies": {
+ "mongoose": "*"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vkarpov15/mongoose-legacy-pluralize.git"
+ },
+ "version": "1.0.2"
+}
diff --git a/node_modules/mongoose/.travis.yml b/node_modules/mongoose/.travis.yml
new file mode 100644
index 0000000..d1529aa
--- /dev/null
+++ b/node_modules/mongoose/.travis.yml
@@ -0,0 +1,21 @@
+language: node_js
+sudo: false
+node_js: [10, 9, 8, 7, 6, 5, 4]
+install:
+ - travis_retry npm install
+before_script:
+ - wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.6.6.tgz
+ - tar -zxvf mongodb-linux-x86_64-3.6.6.tgz
+ - mkdir -p ./data/db/27017
+ - mkdir -p ./data/db/27000
+ - printf "\n--timeout 8000" >> ./test/mocha.opts
+ - ./mongodb-linux-x86_64-3.6.6/bin/mongod --fork --dbpath ./data/db/27017 --syslog --port 27017
+ - sleep 2
+matrix:
+ include:
+ - name: "👕Linter"
+ node_js: 10
+ before_script: skip
+ script: npm run lint
+notifications:
+ email: false
diff --git a/node_modules/mongoose/History.md b/node_modules/mongoose/History.md
new file mode 100644
index 0000000..6b0f2d4
--- /dev/null
+++ b/node_modules/mongoose/History.md
@@ -0,0 +1,4828 @@
+5.4.3 / 2019-01-09
+==================
+ * fix(populate): handle `count` option when using `Document#populate()` on a virtual #7380
+ * fix(connection): set connection state to DISCONNECTED if replica set has no primary #7330
+ * fix(mongoose): apply global plugins to schemas nested underneath embedded discriminators #7370
+ * fix(document): make modifiedPaths() return nested paths 1 level down on initial set #7313
+ * fix(plugins): ensure sharding plugin works even if ObjectId has a `valueOf()` #7353
+
+5.4.2 / 2019-01-03
+==================
+ * fix(document): ensure Document#updateOne() returns a query but still calls hooks #7366
+ * fix(query): allow explicitly projecting out populated paths that are automatically projected in #7383
+ * fix(document): support setting `flattenMaps` option for `toObject()` and `toJSON()` at schema level #7274
+ * fix(query): handle merging objectids with `.where()` #7360
+ * fix(schema): copy `.base` when cloning #7377
+ * docs: remove links to plugins.mongoosejs.com in favor of plugins.mongoosejs.io #7364
+
+5.4.1 / 2018-12-26
+==================
+ * fix(document): ensure doc array defaults get casted #7337
+ * fix(document): make `save()` not crash if nested doc has a property 'get' #7316
+ * fix(schema): allow using Schema.Types.Map as well as Map to declare a map type #7305
+ * fix(map): make set after init mark correct path as modified #7321
+ * fix(mongoose): don't recompile model if same collection and schema passed in to `mongoose.model()` #5767
+ * fix(schema): improve error message when type is invalid #7303
+ * fix(schema): add `populated` to reserved property names #7317
+ * fix(model): don't run built-in middleware on custom methods and ensure timestamp hooks don't run if children don't have timestamps set #7342
+ * docs(schematypes): clarify that you can add arbitrary options to a SchemaType #7340
+ * docs(mongoose): clarify that passing same name+schema to `mongoose.model()` returns the model #5767
+ * docs(index): add useNewUrlParser to example #7368 [JIBIN-P](https://github.com/JIBIN-P)
+ * docs(connection): add useNewUrlParser to examples #7362 [JIBIN-P](https://github.com/JIBIN-P)
+ * docs(discriminators): add back missing example from 'recursive embedded discriminators section' #7349
+ * docs(schema): improve docs for string and boolean cast() #7351
+
+5.4.0 / 2018-12-14
+==================
+ * feat(schematype): add `SchemaType.get()`, custom getters across all instances of a schematype #6912
+ * feat(schematype): add `SchemaType.cast()`, configure casting for individual schematypes #7045
+ * feat(schematype): add `SchemaType.checkRequired()`, configure what values pass `required` check for a schematype #7186 #7150
+ * feat(model): add `Model.findOneAndReplace()` #7162
+ * feat(model): add `Model.events` emitter that emits all `error`'s that occur with a given model #7125
+ * feat(populate): add `count` option to populate virtuals, support returning # of populated docs instead of docs themselves #4469
+ * feat(aggregate): add `.catch()` helper to make aggregations full thenables #7267
+ * feat(query): add hooks for `deleteOne()` and `deleteMany()` #7195
+ * feat(document): add hooks for `updateOne()` #7133
+ * feat(query): add `Query#map()` for synchronously transforming results before post middleware runs #7142
+ * feat(schema): support passing an array of objects or schemas to `Schema` constructor #7218
+ * feat(populate): add `clone` option to ensure multiple docs don't share the same populated doc #3258
+ * feat(query): add `Query#maxTimeMS()` helper #7254
+ * fix(query): deprecate broken `Aggregate#addCursorFlag()` #7120
+ * docs(populate): fix incorrect example #7335 [zcfan](https://github.com/zcfan)
+ * docs(middleware): add `findOneAndDelete` to middleware list #7327 [danielkesselberg](https://github.com/danielkesselberg)
+
+5.3.16 / 2018-12-11
+===================
+ * fix(document): handle `__proto__` in queries #7290
+ * fix(document): use Array.isArray() instead of checking constructor name for arrays #7290
+ * docs(populate): add section about what happens when no document matches #7279
+ * fix(mongoose): avoid crash on `import mongoose, {Schema} from 'mongoose'` #5648
+
+5.3.15 / 2018-12-05
+===================
+ * fix(query): handle `orFail()` with `findOneAndUpdate()` and `findOneAndDelete()` #7297 #7280
+ * fix(document): make `save()` succeed if strict: false with a `collection` property #7276
+ * fix(document): add `flattenMaps` option for toObject() #7274
+ * docs(document): document flattenMaps option #7274
+ * fix(populate): support populating individual subdoc path in document array #7273
+ * fix(populate): ensure `model` option overrides `refPath` #7273
+ * fix(map): don't call subdoc setters on init #7272
+ * fix(document): use internal get() helper instead of lodash.get to support `null` projection param #7271
+ * fix(document): continue running validateSync() for all elements in doc array after first error #6746
+
+5.3.14 / 2018-11-27
+===================
+ * docs(api): use `openUri()` instead of legacy `open()` #7277 [artemjackson](https://github.com/artemjackson)
+ * fix(document): don't mark date underneath single nested as modified if setting to string #7264
+ * fix(update): set timestamps on subdocs if not using $set with no overwrite #7261
+ * fix(document): use symbol instead of `__parent` so user code doesn't conflict #7230
+ * fix(mongoose): allow using `mongoose.model()` without context, like `import {model} from 'mongoose'` #3768
+
+5.3.13 / 2018-11-20
+===================
+ * fix: bump mongodb driver -> 3.1.10 #7266
+ * fix(populate): support setting a model as a `ref` #7253
+ * docs(schematype): add ref() function to document what is a valid `ref` path in a schematype #7253
+ * fix(array): clean modified subpaths when calling `splice()` #7249
+ * docs(compatibility): don't show Mongoose 4.11 as compatible with MongoDB 3.6 re: MongoDB's official compatibility table #7248 [a-harrison](https://github.com/a-harrison)
+ * fix(document): report correct validation error if doc array set to primitive #7242
+ * fix(mongoose): print warning when including server-side lib with jest jsdom environment #7240
+
+5.3.12 / 2018-11-13
+===================
+ * docs(compatibility): don't show Mongoose 4.11 as compatible with MongoDB 3.6 re: MongoDB's official compatibility table #7238 [a-harrison](https://github.com/a-harrison)
+ * fix(populate): use `instanceof` rather than class name for comparison #7237 [ivanseidel](https://github.com/ivanseidel)
+ * docs(api): make options show up as a nested list #7232
+ * fix(document): don't mark array as modified on init if doc array has default #7227
+ * docs(api): link to bulk write result object in `bulkWrite()` docs #7225
+
+5.3.11 / 2018-11-09
+===================
+ * fix(model): make parent pointers non-enumerable so they don't crash JSON.stringify() #7220
+ * fix(document): allow saving docs with nested props with '.' using `checkKeys: false` #7144
+ * docs(lambda): use async/await with lambda example #7019
+
+5.3.10 / 2018-11-06
+===================
+ * fix(discriminator): support reusing a schema for multiple discriminators #7200
+ * fix(cursor): handle `lean(false)` correctly with query cursors #7197
+ * fix(document): avoid manual populate if `ref` not set #7193
+ * fix(schema): handle schema without `.base` for browser build #7170
+ * docs: add further reading section
+
+5.3.9 / 2018-11-02
+==================
+ * fix: upgrade bson dep -> 1.1.0 to match mongodb-core #7213 [NewEraCracker](https://github.com/NewEraCracker)
+ * docs(api): fix broken anchor link #7210 [gfranco93](https://github.com/gfranco93)
+ * fix: don't set parent timestamps because a child has timestamps set to false #7203 #7202 [lineus](https://github.com/lineus)
+ * fix(document): run setter only once when doing `.set()` underneath a single nested subdoc #7196
+ * fix(document): surface errors in subdoc pre validate #7187
+ * fix(query): run default functions after hydrating the loaded document #7182
+ * fix(query): handle strictQuery: 'throw' with nested path correctly #7178
+ * fix(update): update timestamps on replaceOne() #7152
+ * docs(transactions): add example of aborting a transaction #7113
+
+5.3.8 / 2018-10-30
+==================
+ * fix: bump mongodb driver -> 3.1.8 to fix connecting to +srv uri with no credentials #7191 #6881 [lineus](https://github.com/lineus)
+ * fix(document): sets defaults correctly in child docs with projection #7159
+ * fix(mongoose): handle setting custom type on a separate mongoose global #7158
+ * fix: add unnecessary files to npmignore #7157
+ * fix(model): set session when creating new subdoc #7104
+
+5.3.7 / 2018-10-26
+==================
+ * fix(browser): fix buffer usage in browser build #7184 #7173 [lineus](https://github.com/lineus)
+ * fix(document): make depopulate() work on populate virtuals and unpopulated docs #7180 #6075 [lineus](https://github.com/lineus)
+ * fix(document): only pass properties as 2nd arg to custom validator if `propsParameter` set #7145
+ * docs(schematypes): add note about nested paths with `type` getting converted to mixed #7143
+ * fix(update): run update validators on nested doc when $set on an array #7135
+ * fix(update): copy exact errors from array subdoc validation into top-level update validator error #7135
+
+5.3.6 / 2018-10-23
+==================
+ * fix(cursor): fix undefined transforms error
+
+5.3.5 / 2018-10-22
+==================
+ * fix(model): make sure versionKey on `replaceOne()` always gets set at top level to prevent cast errors #7138
+ * fix(cursor): handle non-boolean lean option in `eachAsync()` #7137
+ * fix(update): correct cast update that overwrites a map #7111
+ * fix(schema): handle arrays of mixed correctly #7109
+ * fix(query): use correct path when getting schema for child timestamp update #7106
+ * fix(document): make `$session()` propagate sessions to child docs #7104
+ * fix(document): handle user setting `schema.options.strict = 'throw'` #7103
+ * fix(types): use core Node.js buffer prototype instead of safe-buffer because safe-buffer is broken for Node.js 4.x #7102
+ * fix(document): handle setting single doc with refPath to document #7070
+ * fix(model): handle array filters when updating timestamps for subdocs #7032
+
+5.3.4 / 2018-10-15
+==================
+ * fix(schema): make `add()` and `remove()` return the schema instance #7131 [lineus](https://github.com/lineus)
+ * fix(query): don't require passing model to `cast()` #7118
+ * fix: support `useFindAndModify` as a connection-level option #7110 [lineus](https://github.com/lineus)
+ * fix(populate): handle plus path projection with virtual populate #7050
+ * fix(schema): allow using ObjectId type as schema path type #7049
+ * docs(schematypes): elaborate on how schematypes relate to types #7049
+ * docs(deprecations): add note about gridstore deprecation #6922
+ * docs(guide): add storeSubdocValidationError option to guide #6802
+
+5.3.3 / 2018-10-12
+==================
+ * fix(document): enable storing mongoose validation error in MongoDB by removing `$isValidatorError` property #7127
+ * docs(api): clarify that aggregate triggers aggregate middleware #7126 [lineus](https://github.com/lineus)
+ * fix(connection): handle Model.init() when an index exists on schema + autoCreate == true #7122 [jesstelford](https://github.com/jesstelford)
+ * docs(middleware): explain how to switch between document and query hooks for `remove()` #7093
+ * docs(api): clean up encoding issues in SchemaType.prototype.validate docs #7091
+ * docs(schema): add schema types to api docs and update links on schematypes page #7080 #7076 [lineus](https://github.com/lineus)
+ * docs(model): expand model constructor docs with examples and `fields` param #7077
+ * docs(aggregate): remove incorrect description of noCursorTimeout and add description of aggregate options #7056
+ * docs: re-add array type to API docs #7027
+ * docs(connections): add note about `members.host` errors due to bad host names in replica set #7006
+
+5.3.2 / 2018-10-07
+==================
+ * fix(query): make sure to return correct result from `orFail()` #7101 #7099 [gsandorx](https://github.com/gsandorx)
+ * fix(schema): handle `{ timestamps: false }` correctly #7088 #7074 [lineus](https://github.com/lineus)
+ * docs: fix markdown in options.useCreateIndex documentation #7085 [Cyral](https://github.com/Cyral)
+ * docs(schema): correct field name in timestamps example #7082 [kizmo04](https://github.com/kizmo04)
+ * docs(migrating_to_5): correct markdown syntax #7078 [gwuah](https://github.com/gwuah)
+ * fix(connection): add useFindAndModify option in connect #7059 [NormanPerrin](https://github.com/NormanPerrin)
+ * fix(document): dont mark single nested path as modified if setting to the same value #7048
+ * fix(populate): use WeakMap to track lean populate models rather than leanPopulateSymbol #7026
+ * fix(mongoose): avoid unhandled rejection when `mongoose.connect()` errors with a callback #6997
+ * fix(mongoose): isolate Schema.Types between custom Mongoose instances #6933
+
+5.3.1 / 2018-10-02
+==================
+ * fix(ChangeStream): expose driver's `close()` function #7068 #7022 [lineus](https://github.com/lineus)
+ * fix(model): avoid printing warning if `_id` index is set to "hashed" #7053
+ * fix(populate): handle nested populate underneath lean array correctly #7052
+ * fix(update): make timestamps not crash on a null or undefined update #7041
+ * docs(schematypes+validation): clean up links from validation docs to schematypes docs #7040
+ * fix(model): apply timestamps to nested docs in bulkWrite() #7032
+ * docs(populate): rewrite refPath docs to be simpler and more direct #7013
+ * docs(faq): explain why destructuring imports are not supported in FAQ #7009
+
+5.3.0 / 2018-09-28
+==================
+ * feat(mongoose): support `mongoose.set('debug', WritableStream)` so you can pipe debug to stderr, file, or network #7018
+ * feat(query): add useNestedStrict option #6973 #5144 [lineus](https://github.com/lineus)
+ * feat(query): add getPopulatedPaths helper to Query.prototype #6970 #6677 [lineus](https://github.com/lineus)
+ * feat(model): add `createCollection()` helper to make transactions easier #6948 #6711 [Fonger](https://github.com/Fonger)
+ * feat(schema): add ability to do `schema.add(otherSchema)` to merge hooks, virtuals, etc. #6897
+ * feat(query): add `orFail()` helper that throws an error if no documents match `filter` #6841
+ * feat(mongoose): support global toObject and toJSON #6815
+ * feat(connection): add deleteModel() to remove a model from a connection #6813
+ * feat(mongoose): add top-level mongoose.ObjectId, mongoose.Decimal128 for easier schema declarations #6760
+ * feat(aggregate+query): support for/await/of (async iterators) #6737
+ * feat(mongoose): add global `now()` function that you can stub for testing timestamps #6728
+ * feat(schema): support `schema.pre(RegExp, fn)` and `schema.post(RegExp, fn)` #6680
+ * docs(query): add better docs for the `mongooseOptions()` function #6677
+ * feat(mongoose): add support for global strict object #6858
+ * feat(schema+mongoose): add autoCreate option to automatically create collections #6489
+ * feat(update): update timestamps on nested subdocs when using `$set` #4412
+ * feat(query+schema): add query `remove` hook and ability to switch between query `remove` and document `remove` middleware #3054
+
+5.2.18 / 2018-09-27
+===================
+ * docs(migrating_to_5): add note about overwriting filter properties #7030
+ * fix(query): correctly handle `select('+c')` if c is not in schema #7017
+ * fix(document): check path exists before checking for required #6974
+ * fix(document): retain user-defined key order on initial set with nested docs #6944
+ * fix(populate): handle multiple localFields + foreignFields using `localField: function() {}` syntax #5704
+
+5.2.17 / 2018-09-21
+===================
+ * docs(guide): clarify that Mongoose only increments versionKey on `save()` and add a workaround for `findOneAndUpdate()` #7038
+ * fix(model): correctly handle `createIndex` option to `ensureIndexes()` #7036 #6922 [lineus](https://github.com/lineus)
+ * docs(migrating_to_5): add a note about changing debug output from stderr to stdout #7034 #7018 [lineus](https://github.com/lineus)
+ * fix(query): add `setUpdate()` to allow overwriting update without changing op #7024 #7012 [lineus](https://github.com/lineus)
+ * fix(update): find top-level version key even if there are `$` operators in the update #7003
+ * docs(model+query): explain which operators `count()` supports that `countDocuments()` doesn't #6911
+
+5.2.16 / 2018-09-19
+===================
+ * fix(index): use dynamic require only when needed for better webpack support #7014 #7010 [jaydp17](https://github.com/jaydp17)
+ * fix(map): handle arrays of mixed maps #6995
+ * fix(populate): leave justOne as null if populating underneath a Mixed type #6985
+ * fix(populate): add justOne option to allow overriding any bugs with justOne #6985
+ * fix(query): add option to skip adding timestamps to an update #6980
+ * docs(model+schematype): improve docs about background indexes and init() #6966
+ * fix: bump mongodb -> 3.1.6 to allow connecting to srv url without credentials #6955 #6881 [lineus](https://github.com/lineus)
+ * fix(connection): allow specifying `useCreateIndex` at the connection level, overrides global-level #6922
+ * fix(schema): throw a helpful error if setting `ref` to an invalid value #6915
+
+5.2.15 / 2018-09-15
+===================
+ * fix(populate): handle virtual justOne correctly if it isn't set #6988
+ * fix(populate): consistently use lowercase `model` instead of `Model` so double-populating works with existing docs #6978
+ * fix(model): allow calling `Model.init()` again after calling `dropDatabase()` #6967
+ * fix(populate): find correct justOne when double-populating underneath an array #6798
+ * docs(webpack): make webpack docs use es2015 preset for correct libs and use acorn to test output is valid ES5 #6740
+ * fix(populate): add selectPopulatedPaths option to opt out of auto-adding `populate()`-ed fields to `select()` #6546
+ * fix(model): set timestamps on bulkWrite `insertOne` and `replaceOne` #5708
+
+5.2.14 / 2018-09-09
+===================
+ * docs: fix wording on promise docs to not imply queries only return promises #6983 #6982 [lineus](https://github.com/lineus)
+ * fix(map): throw TypeError if keys are not string #6956
+ * fix(document): ensure you can `validate()` a child doc #6931
+ * fix(populate): avoid cast error if refPath points to localFields with 2 different types #6870
+ * fix(populate): handle populating already-populated paths #6839
+ * fix(schematype): make ObjectIds handle refPaths when checking required #6714
+ * fix(model): set timestamps on bulkWrite() updates #5708
+
+5.2.13 / 2018-09-04
+===================
+ * fix(map): throw TypeError if keys are not string #6968 [Fonger](https://github.com/Fonger)
+ * fix(update): make array op casting work with strict:false and {} #6962 #6952 [lineus](https://github.com/lineus)
+ * fix(document): add doc.deleteOne(), doc.updateOne(), doc.replaceOne() re: deprecation warnings #6959 #6940 [lineus](https://github.com/lineus)
+ * docs(faq+schematypes): add note about map keys needing to be strings #6957 #6956 [lineus](https://github.com/lineus)
+ * fix(schematype): remove unused if statement #6950 #6949 [cacothi](https://github.com/cacothi)
+ * docs: add /docs/deprecations.html for dealing with MongoDB driver deprecation warnings #6922
+ * fix(populate): handle refPath where first element in array has no refPath #6913
+ * fix(mongoose): allow setting useCreateIndex option after creating a model but before initial connection succeeds #6890
+ * fix(updateValidators): ensure $pull validators always get an array #6889
+
+5.2.12 / 2018-08-30
+===================
+ * fix(document): disallow setting `constructor` and `prototype` if strict mode false
+
+4.13.17 / 2018-08-30
+====================
+ * fix(document): disallow setting `constructor` and `prototype` if strict mode false
+
+5.2.11 / 2018-08-30
+===================
+ * fix(document): disallow setting __proto__ if strict mode false
+ * fix(document): run document middleware on docs embedded in maps #6945 #6938 [Fonger](https://github.com/Fonger)
+ * fix(query): make castForQuery return a CastError #6943 #6927 [lineus](https://github.com/lineus)
+ * fix(query): use correct `this` scope when casting query with legacy 2dsphere pairs defined in schema #6939 #6937 [Fonger](https://github.com/Fonger)
+ * fix(document): avoid crash when calling `get()` on deeply nested subdocs #6929 #6925 [jakemccloskey](https://github.com/jakemccloskey)
+ * fix(plugins): make saveSubdocs execute child post save hooks _after_ the actual save #6926
+ * docs: add dbName to api docs for .connect() #6923 [p722](https://github.com/p722)
+ * fix(populate): convert to array when schema specifies array, even if doc doesn't have an array #6908
+ * fix(populate): handle `justOne` virtual populate underneath array #6867
+ * fix(model): dont set versionKey on upsert if it is already `$set` #5973
+
+4.13.16 / 2018-08-30
+====================
+ * fix(document): disallow setting __proto__ if strict mode false
+ * feat(error): backport adding modified paths to VersionError #6928 [freewil](https://github.com/freewil)
+
+5.2.10 / 2018-08-27
+===================
+ * fix: bump mongodb driver -> 3.1.4 #6920 #6903 #6884 #6799 #6741 [Fonger](https://github.com/Fonger)
+ * fix(model): track `session` option for `save()` as the document's `$session()` #6909
+ * fix(query): add Query.getOptions() helper #6907 [Fonger](https://github.com/Fonger)
+ * fix(document): ensure array atomics get cleared after save() #6900
+ * fix(aggregate): add missing redact and readConcern helpers #6895 [Fonger](https://github.com/Fonger)
+ * fix: add global option `mongoose.set('useCreateIndex', true)` to avoid ensureIndex deprecation warning #6890
+ * fix(query): use `projection` option to avoid deprecation warnings #6888 #6880 [Fonger](https://github.com/Fonger)
+ * fix(query): use `findOneAndReplace()` internally if using `overwrite: true` with `findOneAndUpdate()` #6888 [Fonger](https://github.com/Fonger)
+ * fix(document): ensure required cache gets cleared correctly between subsequent saves #6892
+ * fix(aggregate): support session chaining correctly #6886 #6885 [Fonger](https://github.com/Fonger)
+ * fix(query): use `projection` instead of `fields` internally for `find()` and `findOne()` to avoid deprecation warning #6880
+ * fix(populate): add `getters` option to opt in to calling getters on populate #6844
+
+5.2.9 / 2018-08-17
+==================
+ * fix(document): correctly propagate write concern options in save() #6877 [Fonger](https://github.com/Fonger)
+ * fix: upgrade mongodb driver -> 3.1.3 for numerous fixes #6869 #6843 #6692 #6670 [simllll](https://github.com/simllll)
+ * fix: correct `this` scope of default functions for DocumentArray and Array #6868 #6840 [Fonger](https://github.com/Fonger)
+ * fix(types): support casting JSON form of buffers #6866 #6863 [Fonger](https://github.com/Fonger)
+ * fix(query): get global runValidators option correctly #6865 #6578
+ * fix(query): add Query.prototype.setQuery() analogous to `getQuery()` #6855 #6854
+ * docs(connections): add note about the `family` option for IPv4 vs IPv6 and add port to example URIs #6784
+ * fix(query): get global runValidators option correctly #6578
+
+4.13.15 / 2018-08-14
+====================
+ * fix(mongoose): add global `usePushEach` option for easier Mongoose 4.x + MongoDB 3.6 #6858
+ * chore: fix flakey tests for 4.x #6853 [Fonger](https://github.com/Fonger)
+ * feat(error): add version number to VersionError #6852 [freewil](https://github.com/freewil)
+
+5.2.8 / 2018-08-13
+==================
+ * docs: update `execPopulate()` code example #6851 [WJakub](https://github.com/WJakub)
+ * fix(document): allow passing callback to `execPopulate()` #6851
+ * fix(populate): populate with undefined fields without error #6848 #6845 [Fonger](https://github.com/Fonger)
+ * docs(migrating_to_5): Add `objectIdGetter` option docs #6842 [jwalton](https://github.com/jwalton)
+ * chore: run lint in parallel and only on Node.js v10 #6836 [Fonger](https://github.com/Fonger)
+ * fix(populate): throw helpful error if refPath excluded in query #6834
+ * docs(migrating_to_5): add note about removing runSettersOnQuery #6832
+ * fix: use safe-buffer to avoid buffer deprecation errors in Node.js 10 #6829 [Fonger](https://github.com/Fonger)
+ * docs(query): fix broken links #6828 [yaynick](https://github.com/yaynick)
+ * docs(defaults): clarify that defaults only run on undefined #6827
+ * chore: fix flakey tests #6824 [Fonger](https://github.com/Fonger)
+ * docs: fix custom inspect function deprecation warning in Node.js 10 #6821 [yelworc](https://github.com/yelworc)
+ * fix(document): ensure subdocs get set to init state after save() so validators can run again #6818
+ * fix(query): make sure embedded query casting always throws a CastError #6803
+ * fix(document): ensure `required` function only gets called once when validating #6801
+ * docs(connections): note that you must specify port if using `useNewUrlParser: true` #6789
+ * fix(populate): support `options.match` in virtual populate schema definition #6787
+ * fix(update): strip out virtuals from updates if strict: 'throw' rather than returning an error #6731
+
+5.2.7 / 2018-08-06
+==================
+ * fix(model): check `expireAfterSeconds` option when diffing indexes in syncIndexes() #6820 #6819 [christopherhex](https://github.com/christopherhex)
+ * chore: fix some common test flakes in travis #6816 [Fonger](https://github.com/Fonger)
+ * chore: bump eslint and webpack to avoid bad versions of eslint-scope #6814
+ * test(model): add delay to session tests to improve pass rate #6811 [Fonger](https://github.com/Fonger)
+ * fix(model): support options in `deleteMany` #6810 [Fonger](https://github.com/Fonger)
+ * fix(query): don't use $each when pushing an array into an array #6809 [lineus](https://github.com/lineus)
+ * chore: bump mquery so eslint isn't a prod dependency #6800
+ * fix(populate): correctly get schema type when calling `populate()` on already populated path #6798
+ * fix(populate): propagate readConcern options in populate from parent query #6792 #6785 [Fonger](https://github.com/Fonger)
+ * docs(connection): add description of useNewUrlParser option #6789
+ * fix(query): make select('+path') a no-op if no select prop in schema #6785
+ * docs(schematype+validation): document using function syntax for custom validator message #6772
+ * fix(update): throw CastError if updating with `$inc: null` #6770
+ * fix(connection): throw helpful error when calling `createConnection(undefined)` #6763
+
+5.2.6 / 2018-07-30
+==================
+ * fix(document): don't double-call deeply nested custom getters when using `get()` #6779 #6637
+ * fix(query): upgrade mquery for readConcern() helper #6777
+ * docs(schematypes): clean up typos #6773 [sajadtorkamani](https://github.com/sajadtorkamani)
+ * refactor(browser): fix webpack warnings #6771 #6705
+ * fix(populate): make error reported when no `localField` specified catchable #6767
+ * docs(connection): use correct form in createConnection example #6766 [lineus](https://github.com/lineus)
+ * fix(connection): throw helpful error when using legacy `mongoose.connect()` syntax #6756
+ * fix(document): handle overwriting `$session` in `execPopulate()` #6754
+ * fix(query): propagate top-level session down to `populate()` #6754
+ * fix(aggregate): add `session()` helper for consistency with query api #6752
+ * fix(map): avoid infinite recursion when update overwrites a map #6750
+ * fix(model): be consistent about passing noop callback to mongoose.model() `init()` as well as db.model() #6707
+
+5.2.5 / 2018-07-23
+==================
+ * fix(boolean): expose `convertToTrue` and `convertToFalse` for custom boolean casting #6758
+ * docs(schematypes): add note about what values are converted to booleans #6758
+ * fix(document): fix(document): report castError when setting single nested doc to array #6753
+ * docs: prefix mongoose.Schema call with new operator #6751 [sajadtorkamani](https://github.com/sajadtorkamani)
+ * docs(query): add examples and links to schema writeConcern option for writeConcern helpers #6748
+ * docs(middleware): clarify that init middleware is sync #6747
+ * perf(model): create error rather than modifying stack for source map perf #6735
+ * fix(model): throw helpful error when passing object to aggregate() #6732
+ * fix(model): pass Model instance as context to applyGetters when calling getters for virtual populate #6726 [lineus](https://github.com/lineus)
+ * fix(documentarray): remove `isNew` and `save` listeners on CastError because otherwise they never get removed #6723
+ * docs(model+query): clarify when to use `countDocuments()` vs `estimatedDocumentCount()` #6713
+ * fix(populate): correctly set virtual nestedSchemaPath when calling populate() multiple times #6644
+ * docs(connections): add note about the `family` option for IPv4 vs IPv6 and add port to example URIs #6566
+
+5.2.4 / 2018-07-16
+==================
+ * docs: Model.insertMany rawResult option in api docs #6724 [lineus](https://github.com/lineus)
+ * docs: fix typo on migrating to 5 guide #6722 [iagowp](https://github.com/iagowp)
+ * docs: update doc about keepalive #6719 #6718 [simllll](https://github.com/simllll)
+ * fix: ensure debug mode doesn't crash with sessions #6712
+ * fix(document): report castError when setting single nested doc to primitive value #6710
+ * fix(connection): throw helpful error if using `new db.model(foo)(bar)` #6698
+ * fix(model): throw readable error with better stack trace when non-cb passed to $wrapCallback() #6640
+
+5.2.3 / 2018-07-11
+==================
+ * fix(populate): if a getter is defined on the localField, use it when populating #6702 #6618 [lineus](https://github.com/lineus)
+ * docs(schema): add example of nested aliases #6671
+ * fix(query): add `session()` function to queries to avoid positional argument mistakes #6663
+ * docs(transactions): use new session() helper to make positional args less confusing #6663
+ * fix(query+model+schema): add support for `writeConcern` option and writeConcern helpers #6620
+ * docs(guide): add `writeConcern` option and re-add description for `safe` option #6620
+ * docs(schema): fix broken API links #6619
+ * docs(connections): add information re: socketTimeoutMS and connectTimeoutMS #4789
+
+5.2.2 / 2018-07-08
+==================
+ * fix(model+query): add missing estimatedDocumentCount() function #6697
+ * docs(faq): improve array-defaults section #6695 [lineus](https://github.com/lineus)
+ * docs(model): fix countDocuments() docs, bad copy/paste from count() docs #6694 #6643
+ * fix(connection): add `startSession()` helper to connection and mongoose global #6689
+ * fix: upgrade mongodb driver -> 3.1.1 for countDocuments() fix #6688 #6666
+ * docs(compatibility): add MongoDB 4 range #6685
+ * fix(populate): add ability to define refPath as a function #6683 [lineus](https://github.com/lineus)
+ * docs: add rudimentary transactions guide #6672
+ * fix(update): make setDefaultsOnInsert handle nested subdoc updates with deeply nested defaults #6665
+ * docs: use latest acquit-ignore to handle examples that start with acquit:ignore:start #6657
+ * fix(validation): format `properties.message` as well as `message` #6621
+
+5.2.1 / 2018-07-03
+==================
+ * fix(connection): allow setting the mongodb driver's useNewUrlParser option, default to false #6656 #6648 #6647
+ * fix(model): only warn on custom _id index if index only has _id key #6650
+
+5.2.0 / 2018-07-02
+==================
+ * feat(model): add `countDocuments()` #6643
+ * feat(model): make ensureIndexes() fail if specifying an index on _id #6605
+ * feat(mongoose): add `objectIdGetter` option to remove ObjectId.prototype._id #6588
+ * feat: upgrade mongodb -> 3.1.0 for full MongoDB 4.0 support #6579
+ * feat(query): support `runValidators` as a global option #6578
+ * perf(schema): use WeakMap instead of array for schema stack #6503
+ * feat(model): decorate unique discriminator indexes with partialFilterExpressions #6347
+ * feat(model): add `syncIndexes()`, drops indexes that aren't in schema #6281
+ * feat(document): add default getter/setter if virtual doesn't have one #6262
+ * feat(discriminator): support discriminators on nested doc arrays #6202
+ * feat(update): add `Query.prototype.set()` #5770
+
+5.1.8 / 2018-07-02
+==================
+ * fix: don't throw TypeError if calling save() after original save() failed with push() #6638 [evanhenke](https://github.com/evanhenke)
+ * fix(query): add explain() helper and don't hydrate explain output #6625
+ * docs(query): fix `setOptions()` lists #6624
+ * docs: add geojson docs #6607
+ * fix: bump mongodb -> 3.0.11 to avoid cyclic dependency error with retryWrites #6109
+
+5.1.7 / 2018-06-26
+==================
+ * docs: add npm badge to readme #6623 [VFedyk](https://github.com/VFedyk)
+ * fix(document): don't throw parallel save error if post save hooks in parallel #6614 #6611 [lineus](https://github.com/lineus)
+ * fix(populate): allow dynamic ref to handle >1 model getModelsMapForPopulate #6613 #6612 [jimmytsao](https://github.com/jimmytsao)
+ * fix(document): handle `push()` on triple nested document array #6602
+ * docs(validation): improve update validator doc headers #6577 [joeytwiddle](https://github.com/joeytwiddle)
+ * fix(document): handle document arrays in `modifiedPaths()` with includeChildren option #5904
+
+5.1.6 / 2018-06-19
+==================
+ * fix: upgrade mongodb -> 3.0.10
+ * docs(model+document): clarify that `save()` returns `undefined` if passed a callback #6604 [lineus](https://github.com/lineus)
+ * fix(schema): apply alias when adding fields with .add() #6593
+ * docs: add full list of guides and streamline nav #6592
+ * docs(model): add `projection` option to `findOneAndUpdate()` #6590 [lineus](https://github.com/lineus)
+ * docs: support @static JSDoc declaration #6584
+ * fix(query): use boolean casting logic for $exists #6581
+ * fix(query): cast all $text options to correct values #6581
+ * fix(model): add support synchronous pre hooks for `createModel` #6552 [profbiss](https://github.com/profbiss)
+ * docs: add note about the `applyPluginsToDiscriminators` option #4965
+
+5.1.5 / 2018-06-11
+==================
+ * docs(guide): rework query helper example #6575 [lineus](https://github.com/lineus)
+ * fix(populate): handle virtual populate with embedded discriminator under single nested subdoc #6571
+ * docs: add string option to projections that call query select #6563 [lineus](https://github.com/lineus)
+ * style: use ES6 in collection.js #6560 [l33ds](https://github.com/l33ds)
+ * fix(populate): add virtual ref function ability getModelsMapForPopulate #6559 #6554 [lineus](https://github.com/lineus)
+ * docs(queries): fix link #6557 [sun1x](https://github.com/sun1x)
+ * fix(schema): rename indexes -> getIndexes to avoid webpack duplicate declaration #6547
+ * fix(document): support `toString()` as custom method #6538
+ * docs: add @instance for instance methods to be more compliant with JSDoc #6516 [treble-snake](https://github.com/treble-snake)
+ * fix(populate): avoid converting to map when using mongoose-deep-populate #6460
+ * docs(browser): create new browser docs page #6061
+
+5.1.4 / 2018-06-04
+==================
+ * docs(faq): add hr tags for parallel save error #6550 [lineus](https://github.com/lineus)
+ * docs(connection): fix broken link #6545 [iblamefish](https://github.com/iblamefish)
+ * fix(populate): honor subpopulate options #6539 #6528 [lineus](https://github.com/lineus)
+ * fix(populate): allow population of refpath under array #6537 #6509 [lineus](https://github.com/lineus)
+ * fix(query): dont treat $set the same as the other ops in update casting #6535 [lineus](https://github.com/lineus)
+ * fix: bump async -> 2.6.1 #6534 #6505 [lineus](https://github.com/lineus)
+ * fix: support using a function as validation error message #6530 [lucandrade](https://github.com/lucandrade)
+ * fix(populate): propagate `lean()` down to subpopulate #6498 [AbdelrahmanHafez](https://github.com/AbdelrahmanHafez)
+ * docs(lambda): add info on what happens if database does down between lambda function calls #6409
+ * fix(update): allow updating embedded discriminator path if discriminator key is in filter #5841
+
+5.1.3 / 2018-05-28
+==================
+ * fix(document): support set() on path underneath array embedded discriminator #6526
+ * chore: update lodash and nsp dev dependencies #6514 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix(document): throw readable error when saving the same doc instance more than once in parallel #6511 #6456 #4064 [lineus](https://github.com/lineus)
+ * fix(populate): set correct nestedSchemaPath for virtual underneath embedded discriminator #6501 #6487 [lineus](https://github.com/lineus)
+ * docs(query): add section on promises and warning about not mixing promises and callbacks #6495
+ * docs(connection): add concrete example of connecting to multiple hosts #6492 [lineus](https://github.com/lineus)
+ * fix(populate): handle virtual populate under single nested doc under embedded discriminator #6488
+ * fix(schema): collect indexes from embedded discriminators for autoIndex build #6485
+ * fix(document): handle `doc.set()` underneath embedded discriminator #6482
+ * fix(document): handle set() on path under embedded discriminator with object syntax #6482
+ * fix(document): handle setting nested property to object with only non-schema properties #6436
+
+4.13.14 / 2018-05-25
+====================
+ * fix(model): handle retainKeyOrder option in findOneAndUpdate() #6484
+
+5.1.2 / 2018-05-21
+==================
+ * docs(guide): add missing SchemaTypes #6490 [distancesprinter](https://github.com/distancesprinter)
+ * fix(map): make MongooseMap.toJSON return a serialized object #6486 #6478 [lineus](https://github.com/lineus)
+ * fix(query): make CustomQuery inherit from model.Query for hooks #6483 #6455 [lineus](https://github.com/lineus)
+ * fix(document): prevent default falses from being skipped by $__dirty #6481 #6477 [lineus](https://github.com/lineus)
+ * docs(connection): document `useDb()` #6480
+ * fix(model): skip redundant clone in insertMany #6479 [d1manson](https://github.com/d1manson)
+ * fix(aggregate): let replaceRoot accept objects as well as strings #6475 #6474 [lineus](https://github.com/lineus)
+ * docs(model): clarify `emit()` in mapReduce and how map/reduce are run #6465
+ * fix(populate): flatten array to handle multi-level nested `refPath` #6457
+ * fix(date): cast small numeric strings as years #6444 [AbdelrahmanHafez](https://github.com/AbdelrahmanHafez)
+ * fix(populate): remove unmatched ids when using virtual populate on already hydrated document #6435
+ * fix(array): use custom array class to avoid clobbered property names #6431
+ * fix(model): handle hooks for custom methods that return promises #6385
+
+4.13.13 / 2018-05-17
+====================
+ * fix(update): stop clobbering $in when casting update #6441 #6339
+ * fix: upgrade async -> 2.6.0 re: security warning
+
+5.1.1 / 2018-05-14
+==================
+ * docs(schema): add notes in api and guide about schema.methods object #6470 #6440 [lineus](https://github.com/lineus)
+ * fix(error): add modified paths to VersionError #6464 #6433 [paglias](https://github.com/paglias)
+ * fix(populate): only call populate with full param signature when match is not present #6458 #6451 [lineus](https://github.com/lineus)
+ * docs: fix geoNear link in migration guide #6450 [kawache](https://github.com/kawache)
+ * fix(discriminator): throw readable error when `create()` with a non-existent discriminator key #6434
+ * fix(populate): add `retainNullValues` option to avoid stripping out null keys #6432
+ * fix(populate): handle populate in embedded discriminators underneath nested paths #6411
+ * docs(model): add change streams and ToC, make terminology more consistent #5888
+
+5.1.0 / 2018-05-10
+==================
+ * feat(ObjectId): add `_id` getter so you can get a usable id whether or not the path is populated #6415 #6115
+ * feat(model): add Model.startSession() #6362
+ * feat(document): add doc.$session() and set session on doc after query #6362
+ * feat: add Map type that supports arbitrary keys #6287 #681
+ * feat: add `cloneSchemas` option to mongoose global to opt in to always cloning schemas before use #6274
+ * feat(model): add `findOneAndDelete()` and `findByIdAndDelete()` #6164
+ * feat(document): support `$ignore()` on single nested and array subdocs #6152
+ * feat(document): add warning about calling `save()` on subdocs #6152
+ * fix(model): make `save()` use `updateOne()` instead of `update()` #6031
+ * feat(error): add version number to VersionError #5966
+ * fix(query): allow `[]` as a value for `$in` when casting #5913
+ * fix(document): avoid running validators on single nested paths if only a child path is modified #5885
+ * feat(schema): print warning if method conflicts with mongoose internals #5860
+
+5.0.18 / 2018-05-09
+===================
+ * fix(update): stop clobbering $in when casting update #6441 #6339 [lineus](https://github.com/lineus)
+ * fix: upgrade mongodb driver -> 3.0.8 to fix session issue #6437 #6357 [simllll](https://github.com/simllll)
+ * fix: upgrade bson -> 1.0.5 re: https://snyk.io/vuln/npm:bson:20180225 #6423 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix: look for `valueOf()` when casting to Decimal128 #6419 #6418 [lineus](https://github.com/lineus)
+ * fix: populate array of objects with space separated paths #6414 [lineus](https://github.com/lineus)
+ * test: add coverage for `mongoose.pluralize()` #6412 [FastDeath](https://github.com/FastDeath)
+ * fix(document): avoid running default functions on init() if path has value #6410
+ * fix(document): allow saving document with `null` id #6406
+ * fix: prevent casting of populated docs in document.init #6390 [lineus](https://github.com/lineus)
+ * fix: remove `toHexString()` helper that was added in 5.0.15 #6359
+
+5.0.17 / 2018-04-30
+===================
+ * docs(migration): certain chars in passwords may cause connection failures #6401 [markstos](https://github.com/markstos)
+ * fix(document): don't throw when `push()` on a nested doc array #6398
+ * fix(model): apply hooks to custom methods if specified #6385
+ * fix(schema): support opting out of one timestamp field but not the other for `insertMany()` #6381
+ * fix(documentarray): handle `required: true` within documentarray definition #6364
+ * fix(document): ensure `isNew` is set before default functions run on init #3793
+
+5.0.16 / 2018-04-23
+===================
+ * docs(api): sort api methods based on their string property #6374 [lineus](https://github.com/lineus)
+ * docs(connection): fix typo in `createCollection()` #6370 [mattc41190](https://github.com/mattc41190)
+ * docs(document): remove vestigial reference to `numAffected` #6367 [ekulabuhov](https://github.com/ekulabuhov)
+ * docs(schema): fix typo #6366 [dhritzkiv](https://github.com/dhritzkiv)
+ * docs(schematypes): add missing `minlength` and `maxlength` docs #6365 [treble-snake](https://github.com/treble-snake)
+ * docs(queries): fix formatting #6360 [treble-snake](https://github.com/treble-snake)
+ * docs(api): add cursors to API docs #6353 #6344 [lineus](https://github.com/lineus)
+ * docs(aggregate): remove reference to non-existent `.select()` method #6346
+ * fix(update): handle `required` array with update validators and $pull #6341
+ * fix(update): avoid setting __v in findOneAndUpdate if it is `$set` #5973
+
+5.0.15 / 2018-04-16
+===================
+ * fix: add ability for casting from number to decimal128 #6336 #6331 [lineus](https://github.com/lineus)
+ * docs(middleware): enumerate the ways to error out in a hook #6315
+ * fix(document): respect schema-level depopulate option for toObject() #6313
+ * fix: bump mongodb driver -> 3.0.6 #6310
+ * fix(number): check for `valueOf()` function to support Decimal.js #6306 #6299 [lineus](https://github.com/lineus)
+ * fix(query): run array setters on query if input value is an array #6277
+ * fix(versioning): don't require matching version when using array.pull() #6190
+ * fix(document): add `toHexString()` function so you don't need to check whether a path is populated to get an id #6115
+
+5.0.14 / 2018-04-09
+===================
+ * fix(schema): clone aliases and alternative option syntax correctly
+ * fix(query): call utils.toObject in query.count like in query.find #6325 [lineus](https://github.com/lineus)
+ * docs(populate): add middleware examples #6320 [BorntraegerMarc](https://github.com/BorntraegerMarc)
+ * docs(compatibility): fix dead link #6319 [lacivert](https://github.com/lacivert)
+ * docs(api): fix markdown parsing for parameters #6318 #6314 [lineus](https://github.com/lineus)
+ * fix(populate): handle space-delimited paths in array populate #6296 #6284 [lineus](https://github.com/lineus)
+ * fix(populate): support basic virtual populate underneath embedded discriminators #6273
+
+5.0.13 / 2018-04-05
+===================
+ * docs(faq): add middleware to faq arrow function warning #6309 [lineus](https://github.com/lineus)
+ * docs(schema): add example to loadClass() docs #6308
+ * docs: clean up misc typos #6304 [sfrieson](https://github.com/sfrieson)
+ * fix(document): apply virtuals when calling `toJSON()` on a nested path #6294
+ * refactor(connection): use `client.db()` syntax rather than double-parsing the URI #6292 #6286
+ * docs: document new behavior of required validator for arrays #6288 [daltones](https://github.com/daltones)
+ * fix(schema): treat set() options as user-provided options #6274
+ * fix(schema): clone discriminators correctly #6274
+ * fix(update): make setDefaultsOnInsert not create subdoc if only default is id #6269
+ * docs(discriminator): clarify 3rd argument to Model.discriminator() #2596
+
+5.0.12 / 2018-03-27
+===================
+ * docs(query): updating model name in query API docs #6280 [lineus](https://github.com/lineus)
+ * docs: fix typo in tests #6275 [styler](https://github.com/styler)
+ * fix: add missing `.hint()` to aggregate #6272 #6251 [lineus](https://github.com/lineus)
+ * docs(api): add headers to each API docs section for easer nav #6261
+ * fix(query): ensure hooked query functions always run on next tick for chaining #6250
+ * fix(populate): ensure populated array not set to null if it isn't set #6245
+ * fix(connection): set readyState to disconnected if initial connection fails #6244 #6131
+ * docs(model): make `create()` params show up correctly in docs #6242
+ * fix(model): make error handlers work with MongoDB server errors and `insertMany()` #6228
+ * fix(browser): ensure browser document builds defaults for embedded arrays correctly #6175
+ * fix(timestamps): set timestamps when using `updateOne()` and `updateMany()` #6282 [gualopezb](https://github.com/gualopezb)
+
+5.0.11 / 2018-03-19
+===================
+ * fix(update): handle $pull with $in in update validators #6240
+ * fix(query): don't convert undefined to null when casting so driver `ignoreUndefined` option works #6236
+ * docs(middleware): add example of using async/await with middleware #6235
+ * fix(populate): apply justOne option before `completeMany()` so it works with lean() #6234
+ * fix(query): ensure errors in user callbacks aren't caught in init #6195 #6178
+ * docs(connections): document dbName option for Atlas connections #6179
+ * fix(discriminator): make child schema nested paths overwrite parent schema paths #6076
+
+4.13.12 / 2018-03-13
+====================
+ * fix(document): make virtual get() return undefined instead of null if no getters #6223
+ * docs: fix url in useMongoClient error message #6219 #6217 [lineus](https://github.com/lineus)
+ * fix(discriminator): don't copy `discriminators` property from base schema #6122 #6064
+
+5.0.10 / 2018-03-12
+===================
+ * docs(schematype): add notes re: running setters on queries #6209
+ * docs: fix typo #6208 [kamagatos](https://github.com/kamagatos)
+ * fix(query): only call setters once on query filter props for findOneAndUpdate and findOneAndRemove #6203
+ * docs: elaborate on connection string changes in migration guide #6193
+ * fix(document): skip applyDefaults if subdoc is null #6187
+ * docs: fix schematypes docs and link to them #6176
+ * docs(faq): add FAQs re: array defaults and casting aggregation pipelines #6184 #6176 #6170 [lineus](https://github.com/lineus)
+ * fix(document): ensure primitive defaults are set and built-in default functions run before setters #6155
+ * fix(query): handle single embedded embedded discriminators in castForQuery #6027
+
+5.0.9 / 2018-03-05
+==================
+ * perf: bump mongodb -> 3.0.4 to fix SSL perf issue #6065
+
+5.0.8 / 2018-03-03
+==================
+ * docs: remove obsolete references to `emitIndexErrors` #6186 [isaackwan](https://github.com/isaackwan)
+ * fix(query): don't cast findOne() until exec() so setters don't run twice #6157
+ * fix: remove document_provider.web.js file #6186
+ * fix(discriminator): support custom discriminator model names #6100 [wentout](https://github.com/wentout)
+ * fix: support caching calls to `useDb()` #6036 [rocketspacer](https://github.com/rocketspacer)
+ * fix(query): add omitUndefined option so setDefaultsOnInsert can kick in on undefined #6034
+ * fix: upgrade mongodb -> 3.0.3 for reconnectTries: 0 blocking process exit fix #6028
+
+5.0.7 / 2018-02-23
+==================
+ * fix: support eachAsync options with aggregation cursor #6169 #6168 [vichle](https://github.com/vichle)
+ * docs: fix link to MongoDB compound indexes docs #6162 [br0p0p](https://github.com/br0p0p)
+ * docs(aggregate): use eachAsync instead of incorrect `each()` #6160 [simllll](https://github.com/simllll)
+ * chore: fix benchmarks #6158 [pradel](https://github.com/pradel)
+ * docs: remove dead link to old blog post #6154 [markstos](https://github.com/markstos)
+ * fix: don't convert dates to numbers when updating mixed path #6146 #6145 [s4rbagamble](https://github.com/s4rbagamble)
+ * feat(aggregate): add replaceRoot, count, sortByCount helpers #6142 [jakesjews](https://github.com/jakesjews)
+ * fix(document): add includedChildren flag to modifiedPaths() #6134
+ * perf: don't create wrapper function if no hooks specified #6126
+ * fix(schema): allow indexes on single nested subdocs for geoJSON #6113
+ * fix(document): allow depopulating all fields #6073
+ * feat(mongoose): add support for `useFindAndModify` option on singleton #5616
+
+5.0.6 / 2018-02-15
+==================
+ * refactor(query.castUpdate): avoid creating error until necessary #6137
+ * docs(api): fix missing api docs #6136 [lineus](https://github.com/lineus)
+ * fix(schema): copy virtuals when using `clone()` #6133
+ * fix(update): avoid digging into buffers with upsert and replaceOne #6124
+ * fix(schema): support `enum` on arrays of strings #6102
+ * fix(update): cast `$addToSet: [1, 2]` -> `$addToSet: { $each: [1, 2] }` #6086
+
+5.0.5 / 2018-02-13
+==================
+ * docs: make > show up correctly in API docs #6114
+ * fix(query): support `where()` overwriting primitive with object #6097
+ * fix(schematype): don't run internal `resetId` setter on queries with _id #6093
+ * fix(discriminator): don't copy `discriminators` property from base schema #6064
+ * fix(utils): respect `valueOf()` when merging object for update #6059
+ * docs(validation): fix typo 'maxLength' #4720
+ * fix(document): apply defaults after setting initial value so default functions don't see empty doc #3781
+
+5.0.4 / 2018-02-08
+==================
+ * docs: add lambda guide #6107
+ * fix(connection): add `dbName` option to work around `mongodb+srv` not supporting db name in URI #6106
+ * fix(schematype): fix regexp typo in ObjectId #6098 [JoshuaWise](https://github.com/JoshuaWise)
+ * perf(document): re-use the modifiedPaths list #6092 [tarun1793](https://github.com/tarun1793)
+ * fix: use console.info() instead of console.error() for debug output #6088 [yuristsepaniuk](https://github.com/yuristsepaniuk)
+ * docs(validation): clean up runValidators and isAsync options docs for 5.x #6083
+ * docs(model): use array instead of spread consistently for aggregate() #6070
+ * fix(schema): make aliases handle mongoose-lean-virtuals #6069
+ * docs(layout): add link to subdocs guide #6056
+ * fix(query): make strictQuery: true strip out fields that aren't in the schema #6032
+ * docs(guide): add notes for `strictQuery` option #6032
+
+4.13.11 / 2018-02-07
+====================
+ * docs: fix links in 4.x docs #6081
+ * chore: add release script that uses --tag for npm publish for 4.x releases #6063
+
+5.0.3 / 2018-01-31
+==================
+ * fix: consistently use process.nextTick() to avoid sinon.useFakeTimers() causing ops to hang #6074
+ * docs(aggregate): fix typo #6072 [adursun](https://github.com/adursun)
+ * chore: add return type to `mongoose.model()` docs [bryant1410](https://github.com/bryant1410)
+ * fix(document): depopulate push()-ed docs when saving #6048
+ * fix: upgrade mongodb -> 3.0.2 #6019
+
+5.0.2 / 2018-01-28
+==================
+ * fix(schema): do not overwrite default values in schema when nested timestamps are provided #6024 [cdeveas](https://github.com/cdeveas)
+ * docs: fix syntax highlighting in models.jade, schematypes.jade, subdocs.jade #6058 [lineus](https://github.com/lineus)
+ * fix: use lazy loading so we can build mongoose with webpack #5993 #5842
+ * docs(connections): clarify multi-mongos with useMongoClient for 4.x docs #5984
+ * fix(populate): handle populating embedded discriminator paths #5970
+
+4.13.10 / 2018-01-28
+====================
+ * docs(model+query): add lean() option to Model helpers #5996 [aguyinmontreal](https://github.com/aguyinmontreal)
+ * fix: use lazy loading so we can build mongoose with webpack #5993 #5842
+ * docs(connections): clarify multi-mongos with useMongoClient for 4.x docs #5984
+ * fix(populate): handle populating embedded discriminator paths #5970
+ * docs(query+aggregate): add more detail re: maxTimeMS #4066
+
+5.0.1 / 2018-01-19
+==================
+ * fix(document): make validate() not resolve to document #6014
+ * fix(model): make save() not return DocumentNotFoundError if using fire-and-forget writes #6012
+ * fix(aggregate): make options() work as advertised #6011 [spederiva](https://github.com/spederiva)
+ * docs(queries): fix code samples #6008
+
+5.0.0 / 2018-01-17
+==================
+ * test: refactor tests to use start fewer connections #5985 [fenanquin](https://github.com/fenanquin)
+ * feat: add global bufferCommands option #5879
+ * docs: new docs site and build system #5976
+ * test: increase timeout on slow test cases #5968 [fenanquin](https://github.com/fenanquin)
+ * fix: avoid casting out array filter elements #5965
+ * feat: add Model.watch() wrapper #5964
+ * chore: replace istanbul with nyc #5962 [ChristianMurphy](https://github.com/ChristianMurphy)
+
+4.13.9 / 2018-01-07
+===================
+ * chore: update marked (dev dependency) re: security vulnerability #5951 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix: upgrade mongodb -> 2.2.34 for ipv6 and autoReconnect fixes #5794 #5760
+ * docs: use useMongooseAggCursor for aggregate docs #2955
+
+5.0.0-rc2 / 2018-01-04
+======================
+ * fix: add cleaner warning about no longer needing `useMongoClient` in 5.x #5961
+ * chore: update acquit -> 0.5.1 for minor security patch #5961 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * docs: add docs for mongoose 4.x at http://mongoosejs.com/docs/4.x #5959
+ * docs: add link to migration guide #5957
+ * chore: update eslint to version 4.14.0 #5955 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * chore: update mocha to version 4.1.0 [ChristianMurphy](https://github.com/ChristianMurphy)
+
+5.0.0-rc1 / 2018-01-02
+======================
+ * fix(index): use pluralize correctly for `mongoose.model()` #5958
+ * fix: make mquery use native promises by default #5945
+ * fix(connection): ensure 'joined' and 'left' events get bubbled up #5944
+
+5.0.0-rc0 / 2017-12-28
+======================
+ * BREAKING CHANGE: always use mongoose aggregation cursor when using `.aggregate().cursor()` #5941
+ * BREAKING CHANGE: attach query middleware when compiling model #5939
+ * BREAKING CHANGE: `emitIndexErrors` is on by default, failing index build will throw uncaught error if not handled #5910
+ * BREAKING CHANGE: remove precompiled browser bundle #5895
+ * feat: add `mongoose.pluralize()` function #5877
+ * BREAKING CHANGE: remove `passRawResult` option for `findOneAndUpdate`, use `rawResult` #5869
+ * BREAKING CHANGE: implicit async validators (based on number of function args) are removed, return a promise instead #5824
+ * BREAKING CHANGE: fail fast if user sets a unique index on `_id` #5820 [varunjayaraman](https://github.com/varunjayaraman)
+ * BREAKING CHANGE: mapReduce resolves to an object with 2 keys rather than 2 separate args #5816
+ * BREAKING CHANGE: `mongoose.connect()` returns a promise, removed MongooseThenable #5796
+ * BREAKING CHANGE: query stream removed, use `cursor()` instead #5795
+ * BREAKING CHANGE: connection `open()` and `openSet()` removed, use `openUri()` instead #5795
+ * BREAKING CHANGE: use MongoDB driver 3.0.0, drop support for MongoDB server < 3.0.0 #5791 #4740
+ * BREAKING CHANGE: remove support for `$pushAll`, remove `usePushEach` option #5670
+ * BREAKING CHANGE: make date casting use native Date #5395 [varunjayaraman](https://github.com/varunjayaraman)
+ * BREAKING CHANGE: remove `runSettersOnQuery`, always run setters on query #5340
+ * BREAKING CHANGE: array of length 0 now satisfies `required: true` for arays #5139 [wlingke](https://github.com/wlingke)
+ * BREAKING CHANGE: remove `saveErrorIfNotFound`, always error out if `save()` did not update a document #4973
+ * BREAKING CHANGE: don't execute getters in reverse order #4835
+ * BREAKING CHANGE: make boolean casting more strict #4245
+ * BREAKING CHANGE: `toObject()` and `toJSON()` option parameter merges with defaults rather than overwriting #4131
+ * feat: allow setting `default` on `_id` #4069
+ * BREAKING CHANGE: `deleteX()` and `remove()` promise resolves to the write object result #4013
+ * feat: support returning a promise from middleware functions #3779
+ * BREAKING CHANGE: don't return a promise if callback specified #3670
+ * BREAKING CHANGE: only cast `update()`, `updateX()`, `replaceOne()`, `remove()`, `deleteX()` in exec #3529
+ * BREAKING CHANGE: sync errors in middleware functions are now handled #3483
+ * BREAKING CHANGE: post hooks get flow control #3232
+ * BREAKING CHANGE: deduplicate hooks when merging discriminator schema #2945
+ * BREAKING CHANGE: use native promises by default, remove support for mpromise #2917
+ * BREAKING CHANGE: remove `retainKeyOrder`, always use forward order when iterating through objects #2749
+ * BREAKING CHANGE: `aggregate()` no longer accepts a spread #2716
+
+4.13.8 / 2017-12-27
+===================
+ * docs(guide): use more up-to-date syntax for autoIndex example #5933
+ * docs: fix grammar #5927 [abagh0703](https://github.com/abagh0703)
+ * fix: propagate lean options to child schemas #5914
+ * fix(populate): use correct model with discriminators + nested populate #5858
+
+4.13.7 / 2017-12-11
+===================
+ * docs(schematypes): fix typo #5889 [gokaygurcan](https://github.com/gokaygurcan)
+ * fix(cursor): handle `reject(null)` with eachAsync callback #5875 #5874 [ZacharyRSmith](https://github.com/ZacharyRSmith)
+ * fix: disallow setting `mongoose.connection` to invalid values #5871 [jinasonlin](https://github.com/jinasonlin)
+ * docs(middleware): suggest using `return next()` to stop middleware execution #5866
+ * docs(connection): improve connection string query param docs #5864
+ * fix(document): run validate hooks on array subdocs even if not directly modified #5861
+ * fix(discriminator): don't treat $meta as defining projection when querying #5859
+ * fix(types): handle Decimal128 when using bson-ext on server side #5850
+ * fix(document): ensure projection with only $slice isn't treated as inclusive for discriminators #4991
+ * fix(model): throw error when passing non-object to create() #2037
+
+4.13.6 / 2017-12-02
+===================
+ * fix(schema): support strictBool option in schema #5856 [ekulabuhov](https://github.com/ekulabuhov)
+ * fix(update): make upsert option consistently handle truthy values, not just booleans, for updateOne() #5839
+ * refactor: remove unnecessary constructor check #2057
+ * docs(query): correct function signature for .mod() helper #1806
+ * fix(query): report ObjectParameterError when passing non-object as filter to find() and findOne() #1698
+
+4.13.5 / 2017-11-24
+===================
+ * fix(model): handle update cast errors correctly with bulkWrite #5845 [Michael77](https://github.com/Michael77)
+ * docs: add link to bufferCommands option #5844 [ralphite](https://github.com/ralphite)
+ * fix(model): allow virtual ref function to return arrays #5834 [brunohcastro](https://github.com/brunohcastro)
+ * fix(query): don't throw uncaught error if query filter too big #5812
+ * fix(document): if setting unselected nested path, don't overwrite nested path #5800
+ * fix(document): support calling `populate()` on nested document props #5703
+ * fix: add `strictBool` option for schema type boolean #5344 #5211 #4245
+ * docs(faq): add faq re: typeKey #1886
+ * docs(query): add more detailed docs re: options #1702
+
+4.13.4 / 2017-11-17
+===================
+ * fix(aggregate): add chainable .option() helper for setting arbitrary options #5829
+ * fix(aggregate): add `.pipeline()` helper to get the current pipeline #5825
+ * docs: grammar fixes for `unique` FAQ #5823 [mfluehr](https://github.com/mfluehr)
+ * chore: add node 9 to travis #5822 [superheri](https://github.com/superheri)
+ * fix(model): fix infinite recursion with recursive embedded discriminators #5821 [Faibk](https://github.com/Faibk)
+
+4.13.3 / 2017-11-15
+===================
+ * chore: add node 8 to travis #5818 [superheri](https://github.com/superheri)
+ * fix(document): don't apply transforms to nested docs when updating already saved doc #5807
+
+4.13.2 / 2017-11-11
+===================
+ * feat(buffer): add support for subtype prop #5530
+
+4.13.1 / 2017-11-08
+===================
+ * fix: accept multiple paths or array of paths to depopulate #5798 #5797 [adamreisnz](https://github.com/adamreisnz)
+ * fix(document): pass default array as actual array rather than taking first element #5780
+ * fix(model): increment version when $set-ing it in a save() that requires a version bump #5779
+ * fix(query): don't explicitly project in discriminator key if user projected in parent path #5775 #5754
+ * fix(model): cast query option to geoNear() #5765
+ * fix(query): don't treat projection with just $slice as inclusive #5737
+ * fix(discriminator): defer applying embedded discriminator hooks until top-level model is compiled #5706
+ * docs(discriminator): add warning to always attach hooks before calling discriminator() #5706
+
+4.13.0 / 2017-11-02
+===================
+ * feat(aggregate): add $addFields helper #5740 [AyushG3112](https://github.com/AyushG3112)
+ * feat(connection): add connection-level bufferCommands #5720
+ * feat(connection): add createCollection() helper #5712
+ * feat(populate): support setting localField and foreignField to functions #5704 #5602
+ * feat(query): add multipleCastError option for aggregating cast errors when casting update #5609
+ * feat(populate): allow passing a function to virtual ref #5602
+ * feat(schema): add excludeIndexes option to optionally prevent collecting indexes from nested schemas #5575
+ * feat(model): report validation errors from `insertMany()` if using `ordered: false` and `rawResult: true` #5337
+ * feat(aggregate): add pre/post aggregate middleware #5251
+ * feat(schema): allow using `set` as a schema path #1939
+
+4.12.6 / 2017-11-01
+===================
+ * fix(schema): make clone() copy query helpers correctly #5752
+ * fix: undeprecate `ensureIndex()` and use it by default #3280
+
+4.12.5 / 2017-10-29
+===================
+ * fix(query): correctly handle `$in` and required for $pull and update validators #5744
+ * feat(aggegate): add $addFields pipeline operator #5740 [AyushG3112](https://github.com/AyushG3112)
+ * fix(document): catch sync errors in document pre hooks and report as error #5738
+ * fix(populate): handle slice projections correctly when automatically selecting populated fields #5737
+ * fix(discriminator): fix hooks for embedded discriminators #5706 [wlingke](https://github.com/wlingke)
+ * fix(model): throw sane error when customer calls `mongoose.Model()` over `mongoose.model()` #2005
+
+4.12.4 / 2017-10-21
+===================
+ * test(plugins): add coverage for idGetter with id as a schema property #5713 [wlingke](https://github.com/wlingke)
+ * fix(model): avoid copying recursive $$context object when creating discriminator after querying #5721
+ * fix(connection): ensure connection promise helpers are removed before emitting 'connected' #5714
+ * docs(schema): add notes about runSettersOnQuery to schema setters #5705
+ * fix(collection): ensure queued operations run on the next tick #5562
+
+4.12.3 / 2017-10-16
+===================
+ * fix(connection): emit 'reconnect' event as well as 'reconnected' for consistency with driver #5719
+ * fix: correctly bubble up left/joined events for replica set #5718
+ * fix(connection): allow passing in `autoIndex` as top-level option rather than requiring `config.autoIndex` #5711
+ * docs(connection): improve docs regarding reconnectTries, autoReconnect, and bufferMaxEntries #5711
+ * fix(query): handle null with addToSet/push/pull/pullAll update validators #5710
+ * fix(model): handle setDefaultsOnInsert option for bulkWrite updateOne and updateMany #5708
+ * fix(query): avoid infinite recursion edge case when cloning a buffer #5702
+
+4.12.2 / 2017-10-14
+===================
+ * docs(faq): add FAQ about using arrow functions for getters/setters, virtuals, and methods #5700
+ * docs(schema): document the childSchemas property and add to public API #5695
+ * fix(query): don't project in populated field if parent field is already projected in #5669
+ * fix: bump mongodb -> 2.2.33 for issue with autoReconnect #4513
+
+4.12.1 / 2017-10-08
+===================
+ * fix(document): create new doc when setting single nested, no more set() on copy of priorVal #5693
+ * fix(model): recursively call applyMethods on child schemas for global plugins #5690
+ * docs: fix bad promise lib example on home page #5686
+ * fix(query): handle false when checking for inclusive/exclusive projection #5685
+ * fix(discriminator): allow reusing child schema #5684
+ * fix: make addToSet() on empty array with subdoc trigger manual population #5504
+
+4.12.0 / 2017-10-02
+===================
+ * docs(validation): add docs coverage for ValidatorError.reason #5681
+ * feat(discriminator): always add discriminatorKey to base schema to allow updating #5613
+ * fix(document): make nested docs no longer inherit parent doc's schema props #5586 #5546 #5470
+ * feat(query): run update validators on $pull and $pullAll #5555
+ * feat(query): add .error() helper to query to error out in pre hooks #5520
+ * feat(connection): add dropCollection() helper #5393
+ * feat(schema): add schema-level collation option #5295
+ * feat(types): add `discriminator()` function for single nested subdocs #5244
+ * feat(document): add $isDeleted() getter/setter for better support for soft deletes #4428
+ * feat(connection): bubble up reconnectFailed event when driver gives up reconnecting #4027
+ * fix(query): report error if passing array or other non-object as filter to update query #3677
+ * fix(collection): use createIndex() instead of deprecated ensureIndex() #3280
+
+4.11.14 / 2017-09-30
+====================
+ * chore: add nsp check to the CI build #5679 [hairyhenderson](https://github.com/hairyhenderson)
+ * fix: bump mquery because of security issue with debug package #5677 #5675 [jonathanprl](https://github.com/jonathanprl)
+ * fix(populate): automatically select() populated()-ed fields #5669
+ * fix(connection): make force close work as expected #5664
+ * fix(document): treat $elemMatch as inclusive projection #5661
+ * docs(model/query): clarify which functions fire which middleware #5654
+ * fix(model): make `init()` public and return a promise that resolves when indexes are done building #5563
+
+4.11.13 / 2017-09-24
+====================
+ * fix(query): correctly run replaceOne with update validators #5665 [sime1](https://github.com/sime1)
+ * fix(schema): replace mistype in setupTimestamp method #5656 [zipp3r](https://github.com/zipp3r)
+ * fix(query): avoid throwing cast error for strict: throw with nested id in query #5640
+ * fix(model): ensure class gets combined schema when using class syntax with discriminators #5635
+ * fix(document): handle setting doc array to array of top-level docs #5632
+ * fix(model): handle casting findOneAndUpdate() with overwrite and upsert #5631
+ * fix(update): correctly handle $ in updates #5628
+ * fix(types): handle manual population consistently for unshift() and splice() #5504
+
+4.11.12 / 2017-09-18
+====================
+ * docs(model): asterisk should not render as markdown bullet #5644 [timkinnane](https://github.com/timkinnane)
+ * docs: use useMongoClient in connection example #5627 [GabrielNicolasAvellaneda](https://github.com/GabrielNicolasAvellaneda)
+ * fix(connection): call callback when initial connection failed #5626
+ * fix(query): apply select correctly if a given nested schema is used for 2 different paths #5603
+ * fix(document): add graceful fallback for setting a doc array value and `pull()`-ing a doc #3511
+
+4.11.11 / 2017-09-10
+====================
+ * fix(connection): properly set readyState in response to driver 'close' and 'reconnect' events #5604
+ * fix(document): ensure single embedded doc setters only get called once, with correct value #5601
+ * fix(timestamps): allow enabling updatedAt without createdAt #5598
+ * test: improve unique validator test by making create run before ensureIndex #5595 #5562
+ * fix(query): ensure find callback only gets called once when post init hook throws error #5592
+
+4.11.10 / 2017-09-03
+====================
+ * docs: add KeenIO tracking #5612
+ * fix(schema): ensure validators declared with `.validate()` get copied with clone() #5607
+ * fix: remove unnecessary jest warning #5480
+ * fix(discriminator): prevent implicit discriminator schema id from clobbering base schema custom id #5591
+ * fix(schema): hide schema objectid warning for non-hex strings of length 24 #5587
+ * docs(populate): use story schema defined key author instead of creator #5578 [dmric](https://github.com/dmric)
+ * docs(document): describe usage of `.set()` #5576
+ * fix(document): ensure correct scope in single nested validators #5569
+ * fix(populate): don't mark path as populated until populate() is done #5564
+ * fix(document): make push()-ing a doc onto an empty array act as manual population #5504
+ * fix(connection): emit timeout event on socket timeout #4513
+
+4.11.9 / 2017-08-27
+===================
+ * fix(error): avoid using arguments.callee because that breaks strict mode #5572
+ * docs(schematypes): fix spacing #5567
+ * fix(query): enforce binary subtype always propagates to mongodb #5551
+ * fix(query): only skip castForQuery for mongoose arrays #5536
+ * fix(browser): rely on browser entrypoint to decide whether to use BrowserDocument or NodeDocument #5480
+
+4.11.8 / 2017-08-23
+===================
+ * feat: add warning about using schema ObjectId as type ObjectId #5571 [efkan](https://github.com/efkan)
+ * fix(schema): allow setting `id` property after schema was created #5570 #5548
+ * docs(populate): remove confusing _ from populate docs #5560
+ * fix(connection): expose parsed uri fields (host, port, dbname) when using openUri() #5556
+ * docs: added type boolean to options documentation #5547 [ndabAP](https://github.com/ndabAP)
+ * test: add test coverage for stopping/starting server #5524
+ * fix(aggregate): pull read preference from schema by default #5522
+
+4.11.7 / 2017-08-14
+===================
+ * fix: correct properties when calling toJSON() on populated virtual #5544 #5442 [davidwu226](https://github.com/davidwu226)
+ * docs: fix spelling #5535 [et](https://github.com/et)
+ * fix(error): always set name before stack #5533
+ * fix: add warning about running jest in jsdom environment #5532 #5513 #4943
+ * fix(document): ensure overwriting a doc array cleans out individual docs #5523
+ * fix(schema): handle creating arrays of single nested using type key #5521
+ * fix: upgrade mongodb -> 2.2.31 to support user/pass options #5419
+
+4.11.6 / 2017-08-07
+===================
+ * fix: limiting number of async operations per time in insertMany #5529 [andresattler](https://github.com/andresattler)
+ * fix: upgrade mongodb -> 2.2.30 #5517
+ * fix(browserDocument): prevent stack overflow caused by double-wrapping embedded doc save() in jest #5513
+ * fix(document): clear single nested doc when setting to empty object #5506
+ * fix(connection): emit reconnected and disconnected events correctly with useMongoClient #5498
+ * fix(populate): ensure nested virtual populate gets set even if top-level property is null #5431
+
+4.11.5 / 2017-07-30
+===================
+ * docs: fix link to $lookup #5516 [TalhaAwan](https://github.com/TalhaAwan)
+ * fix: better parallelization for eachAsync #5502 [lchenay](https://github.com/lchenay)
+ * docs(document): copy docs for save from model to doc #5493
+ * fix(document): handle dotted virtuals in toJSON output #5473
+ * fix(populate): restore user-provided limit after mutating so cursor() works with populate limit #5468
+ * fix(query): don't throw StrictModeError if geo query with upsert #5467
+ * fix(populate): propagate readPreference from query to populate queries by default #5460
+ * docs: warn not to use arrow functions for statics and methods #5458
+ * fix(query): iterate over all condition keys for setDefaultsOnInsert #5455
+ * docs(connection): clarify server/replset/mongos option deprecation with useMongoClient #5442
+
+4.11.4 / 2017-07-23
+===================
+ * fix: handle next() errors in `eachAsync()` #5486 [lchenay](https://github.com/lchenay)
+ * fix(schema): propagate runSettersOnQuery option to implicitly created schemas #5479 [https://github.com/ValYouW]
+ * fix(query): run castConditions() correctly in update ops #5477
+ * fix(query): ensure castConditions called for findOne and findOneAnd* #5477
+ * docs: clarify relationship between $lookup and populate #5475 [TalhaAwan](https://github.com/TalhaAwan)
+ * test: add coverage for arrays of arrays [zbjornson](https://github.com/zbjornson)
+ * fix(middleware): ensure that error handlers for save get doc as 2nd param #5466
+ * fix: handle strict: false correctly #5454 #5453 [wookieb](https://github.com/wookieb)
+ * fix(query): apply schema excluded paths if only projection is a $slice #5450
+ * fix(query): correct discriminator handling for schema `select: false` fields in schema #5448
+ * fix(cursor): call next() in series when parallel option used #5446
+ * chore: load bundled driver first to avoid packaging problem #5443 [prototypeme](https://github.com/prototypeme)
+ * fix(query): defer condition casting until final exec #5434
+ * fix(aggregate): don't rely on mongodb aggregate to put a cursor in the callback #5394
+ * docs(aggregate): add useMongooseAggCursor docs #5394
+ * docs(middleware): clarify context for document, query, and model middleware #5381
+
+4.11.3 / 2017-07-14
+===================
+ * fix(connection): remove .then() before resolving to prevent infinite recursion #5471
+
+4.11.2 / 2017-07-13
+===================
+ * docs: fix comment typo in connect example #5435 [ConnorMcF](https://github.com/ConnorMcF)
+ * fix(update): correctly cast document array in update validators with exec() #5430
+ * fix(connection): handle autoIndex with useMongoClient #5423
+ * fix(schema): handle `type: [Array]` in schemas #5416
+ * fix(timestamps): if overwrite is set and there's a $set, use $set instead of top-level update #5413
+ * fix(document): don't double-validate deeply nested doc array elements #5411
+ * fix(schematype): clone default objects so default not shared across object instances unless `shared` specified #5407
+ * fix(document): reset down the nested subdocs when resetting parent doc #5406
+ * fix: don't pass error arg twice to error handlers #5405
+ * fix(connection): make openUri() return connection decorated with then() and catch() #5404
+ * fix: enforce $set on an array must be an array #5403
+ * fix(document): don't crash if calling `validateSync()` after overwriting doc array index #5389
+ * fix(discriminator): ensure discriminator key doesn't count as user-selected field for projection #4629
+
+4.11.1 / 2017-07-02
+===================
+* docs: populate virtuals fix justOne description #5427 [fredericosilva](https://github.com/fredericosilva)
+ * fix(connection): make sure to call onOpen in openUri() #5404
+ * docs(query): justOne is actually single, and it default to false #5402 [zbjornson](https://github.com/zbjornson)
+ * docs: fix small typo in lib/schema.js #5398 #5396 [pjo336](https://github.com/pjo336)
+ * fix: emit remove on single nested subdocs when removing parent #5388
+ * fix(update): handle update with defaults and overwrite but no update validators #5384
+ * fix(populate): handle undefined refPath values in middle of array #5377
+ * fix(document): ensure consistent setter context for single nested #5363
+ * fix(query): support runSettersOnQuery as query option #5350
+
+4.11.0 / 2017-06-25
+===================
+ * feat(query): execute setters with query as context for `runSettersOnQuery` #5339
+ * feat(model): add translateAliases function #5338 [rocketspacer](https://github.com/rocketspacer)
+ * feat(connection): add `useMongoClient` and `openUri` functions, deprecate current connect logic #5304
+ * refactor(schema): make id virtual not access doc internals #5279
+ * refactor: handle non-boolean lean #5279
+ * feat(cursor): add addCursorFlag() support to query and agg cursors #4814
+ * feat(cursor): add parallel option to eachAsync #4244
+ * feat(schema): allow setting custom error constructor for custom validators #4009
+
+4.10.8 / 2017-06-21
+===================
+ * docs: fix small formatting typo on schematypes #5374 [gianpaj](https://github.com/gianpaj)
+ * fix(model): allow null as an _id #5370
+ * fix(populate): don't throw async uncaught exception if model not found in populate #5364
+ * fix: correctly cast decimals in update #5361
+ * fix(error): don't use custom getter for ValidationError message #5359
+ * fix(query): handle runSettersOnQuery in built-in _id setter #5351
+ * fix(document): ensure consistent context for nested doc custom validators #5347
+
+4.10.7 / 2017-06-18
+===================
+ * docs(validation): show overriding custom validator error with 2nd cb arg #5358
+ * fix: `parseOption` mutates user passed option map #5357 [igwejk](https://github.com/igwejk)
+ * docs: fix guide.jade typo #5356 [CalebAnderson2014](https://github.com/CalebAnderson2014)
+ * fix(populate): don't set populate virtual to ids when match fails #5336
+ * fix(query): callback with cast error if remove and delete* args have a cast error #5323
+
+4.10.6 / 2017-06-12
+===================
+ * fix(cursor): handle custom model option for populate #5334
+ * fix(populate): handle empty virtual populate with Model.populate #5331
+ * fix(model): make ensureIndexes() run with autoIndex: false unless called internally #5328 #5324 #5317
+ * fix: wait for all connections to close before resolving disconnect() promise #5316
+ * fix(document): handle setting populated path with custom typeKey in schema #5313
+ * fix(error): add toJSON helper to ValidationError so `message` shows up with JSON.stringify #5309
+ * feat: add `getPromiseConstructor()` to prevent need for `mongoose.Promise.ES6` #5305
+ * fix(document): handle conditional required with undefined props #5296
+ * fix(model): clone options before inserting in save() #5294
+ * docs(populate): clarify that multiple populate() calls on same path overwrite #5274
+
+4.10.5 / 2017-06-06
+===================
+ * chore: improve contrib guide for building docs #5312
+ * fix(populate): handle init-ing nested virtuals properly #5311
+ * fix(update): report update validator error if required path under single nested doc not set
+ * fix(schema): remove default validate pre hook that was causing issues with jest #4943
+
+4.10.4 / 2017-05-29
+===================
+ * chore: dont store test data in same directory #5303
+ * chore: add data dirs to npmignore #5301 [Starfox64](https://github.com/Starfox64)
+ * docs(query): add docs about runSettersOnQuery #5300
+
+4.10.3 / 2017-05-27
+===================
+ * docs: correct inconsistent references to updateOne and replaceOne #5297 [dhritzkiv](https://github.com/dhritzkiv)
+ * docs: fix dropdowns in docs #5292 [nathanallen](https://github.com/nathanallen)
+ * docs: add description of alias option #5287
+ * fix(document): prevent infinite loop if validating nested array #5282
+ * fix(schema): correctly handle ref ObjectIds from different mongoose libs #5259
+ * fix(schema): load child class methods after base class methods to allow override #5227
+
+4.10.2 / 2017-05-22
+===================
+ * fix: bump ms -> 2.0.0 and mquery -> 2.3.1 for minor security vulnerability #5275
+
+4.10.1 / 2017-05-21
+===================
+ * fix(aggregate): handle sorting by text score correctly #5258
+ * fix(populate): handle doc.populate() with virtuals #5240
+ * fix(schema): enforce that `_id` is never null #5236
+
+4.10.0 / 2017-05-18
+===================
+ * fix(schema): update clone method to include indexes #5268 [clozanosanchez](https://github.com/clozanosanchez)
+ * feat(schema): support aliases #5184 [rocketspacer](https://github.com/rocketspacer)
+ * feat(aggregate): add mongoose-specific aggregation cursor option #5145
+ * refactor(model): make sharding into a plugin instead of core #5105
+ * fix(document): make nested doc mongoose internals not enumerable again #5078
+ * feat(model): pass params to pre hooks #5064
+ * feat(timestamps): support already defined timestamp paths in schema #4868
+ * feat(query): add runSettersOnQuery option #4569
+ * fix(query): add strictQuery option that throws when not querying on field not in schema #4136
+ * fix(update): more complete handling for overwrite option with update validators #3556
+ * feat: support `unique: true` in arrays via the mongoose-unique-array plugin #3347
+ * fix(model): always emit 'index', even if no indexes #3347
+ * fix(schema): set unique indexes on primitive arrays #3347
+ * feat(validation): include failed paths in error message and inspect output #3064 #2135
+ * fix(model): return saved docs when create() fails #2190
+
+4.9.10 / 2017-05-17
+===================
+ * fix(connection): ensure callback arg to openSet() is handled properly #5249
+ * docs: remove dead plugins repo and add content links #5247
+ * fix(model): skip index build if connecting after model init and autoIndex false #5176
+
+4.9.9 / 2017-05-13
+==================
+ * docs: correct value for Query#regex() #5230
+ * fix(connection): don't throw if .catch() on open() promise #5229
+ * fix(schema): allow update with $currentDate for updatedAt to succeed #5222
+ * fix(model): versioning doesn't fail if version key undefined #5221 [basileos](https://github.com/basileos)
+ * fix(document): don't emit model error if callback specified for consistency with docs #5216
+ * fix(document): handle errors in subdoc pre validate #5215
+
+4.9.8 / 2017-05-07
+==================
+ * docs(subdocs): rewrite subdocs guide #5217
+ * fix(document): avoid circular JSON if error in doc array under single nested subdoc #5208
+ * fix(document): set intermediate empty objects for deeply nested undefined paths before path itself #5206
+ * fix(schema): throw error if first param to schema.plugin() is not a function #5201
+ * perf(document): major speedup in validating subdocs (50x in some cases) #5191
+
+4.9.7 / 2017-04-30
+==================
+ * docs: fix typo #5204 [phutchins](https://github.com/phutchins)
+ * fix(schema): ensure correct path for deeply nested schema indexes #5199
+ * fix(schema): make remove a reserved name #5197
+ * fix(model): handle Decimal type in insertMany correctly #5190
+ * fix: upgrade kareem to handle async pre hooks correctly #5188
+ * docs: add details about unique not being a validator #5179
+ * fix(validation): handle returning a promise with isAsync: true #5171
+
+4.9.6 / 2017-04-23
+==================
+ * fix: update `parentArray` references when directly assigning document arrays #5192 [jhob](https://github.com/jhob)
+ * docs: improve schematype validator docs #5178 [milesbarr](https://github.com/milesbarr)
+ * fix(model): modify discriminator() class in place #5175
+ * fix(model): handle bulkWrite updateMany casting #5172 [tzellman](https://github.com/tzellman)
+ * docs(model): fix replaceOne example for bulkWrite #5168
+ * fix(document): don't create a new array subdoc when creating schema array #5162
+ * fix(model): merge query hooks from discriminators #5147
+ * fix(document): add parent() function to subdocument to match array subdoc #5134
+
+4.9.5 / 2017-04-16
+==================
+ * fix(query): correct $pullAll casting of null #5164 [Sebmaster](https://github.com/Sebmaster)
+ * docs: add advanced schemas docs for loadClass #5157
+ * fix(document): handle null/undefined gracefully in applyGetters() #5143
+ * fix(model): add resolveToObject option for mapReduce with ES6 promises #4945
+
+4.9.4 / 2017-04-09
+==================
+ * fix(schema): clone query middleware correctly #5153 #5141 [clozanosanchez](https://github.com/clozanosanchez)
+ * docs(aggregate): fix typo #5142
+ * fix(query): cast .$ update to underlying array type #5130
+ * fix(populate): don't mutate populate result in place #5128
+ * fix(query): handle $setOnInsert consistent with $set #5126
+ * docs(query): add strict mode option for findOneAndUpdate #5108
+
+4.9.3 / 2017-04-02
+==================
+ * docs: document.js fixes for functions prepended with `$` #5131 [krmannix](https://github.com/krmannix)
+ * fix: Avoid exception on constructor check #5129 [monkbroc](https://github.com/monkbroc)
+ * docs(schematype): explain how to use `isAsync` with validate() #5125
+ * docs(schematype): explain custom message with required function #5123
+ * fix(populate): only apply refPath duplicate id optimization if not array #5114
+ * fix(document): copy non-objects to doc when init() #5111
+ * perf(populate): dont clone whole options every time #5103
+ * feat(document): add isDirectSelected() to minimize isSelected() changes #5063
+ * docs(schematypes): explain some subtleties with arrays #5059
+
+4.9.2 / 2017-03-26
+==================
+ * fix(discriminator): handle class names consistently #5104
+ * fix(schema): make clone() work with reusing discriminator schemas #5098
+ * fix(querycursor): run pre find hooks with .cursor() #5096
+ * fix(connection): throw error if username:password includes @ or : #5091
+ * fix(timestamps): handle overwriting createdAt+updatedAt consistently #5088
+ * fix(document): ensure subdoc post save runs after parent save #5085
+ * docs(model): improve update docs #5076 [bertolo1988](https://github.com/bertolo1988)
+
+4.9.1 / 2017-03-19
+==================
+ * fix(query): handle $type for arrays #5080 #5079 [zoellner](https://github.com/zoellner)
+ * fix(model): handle ordered param for `insertMany` validation errors #5072 [sjorssnoeren](https://github.com/sjorssnoeren)
+ * fix(populate): avoid duplicate ids in dynref queries #5054
+ * fix(timestamps): dont set timestamps in update if user set it #5045
+ * fix(update): dont double-call setters on arrays #5041
+ * fix: upgrade driver -> 2.2.25 for jest fix #5033
+ * fix(model): get promise each time save() is called rather than once #5030
+ * fix(connection): make connect return value consistent #5006
+
+4.9.0 / 2017-03-13
+==================
+ * feat(document): return this from `depopulate()` #5027
+ * fix(drivers): stop emitting timeouts as errors #5026
+ * feat(schema): add a clone() function for schemas #4983
+ * feat(query): add rawResult option to replace passRawResult, deprecate passRawResult #4977 #4925
+ * feat(schematype): support isAsync validator option and handle returning promises from validators, deprecate implicit async validators #4290
+ * feat(query): add `replaceOne()`, `deleteOne()`, `deleteMany()` #3998
+ * feat(model): add `bulkWrite()` #3998
+
+4.8.7 / 2017-03-12
+==================
+ * fix(model): if last arg in spread is falsy, treat it as a callback #5061
+ * fix(document): use $hook instead of hook to enable 'hook' as a path name #5047
+ * fix(populate): dont select foreign field if parent field is selected #5037
+ * fix(populate): handle passing no args to query.populate #5036
+ * fix(update): use correct method for casting nested arrays #5032
+ * fix(discriminator): handle array discriminators when casting $push #5009
+
+4.8.6 / 2017-03-05
+==================
+ * docs(document): remove text that implies that transform is false by default #5023
+ * fix(applyHooks): dont wrap a function if it is already wrapped #5019
+ * fix(document): ensure nested docs' toObject() clones #5008
+
+4.8.5 / 2017-02-25
+==================
+ * fix: check for empty schemaPath before accessing property $isMongooseDocumentArray #5017 [https://github.com/randyhoulahan](randyhoulahan)
+ * fix(discriminators): handle create() and push() for embedded discriminators #5001
+ * fix(querycursor): ensure close emitted after last data event #4998
+ * fix(discriminators): remove fields not selected in child when querying by base model #4991
+
+4.8.4 / 2017-02-19
+==================
+ * docs(discriminators): explain embedded discriminators #4997
+ * fix(query): fix TypeError when findOneAndUpdate errors #4990
+ * fix(update): handle nested single embedded in update validators correctly #4989
+ * fix(browser): make browser doc constructor not crash #4987
+
+4.8.3 / 2017-02-15
+==================
+ * chore: upgrade mongodb driver -> 2.2.24
+ * docs(connections): addd some details about callbacks #4986
+ * fix: ensure class is created with new keyword #4972 #4947 [benhjames](https://github.com/benhjames)
+ * fix(discriminator): add applyPluginsToDiscriminators option #4965
+ * fix(update): properly cast array subdocs when casting update #4960
+ * fix(populate): ensure foreign field is selected for virtual populate #4959
+ * docs(query): document some query callback params #4949
+ * fix(document): ensure errors in validators get caught #2185
+
+4.8.2 / 2017-02-10
+==================
+ * fix(update): actually run validators on addToSet #4953
+ * fix(update): improve buffer error handling #4944 [ValYouW](https://github.com/ValYouW)
+ * fix(discriminator): handle subclassing with loadClass correctly #4942
+ * fix(query): allow passing Map to sort() #4941
+ * fix(document): handle setting discriminator doc #4935
+ * fix(schema): return correct value from pre init hook #4928
+ * fix(query): ensure consistent params in error handlers if pre hook errors #4927
+
+4.8.1 / 2017-01-30
+==================
+ * fix(query): handle $exists for arrays and embedded docs #4937
+ * fix(query): handle passing string to hint() #4931
+
+4.8.0 / 2017-01-28
+==================
+ * feat(schema): add saveErrorIfNotFound option and $where property #4924 #4004
+ * feat(query): add $in implicitly if passed an array #4912 [QuotableWater7](https://github.com/QuotableWater7)
+ * feat(aggregate): helper for $facet #4904 [varunjayaraman](https://github.com/varunjayaraman)
+ * feat(query): add collation method #4839
+ * feat(schema): propogate strict option to implicit array subschemas #4831 [dkrosso](https://github.com/dkrosso)
+ * feat(aggregate): add helper for graphLookup #4819 [varunjayaraman](https://github.com/varunjayaraman)
+ * feat(types): support Decimal128 #4759
+ * feat(aggregate): add eachAsync() to aggregate cursor #4300
+ * feat(query): add updateOne and updateMany #3997
+ * feat(model): support options for insertMany #3893
+ * fix(document): run validation on single nested docs if not directly modified #3884
+ * feat(model): use discriminator constructor based on discriminatorKey in create() #3624
+ * feat: pass collection as context to debug function #3261
+ * feat(query): support push and addToSet for update validators #2933
+ * perf(document): refactor registerHooksFromSchema so hooks are defined on doc prototype #2754
+ * feat(types): add discriminator() function to doc arrays #2723 #1856
+ * fix(populate): return an error if sorting underneath a doc array #2202
+
+4.7.9 / 2017-01-27
+==================
+ * fix(query): handle casting $exists under $not #4933
+ * chore: upgrade mongodb -> 2.2.22 re: #4931
+
+4.7.8 / 2017-01-23
+==================
+ * fix(populate): better handling for virtual populate under arrays #4923
+ * docs: upgrade contributors count #4918 [AdamZaczek](https://github.com/AdamZaczek)
+ * fix(query): don't set nested path default if setting parent path #4911
+ * docs(promise): add missing bracket #4907
+ * fix(connection): ensure error handling is consistently async #4905
+ * fix: handle authMechanism in query string #4900
+ * fix(document): ensure error handlers run for validate #4885
+
+4.7.7 / 2017-01-15
+==================
+ * fix(utils): don't crash if to[key] is null #4881
+ * fix: upgrade mongodb -> 2.2.21 #4867
+ * fix: add a toBSON to documents for easier querying #4866
+ * fix: suppress bluebird warning #4854 [davidwu226](https://github.com/davidwu226)
+ * fix(populate): handle nested virtuals in virtual populate #4851
+
+4.7.6 / 2017-01-02
+==================
+ * fix(model): allow passing non-array to insertMany #4846
+ * fix(populate): use base model name if no discriminator for backwards compat #4843
+ * fix: allow internal validate callback to be optional #4842 [arciisine](https://github.com/arciisine)
+ * fix(document): don't skip pointCut if save not defined (like in browser doc) #4841
+ * chore: improve benchmarks #4838 [billouboq](https://github.com/billouboq)
+ * perf: remove some unused parameters #4837 [billouboq](https://github.com/billouboq)
+ * fix(query): don't call error handler if passRawResult is true and no error occurred #4836
+
+4.7.5 / 2016-12-26
+==================
+ * docs(model): fix spelling mistake #4828 [paulinoj](https://github.com/paulinoj)
+ * fix(aggregate): remove unhandled rejection when using aggregate.then() #4824
+ * perf: remove try/catch that kills optimizer #4821
+ * fix(model): handles populating with discriminators that may not have a ref #4817
+ * fix(document): handle setting array of discriminators #3575
+
+4.7.4 / 2016-12-21
+==================
+ * docs: fix typo #4810 [GEEKIAM](https://github.com/GEEKIAM)
+ * fix(query): timestamps with $push + $each #4805
+ * fix(document): handle buffers correctly in minimize #4800
+ * fix: don't disallow overwriting default and cast fns #4795 [pdspicer](https://github.com/pdspicer)
+ * fix(document): don't convert single nested docs to POJOs #4793
+ * fix(connection): handle reconnect to replica set correctly #4972 [gfzabarino](https://github.com/gfzabarino)
+
+4.7.3 / 2016-12-16
+==================
+ * fix: upgrade mongodb driver -> 2.2.16 for several bug fixes and 3.4 support #4799
+ * fix(model): ensure discriminator key is correct for child schema on discriminator #4790
+ * fix(document): handle mark valid in subdocs correctly #4778
+ * fix(query): check for objects consistently #4775
+
+4.7.2 / 2016-12-07
+==================
+ * test(populate): fix justOne test #4772 [cblanc](https://github.com/cblanc)
+ * chore: fix benchmarks #4769 [billouboq](https://github.com/billouboq)
+ * fix(document): handle setting subdoc to null after setting parent doc #4766
+ * fix(query): support passRawResult with lean #4762 #4761 [mhfrantz](https://github.com/mhfrantz)
+ * fix(query): throw StrictModeError if upsert with nonexisting field in condition #4757
+ * test: fix a couple of sort tests #4756 [japod](https://github.com/japod)
+ * chore: upgrade mongodb driver -> 2.2.12 #4753 [mdlavin](https://github.com/mdlavin)
+ * fix(query): handle update with upsert and overwrite correctly #4749
+
+4.7.1 / 2016-11-30
+==================
+ * fix(schema): throw error if you use prototype as a schema path #4746
+ * fix(schema): throw helpful error if you define a virtual with the same path as a real path #4744
+ * fix(connection): make createConnection not throw rejected promises #4742
+ * fix(populate): allow specifiying options in model schema #4741
+ * fix(document): handle selected nested elements with defaults #4739
+ * fix(query): add model to cast error if possible #4729
+ * fix(query): handle timestamps with overwrite #4054
+
+4.7.0 / 2016-11-23
+==================
+ * docs: clean up schematypes #4732 [kidlj](https://github.com/kidlj)
+ * perf: only get stack when necessary with VersionError #4726 [Sebmaster](https://github.com/Sebmaster)
+ * fix(query): ensure correct casting when setting array element #4724
+ * fix(connection): ensure db name gets set when you pass 4 params #4721
+ * fix: prevent TypeError in node v7 #4719 #4706
+ * feat(document): support .set() on virtual subpaths #4716
+ * feat(populate): support populate virtuals on nested schemas #4715
+ * feat(querycursor): support transform option and .map() #4714 #4705 [cblanc](https://github.com/cblanc)
+ * fix(document): dont set defaults on not-selected nested paths #4707
+ * fix(populate): don't throw if empty string passed to populate #4702
+ * feat(model): add `loadClass()` function for importing schema from ES6 class #4668 [rockmacaca](https://github.com/rockmacaca)
+
+4.6.8 / 2016-11-14
+==================
+ * fix(querycursor): clear stack when iterating onto next doc #4697
+ * fix: handle null keys in validation error #4693 #4689 [arciisine](https://github.com/arciisine)
+ * fix(populate): handle pre init middleware correctly with populate virtuals #4683
+ * fix(connection): ensure consistent return value for open and openSet #4659
+ * fix(schema): handle falsy defaults for arrays #4620
+
+4.6.7 / 2016-11-10
+==================
+ * fix(document): only invalidate in subdoc if using update validators #4681
+ * fix(document): don't create subdocs when excluded in projection #4669
+ * fix(document): ensure single embedded schema validator runs with correct context #4663
+ * fix(document): make sure to depopulate top level for sharding #4658
+ * fix(connection): throw more helpful error when .model() called incorrectly #4652
+ * fix(populate): throw more descriptive error when trying to populate a virtual that doesn't have proper options #4602
+ * fix(document): ensure subtype gets set properly when saving with a buffer id #4506
+ * fix(query): handle setDefaultsOnInsert with defaults on doc arrays #4456
+ * fix(drivers): make debug output better by calling toBSON() #4356
+
+4.6.6 / 2016-11-03
+==================
+ * chore: upgrade deps #4674 [TrejGun](https://github.com/TrejGun)
+ * chore: run tests on node v7 #4673 [TrejGun](https://github.com/TrejGun)
+ * perf: make setDefaultsOnInsert more efficient if upsert is off #4672 [CamHenlin](https://github.com/CamHenlin)
+ * fix(populate): ensure document array is returned #4656
+ * fix(query): cast doc arrays with positionals correctly for update #4655
+ * fix(document): ensure single nested doc validators run with correct context #4654
+ * fix: handle reconnect failed error in new version of driver #4653 [loris](https://github.com/loris)
+ * fix(populate): if setting a populated doc, take its id #4632
+ * fix(populate): handle populated virtuals in init #4618
+
+4.6.5 / 2016-10-23
+==================
+ * docs: fix grammar issues #4642 #4640 #4639 [silvermanj7](https://github.com/silvermanj7)
+ * fix(populate): filter out nonexistant values for dynref #4637
+ * fix(query): handle $type as a schematype operator #4632
+ * fix(schema): better handling for uppercase: false and lowercase: false #4622
+ * fix(query): don't run transforms on updateForExec() #4621
+ * fix(query): handle id = 0 in findById #4610
+ * fix(query): handle buffers in mergeClone #4609
+ * fix(document): handle undefined with conditional validator for validateSync #4607
+ * fix: upgrade to mongodb driver 2.2.11 #4581
+ * docs(schematypes): clarify schema.path() #4518
+ * fix(query): ensure path is defined before checking in timestamps #4514
+ * fix(model): set version key in upsert #4505
+ * fix(document): never depopulate top-level doc #3057
+ * refactor: ensure sync for setting non-capped collections #2690
+
+4.6.4 / 2016-10-16
+==================
+ * fix(query): cast $not correctly #4616 #4592 [prssn](https://github.com/prssn)
+ * fix: address issue with caching global plugins #4608 #4601 [TrejGun](https://github.com/TrejGun)
+ * fix(model): make sure to depopulate in insertMany #4590
+ * fix(model): buffer autoIndex if bufferCommands disabled #4589
+ * fix(populate): copy ids array before modifying #4585
+ * feat(schema): add retainKeyOrder prop #4542
+ * fix(document): return isModified true for children of direct modified paths #4528
+ * fix(connection): add dropDatabase() helper #4490
+ * fix(model): add usePushEach option for schemas #4455
+ * docs(connections): add some warnings about buffering #4413
+ * fix: add ability to set promise implementation in browser #4395
+
+4.6.3 / 2016-10-05
+==================
+ * fix(document): ensure single nested docs get initialized correctly when setting nested paths #4578
+ * fix: turn off transforms when writing nested docs to db #4574
+ * fix(document): don't set single nested subdocs to null when removing parent doc #4566
+ * fix(model): ensure versionKey gets set in insertMany #4561
+ * fix(schema): handle typeKey in arrays #4548
+ * feat(schema): set $implicitlyCreated on schema if created by interpretAsType #4443
+
+4.6.2 / 2016-09-30
+==================
+ * chore: upgrade to async 2.0.1 internally #4579 [billouboq](https://github.com/billouboq)
+ * fix(types): ensure nested single doc schema errors reach update validators #4557 #4519
+ * fix(connection): handle rs names with leading numbers (muri 1.1.1) #4556
+ * fix(model): don't throw if method name conflicts with Object.prototype prop #4551
+ * docs: fix broken link #4544 [VFedyk](https://github.com/VFedyk)
+ * fix: allow overwriting model on mongoose singleton #4541 [Nainterceptor](https://github.com/Nainterceptor)
+ * fix(document): don't use init: true when building doc defaults #4540
+ * fix(connection): use replSet option if replset not specified #4535
+ * fix(query): cast $not objects #4495
+
+4.6.1 / 2016-09-20
+==================
+ * fix(query): improve handling of $not with $elemMatch #4531 #3719 [timbowhite](https://github.com/timbowhite)
+ * fix: upgrade mongodb -> 2.2.10 #4517
+ * chore: fix webpack build issue #4512 [saiichihashimoto](https://github.com/saiichihashimoto)
+ * fix(query): emit error on next tick when exec callback errors #4500
+ * test: improve test case #4496 [isayme](https://github.com/isayme)
+ * fix(schema): use same check for array types and top-level types #4493
+ * style: fix indentation in docs #4489 [dhurlburtusa](https://github.com/dhurlburtusa)
+ * fix(schema): expose original object passed to constructor #4486
+ * fix(query): handle findOneAndUpdate with array of arrays #4484 #4470 [fedotov](https://github.com/fedotov)
+ * feat(document): add $ignore to make a path ignored #4480
+ * fix(query): properly handle setting single embedded in update #4475 #4466 #4465
+ * fix(updateValidators): handle single nested schema subpaths correctly #4479
+ * fix(model): throw handy error when method name conflicts with property name #4475
+ * fix(schema): handle .set() with array field #4472
+ * fix(query): check nested path when avoiding double-validating Mixed #4441
+ * fix(schema): handle calling path.trim() with no args correctly #4042
+
+4.6.0 / 2016-09-02
+==================
+ * docs(document): clarify the findById and findByIdAndUpdate examples #4471 [mdcanham](https://github.com/mdcanham)
+ * docs(schematypes): add details re: options #4452
+ * docs(middleware): add docs for insertMany hooks #4451
+ * fix(schema): create new array when copying from existing object to preserve change tracking #4449
+ * docs: fix typo in index.jade #4448
+ * fix(query): allow array for populate options #4446
+ * fix(model): create should not cause unhandle reject promise #4439
+ * fix: upgrade to mongodb driver 2.2.9 #4363 #4341 #4311 (see [comments here](https://github.com/mongodb/js-bson/commit/aa0b54597a0af28cce3530d2144af708e4b66bf0#commitcomment-18850498) if you use node 0.10)
+
+4.5.10 / 2016-08-23
+===================
+ * docs: fix typo on documents.jade #4444 [Gabri3l](https://github.com/Gabri3l)
+ * chore: upgrade mocha to 3.0.2 #4437 [TrejGun](https://github.com/TrejGun)
+ * fix: subdocuments causing error with parent timestamp on update #4434 [dyang108](https://github.com/dyang108)
+ * fix(query): don't crash if timestamps on and update doesn't have a path #4425 #4424 #4418
+ * fix(query): ensure single nested subdoc is hydrated when running update validators #4420
+ * fix(query): cast non-$geometry operators for $geoWithin #4419
+ * docs: update contributor count #4415 [AdamZaczek](https://github.com/AdamZaczek)
+ * docs: add more clarification re: the index event #4410
+ * fix(document): only skip modifying subdoc path if parent is direct modified #4405
+ * fix(schema): throw cast error if provided date invalid #4404
+ * feat(error): use util.inspect() so CastError never prints "[object Object]" #4398
+ * fix(model): dont error if the discriminator key is unchanged #4387
+ * fix(query): don't throw unhandled rejection with bluebird when using cbs #4379
+
+4.5.9 / 2016-08-14
+==================
+ * docs: add mixed schema doc for Object literal #4400 [Kikobeats](https://github.com/Kikobeats)
+ * fix(query): cast $geoWithin and convert mongoose objects to POJOs before casting #4392
+ * fix(schematype): dont cast defaults without parent doc #4390
+ * fix(query): disallow passing empty string to findOne() #4378
+ * fix(document): set single nested doc isNew correctly #4369
+ * fix(types): checks field name correctly with nested arrays and populate #4365
+ * fix(drivers): make debug output copy-pastable into mongodb shell #4352
+ * fix(services): run update validators on nested paths #4332
+ * fix(model): handle typeKey with discriminators #4339
+ * fix(query): apply timestamps to child schemas when explicitly specified in update #4049
+ * fix(schema): set prefix as nested path with add() #1730
+
+4.5.8 / 2016-08-01
+==================
+ * fix(model): make changing the discriminator key cause a cast error #4374
+ * fix(query): pass projection fields to cursor #4371 #4342 [Corei13](https://github.com/Corei13)
+ * fix(document): support multiple paths for isModified #4370 [adambuczynski](https://github.com/adambuczynski)
+ * fix(querycursor): always cast fields before returning cursor #4355
+ * fix(query): support projection as alias for fields in findOneAndUpdate #4315
+ * fix(schema): treat index false + unique false as no index #4304
+ * fix(types): dont mark single nested subpath as modified if whole doc already modified #4224
+
+4.5.7 / 2016-07-25
+==================
+ * fix(document): ensure no unhandled rejections if callback specified for save #4364
+
+4.5.6 / 2016-07-23
+==================
+ * fix(schema): don't overwrite createdAt if it isn't selected #4351 [tusbar](https://github.com/tusbar)
+ * docs(api): fix link to populate() and add a new one from depopulate() #4345 [Delapouite](https://github.com/Delapouite)
+ * fix(types): ownerDocument() works properly with single nested docs #4344 [vichle](https://github.com/vichle)
+ * fix(populate): dont use findOne when justOne option set #4329
+ * fix(document): dont trigger .then() deprecated warning when calling doc.remove() #4291
+ * docs(connection): add promiseLibrary option #4280
+ * fix(plugins): apply global plugins to subschemas #4271
+ * fix(model): ensure `ensureIndex()` never calls back in the same tick #4246
+ * docs(schema): improve post hook docs on schema #4238
+
+4.5.5 / 2016-07-18
+==================
+ * fix(document): handle setting root to empty obj if minimize false #4337
+ * fix: downgrade to mongodb 2.1.18 #4335 #4334 #4328 #4323
+ * perf(types): remove defineProperty usage in documentarray #4333
+ * fix(query): correctly pass model in .toConstructor() #4318
+ * fix(services): avoid double-validating mixed types with update validators #4305
+ * docs(middleware): add docs describing error handling middleware #4229
+ * fix(types): throw correct error when invalidating doc array #3602
+
+4.5.4 / 2016-07-11
+==================
+ * fix(types): fix removing embedded documents #4309 [RoCat](https://github.com/RoCat)
+ * docs: various docs improvements #4302 #4294 [simonxca](https://github.com/simonxca)
+ * fix: upgrade mongodb -> 2.1.21 #4295 #4202 [RoCat](https://github.com/RoCat)
+ * fix(populate): convert single result to array for virtual populate because of lean #4288
+ * fix(populate): handle empty results for populate virtuals properly #4285 #4284
+ * fix(query): dont cast $inc to number if type is long #4283
+ * fix(types): allow setting single nested doc to null #4281
+ * fix(populate): handle deeply nested virtual populate #4278
+ * fix(document): allow setting empty obj if strict mode is false #4274
+ * fix(aggregate): allow passing obj to .unwind() #4239
+ * docs(document): add return statements to transform examples #1963
+
+4.5.3 / 2016-06-30
+==================
+ * fix(query): pass correct options to QueryCursor #4277 #4266
+ * fix(querycursor): handle lean option correctly #4276 [gchudnov](https://github.com/gchudnov)
+ * fix(document): fix error handling when no error occurred #4275
+ * fix(error): use strict mode for version error #4272
+ * docs(populate): fix crashing compilation for populate.jade #4267
+ * fix(populate): support `justOne` option for populate virtuals #4263
+ * fix(populate): ensure model param gets used for populate virtuals #4261 #4243
+ * fix(querycursor): add ability to properly close the cursor #4258
+ * docs(model): correct link to Document #4250
+ * docs(populate): correct path for refPath populate #4240
+ * fix(document): support validator.isEmail as validator #4064
+
+4.5.2 / 2016-06-24
+==================
+ * fix(connection): add checks for collection presence for `onOpen` and `onClose` #4259 [nodkz](https://github.com/nodkz)
+ * fix(cast): allow strings for $type operator #4256
+ * fix(querycursor): support lean() #4255 [pyramation](https://github.com/pyramation)
+ * fix(aggregate): allow setting noCursorTimeout option #4241
+ * fix(document): handle undefined for Array.pull #4222 [Sebmaster](https://github.com/Sebmaster)
+ * fix(connection): ensure promise.catch() catches initial connection error #4135
+ * fix(document): show additional context for VersionError #2633
+
+4.5.1 / 2016-06-18
+==================
+ * fix(model): ensure wrapped insertMany() returns a promise #4237
+ * fix(populate): dont overwrite populateVirtuals when populating multiple paths #4234
+ * docs(model): clarify relationship between create() and save() #4233
+ * fix(types): handle option param in subdoc remove() #4231 [tdebarochez](https://github.com/tdebarochez)
+ * fix(document): dedupe modified paths #4226 #4223 [adambuczynski](https://github.com/adambuczynski)
+ * fix(model): don't modify user-provided options object #4221
+ * fix(document): handle setting nested path to empty object #4218 #4182
+ * fix(document): clean subpaths when removing single nested #4216
+ * fix(document): don't force transform on subdocs with inspect #4213
+ * fix(error): allow setting .messages object #4207
+
+4.5.0 / 2016-06-13
+==================
+ * feat(query): added Query.prototype.catch() #4215 #4173 [adambuczynski](https://github.com/adambuczynski)
+ * feat(query): add Query.prototype.cursor() as a .stream() alternative #4117 #3637 #1907
+ * feat(document): add markUnmodified() function #4092 [vincentcr](https://github.com/vincentcr)
+ * feat(aggregate): convert aggregate object to a thenable #3995 #3946 [megagon](https://github.com/megagon)
+ * perf(types): remove defineProperties call for array (**Note:** Because of this, a mongoose array will no longer `assert.deepEqual()` a plain old JS array) #3886
+ * feat(model): add hooks for insertMany() #3846
+ * feat(schema): add support for custom query methods #3740 #2372
+ * feat(drivers): emit error on 'serverClosed' because that means that reconnect failed #3615
+ * feat(model): emit error event when callback throws exception #3499
+ * feat(model): inherit options from discriminator base schema #3414 #1818
+ * feat(populate): expose mongoose-populate-virtuals inspired populate API #2562
+ * feat(document): trigger remove hooks on subdocs when removing parent #2348
+ * feat(schema): add support for express-style error handling middleware #2284
+ * fix(model): disallow setting discriminator key #2041
+ * feat(schema): add support for nested arrays #1361
+
+4.4.20 / 2016-06-05
+===================
+ * docs: clarify command buffering when using driver directly #4195
+ * fix(promise): correct broken mpromise .catch() #4189
+ * fix(document): clean modified subpaths when set path to empty obj #4182
+ * fix(query): support minDistance with query casting and `.near()` #4179
+ * fix(model): remove unnecessary .save() promise #4177
+ * fix(schema): cast all valid ObjectId strings to object ids #3365
+ * docs: remove unclear "unsafe" term in query docs #3282
+
+4.4.19 / 2016-05-21
+===================
+ * fix(model): handle insertMany if timestamps not set #4171
+
+4.4.18 / 2016-05-21
+===================
+ * docs: add missing period #4170 [gitname](https://github.com/gitname)
+ * docs: change build badge to svg #4158 [a0viedo](https://github.com/a0viedo)
+ * fix(model): update timestamps when setting `createdAt` #4155
+ * fix(utils): make sure to require in document properly #4152
+ * fix(model): throw overwrite error when discriminator name conflicts #4148
+
+4.4.17 / 2016-05-13
+===================
+ * docs: remove repetition in QueryStream docs #4147 [hugoabonizio](https://github.com/hugoabonizio)
+ * fix(document): dont double-validate doc array elements #4145
+ * fix(document): call required function with correct scope #4142 [JedWatson](https://github.com/JedWatson)
+
+4.4.16 / 2016-05-09
+===================
+ * refactor(document): use function reference #4133 [dciccale](https://github.com/dciccale)
+ * docs(querystream): clarify `destroy()` and close event #4126 [AnthonyCC](https://github.com/AnthonyCC)
+ * test: make before hook fail fast if it can't connect #4121
+ * docs: add description of CastError constructor params #4120
+ * fix(schematype): ensure single embedded defaults have $parent #4115
+ * fix(document): mark nested paths for validation #4111
+ * fix(schema): make sure element is always a subdoc in doc array validation #3816
+
+4.4.15 / 2016-05-06
+===================
+ * fix(schema): support overwriting array default #4109
+ * fix(populate): assign values when resolving each populate #4104
+ * fix(aggregate): dont send async option to server #4101
+ * fix(model): ensure isNew set to false after insertMany #4099
+ * fix(connection): emit on error if listeners and no callback #4098
+ * fix(document): treat required fn that returns false as required: false #4094
+
+4.4.14 / 2016-04-27
+===================
+ * fix: upgrade mongodb -> 2.1.18 #4102
+ * feat(connection): allow setting mongos as a uri query param #4093 #4035 [burtonjc](https://github.com/burtonjc)
+ * fix(populate): make sure to use correct assignment order for each model #4073
+ * fix(schema): add complete set of geospatial operators for single embedded subdocs #4014
+
+3.8.40 / 2016-04-24
+===================
+ * upgraded; mquery -> 1.10.0 #3989
+
+4.4.13 / 2016-04-21
+===================
+ * docs: add docs favicons #4082 [robertjustjones](https://github.com/robertjustjones)
+ * docs(model): correct Model.remove() return value #4075 [Jokero](https://github.com/Jokero)
+ * fix(query): add $geoWithin query casting for single embedded docs #4044
+ * fix(schema): handle setting trim option to falsy #4042
+ * fix(query): handle setDefaultsOnInsert with empty update #3835
+
+4.4.12 / 2016-04-08
+===================
+ * docs(query): document context option for update and findOneAndUpdate #4055
+ * docs(query): correct link to $geoWithin docs #4050
+ * fix(project): upgrade to mongodb driver 2.1.16 #4048 [schmalliso](https://github.com/schmalliso)
+ * docs(validation): fix validation docs #4028
+ * fix(types): improve .id() check for document arrays #4011
+ * fix(query): remove premature return when using $rename #3171
+ * docs(connection): clarify relationship between models and connections #2157
+
+4.4.11 / 2016-04-03
+===================
+ * fix: upgrade to mongodb driver 2.1.14 #4036 #4030 #3945
+ * fix(connection): allow connecting with { mongos: true } to handle query params #4032 [burtonjc](https://github.com/burtonjc)
+ * docs(connection): add autoIndex example #4026 [tilleps](https://github.com/tilleps)
+ * fix(query): handle passRawResult option when zero results #4023
+ * fix(populate): clone options before modifying #4022
+ * docs: add missing whitespace #4019 [chenxsan](https://github.com/chenxsan)
+ * chore: upgrade to ESLint 2.4.0 #4015 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix(types): single nested subdocs get ids by default #4008
+ * chore(project): add dependency status badge #4007 [Maheshkumar-Kakade](http://github.com/Maheshkumar-Kakade)
+ * fix: make sure timestamps don't trigger unnecessary updates #4005 #3991 [tommarien](https://github.com/tommarien)
+ * fix(document): inspect inherits schema options #4001
+ * fix(populate): don't mark populated path as modified if setting to object w/ same id #3992
+ * fix(document): support kind argument to invalidate #3965
+
+4.4.10 / 2016-03-24
+===================
+ * fix(document): copy isNew when copying a document #3982
+ * fix(document): don't override defaults with undefined keys #3981
+ * fix(populate): merge multiple deep populate options for the same path #3974
+
+4.4.9 / 2016-03-22
+==================
+ * fix: upgrade mongodb -> 2.1.10 re https://jira.mongodb.org/browse/NODE-679 #4010
+ * docs: add syntax highlighting for acquit examples #3975
+
+4.4.8 / 2016-03-18
+==================
+ * docs(aggregate): clarify promises #3990 [megagon](https://github.com/megagon)
+ * fix: upgrade mquery -> 1.10 #3988 [matskiv](https://github.com/matskiv)
+ * feat(connection): 'all' event for repl sets #3986 [xizhibei](https://github.com/xizhibei)
+ * docs(types): clarify Array.pull #3985 [seriousManual](https://github.com/seriousManual)
+ * feat(query): support array syntax for .sort() via mquery 1.9 #3980
+ * fix(populate): support > 3 level nested populate #3973
+ * fix: MongooseThenable exposes connection correctly #3972
+ * docs(connection): add note about reconnectTries and reconnectInterval #3969
+ * feat(document): invalidate returns the new validationError #3964
+ * fix(query): .eq() as shorthand for .equals #3953 [Fonger](https://github.com/Fonger)
+ * docs(connection): clarify connection string vs passed options #3941
+ * docs(query): select option for findOneAndUpdate #3933
+ * fix(error): ValidationError.properties no longer enumerable #3925
+ * docs(validation): clarify how required validators work with nested schemas #3915
+ * fix: upgrade mongodb driver -> 2.1.8 to make partial index errors more sane #3864
+
+4.4.7 / 2016-03-11
+==================
+ * fix(query): stop infinite recursion caused by merging a mongoose buffer #3961
+ * fix(populate): handle deep populate array -> array #3954
+ * fix(schema): allow setting timestamps with .set() #3952 #3951 #3907 [Fonger](https://github.com/Fonger)
+ * fix: MongooseThenable doesn't overwrite constructors #3940
+ * fix(schema): don't cast boolean to date #3935
+ * fix(drivers): support sslValidate in connection string #3929
+ * fix(types): correct markModified() for single nested subdocs #3910
+ * fix(drivers): catch and report any errors that occur in driver methods #3906
+ * fix(populate): get subpopulate model correctly when array under nested #3904
+ * fix(document): allow fields named 'pre' and 'post' #3902
+ * docs(query): clarify runValidators and setDefaultsOnInsert options #3892
+ * docs(validation): show how to use custom required messages in schema #2616
+
+4.4.6 / 2016-03-02
+==================
+ * fix: upgrade mongodb driver to 2.1.7 #3938
+ * docs: fix plugins link #3917 #3909 [fbertone](https://github.com/fbertone)
+ * fix(query): sort+select with count works #3914
+ * fix(query): improve mergeUpdate's ability to handle nested docs #3890
+
+4.4.5 / 2016-02-24
+==================
+ * fix(query): ability to select a length field (upgrade to mquery 1.7.0) #3903
+ * fix: include nested CastError as reason for array CastError #3897 [kotarou3](https://github.com/kotarou3)
+ * fix(schema): check for doc existence before taking fields #3889
+ * feat(schema): useNestedStrict option to take nested strict mode for update #3883
+ * docs(validation): clarify relationship between required and checkRequired #3822
+ * docs(populate): dynamic reference docs #3809
+ * docs: expand dropdown when clicking on file name #3807
+ * docs: plugins.mongoosejs.io is up #3127
+ * fix(schema): ability to add a virtual with same name as removed path #2398
+
+4.4.4 / 2016-02-17
+==================
+ * fix(schema): handle field selection when casting single nested subdocs #3880
+ * fix(populate): populating using base model with multiple child models in result #3878
+ * fix: ability to properly use return value of `mongoose.connect()` #3874
+ * fix(populate): dont hydrate populated subdoc if lean option set #3873
+ * fix(connection): dont re-auth if already connected with useDb #3871
+ * docs: cover how to set underlying driver's promise lib #3869
+ * fix(document): handle conflicting names in validation errors with subdocs #3867
+ * fix(populate): set undefined instead of null consistently when populate couldn't find results #3859
+ * docs: link to `execPopulate()` in `doc.populate()` docs #3836
+ * docs(plugin): link to the `mongoose.plugin()` function #3732
+
+4.4.3 / 2016-02-09
+==================
+ * fix: upgrade to mongodb 2.1.6 to remove kerberos log output #3861 #3860 [cartuchogl](https://github.com/cartuchogl)
+ * fix: require('mongoose') is no longer a pseudo-promise #3856
+ * fix(query): update casting for single nested docs #3820
+ * fix(populate): deep populating multiple paths with same options #3808
+ * docs(middleware): clarify save/validate hook order #1149
+
+4.4.2 / 2016-02-05
+==================
+ * fix(aggregate): handle calling .cursor() with no options #3855
+ * fix: upgrade mongodb driver to 2.1.5 for GridFS memory leak fix #3854
+ * docs: fix schematype.html conflict #3853 #3850 #3843
+ * fix(model): bluebird unhandled rejection with ensureIndexes() on init #3837
+ * docs: autoIndex option for createConnection #3805
+
+4.4.1 / 2016-02-03
+==================
+ * fix: linting broke some cases where we use `== null` as shorthand #3852
+ * docs: fix up schematype.html conflict #3848 #3843 [mynameiscoffey](https://github.com/mynameiscoffey)
+ * fix: backwards breaking change with `.connect()` return value #3847
+ * docs: downgrade dox and highlight.js to fix docs build #3845
+ * docs: clean up typo #3842 [Flash-](https://github.com/Flash-)
+ * fix(document): storeShard handles undefined values #3841
+ * chore: more linting #3838 [TrejGun](https://github.com/TrejGun)
+ * fix(schema): handle `text: true` as a way to declare a text index #3824
+
+4.4.0 / 2016-02-02
+==================
+ * docs: fix expireAfterSeconds index option name #3831 [Flash-](https://github.com/Flash-)
+ * chore: run lint after test #3829 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * chore: use power-assert instead of assert #3828 [TrejGun](https://github.com/TrejGun)
+ * chore: stricter lint #3827 [TrejGun](https://github.com/TrejGun)
+ * feat(types): casting moment to date #3813 [TrejGun](https://github.com/TrejGun)
+ * chore: comma-last lint for test folder #3810 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix: upgrade async mpath, mpromise, muri, and sliced #3801 [TrejGun](https://github.com/TrejGun)
+ * fix(query): geo queries now return proper ES2015 promises #3800 [TrejGun](https://github.com/TrejGun)
+ * perf(types): use `Object.defineProperties()` for array #3799 [TrejGun](https://github.com/TrejGun)
+ * fix(model): mapReduce, ensureIndexes, remove, and save properly return ES2015 promises #3795 #3628 #3595 [TrejGun](https://github.com/TrejGun)
+ * docs: fixed dates in History.md #3791 [Jokero](https://github.com/Jokero)
+ * feat: connect, open, openSet, and disconnect return ES2015 promises #3790 #3622 [TrejGun](https://github.com/TrejGun)
+ * feat: custom type for int32 via mongoose-int32 npm package #3652 #3102
+ * feat: basic custom schema type API #995
+ * feat(model): `insertMany()` for more performant bulk inserts #723
+
+4.3.7 / 2016-01-23
+==================
+ * docs: grammar fix in timestamps docs #3786 [zclancy](https://github.com/zclancy)
+ * fix(document): setting nested populated docs #3783 [slamuu](https://github.com/slamuu)
+ * fix(document): don't call post save hooks twice for pushed docs #3780
+ * fix(model): handle `_id=0` correctly #3776
+ * docs(middleware): async post hooks #3770
+ * docs: remove confusing sentence #3765 [marcusmellis89](https://github.com/marcusmellis89)
+
+3.8.39 / 2016-01-15
+===================
+ * fixed; casting a number to a buffer #3764
+ * fixed; enumerating virtual property with nested objects #3743 [kusold](https://github.com/kusold)
+
+4.3.6 / 2016-01-15
+==================
+ * fix(types): casting a number to a buffer #3764
+ * fix: add "listener" to reserved keywords #3759
+ * chore: upgrade uglify #3757 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix: broken execPopulate() in 4.3.5 #3755 #3753
+ * fix: ability to remove() a single embedded doc #3754
+ * style: comma-last in test folder #3751 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * docs: clarify versionKey option #3747
+ * fix: improve colorization for arrays #3744 [TrejGun](https://github.com/TrejGun)
+ * fix: webpack build #3713
+
+4.3.5 / 2016-01-09
+==================
+ * fix(query): throw when 4th parameter to update not a function #3741 [kasselTrankos](https://github.com/kasselTrankos)
+ * fix(document): separate error type for setting an object to a primitive #3735
+ * fix(populate): Model.populate returns ES6 promise #3734
+ * fix(drivers): re-register event handlers after manual reconnect #3729
+ * docs: broken links #3727
+ * fix(validation): update validators run array validation #3724
+ * docs: clarify the need to use markModified with in-place date ops #3722
+ * fix(document): mark correct path as populated when manually populating array #3721
+ * fix(aggregate): support for array pipeline argument to append #3718 [dbkup](https://github.com/dbkup)
+ * docs: clarify `.connect()` callback #3705
+ * fix(schema): properly validate nested single nested docs #3702
+ * fix(types): handle setting documentarray of wrong type #3701
+ * docs: broken links #3700
+ * fix(drivers): debug output properly displays '0' #3689
+
+3.8.38 / 2016-01-07
+===================
+ * fixed; aggregate.append an array #3730 [dbkup](https://github.com/dbkup)
+
+4.3.4 / 2015-12-23
+==================
+ * fix: upgrade mongodb driver to 2.1.2 for repl set error #3712 [sansmischevia](https://github.com/sansmischevia)
+ * docs: validation docs typo #3709 [ivanmaeder](https://github.com/ivanmaeder)
+ * style: remove unused variables #3708 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix(schema): duck-typing for schemas #3703 [mgcrea](https://github.com/mgcrea)
+ * docs: connection sample code issue #3697
+ * fix(schema): duck-typing for schemas #3693 [mgcrea](https://github.com/mgcrea)
+ * docs: clarify id schema option #3638
+
+4.3.3 / 2015-12-18
+==================
+ * fix(connection): properly support 'replSet' as well as 'replset' #3688 [taxilian](https://github.com/taxilian)
+ * fix(document): single nested doc pre hooks called before nested doc array #3687 [aliatsis](https://github.com/aliatsis)
+
+4.3.2 / 2015-12-17
+==================
+ * fix(document): .set() into single nested schemas #3686
+ * fix(connection): support 'replSet' as well as 'replset' option #3685
+ * fix(document): bluebird unhandled rejection when validating doc arrays #3681
+ * fix(document): hooks for doc arrays in single nested schemas #3680
+ * fix(document): post hooks for single nested schemas #3679
+ * fix: remove unused npm module #3674 [sybarite](https://github.com/sybarite)
+ * fix(model): don't swallow exceptions in nested doc save callback #3671
+ * docs: update keepAlive info #3667 [ChrisZieba](https://github.com/ChrisZieba)
+ * fix(document): strict 'throw' throws a specific mongoose error #3662
+ * fix: flakey test #3332
+ * fix(query): more robust check for RegExp #2969
+
+4.3.1 / 2015-12-11
+==================
+ * feat(aggregate): `.sample()` helper #3665
+ * fix(query): bitwise query operators with buffers #3663
+ * docs(migration): clarify `new` option and findByIdAndUpdate #3661
+
+4.3.0 / 2015-12-09
+==================
+ * feat(query): support for mongodb 3.2 bitwise query operators #3660
+ * style: use comma-last style consistently #3657 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * feat: upgrade mongodb driver to 2.1.0 for full MongoDB 3.2 support #3656
+ * feat(aggregate): `.lookup()` helper #3532
+
+4.2.10 / 2015-12-08
+===================
+ * fixed; upgraded marked #3653 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * docs; cross-db populate #3648
+ * docs; update mocha URL #3646 [ojhaujjwal](https://github.com/ojhaujjwal)
+ * fixed; call close callback asynchronously #3645
+ * docs; virtuals.html issue #3644 [Psarna94](https://github.com/Psarna94)
+ * fixed; single embedded doc casting on init #3642
+ * docs; validation docs improvements #3640
+
+4.2.9 / 2015-12-02
+==================
+ * docs; defaults docs #3625
+ * fix; nested numeric keys causing an embedded document crash #3623
+ * fix; apply path getters before virtual getters #3618
+ * fix; casting for arrays in single nested schemas #3616
+
+4.2.8 / 2015-11-25
+==================
+ * docs; clean up README links #3612 [ReadmeCritic](https://github.com/ReadmeCritic)
+ * fix; ESLint improvements #3605 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix; assigning single nested subdocs #3601
+ * docs; describe custom logging functions in `mongoose.set()` docs #3557
+
+4.2.7 / 2015-11-20
+==================
+ * fixed; readPreference connection string option #3600
+ * fixed; pulling from manually populated arrays #3598 #3579
+ * docs; FAQ about OverwriteModelError #3597 [stcruy](https://github.com/stcruy)
+ * fixed; setting single embedded schemas to null #3596
+ * fixed; indexes for single embedded schemas #3594
+ * docs; clarify projection for `findOne()` #3593 [gunar](https://github.com/gunar)
+ * fixed; .ownerDocument() method on single embedded schemas #3589
+ * fixed; properly throw casterror for query on single embedded schema #3580
+ * upgraded; mongodb driver -> 2.0.49 for reconnect issue fix #3481
+
+4.2.6 / 2015-11-16
+==================
+ * fixed; ability to manually populate an array #3575
+ * docs; clarify `isAsync` parameter to hooks #3573
+ * fixed; use captureStackTrace if possible instead #3571
+ * fixed; crash with buffer and update validators #3565 [johnpeb](https://github.com/johnpeb)
+ * fixed; update casting with operators overwrite: true #3564
+ * fixed; validation with single embedded docs #3562
+ * fixed; inline docs inherit parents $type key #3560
+ * docs; bad grammar in populate docs #3559 [amaurymedeiros](https://github.com/amaurymedeiros)
+ * fixed; properly handle populate option for find() #2321
+
+3.8.37 / 2015-11-16
+===================
+ * fixed; use retainKeyOrder for cloning update op #3572
+
+4.2.5 / 2015-11-09
+==================
+ * fixed; handle setting fields in pre update hooks with exec #3549
+ * upgraded; ESLint #3547 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; bluebird unhandled rejections with cast errors and .exec #3543
+ * fixed; min/max validators handling undefined #3539
+ * fixed; standalone mongos connections #3537
+ * fixed; call `.toObject()` when setting a single nested doc #3535
+ * fixed; single nested docs now have methods #3534
+ * fixed; single nested docs with .create() #3533 #3521 [tusbar](https://github.com/tusbar)
+ * docs; deep populate docs #3528
+ * fixed; deep populate schema ref handling #3507
+ * upgraded; mongodb driver -> 2.0.48 for sort overflow issue #3493
+ * docs; clarify default ids for discriminators #3482
+ * fixed; properly support .update(doc) #3221
+
+4.2.4 / 2015-11-02
+==================
+ * fixed; upgraded `ms` package for security vulnerability #3524 [fhemberger](https://github.com/fhemberger)
+ * fixed; ESlint rules #3517 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * docs; typo in aggregation docs #3513 [rafakato](https://github.com/rafakato)
+ * fixed; add `dontThrowCastError` option to .update() for promises #3512
+ * fixed; don't double-cast buffers in node 4.x #3510 #3496
+ * fixed; population with single embedded schemas #3501
+ * fixed; pre('set') hooks work properly #3479
+ * docs; promises guide #3441
+
+4.2.3 / 2015-10-26
+==================
+ * docs; remove unreferenced function in middleware.jade #3506
+ * fixed; handling auth with no username/password #3500 #3498 #3484 [mleanos](https://github.com/mleanos)
+ * fixed; more ESlint rules #3491 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; swallowing exceptions in save callback #3478
+ * docs; fixed broken links in subdocs guide #3477
+ * fixed; casting booleans to numbers #3475
+ * fixed; report CastError for subdoc arrays in findOneAndUpdate #3468
+ * fixed; geoNear returns ES6 promise #3458
+
+4.2.2 / 2015-10-22
+==================
+ * fixed; go back to old pluralization code #3490
+
+4.2.1 / 2015-10-22
+==================
+ * fixed; pluralization issues #3492 [ChristianMurphy](https://github.com/ChristianMurphy)
+
+4.2.0 / 2015-10-22
+==================
+ * added; support for skipVersioning for document arrays #3467 [chazmo03](https://github.com/chazmo03)
+ * added; ability to customize schema 'type' key #3459 #3245
+ * fixed; writeConcern for index builds #3455
+ * added; emit event when individual index build starts #3440 [objectiveSee](https://github.com/objectiveSee)
+ * added; 'context' option for update validators #3430
+ * refactor; pluralization now in separate pluralize-mongoose npm module #3415 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * added; customizable error validation messages #3406 [geronime](https://github.com/geronime)
+ * added; support for passing 'minimize' option to update #3381
+ * added; ability to customize debug logging format #3261
+ * added; baseModelName property for discriminator models #3202
+ * added; 'emitIndexErrors' option #3174
+ * added; 'async' option for aggregation cursor to support buffering #3160
+ * added; ability to skip validation for individual save() calls #2981
+ * added; single embedded schema support #2689 #585
+ * added; depopulate function #2509
+
+4.1.12 / 2015-10-19
+===================
+ * docs; use readPreference instead of slaveOk for Query.setOptions docs #3471 [buunguyen](https://github.com/buunguyen)
+ * fixed; more helpful error when regexp contains null bytes #3456
+ * fixed; x509 auth issue #3454 [NoxHarmonium](https://github.com/NoxHarmonium)
+
+3.8.36 / 2015-10-18
+===================
+ * fixed; Make array props non-enumerable #3461 [boblauer](https://github.com/boblauer)
+
+4.1.11 / 2015-10-12
+===================
+ * fixed; update timestamps for update() if they're enabled #3450 [isayme](https://github.com/isayme)
+ * fixed; unit test error on node 0.10 #3449 [isayme](https://github.com/isayme)
+ * docs; timestamp option docs #3448 [isayme](https://github.com/isayme)
+ * docs; fix unexpected indent #3443 [isayme](https://github.com/isayme)
+ * fixed; use ES6 promises for Model.prototype.remove() #3442
+ * fixed; don't use unused 'safe' option for index builds #3439
+ * fixed; elemMatch casting bug #3437 #3435 [DefinitelyCarter](https://github.com/DefinitelyCarter)
+ * docs; schema.index docs #3434
+ * fixed; exceptions in save() callback getting swallowed on mongodb 2.4 #3371
+
+4.1.10 / 2015-10-05
+===================
+ * docs; improve virtuals docs to explain virtuals schema option #3433 [zoyaH](https://github.com/zoyaH)
+ * docs; MongoDB server version compatibility guide #3427
+ * docs; clarify that findById and findByIdAndUpdate fire hooks #3422
+ * docs; clean up Model.save() docs #3420
+ * fixed; properly handle projection with just id #3407 #3412
+ * fixed; infinite loop when database document is corrupted #3405
+ * docs; clarify remove middleware #3388
+
+4.1.9 / 2015-09-28
+==================
+ * docs; minlength and maxlength string validation docs #3368 #3413 [cosmosgenius](https://github.com/cosmosgenius)
+ * fixed; linting for infix operators #3397 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; proper casting for $all #3394
+ * fixed; unhandled rejection warnings with .create() #3391
+ * docs; clarify update validators on paths that aren't explicitly set #3386
+ * docs; custom validator examples #2778
+
+4.1.8 / 2015-09-21
+==================
+ * docs; fixed typo in example #3390 [kmctown](https://github.com/kmctown)
+ * fixed; error in toObject() #3387 [guumaster](https://github.com/guumaster)
+ * fixed; handling for casting null dates #3383 [alexmingoia](https://github.com/alexmingoia)
+ * fixed; passing composite ids to `findByIdAndUpdate` #3380
+ * fixed; linting #3376 #3375 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; added NodeJS v4 to Travis #3374 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; casting $elemMatch inside of $not #3373 [gaguirre](https://github.com/gaguirre)
+ * fixed; handle case where $slice is 0 #3369
+ * fixed; avoid running getters if path is populated #3357
+ * fixed; cast documents to objects when setting to a nested path #3346
+
+4.1.7 / 2015-09-14
+==================
+ * docs; typos in SchemaType documentation #3367 [jasson15](https://github.com/jasson15)
+ * fixed; MONGOOSE_DRIVER_PATH env variable again #3360
+ * docs; added validateSync docs #3353
+ * fixed; set findOne op synchronously in query #3344
+ * fixed; handling for `.pull()` on a documentarray without an id #3341
+ * fixed; use natural order for cloning update conditions #3338
+ * fixed; issue with strict mode casting for mixed type updates #3337
+
+4.1.6 / 2015-09-08
+==================
+ * fixed; MONGOOSE_DRIVER_PATH env variable #3345 [g13013](https://github.com/g13013)
+ * docs; global autoIndex option #3335 [albertorestifo](https://github.com/albertorestifo)
+ * docs; model documentation typos #3330
+ * fixed; report reason for CastError #3320
+ * fixed; .populate() no longer returns true after re-assigning #3308
+ * fixed; discriminators with aggregation geoNear #3304
+ * docs; discriminator docs #2743
+
+4.1.5 / 2015-09-01
+==================
+ * fixed; document.remove() removing all docs #3326 #3325
+ * fixed; connect() checks for rs_name in options #3299
+ * docs; examples for schema.set() #3288
+ * fixed; checkKeys issue with bluebird #3286 [gregthegeek](https://github.com/gregthegeek)
+
+4.1.4 / 2015-08-31
+==================
+ * fixed; ability to set strict: false for update #3305
+ * fixed; .create() properly uses ES6 promises #3297
+ * fixed; pre hooks on nested subdocs #3291 #3284 [aliatsis](https://github.com/aliatsis)
+ * docs; remove unclear text in .remove() docs #3282
+ * fixed; pre hooks called twice for 3rd-level nested doc #3281
+ * fixed; nested transforms #3279
+ * upgraded; mquery -> 1.6.3 #3278 #3272
+ * fixed; don't swallow callback errors by default #3273 #3222
+ * fixed; properly get nested paths from nested schemas #3265
+ * fixed; remove() with id undefined deleting all docs #3260 [thanpolas](https://github.com/thanpolas)
+ * fixed; handling for non-numeric projections #3256
+ * fixed; findById with id undefined returning first doc #3255
+ * fixed; use retainKeyOrder for update #3215
+ * added; passRawResult option to findOneAndUpdate for backwards compat #3173
+
+4.1.3 / 2015-08-16
+==================
+ * fixed; getUpdate() in pre update hooks #3520 [gregthegeek](https://github.com/gregthegeek)
+ * fixed; handleArray() ensures arg is an array #3238 [jloveridge](https://github.com/jloveridge)
+ * fixed; refresh required path cache when recreating docs #3199
+ * fixed; $ operator on unwind aggregation helper #3197
+ * fixed; findOneAndUpdate() properly returns raw result as third arg to callback #3173
+ * fixed; querystream with dynamic refs #3108
+
+3.8.35 / 2015-08-14
+===================
+ * fixed; handling for minimize on nested objects #2930
+ * fixed; don't crash when schema.path.options undefined #1824
+
+4.1.2 / 2015-08-10
+==================
+ * fixed; better handling for Jade templates #3241 [kbadk](https://github.com/kbadk)
+ * added; ESlint trailing spaces #3234 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * added; ESlint #3191 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; properly emit event on disconnect #3183
+ * fixed; copy options properly using Query.toConstructor() #3176
+ * fixed; setMaxListeners() issue in browser build #3170
+ * fixed; node driver -> 2.0.40 to not store undefined keys as null #3169
+ * fixed; update validators handle positional operator #3167
+ * fixed; handle $all + $elemMatch query casting #3163
+ * fixed; post save hooks don't swallow extra args #3155
+ * docs; spelling mistake in index.jade #3154
+ * fixed; don't crash when toObject() has no fields #3130
+ * fixed; apply toObject() recursively for find and update queries #3086 [naoina](https://github.com/naoina)
+
+4.1.1 / 2015-08-03
+==================
+ * fixed; aggregate exec() crash with no callback #3212 #3198 [jpgarcia](https://github.com/jpgarcia)
+ * fixed; pre init hooks now properly synchronous #3207 [burtonjc](https://github.com/burtonjc)
+ * fixed; updateValidators doesn't flatten dates #3206 #3194 [victorkohl](https://github.com/victorkohl)
+ * fixed; default fields don't make document dirty between saves #3205 [burtonjc](https://github.com/burtonjc)
+ * fixed; save passes 0 as numAffected rather than undefined when no change #3195 [burtonjc](https://github.com/burtonjc)
+ * fixed; better handling for positional operator in update #3185
+ * fixed; use Travis containers #3181 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; leaked variable #3180 [ChristianMurphy](https://github.com/ChristianMurphy)
+
+4.1.0 / 2015-07-24
+==================
+ * added; `schema.queue()` now public #3193
+ * added; raw result as third parameter to findOneAndX callback #3173
+ * added; ability to run validateSync() on only certain fields #3153
+ * added; subPopulate #3103 [timbur](https://github.com/timbur)
+ * added; $isDefault function on documents #3077
+ * added; additional properties for built-in validator messages #3063 [KLicheR](https://github.com/KLicheR)
+ * added; getQuery() and getUpdate() functions for Query #3013
+ * added; export DocumentProvider #2996
+ * added; ability to remove path from schema #2993 [JohnnyEstilles](https://github.com/JohnnyEstilles)
+ * added; .explain() helper for aggregate #2714
+ * added; ability to specify which ES6-compatible promises library mongoose uses #2688
+ * added; export Aggregate #1910
+
+4.0.8 / 2015-07-20
+==================
+ * fixed; assignment with document arrays #3178 [rosston](https://github.com/rosston)
+ * docs; remove duplicate paragraph #3164 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; improve findOneAndXYZ parameter descriptions #3159 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; add findOneAndRemove to list of supported middleware #3158
+ * docs; clarify ensureIndex #3156
+ * fixed; refuse to save/remove document without id #3118
+ * fixed; hooks next() no longer accidentally returns promise #3104
+ * fixed; strict mode for findOneAndUpdate #2947
+ * added; .min.js.gz file for browser component #2806
+
+3.8.34 / 2015-07-20
+===================
+ * fixed; allow using $rename #3171
+ * fixed; no longer modifies update arguments #3008
+
+4.0.7 / 2015-07-11
+==================
+ * fixed; documentarray id method when using object id #3157 [siboulet](https://github.com/siboulet)
+ * docs; improve findById docs #3147
+ * fixed; update validators handle null properly #3136 [odeke-em](https://github.com/odeke-em)
+ * docs; jsdoc syntax errors #3128 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; fix typo #3126 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; proper formatting in queries.jade #3121 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; correct example for string maxlength validator #3111 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * fixed; setDefaultsOnInsert with arrays #3107
+ * docs; LearnBoost -> Automattic in package.json #3099
+ * docs; pre update hook example #3094 [danpe](https://github.com/danpe)
+ * docs; clarify query middleware example #3051
+ * fixed; ValidationErrors in strict mode #3046
+ * fixed; set findOneAndUpdate properties before hooks run #3024
+
+3.8.33 / 2015-07-10
+===================
+ * upgraded; node driver -> 1.4.38
+ * fixed; dont crash when `match` validator undefined
+
+4.0.6 / 2015-06-21
+==================
+ * upgraded; node driver -> 2.0.34 #3087
+ * fixed; apply setters on addToSet, etc #3067 [victorkohl](https://github.com/victorkohl)
+ * fixed; missing semicolons #3065 [sokolikp](https://github.com/sokolikp)
+ * fixed; proper handling for async doc hooks #3062 [gregthegeek](https://github.com/gregthegeek)
+ * fixed; dont set failed populate field to null if other docs are successfully populated #3055 [eloytoro](https://github.com/eloytoro)
+ * fixed; setDefaultsOnInsert with document arrays #3034 [taxilian](https://github.com/taxilian)
+ * fixed; setters fired on array items #3032
+ * fixed; stop validateSync() on first error #3025 [victorkohl](https://github.com/victorkohl)
+ * docs; improve query docs #3016
+ * fixed; always exclude _id when its deselected #3010
+ * fixed; enum validator kind property #3009
+ * fixed; mquery collection names #3005
+ * docs; clarify mongos option #3000
+ * docs; clarify that query builder has a .then() #2995
+ * fixed; race condition in dynamic ref #2992
+
+3.8.31 / 2015-06-20
+===================
+ * fixed; properly handle text search with discriminators and $meta #2166
+
+4.0.5 / 2015-06-05
+==================
+ * fixed; ObjectIds and buffers when mongodb driver is a sibling dependency #3050 #3048 #3040 #3031 #3020 #2988 #2951
+ * fixed; warn user when 'increment' is used in schema #3039
+ * fixed; setDefaultsOnInsert with array in schema #3035
+ * fixed; dont use default Object toString to cast to string #3030
+ * added; npm badge #3020 [odeke-em](https://github.com/odeke-em)
+ * fixed; proper handling for calling .set() with a subdoc #2782
+ * fixed; dont throw cast error when using $rename on non-string path #1845
+
+3.8.30 / 2015-06-05
+===================
+ * fixed; enable users to set all options with tailable() #2883
+
+4.0.4 / 2015-05-28
+==================
+ * docs; findAndModify new parameter correct default value #3012 [JonForest](https://github.com/JonForest)
+ * docs; clarify pluralization rules #2999 [anonmily](https://github.com/anonmily)
+ * fix; discriminators with schema methods #2978
+ * fix; make `isModified` a schema reserved keyword #2975
+ * fix; properly fire setters when initializing path with object #2943
+ * fix; can use `setDefaultsOnInsert` without specifying `runValidators` #2938
+ * fix; always set validation errors `kind` property #2885
+ * upgraded; node driver -> 2.0.33 #2865
+
+3.8.29 / 2015-05-27
+===================
+ * fixed; Handle JSON.stringify properly for nested docs #2990
+
+4.0.3 / 2015-05-13
+==================
+ * upgraded; mquery -> 1.5.1 #2983
+ * docs; clarify context for query middleware #2974
+ * docs; fix missing type -> kind rename in History.md #2961
+ * fixed; broken ReadPreference include on Heroku #2957
+ * docs; correct form for cursor aggregate option #2955
+ * fixed; sync post hooks now properly called after function #2949 #2925
+ * fixed; fix sub-doc validate() function #2929
+ * upgraded; node driver -> 2.0.30 #2926
+ * docs; retainKeyOrder for save() #2924
+ * docs; fix broken class names #2913
+ * fixed; error when using node-clone on a doc #2909
+ * fixed; no more hard references to bson #2908 #2906
+ * fixed; dont overwrite array values #2907 [naoina](https://github.com/naoina)
+ * fixed; use readPreference=primary for findOneAndUpdate #2899 #2823
+ * docs; clarify that update validators only run on $set and $unset #2889
+ * fixed; set kind consistently for built-in validators #2885
+ * docs; single field populated documents #2884
+ * fixed; nested objects are now enumerable #2880 [toblerpwn](https://github.com/toblerpwn)
+ * fixed; properly populate field when ref, lean, stream used together #2841
+ * docs; fixed migration guide jade error #2807
+
+3.8.28 / 2015-05-12
+===================
+ * fixed; proper handling for toJSON options #2910
+ * fixed; dont attach virtuals to embedded docs in update() #2046
+
+4.0.2 / 2015-04-23
+==================
+ * fixed; error thrown when calling .validate() on subdoc not in an array #2902
+ * fixed; rename define() to play nice with webpack #2900 [jspears](https://github.com/jspears)
+ * fixed; pre validate called twice with discriminators #2892
+ * fixed; .inspect() on mongoose.Types #2875
+ * docs; correct callback params for Model.update #2872
+ * fixed; setDefaultsOnInsert now works when runValidators not specified #2870
+ * fixed; Document now wraps EventEmitter.addListener #2867
+ * fixed; call non-hook functions in schema queue #2856
+ * fixed; statics can be mocked out for tests #2848 [ninelb](https://github.com/ninelb)
+ * upgraded; mquery 1.4.0 for bluebird bug fix #2846
+ * fixed; required validators run first #2843
+ * docs; improved docs for new option to findAndMody #2838
+ * docs; populate example now uses correct field #2837 [swilliams](https://github.com/swilliams)
+ * fixed; pre validate changes causing VersionError #2835
+ * fixed; get path from correct place when setting CastError #2832
+ * docs; improve docs for Model.update() function signature #2827 [irnc](https://github.com/irnc)
+ * fixed; populating discriminators #2825 [chetverikov](https://github.com/chetverikov)
+ * fixed; discriminators with nested schemas #2821
+ * fixed; CastErrors with embedded docs #2819
+ * fixed; post save hook context #2816
+ * docs; 3.8.x -> 4.x migration guide #2807
+ * fixed; proper _distinct copying for query #2765 [cdelauder](https://github.com/cdelauder)
+
+3.8.27 / 2015-04-22
+===================
+ * fixed; dont duplicate db calls on Q.ninvoke() #2864
+ * fixed; Model.find arguments naming in docs #2828
+ * fixed; Support ipv6 in connection strings #2298
+
+3.8.26 / 2015-04-07
+===================
+ * fixed; TypeError when setting date to undefined #2833
+ * fixed; handle CastError properly in distinct() with no callback #2786
+ * fixed; broken links in queries docs #2779
+ * fixed; dont mark buffer as modified when setting type initially #2738
+ * fixed; dont crash when using slice with populate #1934
+
+4.0.1 / 2015-03-28
+==================
+ * fixed; properly handle empty cast doc in update() with promises #2796
+ * fixed; unstable warning #2794
+ * fixed; findAndModify docs now show new option is false by default #2793
+
+4.0.0 / 2015-03-25
+==================
+ * fixed; on-the-fly schema docs typo #2783 [artiifix](https://github.com/artiifix)
+ * fixed; cast error validation handling #2775 #2766 #2678
+ * fixed; discriminators with populate() #2773 #2719 [chetverikov](https://github.com/chetverikov)
+ * fixed; increment now a reserved path #2709
+ * fixed; avoid sending duplicate object ids in populate() #2683
+ * upgraded; mongodb to 2.0.24 to properly emit reconnect event multiple times #2656
+
+4.0.0-rc4 / 2015-03-14
+======================
+ * fixed; toObject virtuals schema option handled properly #2751
+ * fixed; update validators work on document arrays #2733
+ * fixed; check for cast errors on $set #2729
+ * fixed; instance field set for all schema types #2727 [csdco](https://github.com/csdco)
+ * fixed; dont run other validators if required fails #2725
+ * fixed; custom getters execute on ref paths #2610
+ * fixed; save defaults if they were set when doc was loaded from db #2558
+ * fixed; pre validate now runs before pre save #2462
+ * fixed; no longer throws errors with --use_strict #2281
+
+3.8.25 / 2015-03-13
+===================
+ * fixed; debug output reverses order of aggregation keys #2759
+ * fixed; $eq is a valid query selector in 3.0 #2752
+ * fixed; upgraded node driver to 1.4.32 for handling non-numeric poolSize #2682
+ * fixed; update() with overwrite sets _id for nested docs #2658
+ * fixed; casting for operators in $elemMatch #2199
+
+4.0.0-rc3 / 2015-02-28
+======================
+ * fixed; update() pre hooks run before validators #2706
+ * fixed; setters not called on arrays of refs #2698 [brandom](https://github.com/brandom)
+ * fixed; use node driver 2.0.18 for nodejs 0.12 support #2685
+ * fixed; comments reference file that no longer exists #2681
+ * fixed; populated() returns _id of manually populated doc #2678
+ * added; ability to exclude version key in toObject() #2675
+ * fixed; dont allow setting nested path to a string #2592
+ * fixed; can cast objects with _id field to ObjectIds #2581
+ * fixed; on-the-fly schema getters #2360
+ * added; strict option for findOneAndUpdate() #1967
+
+3.8.24 / 2015-02-25
+===================
+ * fixed; properly apply child schema transforms #2691
+ * fixed; make copy of findOneAndUpdate options before modifying #2687
+ * fixed; apply defaults when parent path is selected #2670 #2629
+ * fixed; properly get ref property for nested paths #2665
+ * fixed; node driver makes copy of authenticate options before modifying them #2619
+ * fixed; dont block process exit when auth fails #2599
+ * fixed; remove redundant clone in update() #2537
+
+4.0.0-rc2 / 2015-02-10
+======================
+ * added; io.js to travis build
+ * removed; browser build dependencies not installed by default
+ * added; dynamic refpaths #2640 [chetverikov](https://github.com/chetverikov)
+ * fixed; dont call child schema transforms on parent #2639 [chetverikov](https://github.com/chetverikov)
+ * fixed; get rid of remove option if new is set in findAndModify #2598
+ * fixed; aggregate all document array validation errors #2589
+ * fixed; custom setters called when setting value to undefined #1892
+
+3.8.23 / 2015-02-06
+===================
+ * fixed; unset opts.remove when upsert is true #2519
+ * fixed; array saved as object when path is object in array #2442
+ * fixed; inline transforms #2440
+ * fixed; check for callback in count() #2204
+ * fixed; documentation for selecting fields #1534
+
+4.0.0-rc1 / 2015-02-01
+======================
+ * fixed; use driver 2.0.14
+ * changed; use transform: true by default #2245
+
+4.0.0-rc0 / 2015-01-31
+===================
+ * fixed; wrong order for distinct() params #2628
+ * fixed; handling no query argument to remove() #2627
+ * fixed; createModel and discriminators #2623 [ashaffer](https://github.com/ashaffer)
+ * added; pre('count') middleware #2621
+ * fixed; double validation calls on document arrays #2618
+ * added; validate() catches cast errors #2611
+ * fixed; respect replicaSet parameter in connection string #2609
+ * added; can explicitly exclude paths from versioning #2576 [csabapalfi](https://github.com/csabapalfi)
+ * upgraded; driver to 2.0.15 #2552
+ * fixed; save() handles errors more gracefully in ES6 #2371
+ * fixed; undefined is now a valid argument to findOneAndUpdate #2272
+ * changed; `new` option to findAndModify ops is false by default #2262
+
+3.8.22 / 2015-01-24
+===================
+ * upgraded; node-mongodb-native to 1.4.28 #2587 [Climax777](https://github.com/Climax777)
+ * added; additional documentation for validators #2449
+ * fixed; stack overflow when creating massive arrays #2423
+ * fixed; undefined is a valid id for queries #2411
+ * fixed; properly create nested schema index when same schema used twice #2322
+ * added; link to plugin generator in docs #2085 [huei90](https://github.com/huei90)
+ * fixed; optional arguments documentation for findOne() #1971 [nachinius](https://github.com/nachinius)
+
+3.9.7 / 2014-12-19
+===================
+ * added; proper cursors for aggregate #2539 [changyy](https://github.com/changyy)
+ * added; min/max built-in validators for dates #2531 [bshamblen](https://github.com/bshamblen)
+ * fixed; save and validate are now reserved keywords #2380
+ * added; basic documentation for browser component #2256
+ * added; find and findOne hooks (query middleware) #2138
+ * fixed; throw a DivergentArrayError when saving positional operator queries #2031
+ * added; ability to use options as a document property #1416
+ * fixed; document no longer inherits from event emitter and so domain and _events are no longer reserved #1351
+ * removed; setProfiling #1349
+
+3.8.21 / 2014-12-18
+===================
+ * fixed; syntax in index.jade #2517 [elderbas](https://github.com/elderbas)
+ * fixed; writable statics #2510 #2528
+ * fixed; overwrite and explicit $set casting #2515
+
+3.9.6 / 2014-12-05
+===================
+ * added; correctly run validators on each element of array when entire array is modified #661 #1227
+ * added; castErrors in validation #1013 [jondavidjohn](https://github.com/jondavidjohn)
+ * added; specify text indexes in schema fields #1401 [sr527](https://github.com/sr527)
+ * added; ability to set field with validators to undefined #1594 [alabid](https://github.com/alabid)
+ * added; .create() returns an array when passed an array #1746 [alabid](https://github.com/alabid)
+ * added; test suite and docs for use with co and yield #2177 #2474
+ * fixed; subdocument toObject() transforms #2447 [chmanie](https://github.com/chmanie)
+ * fixed; Model.create() with save errors #2484
+ * added; pass options to .save() and .remove() #2494 [jondavidjohn](https://github.com/jondavidjohn)
+
+3.8.20 / 2014-12-01
+===================
+ * fixed; recursive readPref #2490 [kjvalencik](https://github.com/kjvalencik)
+ * fixed; make sure to copy parameters to update() before modifying #2406 [alabid](https://github.com/alabid)
+ * fixed; unclear documentation about query callbacks #2319
+ * fixed; setting a schema-less field to an empty object #2314 [alabid](https://github.com/alabid)
+ * fixed; registering statics and methods for discriminators #2167 [alabid](https://github.com/alabid)
+
+3.9.5 / 2014-11-10
+===================
+ * added; ability to disable autoIndex on a per-connection basis #1875 [sr527](https://github.com/sr527)
+ * fixed; `geoNear()` no longer enforces legacy coordinate pairs - supports GeoJSON #1987 [alabid](https://github.com/alabid)
+ * fixed; browser component works when minified with mangled variable names #2302
+ * fixed; `doc.errors` now cleared before `validate()` called #2302
+ * added; `execPopulate()` function to make `doc.populate()` compatible with promises #2317
+ * fixed; `count()` no longer throws an error when used with `sort()` #2374
+ * fixed; `save()` no longer recursively calls `save()` on populated fields #2418
+
+3.8.19 / 2014-11-09
+===================
+ * fixed; make sure to not override subdoc _ids on find #2276 [alabid](https://github.com/alabid)
+ * fixed; exception when comparing two documents when one lacks _id #2333 [slawo](https://github.com/slawo)
+ * fixed; getters for properties with non-strict schemas #2439 [alabid](https://github.com/alabid)
+ * fixed; inconsistent URI format in docs #2414 [sr527](https://github.com/sr527)
+
+3.9.4 / 2014-10-25
+==================
+ * fixed; statics no longer can be overwritten #2343 [nkcmr](https://github.com/chetverikov)
+ * added; ability to set single populated paths to documents #1530
+ * added; setDefaultsOnInsert and runValidator options for findOneAndUpdate() #860
+
+3.8.18 / 2014-10-22
+==================
+ * fixed; Dont use all toObject options in save #2340 [chetverikov](https://github.com/chetverikov)
+
+3.9.3 / 2014-10-01
+=================
+ * added; support for virtuals that return objects #2294
+ * added; ability to manually hydrate POJOs into mongoose objects #2292
+ * added; setDefaultsOnInsert and runValidator options for update() #860
+
+3.8.17 / 2014-09-29
+==================
+ * fixed; use schema options retainKeyOrder in save() #2274
+ * fixed; fix skip in populate when limit is set #2252
+ * fixed; fix stack overflow when passing MongooseArray to findAndModify #2214
+ * fixed; optimize .length usage in populate #2289
+
+3.9.2 / 2014-09-08
+==================
+ * added; test coverage for browser component #2255
+ * added; in-order execution of validators #2243
+ * added; custom fields for validators #2132
+ * removed; exception thrown when find() used with count() #1950
+
+3.8.16 / 2014-09-08
+==================
+ * fixed; properly remove modified array paths if array has been overwritten #1638
+ * fixed; key check errors #1884
+ * fixed; make sure populate on an array always returns a Mongoose array #2214
+ * fixed; SSL connections with node 0.11 #2234
+ * fixed; return sensible strings for promise errors #2239
+
+3.9.1 / 2014-08-17
+==================
+ * added; alpha version of browser-side schema validation #2254
+ * added; support passing a function to schemas `required` field #2247
+ * added; support for setting updatedAt and createdAt timestamps #2227
+ * added; document.validate() returns a promise #2131
+
+3.8.15 / 2014-08-17
+==================
+ * fixed; Replica set connection string example in docs #2246
+ * fixed; bubble up parseError event #2229
+ * fixed; removed buggy populate cache #2176
+ * fixed; dont $inc versionKey if its being $set #1933
+ * fixed; cast $or and $and in $pull #1932
+ * fixed; properly cast to schema in stream() #1862
+ * fixed; memory leak in nested objects #1565 #2211 [devongovett](https://github.com/devongovett)
+
+3.8.14 / 2014-07-26
+==================
+ * fixed; stringifying MongooseArray shows nested arrays #2002
+ * fixed; use populated doc schema in toObject and toJSON by default #2035
+ * fixed; dont crash on arrays containing null #2140
+ * fixed; model.update w/ upsert has same return values on .exec and promise #2143
+ * fixed; better handling for populate limit with multiple documents #2151
+ * fixed; dont prevent users from adding weights to text index #2183
+ * fixed; helper for aggregation cursor #2187
+ * updated; node-mongodb-native to 1.4.7
+
+3.8.13 / 2014-07-15
+==================
+ * fixed; memory leak with isNew events #2159
+ * fixed; docs for overwrite option for update() #2144
+ * fixed; storeShard() handles dates properly #2127
+ * fixed; sub-doc changes not getting persisted to db after save #2082
+ * fixed; populate with _id: 0 actually removes _id instead of setting to undefined #2123
+ * fixed; save versionKey on findOneAndUpdate w/ upsert #2122
+ * fixed; fix typo in 2.8 docs #2120 [shakirullahi](https://github.com/shakirullahi)
+ * fixed; support maxTimeMs #2102 [yuukinajima](https://github.com/yuukinajima)
+ * fixed; support $currentDate #2019
+ * fixed; $addToSet handles objects without _ids properly #1973
+ * fixed; dont crash on invalid nearSphere query #1874
+
+3.8.12 / 2014-05-30
+==================
+ * fixed; single-server reconnect event fires #1672
+ * fixed; sub-docs not saved when pushed into populated array #1794
+ * fixed; .set() sometimes converts embedded docs to pojos #1954 [archangel-irk](https://github.com/archangel-irk)
+ * fixed; sub-doc changes not getting persisted to db after save #2082
+ * fixed; custom getter might cause mongoose to mistakenly think a path is dirty #2100 [pgherveou](https://github.com/pgherveou)
+ * fixed; chainable helper for allowDiskUse option in aggregation #2114
+
+3.9.0 (unstable) / 2014-05-22
+==================
+ * changed; added `domain` to reserved keywords #1338 #2052 [antoinepairet](https://github.com/antoinepairet)
+ * added; asynchronous post hooks #1977 #2081 [chopachom](https://github.com/chopachom) [JasonGhent](https://github.com/JasonGhent)
+ * added; using model for population, cross-db populate [mihai-chiorean](https://github.com/mihai-chiorean)
+ * added; can define a type for schema validators
+ * added; `doc.remove()` returns a promise #1619 [refack](https://github.com/refack)
+ * added; internal promises for hooks, pre-save hooks run in parallel #1732 [refack](https://github.com/refack)
+ * fixed; geoSearch hanging when no results returned #1846 [ghartnett](https://github.com/ghartnett)
+ * fixed; do not set .type property on ValidationError, use .kind instead #1323
+
+3.8.11 / 2014-05-22
+==================
+ * updated; node-mongodb-native to 1.4.5
+ * reverted; #2052, fixes #2097
+
+3.8.10 / 2014-05-20
+==================
+
+ * updated; node-mongodb-native to 1.4.4
+ * fixed; _.isEqual false negatives bug in js-bson #2070
+ * fixed; missing check for schema.options #2014
+ * fixed; missing support for $position #2024
+ * fixed; options object corruption #2049
+ * fixed; improvements to virtuals docs #2055
+ * fixed; added `domain` to reserved keywords #2052 #1338
+
+3.8.9 / 2014-05-08
+==================
+
+ * updated; mquery to 0.7.0
+ * updated; node-mongodb-native to 1.4.3
+ * fixed; $near failing against MongoDB 2.6
+ * fixed; relying on .options() to determine if collection exists
+ * fixed; $out aggregate helper
+ * fixed; all test failures against MongoDB 2.6.1, with caveat #2065
+
+3.8.8 / 2014-02-22
+==================
+
+ * fixed; saving Buffers #1914
+ * updated; expose connection states for user-land #1926 [yorkie](https://github.com/yorkie)
+ * updated; mquery to 0.5.3
+ * updated; added get / set to reserved path list #1903 [tstrimple](https://github.com/tstrimple)
+ * docs; README code highlighting, syntax fixes #1930 [IonicaBizau](https://github.com/IonicaBizau)
+ * docs; fixes link in the doc at #1925 [kapeels](https://github.com/kapeels)
+ * docs; add a missed word 'hook' for the description of the post-hook api #1924 [ipoval](https://github.com/ipoval)
+
+3.8.7 / 2014-02-09
+==================
+
+ * fixed; sending safe/read options in Query#exec #1895
+ * fixed; findOneAnd..() with sort #1887
+
+3.8.6 / 2014-01-30
+==================
+
+ * fixed; setting readPreferences #1895
+
+3.8.5 / 2014-01-23
+==================
+
+ * fixed; ssl setting when using URI #1882
+ * fixed; findByIdAndUpdate now respects the overwrite option #1809 [owenallenaz](https://github.com/owenallenaz)
+
+3.8.4 / 2014-01-07
+==================
+
+ * updated; mongodb driver to 1.3.23
+ * updated; mquery to 0.4.1
+ * updated; mpromise to 0.4.3
+ * fixed; discriminators now work when selecting fields #1820 [daemon1981](https://github.com/daemon1981)
+ * fixed; geoSearch with no results timeout #1846 [ghartnett](https://github.com/ghartnett)
+ * fixed; infitite recursion in ValidationError #1834 [chetverikov](https://github.com/chetverikov)
+
+3.8.3 / 2013-12-17
+==================
+
+ * fixed; setting empty array with model.update #1838
+ * docs; fix url
+
+3.8.2 / 2013-12-14
+==================
+
+ * fixed; enum validation of multiple values #1778 [heroicyang](https://github.com/heroicyang)
+ * fixed; global var leak #1803
+ * fixed; post remove now fires on subdocs #1810
+ * fixed; no longer set default empty array for geospatial-indexed fields #1668 [shirish87](https://github.com/shirish87)
+ * fixed; model.stream() not hydrating discriminators correctly #1792 [j](https://github.com/j)
+ * docs: Stablility -> Stability [nikmartin](https://github.com/nikmartin)
+ * tests; improve shard error handling
+
+3.8.1 / 2013-11-19
+==================
+
+ * fixed; mishandling of Dates with minimize/getters #1764
+ * fixed; Normalize bugs.email, so `npm` will shut up #1769 [refack](https://github.com/refack)
+ * docs; Improve the grammar where "lets us" was used #1777 [alexyoung](https://github.com/alexyoung)
+ * docs; Fix some grammatical issues in the documentation #1777 [alexyoung](https://github.com/alexyoung)
+ * docs; fix Query api exposure
+ * docs; fix return description
+ * docs; Added Notes on findAndUpdate() #1750 [sstadelman](https://github.com/sstadelman)
+ * docs; Update version number in README #1762 [Fodi69](https://github.com/Fodi69)
+
+3.8.0 / 2013-10-31
+==================
+
+ * updated; warn when using an unstable version
+ * updated; error message returned in doc.save() #1595
+ * updated; mongodb driver to 1.3.19 (fix error swallowing behavior)
+ * updated; mquery to 0.3.2
+ * updated; mocha to 1.12.0
+ * updated; mpromise 0.3.0
+ * updated; sliced 0.0.5
+ * removed; mongoose.Error.DocumentError (never used)
+ * removed; namedscope (undocumented and broken) #679 #642 #455 #379
+ * changed; no longer offically supporting node 0.6.x
+ * changed; query.within getter is now a method -> query.within()
+ * changed; query.intersects getter is now a method -> query.intersects()
+ * added; custom error msgs for built-in validators #747
+ * added; discriminator support #1647 #1003 [j](https://github.com/j)
+ * added; support disabled collection name pluralization #1350 #1707 [refack](https://github.com/refack)
+ * added; support for GeoJSON to Query#near [ebensing](https://github.com/ebensing)
+ * added; stand-alone base query support - query.toConstructor() [ebensing](https://github.com/ebensing)
+ * added; promise support to geoSearch #1614 [ebensing](https://github.com/ebensing)
+ * added; promise support for geoNear #1614 [ebensing](https://github.com/ebensing)
+ * added; connection.useDb() #1124 [ebensing](https://github.com/ebensing)
+ * added; promise support to model.mapReduce()
+ * added; promise support to model.ensureIndexes()
+ * added; promise support to model.populate()
+ * added; benchmarks [ebensing](https://github.com/ebensing)
+ * added; publicly exposed connection states #1585
+ * added; $geoWithin support #1529 $1455 [ebensing](https://github.com/ebensing)
+ * added; query method chain validation
+ * added; model.update `overwrite` option
+ * added; model.geoNear() support #1563 [ebensing](https://github.com/ebensing)
+ * added; model.geoSearch() support #1560 [ebensing](https://github.com/ebensing)
+ * added; MongooseBuffer#subtype()
+ * added; model.create() now returns a promise #1340
+ * added; support for `awaitdata` query option
+ * added; pass the doc to doc.remove() callback #1419 [JoeWagner](https://github.com/JoeWagner)
+ * added; aggregation query builder #1404 [njoyard](https://github.com/njoyard)
+ * fixed; document.toObject when using `minimize` and `getters` options #1607 [JedWatson](https://github.com/JedWatson)
+ * fixed; Mixed types can now be required #1722 [Reggino](https://github.com/Reggino)
+ * fixed; do not pluralize model names not ending with letters #1703 [refack](https://github.com/refack)
+ * fixed; repopulating modified populated paths #1697
+ * fixed; doc.equals() when _id option is set to false #1687
+ * fixed; strict mode warnings #1686
+ * fixed; $near GeoJSON casting #1683
+ * fixed; nearSphere GeoJSON query builder
+ * fixed; population field selection w/ strings #1669
+ * fixed; setters not firing on null values #1445 [ebensing](https://github.com/ebensing)
+ * fixed; handle another versioning edge case #1520
+ * fixed; excluding subdocument fields #1280 [ebensing](https://github.com/ebensing)
+ * fixed; allow array properties to be set to null with findOneAndUpdate [aheuermann](https://github.com/aheuermann)
+ * fixed; subdocuments now use own toJSON opts #1376 [ebensing](https://github.com/ebensing)
+ * fixed; model#geoNear fulfills promise when results empty #1658 [ebensing](https://github.com/ebensing)
+ * fixed; utils.merge no longer overrides props and methods #1655 [j](https://github.com/j)
+ * fixed; subdocuments now use their own transform #1412 [ebensing](https://github.com/ebensing)
+ * fixed; model.remove() removes only what is necessary #1649
+ * fixed; update() now only runs with cb or explicit true #1644
+ * fixed; casting ref docs on creation #1606 [ebensing](https://github.com/ebensing)
+ * fixed; model.update "overwrite" option works as documented
+ * fixed; query#remove() works as documented
+ * fixed; "limit" correctly applies to individual items on population #1490 [ebensing](https://github.com/ebensing)
+ * fixed; issue with positional operator on ref docs #1572 [ebensing](https://github.com/ebensing)
+ * fixed; benchmarks to actually output valid json
+ * deprecated; promise#addBack (use promise#onResolve)
+ * deprecated; promise#complete (use promise#fulfill)
+ * deprecated; promise#addCallback (use promise#onFulFill)
+ * deprecated; promise#addErrback (use promise#onReject)
+ * deprecated; query.nearSphere() (use query.near)
+ * deprecated; query.center() (use query.circle)
+ * deprecated; query.centerSphere() (use query.circle)
+ * deprecated; query#slaveOk (use query#read)
+ * docs; custom validator messages
+ * docs; 10gen -> MongoDB
+ * docs; add Date method caveats #1598
+ * docs; more validation details
+ * docs; state which branch is stable/unstable
+ * docs; mention that middleware does not run on Models
+ * docs; promise.fulfill()
+ * docs; fix readme spelling #1483 [yorchopolis](https://github.com/yorchopolis)
+ * docs; fixed up the README and examples [ebensing](https://github.com/ebensing)
+ * website; add "show code" for properties
+ * website; move "show code" links down
+ * website; update guide
+ * website; add unstable docs
+ * website; many improvements
+ * website; fix copyright #1439
+ * website; server.js -> static.js #1546 [nikmartin](https://github.com/nikmartin)
+ * tests; refactor 1703
+ * tests; add test generator
+ * tests; validate formatMessage() throws
+ * tests; add script for continuously running tests
+ * tests; fixed versioning tests
+ * tests; race conditions in tests
+ * tests; added for nested and/or queries
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; replace deprecated method names
+ * tests; convert id to string
+ * tests; fix sharding tests for MongoDB 2.4.5
+ * tests; now 4-5 seconds faster
+ * tests; fix race condition
+ * make; suppress warning msg in test
+ * benchmarks; updated for pull requests
+ * examples; improved and expanded [ebensing](https://github.com/ebensing)
+
+3.7.4 (unstable) / 2013-10-01
+=============================
+
+ * updated; mquery to 0.3.2
+ * removed; mongoose.Error.DocumentError (never used)
+ * added; custom error msgs for built-in validators #747
+ * added; discriminator support #1647 #1003 [j](https://github.com/j)
+ * added; support disabled collection name pluralization #1350 #1707 [refack](https://github.com/refack)
+ * fixed; do not pluralize model names not ending with letters #1703 [refack](https://github.com/refack)
+ * fixed; repopulating modified populated paths #1697
+ * fixed; doc.equals() when _id option is set to false #1687
+ * fixed; strict mode warnings #1686
+ * fixed; $near GeoJSON casting #1683
+ * fixed; nearSphere GeoJSON query builder
+ * fixed; population field selection w/ strings #1669
+ * docs; custom validator messages
+ * docs; 10gen -> MongoDB
+ * docs; add Date method caveats #1598
+ * docs; more validation details
+ * website; add "show code" for properties
+ * website; move "show code" links down
+ * tests; refactor 1703
+ * tests; add test generator
+ * tests; validate formatMessage() throws
+
+3.7.3 (unstable) / 2013-08-22
+=============================
+
+ * updated; warn when using an unstable version
+ * updated; mquery to 0.3.1
+ * updated; mocha to 1.12.0
+ * updated; mongodb driver to 1.3.19 (fix error swallowing behavior)
+ * changed; no longer offically supporting node 0.6.x
+ * added; support for GeoJSON to Query#near [ebensing](https://github.com/ebensing)
+ * added; stand-alone base query support - query.toConstructor() [ebensing](https://github.com/ebensing)
+ * added; promise support to geoSearch #1614 [ebensing](https://github.com/ebensing)
+ * added; promise support for geoNear #1614 [ebensing](https://github.com/ebensing)
+ * fixed; setters not firing on null values #1445 [ebensing](https://github.com/ebensing)
+ * fixed; handle another versioning edge case #1520
+ * fixed; excluding subdocument fields #1280 [ebensing](https://github.com/ebensing)
+ * fixed; allow array properties to be set to null with findOneAndUpdate [aheuermann](https://github.com/aheuermann)
+ * fixed; subdocuments now use own toJSON opts #1376 [ebensing](https://github.com/ebensing)
+ * fixed; model#geoNear fulfills promise when results empty #1658 [ebensing](https://github.com/ebensing)
+ * fixed; utils.merge no longer overrides props and methods #1655 [j](https://github.com/j)
+ * fixed; subdocuments now use their own transform #1412 [ebensing](https://github.com/ebensing)
+ * make; suppress warning msg in test
+ * docs; state which branch is stable/unstable
+ * docs; mention that middleware does not run on Models
+ * tests; add script for continuously running tests
+ * tests; fixed versioning tests
+ * benchmarks; updated for pull requests
+
+3.7.2 (unstable) / 2013-08-15
+==================
+
+ * fixed; model.remove() removes only what is necessary #1649
+ * fixed; update() now only runs with cb or explicit true #1644
+ * tests; race conditions in tests
+ * website; update guide
+
+3.7.1 (unstable) / 2013-08-13
+=============================
+
+ * updated; driver to 1.3.18 (fixes memory leak)
+ * added; connection.useDb() #1124 [ebensing](https://github.com/ebensing)
+ * added; promise support to model.mapReduce()
+ * added; promise support to model.ensureIndexes()
+ * added; promise support to model.populate()
+ * fixed; casting ref docs on creation #1606 [ebensing](https://github.com/ebensing)
+ * fixed; model.update "overwrite" option works as documented
+ * fixed; query#remove() works as documented
+ * fixed; "limit" correctly applies to individual items on population #1490 [ebensing](https://github.com/ebensing)
+ * fixed; issue with positional operator on ref docs #1572 [ebensing](https://github.com/ebensing)
+ * fixed; benchmarks to actually output valid json
+ * tests; added for nested and/or queries
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; replace deprecated method names
+ * tests; convert id to string
+ * docs; promise.fulfill()
+
+3.7.0 (unstable) / 2013-08-05
+===================
+
+ * changed; query.within getter is now a method -> query.within()
+ * changed; query.intersects getter is now a method -> query.intersects()
+ * deprecated; promise#addBack (use promise#onResolve)
+ * deprecated; promise#complete (use promise#fulfill)
+ * deprecated; promise#addCallback (use promise#onFulFill)
+ * deprecated; promise#addErrback (use promise#onReject)
+ * deprecated; query.nearSphere() (use query.near)
+ * deprecated; query.center() (use query.circle)
+ * deprecated; query.centerSphere() (use query.circle)
+ * deprecated; query#slaveOk (use query#read)
+ * removed; namedscope (undocumented and broken) #679 #642 #455 #379
+ * added; benchmarks [ebensing](https://github.com/ebensing)
+ * added; publicly exposed connection states #1585
+ * added; $geoWithin support #1529 $1455 [ebensing](https://github.com/ebensing)
+ * added; query method chain validation
+ * added; model.update `overwrite` option
+ * added; model.geoNear() support #1563 [ebensing](https://github.com/ebensing)
+ * added; model.geoSearch() support #1560 [ebensing](https://github.com/ebensing)
+ * added; MongooseBuffer#subtype()
+ * added; model.create() now returns a promise #1340
+ * added; support for `awaitdata` query option
+ * added; pass the doc to doc.remove() callback #1419 [JoeWagner](https://github.com/JoeWagner)
+ * added; aggregation query builder #1404 [njoyard](https://github.com/njoyard)
+ * updated; integrate mquery #1562 [ebensing](https://github.com/ebensing)
+ * updated; error msg in doc.save() #1595
+ * updated; bump driver to 1.3.15
+ * updated; mpromise 0.3.0
+ * updated; sliced 0.0.5
+ * tests; fix sharding tests for MongoDB 2.4.5
+ * tests; now 4-5 seconds faster
+ * tests; fix race condition
+ * docs; fix readme spelling #1483 [yorchopolis](https://github.com/yorchopolis)
+ * docs; fixed up the README and examples [ebensing](https://github.com/ebensing)
+ * website; add unstable docs
+ * website; many improvements
+ * website; fix copyright #1439
+ * website; server.js -> static.js #1546 [nikmartin](https://github.com/nikmartin)
+ * examples; improved and expanded [ebensing](https://github.com/ebensing)
+
+3.6.20 (stable) / 2013-09-23
+===================
+
+ * fixed; repopulating modified populated paths #1697
+ * fixed; doc.equals w/ _id false #1687
+ * fixed; strict mode warning #1686
+ * docs; near/nearSphere
+
+3.6.19 (stable) / 2013-09-04
+==================
+
+ * fixed; population field selection w/ strings #1669
+ * docs; Date method caveats #1598
+
+3.6.18 (stable) / 2013-08-22
+===================
+
+ * updated; warn when using an unstable version of mongoose
+ * updated; mocha to 1.12.0
+ * updated; mongodb driver to 1.3.19 (fix error swallowing behavior)
+ * fixed; setters not firing on null values #1445 [ebensing](https://github.com/ebensing)
+ * fixed; properly exclude subdocument fields #1280 [ebensing](https://github.com/ebensing)
+ * fixed; cast error in findAndModify #1643 [aheuermann](https://github.com/aheuermann)
+ * website; update guide
+ * website; added documentation for safe:false and versioning interaction
+ * docs; mention that middleware dont run on Models
+ * docs; fix indexes link
+ * make; suppress warning msg in test
+ * tests; moar
+
+3.6.17 / 2013-08-13
+===================
+
+ * updated; driver to 1.3.18 (fixes memory leak)
+ * fixed; casting ref docs on creation #1606
+ * docs; query options
+
+3.6.16 / 2013-08-08
+===================
+
+ * added; publicly expose connection states #1585
+ * fixed; limit applies to individual items on population #1490 [ebensing](https://github.com/ebensing)
+ * fixed; positional operator casting in updates #1572 [ebensing](https://github.com/ebensing)
+ * updated; MongoDB driver to 1.3.17
+ * updated; sliced to 0.0.5
+ * website; tweak homepage
+ * tests; fixed + added
+ * docs; fix some examples
+ * docs; multi-mongos support details
+ * docs; auto open browser after starting static server
+
+3.6.15 / 2013-07-16
+==================
+
+ * added; mongos failover support #1037
+ * updated; make schematype return vals return self #1580
+ * docs; add note to model.update #571
+ * docs; document third param to document.save callback #1536
+ * tests; tweek mongos test timeout
+
+3.6.14 / 2013-07-05
+===================
+
+ * updated; driver to 1.3.11
+ * fixed; issue with findOneAndUpdate not returning null on upserts #1533 [ebensing](https://github.com/ebensing)
+ * fixed; missing return statement in SchemaArray#$geoIntersects() #1498 [bsrykt](https://github.com/bsrykt)
+ * fixed; wrong isSelected() behavior #1521 [kyano](https://github.com/kyano)
+ * docs; note about toObject behavior during save()
+ * docs; add callbacks details #1547 [nikmartin](https://github.com/nikmartin)
+
+3.6.13 / 2013-06-27
+===================
+
+ * fixed; calling model.distinct without conditions #1541
+ * fixed; regression in Query#count() #1542
+ * now working on 3.6.13
+
+3.6.12 / 2013-06-25
+===================
+
+ * updated; driver to 1.3.10
+ * updated; clearer capped collection error message #1509 [bitmage](https://github.com/bitmage)
+ * fixed; MongooseBuffer subtype loss during casting #1517 [zedgu](https://github.com/zedgu)
+ * fixed; docArray#id when doc.id is disabled #1492
+ * fixed; docArray#id now supports matches on populated arrays #1492 [pgherveou](https://github.com/pgherveou)
+ * website; fix example
+ * website; improve _id disabling example
+ * website; fix typo #1494 [dejj](https://github.com/dejj)
+ * docs; added a 'Requesting new features' section #1504 [shovon](https://github.com/shovon)
+ * docs; improve subtypes description
+ * docs; clarify _id disabling
+ * docs: display by alphabetical order the methods list #1508 [nicolasleger](https://github.com/nicolasleger)
+ * tests; refactor isSelected checks
+ * tests; remove pointless test
+ * tests; fixed timeouts
+
+3.6.11 / 2013-05-15
+===================
+
+ * updated; driver to 1.3.5
+ * fixed; compat w/ Object.create(null) #1484 #1485
+ * fixed; cloning objects w/ missing constructors
+ * fixed; prevent multiple min number validators #1481 [nrako](https://github.com/nrako)
+ * docs; add doc.increment() example
+ * docs; add $size example
+ * docs; add "distinct" example
+
+3.6.10 / 2013-05-09
+==================
+
+ * update driver to 1.3.3
+ * fixed; increment() works without other changes #1475
+ * website; fix links to posterous
+ * docs; fix link #1472
+
+3.6.9 / 2013-05-02
+==================
+
+ * fixed; depopulation of mixed documents #1471
+ * fixed; use of $options in array #1462
+ * tests; fix race condition
+ * docs; fix default example
+
+3.6.8 / 2013-04-25
+==================
+
+ * updated; driver to 1.3.0
+ * fixed; connection.model should retain options #1458 [vedmalex](https://github.com/vedmalex)
+ * tests; 4-5 seconds faster
+
+3.6.7 / 2013-04-19
+==================
+
+ * fixed; population regression in 3.6.6 #1444
+
+3.6.6 / 2013-04-18
+==================
+
+ * fixed; saving populated new documents #1442
+ * fixed; population regession in 3.6.5 #1441
+ * website; fix copyright #1439
+
+3.6.5 / 2013-04-15
+==================
+
+ * fixed; strict:throw edge case using .set(path, val)
+ * fixed; schema.pathType() on some numbericAlpha paths
+ * fixed; numbericAlpha path versioning
+ * fixed; setting nested mixed paths #1418
+ * fixed; setting nested objects with null prop #1326
+ * fixed; regression in v3.6 population performance #1426 [vedmalex](https://github.com/vedmalex)
+ * fixed; read pref typos #1422 [kyano](https://github.com/kyano)
+ * docs; fix method example
+ * website; update faq
+ * website; add more deep links
+ * website; update poolSize docs
+ * website; add 3.6 release notes
+ * website; note about keepAlive
+
+3.6.4 / 2013-04-03
+==================
+
+ * fixed; +field conflict with $slice #1370
+ * fixed; nested deselection conflict #1333
+ * fixed; RangeError in ValidationError.toString() #1296
+ * fixed; do not save user defined transforms #1415
+ * tests; fix race condition
+
+3.6.3 / 2013-04-02
+==================
+
+ * fixed; setting subdocuments deeply nested fields #1394
+ * fixed; regression: populated streams #1411
+ * docs; mention hooks/validation with findAndModify
+ * docs; mention auth
+ * docs; add more links
+ * examples; add document methods example
+ * website; display "see" links for properties
+ * website; clean up homepage
+
+3.6.2 / 2013-03-29
+==================
+
+ * fixed; corrupted sub-doc array #1408
+ * fixed; document#update returns a Query #1397
+ * docs; readpref strategy
+
+3.6.1 / 2013-03-27
+==================
+
+ * added; populate support to findAndModify varients #1395
+ * added; text index type to schematypes
+ * expose allowed index types as Schema.indexTypes
+ * fixed; use of `setMaxListeners` as path
+ * fixed; regression in node 0.6 on docs with > 10 arrays
+ * fixed; do not alter schema arguments #1364
+ * fixed; subdoc#ownerDocument() #1385
+ * website; change search id
+ * website; add search from google [jackdbernier](https://github.com/jackdbernier)
+ * website; fix link
+ * website; add 3.5.x docs release
+ * website; fix link
+ * docs; fix geometry
+ * docs; hide internal constructor
+ * docs; aggregation does not cast arguments #1399
+ * docs; querystream options
+ * examples; added for population
+
+3.6.0 / 2013-03-18
+==================
+
+ * changed; cast 'true'/'false' to boolean #1282 [mgrach](https://github.com/mgrach)
+ * changed; Buffer arrays can now contain nulls
+ * added; QueryStream transform option
+ * added; support for authSource driver option
+ * added; {mongoose,db}.modelNames()
+ * added; $push w/ $slice,$sort support (MongoDB 2.4)
+ * added; hashed index type (MongoDB 2.4)
+ * added; support for mongodb 2.4 geojson (MongoDB 2.4)
+ * added; value at time of validation error
+ * added; support for object literal schemas
+ * added; bufferCommands schema option
+ * added; allow auth option in connections #1360 [geoah](https://github.com/geoah)
+ * added; performance improvements to populate() [263ece9](https://github.com/LearnBoost/mongoose/commit/263ece9)
+ * added; allow adding uncasted docs to populated arrays and properties #570
+ * added; doc#populated(path) stores original populated _ids
+ * added; lean population #1260
+ * added; query.populate() now accepts an options object
+ * added; document#populate(opts, callback)
+ * added; Model.populate(docs, opts, callback)
+ * added; support for rich nested path population
+ * added; doc.array.remove(value) subdoc with _id value support #1278
+ * added; optionally allow non-strict sets and updates
+ * added; promises/A+ comformancy with [mpromise](https://github.com/aheckmann/mpromise)
+ * added; promise#then
+ * added; promise#end
+ * fixed; use of `model` as doc property
+ * fixed; lean population #1382
+ * fixed; empty object mixed defaults #1380
+ * fixed; populate w/ deselected _id using string syntax
+ * fixed; attempted save of divergent populated arrays #1334 related
+ * fixed; better error msg when attempting toObject as property name
+ * fixed; non population buffer casting from doc
+ * fixed; setting populated paths #570
+ * fixed; casting when added docs to populated arrays #570
+ * fixed; prohibit updating arrays selected with $elemMatch #1334
+ * fixed; pull / set subdoc combination #1303
+ * fixed; multiple bg index creation #1365
+ * fixed; manual reconnection to single mongod
+ * fixed; Constructor / version exposure #1124
+ * fixed; CastError race condition
+ * fixed; no longer swallowing misuse of subdoc#invalidate()
+ * fixed; utils.clone retains RegExp opts
+ * fixed; population of non-schema property
+ * fixed; allow updating versionKey #1265
+ * fixed; add EventEmitter props to reserved paths #1338
+ * fixed; can now deselect populated doc _ids #1331
+ * fixed; properly pass subtype to Binary in MongooseBuffer
+ * fixed; casting _id from document with non-ObjectId _id
+ * fixed; specifying schema type edge case { path: [{type: "String" }] }
+ * fixed; typo in schemdate #1329 [jplock](https://github.com/jplock)
+ * updated; driver to 1.2.14
+ * updated; muri to 0.3.1
+ * updated; mpromise to 0.2.1
+ * updated; mocha 1.8.1
+ * updated; mpath to 0.1.1
+ * deprecated; pluralization will die in 4.x
+ * refactor; rename private methods to something unusable as doc properties
+ * refactor MongooseArray#remove
+ * refactor; move expires index to SchemaDate #1328
+ * refactor; internal document properties #1171 #1184
+ * tests; added
+ * docs; indexes
+ * docs; validation
+ * docs; populate
+ * docs; populate
+ * docs; add note about stream compatibility with node 0.8
+ * docs; fix for private names
+ * docs; Buffer -> mongodb.Binary #1363
+ * docs; auth options
+ * docs; improved
+ * website; update FAQ
+ * website; add more api links
+ * website; add 3.5.x docs to prior releases
+ * website; Change mongoose-types to an active repo [jackdbernier](https://github.com/jackdbernier)
+ * website; compat with node 0.10
+ * website; add news section
+ * website; use T for generic type
+ * benchmark; make adjustable
+
+3.6.0rc1 / 2013-03-12
+======================
+
+ * refactor; rename private methods to something unusable as doc properties
+ * added; {mongoose,db}.modelNames()
+ * added; $push w/ $slice,$sort support (MongoDB 2.4)
+ * added; hashed index type (MongoDB 2.4)
+ * added; support for mongodb 2.4 geojson (MongoDB 2.4)
+ * added; value at time of validation error
+ * added; support for object literal schemas
+ * added; bufferCommands schema option
+ * added; allow auth option in connections #1360 [geoah](https://github.com/geoah)
+ * fixed; lean population #1382
+ * fixed; empty object mixed defaults #1380
+ * fixed; populate w/ deselected _id using string syntax
+ * fixed; attempted save of divergent populated arrays #1334 related
+ * fixed; better error msg when attempting toObject as property name
+ * fixed; non population buffer casting from doc
+ * fixed; setting populated paths #570
+ * fixed; casting when added docs to populated arrays #570
+ * fixed; prohibit updating arrays selected with $elemMatch #1334
+ * fixed; pull / set subdoc combination #1303
+ * fixed; multiple bg index creation #1365
+ * fixed; manual reconnection to single mongod
+ * fixed; Constructor / version exposure #1124
+ * fixed; CastError race condition
+ * fixed; no longer swallowing misuse of subdoc#invalidate()
+ * fixed; utils.clone retains RegExp opts
+ * fixed; population of non-schema property
+ * fixed; allow updating versionKey #1265
+ * fixed; add EventEmitter props to reserved paths #1338
+ * fixed; can now deselect populated doc _ids #1331
+ * updated; muri to 0.3.1
+ * updated; driver to 1.2.12
+ * updated; mpromise to 0.2.1
+ * deprecated; pluralization will die in 4.x
+ * docs; Buffer -> mongodb.Binary #1363
+ * docs; auth options
+ * docs; improved
+ * website; add news section
+ * benchmark; make adjustable
+
+3.6.0rc0 / 2013-02-03
+======================
+
+ * changed; cast 'true'/'false' to boolean #1282 [mgrach](https://github.com/mgrach)
+ * changed; Buffer arrays can now contain nulls
+ * fixed; properly pass subtype to Binary in MongooseBuffer
+ * fixed; casting _id from document with non-ObjectId _id
+ * fixed; specifying schema type edge case { path: [{type: "String" }] }
+ * fixed; typo in schemdate #1329 [jplock](https://github.com/jplock)
+ * refactor; move expires index to SchemaDate #1328
+ * refactor; internal document properties #1171 #1184
+ * added; performance improvements to populate() [263ece9](https://github.com/LearnBoost/mongoose/commit/263ece9)
+ * added; allow adding uncasted docs to populated arrays and properties #570
+ * added; doc#populated(path) stores original populated _ids
+ * added; lean population #1260
+ * added; query.populate() now accepts an options object
+ * added; document#populate(opts, callback)
+ * added; Model.populate(docs, opts, callback)
+ * added; support for rich nested path population
+ * added; doc.array.remove(value) subdoc with _id value support #1278
+ * added; optionally allow non-strict sets and updates
+ * added; promises/A+ comformancy with [mpromise](https://github.com/aheckmann/mpromise)
+ * added; promise#then
+ * added; promise#end
+ * updated; mocha 1.8.1
+ * updated; muri to 0.3.0
+ * updated; mpath to 0.1.1
+ * updated; docs
+
+3.5.16 / 2013-08-13
+===================
+
+ * updated; driver to 1.3.18
+
+3.5.15 / 2013-07-26
+==================
+
+ * updated; sliced to 0.0.5
+ * updated; driver to 1.3.12
+ * fixed; regression in Query#count() due to driver change
+ * tests; fixed timeouts
+ * tests; handle differing test uris
+
+3.5.14 / 2013-05-15
+===================
+
+ * updated; driver to 1.3.5
+ * fixed; compat w/ Object.create(null) #1484 #1485
+ * fixed; cloning objects missing constructors
+ * fixed; prevent multiple min number validators #1481 [nrako](https://github.com/nrako)
+
+3.5.13 / 2013-05-09
+==================
+
+ * update driver to 1.3.3
+ * fixed; use of $options in array #1462
+
+3.5.12 / 2013-04-25
+===================
+
+ * updated; driver to 1.3.0
+ * fixed; connection.model should retain options #1458 [vedmalex](https://github.com/vedmalex)
+ * fixed; read pref typos #1422 [kyano](https://github.com/kyano)
+
+3.5.11 / 2013-04-03
+==================
+
+ * fixed; +field conflict with $slice #1370
+ * fixed; RangeError in ValidationError.toString() #1296
+ * fixed; nested deselection conflict #1333
+ * remove time from Makefile
+
+3.5.10 / 2013-04-02
+==================
+
+ * fixed; setting subdocuments deeply nested fields #1394
+ * fixed; do not alter schema arguments #1364
+
+3.5.9 / 2013-03-15
+==================
+
+ * updated; driver to 1.2.14
+ * added; support for authSource driver option (mongodb 2.4)
+ * added; QueryStream transform option (node 0.10 helper)
+ * fixed; backport for saving required populated buffers
+ * fixed; pull / set subdoc combination #1303
+ * fixed; multiple bg index creation #1365
+ * test; added for saveable required populated buffers
+ * test; added for #1365
+ * test; add authSource test
+
+3.5.8 / 2013-03-12
+==================
+
+ * added; auth option in connection [geoah](https://github.com/geoah)
+ * fixed; CastError race condition
+ * docs; add note about stream compatibility with node 0.8
+
+3.5.7 / 2013-02-22
+==================
+
+ * updated; driver to 1.2.13
+ * updated; muri to 0.3.1 #1347
+ * fixed; utils.clone retains RegExp opts #1355
+ * fixed; deepEquals RegExp support
+ * tests; fix a connection test
+ * website; clean up docs [afshinm](https://github.com/afshinm)
+ * website; update homepage
+ * website; migragtion: emphasize impact of strict docs #1264
+
+3.5.6 / 2013-02-14
+==================
+
+ * updated; driver to 1.2.12
+ * fixed; properly pass Binary subtype
+ * fixed; add EventEmitter props to reserved paths #1338
+ * fixed; use correct node engine version
+ * fixed; display empty docs as {} in log output #953 follow up
+ * improved; "bad $within $box argument" error message
+ * populate; add unscientific benchmark
+ * website; add stack overflow to help section
+ * website; use better code font #1336 [risseraka](https://github.com/risseraka)
+ * website; clarify where help is available
+ * website; fix source code links #1272 [floatingLomas](https://github.com/floatingLomas)
+ * docs; be specific about _id schema option #1103
+ * docs; add ensureIndex error handling example
+ * docs; README
+ * docs; CONTRIBUTING.md
+
+3.5.5 / 2013-01-29
+==================
+
+ * updated; driver to 1.2.11
+ * removed; old node < 0.6x shims
+ * fixed; documents with Buffer _ids equality
+ * fixed; MongooseBuffer properly casts numbers
+ * fixed; reopening closed connection on alt host/port #1287
+ * docs; fixed typo in Readme #1298 [rened](https://github.com/rened)
+ * docs; fixed typo in migration docs [Prinzhorn](https://github.com/Prinzhorn)
+ * docs; fixed incorrect annotation in SchemaNumber#min [bilalq](https://github.com/bilalq)
+ * docs; updated
+
+3.5.4 / 2013-01-07
+==================
+
+ * changed; "_pres" & "_posts" are now reserved pathnames #1261
+ * updated; driver to 1.2.8
+ * fixed; exception when reopening a replica set. #1263 [ethankan](https://github.com/ethankan)
+ * website; updated
+
+3.5.3 / 2012-12-26
+==================
+
+ * added; support for geo object notation #1257
+ * fixed; $within query casting with arrays
+ * fixed; unix domain socket support #1254
+ * updated; driver to 1.2.7
+ * updated; muri to 0.0.5
+
+3.5.2 / 2012-12-17
+==================
+
+ * fixed; using auth with replica sets #1253
+
+3.5.1 / 2012-12-12
+==================
+
+ * fixed; regression when using subdoc with `path` as pathname #1245 [daeq](https://github.com/daeq)
+ * fixed; safer db option checks
+ * updated; driver to 1.2.5
+ * website; add more examples
+ * website; clean up old docs
+ * website; fix prev release urls
+ * docs; clarify streaming with HTTP responses
+
+3.5.0 / 2012-12-10
+==================
+
+ * added; paths to CastErrors #1239
+ * added; support for mongodb connection string spec #1187
+ * added; post validate event
+ * added; Schema#get (to retrieve schema options)
+ * added; VersionError #1071
+ * added; npmignore [hidekiy](https://github.com/hidekiy)
+ * update; driver to 1.2.3
+ * fixed; stackoverflow in setter #1234
+ * fixed; utils.isObject()
+ * fixed; do not clobber user specified driver writeConcern #1227
+ * fixed; always pass current document to post hooks
+ * fixed; throw error when user attempts to overwrite a model
+ * fixed; connection.model only caches on connection #1209
+ * fixed; respect conn.model() creation when matching global model exists #1209
+ * fixed; passing model name + collection name now always honors collection name
+ * fixed; setting virtual field to an empty object #1154
+ * fixed; subclassed MongooseErrors exposure, now available in mongoose.Error.xxxx
+ * fixed; model.remove() ignoring callback when executed twice [daeq](https://github.com/daeq) #1210
+ * docs; add collection option to schema api docs #1222
+ * docs; NOTE about db safe options
+ * docs; add post hooks docs
+ * docs; connection string options
+ * docs; middleware is not executed with Model.remove #1241
+ * docs; {g,s}etter introspection #777
+ * docs; update validation docs
+ * docs; add link to plugins page
+ * docs; clarify error returned by unique indexes #1225
+ * docs; more detail about disabling autoIndex behavior
+ * docs; add homepage section to package (npm docs mongoose)
+ * docs; more detail around collection name pluralization #1193
+ * website; add .important css
+ * website; update models page
+ * website; update getting started
+ * website; update quick start
+
+3.4.0 / 2012-11-10
+==================
+
+ * added; support for generic toJSON/toObject transforms #1160 #1020 #1197
+ * added; doc.set() merge support #1148 [NuORDER](https://github.com/NuORDER)
+ * added; query#add support #1188 [aleclofabbro](https://github.com/aleclofabbro)
+ * changed; adding invalid nested paths to non-objects throws 4216f14
+ * changed; fixed; stop invalid function cloning (internal fix)
+ * fixed; add query $and casting support #1180 [anotheri](https://github.com/anotheri)
+ * fixed; overwriting of query arguments #1176
+ * docs; fix expires examples
+ * docs; transforms
+ * docs; schema `collection` option docs [hermanjunge](https://github.com/hermanjunge)
+ * website; updated
+ * tests; added
+
+3.3.1 / 2012-10-11
+==================
+
+ * fixed; allow goose.connect(uris, dbname, opts) #1144
+ * docs; persist API private checked state across page loads
+
+3.3.0 / 2012-10-10
+==================
+
+ * fixed; passing options as 2nd arg to connect() #1144
+ * fixed; race condition after no-op save #1139
+ * fixed; schema field selection application in findAndModify #1150
+ * fixed; directly setting arrays #1126
+ * updated; driver to 1.1.11
+ * updated; collection pluralization rules [mrickard](https://github.com/mrickard)
+ * tests; added
+ * docs; updated
+
+3.2.2 / 2012-10-08
+==================
+
+ * updated; driver to 1.1.10 #1143
+ * updated; use sliced 0.0.3
+ * fixed; do not recast embedded docs unnecessarily
+ * fixed; expires schema option helper #1132
+ * fixed; built in string setters #1131
+ * fixed; debug output for Dates/ObjectId properties #1129
+ * docs; fixed Javascript syntax error in example [olalonde](https://github.com/olalonde)
+ * docs; fix toJSON example #1137
+ * docs; add ensureIndex production notes
+ * docs; fix spelling
+ * docs; add blogposts about v3
+ * website; updated
+ * removed; undocumented inGroupsOf util
+ * tests; added
+
+3.2.1 / 2012-09-28
+==================
+
+ * fixed; remove query batchSize option default of 1000 https://github.com/learnboost/mongoose/commit/3edaa8651
+ * docs; updated
+ * website; updated
+
+3.2.0 / 2012-09-27
+==================
+
+ * added; direct array index assignment with casting support `doc.array.set(index, value)`
+ * fixed; QueryStream#resume within same tick as pause() #1116
+ * fixed; default value validatation #1109
+ * fixed; array splice() not casting #1123
+ * fixed; default array construction edge case #1108
+ * fixed; query casting for inequalities in arrays #1101 [dpatti](https://github.com/dpatti)
+ * tests; added
+ * website; more documentation
+ * website; fixed layout issue #1111 [SlashmanX](https://github.com/SlashmanX)
+ * website; refactored [guille](https://github.com/guille)
+
+3.1.2 / 2012-09-10
+==================
+
+ * added; ReadPreferrence schema option #1097
+ * updated; driver to 1.1.7
+ * updated; default query batchSize to 1000
+ * fixed; we now cast the mapReduce query option #1095
+ * fixed; $elemMatch+$in with field selection #1091
+ * fixed; properly cast $elemMatch+$in conditions #1100
+ * fixed; default field application of subdocs #1027
+ * fixed; querystream prematurely dying #1092
+ * fixed; querystream never resumes when paused at getMore boundries #1092
+ * fixed; querystream occasionally emits data events after destroy #1092
+ * fixed; remove unnecessary ObjectId creation in querystream
+ * fixed; allow ne(boolean) again #1093
+ * docs; add populate/field selection syntax notes
+ * docs; add toObject/toJSON options detail
+ * docs; `read` schema option
+
+3.1.1 / 2012-08-31
+==================
+
+ * updated; driver to 1.1.6
+
+3.1.0 / 2012-08-29
+==================
+
+ * changed; fixed; directly setting nested objects now overwrites entire object (previously incorrectly merged them)
+ * added; read pref support (mongodb 2.2) 205a709c
+ * added; aggregate support (mongodb 2.2) f3a5bd3d
+ * added; virtual {g,s}etter introspection (#1070)
+ * updated; docs [brettz9](https://github.com/brettz9)
+ * updated; driver to 1.1.5
+ * fixed; retain virtual setter return values (#1069)
+
+3.0.3 / 2012-08-23
+==================
+
+ * fixed; use of nested paths beginning w/ numbers #1062
+ * fixed; query population edge case #1053 #1055 [jfremy](https://github.com/jfremy)
+ * fixed; simultaneous top and sub level array modifications #1073
+ * added; id and _id schema option aliases + tests
+ * improve debug formatting to allow copy/paste logged queries into mongo shell [eknkc](https://github.com/eknkc)
+ * docs
+
+3.0.2 / 2012-08-17
+==================
+
+ * added; missing support for v3 sort/select syntax to findAndModify helpers (#1058)
+ * fixed; replset fullsetup event emission
+ * fixed; reconnected event for replsets
+ * fixed; server reconnection setting discovery
+ * fixed; compat with non-schema path props using positional notation (#1048)
+ * fixed; setter/casting order (#665)
+ * docs; updated
+
+3.0.1 / 2012-08-11
+==================
+
+ * fixed; throw Error on bad validators (1044)
+ * fixed; typo in EmbeddedDocument#parentArray [lackac]
+ * fixed; repair mongoose.SchemaTypes alias
+ * updated; docs
+
+3.0.0 / 2012-08-07
+==================
+
+ * removed; old subdocument#commit method
+ * fixed; setting arrays of matching docs [6924cbc2]
+ * fixed; doc!remove event now emits in save order as save for consistency
+ * fixed; pre-save hooks no longer fire on subdocuments when validation fails
+ * added; subdoc#parent() and subdoc#parentArray() to access subdocument parent objects
+ * added; query#lean() helper
+
+3.0.0rc0 / 2012-08-01
+=====================
+
+ * fixed; allow subdoc literal declarations containing "type" pathname (#993)
+ * fixed; unsetting a default array (#758)
+ * fixed; boolean $in queries (#998)
+ * fixed; allow use of `options` as a pathname (#529)
+ * fixed; `model` is again a permitted schema path name
+ * fixed; field selection option on subdocs (#1022)
+ * fixed; handle another edge case with subdoc saving (#975)
+ * added; emit save err on model if listening
+ * added; MongoDB TTL collection support (#1006)
+ * added; $center options support
+ * added; $nearSphere and $polygon support
+ * updated; driver version to 1.1.2
+
+3.0.0alpha2 / 2012-07-18
+=========================
+
+ * changed; index errors are now emitted on their model and passed to an optional callback (#984)
+ * fixed; specifying index along with sparse/unique option no longer overwrites (#1004)
+ * fixed; never swallow connection errors (#618)
+ * fixed; creating object from model with emded object no longer overwrites defaults [achurkin] (#859)
+ * fixed; stop needless validation of unchanged/unselected fields (#891)
+ * fixed; document#equals behavior of objectids (#974)
+ * fixed; honor the minimize schema option (#978)
+ * fixed; provide helpful error msgs when reserved schema path is used (#928)
+ * fixed; callback to conn#disconnect is optional (#875)
+ * fixed; handle missing protocols in connection urls (#987)
+ * fixed; validate args to query#where (#969)
+ * fixed; saving modified/removed subdocs (#975)
+ * fixed; update with $pull from Mixed array (#735)
+ * fixed; error with null shard key value
+ * fixed; allow unsetting enums (#967)
+ * added; support for manual index creation (#984)
+ * added; support for disabled auto-indexing (#984)
+ * added; support for preserving MongooseArray#sort changes (#752)
+ * added; emit state change events on connection
+ * added; support for specifying BSON subtype in MongooseBuffer#toObject [jcrugzz]
+ * added; support for disabled versioning (#977)
+ * added; implicit "new" support for models and Schemas
+
+3.0.0alpha1 / 2012-06-15
+=========================
+
+ * removed; doc#commit (use doc#markModified)
+ * removed; doc.modified getter (#950)
+ * removed; mongoose{connectSet,createSetConnection}. use connect,createConnection instead
+ * removed; query alias methods 1149804c
+ * removed; MongooseNumber
+ * changed; now creating indexes in background by default
+ * changed; strict mode now enabled by default (#952)
+ * changed; doc#modifiedPaths is now a method (#950)
+ * changed; getters no longer cast (#820); casting happens during set
+ * fixed; no need to pass updateArg to findOneAndUpdate (#931)
+ * fixed: utils.merge bug when merging nested non-objects. [treygriffith]
+ * fixed; strict:throw should produce errors in findAndModify (#963)
+ * fixed; findAndUpdate no longer overwrites document (#962)
+ * fixed; setting default DocumentArrays (#953)
+ * fixed; selection of _id with schema deselection (#954)
+ * fixed; ensure promise#error emits instanceof Error
+ * fixed; CursorStream: No stack overflow on any size result (#929)
+ * fixed; doc#remove now passes safe options
+ * fixed; invalid use of $set during $pop
+ * fixed; array#{$pop,$shift} mirror MongoDB behavior
+ * fixed; no longer test non-required vals in string match (#934)
+ * fixed; edge case with doc#inspect
+ * fixed; setter order (#665)
+ * fixed; setting invalid paths in strict mode (#916)
+ * fixed; handle docs without id in DocumentArray#id method (#897)
+ * fixed; do not save virtuals during model.update (#894)
+ * fixed; sub doc toObject virtuals application (#889)
+ * fixed; MongooseArray#pull of ObjectId (#881)
+ * fixed; handle passing db name with any repl set string
+ * fixed; default application of selected fields (#870)
+ * fixed; subdoc paths reported in validation errors (#725)
+ * fixed; incorrect reported num of affected docs in update ops (#862)
+ * fixed; connection assignment in Model#model (#853)
+ * fixed; stringifying arrays of docs (#852)
+ * fixed; modifying subdoc and parent array works (#842)
+ * fixed; passing undefined to next hook (#785)
+ * fixed; Query#{update,remove}() works without callbacks (#788)
+ * fixed; set/updating nested objects by parent pathname (#843)
+ * fixed; allow null in number arrays (#840)
+ * fixed; isNew on sub doc after insertion error (#837)
+ * fixed; if an insert fails, set isNew back to false [boutell]
+ * fixed; isSelected when only _id is selected (#730)
+ * fixed; setting an unset default value (#742)
+ * fixed; query#sort error messaging (#671)
+ * fixed; support for passing $options with $regex
+ * added; array of object literal notation in schema creates DocumentArrays
+ * added; gt,gte,lt,lte query support for arrays (#902)
+ * added; capped collection support (#938)
+ * added; document versioning support
+ * added; inclusion of deselected schema path (#786)
+ * added; non-atomic array#pop
+ * added; EmbeddedDocument constructor is now exposed in DocArray#create 7cf8beec
+ * added; mapReduce support (#678)
+ * added; support for a configurable minimize option #to{Object,JSON}(option) (#848)
+ * added; support for strict: `throws` [regality]
+ * added; support for named schema types (#795)
+ * added; to{Object,JSON} schema options (#805)
+ * added; findByIdAnd{Update,Remove}()
+ * added; findOneAnd{Update,Remove}()
+ * added; query.setOptions()
+ * added; instance.update() (#794)
+ * added; support specifying model in populate() [DanielBaulig]
+ * added; `lean` query option [gitfy]
+ * added; multi-atomic support to MongooseArray#nonAtomicPush
+ * added; support for $set + other $atomic ops on single array
+ * added; tests
+ * updated; driver to 1.0.2
+ * updated; query.sort() syntax to mirror query.select()
+ * updated; clearer cast error msg for array numbers
+ * updated; docs
+ * updated; doc.clone 3x faster (#950)
+ * updated; only create _id if necessary (#950)
+
+2.7.3 / 2012-08-01
+==================
+
+ * fixed; boolean $in queries (#998)
+ * fixed field selection option on subdocs (#1022)
+
+2.7.2 / 2012-07-18
+==================
+
+ * fixed; callback to conn#disconnect is optional (#875)
+ * fixed; handle missing protocols in connection urls (#987)
+ * fixed; saving modified/removed subdocs (#975)
+ * updated; tests
+
+2.7.1 / 2012-06-26
+===================
+
+ * fixed; sharding: when a document holds a null as a value of the shard key
+ * fixed; update() using $pull on an array of Mixed (gh-735)
+ * deprecated; MongooseNumber#{inc, increment, decrement} methods
+ * tests; now using mocha
+
+2.7.0 / 2012-06-14
+===================
+
+ * added; deprecation warnings to methods being removed in 3.x
+
+2.6.8 / 2012-06-14
+===================
+
+ * fixed; edge case when using 'options' as a path name (#961)
+
+2.6.7 / 2012-06-08
+===================
+
+ * fixed; ensure promise#error always emits instanceof Error
+ * fixed; selection of _id w/ another excluded path (#954)
+ * fixed; setting default DocumentArrays (#953)
+
+2.6.6 / 2012-06-06
+===================
+
+ * fixed; stack overflow in query stream with large result sets (#929)
+ * added; $gt, $gte, $lt, $lte support to arrays (#902)
+ * fixed; pass option `safe` along to doc#remove() calls
+
+2.6.5 / 2012-05-24
+===================
+
+ * fixed; do not save virtuals in Model.update (#894)
+ * added; missing $ prefixed query aliases (going away in 3.x) (#884) [timoxley]
+ * fixed; setting invalid paths in strict mode (#916)
+ * fixed; resetting isNew after insert failure (#837) [boutell]
+
+2.6.4 / 2012-05-15
+===================
+
+ * updated; backport string regex $options to 2.x
+ * updated; use driver 1.0.2 (performance improvements) (#914)
+ * fixed; calling MongooseDocumentArray#id when the doc has no _id (#897)
+
+2.6.3 / 2012-05-03
+===================
+
+ * fixed; repl-set connectivity issues during failover on MongoDB 2.0.1
+ * updated; driver to 1.0.0
+ * fixed; virtuals application of subdocs when using toObject({ virtuals: true }) (#889)
+ * fixed; MongooseArray#pull of ObjectId correctly updates the array itself (#881)
+
+2.6.2 / 2012-04-30
+===================
+
+ * fixed; default field application of selected fields (#870)
+
+2.6.1 / 2012-04-30
+===================
+
+ * fixed; connection assignment in mongoose#model (#853, #877)
+ * fixed; incorrect reported num of affected docs in update ops (#862)
+
+2.6.0 / 2012-04-19
+===================
+
+ * updated; hooks.js to 0.2.1
+ * fixed; issue with passing undefined to a hook callback. thanks to [chrisleishman] for reporting.
+ * fixed; updating/setting nested objects in strict schemas (#843) as reported by [kof]
+ * fixed; Query#{update,remove}() work without callbacks again (#788)
+ * fixed; modifying subdoc along with parent array $atomic op (#842)
+
+2.5.14 / 2012-04-13
+===================
+
+ * fixed; setting an unset default value (#742)
+ * fixed; doc.isSelected(otherpath) when only _id is selected (#730)
+ * updated; docs
+
+2.5.13 / 2012-03-22
+===================
+
+ * fixed; failing validation of unselected required paths (#730,#713)
+ * fixed; emitting connection error when only one listener (#759)
+ * fixed; MongooseArray#splice was not returning values (#784) [chrisleishman]
+
+2.5.12 / 2012-03-21
+===================
+
+ * fixed; honor the `safe` option in all ensureIndex calls
+ * updated; node-mongodb-native driver to 0.9.9-7
+
+2.5.11 / 2012-03-15
+===================
+
+ * added; introspection for getters/setters (#745)
+ * updated; node-mongodb-driver to 0.9.9-5
+ * added; tailable method to Query (#769) [holic]
+ * fixed; Number min/max validation of null (#764) [btamas]
+ * added; more flexible user/password connection options (#738) [KarneAsada]
+
+2.5.10 / 2012-03-06
+===================
+
+ * updated; node-mongodb-native driver to 0.9.9-4
+ * added; Query#comment()
+ * fixed; allow unsetting arrays
+ * fixed; hooking the set method of subdocuments (#746)
+ * fixed; edge case in hooks
+ * fixed; allow $id and $ref in queries (fixes compatibility with mongoose-dbref) (#749) [richtera]
+ * added; default path selection to SchemaTypes
+
+2.5.9 / 2012-02-22
+===================
+
+ * fixed; properly cast nested atomic update operators for sub-documents
+
+2.5.8 / 2012-02-21
+===================
+
+ * added; post 'remove' middleware includes model that was removed (#729) [timoxley]
+
+2.5.7 / 2012-02-09
+===================
+
+ * fixed; RegExp validators on node >= v0.6.x
+
+2.5.6 / 2012-02-09
+===================
+
+ * fixed; emit errors returned from db.collection() on the connection (were being swallowed)
+ * added; can add multiple validators in your schema at once (#718) [diogogmt]
+ * fixed; strict embedded documents (#717)
+ * updated; docs [niemyjski]
+ * added; pass number of affected docs back in model.update/save
+
+2.5.5 / 2012-02-03
+===================
+
+ * fixed; RangeError: maximum call stack exceed error when removing docs with Number _id (#714)
+
+2.5.4 / 2012-02-03
+===================
+
+ * fixed; RangeError: maximum call stack exceed error (#714)
+
+2.5.3 / 2012-02-02
+===================
+
+ * added; doc#isSelected(path)
+ * added; query#equals()
+ * added; beta sharding support
+ * added; more descript error msgs (#700) [obeleh]
+ * added; document.modifiedPaths (#709) [ljharb]
+ * fixed; only functions can be added as getters/setters (#707,704) [ljharb]
+
+2.5.2 / 2012-01-30
+===================
+
+ * fixed; rollback -native driver to 0.9.7-3-5 (was causing timeouts and other replica set weirdness)
+ * deprecated; MongooseNumber (will be moved to a separate repo for 3.x)
+ * added; init event is emitted on schemas
+
+2.5.1 / 2012-01-27
+===================
+
+ * fixed; honor strict schemas in Model.update (#699)
+
+2.5.0 / 2012-01-26
+===================
+
+ * added; doc.toJSON calls toJSON on embedded docs when exists [jerem]
+ * added; populate support for refs of type Buffer (#686) [jerem]
+ * added; $all support for ObjectIds and Dates (#690)
+ * fixed; virtual setter calling on instantiation when strict: true (#682) [hunterloftis]
+ * fixed; doc construction triggering getters (#685)
+ * fixed; MongooseBuffer check in deepEquals (#688)
+ * fixed; range error when using Number _ids with `instance.save()` (#691)
+ * fixed; isNew on embedded docs edge case (#680)
+ * updated; driver to 0.9.8-3
+ * updated; expose `model()` method within static methods
+
+2.4.10 / 2012-01-10
+===================
+
+ * added; optional getter application in .toObject()/.toJSON() (#412)
+ * fixed; nested $operators in $all queries (#670)
+ * added; $nor support (#674)
+ * fixed; bug when adding nested schema (#662) [paulwe]
+
+2.4.9 / 2012-01-04
+===================
+
+ * updated; driver to 0.9.7-3-5 to fix Linux performance degradation on some boxes
+
+2.4.8 / 2011-12-22
+===================
+
+ * updated; bump -native to 0.9.7.2-5
+ * fixed; compatibility with date.js (#646) [chrisleishman]
+ * changed; undocumented schema "lax" option to "strict"
+ * fixed; default value population for strict schemas
+ * updated; the nextTick helper for small performance gain. 1bee2a2
+
+2.4.7 / 2011-12-16
+===================
+
+ * fixed; bug in 2.4.6 with path setting
+ * updated; bump -native to 0.9.7.2-1
+ * added; strict schema option [nw]
+
+2.4.6 / 2011-12-16
+===================
+
+ * fixed; conflicting mods on update bug [sirlantis]
+ * improved; doc.id getter performance
+
+2.4.5 / 2011-12-14
+===================
+
+ * fixed; bad MongooseArray behavior in 2.4.2 - 2.4.4
+
+2.4.4 / 2011-12-14
+===================
+
+ * fixed; MongooseArray#doAtomics throwing after sliced
+
+2.4.3 / 2011-12-14
+===================
+
+ * updated; system.profile schema for MongoDB 2x
+
+2.4.2 / 2011-12-12
+===================
+
+ * fixed; partially populating multiple children of subdocs (#639) [kenpratt]
+ * fixed; allow Update of numbers to null (#640) [jerem]
+
+2.4.1 / 2011-12-02
+===================
+
+ * added; options support for populate() queries
+ * updated; -native driver to 0.9.7-1.4
+
+2.4.0 / 2011-11-29
+===================
+
+ * added; QueryStreams (#614)
+ * added; debug print mode for development
+ * added; $within support to Array queries (#586) [ggoodale]
+ * added; $centerSphere query support
+ * fixed; $within support
+ * added; $unset is now used when setting a path to undefined (#519)
+ * added; query#batchSize support
+ * updated; docs
+ * updated; -native driver to 0.9.7-1.3 (provides Windows support)
+
+2.3.13 / 2011-11-15
+===================
+
+ * fixed; required validation for Refs (#612) [ded]
+ * added; $nearSphere support for Arrays (#610)
+
+2.3.12 / 2011-11-09
+===================
+
+ * fixed; regression, objects passed to Model.update should not be changed (#605)
+ * fixed; regression, empty Model.update should not be executed
+
+2.3.11 / 2011-11-08
+===================
+
+ * fixed; using $elemMatch on arrays of Mixed types (#591)
+ * fixed; allow using $regex when querying Arrays (#599)
+ * fixed; calling Model.update with no atomic keys (#602)
+
+2.3.10 / 2011-11-05
+===================
+
+ * fixed; model.update casting for nested paths works (#542)
+
+2.3.9 / 2011-11-04
+==================
+
+ * fixed; deepEquals check for MongooseArray returned false
+ * fixed; reset modified flags of embedded docs after save [gitfy]
+ * fixed; setting embedded doc with identical values no longer marks modified [gitfy]
+ * updated; -native driver to 0.9.6.23 [mlazarov]
+ * fixed; Model.update casting (#542, #545, #479)
+ * fixed; populated refs no longer fail required validators (#577)
+ * fixed; populating refs of objects with custom ids works
+ * fixed; $pop & $unset work with Model.update (#574)
+ * added; more helpful debugging message for Schema#add (#578)
+ * fixed; accessing .id when no _id exists now returns null (#590)
+
+2.3.8 / 2011-10-26
+==================
+
+ * added; callback to query#findOne is now optional (#581)
+
+2.3.7 / 2011-10-24
+==================
+
+ * fixed; wrapped save/remove callbacks in nextTick to mitigate -native swallowing thrown errors
+
+2.3.6 / 2011-10-21
+==================
+
+ * fixed; exclusion of embedded doc _id from query results (#541)
+
+2.3.5 / 2011-10-19
+==================
+
+ * fixed; calling queries without passing a callback works (#569)
+ * fixed; populate() works with String and Number _ids too (#568)
+
+2.3.4 / 2011-10-18
+==================
+
+ * added; Model.create now accepts an array as a first arg
+ * fixed; calling toObject on a DocumentArray with nulls no longer throws
+ * fixed; calling inspect on a DocumentArray with nulls no longer throws
+ * added; MongooseArray#unshift support
+ * fixed; save hooks now fire on embedded documents [gitfy] (#456)
+ * updated; -native driver to 0.9.6-22
+ * fixed; correctly pass $addToSet op instead of $push
+ * fixed; $addToSet properly detects dates
+ * fixed; $addToSet with multiple items works
+ * updated; better node 0.6 Buffer support
+
+2.3.3 / 2011-10-12
+==================
+
+ * fixed; population conditions in multi-query settings [vedmalex] (#563)
+ * fixed; now compatible with Node v0.5.x
+
+2.3.2 / 2011-10-11
+==================
+
+ * fixed; population of null subdoc properties no longer hangs (#561)
+
+2.3.1 / 2011-10-10
+==================
+
+ * added; support for Query filters to populate() [eneko]
+ * fixed; querying with number no longer crashes mongodb (#555) [jlbyrey]
+ * updated; version of -native driver to 0.9.6-21
+ * fixed; prevent query callbacks that throw errors from corrupting -native connection state
+
+2.3.0 / 2011-10-04
+==================
+
+ * fixed; nulls as default values for Boolean now works as expected
+ * updated; version of -native driver to 0.9.6-20
+
+2.2.4 / 2011-10-03
+==================
+
+ * fixed; populate() works when returned array contains undefined/nulls
+
+2.2.3 / 2011-09-29
+==================
+
+ * updated; version of -native driver to 0.9.6-19
+
+2.2.2 / 2011-09-28
+==================
+
+ * added; $regex support to String [davidandrewcope]
+ * added; support for other contexts like repl etc (#535)
+ * fixed; clear modified state properly after saving
+ * added; $addToSet support to Array
+
+2.2.1 / 2011-09-22
+==================
+
+ * more descript error when casting undefined to string
+ * updated; version of -native driver to 0.9.6-18
+
+2.2.0 / 2011-09-22
+==================
+
+ * fixed; maxListeners warning on schemas with many arrays (#530)
+ * changed; return / apply defaults based on fields selected in query (#423)
+ * fixed; correctly detect Mixed types within schema arrays (#532)
+
+2.1.4 / 2011-09-20
+==================
+
+ * fixed; new private methods that stomped on users code
+ * changed; finished removing old "compat" support which did nothing
+
+2.1.3 / 2011-09-16
+==================
+
+ * updated; version of -native driver to 0.9.6-15
+ * added; emit `error` on connection when open fails [edwardhotchkiss]
+ * added; index support to Buffers (thanks justmoon for helping track this down)
+ * fixed; passing collection name via schema in conn.model() now works (thanks vedmalex for reporting)
+
+2.1.2 / 2011-09-07
+==================
+
+ * fixed; Query#find with no args no longer throws
+
+2.1.1 / 2011-09-07
+==================
+
+ * added; support Model.count(fn)
+ * fixed; compatibility with node >=0.4.0 < 0.4.3
+ * added; pass model.options.safe through with .save() so w:2, wtimeout:5000 options work [andrewjstone]
+ * added; support for $type queries
+ * added; support for Query#or
+ * added; more tests
+ * optimized populate queries
+
+2.1.0 / 2011-09-01
+==================
+
+ * changed; document#validate is a public method
+ * fixed; setting number to same value no longer marks modified (#476) [gitfy]
+ * fixed; Buffers shouldn't have default vals
+ * added; allow specifying collection name in schema (#470) [ixti]
+ * fixed; reset modified paths and atomics after saved (#459)
+ * fixed; set isNew on embedded docs to false after save
+ * fixed; use self to ensure proper scope of options in doOpenSet (#483) [andrewjstone]
+
+2.0.4 / 2011-08-29
+==================
+
+ * Fixed; Only send the depopulated ObjectId instead of the entire doc on save (DBRefs)
+ * Fixed; Properly cast nested array values in Model.update (the data was stored in Mongo incorrectly but recast on document fetch was "fixing" it)
+
+2.0.3 / 2011-08-28
+==================
+
+ * Fixed; manipulating a populated array no longer causes infinite loop in BSON serializer during save (#477)
+ * Fixed; populating an empty array no longer hangs foreeeeeeeever (#481)
+
+2.0.2 / 2011-08-25
+==================
+
+ * Fixed; Maintain query option key order (fixes 'bad hint' error from compound query hints)
+
+2.0.1 / 2011-08-25
+==================
+
+ * Fixed; do not over-write the doc when no valide props exist in Model.update (#473)
+
+2.0.0 / 2011-08-24
+===================
+
+ * Added; support for Buffers [justmoon]
+ * Changed; improved error handling [maelstrom]
+ * Removed: unused utils.erase
+ * Fixed; support for passing other context object into Schemas (#234) [Sija]
+ * Fixed; getters are no longer circular refs to themselves (#366)
+ * Removed; unused compat.js
+ * Fixed; getter/setter scopes are set properly
+ * Changed; made several private properties more obvious by prefixing _
+ * Added; DBRef support [guille]
+ * Changed; removed support for multiple collection names per model
+ * Fixed; no longer applying setters when document returned from db
+ * Changed; default auto_reconnect to true
+ * Changed; Query#bind no longer clones the query
+ * Fixed; Model.update now accepts $pull, $inc and friends (#404)
+ * Added; virtual type option support [nw]
+
+1.8.4 / 2011-08-21
+===================
+
+ * Fixed; validation bug when instantiated with non-schema properties (#464) [jmreidy]
+
+1.8.3 / 2011-08-19
+===================
+
+ * Fixed; regression in connection#open [jshaw86]
+
+1.8.2 / 2011-08-17
+===================
+
+ * fixed; reset connection.readyState after failure [tomseago]
+ * fixed; can now query positionally for non-embedded docs (arrays of numbers/strings etc)
+ * fixed; embedded document query casting
+ * added; support for passing options to node-mongo-native db, server, and replsetserver [tomseago]
+
+1.8.1 / 2011-08-10
+===================
+
+ * fixed; ObjectIds were always marked modified
+ * fixed; can now query using document instances
+ * fixed; can now query/update using documents with subdocs
+
+1.8.0 / 2011-08-04
+===================
+
+ * fixed; can now use $all with String and Number
+ * fixed; can query subdoc array with $ne: null
+ * fixed; instance.subdocs#id now works with custom _ids
+ * fixed; do not apply setters when doc returned from db (change in bad behavior)
+
+1.7.4 / 2011-07-25
+===================
+
+ * fixed; sparse now a valid seperate schema option
+ * fixed; now catching cast errors in queries
+ * fixed; calling new Schema with object created in vm.runInNewContext now works (#384) [Sija]
+ * fixed; String enum was disallowing null
+ * fixed; Find by nested document _id now works (#389)
+
+1.7.3 / 2011-07-16
+===================
+
+ * fixed; MongooseArray#indexOf now works with ObjectIds
+ * fixed; validation scope now set properly (#418)
+ * fixed; added missing colors dependency (#398)
+
+1.7.2 / 2011-07-13
+===================
+
+ * changed; node-mongodb-native driver to v0.9.6.7
+
+1.7.1 / 2011-07-12
+===================
+
+ * changed; roll back node-mongodb-native driver to v0.9.6.4
+
+1.7.0 / 2011-07-12
+===================
+
+ * fixed; collection name misspelling [mathrawka]
+ * fixed; 2nd param is required for ReplSetServers [kevinmarvin]
+ * fixed; MongooseArray behaves properly with Object.keys
+ * changed; node-mongodb-native driver to v0.9.6.6
+ * fixed/changed; Mongodb segfault when passed invalid ObjectId (#407)
+ - This means invalid data passed to the ObjectId constructor will now error
+
+1.6.0 / 2011-07-07
+===================
+
+ * changed; .save() errors are now emitted on the instances db instead of the instance 9782463fc
+ * fixed; errors occurring when creating indexes now properly emit on db
+ * added; $maxDistance support to MongooseArrays
+ * fixed; RegExps now work with $all
+ * changed; node-mongodb-native driver to v0.9.6.4
+ * fixed; model names are now accessible via .modelName
+ * added; Query#slaveOk support
+
+1.5.0 / 2011-06-27
+===================
+
+ * changed; saving without a callback no longer ignores the error (@bnoguchi)
+ * changed; hook-js version bump to 0.1.9
+ * changed; node-mongodb-native version bumped to 0.9.6.1 - When .remove() doesn't
+ return an error, null is no longer passed.
+ * fixed; two memory leaks (@justmoon)
+ * added; sparse index support
+ * added; more ObjectId conditionals (gt, lt, gte, lte) (@phillyqueso)
+ * added; options are now passed in model#remote (@JerryLuke)
+
+1.4.0 / 2011-06-10
+===================
+
+ * bumped hooks-js dependency (fixes issue passing null as first arg to next())
+ * fixed; document#inspect now works properly with nested docs
+ * fixed; 'set' now works as a schema attribute (GH-365)
+ * fixed; _id is now set properly within pre-init hooks (GH-289)
+ * added; Query#distinct / Model#distinct support (GH-155)
+ * fixed; embedded docs now can use instance methods (GH-249)
+ * fixed; can now overwrite strings conflicting with schema type
+
+1.3.7 / 2011-06-03
+===================
+
+ * added MongooseArray#splice support
+ * fixed; 'path' is now a valid Schema pathname
+ * improved hooks (utilizing https://github.com/bnoguchi/hooks-js)
+ * fixed; MongooseArray#$shift now works (never did)
+ * fixed; Document.modified no longer throws
+ * fixed; modifying subdoc property sets modified paths for subdoc and parent doc
+ * fixed; marking subdoc path as modified properly persists the value to the db
+ * fixed; RexExps can again be saved ( #357 )
+
+1.3.6 / 2011-05-18
+===================
+
+ * fixed; corrected casting for queries against array types
+ * added; Document#set now accepts Document instances
+
+1.3.5 / 2011-05-17
+===================
+
+ * fixed; $ne queries work properly with single vals
+ * added; #inspect() methods to improve console.log output
+
+1.3.4 / 2011-05-17
+===================
+
+ * fixed; find by Date works as expected (#336)
+ * added; geospatial 2d index support
+ * added; support for $near (#309)
+ * updated; node-mongodb-native driver
+ * fixed; updating numbers work (#342)
+ * added; better error msg when try to remove an embedded doc without an _id (#307)
+ * added; support for 'on-the-fly' schemas (#227)
+ * changed; virtual id getters can now be skipped
+ * fixed; .index() called on subdoc schema now works as expected
+ * fixed; db.setProfile() now buffers until the db is open (#340)
+
+1.3.3 / 2011-04-27
+===================
+
+ * fixed; corrected query casting on nested mixed types
+
+1.3.2 / 2011-04-27
+===================
+
+ * fixed; query hints now retain key order
+
+1.3.1 / 2011-04-27
+===================
+
+ * fixed; setting a property on an embedded array no longer overwrites entire array (GH-310)
+ * fixed; setting nested properties works when sibling prop is named "type"
+ * fixed; isModified is now much finer grained when .set() is used (GH-323)
+ * fixed; mongoose.model() and connection.model() now return the Model (GH-308, GH-305)
+ * fixed; can now use $gt, $lt, $gte, $lte with String schema types (GH-317)
+ * fixed; .lowercase() -> .toLowerCase() in pluralize()
+ * fixed; updating an embedded document by index works (GH-334)
+ * changed; .save() now passes the instance to the callback (GH-294, GH-264)
+ * added; can now query system.profile and system.indexes collections
+ * added; db.model('system.profile') is now included as a default Schema
+ * added; db.setProfiling(level, ms, callback)
+ * added; Query#hint() support
+ * added; more tests
+ * updated node-mongodb-native to 0.9.3
+
+1.3.0 / 2011-04-19
+===================
+
+ * changed; save() callbacks now fire only once on failed validation
+ * changed; Errors returned from save() callbacks now instances of ValidationError
+ * fixed; MongooseArray#indexOf now works properly
+
+1.2.0 / 2011-04-11
+===================
+
+ * changed; MongooseNumber now casts empty string to null
+
+1.1.25 / 2011-04-08
+===================
+
+ * fixed; post init now fires at proper time
+
+1.1.24 / 2011-04-03
+===================
+
+ * fixed; pushing an array onto an Array works on existing docs
+
+1.1.23 / 2011-04-01
+===================
+
+ * Added Model#model
+
+1.1.22 / 2011-03-31
+===================
+
+ * Fixed; $in queries on mixed types now work
+
+1.1.21 / 2011-03-31
+===================
+
+ * Fixed; setting object root to null/undefined works
+
+1.1.20 / 2011-03-31
+===================
+
+ * Fixed; setting multiple props on null field works
+
+1.1.19 / 2011-03-31
+===================
+
+ * Fixed; no longer using $set on paths to an unexisting fields
+
+1.1.18 / 2011-03-30
+===================
+
+ * Fixed; non-mixed type object setters work after initd from null
+
+1.1.17 / 2011-03-30
+===================
+
+ * Fixed; nested object property access works when root initd with null value
+
+1.1.16 / 2011-03-28
+===================
+
+ * Fixed; empty arrays are now saved
+
+1.1.15 / 2011-03-28
+===================
+
+ * Fixed; `null` and `undefined` are set atomically.
+
+1.1.14 / 2011-03-28
+===================
+
+ * Changed; more forgiving date casting, accepting '' as null.
+
+1.1.13 / 2011-03-26
+===================
+
+ * Fixed setting values as `undefined`.
+
+1.1.12 / 2011-03-26
+===================
+
+ * Fixed; nested objects now convert to JSON properly
+ * Fixed; setting nested objects directly now works
+ * Update node-mongodb-native
+
+1.1.11 / 2011-03-25
+===================
+
+ * Fixed for use of `type` as a key.
+
+1.1.10 / 2011-03-23
+===================
+
+ * Changed; Make sure to only ensure indexes while connected
+
+1.1.9 / 2011-03-2
+==================
+
+ * Fixed; Mixed can now default to empty arrays
+ * Fixed; keys by the name 'type' are now valid
+ * Fixed; null values retrieved from the database are hydrated as null values.
+ * Fixed repeated atomic operations when saving a same document twice.
+
+1.1.8 / 2011-03-23
+==================
+
+ * Fixed 'id' overriding. [bnoguchi]
+
+1.1.7 / 2011-03-22
+==================
+
+ * Fixed RegExp query casting when querying against an Array of Strings [bnoguchi]
+ * Fixed getters/setters for nested virtualsl. [bnoguchi]
+
+1.1.6 / 2011-03-22
+==================
+
+ * Only doValidate when path exists in Schema [aheckmann]
+ * Allow function defaults for Array types [aheckmann]
+ * Fix validation hang [aheckmann]
+ * Fix setting of isRequired of SchemaType [aheckmann]
+ * Fix SchemaType#required(false) filter [aheckmann]
+ * More backwards compatibility [aheckmann]
+ * More tests [aheckmann]
+
+1.1.5 / 2011-03-14
+==================
+
+ * Added support for `uri, db, fn` and `uri, fn` signatures for replica sets.
+ * Improved/extended replica set tests.
+
+1.1.4 / 2011-03-09
+==================
+
+ * Fixed; running an empty Query doesn't throw. [aheckmann]
+ * Changed; Promise#addBack returns promise. [aheckmann]
+ * Added streaming cursor support. [aheckmann]
+ * Changed; Query#update defaults to use$SetOnSave now. [brian]
+ * Added more docs.
+
+1.1.3 / 2011-03-04
+==================
+
+ * Added Promise#resolve [aheckmann]
+ * Fixed backward compatibility with nulls [aheckmann]
+ * Changed; Query#{run,exec} return promises [aheckmann]
+
+1.1.2 / 2011-03-03
+==================
+
+ * Restored Query#exec and added notion of default operation [brian]
+ * Fixed ValidatorError messages [brian]
+
+1.1.1 / 2011-03-01
+==================
+
+ * Added SchemaType String `lowercase`, `uppercase`, `trim`.
+ * Public exports (`Model`, `Document`) and tests.
+ * Added ObjectId casting support for `Document`s.
+
+1.1.0 / 2011-02-25
+==================
+
+ * Added support for replica sets.
+
+1.0.16 / 2011-02-18
+===================
+
+ * Added $nin as another whitelisted $conditional for SchemaArray [brian]
+ * Changed #with to #where [brian]
+ * Added ability to use $in conditional with Array types [brian]
+
+1.0.15 / 2011-02-18
+===================
+
+ * Added `id` virtual getter for documents to easily access the hexString of
+ the `_id`.
+
+1.0.14 / 2011-02-17
+===================
+
+ * Fix for arrays within subdocuments [brian]
+
+1.0.13 / 2011-02-16
+===================
+
+ * Fixed embedded documents saving.
+
+1.0.12 / 2011-02-14
+===================
+
+ * Minor refactorings [brian]
+
+1.0.11 / 2011-02-14
+===================
+
+ * Query refactor and $ne, $slice, $or, $size, $elemMatch, $nin, $exists support [brian]
+ * Named scopes sugar [brian]
+
+1.0.10 / 2011-02-11
+===================
+
+ * Updated node-mongodb-native driver [thanks John Allen]
+
+1.0.9 / 2011-02-09
+==================
+
+ * Fixed single member arrays as defaults [brian]
+
+1.0.8 / 2011-02-09
+==================
+
+ * Fixed for collection-level buffering of commands [gitfy]
+ * Fixed `Document#toJSON` [dalejefferson]
+ * Fixed `Connection` authentication [robrighter]
+ * Fixed clash of accessors in getters/setters [eirikurn]
+ * Improved `Model#save` promise handling
+
+1.0.7 / 2011-02-05
+==================
+
+ * Fixed memory leak warnings for test suite on 0.3
+ * Fixed querying documents that have an array that contain at least one
+ specified member. [brian]
+ * Fixed default value for Array types (fixes GH-210). [brian]
+ * Fixed example code.
+
+1.0.6 / 2011-02-03
+==================
+
+ * Fixed `post` middleware
+ * Fixed; it's now possible to instantiate a model even when one of the paths maps
+ to an undefined value [brian]
+
+1.0.5 / 2011-02-02
+==================
+
+ * Fixed; combo $push and $pushAll auto-converts into a $pushAll [brian]
+ * Fixed; combo $pull and $pullAll auto-converts to a single $pullAll [brian]
+ * Fixed; $pullAll now removes said members from array before save (so it acts just
+ like pushAll) [brian]
+ * Fixed; multiple $pulls and $pushes become a single $pullAll and $pushAll.
+ Moreover, $pull now modifies the array before save to reflect the immediate
+ change [brian]
+ * Added tests for nested shortcut getters [brian]
+ * Added tests that show that Schemas with nested Arrays don't apply defaults
+ [brian]
+
+1.0.4 / 2011-02-02
+==================
+
+ * Added MongooseNumber#toString
+ * Added MongooseNumber unit tests
+
+1.0.3 / 2011-02-02
+==================
+
+ * Make sure safe mode works with Model#save
+ * Changed Schema options: safe mode is now the default
+ * Updated node-mongodb-native to HEAD
+
+1.0.2 / 2011-02-02
+==================
+
+ * Added a Model.create shortcut for creating documents. [brian]
+ * Fixed; we can now instantiate models with hashes that map to at least one
+ null value. [brian]
+ * Fixed Schema with more than 2 nested levels. [brian]
+
+1.0.1 / 2011-02-02
+==================
+
+ * Improved `MongooseNumber`, works almost like the native except for `typeof`
+ not being `'number'`.
diff --git a/node_modules/mongoose/README.md b/node_modules/mongoose/README.md
new file mode 100644
index 0000000..e9f696a
--- /dev/null
+++ b/node_modules/mongoose/README.md
@@ -0,0 +1,332 @@
+# Mongoose
+
+Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment.
+
+[](http://slack.mongoosejs.io)
+[](https://travis-ci.org/Automattic/mongoose)
+[](http://badge.fury.io/js/mongoose)
+
+[](https://www.npmjs.com/package/mongoose)
+
+## Documentation
+
+[mongoosejs.com](http://mongoosejs.com/)
+
+[Mongoose 5.0.0](https://github.com/Automattic/mongoose/blob/master/History.md#500--2018-01-17) was released on January 17, 2018. You can find more details on backwards breaking changes in 5.0.0 on [GitHub](https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md).
+
+## Support
+
+ - [Stack Overflow](http://stackoverflow.com/questions/tagged/mongoose)
+ - [Bug Reports](https://github.com/Automattic/mongoose/issues/)
+ - [Mongoose Slack Channel](http://slack.mongoosejs.io/)
+ - [Help Forum](http://groups.google.com/group/mongoose-orm)
+ - [MongoDB Support](https://docs.mongodb.org/manual/support/)
+
+## Importing
+
+```javascript
+// Using Node.js `require()`
+const mongoose = require('mongoose');
+
+// Using ES6 imports
+import mongoose from 'mongoose';
+```
+
+## Plugins
+
+Check out the [plugins search site](http://plugins.mongoosejs.io/) to see hundreds of related modules from the community. Next, learn how to write your own plugin from the [docs](http://mongoosejs.com/docs/plugins.html) or [this blog post](http://thecodebarbarian.com/2015/03/06/guide-to-mongoose-plugins).
+
+## Contributors
+
+Pull requests are always welcome! Please base pull requests against the `master`
+branch and follow the [contributing guide](https://github.com/Automattic/mongoose/blob/master/CONTRIBUTING.md).
+
+If your pull requests makes documentation changes, please do **not**
+modify any `.html` files. The `.html` files are compiled code, so please make
+your changes in `docs/*.jade`, `lib/*.js`, or `test/docs/*.js`.
+
+View all 300+ [contributors](https://github.com/Automattic/mongoose/graphs/contributors).
+
+## Installation
+
+First install [node.js](http://nodejs.org/) and [mongodb](https://www.mongodb.org/downloads). Then:
+
+```sh
+$ npm install mongoose
+```
+
+## Overview
+
+### Connecting to MongoDB
+
+First, we need to define a connection. If your app uses only one database, you should use `mongoose.connect`. If you need to create additional connections, use `mongoose.createConnection`.
+
+Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`.
+
+```js
+const mongoose = require('mongoose');
+
+mongoose.connect('mongodb://localhost/my_database');
+```
+
+Once connected, the `open` event is fired on the `Connection` instance. If you're using `mongoose.connect`, the `Connection` is `mongoose.connection`. Otherwise, `mongoose.createConnection` return value is a `Connection`.
+
+**Note:** _If the local connection fails then try using 127.0.0.1 instead of localhost. Sometimes issues may arise when the local hostname has been changed._
+
+**Important!** Mongoose buffers all the commands until it's connected to the database. This means that you don't have to wait until it connects to MongoDB in order to define models, run queries, etc.
+
+### Defining a Model
+
+Models are defined through the `Schema` interface.
+
+```js
+const Schema = mongoose.Schema;
+const ObjectId = Schema.ObjectId;
+
+const BlogPost = new Schema({
+ author: ObjectId,
+ title: String,
+ body: String,
+ date: Date
+});
+```
+
+Aside from defining the structure of your documents and the types of data you're storing, a Schema handles the definition of:
+
+* [Validators](http://mongoosejs.com/docs/validation.html) (async and sync)
+* [Defaults](http://mongoosejs.com/docs/api.html#schematype_SchemaType-default)
+* [Getters](http://mongoosejs.com/docs/api.html#schematype_SchemaType-get)
+* [Setters](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set)
+* [Indexes](http://mongoosejs.com/docs/guide.html#indexes)
+* [Middleware](http://mongoosejs.com/docs/middleware.html)
+* [Methods](http://mongoosejs.com/docs/guide.html#methods) definition
+* [Statics](http://mongoosejs.com/docs/guide.html#statics) definition
+* [Plugins](http://mongoosejs.com/docs/plugins.html)
+* [pseudo-JOINs](http://mongoosejs.com/docs/populate.html)
+
+The following example shows some of these features:
+
+```js
+const Comment = new Schema({
+ name: { type: String, default: 'hahaha' },
+ age: { type: Number, min: 18, index: true },
+ bio: { type: String, match: /[a-z]/ },
+ date: { type: Date, default: Date.now },
+ buff: Buffer
+});
+
+// a setter
+Comment.path('name').set(function (v) {
+ return capitalize(v);
+});
+
+// middleware
+Comment.pre('save', function (next) {
+ notify(this.get('email'));
+ next();
+});
+```
+
+Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
+
+### Accessing a Model
+
+Once we define a model through `mongoose.model('ModelName', mySchema)`, we can access it through the same function
+
+```js
+const myModel = mongoose.model('ModelName');
+```
+
+Or just do it all at once
+
+```js
+const MyModel = mongoose.model('ModelName', mySchema);
+```
+
+The first argument is the _singular_ name of the collection your model is for. **Mongoose automatically looks for the _plural_ version of your model name.** For example, if you use
+
+```js
+const MyModel = mongoose.model('Ticket', mySchema);
+```
+
+Then Mongoose will create the model for your __tickets__ collection, not your __ticket__ collection.
+
+Once we have our model, we can then instantiate it, and save it:
+
+```js
+const instance = new MyModel();
+instance.my.key = 'hello';
+instance.save(function (err) {
+ //
+});
+```
+
+Or we can find documents from the same collection
+
+```js
+MyModel.find({}, function (err, docs) {
+ // docs.forEach
+});
+```
+
+You can also `findOne`, `findById`, `update`, etc. For more details check out [the docs](http://mongoosejs.com/docs/queries.html).
+
+**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created:
+
+```js
+const conn = mongoose.createConnection('your connection string');
+const MyModel = conn.model('ModelName', schema);
+const m = new MyModel;
+m.save(); // works
+```
+
+vs
+
+```js
+const conn = mongoose.createConnection('your connection string');
+const MyModel = mongoose.model('ModelName', schema);
+const m = new MyModel;
+m.save(); // does not work b/c the default connection object was never connected
+```
+
+### Embedded Documents
+
+In the first example snippet, we defined a key in the Schema that looks like:
+
+```
+comments: [Comment]
+```
+
+Where `Comment` is a `Schema` we created. This means that creating embedded documents is as simple as:
+
+```js
+// retrieve my model
+var BlogPost = mongoose.model('BlogPost');
+
+// create a blog post
+var post = new BlogPost();
+
+// create a comment
+post.comments.push({ title: 'My comment' });
+
+post.save(function (err) {
+ if (!err) console.log('Success!');
+});
+```
+
+The same goes for removing them:
+
+```js
+BlogPost.findById(myId, function (err, post) {
+ if (!err) {
+ post.comments[0].remove();
+ post.save(function (err) {
+ // do something
+ });
+ }
+});
+```
+
+Embedded documents enjoy all the same features as your models. Defaults, validators, middleware. Whenever an error occurs, it's bubbled to the `save()` error callback, so error handling is a snap!
+
+
+### Middleware
+
+See the [docs](http://mongoosejs.com/docs/middleware.html) page.
+
+#### Intercepting and mutating method arguments
+
+You can intercept method arguments via middleware.
+
+For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to a new value:
+
+```js
+schema.pre('set', function (next, path, val, typel) {
+ // `this` is the current Document
+ this.emit('set', path, val);
+
+ // Pass control to the next pre
+ next();
+});
+```
+
+Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments. To do so, just pass the new values to `next`:
+
+```js
+.pre(method, function firstPre (next, methodArg1, methodArg2) {
+ // Mutate methodArg1
+ next("altered-" + methodArg1.toString(), methodArg2);
+});
+
+// pre declaration is chainable
+.pre(method, function secondPre (next, methodArg1, methodArg2) {
+ console.log(methodArg1);
+ // => 'altered-originalValOfMethodArg1'
+
+ console.log(methodArg2);
+ // => 'originalValOfMethodArg2'
+
+ // Passing no arguments to `next` automatically passes along the current argument values
+ // i.e., the following `next()` is equivalent to `next(methodArg1, methodArg2)`
+ // and also equivalent to, with the example method arg
+ // values, `next('altered-originalValOfMethodArg1', 'originalValOfMethodArg2')`
+ next();
+});
+```
+
+#### Schema gotcha
+
+`type`, when used in a schema has special meaning within Mongoose. If your schema requires using `type` as a nested property you must use object notation:
+
+```js
+new Schema({
+ broken: { type: Boolean },
+ asset: {
+ name: String,
+ type: String // uh oh, it broke. asset will be interpreted as String
+ }
+});
+
+new Schema({
+ works: { type: Boolean },
+ asset: {
+ name: String,
+ type: { type: String } // works. asset is an object with a type property
+ }
+});
+```
+
+### Driver Access
+
+Mongoose is built on top of the [official MongoDB Node.js driver](https://github.com/mongodb/node-mongodb-native). Each mongoose model keeps a reference to a [native MongoDB driver collection](http://mongodb.github.io/node-mongodb-native/2.1/api/Collection.html). The collection object can be accessed using `YourModel.collection`. However, using the collection object directly bypasses all mongoose features, including hooks, validation, etc. The one
+notable exception that `YourModel.collection` still buffers
+commands. As such, `YourModel.collection.find()` will **not**
+return a cursor.
+
+## API Docs
+
+Find the API docs [here](http://mongoosejs.com/docs/api.html), generated using [dox](https://github.com/tj/dox)
+and [acquit](https://github.com/vkarpov15/acquit).
+
+## License
+
+Copyright (c) 2010 LearnBoost <dev@learnboost.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/mongoose/browser.js b/node_modules/mongoose/browser.js
new file mode 100644
index 0000000..4cf8228
--- /dev/null
+++ b/node_modules/mongoose/browser.js
@@ -0,0 +1,8 @@
+/**
+ * Export lib/mongoose
+ *
+ */
+
+'use strict';
+
+module.exports = require('./lib/browser');
diff --git a/node_modules/mongoose/index.js b/node_modules/mongoose/index.js
new file mode 100644
index 0000000..a938b44
--- /dev/null
+++ b/node_modules/mongoose/index.js
@@ -0,0 +1,9 @@
+
+/**
+ * Export lib/mongoose
+ *
+ */
+
+'use strict';
+
+module.exports = require('./lib/');
diff --git a/node_modules/mongoose/lib/aggregate.js b/node_modules/mongoose/lib/aggregate.js
new file mode 100644
index 0000000..28bea5c
--- /dev/null
+++ b/node_modules/mongoose/lib/aggregate.js
@@ -0,0 +1,1099 @@
+'use strict';
+
+/*!
+ * Module dependencies
+ */
+
+const AggregationCursor = require('./cursor/AggregationCursor');
+const Query = require('./query');
+const util = require('util');
+const utils = require('./utils');
+const read = Query.prototype.read;
+const readConcern = Query.prototype.readConcern;
+
+/**
+ * Aggregate constructor used for building aggregation pipelines. Do not
+ * instantiate this class directly, use [Model.aggregate()](/docs/api.html#model_Model.aggregate) instead.
+ *
+ * ####Example:
+ *
+ * const aggregate = Model.aggregate([
+ * { $project: { a: 1, b: 1 } },
+ * { $skip: 5 }
+ * ]);
+ *
+ * Model.
+ * aggregate([{ $match: { age: { $gte: 21 }}}]).
+ * unwind('tags').
+ * exec(callback);
+ *
+ * ####Note:
+ *
+ * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
+ * - Mongoose does **not** cast pipeline stages. The below will **not** work unless `_id` is a string in the database
+ *
+ * ```javascript
+ * new Aggregate([{ $match: { _id: '00000000000000000000000a' } }]);
+ * // Do this instead to cast to an ObjectId
+ * new Aggregate([{ $match: { _id: mongoose.Types.ObjectId('00000000000000000000000a') } }]);
+ * ```
+ *
+ * @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
+ * @see driver http://mongodb.github.com/node-mongodb-native/api-generated/collection.html#aggregate
+ * @param {Array} [pipeline] aggregation pipeline as an array of objects
+ * @api public
+ */
+
+function Aggregate(pipeline) {
+ this._pipeline = [];
+ this._model = undefined;
+ this.options = {};
+
+ if (arguments.length === 1 && util.isArray(pipeline)) {
+ this.append.apply(this, pipeline);
+ }
+}
+
+/**
+ * Contains options passed down to the [aggregate command](https://docs.mongodb.com/manual/reference/command/aggregate/).
+ * Supported options are:
+ *
+ * - `readPreference`
+ * - [`cursor`](./api.html#aggregate_Aggregate-cursor)
+ * - [`explain`](./api.html#aggregate_Aggregate-explain)
+ * - [`allowDiskUse`](./api.html#aggregate_Aggregate-allowDiskUse)
+ * - `maxTimeMS`
+ * - `bypassDocumentValidation`
+ * - `raw`
+ * - `promoteLongs`
+ * - `promoteValues`
+ * - `promoteBuffers`
+ * - [`collation`](./api.html#aggregate_Aggregate-collation)
+ * - `comment`
+ * - [`session`](./api.html#aggregate_Aggregate-session)
+ *
+ * @property options
+ * @memberOf Aggregate
+ * @api public
+ */
+
+Aggregate.prototype.options;
+
+/**
+ * Binds this aggregate to a model.
+ *
+ * @param {Model} model the model to which the aggregate is to be bound
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.model = function(model) {
+ this._model = model;
+ if (model.schema != null) {
+ if (this.options.readPreference == null &&
+ model.schema.options.read != null) {
+ this.options.readPreference = model.schema.options.read;
+ }
+ if (this.options.collation == null &&
+ model.schema.options.collation != null) {
+ this.options.collation = model.schema.options.collation;
+ }
+ }
+ return this;
+};
+
+/**
+ * Appends new operators to this aggregate pipeline
+ *
+ * ####Examples:
+ *
+ * aggregate.append({ $project: { field: 1 }}, { $limit: 2 });
+ *
+ * // or pass an array
+ * var pipeline = [{ $match: { daw: 'Logic Audio X' }} ];
+ * aggregate.append(pipeline);
+ *
+ * @param {Object} ops operator(s) to append
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.append = function() {
+ const args = (arguments.length === 1 && util.isArray(arguments[0]))
+ ? arguments[0]
+ : utils.args(arguments);
+
+ if (!args.every(isOperator)) {
+ throw new Error('Arguments must be aggregate pipeline operators');
+ }
+
+ this._pipeline = this._pipeline.concat(args);
+
+ return this;
+};
+
+/**
+ * Appends a new $addFields operator to this aggregate pipeline.
+ * Requires MongoDB v3.4+ to work
+ *
+ * ####Examples:
+ *
+ * // adding new fields based on existing fields
+ * aggregate.addFields({
+ * newField: '$b.nested'
+ * , plusTen: { $add: ['$val', 10]}
+ * , sub: {
+ * name: '$a'
+ * }
+ * })
+ *
+ * // etc
+ * aggregate.addFields({ salary_k: { $divide: [ "$salary", 1000 ] } });
+ *
+ * @param {Object} arg field specification
+ * @see $addFields https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/
+ * @return {Aggregate}
+ * @api public
+ */
+Aggregate.prototype.addFields = function(arg) {
+ const fields = {};
+ if (typeof arg === 'object' && !util.isArray(arg)) {
+ Object.keys(arg).forEach(function(field) {
+ fields[field] = arg[field];
+ });
+ } else {
+ throw new Error('Invalid addFields() argument. Must be an object');
+ }
+ return this.append({$addFields: fields});
+};
+
+/**
+ * Appends a new $project operator to this aggregate pipeline.
+ *
+ * Mongoose query [selection syntax](#query_Query-select) is also supported.
+ *
+ * ####Examples:
+ *
+ * // include a, include b, exclude _id
+ * aggregate.project("a b -_id");
+ *
+ * // or you may use object notation, useful when
+ * // you have keys already prefixed with a "-"
+ * aggregate.project({a: 1, b: 1, _id: 0});
+ *
+ * // reshaping documents
+ * aggregate.project({
+ * newField: '$b.nested'
+ * , plusTen: { $add: ['$val', 10]}
+ * , sub: {
+ * name: '$a'
+ * }
+ * })
+ *
+ * // etc
+ * aggregate.project({ salary_k: { $divide: [ "$salary", 1000 ] } });
+ *
+ * @param {Object|String} arg field specification
+ * @see projection http://docs.mongodb.org/manual/reference/aggregation/project/
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.project = function(arg) {
+ const fields = {};
+
+ if (typeof arg === 'object' && !util.isArray(arg)) {
+ Object.keys(arg).forEach(function(field) {
+ fields[field] = arg[field];
+ });
+ } else if (arguments.length === 1 && typeof arg === 'string') {
+ arg.split(/\s+/).forEach(function(field) {
+ if (!field) {
+ return;
+ }
+ const include = field[0] === '-' ? 0 : 1;
+ if (include === 0) {
+ field = field.substring(1);
+ }
+ fields[field] = include;
+ });
+ } else {
+ throw new Error('Invalid project() argument. Must be string or object');
+ }
+
+ return this.append({$project: fields});
+};
+
+/**
+ * Appends a new custom $group operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.group({ _id: "$department" });
+ *
+ * @see $group http://docs.mongodb.org/manual/reference/aggregation/group/
+ * @method group
+ * @memberOf Aggregate
+ * @instance
+ * @param {Object} arg $group operator contents
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new custom $match operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.match({ department: { $in: [ "sales", "engineering" ] } });
+ *
+ * @see $match http://docs.mongodb.org/manual/reference/aggregation/match/
+ * @method match
+ * @memberOf Aggregate
+ * @instance
+ * @param {Object} arg $match operator contents
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new $skip operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.skip(10);
+ *
+ * @see $skip http://docs.mongodb.org/manual/reference/aggregation/skip/
+ * @method skip
+ * @memberOf Aggregate
+ * @instance
+ * @param {Number} num number of records to skip before next stage
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new $limit operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.limit(10);
+ *
+ * @see $limit http://docs.mongodb.org/manual/reference/aggregation/limit/
+ * @method limit
+ * @memberOf Aggregate
+ * @instance
+ * @param {Number} num maximum number of records to pass to the next stage
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new $geoNear operator to this aggregate pipeline.
+ *
+ * ####NOTE:
+ *
+ * **MUST** be used as the first operator in the pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.near({
+ * near: [40.724, -73.997],
+ * distanceField: "dist.calculated", // required
+ * maxDistance: 0.008,
+ * query: { type: "public" },
+ * includeLocs: "dist.location",
+ * uniqueDocs: true,
+ * num: 5
+ * });
+ *
+ * @see $geoNear http://docs.mongodb.org/manual/reference/aggregation/geoNear/
+ * @method near
+ * @memberOf Aggregate
+ * @instance
+ * @param {Object} arg
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.near = function(arg) {
+ const op = {};
+ op.$geoNear = arg;
+ return this.append(op);
+};
+
+/*!
+ * define methods
+ */
+
+'group match skip limit out'.split(' ').forEach(function($operator) {
+ Aggregate.prototype[$operator] = function(arg) {
+ const op = {};
+ op['$' + $operator] = arg;
+ return this.append(op);
+ };
+});
+
+/**
+ * Appends new custom $unwind operator(s) to this aggregate pipeline.
+ *
+ * Note that the `$unwind` operator requires the path name to start with '$'.
+ * Mongoose will prepend '$' if the specified field doesn't start '$'.
+ *
+ * ####Examples:
+ *
+ * aggregate.unwind("tags");
+ * aggregate.unwind("a", "b", "c");
+ *
+ * @see $unwind http://docs.mongodb.org/manual/reference/aggregation/unwind/
+ * @param {String} fields the field(s) to unwind
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.unwind = function() {
+ const args = utils.args(arguments);
+
+ const res = [];
+ for (let i = 0; i < args.length; ++i) {
+ const arg = args[i];
+ if (arg && typeof arg === 'object') {
+ res.push({ $unwind: arg });
+ } else if (typeof arg === 'string') {
+ res.push({
+ $unwind: (arg && arg.charAt(0) === '$') ? arg : '$' + arg
+ });
+ } else {
+ throw new Error('Invalid arg "' + arg + '" to unwind(), ' +
+ 'must be string or object');
+ }
+ }
+
+ return this.append.apply(this, res);
+};
+
+/**
+ * Appends a new $replaceRoot operator to this aggregate pipeline.
+ *
+ * Note that the `$replaceRoot` operator requires field strings to start with '$'.
+ * If you are passing in a string Mongoose will prepend '$' if the specified field doesn't start '$'.
+ * If you are passing in an object the strings in your expression will not be altered.
+ *
+ * ####Examples:
+ *
+ * aggregate.replaceRoot("user");
+ *
+ * aggregate.replaceRoot({ x: { $concat: ['$this', '$that'] } });
+ *
+ * @see $replaceRoot https://docs.mongodb.org/manual/reference/operator/aggregation/replaceRoot
+ * @param {String|Object} the field or document which will become the new root document
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.replaceRoot = function(newRoot) {
+ let ret;
+
+ if (typeof newRoot === 'string') {
+ ret = newRoot.startsWith('$') ? newRoot : '$' + newRoot;
+ } else {
+ ret = newRoot;
+ }
+
+ return this.append({
+ $replaceRoot: {
+ newRoot: ret
+ }
+ });
+};
+
+/**
+ * Appends a new $count operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.count("userCount");
+ *
+ * @see $count https://docs.mongodb.org/manual/reference/operator/aggregation/count
+ * @param {String} the name of the count field
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.count = function(countName) {
+ return this.append({ $count: countName });
+};
+
+/**
+ * Appends a new $sortByCount operator to this aggregate pipeline. Accepts either a string field name
+ * or a pipeline object.
+ *
+ * Note that the `$sortByCount` operator requires the new root to start with '$'.
+ * Mongoose will prepend '$' if the specified field name doesn't start with '$'.
+ *
+ * ####Examples:
+ *
+ * aggregate.sortByCount('users');
+ * aggregate.sortByCount({ $mergeObjects: [ "$employee", "$business" ] })
+ *
+ * @see $sortByCount https://docs.mongodb.com/manual/reference/operator/aggregation/sortByCount/
+ * @param {Object|String} arg
+ * @return {Aggregate} this
+ * @api public
+ */
+
+Aggregate.prototype.sortByCount = function(arg) {
+ if (arg && typeof arg === 'object') {
+ return this.append({ $sortByCount: arg });
+ } else if (typeof arg === 'string') {
+ return this.append({
+ $sortByCount: (arg && arg.charAt(0) === '$') ? arg : '$' + arg
+ });
+ } else {
+ throw new TypeError('Invalid arg "' + arg + '" to sortByCount(), ' +
+ 'must be string or object');
+ }
+};
+
+/**
+ * Appends new custom $lookup operator(s) to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.lookup({ from: 'users', localField: 'userId', foreignField: '_id', as: 'users' });
+ *
+ * @see $lookup https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/#pipe._S_lookup
+ * @param {Object} options to $lookup as described in the above link
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.lookup = function(options) {
+ return this.append({$lookup: options});
+};
+
+/**
+ * Appends new custom $graphLookup operator(s) to this aggregate pipeline, performing a recursive search on a collection.
+ *
+ * Note that graphLookup can only consume at most 100MB of memory, and does not allow disk use even if `{ allowDiskUse: true }` is specified.
+ *
+ * #### Examples:
+ * // Suppose we have a collection of courses, where a document might look like `{ _id: 0, name: 'Calculus', prerequisite: 'Trigonometry'}` and `{ _id: 0, name: 'Trigonometry', prerequisite: 'Algebra' }`
+ * aggregate.graphLookup({ from: 'courses', startWith: '$prerequisite', connectFromField: 'prerequisite', connectToField: 'name', as: 'prerequisites', maxDepth: 3 }) // this will recursively search the 'courses' collection up to 3 prerequisites
+ *
+ * @see $graphLookup https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/#pipe._S_graphLookup
+ * @param {Object} options to $graphLookup as described in the above link
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.graphLookup = function(options) {
+ const cloneOptions = {};
+ if (options) {
+ if (!utils.isObject(options)) {
+ throw new TypeError('Invalid graphLookup() argument. Must be an object.');
+ }
+
+ utils.mergeClone(cloneOptions, options);
+ const startWith = cloneOptions.startWith;
+
+ if (startWith && typeof startWith === 'string') {
+ cloneOptions.startWith = cloneOptions.startWith.charAt(0) === '$' ?
+ cloneOptions.startWith :
+ '$' + cloneOptions.startWith;
+ }
+
+ }
+ return this.append({ $graphLookup: cloneOptions });
+};
+
+/**
+ * Appends new custom $sample operator(s) to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.sample(3); // Add a pipeline that picks 3 random documents
+ *
+ * @see $sample https://docs.mongodb.org/manual/reference/operator/aggregation/sample/#pipe._S_sample
+ * @param {Number} size number of random documents to pick
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.sample = function(size) {
+ return this.append({$sample: {size: size}});
+};
+
+/**
+ * Appends a new $sort operator to this aggregate pipeline.
+ *
+ * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
+ *
+ * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending.
+ *
+ * ####Examples:
+ *
+ * // these are equivalent
+ * aggregate.sort({ field: 'asc', test: -1 });
+ * aggregate.sort('field -test');
+ *
+ * @see $sort http://docs.mongodb.org/manual/reference/aggregation/sort/
+ * @param {Object|String} arg
+ * @return {Aggregate} this
+ * @api public
+ */
+
+Aggregate.prototype.sort = function(arg) {
+ // TODO refactor to reuse the query builder logic
+
+ const sort = {};
+
+ if (arg.constructor.name === 'Object') {
+ const desc = ['desc', 'descending', -1];
+ Object.keys(arg).forEach(function(field) {
+ // If sorting by text score, skip coercing into 1/-1
+ if (arg[field] instanceof Object && arg[field].$meta) {
+ sort[field] = arg[field];
+ return;
+ }
+ sort[field] = desc.indexOf(arg[field]) === -1 ? 1 : -1;
+ });
+ } else if (arguments.length === 1 && typeof arg === 'string') {
+ arg.split(/\s+/).forEach(function(field) {
+ if (!field) {
+ return;
+ }
+ const ascend = field[0] === '-' ? -1 : 1;
+ if (ascend === -1) {
+ field = field.substring(1);
+ }
+ sort[field] = ascend;
+ });
+ } else {
+ throw new TypeError('Invalid sort() argument. Must be a string or object.');
+ }
+
+ return this.append({$sort: sort});
+};
+
+/**
+ * Sets the readPreference option for the aggregation query.
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).read('primaryPreferred').exec(callback)
+ *
+ * @param {String} pref one of the listed preference options or their aliases
+ * @param {Array} [tags] optional tags for this query
+ * @return {Aggregate} this
+ * @api public
+ * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
+ * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
+ */
+
+Aggregate.prototype.read = function(pref, tags) {
+ if (!this.options) {
+ this.options = {};
+ }
+ read.call(this, pref, tags);
+ return this;
+};
+
+/**
+ * Sets the readConcern level for the aggregation query.
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).readConcern('majority').exec(callback)
+ *
+ * @param {String} level one of the listed read concern level or their aliases
+ * @see mongodb https://docs.mongodb.com/manual/reference/read-concern/
+ * @return {Aggregate} this
+ * @api public
+ */
+
+Aggregate.prototype.readConcern = function(level) {
+ if (!this.options) {
+ this.options = {};
+ }
+ readConcern.call(this, level);
+ return this;
+};
+
+/**
+ * Appends a new $redact operator to this aggregate pipeline.
+ *
+ * If 3 arguments are supplied, Mongoose will wrap them with if-then-else of $cond operator respectively
+ * If `thenExpr` or `elseExpr` is string, make sure it starts with $$, like `$$DESCEND`, `$$PRUNE` or `$$KEEP`.
+ *
+ * ####Example:
+ *
+ * Model.aggregate(...)
+ * .redact({
+ * $cond: {
+ * if: { $eq: [ '$level', 5 ] },
+ * then: '$$PRUNE',
+ * else: '$$DESCEND'
+ * }
+ * })
+ * .exec();
+ *
+ * // $redact often comes with $cond operator, you can also use the following syntax provided by mongoose
+ * Model.aggregate(...)
+ * .redact({ $eq: [ '$level', 5 ] }, '$$PRUNE', '$$DESCEND')
+ * .exec();
+ *
+ * @param {Object} expression redact options or conditional expression
+ * @param {String|Object} [thenExpr] true case for the condition
+ * @param {String|Object} [elseExpr] false case for the condition
+ * @return {Aggregate} this
+ * @see $redact https://docs.mongodb.com/manual/reference/operator/aggregation/redact/
+ * @api public
+ */
+
+Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) {
+ if (arguments.length === 3) {
+ if ((typeof thenExpr === 'string' && !thenExpr.startsWith('$$')) ||
+ (typeof elseExpr === 'string' && !elseExpr.startsWith('$$'))) {
+ throw new Error('If thenExpr or elseExpr is string, it must start with $$. e.g. $$DESCEND, $$PRUNE, $$KEEP');
+ }
+
+ expression = {
+ $cond: {
+ if: expression,
+ then: thenExpr,
+ else: elseExpr
+ }
+ };
+ } else if (arguments.length !== 1) {
+ throw new TypeError('Invalid arguments');
+ }
+
+ return this.append({$redact: expression});
+};
+
+/**
+ * Execute the aggregation with explain
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).explain(callback)
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ */
+
+Aggregate.prototype.explain = function(callback) {
+ return utils.promiseOrCallback(callback, cb => {
+ if (!this._pipeline.length) {
+ const err = new Error('Aggregate has empty pipeline');
+ return cb(err);
+ }
+
+ prepareDiscriminatorPipeline(this);
+
+ this._model.collection.
+ aggregate(this._pipeline, this.options || {}).
+ explain(function(error, result) {
+ if (error) {
+ return cb(error);
+ }
+ cb(null, result);
+ });
+ }, this._model.events);
+};
+
+/**
+ * Sets the allowDiskUse option for the aggregation query (ignored for < 2.6.0)
+ *
+ * ####Example:
+ *
+ * await Model.aggregate([{ $match: { foo: 'bar' } }]).allowDiskUse(true);
+ *
+ * @param {Boolean} value Should tell server it can use hard drive to store data during aggregation.
+ * @param {Array} [tags] optional tags for this query
+ * @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
+ */
+
+Aggregate.prototype.allowDiskUse = function(value) {
+ this.options.allowDiskUse = value;
+ return this;
+};
+
+/**
+ * Sets the hint option for the aggregation query (ignored for < 3.6.0)
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).hint({ qty: 1, category: 1 } }).exec(callback)
+ *
+ * @param {Object|String} value a hint object or the index name
+ * @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
+ */
+
+Aggregate.prototype.hint = function(value) {
+ this.options.hint = value;
+ return this;
+};
+
+/**
+ * Sets the session for this aggregation. Useful for [transactions](/docs/transactions.html).
+ *
+ * ####Example:
+ *
+ * const session = await Model.startSession();
+ * await Model.aggregate(..).session(session);
+ *
+ * @param {ClientSession} session
+ * @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
+ */
+
+Aggregate.prototype.session = function(session) {
+ if (session == null) {
+ delete this.options.session;
+ } else {
+ this.options.session = session;
+ }
+ return this;
+};
+
+/**
+ * Lets you set arbitrary options, for middleware or plugins.
+ *
+ * ####Example:
+ *
+ * var agg = Model.aggregate(..).option({ allowDiskUse: true }); // Set the `allowDiskUse` option
+ * agg.options; // `{ allowDiskUse: true }`
+ *
+ * @param {Object} options keys to merge into current options
+ * @param [options.maxTimeMS] number limits the time this aggregation will run, see [MongoDB docs on `maxTimeMS`](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/)
+ * @param [options.allowDiskUse] boolean if true, the MongoDB server will use the hard drive to store data during this aggregation
+ * @param [options.collation] object see [`Aggregate.prototype.collation()`](./docs/api.html#aggregate_Aggregate-collation)
+ * @param [options.session] ClientSession see [`Aggregate.prototype.session()`](./docs/api.html#aggregate_Aggregate-session)
+ * @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
+ * @return {Aggregate} this
+ * @api public
+ */
+
+Aggregate.prototype.option = function(value) {
+ for (const key in value) {
+ this.options[key] = value[key];
+ }
+ return this;
+};
+
+/**
+ * Sets the cursor option option for the aggregation query (ignored for < 2.6.0).
+ * Note the different syntax below: .exec() returns a cursor object, and no callback
+ * is necessary.
+ *
+ * ####Example:
+ *
+ * var cursor = Model.aggregate(..).cursor({ batchSize: 1000 }).exec();
+ * cursor.each(function(error, doc) {
+ * // use doc
+ * });
+ *
+ * @param {Object} options
+ * @param {Number} options.batchSize set the cursor batch size
+ * @param {Boolean} [options.useMongooseAggCursor] use experimental mongoose-specific aggregation cursor (for `eachAsync()` and other query cursor semantics)
+ * @return {Aggregate} this
+ * @api public
+ * @see mongodb http://mongodb.github.io/node-mongodb-native/2.0/api/AggregationCursor.html
+ */
+
+Aggregate.prototype.cursor = function(options) {
+ if (!this.options) {
+ this.options = {};
+ }
+ this.options.cursor = options || {};
+ return this;
+};
+
+/**
+ * Sets an option on this aggregation. This function will be deprecated in a
+ * future release. Use the [`cursor()`](./api.html#aggregate_Aggregate-cursor),
+ * [`collation()`](./api.html#aggregate_Aggregate-collation), etc. helpers to
+ * set individual options, or access `agg.options` directly.
+ *
+ * Note that MongoDB aggregations [do **not** support the `noCursorTimeout` flag](https://jira.mongodb.org/browse/SERVER-6036),
+ * if you try setting that flag with this function you will get a "unrecognized field 'noCursorTimeout'" error.
+ *
+ * @param {String} flag
+ * @param {Boolean} value
+ * @return {Aggregate} this
+ * @api public
+ * @deprecated Use [`.option()`](api.html#aggregate_Aggregate-option) instead. Note that MongoDB aggregations do **not** support a `noCursorTimeout` option.
+ */
+
+Aggregate.prototype.addCursorFlag = util.deprecate(function(flag, value) {
+ if (!this.options) {
+ this.options = {};
+ }
+ this.options[flag] = value;
+ return this;
+}, 'Mongoose: `Aggregate#addCursorFlag()` is deprecated, use `option()` instead');
+
+/**
+ * Adds a collation
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).collation({ locale: 'en_US', strength: 1 }).exec();
+ *
+ * @param {Object} collation options
+ * @return {Aggregate} this
+ * @api public
+ * @see mongodb http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#aggregate
+ */
+
+Aggregate.prototype.collation = function(collation) {
+ if (!this.options) {
+ this.options = {};
+ }
+ this.options.collation = collation;
+ return this;
+};
+
+/**
+ * Combines multiple aggregation pipelines.
+ *
+ * ####Example:
+ *
+ * Model.aggregate(...)
+ * .facet({
+ * books: [{ groupBy: '$author' }],
+ * price: [{ $bucketAuto: { groupBy: '$price', buckets: 2 } }]
+ * })
+ * .exec();
+ *
+ * // Output: { books: [...], price: [{...}, {...}] }
+ *
+ * @param {Object} facet options
+ * @return {Aggregate} this
+ * @see $facet https://docs.mongodb.com/v3.4/reference/operator/aggregation/facet/
+ * @api public
+ */
+
+Aggregate.prototype.facet = function(options) {
+ return this.append({$facet: options});
+};
+
+/**
+ * Returns the current pipeline
+ *
+ * ####Example:
+ *
+ * MyModel.aggregate().match({ test: 1 }).pipeline(); // [{ $match: { test: 1 } }]
+ *
+ * @return {Array}
+ * @api public
+ */
+
+
+Aggregate.prototype.pipeline = function() {
+ return this._pipeline;
+};
+
+/**
+ * Executes the aggregate pipeline on the currently bound Model.
+ *
+ * ####Example:
+ *
+ * aggregate.exec(callback);
+ *
+ * // Because a promise is returned, the `callback` is optional.
+ * var promise = aggregate.exec();
+ * promise.then(..);
+ *
+ * @see Promise #promise_Promise
+ * @param {Function} [callback]
+ * @return {Promise}
+ * @api public
+ */
+
+Aggregate.prototype.exec = function(callback) {
+ if (!this._model) {
+ throw new Error('Aggregate not bound to any Model');
+ }
+ const model = this._model;
+ const options = utils.clone(this.options || {});
+ const pipeline = this._pipeline;
+ const collection = this._model.collection;
+
+ if (options && options.cursor) {
+ return new AggregationCursor(this);
+ }
+
+ return utils.promiseOrCallback(callback, cb => {
+ if (!pipeline.length) {
+ const err = new Error('Aggregate has empty pipeline');
+ return cb(err);
+ }
+
+ prepareDiscriminatorPipeline(this);
+
+ model.hooks.execPre('aggregate', this, error => {
+ if (error) {
+ const _opts = { error: error };
+ return model.hooks.execPost('aggregate', this, [null], _opts, error => {
+ cb(error);
+ });
+ }
+
+ collection.aggregate(pipeline, options, (error, cursor) => {
+ if (error) {
+ const _opts = { error: error };
+ return model.hooks.execPost('aggregate', this, [null], _opts, error => {
+ if (error) {
+ return cb(error);
+ }
+ return cb(null);
+ });
+ }
+ cursor.toArray((error, result) => {
+ const _opts = { error: error };
+ model.hooks.execPost('aggregate', this, [result], _opts, (error, result) => {
+ if (error) {
+ return cb(error);
+ }
+
+ cb(null, result);
+ });
+ });
+ });
+ });
+ }, model.events);
+};
+
+/**
+ * Provides promise for aggregate.
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).then(successCallback, errorCallback);
+ *
+ * @see Promise #promise_Promise
+ * @param {Function} [resolve] successCallback
+ * @param {Function} [reject] errorCallback
+ * @return {Promise}
+ */
+Aggregate.prototype.then = function(resolve, reject) {
+ return this.exec().then(resolve, reject);
+};
+
+/**
+ * Executes the query returning a `Promise` which will be
+ * resolved with either the doc(s) or rejected with the error.
+ * Like [`.then()`](#query_Query-then), but only takes a rejection handler.
+ *
+ * @param {Function} [reject]
+ * @return {Promise}
+ * @api public
+ */
+
+Aggregate.prototype.catch = function(reject) {
+ return this.exec().then(null, reject);
+};
+
+/**
+ * Returns an asyncIterator for use with [`for/await/of` loops](http://bit.ly/async-iterators)
+ * This function *only* works for `find()` queries.
+ * You do not need to call this function explicitly, the JavaScript runtime
+ * will call it for you.
+ *
+ * ####Example
+ *
+ * for await (const doc of Model.find().sort({ name: 1 })) {
+ * console.log(doc.name);
+ * }
+ *
+ * Node.js 10.x supports async iterators natively without any flags. You can
+ * enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
+ *
+ * **Note:** This function is not if `Symbol.asyncIterator` is undefined. If
+ * `Symbol.asyncIterator` is undefined, that means your Node.js version does not
+ * support async iterators.
+ *
+ * @method Symbol.asyncIterator
+ * @memberOf Aggregate
+ * @instance
+ * @api public
+ */
+
+if (Symbol.asyncIterator != null) {
+ Aggregate.prototype[Symbol.asyncIterator] = function() {
+ return this.cursor({ useMongooseAggCursor: true }).
+ exec().
+ transformNull().
+ map(doc => {
+ return doc == null ? { done: true } : { value: doc, done: false };
+ });
+ };
+}
+
+/*!
+ * Helpers
+ */
+
+/**
+ * Checks whether an object is likely a pipeline operator
+ *
+ * @param {Object} obj object to check
+ * @return {Boolean}
+ * @api private
+ */
+
+function isOperator(obj) {
+ if (typeof obj !== 'object') {
+ return false;
+ }
+
+ const k = Object.keys(obj);
+
+ return k.length === 1 && k.some(key => { return key[0] === '$'; });
+}
+
+/*!
+ * Adds the appropriate `$match` pipeline step to the top of an aggregate's
+ * pipeline, should it's model is a non-root discriminator type. This is
+ * analogous to the `prepareDiscriminatorCriteria` function in `lib/query.js`.
+ *
+ * @param {Aggregate} aggregate Aggregate to prepare
+ */
+
+Aggregate._prepareDiscriminatorPipeline = prepareDiscriminatorPipeline;
+
+function prepareDiscriminatorPipeline(aggregate) {
+ const schema = aggregate._model.schema;
+ const discriminatorMapping = schema && schema.discriminatorMapping;
+
+ if (discriminatorMapping && !discriminatorMapping.isRoot) {
+ const originalPipeline = aggregate._pipeline;
+ const discriminatorKey = discriminatorMapping.key;
+ const discriminatorValue = discriminatorMapping.value;
+
+ // If the first pipeline stage is a match and it doesn't specify a `__t`
+ // key, add the discriminator key to it. This allows for potential
+ // aggregation query optimizations not to be disturbed by this feature.
+ if (originalPipeline[0] && originalPipeline[0].$match && !originalPipeline[0].$match[discriminatorKey]) {
+ originalPipeline[0].$match[discriminatorKey] = discriminatorValue;
+ // `originalPipeline` is a ref, so there's no need for
+ // aggregate._pipeline = originalPipeline
+ } else if (originalPipeline[0] && originalPipeline[0].$geoNear) {
+ originalPipeline[0].$geoNear.query =
+ originalPipeline[0].$geoNear.query || {};
+ originalPipeline[0].$geoNear.query[discriminatorKey] = discriminatorValue;
+ } else {
+ const match = {};
+ match[discriminatorKey] = discriminatorValue;
+ aggregate._pipeline.unshift({ $match: match });
+ }
+ }
+}
+
+/*!
+ * Exports
+ */
+
+module.exports = Aggregate;
diff --git a/node_modules/mongoose/lib/browser.js b/node_modules/mongoose/lib/browser.js
new file mode 100644
index 0000000..8b9b4b7
--- /dev/null
+++ b/node_modules/mongoose/lib/browser.js
@@ -0,0 +1,133 @@
+/* eslint-env browser */
+
+'use strict';
+
+require('./driver').set(require('./drivers/browser'));
+
+const DocumentProvider = require('./document_provider.js');
+const PromiseProvider = require('./promise_provider');
+
+DocumentProvider.setBrowser(true);
+
+/**
+ * The Mongoose [Promise](#promise_Promise) constructor.
+ *
+ * @method Promise
+ * @api public
+ */
+
+Object.defineProperty(exports, 'Promise', {
+ get: function() {
+ return PromiseProvider.get();
+ },
+ set: function(lib) {
+ PromiseProvider.set(lib);
+ }
+});
+
+/**
+ * Storage layer for mongoose promises
+ *
+ * @method PromiseProvider
+ * @api public
+ */
+
+exports.PromiseProvider = PromiseProvider;
+
+/**
+ * The [MongooseError](#error_MongooseError) constructor.
+ *
+ * @method Error
+ * @api public
+ */
+
+exports.Error = require('./error');
+
+/**
+ * The Mongoose [Schema](#schema_Schema) constructor
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var Schema = mongoose.Schema;
+ * var CatSchema = new Schema(..);
+ *
+ * @method Schema
+ * @api public
+ */
+
+exports.Schema = require('./schema');
+
+/**
+ * The various Mongoose Types.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var array = mongoose.Types.Array;
+ *
+ * ####Types:
+ *
+ * - [ObjectId](#types-objectid-js)
+ * - [Buffer](#types-buffer-js)
+ * - [SubDocument](#types-embedded-js)
+ * - [Array](#types-array-js)
+ * - [DocumentArray](#types-documentarray-js)
+ *
+ * Using this exposed access to the `ObjectId` type, we can construct ids on demand.
+ *
+ * var ObjectId = mongoose.Types.ObjectId;
+ * var id1 = new ObjectId;
+ *
+ * @property Types
+ * @api public
+ */
+exports.Types = require('./types');
+
+/**
+ * The Mongoose [VirtualType](#virtualtype_VirtualType) constructor
+ *
+ * @method VirtualType
+ * @api public
+ */
+exports.VirtualType = require('./virtualtype');
+
+/**
+ * The various Mongoose SchemaTypes.
+ *
+ * ####Note:
+ *
+ * _Alias of mongoose.Schema.Types for backwards compatibility._
+ *
+ * @property SchemaTypes
+ * @see Schema.SchemaTypes #schema_Schema.Types
+ * @api public
+ */
+
+exports.SchemaType = require('./schematype.js');
+
+/**
+ * Internal utils
+ *
+ * @property utils
+ * @api private
+ */
+
+exports.utils = require('./utils.js');
+
+/**
+ * The Mongoose browser [Document](#document-js) constructor.
+ *
+ * @method Document
+ * @api public
+ */
+exports.Document = DocumentProvider();
+
+/*!
+ * Module exports.
+ */
+
+if (typeof window !== 'undefined') {
+ window.mongoose = module.exports;
+ window.Buffer = Buffer;
+}
diff --git a/node_modules/mongoose/lib/browserDocument.js b/node_modules/mongoose/lib/browserDocument.js
new file mode 100644
index 0000000..b1c803d
--- /dev/null
+++ b/node_modules/mongoose/lib/browserDocument.js
@@ -0,0 +1,102 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const NodeJSDocument = require('./document');
+const EventEmitter = require('events').EventEmitter;
+const MongooseError = require('./error');
+const Schema = require('./schema');
+const ObjectId = require('./types/objectid');
+const ValidationError = MongooseError.ValidationError;
+const applyHooks = require('./helpers/model/applyHooks');
+const utils = require('./utils');
+
+/**
+ * Document constructor.
+ *
+ * @param {Object} obj the values to set
+ * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
+ * @param {Boolean} [skipId] bool, should we auto create an ObjectId _id
+ * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
+ * @event `init`: Emitted on a document after it has was retrieved from the db and fully hydrated by Mongoose.
+ * @event `save`: Emitted when the document is successfully saved
+ * @api private
+ */
+
+function Document(obj, schema, fields, skipId, skipInit) {
+ if (!(this instanceof Document)) {
+ return new Document(obj, schema, fields, skipId, skipInit);
+ }
+
+ if (utils.isObject(schema) && !schema.instanceOfSchema) {
+ schema = new Schema(schema);
+ }
+
+ // When creating EmbeddedDocument, it already has the schema and he doesn't need the _id
+ schema = this.schema || schema;
+
+ // Generate ObjectId if it is missing, but it requires a scheme
+ if (!this.schema && schema.options._id) {
+ obj = obj || {};
+
+ if (obj._id === undefined) {
+ obj._id = new ObjectId();
+ }
+ }
+
+ if (!schema) {
+ throw new MongooseError.MissingSchemaError();
+ }
+
+ this.$__setSchema(schema);
+
+ NodeJSDocument.call(this, obj, fields, skipId, skipInit);
+
+ applyHooks(this, schema, { decorateDoc: true });
+
+ // apply methods
+ for (const m in schema.methods) {
+ this[m] = schema.methods[m];
+ }
+ // apply statics
+ for (const s in schema.statics) {
+ this[s] = schema.statics[s];
+ }
+}
+
+/*!
+ * Inherit from the NodeJS document
+ */
+
+Document.prototype = Object.create(NodeJSDocument.prototype);
+Document.prototype.constructor = Document;
+
+/*!
+ * ignore
+ */
+
+Document.events = new EventEmitter();
+
+/*!
+ * Browser doc exposes the event emitter API
+ */
+
+Document.$emitter = new EventEmitter();
+
+utils.each(
+ ['on', 'once', 'emit', 'listeners', 'removeListener', 'setMaxListeners',
+ 'removeAllListeners', 'addListener'],
+ function(emitterFn) {
+ Document[emitterFn] = function() {
+ return Document.$emitter[emitterFn].apply(Document.$emitter, arguments);
+ };
+ });
+
+/*!
+ * Module exports.
+ */
+
+Document.ValidationError = ValidationError;
+module.exports = exports = Document;
diff --git a/node_modules/mongoose/lib/cast.js b/node_modules/mongoose/lib/cast.js
new file mode 100644
index 0000000..398734b
--- /dev/null
+++ b/node_modules/mongoose/lib/cast.js
@@ -0,0 +1,340 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const StrictModeError = require('./error/strict');
+const Types = require('./schema/index');
+const castTextSearch = require('./schema/operators/text');
+const get = require('./helpers/get');
+const util = require('util');
+const utils = require('./utils');
+
+const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
+
+/**
+ * Handles internal casting for query filters.
+ *
+ * @param {Schema} schema
+ * @param {Object} obj Object to cast
+ * @param {Object} options the query options
+ * @param {Query} context passed to setters
+ * @api private
+ */
+module.exports = function cast(schema, obj, options, context) {
+ if (Array.isArray(obj)) {
+ throw new Error('Query filter must be an object, got an array ', util.inspect(obj));
+ }
+
+ const paths = Object.keys(obj);
+ let i = paths.length;
+ let _keys;
+ let any$conditionals;
+ let schematype;
+ let nested;
+ let path;
+ let type;
+ let val;
+
+ options = options || {};
+
+ while (i--) {
+ path = paths[i];
+ val = obj[path];
+
+ if (path === '$or' || path === '$nor' || path === '$and') {
+ let k = val.length;
+
+ while (k--) {
+ val[k] = cast(schema, val[k], options, context);
+ }
+ } else if (path === '$where') {
+ type = typeof val;
+
+ if (type !== 'string' && type !== 'function') {
+ throw new Error('Must have a string or function for $where');
+ }
+
+ if (type === 'function') {
+ obj[path] = val.toString();
+ }
+
+ continue;
+ } else if (path === '$elemMatch') {
+ val = cast(schema, val, options, context);
+ } else if (path === '$text') {
+ val = castTextSearch(val, path);
+ } else {
+ if (!schema) {
+ // no casting for Mixed types
+ continue;
+ }
+
+ schematype = schema.path(path);
+
+ // Check for embedded discriminator paths
+ if (!schematype) {
+ const split = path.split('.');
+ let j = split.length;
+ while (j--) {
+ const pathFirstHalf = split.slice(0, j).join('.');
+ const pathLastHalf = split.slice(j).join('.');
+ const _schematype = schema.path(pathFirstHalf);
+ const discriminatorKey = get(_schematype, 'schema.options.discriminatorKey');
+
+ // gh-6027: if we haven't found the schematype but this path is
+ // underneath an embedded discriminator and the embedded discriminator
+ // key is in the query, use the embedded discriminator schema
+ if (_schematype != null &&
+ get(_schematype, 'schema.discriminators') != null &&
+ discriminatorKey != null &&
+ pathLastHalf !== discriminatorKey) {
+ const discriminatorVal = get(obj, pathFirstHalf + '.' + discriminatorKey);
+ if (discriminatorVal != null) {
+ schematype = _schematype.schema.discriminators[discriminatorVal].
+ path(pathLastHalf);
+ }
+ }
+ }
+ }
+
+ if (!schematype) {
+ // Handle potential embedded array queries
+ const split = path.split('.');
+ let j = split.length;
+ let pathFirstHalf;
+ let pathLastHalf;
+ let remainingConds;
+
+ // Find the part of the var path that is a path of the Schema
+ while (j--) {
+ pathFirstHalf = split.slice(0, j).join('.');
+ schematype = schema.path(pathFirstHalf);
+ if (schematype) {
+ break;
+ }
+ }
+
+ // If a substring of the input path resolves to an actual real path...
+ if (schematype) {
+ // Apply the casting; similar code for $elemMatch in schema/array.js
+ if (schematype.caster && schematype.caster.schema) {
+ remainingConds = {};
+ pathLastHalf = split.slice(j).join('.');
+ remainingConds[pathLastHalf] = val;
+ obj[path] = cast(schematype.caster.schema, remainingConds, options, context)[pathLastHalf];
+ } else {
+ obj[path] = val;
+ }
+ continue;
+ }
+
+ if (utils.isObject(val)) {
+ // handle geo schemas that use object notation
+ // { loc: { long: Number, lat: Number }
+
+ let geo = '';
+ if (val.$near) {
+ geo = '$near';
+ } else if (val.$nearSphere) {
+ geo = '$nearSphere';
+ } else if (val.$within) {
+ geo = '$within';
+ } else if (val.$geoIntersects) {
+ geo = '$geoIntersects';
+ } else if (val.$geoWithin) {
+ geo = '$geoWithin';
+ }
+
+ if (geo) {
+ const numbertype = new Types.Number('__QueryCasting__');
+ let value = val[geo];
+
+ if (val.$maxDistance != null) {
+ val.$maxDistance = numbertype.castForQueryWrapper({
+ val: val.$maxDistance,
+ context: context
+ });
+ }
+ if (val.$minDistance != null) {
+ val.$minDistance = numbertype.castForQueryWrapper({
+ val: val.$minDistance,
+ context: context
+ });
+ }
+
+ if (geo === '$within') {
+ const withinType = value.$center
+ || value.$centerSphere
+ || value.$box
+ || value.$polygon;
+
+ if (!withinType) {
+ throw new Error('Bad $within parameter: ' + JSON.stringify(val));
+ }
+
+ value = withinType;
+ } else if (geo === '$near' &&
+ typeof value.type === 'string' && Array.isArray(value.coordinates)) {
+ // geojson; cast the coordinates
+ value = value.coordinates;
+ } else if ((geo === '$near' || geo === '$nearSphere' || geo === '$geoIntersects') &&
+ value.$geometry && typeof value.$geometry.type === 'string' &&
+ Array.isArray(value.$geometry.coordinates)) {
+ if (value.$maxDistance != null) {
+ value.$maxDistance = numbertype.castForQueryWrapper({
+ val: value.$maxDistance,
+ context: context
+ });
+ }
+ if (value.$minDistance != null) {
+ value.$minDistance = numbertype.castForQueryWrapper({
+ val: value.$minDistance,
+ context: context
+ });
+ }
+ if (utils.isMongooseObject(value.$geometry)) {
+ value.$geometry = value.$geometry.toObject({
+ transform: false,
+ virtuals: false
+ });
+ }
+ value = value.$geometry.coordinates;
+ } else if (geo === '$geoWithin') {
+ if (value.$geometry) {
+ if (utils.isMongooseObject(value.$geometry)) {
+ value.$geometry = value.$geometry.toObject({ virtuals: false });
+ }
+ const geoWithinType = value.$geometry.type;
+ if (ALLOWED_GEOWITHIN_GEOJSON_TYPES.indexOf(geoWithinType) === -1) {
+ throw new Error('Invalid geoJSON type for $geoWithin "' +
+ geoWithinType + '", must be "Polygon" or "MultiPolygon"');
+ }
+ value = value.$geometry.coordinates;
+ } else {
+ value = value.$box || value.$polygon || value.$center ||
+ value.$centerSphere;
+ if (utils.isMongooseObject(value)) {
+ value = value.toObject({ virtuals: false });
+ }
+ }
+ }
+
+ _cast(value, numbertype, context);
+ continue;
+ }
+ }
+
+ if (schema.nested[path]) {
+ continue;
+ }
+ if (options.upsert && options.strict) {
+ if (options.strict === 'throw') {
+ throw new StrictModeError(path);
+ }
+ throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
+ 'schema, strict mode is `true`, and upsert is `true`.');
+ } else if (options.strictQuery === 'throw') {
+ throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
+ 'schema and strictQuery is true.');
+ } else if (options.strictQuery) {
+ delete obj[path];
+ }
+ } else if (val == null) {
+ continue;
+ } else if (val.constructor.name === 'Object') {
+ any$conditionals = Object.keys(val).some(function(k) {
+ return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
+ });
+
+ if (!any$conditionals) {
+ obj[path] = schematype.castForQueryWrapper({
+ val: val,
+ context: context
+ });
+ } else {
+ const ks = Object.keys(val);
+ let $cond;
+
+ let k = ks.length;
+
+ while (k--) {
+ $cond = ks[k];
+ nested = val[$cond];
+
+ if ($cond === '$not') {
+ if (nested && schematype && !schematype.caster) {
+ _keys = Object.keys(nested);
+ if (_keys.length && _keys[0].charAt(0) === '$') {
+ for (const key in nested) {
+ nested[key] = schematype.castForQueryWrapper({
+ $conditional: key,
+ val: nested[key],
+ context: context
+ });
+ }
+ } else {
+ val[$cond] = schematype.castForQueryWrapper({
+ $conditional: $cond,
+ val: nested,
+ context: context
+ });
+ }
+ continue;
+ }
+ cast(schematype.caster ? schematype.caster.schema : schema, nested, options, context);
+ } else {
+ val[$cond] = schematype.castForQueryWrapper({
+ $conditional: $cond,
+ val: nested,
+ context: context
+ });
+ }
+ }
+ }
+ } else if (Array.isArray(val) && ['Buffer', 'Array'].indexOf(schematype.instance) === -1) {
+ const casted = [];
+ for (let valIndex = 0; valIndex < val.length; valIndex++) {
+ casted.push(schematype.castForQueryWrapper({
+ val: val[valIndex],
+ context: context
+ }));
+ }
+
+ obj[path] = { $in: casted };
+ } else {
+ obj[path] = schematype.castForQueryWrapper({
+ val: val,
+ context: context
+ });
+ }
+ }
+ }
+
+ return obj;
+};
+
+function _cast(val, numbertype, context) {
+ if (Array.isArray(val)) {
+ val.forEach(function(item, i) {
+ if (Array.isArray(item) || utils.isObject(item)) {
+ return _cast(item, numbertype, context);
+ }
+ val[i] = numbertype.castForQueryWrapper({ val: item, context: context });
+ });
+ } else {
+ const nearKeys = Object.keys(val);
+ let nearLen = nearKeys.length;
+ while (nearLen--) {
+ const nkey = nearKeys[nearLen];
+ const item = val[nkey];
+ if (Array.isArray(item) || utils.isObject(item)) {
+ _cast(item, numbertype, context);
+ val[nkey] = item;
+ } else {
+ val[nkey] = numbertype.castForQuery({ val: item, context: context });
+ }
+ }
+ }
+}
diff --git a/node_modules/mongoose/lib/cast/boolean.js b/node_modules/mongoose/lib/cast/boolean.js
new file mode 100644
index 0000000..4843e1f
--- /dev/null
+++ b/node_modules/mongoose/lib/cast/boolean.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const CastError = require('../error/cast');
+
+/*!
+ * Given a value, cast it to a boolean, or throw a `CastError` if the value
+ * cannot be casted. `null` and `undefined` are considered valid.
+ *
+ * @param {Any} value
+ * @param {String} [path] optional the path to set on the CastError
+ * @return {Boolean|null|undefined}
+ * @throws {CastError} if `value` is not one of the allowed values
+ * @api private
+ */
+
+module.exports = function castBoolean(value, path) {
+ if (value == null) {
+ return value;
+ }
+
+ if (module.exports.convertToTrue.has(value)) {
+ return true;
+ }
+ if (module.exports.convertToFalse.has(value)) {
+ return false;
+ }
+ throw new CastError('boolean', value, path);
+};
+
+module.exports.convertToTrue = new Set([true, 'true', 1, '1', 'yes']);
+module.exports.convertToFalse = new Set([false, 'false', 0, '0', 'no']);
diff --git a/node_modules/mongoose/lib/cast/date.js b/node_modules/mongoose/lib/cast/date.js
new file mode 100644
index 0000000..c7f9b04
--- /dev/null
+++ b/node_modules/mongoose/lib/cast/date.js
@@ -0,0 +1,41 @@
+'use strict';
+
+const assert = require('assert');
+
+module.exports = function castDate(value) {
+ // Support empty string because of empty form values. Originally introduced
+ // in https://github.com/Automattic/mongoose/commit/efc72a1898fc3c33a319d915b8c5463a22938dfe
+ if (value == null || value === '') {
+ return null;
+ }
+
+ if (value instanceof Date) {
+ assert.ok(!isNaN(value.valueOf()));
+
+ return value;
+ }
+
+ let date;
+
+ assert.ok(typeof value !== 'boolean');
+
+ if (value instanceof Number || typeof value === 'number') {
+ date = new Date(value);
+ } else if (typeof value === 'string' && !isNaN(Number(value)) && (Number(value) >= 275761 || Number(value) < -271820)) {
+ // string representation of milliseconds take this path
+ date = new Date(Number(value));
+ } else if (typeof value.valueOf === 'function') {
+ // support for moment.js. This is also the path strings will take because
+ // strings have a `valueOf()`
+ date = new Date(value.valueOf());
+ } else {
+ // fallback
+ date = new Date(value);
+ }
+
+ if (!isNaN(date.valueOf())) {
+ return date;
+ }
+
+ assert.ok(false);
+};
diff --git a/node_modules/mongoose/lib/cast/decimal128.js b/node_modules/mongoose/lib/cast/decimal128.js
new file mode 100644
index 0000000..2cd9208
--- /dev/null
+++ b/node_modules/mongoose/lib/cast/decimal128.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const Decimal128Type = require('../types/decimal128');
+const assert = require('assert');
+
+module.exports = function castDecimal128(value) {
+ if (value == null) {
+ return value;
+ }
+
+ if (typeof value === 'object' && typeof value.$numberDecimal === 'string') {
+ return Decimal128Type.fromString(value.$numberDecimal);
+ }
+
+ if (value instanceof Decimal128Type) {
+ return value;
+ }
+
+ if (typeof value === 'string') {
+ return Decimal128Type.fromString(value);
+ }
+
+ if (Buffer.isBuffer(value)) {
+ return new Decimal128Type(value);
+ }
+
+ if (typeof value === 'number') {
+ return Decimal128Type.fromString(String(value));
+ }
+
+ if (typeof value.valueOf === 'function' && typeof value.valueOf() === 'string') {
+ return Decimal128Type.fromString(value.valueOf());
+ }
+
+ assert.ok(false);
+};
diff --git a/node_modules/mongoose/lib/cast/number.js b/node_modules/mongoose/lib/cast/number.js
new file mode 100644
index 0000000..abc22f6
--- /dev/null
+++ b/node_modules/mongoose/lib/cast/number.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const assert = require('assert');
+
+/*!
+ * Given a value, cast it to a number, or throw a `CastError` if the value
+ * cannot be casted. `null` and `undefined` are considered valid.
+ *
+ * @param {Any} value
+ * @param {String} [path] optional the path to set on the CastError
+ * @return {Boolean|null|undefined}
+ * @throws {Error} if `value` is not one of the allowed values
+ * @api private
+ */
+
+module.exports = function castNumber(val) {
+ assert.ok(!isNaN(val));
+
+ if (val == null) {
+ return val;
+ }
+ if (val === '') {
+ return null;
+ }
+
+ if (typeof val === 'string' || typeof val === 'boolean') {
+ val = Number(val);
+ }
+
+ assert.ok(!isNaN(val));
+ if (val instanceof Number) {
+ return val;
+ }
+ if (typeof val === 'number') {
+ return val;
+ }
+ if (!Array.isArray(val) && typeof val.valueOf === 'function') {
+ return Number(val.valueOf());
+ }
+ if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) {
+ return new Number(val);
+ }
+
+ assert.ok(false);
+};
diff --git a/node_modules/mongoose/lib/cast/objectid.js b/node_modules/mongoose/lib/cast/objectid.js
new file mode 100644
index 0000000..a55762a
--- /dev/null
+++ b/node_modules/mongoose/lib/cast/objectid.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const ObjectId = require('../driver').get().ObjectId;
+const assert = require('assert');
+
+module.exports = function castObjectId(value) {
+ if (value == null) {
+ return value;
+ }
+
+ if (value instanceof ObjectId) {
+ return value;
+ }
+
+ if (value._id) {
+ if (value._id instanceof ObjectId) {
+ return value._id;
+ }
+ if (value._id.toString instanceof Function) {
+ return new ObjectId(value._id.toString());
+ }
+ }
+
+ if (value.toString instanceof Function) {
+ return new ObjectId(value.toString());
+ }
+
+ assert.ok(false);
+};
diff --git a/node_modules/mongoose/lib/cast/string.js b/node_modules/mongoose/lib/cast/string.js
new file mode 100644
index 0000000..c908013
--- /dev/null
+++ b/node_modules/mongoose/lib/cast/string.js
@@ -0,0 +1,35 @@
+'use strict';
+
+const CastError = require('../error/cast');
+
+/*!
+ * Given a value, cast it to a string, or throw a `CastError` if the value
+ * cannot be casted. `null` and `undefined` are considered valid.
+ *
+ * @param {Any} value
+ * @param {String} [path] optional the path to set on the CastError
+ * @return {string|null|undefined}
+ * @throws {CastError}
+ * @api private
+ */
+
+module.exports = function castString(value, path) {
+ // If null or undefined
+ if (value == null) {
+ return value;
+ }
+
+ // handle documents being passed
+ if (value._id && typeof value._id === 'string') {
+ return value._id;
+ }
+
+ // Re: gh-647 and gh-3030, we're ok with casting using `toString()`
+ // **unless** its the default Object.toString, because "[object Object]"
+ // doesn't really qualify as useful data
+ if (value.toString && value.toString !== Object.prototype.toString) {
+ return value.toString();
+ }
+
+ throw new CastError('string', value, path);
+};
diff --git a/node_modules/mongoose/lib/collection.js b/node_modules/mongoose/lib/collection.js
new file mode 100644
index 0000000..1f79705
--- /dev/null
+++ b/node_modules/mongoose/lib/collection.js
@@ -0,0 +1,269 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const EventEmitter = require('events').EventEmitter;
+const STATES = require('./connectionstate');
+const immediate = require('./helpers/immediate');
+
+/**
+ * Abstract Collection constructor
+ *
+ * This is the base class that drivers inherit from and implement.
+ *
+ * @param {String} name name of the collection
+ * @param {Connection} conn A MongooseConnection instance
+ * @param {Object} opts optional collection options
+ * @api public
+ */
+
+function Collection(name, conn, opts) {
+ if (opts === void 0) {
+ opts = {};
+ }
+ if (opts.capped === void 0) {
+ opts.capped = {};
+ }
+
+ opts.bufferCommands = undefined === opts.bufferCommands
+ ? true
+ : opts.bufferCommands;
+
+ if (typeof opts.capped === 'number') {
+ opts.capped = {size: opts.capped};
+ }
+
+ this.opts = opts;
+ this.name = name;
+ this.collectionName = name;
+ this.conn = conn;
+ this.queue = [];
+ this.buffer = this.opts.bufferCommands;
+ this.emitter = new EventEmitter();
+
+ if (STATES.connected === this.conn.readyState) {
+ this.onOpen();
+ }
+}
+
+/**
+ * The collection name
+ *
+ * @api public
+ * @property name
+ */
+
+Collection.prototype.name;
+
+/**
+ * The collection name
+ *
+ * @api public
+ * @property collectionName
+ */
+
+Collection.prototype.collectionName;
+
+/**
+ * The Connection instance
+ *
+ * @api public
+ * @property conn
+ */
+
+Collection.prototype.conn;
+
+/**
+ * Called when the database connects
+ *
+ * @api private
+ */
+
+Collection.prototype.onOpen = function() {
+ this.buffer = false;
+ immediate(() => this.doQueue());
+};
+
+/**
+ * Called when the database disconnects
+ *
+ * @api private
+ */
+
+Collection.prototype.onClose = function(force) {
+ if (this.opts.bufferCommands && !force) {
+ this.buffer = true;
+ }
+};
+
+/**
+ * Queues a method for later execution when its
+ * database connection opens.
+ *
+ * @param {String} name name of the method to queue
+ * @param {Array} args arguments to pass to the method when executed
+ * @api private
+ */
+
+Collection.prototype.addQueue = function(name, args) {
+ this.queue.push([name, args]);
+ return this;
+};
+
+/**
+ * Executes all queued methods and clears the queue.
+ *
+ * @api private
+ */
+
+Collection.prototype.doQueue = function() {
+ for (let i = 0, l = this.queue.length; i < l; i++) {
+ if (typeof this.queue[i][0] === 'function') {
+ this.queue[i][0].apply(this, this.queue[i][1]);
+ } else {
+ this[this.queue[i][0]].apply(this, this.queue[i][1]);
+ }
+ }
+ this.queue = [];
+ const _this = this;
+ process.nextTick(function() {
+ _this.emitter.emit('queue');
+ });
+ return this;
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.ensureIndex = function() {
+ throw new Error('Collection#ensureIndex unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.createIndex = function() {
+ throw new Error('Collection#ensureIndex unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.findAndModify = function() {
+ throw new Error('Collection#findAndModify unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.findOneAndUpdate = function() {
+ throw new Error('Collection#findOneAndUpdate unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.findOneAndDelete = function() {
+ throw new Error('Collection#findOneAndDelete unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.findOneAndReplace = function() {
+ throw new Error('Collection#findOneAndReplace unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.findOne = function() {
+ throw new Error('Collection#findOne unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.find = function() {
+ throw new Error('Collection#find unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.insert = function() {
+ throw new Error('Collection#insert unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.insertOne = function() {
+ throw new Error('Collection#insertOne unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.insertMany = function() {
+ throw new Error('Collection#insertMany unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.save = function() {
+ throw new Error('Collection#save unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.update = function() {
+ throw new Error('Collection#update unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.getIndexes = function() {
+ throw new Error('Collection#getIndexes unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.mapReduce = function() {
+ throw new Error('Collection#mapReduce unimplemented by driver');
+};
+
+/**
+ * Abstract method that drivers must implement.
+ */
+
+Collection.prototype.watch = function() {
+ throw new Error('Collection#watch unimplemented by driver');
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = Collection;
diff --git a/node_modules/mongoose/lib/connection.js b/node_modules/mongoose/lib/connection.js
new file mode 100644
index 0000000..875d359
--- /dev/null
+++ b/node_modules/mongoose/lib/connection.js
@@ -0,0 +1,954 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const EventEmitter = require('events').EventEmitter;
+const Schema = require('./schema');
+const Collection = require('./driver').get().Collection;
+const STATES = require('./connectionstate');
+const MongooseError = require('./error');
+const PromiseProvider = require('./promise_provider');
+const get = require('./helpers/get');
+const mongodb = require('mongodb');
+const utils = require('./utils');
+
+const parseConnectionString = require('mongodb-core').parseConnectionString;
+
+/*!
+ * A list of authentication mechanisms that don't require a password for authentication.
+ * This is used by the authMechanismDoesNotRequirePassword method.
+ *
+ * @api private
+ */
+const noPasswordAuthMechanisms = [
+ 'MONGODB-X509'
+];
+
+/**
+ * Connection constructor
+ *
+ * For practical reasons, a Connection equals a Db.
+ *
+ * @param {Mongoose} base a mongoose instance
+ * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
+ * @event `connecting`: Emitted when `connection.openUri()` is executed on this connection.
+ * @event `connected`: Emitted when this connection successfully connects to the db. May be emitted _multiple_ times in `reconnected` scenarios.
+ * @event `open`: Emitted after we `connected` and `onOpen` is executed on all of this connections models.
+ * @event `disconnecting`: Emitted when `connection.close()` was executed.
+ * @event `disconnected`: Emitted after getting disconnected from the db.
+ * @event `close`: Emitted after we `disconnected` and `onClose` executed on all of this connections models.
+ * @event `reconnected`: Emitted after we `connected` and subsequently `disconnected`, followed by successfully another successfull connection.
+ * @event `error`: Emitted when an error occurs on this connection.
+ * @event `fullsetup`: Emitted in a replica-set scenario, when primary and at least one seconaries specified in the connection string are connected.
+ * @event `all`: Emitted in a replica-set scenario, when all nodes specified in the connection string are connected.
+ * @api public
+ */
+
+function Connection(base) {
+ this.base = base;
+ this.collections = {};
+ this.models = {};
+ this.config = {autoIndex: true};
+ this.replica = false;
+ this.options = null;
+ this.otherDbs = []; // FIXME: To be replaced with relatedDbs
+ this.relatedDbs = {}; // Hashmap of other dbs that share underlying connection
+ this.states = STATES;
+ this._readyState = STATES.disconnected;
+ this._closeCalled = false;
+ this._hasOpened = false;
+
+ this.$internalEmitter = new EventEmitter();
+ this.$internalEmitter.setMaxListeners(0);
+}
+
+/*!
+ * Inherit from EventEmitter
+ */
+
+Connection.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Connection ready state
+ *
+ * - 0 = disconnected
+ * - 1 = connected
+ * - 2 = connecting
+ * - 3 = disconnecting
+ *
+ * Each state change emits its associated event name.
+ *
+ * ####Example
+ *
+ * conn.on('connected', callback);
+ * conn.on('disconnected', callback);
+ *
+ * @property readyState
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Object.defineProperty(Connection.prototype, 'readyState', {
+ get: function() {
+ return this._readyState;
+ },
+ set: function(val) {
+ if (!(val in STATES)) {
+ throw new Error('Invalid connection state: ' + val);
+ }
+
+ if (this._readyState !== val) {
+ this._readyState = val;
+ // [legacy] loop over the otherDbs on this connection and change their state
+ for (let i = 0; i < this.otherDbs.length; i++) {
+ this.otherDbs[i].readyState = val;
+ }
+
+ // loop over relatedDbs on this connection and change their state
+ for (const k in this.relatedDbs) {
+ this.relatedDbs[k].readyState = val;
+ }
+
+ if (STATES.connected === val) {
+ this._hasOpened = true;
+ }
+
+ this.emit(STATES[val]);
+ }
+ }
+});
+
+/**
+ * A hash of the collections associated with this connection
+ *
+ * @property collections
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Connection.prototype.collections;
+
+/**
+ * The name of the database this connection points to.
+ *
+ * ####Example
+ *
+ * mongoose.createConnection('mongodb://localhost:27017/mydb').name; // "mydb"
+ *
+ * @property name
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Connection.prototype.name;
+
+/**
+ * The host name portion of the URI. If multiple hosts, such as a replica set,
+ * this will contain the first host name in the URI
+ *
+ * ####Example
+ *
+ * mongoose.createConnection('mongodb://localhost:27017/mydb').host; // "localhost"
+ *
+ * @property host
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Object.defineProperty(Connection.prototype, 'host', {
+ configurable: true,
+ enumerable: true,
+ writable: true
+});
+
+/**
+ * The port portion of the URI. If multiple hosts, such as a replica set,
+ * this will contain the port from the first host name in the URI.
+ *
+ * ####Example
+ *
+ * mongoose.createConnection('mongodb://localhost:27017/mydb').port; // 27017
+ *
+ * @property port
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Object.defineProperty(Connection.prototype, 'port', {
+ configurable: true,
+ enumerable: true,
+ writable: true
+});
+
+/**
+ * The username specified in the URI
+ *
+ * ####Example
+ *
+ * mongoose.createConnection('mongodb://val:psw@localhost:27017/mydb').user; // "val"
+ *
+ * @property user
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Object.defineProperty(Connection.prototype, 'user', {
+ configurable: true,
+ enumerable: true,
+ writable: true
+});
+
+/**
+ * The password specified in the URI
+ *
+ * ####Example
+ *
+ * mongoose.createConnection('mongodb://val:psw@localhost:27017/mydb').pass; // "psw"
+ *
+ * @property pass
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Object.defineProperty(Connection.prototype, 'pass', {
+ configurable: true,
+ enumerable: true,
+ writable: true
+});
+
+/**
+ * The mongodb.Db instance, set when the connection is opened
+ *
+ * @property db
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Connection.prototype.db;
+
+/**
+ * A hash of the global options that are associated with this connection
+ *
+ * @property config
+ * @memberOf Connection
+ * @instance
+ * @api public
+ */
+
+Connection.prototype.config;
+
+/**
+ * Helper for `createCollection()`. Will explicitly create the given collection
+ * with specified options. Used to create [capped collections](https://docs.mongodb.com/manual/core/capped-collections/)
+ * and [views](https://docs.mongodb.com/manual/core/views/) from mongoose.
+ *
+ * Options are passed down without modification to the [MongoDB driver's `createCollection()` function](http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#createCollection)
+ *
+ * @method createCollection
+ * @param {string} collection The collection to create
+ * @param {Object} [options] see [MongoDB driver docs](http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#createCollection)
+ * @param {Function} [callback]
+ * @return {Promise}
+ * @api public
+ */
+
+Connection.prototype.createCollection = _wrapConnHelper(function createCollection(collection, options, cb) {
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
+ this.db.createCollection(collection, options, cb);
+});
+
+/**
+ * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://docs.mongodb.com/manual/release-notes/3.6/#client-sessions)
+ * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/),
+ * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html).
+ *
+ * ####Example:
+ *
+ * const session = await conn.startSession();
+ * let doc = await Person.findOne({ name: 'Ned Stark' }, null, { session });
+ * await doc.remove();
+ * // `doc` will always be null, even if reading from a replica set
+ * // secondary. Without causal consistency, it is possible to
+ * // get a doc back from the below query if the query reads from a
+ * // secondary that is experiencing replication lag.
+ * doc = await Person.findOne({ name: 'Ned Stark' }, null, { session, readPreference: 'secondary' });
+ *
+ *
+ * @method startSession
+ * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#startSession)
+ * @param {Boolean} [options.causalConsistency=true] set to false to disable causal consistency
+ * @param {Function} [callback]
+ * @return {Promise} promise that resolves to a MongoDB driver `ClientSession`
+ * @api public
+ */
+
+Connection.prototype.startSession = _wrapConnHelper(function startSession(options, cb) {
+ if (typeof options === 'function') {
+ cb = options;
+ options = null;
+ }
+ const session = this.client.startSession(options);
+ cb(null, session);
+});
+
+/**
+ * Helper for `dropCollection()`. Will delete the given collection, including
+ * all documents and indexes.
+ *
+ * @method dropCollection
+ * @param {string} collection The collection to delete
+ * @param {Function} [callback]
+ * @return {Promise}
+ * @api public
+ */
+
+Connection.prototype.dropCollection = _wrapConnHelper(function dropCollection(collection, cb) {
+ this.db.dropCollection(collection, cb);
+});
+
+/**
+ * Helper for `dropDatabase()`. Deletes the given database, including all
+ * collections, documents, and indexes.
+ *
+ * @method dropDatabase
+ * @param {Function} [callback]
+ * @return {Promise}
+ * @api public
+ */
+
+Connection.prototype.dropDatabase = _wrapConnHelper(function dropDatabase(cb) {
+ this.$internalEmitter.emit('dropDatabase');
+ this.db.dropDatabase(cb);
+});
+
+/*!
+ * ignore
+ */
+
+function _wrapConnHelper(fn) {
+ return function() {
+ const cb = arguments.length > 0 ? arguments[arguments.length - 1] : null;
+ const argsWithoutCb = typeof cb === 'function' ?
+ Array.prototype.slice.call(arguments, 0, arguments.length - 1) :
+ Array.prototype.slice.call(arguments);
+ return utils.promiseOrCallback(cb, cb => {
+ if (this.readyState !== STATES.connected) {
+ this.once('open', function() {
+ fn.apply(this, argsWithoutCb.concat([cb]));
+ });
+ } else {
+ fn.apply(this, argsWithoutCb.concat([cb]));
+ }
+ });
+ };
+}
+
+/**
+ * error
+ *
+ * Graceful error handling, passes error to callback
+ * if available, else emits error on the connection.
+ *
+ * @param {Error} err
+ * @param {Function} callback optional
+ * @api private
+ */
+
+Connection.prototype.error = function(err, callback) {
+ if (callback) {
+ callback(err);
+ return null;
+ }
+ if (this.listeners('error').length > 0) {
+ this.emit('error', err);
+ }
+ return Promise.reject(err);
+};
+
+/**
+ * Called when the connection is opened
+ *
+ * @api private
+ */
+
+Connection.prototype.onOpen = function() {
+ this.readyState = STATES.connected;
+
+ // avoid having the collection subscribe to our event emitter
+ // to prevent 0.3 warning
+ for (const i in this.collections) {
+ if (utils.object.hasOwnProperty(this.collections, i)) {
+ this.collections[i].onOpen();
+ }
+ }
+
+ this.emit('open');
+};
+
+/**
+ * Opens the connection with a URI using `MongoClient.connect()`.
+ *
+ * @param {String} uri The URI to connect with.
+ * @param {Object} [options] Passed on to http://mongodb.github.io/node-mongodb-native/2.2/api/MongoClient.html#connect
+ * @param {Function} [callback]
+ * @returns {Connection} this
+ * @api private
+ */
+
+Connection.prototype.openUri = function(uri, options, callback) {
+ this.readyState = STATES.connecting;
+ this._closeCalled = false;
+
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ if (['string', 'number'].indexOf(typeof options) !== -1) {
+ throw new MongooseError('Mongoose 5.x no longer supports ' +
+ '`mongoose.connect(host, dbname, port)` or ' +
+ '`mongoose.createConnection(host, dbname, port)`. See ' +
+ 'http://mongoosejs.com/docs/connections.html for supported connection syntax');
+ }
+
+ if (typeof uri !== 'string') {
+ throw new MongooseError('The `uri` parameter to `openUri()` must be a ' +
+ `string, got "${typeof uri}". Make sure the first parameter to ` +
+ '`mongoose.connect()` or `mongoose.createConnection()` is a string.');
+ }
+
+ const Promise = PromiseProvider.get();
+ const _this = this;
+
+ if (options) {
+ options = utils.clone(options);
+ const autoIndex = options.config && options.config.autoIndex != null ?
+ options.config.autoIndex :
+ options.autoIndex;
+ if (autoIndex != null) {
+ this.config.autoIndex = autoIndex !== false;
+ delete options.config;
+ delete options.autoIndex;
+ }
+
+ if ('autoCreate' in options) {
+ this.config.autoCreate = !!options.autoCreate;
+ delete options.autoCreate;
+ }
+ if ('useCreateIndex' in options) {
+ this.config.useCreateIndex = !!options.useCreateIndex;
+ delete options.useCreateIndex;
+ }
+
+ if ('useFindAndModify' in options) {
+ this.config.useFindAndModify = !!options.useFindAndModify;
+ delete options.useFindAndModify;
+ }
+
+ // Backwards compat
+ if (options.user || options.pass) {
+ options.auth = options.auth || {};
+ options.auth.user = options.user;
+ options.auth.password = options.pass;
+
+ this.user = options.user;
+ this.pass = options.pass;
+ }
+ delete options.user;
+ delete options.pass;
+
+ if (options.bufferCommands != null) {
+ options.bufferMaxEntries = 0;
+ this.config.bufferCommands = options.bufferCommands;
+ delete options.bufferCommands;
+ }
+
+ if (options.useMongoClient != null) {
+ handleUseMongoClient(options);
+ }
+ } else {
+ options = {};
+ }
+
+ this._connectionOptions = options;
+ const dbName = options.dbName;
+ if (dbName != null) {
+ this.$dbName = dbName;
+ }
+ delete options.dbName;
+
+ if (!('promiseLibrary' in options)) {
+ options.promiseLibrary = PromiseProvider.get();
+ }
+ if (!('useNewUrlParser' in options)) {
+ if ('useNewUrlParser' in this.base.options) {
+ options.useNewUrlParser = this.base.options.useNewUrlParser;
+ } else {
+ options.useNewUrlParser = false;
+ }
+ }
+
+ const parsePromise = new Promise((resolve, reject) => {
+ parseConnectionString(uri, options, (err, parsed) => {
+ if (err) {
+ return reject(err);
+ }
+ this.name = dbName != null ? dbName : get(parsed, 'auth.db', null);
+ this.host = get(parsed, 'hosts.0.host', 'localhost');
+ this.port = get(parsed, 'hosts.0.port', 27017);
+ this.user = this.user || get(parsed, 'auth.username');
+ this.pass = this.pass || get(parsed, 'auth.password');
+ resolve();
+ });
+ });
+
+ const promise = new Promise((resolve, reject) => {
+ const client = new mongodb.MongoClient(uri, options);
+ _this.client = client;
+ client.connect(function(error) {
+ if (error) {
+ _this.readyState = STATES.disconnected;
+ return reject(error);
+ }
+
+ const db = dbName != null ? client.db(dbName) : client.db();
+ _this.db = db;
+
+ // Backwards compat for mongoose 4.x
+ db.on('reconnect', function() {
+ _this.readyState = STATES.connected;
+ _this.emit('reconnect');
+ _this.emit('reconnected');
+ });
+ db.s.topology.on('reconnectFailed', function() {
+ _this.emit('reconnectFailed');
+ });
+ db.s.topology.on('left', function(data) {
+ _this.emit('left', data);
+ });
+ db.s.topology.on('joined', function(data) {
+ _this.emit('joined', data);
+ });
+ db.s.topology.on('fullsetup', function(data) {
+ _this.emit('fullsetup', data);
+ });
+ db.on('close', function() {
+ // Implicitly emits 'disconnected'
+ _this.readyState = STATES.disconnected;
+ });
+ client.on('left', function() {
+ if (_this.readyState === STATES.connected &&
+ get(db, 's.topology.s.coreTopology.s.replicaSetState.topologyType') === 'ReplicaSetNoPrimary') {
+ _this.readyState = STATES.disconnected;
+ }
+ });
+ db.on('timeout', function() {
+ _this.emit('timeout');
+ });
+
+ delete _this.then;
+ delete _this.catch;
+ _this.readyState = STATES.connected;
+
+ for (const i in _this.collections) {
+ if (utils.object.hasOwnProperty(_this.collections, i)) {
+ _this.collections[i].onOpen();
+ }
+ }
+
+ resolve(_this);
+ _this.emit('open');
+ });
+ });
+
+ this.$initialConnection = Promise.all([promise, parsePromise]).
+ then(res => res[0]).
+ catch(err => {
+ if (this.listeners('error').length > 0) {
+ process.nextTick(() => this.emit('error', err));
+ return;
+ }
+ throw err;
+ });
+ this.then = function(resolve, reject) {
+ return this.$initialConnection.then(resolve, reject);
+ };
+ this.catch = function(reject) {
+ return this.$initialConnection.catch(reject);
+ };
+
+ if (callback != null) {
+ this.$initialConnection = this.$initialConnection.then(
+ () => callback(null, this),
+ err => callback(err)
+ );
+ }
+
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+const handleUseMongoClient = function handleUseMongoClient(options) {
+ console.warn('WARNING: The `useMongoClient` option is no longer ' +
+ 'necessary in mongoose 5.x, please remove it.');
+ const stack = new Error().stack;
+ console.warn(stack.substr(stack.indexOf('\n') + 1));
+ delete options.useMongoClient;
+};
+
+/**
+ * Closes the connection
+ *
+ * @param {Boolean} [force] optional
+ * @param {Function} [callback] optional
+ * @return {Connection} self
+ * @api public
+ */
+
+Connection.prototype.close = function(force, callback) {
+ if (typeof force === 'function') {
+ callback = force;
+ force = false;
+ }
+
+ this.$wasForceClosed = !!force;
+
+ return utils.promiseOrCallback(callback, cb => {
+ this._close(force, cb);
+ });
+};
+
+/**
+ * Handles closing the connection
+ *
+ * @param {Boolean} force
+ * @param {Function} callback
+ * @api private
+ */
+Connection.prototype._close = function(force, callback) {
+ const _this = this;
+ this._closeCalled = true;
+
+ switch (this.readyState) {
+ case 0: // disconnected
+ callback();
+ break;
+
+ case 1: // connected
+ this.readyState = STATES.disconnecting;
+ this.doClose(force, function(err) {
+ if (err) {
+ return callback(err);
+ }
+ _this.onClose(force);
+ callback(null);
+ });
+
+ break;
+ case 2: // connecting
+ this.once('open', function() {
+ _this.close(callback);
+ });
+ break;
+
+ case 3: // disconnecting
+ this.once('close', function() {
+ callback();
+ });
+ break;
+ }
+
+ return this;
+};
+
+/**
+ * Called when the connection closes
+ *
+ * @api private
+ */
+
+Connection.prototype.onClose = function(force) {
+ this.readyState = STATES.disconnected;
+
+ // avoid having the collection subscribe to our event emitter
+ // to prevent 0.3 warning
+ for (const i in this.collections) {
+ if (utils.object.hasOwnProperty(this.collections, i)) {
+ this.collections[i].onClose(force);
+ }
+ }
+
+ this.emit('close', force);
+};
+
+/**
+ * Retrieves a collection, creating it if not cached.
+ *
+ * Not typically needed by applications. Just talk to your collection through your model.
+ *
+ * @param {String} name of the collection
+ * @param {Object} [options] optional collection options
+ * @return {Collection} collection instance
+ * @api public
+ */
+
+Connection.prototype.collection = function(name, options) {
+ options = options ? utils.clone(options) : {};
+ options.$wasForceClosed = this.$wasForceClosed;
+ if (!(name in this.collections)) {
+ this.collections[name] = new Collection(name, this, options);
+ }
+ return this.collections[name];
+};
+
+/**
+ * Defines or retrieves a model.
+ *
+ * var mongoose = require('mongoose');
+ * var db = mongoose.createConnection(..);
+ * db.model('Venue', new Schema(..));
+ * var Ticket = db.model('Ticket', new Schema(..));
+ * var Venue = db.model('Venue');
+ *
+ * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the [utils.toCollectionName](#utils_exports.toCollectionName) method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ name: String }, { collection: 'actor' });
+ *
+ * // or
+ *
+ * schema.set('collection', 'actor');
+ *
+ * // or
+ *
+ * var collectionName = 'actor'
+ * var M = conn.model('Actor', schema, collectionName)
+ *
+ * @param {String|Function} name the model name or class extending Model
+ * @param {Schema} [schema] a schema. necessary when defining a model
+ * @param {String} [collection] name of mongodb collection (optional) if not given it will be induced from model name
+ * @see Mongoose#model #index_Mongoose-model
+ * @return {Model} The compiled model
+ * @api public
+ */
+
+Connection.prototype.model = function(name, schema, collection) {
+ if (!(this instanceof Connection)) {
+ throw new MongooseError('`connection.model()` should not be run with ' +
+ '`new`. If you are doing `new db.model(foo)(bar)`, use ' +
+ '`db.model(foo)(bar)` instead');
+ }
+
+ let fn;
+ if (typeof name === 'function') {
+ fn = name;
+ name = fn.name;
+ }
+
+ // collection name discovery
+ if (typeof schema === 'string') {
+ collection = schema;
+ schema = false;
+ }
+
+ if (utils.isObject(schema) && !schema.instanceOfSchema) {
+ schema = new Schema(schema);
+ }
+ if (schema && !schema.instanceOfSchema) {
+ throw new Error('The 2nd parameter to `mongoose.model()` should be a ' +
+ 'schema or a POJO');
+ }
+
+ if (this.models[name] && !collection) {
+ // model exists but we are not subclassing with custom collection
+ if (schema && schema.instanceOfSchema && schema !== this.models[name].schema) {
+ throw new MongooseError.OverwriteModelError(name);
+ }
+ return this.models[name];
+ }
+
+ const opts = {cache: false, connection: this};
+ let model;
+
+ if (schema && schema.instanceOfSchema) {
+ // compile a model
+ model = this.base.model(fn || name, schema, collection, opts);
+
+ // only the first model with this name is cached to allow
+ // for one-offs with custom collection names etc.
+ if (!this.models[name]) {
+ this.models[name] = model;
+ }
+
+ // Errors handled internally, so safe to ignore error
+ model.init(function $modelInitNoop() {});
+
+ return model;
+ }
+
+ if (this.models[name] && collection) {
+ // subclassing current model with alternate collection
+ model = this.models[name];
+ schema = model.prototype.schema;
+ const sub = model.__subclass(this, schema, collection);
+ // do not cache the sub model
+ return sub;
+ }
+
+ // lookup model in mongoose module
+ model = this.base.models[name];
+
+ if (!model) {
+ throw new MongooseError.MissingSchemaError(name);
+ }
+
+ if (this === model.prototype.db
+ && (!collection || collection === model.collection.name)) {
+ // model already uses this connection.
+
+ // only the first model with this name is cached to allow
+ // for one-offs with custom collection names etc.
+ if (!this.models[name]) {
+ this.models[name] = model;
+ }
+
+ return model;
+ }
+ this.models[name] = model.__subclass(this, schema, collection);
+ return this.models[name];
+};
+
+/**
+ * Removes the model named `name` from this connection, if it exists. You can
+ * use this function to clean up any models you created in your tests to
+ * prevent OverwriteModelErrors.
+ *
+ * ####Example:
+ *
+ * conn.model('User', new Schema({ name: String }));
+ * console.log(conn.model('User')); // Model object
+ * conn.deleteModel('User');
+ * console.log(conn.model('User')); // undefined
+ *
+ * // Usually useful in a Mocha `afterEach()` hook
+ * afterEach(function() {
+ * conn.deleteModel(/.+/); // Delete every model
+ * });
+ *
+ * @api public
+ * @param {String|RegExp} name if string, the name of the model to remove. If regexp, removes all models whose name matches the regexp.
+ * @return {Connection} this
+ */
+
+Connection.prototype.deleteModel = function(name) {
+ if (typeof name === 'string') {
+ const model = this.model(name);
+ if (model == null) {
+ return this;
+ }
+ delete this.models[name];
+ delete this.collections[model.collection.name];
+ delete this.base.modelSchemas[name];
+ } else if (name instanceof RegExp) {
+ const pattern = name;
+ const names = this.modelNames();
+ for (const name of names) {
+ if (pattern.test(name)) {
+ this.deleteModel(name);
+ }
+ }
+ } else {
+ throw new Error('First parameter to `deleteModel()` must be a string ' +
+ 'or regexp, got "' + name + '"');
+ }
+
+ return this;
+};
+
+/**
+ * Returns an array of model names created on this connection.
+ * @api public
+ * @return {Array}
+ */
+
+Connection.prototype.modelNames = function() {
+ return Object.keys(this.models);
+};
+
+/**
+ * @brief Returns if the connection requires authentication after it is opened. Generally if a
+ * username and password are both provided than authentication is needed, but in some cases a
+ * password is not required.
+ * @api private
+ * @return {Boolean} true if the connection should be authenticated after it is opened, otherwise false.
+ */
+Connection.prototype.shouldAuthenticate = function() {
+ return this.user != null &&
+ (this.pass != null || this.authMechanismDoesNotRequirePassword());
+};
+
+/**
+ * @brief Returns a boolean value that specifies if the current authentication mechanism needs a
+ * password to authenticate according to the auth objects passed into the openUri methods.
+ * @api private
+ * @return {Boolean} true if the authentication mechanism specified in the options object requires
+ * a password, otherwise false.
+ */
+Connection.prototype.authMechanismDoesNotRequirePassword = function() {
+ if (this.options && this.options.auth) {
+ return noPasswordAuthMechanisms.indexOf(this.options.auth.authMechanism) >= 0;
+ }
+ return true;
+};
+
+/**
+ * @brief Returns a boolean value that specifies if the provided objects object provides enough
+ * data to authenticate with. Generally this is true if the username and password are both specified
+ * but in some authentication methods, a password is not required for authentication so only a username
+ * is required.
+ * @param {Object} [options] the options object passed into the openUri methods.
+ * @api private
+ * @return {Boolean} true if the provided options object provides enough data to authenticate with,
+ * otherwise false.
+ */
+Connection.prototype.optionsProvideAuthenticationData = function(options) {
+ return (options) &&
+ (options.user) &&
+ ((options.pass) || this.authMechanismDoesNotRequirePassword());
+};
+
+/**
+ * Switches to a different database using the same connection pool.
+ *
+ * Returns a new connection object, with the new db.
+ *
+ * @method useDb
+ * @memberOf Connection
+ * @param {String} name The database name
+ * @return {Connection} New Connection Object
+ * @api public
+ */
+
+/*!
+ * Module exports.
+ */
+
+Connection.STATES = STATES;
+module.exports = Connection;
diff --git a/node_modules/mongoose/lib/connectionstate.js b/node_modules/mongoose/lib/connectionstate.js
new file mode 100644
index 0000000..920f45b
--- /dev/null
+++ b/node_modules/mongoose/lib/connectionstate.js
@@ -0,0 +1,26 @@
+
+/*!
+ * Connection states
+ */
+
+'use strict';
+
+const STATES = module.exports = exports = Object.create(null);
+
+const disconnected = 'disconnected';
+const connected = 'connected';
+const connecting = 'connecting';
+const disconnecting = 'disconnecting';
+const uninitialized = 'uninitialized';
+
+STATES[0] = disconnected;
+STATES[1] = connected;
+STATES[2] = connecting;
+STATES[3] = disconnecting;
+STATES[99] = uninitialized;
+
+STATES[disconnected] = 0;
+STATES[connected] = 1;
+STATES[connecting] = 2;
+STATES[disconnecting] = 3;
+STATES[uninitialized] = 99;
diff --git a/node_modules/mongoose/lib/cursor/AggregationCursor.js b/node_modules/mongoose/lib/cursor/AggregationCursor.js
new file mode 100644
index 0000000..45acc94
--- /dev/null
+++ b/node_modules/mongoose/lib/cursor/AggregationCursor.js
@@ -0,0 +1,296 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const Readable = require('stream').Readable;
+const eachAsync = require('../helpers/cursor/eachAsync');
+const util = require('util');
+const utils = require('../utils');
+
+/**
+ * An AggregationCursor is a concurrency primitive for processing aggregation
+ * results one document at a time. It is analogous to QueryCursor.
+ *
+ * An AggregationCursor fulfills the Node.js streams3 API,
+ * in addition to several other mechanisms for loading documents from MongoDB
+ * one at a time.
+ *
+ * Creating an AggregationCursor executes the model's pre aggregate hooks,
+ * but **not** the model's post aggregate hooks.
+ *
+ * Unless you're an advanced user, do **not** instantiate this class directly.
+ * Use [`Aggregate#cursor()`](/docs/api.html#aggregate_Aggregate-cursor) instead.
+ *
+ * @param {Aggregate} agg
+ * @param {Object} options
+ * @inherits Readable
+ * @event `cursor`: Emitted when the cursor is created
+ * @event `error`: Emitted when an error occurred
+ * @event `data`: Emitted when the stream is flowing and the next doc is ready
+ * @event `end`: Emitted when the stream is exhausted
+ * @api public
+ */
+
+function AggregationCursor(agg) {
+ Readable.call(this, { objectMode: true });
+
+ this.cursor = null;
+ this.agg = agg;
+ this._transforms = [];
+ const model = agg._model;
+ delete agg.options.cursor.useMongooseAggCursor;
+ this._mongooseOptions = {};
+
+ _init(model, this, agg);
+}
+
+util.inherits(AggregationCursor, Readable);
+
+/*!
+ * ignore
+ */
+
+function _init(model, c, agg) {
+ if (!model.collection.buffer) {
+ model.hooks.execPre('aggregate', agg, function() {
+ c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
+ c.emit('cursor', c.cursor);
+ });
+ } else {
+ model.collection.emitter.once('queue', function() {
+ model.hooks.execPre('aggregate', agg, function() {
+ c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
+ c.emit('cursor', c.cursor);
+ });
+ });
+ }
+}
+
+/*!
+ * Necessary to satisfy the Readable API
+ */
+
+AggregationCursor.prototype._read = function() {
+ const _this = this;
+ _next(this, function(error, doc) {
+ if (error) {
+ return _this.emit('error', error);
+ }
+ if (!doc) {
+ _this.push(null);
+ _this.cursor.close(function(error) {
+ if (error) {
+ return _this.emit('error', error);
+ }
+ setTimeout(function() {
+ _this.emit('close');
+ }, 0);
+ });
+ return;
+ }
+ _this.push(doc);
+ });
+};
+
+/**
+ * Registers a transform function which subsequently maps documents retrieved
+ * via the streams interface or `.next()`
+ *
+ * ####Example
+ *
+ * // Map documents returned by `data` events
+ * Thing.
+ * find({ name: /^hello/ }).
+ * cursor().
+ * map(function (doc) {
+ * doc.foo = "bar";
+ * return doc;
+ * })
+ * on('data', function(doc) { console.log(doc.foo); });
+ *
+ * // Or map documents returned by `.next()`
+ * var cursor = Thing.find({ name: /^hello/ }).
+ * cursor().
+ * map(function (doc) {
+ * doc.foo = "bar";
+ * return doc;
+ * });
+ * cursor.next(function(error, doc) {
+ * console.log(doc.foo);
+ * });
+ *
+ * @param {Function} fn
+ * @return {AggregationCursor}
+ * @api public
+ * @method map
+ */
+
+AggregationCursor.prototype.map = function(fn) {
+ this._transforms.push(fn);
+ return this;
+};
+
+/*!
+ * Marks this cursor as errored
+ */
+
+AggregationCursor.prototype._markError = function(error) {
+ this._error = error;
+ return this;
+};
+
+/**
+ * Marks this cursor as closed. Will stop streaming and subsequent calls to
+ * `next()` will error.
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ * @api public
+ * @method close
+ * @emits close
+ * @see MongoDB driver cursor#close http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#close
+ */
+
+AggregationCursor.prototype.close = function(callback) {
+ return utils.promiseOrCallback(callback, cb => {
+ this.cursor.close(error => {
+ if (error) {
+ cb(error);
+ return this.listeners('error').length > 0 && this.emit('error', error);
+ }
+ this.emit('close');
+ cb(null);
+ });
+ });
+};
+
+/**
+ * Get the next document from this cursor. Will return `null` when there are
+ * no documents left.
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ * @api public
+ * @method next
+ */
+
+AggregationCursor.prototype.next = function(callback) {
+ return utils.promiseOrCallback(callback, cb => {
+ _next(this, cb);
+ });
+};
+
+/**
+ * Execute `fn` for every document in the cursor. If `fn` returns a promise,
+ * will wait for the promise to resolve before iterating on to the next one.
+ * Returns a promise that resolves when done.
+ *
+ * @param {Function} fn
+ * @param {Object} [options]
+ * @param {Number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.
+ * @param {Function} [callback] executed when all docs have been processed
+ * @return {Promise}
+ * @api public
+ * @method eachAsync
+ */
+
+AggregationCursor.prototype.eachAsync = function(fn, opts, callback) {
+ const _this = this;
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ }
+ opts = opts || {};
+
+ return eachAsync(function(cb) { return _next(_this, cb); }, fn, opts, callback);
+};
+
+/*!
+ * ignore
+ */
+
+AggregationCursor.prototype.transformNull = function(val) {
+ if (arguments.length === 0) {
+ val = true;
+ }
+ this._mongooseOptions.transformNull = val;
+ return this;
+};
+
+/**
+ * Adds a [cursor flag](http://mongodb.github.io/node-mongodb-native/2.2/api/Cursor.html#addCursorFlag).
+ * Useful for setting the `noCursorTimeout` and `tailable` flags.
+ *
+ * @param {String} flag
+ * @param {Boolean} value
+ * @return {AggregationCursor} this
+ * @api public
+ * @method addCursorFlag
+ */
+
+AggregationCursor.prototype.addCursorFlag = function(flag, value) {
+ const _this = this;
+ _waitForCursor(this, function() {
+ _this.cursor.addCursorFlag(flag, value);
+ });
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+function _waitForCursor(ctx, cb) {
+ if (ctx.cursor) {
+ return cb();
+ }
+ ctx.once('cursor', function() {
+ cb();
+ });
+}
+
+/*!
+ * Get the next doc from the underlying cursor and mongooseify it
+ * (populate, etc.)
+ */
+
+function _next(ctx, cb) {
+ let callback = cb;
+ if (ctx._transforms.length) {
+ callback = function(err, doc) {
+ if (err || (doc === null && !ctx._mongooseOptions.transformNull)) {
+ return cb(err, doc);
+ }
+ cb(err, ctx._transforms.reduce(function(doc, fn) {
+ return fn(doc);
+ }, doc));
+ };
+ }
+
+ if (ctx._error) {
+ return process.nextTick(function() {
+ callback(ctx._error);
+ });
+ }
+
+ if (ctx.cursor) {
+ return ctx.cursor.next(function(error, doc) {
+ if (error) {
+ return callback(error);
+ }
+ if (!doc) {
+ return callback(null, null);
+ }
+
+ callback(null, doc);
+ });
+ } else {
+ ctx.once('cursor', function() {
+ _next(ctx, cb);
+ });
+ }
+}
+
+module.exports = AggregationCursor;
diff --git a/node_modules/mongoose/lib/cursor/ChangeStream.js b/node_modules/mongoose/lib/cursor/ChangeStream.js
new file mode 100644
index 0000000..e435216
--- /dev/null
+++ b/node_modules/mongoose/lib/cursor/ChangeStream.js
@@ -0,0 +1,58 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const EventEmitter = require('events').EventEmitter;
+
+/*!
+ * ignore
+ */
+
+class ChangeStream extends EventEmitter {
+ constructor(model, pipeline, options) {
+ super();
+
+ this.driverChangeStream = null;
+ this.closed = false;
+ // This wrapper is necessary because of buffering.
+ if (model.collection.buffer) {
+ model.collection.addQueue(() => {
+ if (this.closed) {
+ return;
+ }
+ this.driverChangeStream = model.collection.watch(pipeline, options);
+ this._bindEvents();
+ this.emit('ready');
+ });
+ } else {
+ this.driverChangeStream = model.collection.watch(pipeline, options);
+ this._bindEvents();
+ this.emit('ready');
+ }
+ }
+
+ _bindEvents() {
+ ['close', 'change', 'end', 'error'].forEach(ev => {
+ this.driverChangeStream.on(ev, data => this.emit(ev, data));
+ });
+ }
+
+ _queue(cb) {
+ this.once('ready', () => cb());
+ }
+
+ close() {
+ this.closed = true;
+ if (this.driverChangeStream) {
+ this.driverChangeStream.close();
+ }
+ }
+}
+
+/*!
+ * ignore
+ */
+
+module.exports = ChangeStream;
diff --git a/node_modules/mongoose/lib/cursor/QueryCursor.js b/node_modules/mongoose/lib/cursor/QueryCursor.js
new file mode 100644
index 0000000..d0f2f2c
--- /dev/null
+++ b/node_modules/mongoose/lib/cursor/QueryCursor.js
@@ -0,0 +1,331 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const Readable = require('stream').Readable;
+const eachAsync = require('../helpers/cursor/eachAsync');
+const helpers = require('../queryhelpers');
+const util = require('util');
+const utils = require('../utils');
+
+/**
+ * A QueryCursor is a concurrency primitive for processing query results
+ * one document at a time. A QueryCursor fulfills the Node.js streams3 API,
+ * in addition to several other mechanisms for loading documents from MongoDB
+ * one at a time.
+ *
+ * QueryCursors execute the model's pre find hooks, but **not** the model's
+ * post find hooks.
+ *
+ * Unless you're an advanced user, do **not** instantiate this class directly.
+ * Use [`Query#cursor()`](/docs/api.html#query_Query-cursor) instead.
+ *
+ * @param {Query} query
+ * @param {Object} options query options passed to `.find()`
+ * @inherits Readable
+ * @event `cursor`: Emitted when the cursor is created
+ * @event `error`: Emitted when an error occurred
+ * @event `data`: Emitted when the stream is flowing and the next doc is ready
+ * @event `end`: Emitted when the stream is exhausted
+ * @api public
+ */
+
+function QueryCursor(query, options) {
+ Readable.call(this, { objectMode: true });
+
+ this.cursor = null;
+ this.query = query;
+ const _this = this;
+ const model = query.model;
+ this._mongooseOptions = {};
+ this._transforms = [];
+ this.model = model;
+ model.hooks.execPre('find', query, () => {
+ this._transforms = this._transforms.concat(query._transforms.slice());
+ if (options.transform) {
+ this._transforms.push(options.transform);
+ }
+ model.collection.find(query._conditions, options, function(err, cursor) {
+ if (_this._error) {
+ cursor.close(function() {});
+ _this.listeners('error').length > 0 && _this.emit('error', _this._error);
+ }
+ if (err) {
+ return _this.emit('error', err);
+ }
+ _this.cursor = cursor;
+ _this.emit('cursor', cursor);
+ });
+ });
+}
+
+util.inherits(QueryCursor, Readable);
+
+/*!
+ * Necessary to satisfy the Readable API
+ */
+
+QueryCursor.prototype._read = function() {
+ const _this = this;
+ _next(this, function(error, doc) {
+ if (error) {
+ return _this.emit('error', error);
+ }
+ if (!doc) {
+ _this.push(null);
+ _this.cursor.close(function(error) {
+ if (error) {
+ return _this.emit('error', error);
+ }
+ setTimeout(function() {
+ _this.emit('close');
+ }, 0);
+ });
+ return;
+ }
+ _this.push(doc);
+ });
+};
+
+/**
+ * Registers a transform function which subsequently maps documents retrieved
+ * via the streams interface or `.next()`
+ *
+ * ####Example
+ *
+ * // Map documents returned by `data` events
+ * Thing.
+ * find({ name: /^hello/ }).
+ * cursor().
+ * map(function (doc) {
+ * doc.foo = "bar";
+ * return doc;
+ * })
+ * on('data', function(doc) { console.log(doc.foo); });
+ *
+ * // Or map documents returned by `.next()`
+ * var cursor = Thing.find({ name: /^hello/ }).
+ * cursor().
+ * map(function (doc) {
+ * doc.foo = "bar";
+ * return doc;
+ * });
+ * cursor.next(function(error, doc) {
+ * console.log(doc.foo);
+ * });
+ *
+ * @param {Function} fn
+ * @return {QueryCursor}
+ * @api public
+ * @method map
+ */
+
+QueryCursor.prototype.map = function(fn) {
+ this._transforms.push(fn);
+ return this;
+};
+
+/*!
+ * Marks this cursor as errored
+ */
+
+QueryCursor.prototype._markError = function(error) {
+ this._error = error;
+ return this;
+};
+
+/**
+ * Marks this cursor as closed. Will stop streaming and subsequent calls to
+ * `next()` will error.
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ * @api public
+ * @method close
+ * @emits close
+ * @see MongoDB driver cursor#close http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#close
+ */
+
+QueryCursor.prototype.close = function(callback) {
+ return utils.promiseOrCallback(callback, cb => {
+ this.cursor.close(error => {
+ if (error) {
+ cb(error);
+ return this.listeners('error').length > 0 && this.emit('error', error);
+ }
+ this.emit('close');
+ cb(null);
+ });
+ }, this.model.events);
+};
+
+/**
+ * Get the next document from this cursor. Will return `null` when there are
+ * no documents left.
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ * @api public
+ * @method next
+ */
+
+QueryCursor.prototype.next = function(callback) {
+ return utils.promiseOrCallback(callback, cb => {
+ _next(this, function(error, doc) {
+ if (error) {
+ return cb(error);
+ }
+ cb(null, doc);
+ });
+ }, this.model.events);
+};
+
+/**
+ * Execute `fn` for every document in the cursor. If `fn` returns a promise,
+ * will wait for the promise to resolve before iterating on to the next one.
+ * Returns a promise that resolves when done.
+ *
+ * @param {Function} fn
+ * @param {Object} [options]
+ * @param {Number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.
+ * @param {Function} [callback] executed when all docs have been processed
+ * @return {Promise}
+ * @api public
+ * @method eachAsync
+ */
+
+QueryCursor.prototype.eachAsync = function(fn, opts, callback) {
+ const _this = this;
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ }
+ opts = opts || {};
+
+ return eachAsync(function(cb) { return _next(_this, cb); }, fn, opts, callback);
+};
+
+/**
+ * Adds a [cursor flag](http://mongodb.github.io/node-mongodb-native/2.2/api/Cursor.html#addCursorFlag).
+ * Useful for setting the `noCursorTimeout` and `tailable` flags.
+ *
+ * @param {String} flag
+ * @param {Boolean} value
+ * @return {AggregationCursor} this
+ * @api public
+ * @method addCursorFlag
+ */
+
+QueryCursor.prototype.addCursorFlag = function(flag, value) {
+ const _this = this;
+ _waitForCursor(this, function() {
+ _this.cursor.addCursorFlag(flag, value);
+ });
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+QueryCursor.prototype.transformNull = function(val) {
+ if (arguments.length === 0) {
+ val = true;
+ }
+ this._mongooseOptions.transformNull = val;
+ return this;
+};
+
+/*!
+ * Get the next doc from the underlying cursor and mongooseify it
+ * (populate, etc.)
+ */
+
+function _next(ctx, cb) {
+ let callback = cb;
+ if (ctx._transforms.length) {
+ callback = function(err, doc) {
+ if (err || (doc === null && !ctx._mongooseOptions.transformNull)) {
+ return cb(err, doc);
+ }
+ cb(err, ctx._transforms.reduce(function(doc, fn) {
+ return fn.call(ctx, doc);
+ }, doc));
+ };
+ }
+
+ if (ctx._error) {
+ return process.nextTick(function() {
+ callback(ctx._error);
+ });
+ }
+
+ if (ctx.cursor) {
+ return ctx.cursor.next(function(error, doc) {
+ if (error) {
+ return callback(error);
+ }
+ if (!doc) {
+ return callback(null, null);
+ }
+
+ const opts = ctx.query._mongooseOptions;
+ if (!opts.populate) {
+ return opts.lean ?
+ callback(null, doc) :
+ _create(ctx, doc, null, callback);
+ }
+
+ const pop = helpers.preparePopulationOptionsMQ(ctx.query,
+ ctx.query._mongooseOptions);
+ pop.__noPromise = true;
+ ctx.query.model.populate(doc, pop, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+ return opts.lean ?
+ callback(null, doc) :
+ _create(ctx, doc, pop, callback);
+ });
+ });
+ } else {
+ ctx.once('cursor', function() {
+ _next(ctx, cb);
+ });
+ }
+}
+
+/*!
+ * ignore
+ */
+
+function _waitForCursor(ctx, cb) {
+ if (ctx.cursor) {
+ return cb();
+ }
+ ctx.once('cursor', function() {
+ cb();
+ });
+}
+
+/*!
+ * Convert a raw doc into a full mongoose doc.
+ */
+
+function _create(ctx, doc, populatedIds, cb) {
+ const instance = helpers.createModel(ctx.query.model, doc, ctx.query._fields);
+ const opts = populatedIds ?
+ { populated: populatedIds } :
+ undefined;
+
+ instance.init(doc, opts, function(err) {
+ if (err) {
+ return cb(err);
+ }
+ cb(null, instance);
+ });
+}
+
+module.exports = QueryCursor;
diff --git a/node_modules/mongoose/lib/document.js b/node_modules/mongoose/lib/document.js
new file mode 100644
index 0000000..d12322d
--- /dev/null
+++ b/node_modules/mongoose/lib/document.js
@@ -0,0 +1,3170 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const EventEmitter = require('events').EventEmitter;
+const InternalCache = require('./internal');
+const MongooseError = require('./error');
+const MixedSchema = require('./schema/mixed');
+const ObjectExpectedError = require('./error/objectExpected');
+const ObjectParameterError = require('./error/objectParameter');
+const StrictModeError = require('./error/strict');
+const ValidatorError = require('./schematype').ValidatorError;
+const VirtualType = require('./virtualtype');
+const cleanModifiedSubpaths = require('./helpers/document/cleanModifiedSubpaths');
+const compile = require('./helpers/document/compile').compile;
+const defineKey = require('./helpers/document/compile').defineKey;
+const flatten = require('./helpers/common').flatten;
+const get = require('./helpers/get');
+const getEmbeddedDiscriminatorPath = require('./helpers/document/getEmbeddedDiscriminatorPath');
+const idGetter = require('./plugins/idGetter');
+const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
+const isExclusive = require('./helpers/projection/isExclusive');
+const inspect = require('util').inspect;
+const internalToObjectOptions = require('./options').internalToObjectOptions;
+const mpath = require('mpath');
+const utils = require('./utils');
+
+const ValidationError = MongooseError.ValidationError;
+const clone = utils.clone;
+const deepEqual = utils.deepEqual;
+const isMongooseObject = utils.isMongooseObject;
+
+const documentArrayParent = require('./helpers/symbols').documentArrayParent;
+const getSymbol = require('./helpers/symbols').getSymbol;
+
+let DocumentArray;
+let MongooseArray;
+let Embedded;
+
+const specialProperties = utils.specialProperties;
+
+/**
+ * The core Mongoose document constructor. You should not call this directly,
+ * the Mongoose [Model constructor](./api.html#Model) calls this for you.
+ *
+ * @param {Object} obj the values to set
+ * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
+ * @param {Boolean} [skipId] bool, should we auto create an ObjectId _id
+ * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
+ * @event `init`: Emitted on a document after it has was retreived from the db and fully hydrated by Mongoose.
+ * @event `save`: Emitted when the document is successfully saved
+ * @api private
+ */
+
+function Document(obj, fields, skipId, options) {
+ if (typeof skipId === 'object' && skipId != null) {
+ options = skipId;
+ skipId = options.skipId;
+ }
+ options = options || {};
+
+ this.$__ = new InternalCache;
+ this.$__.emitter = new EventEmitter();
+ this.isNew = 'isNew' in options ? options.isNew : true;
+ this.errors = undefined;
+ this.$__.$options = options || {};
+
+ if (obj != null && typeof obj !== 'object') {
+ throw new ObjectParameterError(obj, 'obj', 'Document');
+ }
+
+ const schema = this.schema;
+
+ if (typeof fields === 'boolean') {
+ this.$__.strictMode = fields;
+ fields = undefined;
+ } else {
+ this.$__.strictMode = schema.options.strict;
+ this.$__.selected = fields;
+ }
+
+ const required = schema.requiredPaths(true);
+ for (let i = 0; i < required.length; ++i) {
+ this.$__.activePaths.require(required[i]);
+ }
+
+ this.$__.emitter.setMaxListeners(0);
+
+ let exclude = null;
+
+ // determine if this doc is a result of a query with
+ // excluded fields
+ if (fields && utils.getFunctionName(fields.constructor) === 'Object') {
+ exclude = isExclusive(fields);
+ }
+
+ const hasIncludedChildren = exclude === false && fields ?
+ $__hasIncludedChildren(fields) :
+ {};
+
+ this.$__buildDoc(obj, fields, skipId, exclude, hasIncludedChildren, false);
+
+ // By default, defaults get applied **before** setting initial values
+ // Re: gh-6155
+ $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, true, {
+ isNew: this.isNew
+ });
+
+ if (obj) {
+ if (obj instanceof Document) {
+ this.isNew = obj.isNew;
+ }
+ // Skip set hooks
+ if (this.$__original_set) {
+ this.$__original_set(obj, undefined, true);
+ } else {
+ this.$set(obj, undefined, true);
+ }
+ }
+
+ // Function defaults get applied **after** setting initial values so they
+ // see the full doc rather than an empty one, unless they opt out.
+ // Re: gh-3781, gh-6155
+ if (options.willInit) {
+ this.once('init', () => {
+ $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, false, options.skipDefaults, {
+ isNew: this.isNew
+ });
+ });
+ } else {
+ $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, false, options.skipDefaults, {
+ isNew: this.isNew
+ });
+ }
+
+ this.$__._id = this._id;
+
+ if (!schema.options.strict && obj) {
+ const _this = this;
+ const keys = Object.keys(this._doc);
+
+ keys.forEach(function(key) {
+ if (!(key in schema.tree)) {
+ defineKey(key, null, _this);
+ }
+ });
+ }
+
+ applyQueue(this);
+}
+
+/*!
+ * Document exposes the NodeJS event emitter API, so you can use
+ * `on`, `once`, etc.
+ */
+utils.each(
+ ['on', 'once', 'emit', 'listeners', 'removeListener', 'setMaxListeners',
+ 'removeAllListeners', 'addListener'],
+ function(emitterFn) {
+ Document.prototype[emitterFn] = function() {
+ return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments);
+ };
+ });
+
+Document.prototype.constructor = Document;
+
+/**
+ * The documents schema.
+ *
+ * @api public
+ * @property schema
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.schema;
+
+/**
+ * Boolean flag specifying if the document is new.
+ *
+ * @api public
+ * @property isNew
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.isNew;
+
+/**
+ * The string version of this documents _id.
+ *
+ * ####Note:
+ *
+ * This getter exists on all documents by default. The getter can be disabled by setting the `id` [option](/docs/guide.html#id) of its `Schema` to false at construction time.
+ *
+ * new Schema({ name: String }, { id: false });
+ *
+ * @api public
+ * @see Schema options /docs/guide.html#options
+ * @property id
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.id;
+
+/**
+ * Hash containing current validation errors.
+ *
+ * @api public
+ * @property errors
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.errors;
+
+/*!
+ * ignore
+ */
+
+function $__hasIncludedChildren(fields) {
+ const hasIncludedChildren = {};
+ const keys = Object.keys(fields);
+ for (let j = 0; j < keys.length; ++j) {
+ const parts = keys[j].split('.');
+ const c = [];
+ for (let k = 0; k < parts.length; ++k) {
+ c.push(parts[k]);
+ hasIncludedChildren[c.join('.')] = 1;
+ }
+ }
+
+ return hasIncludedChildren;
+}
+
+/*!
+ * ignore
+ */
+
+function $__applyDefaults(doc, fields, skipId, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip) {
+ const paths = Object.keys(doc.schema.paths);
+ const plen = paths.length;
+
+ for (let i = 0; i < plen; ++i) {
+ let def;
+ let curPath = '';
+ const p = paths[i];
+
+ if (p === '_id' && skipId) {
+ continue;
+ }
+
+ const type = doc.schema.paths[p];
+ const path = p.split('.');
+ const len = path.length;
+ let included = false;
+ let doc_ = doc._doc;
+
+ for (let j = 0; j < len; ++j) {
+ if (doc_ == null) {
+ break;
+ }
+
+ const piece = path[j];
+ curPath += (!curPath.length ? '' : '.') + piece;
+
+ if (exclude === true) {
+ if (curPath in fields) {
+ break;
+ }
+ } else if (exclude === false && fields && !included) {
+ if (curPath in fields) {
+ included = true;
+ } else if (!hasIncludedChildren[curPath]) {
+ break;
+ }
+ }
+
+ if (j === len - 1) {
+ if (doc_[piece] !== void 0) {
+ break;
+ }
+
+ if (typeof type.defaultValue === 'function') {
+ if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) {
+ break;
+ }
+ if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) {
+ break;
+ }
+ } else if (!isBeforeSetters) {
+ // Non-function defaults should always run **before** setters
+ continue;
+ }
+
+ if (pathsToSkip && pathsToSkip[curPath]) {
+ break;
+ }
+
+ if (fields && exclude !== null) {
+ if (exclude === true) {
+ // apply defaults to all non-excluded fields
+ if (p in fields) {
+ continue;
+ }
+
+ def = type.getDefault(doc, false);
+ if (typeof def !== 'undefined') {
+ doc_[piece] = def;
+ doc.$__.activePaths.default(p);
+ }
+ } else if (included) {
+ // selected field
+ def = type.getDefault(doc, false);
+ if (typeof def !== 'undefined') {
+ doc_[piece] = def;
+ doc.$__.activePaths.default(p);
+ }
+ }
+ } else {
+ def = type.getDefault(doc, false);
+ if (typeof def !== 'undefined') {
+ doc_[piece] = def;
+ doc.$__.activePaths.default(p);
+ }
+ }
+ } else {
+ doc_ = doc_[piece];
+ }
+ }
+ }
+}
+
+/**
+ * Builds the default doc structure
+ *
+ * @param {Object} obj
+ * @param {Object} [fields]
+ * @param {Boolean} [skipId]
+ * @api private
+ * @method $__buildDoc
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__buildDoc = function(obj, fields, skipId, exclude, hasIncludedChildren) {
+ const doc = {};
+
+ const paths = Object.keys(this.schema.paths).
+ // Don't build up any paths that are underneath a map, we don't know
+ // what the keys will be
+ filter(p => !p.includes('$*'));
+ const plen = paths.length;
+ let ii = 0;
+
+ for (; ii < plen; ++ii) {
+ const p = paths[ii];
+
+ if (p === '_id') {
+ if (skipId) {
+ continue;
+ }
+ if (obj && '_id' in obj) {
+ continue;
+ }
+ }
+
+ const path = p.split('.');
+ const len = path.length;
+ const last = len - 1;
+ let curPath = '';
+ let doc_ = doc;
+ let included = false;
+
+ for (let i = 0; i < len; ++i) {
+ const piece = path[i];
+
+ curPath += (!curPath.length ? '' : '.') + piece;
+
+ // support excluding intermediary levels
+ if (exclude === true) {
+ if (curPath in fields) {
+ break;
+ }
+ } else if (exclude === false && fields && !included) {
+ if (curPath in fields) {
+ included = true;
+ } else if (!hasIncludedChildren[curPath]) {
+ break;
+ }
+ }
+
+ if (i < last) {
+ doc_ = doc_[piece] || (doc_[piece] = {});
+ }
+ }
+ }
+
+ this._doc = doc;
+};
+
+/*!
+ * Converts to POJO when you use the document for querying
+ */
+
+Document.prototype.toBSON = function() {
+ return this.toObject(internalToObjectOptions);
+};
+
+/**
+ * Initializes the document without setters or marking anything modified.
+ *
+ * Called internally after a document is returned from mongodb. Normally,
+ * you do **not** need to call this function on your own.
+ *
+ * This function triggers `init` [middleware](/docs/middleware.html).
+ * Note that `init` hooks are [synchronous](/docs/middleware.html#synchronous).
+ *
+ * @param {Object} doc document returned by mongo
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.init = function(doc, opts, fn) {
+ if (typeof opts === 'function') {
+ fn = opts;
+ opts = null;
+ }
+
+ this.$__init(doc, opts);
+
+ if (fn) {
+ fn(null, this);
+ }
+
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+Document.prototype.$__init = function(doc, opts) {
+ this.isNew = false;
+ this.$init = true;
+
+ // handle docs with populated paths
+ // If doc._id is not null or undefined
+ if (doc._id !== null && doc._id !== undefined &&
+ opts && opts.populated && opts.populated.length) {
+ const id = String(doc._id);
+ for (let i = 0; i < opts.populated.length; ++i) {
+ const item = opts.populated[i];
+ if (item.isVirtual) {
+ this.populated(item.path, utils.getValue(item.path, doc), item);
+ } else {
+ this.populated(item.path, item._docs[id], item);
+ }
+ }
+ }
+
+ init(this, doc, this._doc);
+
+ this.emit('init', this);
+ this.constructor.emit('init', this);
+
+ this.$__._id = this._id;
+
+ return this;
+};
+
+/*!
+ * Init helper.
+ *
+ * @param {Object} self document instance
+ * @param {Object} obj raw mongodb doc
+ * @param {Object} doc object we are initializing
+ * @api private
+ */
+
+function init(self, obj, doc, prefix) {
+ prefix = prefix || '';
+
+ const keys = Object.keys(obj);
+ const len = keys.length;
+ let schema;
+ let path;
+ let i;
+ let index = 0;
+
+ while (index < len) {
+ _init(index++);
+ }
+
+ function _init(index) {
+ i = keys[index];
+ path = prefix + i;
+ schema = self.schema.path(path);
+
+ // Should still work if not a model-level discriminator, but should not be
+ // necessary. This is *only* to catch the case where we queried using the
+ // base model and the discriminated model has a projection
+ if (self.schema.$isRootDiscriminator && !self.isSelected(path)) {
+ return;
+ }
+
+ if (!schema && utils.isObject(obj[i]) &&
+ (!obj[i].constructor || utils.getFunctionName(obj[i].constructor) === 'Object')) {
+ // assume nested object
+ if (!doc[i]) {
+ doc[i] = {};
+ }
+ init(self, obj[i], doc[i], path + '.');
+ } else if (!schema) {
+ doc[i] = obj[i];
+ } else {
+ if (obj[i] === null) {
+ doc[i] = null;
+ } else if (obj[i] !== undefined) {
+ const intCache = obj[i].$__ || {};
+ const wasPopulated = intCache.wasPopulated || null;
+ if (schema && !wasPopulated) {
+ try {
+ doc[i] = schema.cast(obj[i], self, true);
+ } catch (e) {
+ self.invalidate(e.path, new ValidatorError({
+ path: e.path,
+ message: e.message,
+ type: 'cast',
+ value: e.value
+ }));
+ }
+ } else {
+ doc[i] = obj[i];
+ }
+ }
+ // mark as hydrated
+ if (!self.isModified(path)) {
+ self.$__.activePaths.init(path);
+ }
+ }
+ }
+}
+
+/**
+ * Sends an update command with this document `_id` as the query selector.
+ *
+ * ####Example:
+ *
+ * weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
+ *
+ * ####Valid options:
+ *
+ * - same as in [Model.update](#model_Model.update)
+ *
+ * @see Model.update #model_Model.update
+ * @param {Object} doc
+ * @param {Object} options
+ * @param {Function} callback
+ * @return {Query}
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.update = function update() {
+ const args = utils.args(arguments);
+ args.unshift({_id: this._id});
+ return this.constructor.update.apply(this.constructor, args);
+};
+
+/**
+ * Sends an updateOne command with this document `_id` as the query selector.
+ *
+ * ####Example:
+ *
+ * weirdCar.updateOne({$inc: {wheels:1}}, { w: 1 }, callback);
+ *
+ * ####Valid options:
+ *
+ * - same as in [Model.updateOne](#model_Model.updateOne)
+ *
+ * @see Model.updateOne #model_Model.updateOne
+ * @param {Object} doc
+ * @param {Object} options
+ * @param {Function} callback
+ * @return {Query}
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.updateOne = function updateOne(doc, options, callback) {
+ const query = this.constructor.updateOne({_id: this._id}, doc, options);
+ query._pre(cb => {
+ this.constructor._middleware.execPre('updateOne', this, [], cb);
+ });
+ query._post(cb => {
+ this.constructor._middleware.execPost('updateOne', this, [], {}, cb);
+ });
+
+ if (callback != null) {
+ return query.exec(callback);
+ }
+
+ return query;
+};
+
+/**
+ * Sends a replaceOne command with this document `_id` as the query selector.
+ *
+ * ####Valid options:
+ *
+ * - same as in [Model.replaceOne](#model_Model.replaceOne)
+ *
+ * @see Model.replaceOne #model_Model.replaceOne
+ * @param {Object} doc
+ * @param {Object} options
+ * @param {Function} callback
+ * @return {Query}
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.replaceOne = function replaceOne() {
+ const args = utils.args(arguments);
+ args.unshift({ _id: this._id });
+ return this.constructor.replaceOne.apply(this.constructor, args);
+};
+
+/**
+ * Getter/setter around the session associated with this document. Used to
+ * automatically set `session` if you `save()` a doc that you got from a
+ * query with an associated session.
+ *
+ * ####Example:
+ *
+ * const session = MyModel.startSession();
+ * const doc = await MyModel.findOne().session(session);
+ * doc.$session() === session; // true
+ * doc.$session(null);
+ * doc.$session() === null; // true
+ *
+ * If this is a top-level document, setting the session propagates to all child
+ * docs.
+ *
+ * @param {ClientSession} [session] overwrite the current session
+ * @return {ClientSession}
+ * @method $session
+ * @api public
+ * @memberOf Document
+ */
+
+Document.prototype.$session = function $session(session) {
+ if (arguments.length === 0) {
+ return this.$__.session;
+ }
+ this.$__.session = session;
+
+ if (!this.ownerDocument) {
+ const subdocs = this.$__getAllSubdocs();
+ for (const child of subdocs) {
+ child.$session(session);
+ }
+ }
+
+ return session;
+};
+
+/**
+ * Alias for `set()`, used internally to avoid conflicts
+ *
+ * @param {String|Object} path path or object of key/vals to set
+ * @param {Any} val the value to set
+ * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for "on-the-fly" attributes
+ * @param {Object} [options] optionally specify options that modify the behavior of the set
+ * @method $set
+ * @name $set
+ * @memberOf Document
+ * @instance
+ * @api public
+ */
+
+Document.prototype.$set = function $set(path, val, type, options) {
+ if (type && utils.getFunctionName(type.constructor) === 'Object') {
+ options = type;
+ type = undefined;
+ }
+
+ options = options || {};
+ const merge = options.merge;
+ const adhoc = type && type !== true;
+ const constructing = type === true;
+ let adhocs;
+ let keys;
+ let i = 0;
+ let pathtype;
+ let key;
+ let prefix;
+
+ const strict = 'strict' in options
+ ? options.strict
+ : this.$__.strictMode;
+
+ if (adhoc) {
+ adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {});
+ adhocs[path] = this.schema.interpretAsType(path, type, this.schema.options);
+ }
+
+ if (typeof path !== 'string') {
+ // new Document({ key: val })
+ if (path === null || path === void 0) {
+ const _ = path;
+ path = val;
+ val = _;
+ } else {
+ prefix = val ? val + '.' : '';
+
+ if (path instanceof Document) {
+ if (path.$__isNested) {
+ path = path.toObject();
+ } else {
+ path = path._doc;
+ }
+ }
+
+ keys = Object.keys(path);
+ const len = keys.length;
+
+ if (len === 0 && !this.schema.options.minimize) {
+ if (val) {
+ this.$set(val, {});
+ }
+ return this;
+ }
+
+ while (i < len) {
+ _handleIndex.call(this, i++);
+ }
+
+ return this;
+ }
+ }
+
+ function _handleIndex(i) {
+ key = keys[i];
+ const pathName = prefix + key;
+ pathtype = this.schema.pathType(pathName);
+
+ // On initial set, delete any nested keys if we're going to overwrite
+ // them to ensure we keep the user's key order.
+ if (type === true &&
+ !prefix &&
+ path[key] != null &&
+ pathtype === 'nested' &&
+ this._doc[key] != null &&
+ Object.keys(this._doc[key]).length === 0) {
+ delete this._doc[key];
+ }
+
+ if (path[key] !== null &&
+ path[key] !== void 0 &&
+ // need to know if plain object - no Buffer, ObjectId, ref, etc
+ utils.isObject(path[key]) &&
+ (!path[key].constructor || utils.getFunctionName(path[key].constructor) === 'Object') &&
+ pathtype !== 'virtual' &&
+ pathtype !== 'real' &&
+ !(this.$__path(pathName) instanceof MixedSchema) &&
+ !(this.schema.paths[pathName] &&
+ this.schema.paths[pathName].options &&
+ this.schema.paths[pathName].options.ref)) {
+ this.$set(path[key], prefix + key, constructing);
+ } else if (strict) {
+ // Don't overwrite defaults with undefined keys (gh-3981)
+ if (constructing && path[key] === void 0 &&
+ this.get(key) !== void 0) {
+ return;
+ }
+
+ if (pathtype === 'adhocOrUndefined') {
+ pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true });
+ }
+
+ if (pathtype === 'real' || pathtype === 'virtual') {
+ // Check for setting single embedded schema to document (gh-3535)
+ let p = path[key];
+ if (this.schema.paths[pathName] &&
+ this.schema.paths[pathName].$isSingleNested &&
+ path[key] instanceof Document) {
+ p = p.toObject({ virtuals: false, transform: false });
+ }
+ this.$set(prefix + key, p, constructing);
+ } else if (pathtype === 'nested' && path[key] instanceof Document) {
+ this.$set(prefix + key,
+ path[key].toObject({transform: false}), constructing);
+ } else if (strict === 'throw') {
+ if (pathtype === 'nested') {
+ throw new ObjectExpectedError(key, path[key]);
+ } else {
+ throw new StrictModeError(key);
+ }
+ }
+ } else if (path[key] !== void 0) {
+ this.$set(prefix + key, path[key], constructing);
+ }
+ }
+
+ const pathType = this.schema.pathType(path);
+ if (pathType === 'nested' && val) {
+ if (utils.isObject(val) &&
+ (!val.constructor || utils.getFunctionName(val.constructor) === 'Object')) {
+ if (!merge) {
+ this.setValue(path, null);
+ cleanModifiedSubpaths(this, path);
+ } else {
+ return this.$set(val, path, constructing);
+ }
+
+ const keys = Object.keys(val);
+ this.setValue(path, {});
+ for (const key of keys) {
+ this.$set(path + '.' + key, val[key], constructing);
+ }
+ this.markModified(path);
+ cleanModifiedSubpaths(this, path, { skipDocArrays: true });
+ return this;
+ }
+ this.invalidate(path, new MongooseError.CastError('Object', val, path));
+ return this;
+ }
+
+ let schema;
+ const parts = path.split('.');
+
+ if (pathType === 'adhocOrUndefined' && strict) {
+ // check for roots that are Mixed types
+ let mixed;
+
+ for (i = 0; i < parts.length; ++i) {
+ const subpath = parts.slice(0, i + 1).join('.');
+
+ // If path is underneath a virtual, bypass everything and just set it.
+ if (i + 1 < parts.length && this.schema.pathType(subpath) === 'virtual') {
+ mpath.set(path, val, this);
+ return this;
+ }
+
+ schema = this.schema.path(subpath);
+ if (schema == null) {
+ continue;
+ }
+
+ if (schema instanceof MixedSchema) {
+ // allow changes to sub paths of mixed types
+ mixed = true;
+ break;
+ }
+ }
+
+ if (schema == null) {
+ // Check for embedded discriminators
+ schema = getEmbeddedDiscriminatorPath(this, path);
+ }
+
+ if (!mixed && !schema) {
+ if (strict === 'throw') {
+ throw new StrictModeError(path);
+ }
+ return this;
+ }
+ } else if (pathType === 'virtual') {
+ schema = this.schema.virtualpath(path);
+ schema.applySetters(val, this);
+ return this;
+ } else {
+ schema = this.$__path(path);
+ }
+
+ // gh-4578, if setting a deeply nested path that doesn't exist yet, create it
+ let cur = this._doc;
+ let curPath = '';
+ for (i = 0; i < parts.length - 1; ++i) {
+ cur = cur[parts[i]];
+ curPath += (curPath.length > 0 ? '.' : '') + parts[i];
+ if (!cur) {
+ this.$set(curPath, {});
+ // Hack re: gh-5800. If nested field is not selected, it probably exists
+ // so `MongoError: cannot use the part (nested of nested.num) to
+ // traverse the element ({nested: null})` is not likely. If user gets
+ // that error, its their fault for now. We should reconsider disallowing
+ // modifying not selected paths for 6.x
+ if (!this.isSelected(curPath)) {
+ this.unmarkModified(curPath);
+ }
+ cur = this.getValue(curPath);
+ }
+ }
+
+ let pathToMark;
+
+ // When using the $set operator the path to the field must already exist.
+ // Else mongodb throws: "LEFT_SUBFIELD only supports Object"
+
+ if (parts.length <= 1) {
+ pathToMark = path;
+ } else {
+ for (i = 0; i < parts.length; ++i) {
+ const subpath = parts.slice(0, i + 1).join('.');
+ if (this.get(subpath) === null) {
+ pathToMark = subpath;
+ break;
+ }
+ }
+
+ if (!pathToMark) {
+ pathToMark = path;
+ }
+ }
+
+ // if this doc is being constructed we should not trigger getters
+ const priorVal = (() => {
+ if (this.$__.$options.priorDoc != null) {
+ return this.$__.$options.priorDoc.getValue(path);
+ }
+ if (constructing) {
+ return void 0;
+ }
+ return this.getValue(path);
+ })();
+
+ if (!schema) {
+ this.$__set(pathToMark, path, constructing, parts, schema, val, priorVal);
+ return this;
+ }
+
+ let shouldSet = true;
+ try {
+ // If the user is trying to set a ref path to a document with
+ // the correct model name, treat it as populated
+ const refMatches = (() => {
+ if (schema.options == null) {
+ return false;
+ }
+ if (!(val instanceof Document)) {
+ return false;
+ }
+ const model = val.constructor;
+
+ // Check ref
+ const ref = schema.options.ref;
+ if (ref != null && (ref === model.modelName || ref === model.baseModelName)) {
+ return true;
+ }
+
+ // Check refPath
+ const refPath = schema.options.refPath;
+ if (refPath == null) {
+ return false;
+ }
+ const modelName = val.get(refPath);
+ if (modelName === model.modelName || modelName === model.baseModelName) {
+ return true;
+ }
+ return false;
+ })();
+
+ let didPopulate = false;
+ if (refMatches && val instanceof Document) {
+ if (this.ownerDocument) {
+ this.ownerDocument().populated(this.$__fullPath(path),
+ val._id, {model: val.constructor});
+ } else {
+ this.populated(path, val._id, {model: val.constructor});
+ }
+ didPopulate = true;
+ }
+
+ let popOpts;
+ if (schema.options &&
+ Array.isArray(schema.options[this.schema.options.typeKey]) &&
+ schema.options[this.schema.options.typeKey].length &&
+ schema.options[this.schema.options.typeKey][0].ref &&
+ Array.isArray(val) &&
+ val.length > 0 &&
+ val[0] instanceof Document &&
+ val[0].constructor.modelName &&
+ (schema.options[this.schema.options.typeKey][0].ref === val[0].constructor.baseModelName || schema.options[this.schema.options.typeKey][0].ref === val[0].constructor.modelName)) {
+ if (this.ownerDocument) {
+ popOpts = { model: val[0].constructor };
+ this.ownerDocument().populated(this.$__fullPath(path),
+ val.map(function(v) { return v._id; }), popOpts);
+ } else {
+ popOpts = { model: val[0].constructor };
+ this.populated(path, val.map(function(v) { return v._id; }), popOpts);
+ }
+ didPopulate = true;
+ }
+
+ // If this path is underneath a single nested schema, we'll call the setter
+ // later in `$__set()` because we don't take `_doc` when we iterate through
+ // a single nested doc. That's to make sure we get the correct context.
+ // Otherwise we would double-call the setter, see gh-7196.
+ if (this.schema.singleNestedPaths[path] == null) {
+ const setterContext = constructing && this.$__.$options.priorDoc ?
+ this.$__.$options.priorDoc :
+ this;
+ val = schema.applySetters(val, setterContext, false, priorVal);
+ }
+
+ if (!didPopulate && this.$__.populated) {
+ delete this.$__.populated[path];
+ }
+
+ this.$markValid(path);
+ } catch (e) {
+ this.invalidate(path,
+ new MongooseError.CastError(schema.instance, val, path, e));
+ shouldSet = false;
+ }
+
+ if (shouldSet) {
+ this.$__set(pathToMark, path, constructing, parts, schema, val, priorVal);
+ }
+
+ if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) {
+ cleanModifiedSubpaths(this, path);
+ }
+
+ return this;
+};
+
+/**
+ * Sets the value of a path, or many paths.
+ *
+ * ####Example:
+ *
+ * // path, value
+ * doc.set(path, value)
+ *
+ * // object
+ * doc.set({
+ * path : value
+ * , path2 : {
+ * path : value
+ * }
+ * })
+ *
+ * // on-the-fly cast to number
+ * doc.set(path, value, Number)
+ *
+ * // on-the-fly cast to string
+ * doc.set(path, value, String)
+ *
+ * // changing strict mode behavior
+ * doc.set(path, value, { strict: false });
+ *
+ * @param {String|Object} path path or object of key/vals to set
+ * @param {Any} val the value to set
+ * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for "on-the-fly" attributes
+ * @param {Object} [options] optionally specify options that modify the behavior of the set
+ * @api public
+ * @method set
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.set = Document.prototype.$set;
+
+/**
+ * Determine if we should mark this change as modified.
+ *
+ * @return {Boolean}
+ * @api private
+ * @method $__shouldModify
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__shouldModify = function(pathToMark, path, constructing, parts, schema, val, priorVal) {
+ if (this.isNew) {
+ return true;
+ }
+
+ // Re: the note about gh-7196, `val` is the raw value without casting or
+ // setters if the full path is under a single nested subdoc because we don't
+ // want to double run setters. So don't set it as modified. See gh-7264.
+ if (this.schema.singleNestedPaths[path] != null) {
+ return false;
+ }
+
+ if (val === void 0 && !this.isSelected(path)) {
+ // when a path is not selected in a query, its initial
+ // value will be undefined.
+ return true;
+ }
+
+ if (val === void 0 && path in this.$__.activePaths.states.default) {
+ // we're just unsetting the default value which was never saved
+ return false;
+ }
+
+ // gh-3992: if setting a populated field to a doc, don't mark modified
+ // if they have the same _id
+ if (this.populated(path) &&
+ val instanceof Document &&
+ deepEqual(val._id, priorVal)) {
+ return false;
+ }
+
+ if (!deepEqual(val, priorVal || this.get(path))) {
+ return true;
+ }
+
+ if (!constructing &&
+ val !== null &&
+ val !== undefined &&
+ path in this.$__.activePaths.states.default &&
+ deepEqual(val, schema.getDefault(this, constructing))) {
+ // a path with a default was $unset on the server
+ // and the user is setting it to the same value again
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Handles the actual setting of the value and marking the path modified if appropriate.
+ *
+ * @api private
+ * @method $__set
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__set = function(pathToMark, path, constructing, parts, schema, val, priorVal) {
+ Embedded = Embedded || require('./types/embedded');
+
+ const shouldModify = this.$__shouldModify(pathToMark, path, constructing, parts,
+ schema, val, priorVal);
+ const _this = this;
+
+ if (shouldModify) {
+ this.markModified(pathToMark);
+
+ // handle directly setting arrays (gh-1126)
+ MongooseArray || (MongooseArray = require('./types/array'));
+ if (val && val.isMongooseArray) {
+ val._registerAtomic('$set', val);
+
+ // Update embedded document parent references (gh-5189)
+ if (val.isMongooseDocumentArray) {
+ val.forEach(function(item) {
+ item && item.__parentArray && (item.__parentArray = val);
+ });
+ }
+
+ // Small hack for gh-1638: if we're overwriting the entire array, ignore
+ // paths that were modified before the array overwrite
+ this.$__.activePaths.forEach(function(modifiedPath) {
+ if (modifiedPath.indexOf(path + '.') === 0) {
+ _this.$__.activePaths.ignore(modifiedPath);
+ }
+ });
+ }
+ }
+
+ let obj = this._doc;
+ let i = 0;
+ const l = parts.length;
+ let cur = '';
+
+ for (; i < l; i++) {
+ const next = i + 1;
+ const last = next === l;
+ cur += (cur ? '.' + parts[i] : parts[i]);
+ if (specialProperties.has(parts[i])) {
+ return;
+ }
+
+ if (last) {
+ if (obj instanceof Map) {
+ obj.set(parts[i], val);
+ } else {
+ obj[parts[i]] = val;
+ }
+ } else {
+ if (obj[parts[i]] && utils.getFunctionName(obj[parts[i]].constructor) === 'Object') {
+ obj = obj[parts[i]];
+ } else if (obj[parts[i]] && obj[parts[i]] instanceof Embedded) {
+ obj = obj[parts[i]];
+ } else if (obj[parts[i]] && obj[parts[i]].$isSingleNested) {
+ obj = obj[parts[i]];
+ } else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
+ obj = obj[parts[i]];
+ } else {
+ obj[parts[i]] = obj[parts[i]] || {};
+ obj = obj[parts[i]];
+ }
+ }
+ }
+};
+
+/**
+ * Gets a raw value from a path (no getters)
+ *
+ * @param {String} path
+ * @api private
+ */
+
+Document.prototype.getValue = function(path) {
+ return utils.getValue(path, this._doc);
+};
+
+/**
+ * Sets a raw value for a path (no casting, setters, transformations)
+ *
+ * @param {String} path
+ * @param {Object} value
+ * @api private
+ */
+
+Document.prototype.setValue = function(path, val) {
+ utils.setValue(path, val, this._doc);
+ return this;
+};
+
+/**
+ * Returns the value of a path.
+ *
+ * ####Example
+ *
+ * // path
+ * doc.get('age') // 47
+ *
+ * // dynamic casting to a string
+ * doc.get('age', String) // "47"
+ *
+ * @param {String} path
+ * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for on-the-fly attributes
+ * @api public
+ */
+
+Document.prototype.get = function(path, type, options) {
+ let adhoc;
+ options = options || {};
+ if (type) {
+ adhoc = this.schema.interpretAsType(path, type, this.schema.options);
+ }
+
+ const schema = this.$__path(path) || this.schema.virtualpath(path);
+ const pieces = path.split('.');
+ let obj = this._doc;
+
+ if (schema instanceof VirtualType) {
+ if (schema.getters.length === 0) {
+ return void 0;
+ }
+ return schema.applyGetters(null, this);
+ }
+
+ for (let i = 0, l = pieces.length; i < l; i++) {
+ if (obj && obj._doc) {
+ obj = obj._doc;
+ }
+
+ if (obj == null) {
+ obj = void 0;
+ } else if (obj instanceof Map) {
+ obj = obj.get(pieces[i]);
+ } else if (i === l - 1) {
+ obj = utils.getValue(pieces[i], obj);
+ } else {
+ obj = obj[pieces[i]];
+ }
+ }
+
+ if (adhoc) {
+ obj = adhoc.cast(obj);
+ }
+
+ if (schema != null) {
+ obj = schema.applyGetters(obj, this);
+ } else if (this.schema.nested[path] && options.virtuals) {
+ // Might need to apply virtuals if this is a nested path
+ return applyGetters(this, utils.clone(obj), 'virtuals', { path: path });
+ }
+
+ return obj;
+};
+
+/*!
+ * ignore
+ */
+
+Document.prototype[getSymbol] = Document.prototype.get;
+
+/**
+ * Returns the schematype for the given `path`.
+ *
+ * @param {String} path
+ * @api private
+ * @method $__path
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__path = function(path) {
+ const adhocs = this.$__.adhocPaths;
+ const adhocType = adhocs && adhocs.hasOwnProperty(path) ? adhocs[path] : null;
+
+ if (adhocType) {
+ return adhocType;
+ }
+ return this.schema.path(path);
+};
+
+/**
+ * Marks the path as having pending changes to write to the db.
+ *
+ * _Very helpful when using [Mixed](./schematypes.html#mixed) types._
+ *
+ * ####Example:
+ *
+ * doc.mixed.type = 'changed';
+ * doc.markModified('mixed.type');
+ * doc.save() // changes to mixed.type are now persisted
+ *
+ * @param {String} path the path to mark modified
+ * @param {Document} [scope] the scope to run validators with
+ * @api public
+ */
+
+Document.prototype.markModified = function(path, scope) {
+ this.$__.activePaths.modify(path);
+ if (scope != null && !this.ownerDocument) {
+ this.$__.pathsToScopes[path] = scope;
+ }
+};
+
+/**
+ * Clears the modified state on the specified path.
+ *
+ * ####Example:
+ *
+ * doc.foo = 'bar';
+ * doc.unmarkModified('foo');
+ * doc.save(); // changes to foo will not be persisted
+ *
+ * @param {String} path the path to unmark modified
+ * @api public
+ */
+
+Document.prototype.unmarkModified = function(path) {
+ this.$__.activePaths.init(path);
+ delete this.$__.pathsToScopes[path];
+};
+
+/**
+ * Don't run validation on this path or persist changes to this path.
+ *
+ * ####Example:
+ *
+ * doc.foo = null;
+ * doc.$ignore('foo');
+ * doc.save(); // changes to foo will not be persisted and validators won't be run
+ *
+ * @memberOf Document
+ * @instance
+ * @method $ignore
+ * @param {String} path the path to ignore
+ * @api public
+ */
+
+Document.prototype.$ignore = function(path) {
+ this.$__.activePaths.ignore(path);
+};
+
+/**
+ * Returns the list of paths that have been modified.
+ *
+ * @param {Object} [options]
+ * @param {Boolean} [options.includeChildren=false] if true, returns children of modified paths as well. For example, if false, the list of modified paths for `doc.colors = { primary: 'blue' };` will **not** contain `colors.primary`. If true, `modifiedPaths()` will return an array that contains `colors.primary`.
+ * @return {Array}
+ * @api public
+ */
+
+Document.prototype.modifiedPaths = function(options) {
+ options = options || {};
+ const directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
+ const _this = this;
+ return directModifiedPaths.reduce(function(list, path) {
+ const parts = path.split('.');
+ list = list.concat(parts.reduce(function(chains, part, i) {
+ return chains.concat(parts.slice(0, i).concat(part).join('.'));
+ }, []).filter(function(chain) {
+ return (list.indexOf(chain) === -1);
+ }));
+
+ if (!options.includeChildren) {
+ return list;
+ }
+
+ let cur = _this.get(path);
+ if (cur != null && typeof cur === 'object') {
+ if (cur._doc) {
+ cur = cur._doc;
+ }
+ if (Array.isArray(cur)) {
+ const len = cur.length;
+ for (let i = 0; i < len; ++i) {
+ if (list.indexOf(path + '.' + i) === -1) {
+ list.push(path + '.' + i);
+ if (cur[i] != null && cur[i].$__) {
+ const modified = cur[i].modifiedPaths();
+ for (const childPath of modified) {
+ list.push(path + '.' + i + '.' + childPath);
+ }
+ }
+ }
+ }
+ } else {
+ Object.keys(cur).
+ filter(function(key) {
+ return list.indexOf(path + '.' + key) === -1;
+ }).
+ forEach(function(key) {
+ list.push(path + '.' + key);
+ });
+ }
+ }
+
+ return list;
+ }, []);
+};
+
+/**
+ * Returns true if this document was modified, else false.
+ *
+ * If `path` is given, checks if a path or any full path containing `path` as part of its path chain has been modified.
+ *
+ * ####Example
+ *
+ * doc.set('documents.0.title', 'changed');
+ * doc.isModified() // true
+ * doc.isModified('documents') // true
+ * doc.isModified('documents.0.title') // true
+ * doc.isModified('documents otherProp') // true
+ * doc.isDirectModified('documents') // false
+ *
+ * @param {String} [path] optional
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.isModified = function(paths, modifiedPaths) {
+ if (paths) {
+ if (!Array.isArray(paths)) {
+ paths = paths.split(' ');
+ }
+ const modified = modifiedPaths || this.modifiedPaths();
+ const directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
+ const isModifiedChild = paths.some(function(path) {
+ return !!~modified.indexOf(path);
+ });
+ return isModifiedChild || paths.some(function(path) {
+ return directModifiedPaths.some(function(mod) {
+ return mod === path || path.indexOf(mod + '.') === 0;
+ });
+ });
+ }
+ return this.$__.activePaths.some('modify');
+};
+
+/**
+ * Checks if a path is set to its default.
+ *
+ * ####Example
+ *
+ * MyModel = mongoose.model('test', { name: { type: String, default: 'Val '} });
+ * var m = new MyModel();
+ * m.$isDefault('name'); // true
+ *
+ * @memberOf Document
+ * @instance
+ * @method $isDefault
+ * @param {String} [path]
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.$isDefault = function(path) {
+ return (path in this.$__.activePaths.states.default);
+};
+
+/**
+ * Getter/setter, determines whether the document was removed or not.
+ *
+ * ####Example:
+ * product.remove(function (err, product) {
+ * product.isDeleted(); // true
+ * product.remove(); // no-op, doesn't send anything to the db
+ *
+ * product.isDeleted(false);
+ * product.isDeleted(); // false
+ * product.remove(); // will execute a remove against the db
+ * })
+ *
+ * @param {Boolean} [val] optional, overrides whether mongoose thinks the doc is deleted
+ * @return {Boolean} whether mongoose thinks this doc is deleted.
+ * @method $isDeleted
+ * @memberOf Document
+ * @instance
+ * @api public
+ */
+
+Document.prototype.$isDeleted = function(val) {
+ if (arguments.length === 0) {
+ return !!this.$__.isDeleted;
+ }
+
+ this.$__.isDeleted = !!val;
+ return this;
+};
+
+/**
+ * Returns true if `path` was directly set and modified, else false.
+ *
+ * ####Example
+ *
+ * doc.set('documents.0.title', 'changed');
+ * doc.isDirectModified('documents.0.title') // true
+ * doc.isDirectModified('documents') // false
+ *
+ * @param {String} path
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.isDirectModified = function(path) {
+ return (path in this.$__.activePaths.states.modify);
+};
+
+/**
+ * Checks if `path` was initialized.
+ *
+ * @param {String} path
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.isInit = function(path) {
+ return (path in this.$__.activePaths.states.init);
+};
+
+/**
+ * Checks if `path` was selected in the source query which initialized this document.
+ *
+ * ####Example
+ *
+ * Thing.findOne().select('name').exec(function (err, doc) {
+ * doc.isSelected('name') // true
+ * doc.isSelected('age') // false
+ * })
+ *
+ * @param {String} path
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.isSelected = function isSelected(path) {
+ if (this.$__.selected) {
+ if (path === '_id') {
+ return this.$__.selected._id !== 0;
+ }
+
+ const paths = Object.keys(this.$__.selected);
+ let i = paths.length;
+ let inclusive = null;
+ let cur;
+
+ if (i === 1 && paths[0] === '_id') {
+ // only _id was selected.
+ return this.$__.selected._id === 0;
+ }
+
+ while (i--) {
+ cur = paths[i];
+ if (cur === '_id') {
+ continue;
+ }
+ if (!isDefiningProjection(this.$__.selected[cur])) {
+ continue;
+ }
+ inclusive = !!this.$__.selected[cur];
+ break;
+ }
+
+ if (inclusive === null) {
+ return true;
+ }
+
+ if (path in this.$__.selected) {
+ return inclusive;
+ }
+
+ i = paths.length;
+ const pathDot = path + '.';
+
+ while (i--) {
+ cur = paths[i];
+ if (cur === '_id') {
+ continue;
+ }
+
+ if (cur.indexOf(pathDot) === 0) {
+ return inclusive || cur !== pathDot;
+ }
+
+ if (pathDot.indexOf(cur + '.') === 0) {
+ return inclusive;
+ }
+ }
+
+ return !inclusive;
+ }
+
+ return true;
+};
+
+/**
+ * Checks if `path` was explicitly selected. If no projection, always returns
+ * true.
+ *
+ * ####Example
+ *
+ * Thing.findOne().select('nested.name').exec(function (err, doc) {
+ * doc.isDirectSelected('nested.name') // true
+ * doc.isDirectSelected('nested.otherName') // false
+ * doc.isDirectSelected('nested') // false
+ * })
+ *
+ * @param {String} path
+ * @return {Boolean}
+ * @api public
+ */
+
+Document.prototype.isDirectSelected = function isDirectSelected(path) {
+ if (this.$__.selected) {
+ if (path === '_id') {
+ return this.$__.selected._id !== 0;
+ }
+
+ const paths = Object.keys(this.$__.selected);
+ let i = paths.length;
+ let inclusive = null;
+ let cur;
+
+ if (i === 1 && paths[0] === '_id') {
+ // only _id was selected.
+ return this.$__.selected._id === 0;
+ }
+
+ while (i--) {
+ cur = paths[i];
+ if (cur === '_id') {
+ continue;
+ }
+ if (!isDefiningProjection(this.$__.selected[cur])) {
+ continue;
+ }
+ inclusive = !!this.$__.selected[cur];
+ break;
+ }
+
+ if (inclusive === null) {
+ return true;
+ }
+
+ if (path in this.$__.selected) {
+ return inclusive;
+ }
+
+ return !inclusive;
+ }
+
+ return true;
+};
+
+/**
+ * Executes registered validation rules for this document.
+ *
+ * ####Note:
+ *
+ * This method is called `pre` save and if a validation rule is violated, [save](#model_Model-save) is aborted and the error is returned to your `callback`.
+ *
+ * ####Example:
+ *
+ * doc.validate(function (err) {
+ * if (err) handleError(err);
+ * else // validation passed
+ * });
+ *
+ * @param {Object} optional options internal options
+ * @param {Function} callback optional callback called after validation completes, passing an error if one occurred
+ * @return {Promise} Promise
+ * @api public
+ */
+
+Document.prototype.validate = function(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ return utils.promiseOrCallback(callback, cb => this.$__validate(function(error) {
+ cb(error);
+ }), this.constructor.events);
+};
+
+/*!
+ * ignore
+ */
+
+function _evaluateRequiredFunctions(doc) {
+ Object.keys(doc.$__.activePaths.states.require).forEach(path => {
+ const p = doc.schema.path(path);
+
+ if (p != null && typeof p.originalRequiredValue === 'function') {
+ doc.$__.cachedRequired[path] = p.originalRequiredValue.call(doc);
+ }
+ });
+}
+
+/*!
+ * ignore
+ */
+
+function _getPathsToValidate(doc) {
+ let i;
+ let len;
+ const skipSchemaValidators = {};
+
+ _evaluateRequiredFunctions(doc);
+
+ // only validate required fields when necessary
+ let paths = Object.keys(doc.$__.activePaths.states.require).filter(function(path) {
+ if (!doc.isSelected(path) && !doc.isModified(path)) {
+ return false;
+ }
+ if (path in doc.$__.cachedRequired) {
+ return doc.$__.cachedRequired[path];
+ }
+ return true;
+ });
+
+ paths = paths.concat(Object.keys(doc.$__.activePaths.states.init));
+ paths = paths.concat(Object.keys(doc.$__.activePaths.states.modify));
+ paths = paths.concat(Object.keys(doc.$__.activePaths.states.default));
+
+ if (!doc.ownerDocument) {
+ const subdocs = doc.$__getAllSubdocs();
+ let subdoc;
+ len = subdocs.length;
+ const modifiedPaths = doc.modifiedPaths();
+ for (i = 0; i < len; ++i) {
+ subdoc = subdocs[i];
+ if (doc.isModified(subdoc.$basePath, modifiedPaths) &&
+ !doc.isDirectModified(subdoc.$basePath)) {
+ // Remove child paths for now, because we'll be validating the whole
+ // subdoc
+ paths = paths.filter(function(p) {
+ return p != null && p.indexOf(subdoc.$basePath + '.') !== 0;
+ });
+ paths.push(subdoc.$basePath);
+ skipSchemaValidators[subdoc.$basePath] = true;
+ }
+ }
+ }
+
+ // gh-661: if a whole array is modified, make sure to run validation on all
+ // the children as well
+ len = paths.length;
+ for (i = 0; i < len; ++i) {
+ const path = paths[i];
+
+ const _pathType = doc.schema.path(path);
+ if (!_pathType ||
+ !_pathType.$isMongooseArray ||
+ // To avoid potential performance issues, skip doc arrays whose children
+ // are not required. `getPositionalPathType()` may be slow, so avoid
+ // it unless we have a case of #6364
+ (_pathType.$isMongooseDocumentArray && !get(_pathType, 'schemaOptions.required'))) {
+ continue;
+ }
+
+ const val = doc.getValue(path);
+ if (val) {
+ const numElements = val.length;
+ for (let j = 0; j < numElements; ++j) {
+ paths.push(path + '.' + j);
+ }
+ }
+ }
+
+ const flattenOptions = { skipArrays: true };
+ len = paths.length;
+ for (i = 0; i < len; ++i) {
+ const pathToCheck = paths[i];
+ if (doc.schema.nested[pathToCheck]) {
+ let _v = doc.getValue(pathToCheck);
+ if (isMongooseObject(_v)) {
+ _v = _v.toObject({ transform: false });
+ }
+ const flat = flatten(_v, '', flattenOptions);
+ const _subpaths = Object.keys(flat).map(function(p) {
+ return pathToCheck + '.' + p;
+ });
+ paths = paths.concat(_subpaths);
+ }
+ }
+
+ len = paths.length;
+ for (i = 0; i < len; ++i) {
+ const path = paths[i];
+ const _pathType = doc.schema.path(path);
+ if (!_pathType || !_pathType.$isSchemaMap) {
+ continue;
+ }
+
+ const val = doc.getValue(path);
+ if (val == null) {
+ continue;
+ }
+ for (const key of val.keys()) {
+ paths.push(path + '.' + key);
+ }
+ }
+
+ return [paths, skipSchemaValidators];
+}
+
+/*!
+ * ignore
+ */
+
+Document.prototype.$__validate = function(callback) {
+ const _this = this;
+ const _complete = () => {
+ const err = this.$__.validationError;
+ this.$__.validationError = undefined;
+ this.$__.cachedRequired = {};
+ this.emit('validate', _this);
+ this.constructor.emit('validate', _this);
+ if (err) {
+ for (const key in err.errors) {
+ // Make sure cast errors persist
+ if (!this[documentArrayParent] && err.errors[key] instanceof MongooseError.CastError) {
+ this.invalidate(key, err.errors[key]);
+ }
+ }
+
+ return err;
+ }
+ };
+
+ // only validate required fields when necessary
+ const pathDetails = _getPathsToValidate(this);
+ const paths = pathDetails[0];
+ const skipSchemaValidators = pathDetails[1];
+
+ if (paths.length === 0) {
+ return process.nextTick(function() {
+ const error = _complete();
+ if (error) {
+ return _this.schema.s.hooks.execPost('validate:error', _this, [ _this], { error: error }, function(error) {
+ callback(error);
+ });
+ }
+ callback(null, _this);
+ });
+ }
+
+ const validated = {};
+ let total = 0;
+
+ const complete = function() {
+ const error = _complete();
+ if (error) {
+ return _this.schema.s.hooks.execPost('validate:error', _this, [ _this], { error: error }, function(error) {
+ callback(error);
+ });
+ }
+ callback(null, _this);
+ };
+
+ const validatePath = function(path) {
+ if (path == null || validated[path]) {
+ return;
+ }
+
+ validated[path] = true;
+ total++;
+
+ process.nextTick(function() {
+ const p = _this.schema.path(path);
+
+ if (!p) {
+ return --total || complete();
+ }
+
+ // If user marked as invalid or there was a cast error, don't validate
+ if (!_this.$isValid(path)) {
+ --total || complete();
+ return;
+ }
+
+ const val = _this.getValue(path);
+ const scope = path in _this.$__.pathsToScopes ?
+ _this.$__.pathsToScopes[path] :
+ _this;
+
+ p.doValidate(val, function(err) {
+ if (err && (!p.$isMongooseDocumentArray || err.$isArrayValidatorError)) {
+ if (p.$isSingleNested &&
+ err.name === 'ValidationError' &&
+ p.schema.options.storeSubdocValidationError === false) {
+ return --total || complete();
+ }
+ _this.invalidate(path, err, undefined, true);
+ }
+ --total || complete();
+ }, scope, { skipSchemaValidators: skipSchemaValidators[path] });
+ });
+ };
+
+ const numPaths = paths.length;
+ for (let i = 0; i < numPaths; ++i) {
+ validatePath(paths[i]);
+ }
+};
+
+/**
+ * Executes registered validation rules (skipping asynchronous validators) for this document.
+ *
+ * ####Note:
+ *
+ * This method is useful if you need synchronous validation.
+ *
+ * ####Example:
+ *
+ * var err = doc.validateSync();
+ * if ( err ){
+ * handleError( err );
+ * } else {
+ * // validation passed
+ * }
+ *
+ * @param {Array|string} pathsToValidate only validate the given paths
+ * @return {MongooseError|undefined} MongooseError if there are errors during validation, or undefined if there is no error.
+ * @api public
+ */
+
+Document.prototype.validateSync = function(pathsToValidate) {
+ const _this = this;
+
+ if (typeof pathsToValidate === 'string') {
+ pathsToValidate = pathsToValidate.split(' ');
+ }
+
+ // only validate required fields when necessary
+ const pathDetails = _getPathsToValidate(this);
+ let paths = pathDetails[0];
+ const skipSchemaValidators = pathDetails[1];
+
+ if (pathsToValidate && pathsToValidate.length) {
+ const tmp = [];
+ for (let i = 0; i < paths.length; ++i) {
+ if (pathsToValidate.indexOf(paths[i]) !== -1) {
+ tmp.push(paths[i]);
+ }
+ }
+ paths = tmp;
+ }
+
+ const validating = {};
+
+ paths.forEach(function(path) {
+ if (validating[path]) {
+ return;
+ }
+
+ validating[path] = true;
+
+ const p = _this.schema.path(path);
+ if (!p) {
+ return;
+ }
+ if (!_this.$isValid(path)) {
+ return;
+ }
+
+ const val = _this.getValue(path);
+ const err = p.doValidateSync(val, _this, {
+ skipSchemaValidators: skipSchemaValidators[path]
+ });
+ if (err && (!p.$isMongooseDocumentArray || err.$isArrayValidatorError)) {
+ if (p.$isSingleNested &&
+ err.name === 'ValidationError' &&
+ p.schema.options.storeSubdocValidationError === false) {
+ return;
+ }
+ _this.invalidate(path, err, undefined, true);
+ }
+ });
+
+ const err = _this.$__.validationError;
+ _this.$__.validationError = undefined;
+ _this.emit('validate', _this);
+ _this.constructor.emit('validate', _this);
+
+ if (err) {
+ for (const key in err.errors) {
+ // Make sure cast errors persist
+ if (err.errors[key] instanceof MongooseError.CastError) {
+ _this.invalidate(key, err.errors[key]);
+ }
+ }
+ }
+
+ return err;
+};
+
+/**
+ * Marks a path as invalid, causing validation to fail.
+ *
+ * The `errorMsg` argument will become the message of the `ValidationError`.
+ *
+ * The `value` argument (if passed) will be available through the `ValidationError.value` property.
+ *
+ * doc.invalidate('size', 'must be less than 20', 14);
+
+ * doc.validate(function (err) {
+ * console.log(err)
+ * // prints
+ * { message: 'Validation failed',
+ * name: 'ValidationError',
+ * errors:
+ * { size:
+ * { message: 'must be less than 20',
+ * name: 'ValidatorError',
+ * path: 'size',
+ * type: 'user defined',
+ * value: 14 } } }
+ * })
+ *
+ * @param {String} path the field to invalidate
+ * @param {String|Error} errorMsg the error which states the reason `path` was invalid
+ * @param {Object|String|Number|any} value optional invalid value
+ * @param {String} [kind] optional `kind` property for the error
+ * @return {ValidationError} the current ValidationError, with all currently invalidated paths
+ * @api public
+ */
+
+Document.prototype.invalidate = function(path, err, val, kind) {
+ if (!this.$__.validationError) {
+ this.$__.validationError = new ValidationError(this);
+ }
+
+ if (this.$__.validationError.errors[path]) {
+ return;
+ }
+
+ if (!err || typeof err === 'string') {
+ err = new ValidatorError({
+ path: path,
+ message: err,
+ type: kind || 'user defined',
+ value: val
+ });
+ }
+
+ if (this.$__.validationError === err) {
+ return this.$__.validationError;
+ }
+
+ this.$__.validationError.addError(path, err);
+ return this.$__.validationError;
+};
+
+/**
+ * Marks a path as valid, removing existing validation errors.
+ *
+ * @param {String} path the field to mark as valid
+ * @api public
+ * @memberOf Document
+ * @instance
+ * @method $markValid
+ */
+
+Document.prototype.$markValid = function(path) {
+ if (!this.$__.validationError || !this.$__.validationError.errors[path]) {
+ return;
+ }
+
+ delete this.$__.validationError.errors[path];
+ if (Object.keys(this.$__.validationError.errors).length === 0) {
+ this.$__.validationError = null;
+ }
+};
+
+/**
+ * Saves this document.
+ *
+ * ####Example:
+ *
+ * product.sold = Date.now();
+ * product.save(function (err, product) {
+ * if (err) ..
+ * })
+ *
+ * The callback will receive two parameters
+ *
+ * 1. `err` if an error occurred
+ * 2. `product` which is the saved `product`
+ *
+ * As an extra measure of flow control, save will return a Promise.
+ * ####Example:
+ * product.save().then(function(product) {
+ * ...
+ * });
+ *
+ * @param {Object} [options] options optional options
+ * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe)
+ * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
+ * @param {Function} [fn] optional callback
+ * @method save
+ * @memberOf Document
+ * @instance
+ * @return {Promise|undefined} Returns undefined if used with callback or a Promise otherwise.
+ * @api public
+ * @see middleware http://mongoosejs.com/docs/middleware.html
+ */
+
+/**
+ * Checks if a path is invalid
+ *
+ * @param {String} path the field to check
+ * @method $isValid
+ * @memberOf Document
+ * @instance
+ * @api private
+ */
+
+Document.prototype.$isValid = function(path) {
+ return !this.$__.validationError || !this.$__.validationError.errors[path];
+};
+
+/**
+ * Resets the internal modified state of this document.
+ *
+ * @api private
+ * @return {Document}
+ * @method $__reset
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__reset = function reset() {
+ let _this = this;
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+
+ this.$__.activePaths
+ .map('init', 'modify', function(i) {
+ return _this.getValue(i);
+ })
+ .filter(function(val) {
+ return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
+ })
+ .forEach(function(array) {
+ let i = array.length;
+ while (i--) {
+ const doc = array[i];
+ if (!doc) {
+ continue;
+ }
+ doc.$__reset();
+ }
+
+ _this.$__.activePaths.init(array._path);
+
+ array._atomics = {};
+ });
+
+ this.$__.activePaths.
+ map('init', 'modify', function(i) {
+ return _this.getValue(i);
+ }).
+ filter(function(val) {
+ return val && val.$isSingleNested;
+ }).
+ forEach(function(doc) {
+ doc.$__reset();
+ _this.$__.activePaths.init(doc.$basePath);
+ });
+
+ // clear atomics
+ this.$__dirty().forEach(function(dirt) {
+ const type = dirt.value;
+
+ if (type && type._atomics) {
+ type._atomics = {};
+ }
+ });
+
+ // Clear 'dirty' cache
+ this.$__.activePaths.clear('modify');
+ this.$__.activePaths.clear('default');
+ this.$__.validationError = undefined;
+ this.errors = undefined;
+ _this = this;
+ this.schema.requiredPaths().forEach(function(path) {
+ _this.$__.activePaths.require(path);
+ });
+
+ return this;
+};
+
+/**
+ * Returns this documents dirty paths / vals.
+ *
+ * @api private
+ * @method $__dirty
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__dirty = function() {
+ const _this = this;
+
+ let all = this.$__.activePaths.map('modify', function(path) {
+ return {
+ path: path,
+ value: _this.getValue(path),
+ schema: _this.$__path(path)
+ };
+ });
+
+ // gh-2558: if we had to set a default and the value is not undefined,
+ // we have to save as well
+ all = all.concat(this.$__.activePaths.map('default', function(path) {
+ if (path === '_id' || _this.getValue(path) == null) {
+ return;
+ }
+ return {
+ path: path,
+ value: _this.getValue(path),
+ schema: _this.$__path(path)
+ };
+ }));
+
+ // Sort dirty paths in a flat hierarchy.
+ all.sort(function(a, b) {
+ return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0));
+ });
+
+ // Ignore "foo.a" if "foo" is dirty already.
+ const minimal = [];
+ let lastPath;
+ let top;
+
+ all.forEach(function(item) {
+ if (!item) {
+ return;
+ }
+ if (item.path.indexOf(lastPath) !== 0) {
+ lastPath = item.path + '.';
+ minimal.push(item);
+ top = item;
+ } else {
+ // special case for top level MongooseArrays
+ if (top.value && top.value._atomics && top.value.hasAtomics()) {
+ // the `top` array itself and a sub path of `top` are being modified.
+ // the only way to honor all of both modifications is through a $set
+ // of entire array.
+ top.value._atomics = {};
+ top.value._atomics.$set = top.value;
+ }
+ }
+ });
+
+ top = lastPath = null;
+ return minimal;
+};
+
+/**
+ * Assigns/compiles `schema` into this documents prototype.
+ *
+ * @param {Schema} schema
+ * @api private
+ * @method $__setSchema
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__setSchema = function(schema) {
+ schema.plugin(idGetter, { deduplicate: true });
+ compile(schema.tree, this, undefined, schema.options);
+
+ // Apply default getters if virtual doesn't have any (gh-6262)
+ for (const key of Object.keys(schema.virtuals)) {
+ schema.virtuals[key]._applyDefaultGetters();
+ }
+
+ this.schema = schema;
+};
+
+
+/**
+ * Get active path that were changed and are arrays
+ *
+ * @api private
+ * @method $__getArrayPathsToValidate
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__getArrayPathsToValidate = function() {
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+
+ // validate all document arrays.
+ return this.$__.activePaths
+ .map('init', 'modify', function(i) {
+ return this.getValue(i);
+ }.bind(this))
+ .filter(function(val) {
+ return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
+ }).reduce(function(seed, array) {
+ return seed.concat(array);
+ }, [])
+ .filter(function(doc) {
+ return doc;
+ });
+};
+
+
+/**
+ * Get all subdocs (by bfs)
+ *
+ * @api private
+ * @method $__getAllSubdocs
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__getAllSubdocs = function() {
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+ Embedded = Embedded || require('./types/embedded');
+
+ function docReducer(doc, seed, path) {
+ const val = path ? doc[path] : doc;
+ if (val instanceof Embedded) {
+ seed.push(val);
+ }
+ else if (val instanceof Map) {
+ seed = Array.from(val.keys()).reduce(function(seed, path) {
+ return docReducer(val.get(path), seed, null);
+ }, seed);
+ }
+ else if (val && val.$isSingleNested) {
+ seed = Object.keys(val._doc).reduce(function(seed, path) {
+ return docReducer(val._doc, seed, path);
+ }, seed);
+ seed.push(val);
+ }
+ else if (val && val.isMongooseDocumentArray) {
+ val.forEach(function _docReduce(doc) {
+ if (!doc || !doc._doc) {
+ return;
+ }
+ if (doc instanceof Embedded) {
+ seed.push(doc);
+ }
+ seed = Object.keys(doc._doc).reduce(function(seed, path) {
+ return docReducer(doc._doc, seed, path);
+ }, seed);
+ });
+ } else if (val instanceof Document && val.$__isNested) {
+ if (val) {
+ seed = Object.keys(val).reduce(function(seed, path) {
+ return docReducer(val, seed, path);
+ }, seed);
+ }
+ }
+ return seed;
+ }
+
+ const _this = this;
+ const subDocs = Object.keys(this._doc).reduce(function(seed, path) {
+ return docReducer(_this, seed, path);
+ }, []);
+
+ return subDocs;
+};
+
+/*!
+ * Runs queued functions
+ */
+
+function applyQueue(doc) {
+ const q = doc.schema && doc.schema.callQueue;
+ if (!q.length) {
+ return;
+ }
+ let pair;
+
+ for (let i = 0; i < q.length; ++i) {
+ pair = q[i];
+ if (pair[0] !== 'pre' && pair[0] !== 'post' && pair[0] !== 'on') {
+ doc[pair[0]].apply(doc, pair[1]);
+ }
+ }
+}
+
+/*!
+ * ignore
+ */
+
+Document.prototype.$__handleReject = function handleReject(err) {
+ // emit on the Model if listening
+ if (this.listeners('error').length) {
+ this.emit('error', err);
+ } else if (this.constructor.listeners && this.constructor.listeners('error').length) {
+ this.constructor.emit('error', err);
+ } else if (this.listeners && this.listeners('error').length) {
+ this.emit('error', err);
+ }
+};
+
+/**
+ * Internal helper for toObject() and toJSON() that doesn't manipulate options
+ *
+ * @api private
+ * @method $toObject
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$toObject = function(options, json) {
+ let defaultOptions = {
+ transform: true,
+ flattenDecimals: true
+ };
+
+ const path = json ? 'toJSON' : 'toObject';
+ const baseOptions = get(this, 'constructor.base.options.' + path, {});
+ const schemaOptions = get(this, 'schema.options', {});
+ // merge base default options with Schema's set default options if available.
+ // `clone` is necessary here because `utils.options` directly modifies the second input.
+ defaultOptions = utils.options(defaultOptions, clone(baseOptions));
+ defaultOptions = utils.options(defaultOptions, clone(schemaOptions[path] || {}));
+
+ // If options do not exist or is not an object, set it to empty object
+ options = options && utils.getFunctionName(options.constructor) === 'Object' ?
+ clone(options) :
+ {};
+
+ if (!('flattenMaps' in options)) {
+ options.flattenMaps = defaultOptions.flattenMaps;
+ }
+
+ let _minimize;
+ if (options.minimize != null) {
+ _minimize = options.minimize;
+ } else if (defaultOptions.minimize != null) {
+ _minimize = defaultOptions.minimize;
+ } else {
+ _minimize = schemaOptions.minimize;
+ }
+
+ // The original options that will be passed to `clone()`. Important because
+ // `clone()` will recursively call `$toObject()` on embedded docs, so we
+ // need the original options the user passed in, plus `_isNested` and
+ // `_parentOptions` for checking whether we need to depopulate.
+ const cloneOptions = Object.assign(utils.clone(options), {
+ _isNested: true,
+ json: json,
+ minimize: _minimize
+ });
+
+ const depopulate = options.depopulate ||
+ get(options, '_parentOptions.depopulate', false);
+ // _isNested will only be true if this is not the top level document, we
+ // should never depopulate
+ if (depopulate && options._isNested && this.$__.wasPopulated) {
+ // populated paths that we set to a document
+ return clone(this._id, cloneOptions);
+ }
+
+ // merge default options with input options.
+ options = utils.options(defaultOptions, options);
+ options._isNested = true;
+ options.json = json;
+ options.minimize = _minimize;
+
+ cloneOptions._parentOptions = options;
+
+ // remember the root transform function
+ // to save it from being overwritten by sub-transform functions
+ const originalTransform = options.transform;
+
+ let ret = clone(this._doc, cloneOptions) || {};
+
+ if (options.getters) {
+ applyGetters(this, ret, 'paths', cloneOptions);
+ // applyGetters for paths will add nested empty objects;
+ // if minimize is set, we need to remove them.
+ if (options.minimize) {
+ ret = minimize(ret) || {};
+ }
+ }
+
+ if (options.virtuals || options.getters && options.virtuals !== false) {
+ applyGetters(this, ret, 'virtuals', cloneOptions);
+ }
+
+ if (options.versionKey === false && this.schema.options.versionKey) {
+ delete ret[this.schema.options.versionKey];
+ }
+
+ let transform = options.transform;
+
+ // In the case where a subdocument has its own transform function, we need to
+ // check and see if the parent has a transform (options.transform) and if the
+ // child schema has a transform (this.schema.options.toObject) In this case,
+ // we need to adjust options.transform to be the child schema's transform and
+ // not the parent schema's
+ if (transform === true || (schemaOptions.toObject && transform)) {
+ const opts = options.json ? schemaOptions.toJSON : schemaOptions.toObject;
+
+ if (opts) {
+ transform = (typeof options.transform === 'function' ? options.transform : opts.transform);
+ }
+ } else {
+ options.transform = originalTransform;
+ }
+
+ if (typeof transform === 'function') {
+ const xformed = transform(this, ret, options);
+ if (typeof xformed !== 'undefined') {
+ ret = xformed;
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Converts this document into a plain javascript object, ready for storage in MongoDB.
+ *
+ * Buffers are converted to instances of [mongodb.Binary](http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
+ *
+ * ####Options:
+ *
+ * - `getters` apply all getters (path and virtual getters), defaults to false
+ * - `virtuals` apply virtual getters (can override `getters` option), defaults to false
+ * - `minimize` remove empty objects (defaults to true)
+ * - `transform` a transform function to apply to the resulting document before returning
+ * - `depopulate` depopulate any populated paths, replacing them with their original refs (defaults to false)
+ * - `versionKey` whether to include the version key (defaults to true)
+ *
+ * ####Getters/Virtuals
+ *
+ * Example of only applying path getters
+ *
+ * doc.toObject({ getters: true, virtuals: false })
+ *
+ * Example of only applying virtual getters
+ *
+ * doc.toObject({ virtuals: true })
+ *
+ * Example of applying both path and virtual getters
+ *
+ * doc.toObject({ getters: true })
+ *
+ * To apply these options to every document of your schema by default, set your [schemas](#schema_Schema) `toObject` option to the same argument.
+ *
+ * schema.set('toObject', { virtuals: true })
+ *
+ * ####Transform
+ *
+ * We may need to perform a transformation of the resulting object based on some criteria, say to remove some sensitive information or return a custom object. In this case we set the optional `transform` function.
+ *
+ * Transform functions receive three arguments
+ *
+ * function (doc, ret, options) {}
+ *
+ * - `doc` The mongoose document which is being converted
+ * - `ret` The plain object representation which has been converted
+ * - `options` The options in use (either schema options or the options passed inline)
+ *
+ * ####Example
+ *
+ * // specify the transform schema option
+ * if (!schema.options.toObject) schema.options.toObject = {};
+ * schema.options.toObject.transform = function (doc, ret, options) {
+ * // remove the _id of every document before returning the result
+ * delete ret._id;
+ * return ret;
+ * }
+ *
+ * // without the transformation in the schema
+ * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' }
+ *
+ * // with the transformation
+ * doc.toObject(); // { name: 'Wreck-it Ralph' }
+ *
+ * With transformations we can do a lot more than remove properties. We can even return completely new customized objects:
+ *
+ * if (!schema.options.toObject) schema.options.toObject = {};
+ * schema.options.toObject.transform = function (doc, ret, options) {
+ * return { movie: ret.name }
+ * }
+ *
+ * // without the transformation in the schema
+ * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' }
+ *
+ * // with the transformation
+ * doc.toObject(); // { movie: 'Wreck-it Ralph' }
+ *
+ * _Note: if a transform function returns `undefined`, the return value will be ignored._
+ *
+ * Transformations may also be applied inline, overridding any transform set in the options:
+ *
+ * function xform (doc, ret, options) {
+ * return { inline: ret.name, custom: true }
+ * }
+ *
+ * // pass the transform as an inline option
+ * doc.toObject({ transform: xform }); // { inline: 'Wreck-it Ralph', custom: true }
+ *
+ * If you want to skip transformations, use `transform: false`:
+ *
+ * if (!schema.options.toObject) schema.options.toObject = {};
+ * schema.options.toObject.hide = '_id';
+ * schema.options.toObject.transform = function (doc, ret, options) {
+ * if (options.hide) {
+ * options.hide.split(' ').forEach(function (prop) {
+ * delete ret[prop];
+ * });
+ * }
+ * return ret;
+ * }
+ *
+ * var doc = new Doc({ _id: 'anId', secret: 47, name: 'Wreck-it Ralph' });
+ * doc.toObject(); // { secret: 47, name: 'Wreck-it Ralph' }
+ * doc.toObject({ hide: 'secret _id', transform: false });// { _id: 'anId', secret: 47, name: 'Wreck-it Ralph' }
+ * doc.toObject({ hide: 'secret _id', transform: true }); // { name: 'Wreck-it Ralph' }
+ *
+ * Transforms are applied _only to the document and are not applied to sub-documents_.
+ *
+ * Transforms, like all of these options, are also available for `toJSON`.
+ *
+ * See [schema options](/docs/guide.html#toObject) for some more details.
+ *
+ * _During save, no custom options are applied to the document before being sent to the database._
+ *
+ * @param {Object} [options]
+ * @param {Boolean} [options.getters=false] if true, apply all getters, including virtuals
+ * @param {Boolean} [options.virtuals=false] if true, apply virtuals. Use `{ getters: true, virtuals: false }` to just apply getters, not virtuals
+ * @param {Boolean} [options.minimize=true] if true, omit any empty objects from the output
+ * @param {Function|null} [options.transform=null] if set, mongoose will call this function to allow you to transform the returned object
+ * @param {Boolean} [options.depopulate=false] if true, replace any conventionally populated paths with the original id in the output. Has no affect on virtual populated paths.
+ * @param {Boolean} [options.versionKey=true] if false, exclude the version key (`__v` by default) from the output
+ * @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
+ * @return {Object} js object
+ * @see mongodb.Binary http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.toObject = function(options) {
+ return this.$toObject(options);
+};
+
+/*!
+ * Minimizes an object, removing undefined values and empty objects
+ *
+ * @param {Object} object to minimize
+ * @return {Object}
+ */
+
+function minimize(obj) {
+ const keys = Object.keys(obj);
+ let i = keys.length;
+ let hasKeys;
+ let key;
+ let val;
+
+ while (i--) {
+ key = keys[i];
+ val = obj[key];
+
+ if (utils.isObject(val) && !Buffer.isBuffer(val)) {
+ obj[key] = minimize(val);
+ }
+
+ if (undefined === obj[key]) {
+ delete obj[key];
+ continue;
+ }
+
+ hasKeys = true;
+ }
+
+ return hasKeys
+ ? obj
+ : undefined;
+}
+
+/*!
+ * Applies virtuals properties to `json`.
+ *
+ * @param {Document} self
+ * @param {Object} json
+ * @param {String} type either `virtuals` or `paths`
+ * @return {Object} `json`
+ */
+
+function applyGetters(self, json, type, options) {
+ const schema = self.schema;
+ const paths = Object.keys(schema[type]);
+ let i = paths.length;
+ const numPaths = i;
+ let path;
+ let assignPath;
+ let cur = self._doc;
+ let v;
+
+ if (!cur) {
+ return json;
+ }
+
+ if (type === 'virtuals') {
+ options = options || {};
+ for (i = 0; i < numPaths; ++i) {
+ path = paths[i];
+ // We may be applying virtuals to a nested object, for example if calling
+ // `doc.nestedProp.toJSON()`. If so, the path we assign to, `assignPath`,
+ // will be a trailing substring of the `path`.
+ assignPath = path;
+ if (options.path != null) {
+ if (!path.startsWith(options.path + '.')) {
+ continue;
+ }
+ assignPath = path.substr(options.path.length + 1);
+ }
+ const parts = assignPath.split('.');
+ v = clone(self.get(path), options);
+ if (v === void 0) {
+ continue;
+ }
+ const plen = parts.length;
+ cur = json;
+ for (let j = 0; j < plen - 1; ++j) {
+ cur[parts[j]] = cur[parts[j]] || {};
+ cur = cur[parts[j]];
+ }
+ cur[parts[plen - 1]] = v;
+ }
+
+ return json;
+ }
+
+ while (i--) {
+ path = paths[i];
+
+ const parts = path.split('.');
+ const plen = parts.length;
+ const last = plen - 1;
+ let branch = json;
+ let part;
+ cur = self._doc;
+
+ for (let ii = 0; ii < plen; ++ii) {
+ part = parts[ii];
+ v = cur[part];
+ if (ii === last) {
+ branch[part] = clone(self.get(path), options);
+ } else if (v == null) {
+ if (part in cur) {
+ branch[part] = v;
+ }
+ break;
+ } else {
+ branch = branch[part] || (branch[part] = {});
+ }
+ cur = v;
+ }
+ }
+
+ return json;
+}
+
+/**
+ * The return value of this method is used in calls to JSON.stringify(doc).
+ *
+ * This method accepts the same options as [Document#toObject](#document_Document-toObject). To apply the options to every document of your schema by default, set your [schemas](#schema_Schema) `toJSON` option to the same argument.
+ *
+ * schema.set('toJSON', { virtuals: true })
+ *
+ * See [schema options](/docs/guide.html#toJSON) for details.
+ *
+ * @param {Object} options
+ * @return {Object}
+ * @see Document#toObject #document_Document-toObject
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.toJSON = function(options) {
+ return this.$toObject(options, true);
+};
+
+/**
+ * Helper for console.log
+ *
+ * @api public
+ * @method inspect
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.inspect = function(options) {
+ const isPOJO = options &&
+ utils.getFunctionName(options.constructor) === 'Object';
+ let opts;
+ if (isPOJO) {
+ opts = options;
+ opts.minimize = false;
+ }
+ return this.toObject(opts);
+};
+
+if (inspect.custom) {
+ /*!
+ * Avoid Node deprecation warning DEP0079
+ */
+
+ Document.prototype[inspect.custom] = Document.prototype.inspect;
+}
+
+/**
+ * Helper for console.log
+ *
+ * @api public
+ * @method toString
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.toString = function() {
+ return inspect(this.inspect());
+};
+
+/**
+ * Returns true if the Document stores the same data as doc.
+ *
+ * Documents are considered equal when they have matching `_id`s, unless neither
+ * document has an `_id`, in which case this function falls back to using
+ * `deepEqual()`.
+ *
+ * @param {Document} doc a document to compare
+ * @return {Boolean}
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.equals = function(doc) {
+ if (!doc) {
+ return false;
+ }
+
+ const tid = this.get('_id');
+ const docid = doc.get ? doc.get('_id') : doc;
+ if (!tid && !docid) {
+ return deepEqual(this, doc);
+ }
+ return tid && tid.equals
+ ? tid.equals(docid)
+ : tid === docid;
+};
+
+/**
+ * Populates document references, executing the `callback` when complete.
+ * If you want to use promises instead, use this function with
+ * [`execPopulate()`](#document_Document-execPopulate)
+ *
+ * ####Example:
+ *
+ * doc
+ * .populate('company')
+ * .populate({
+ * path: 'notes',
+ * match: /airline/,
+ * select: 'text',
+ * model: 'modelName'
+ * options: opts
+ * }, function (err, user) {
+ * assert(doc._id === user._id) // the document itself is passed
+ * })
+ *
+ * // summary
+ * doc.populate(path) // not executed
+ * doc.populate(options); // not executed
+ * doc.populate(path, callback) // executed
+ * doc.populate(options, callback); // executed
+ * doc.populate(callback); // executed
+ * doc.populate(options).execPopulate() // executed, returns promise
+ *
+ *
+ * ####NOTE:
+ *
+ * Population does not occur unless a `callback` is passed *or* you explicitly
+ * call `execPopulate()`.
+ * Passing the same path a second time will overwrite the previous path options.
+ * See [Model.populate()](#model_Model.populate) for explaination of options.
+ *
+ * @see Model.populate #model_Model.populate
+ * @see Document.execPopulate #document_Document-execPopulate
+ * @param {String|Object} [path] The path to populate or an options object
+ * @param {Function} [callback] When passed, population is invoked
+ * @api public
+ * @return {Document} this
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.populate = function populate() {
+ if (arguments.length === 0) {
+ return this;
+ }
+
+ const pop = this.$__.populate || (this.$__.populate = {});
+ const args = utils.args(arguments);
+ let fn;
+
+ if (typeof args[args.length - 1] === 'function') {
+ fn = args.pop();
+ }
+
+ // allow `doc.populate(callback)`
+ if (args.length) {
+ // use hash to remove duplicate paths
+ const res = utils.populate.apply(null, args);
+ for (let i = 0; i < res.length; ++i) {
+ pop[res[i].path] = res[i];
+ }
+ }
+
+ if (fn) {
+ const paths = utils.object.vals(pop);
+ this.$__.populate = undefined;
+ let topLevelModel = this.constructor;
+ if (this.$__isNested) {
+ topLevelModel = this.$__.scope.constructor;
+ const nestedPath = this.$__.nestedPath;
+ paths.forEach(function(populateOptions) {
+ populateOptions.path = nestedPath + '.' + populateOptions.path;
+ });
+ }
+
+ // Use `$session()` by default if the document has an associated session
+ // See gh-6754
+ if (this.$session() != null) {
+ const session = this.$session();
+ paths.forEach(path => {
+ if (path.options == null) {
+ path.options = { session: session };
+ return;
+ }
+ if (!('session' in path.options)) {
+ path.options.session = session;
+ }
+ });
+ }
+
+ topLevelModel.populate(this, paths, fn);
+ }
+
+ return this;
+};
+
+/**
+ * Explicitly executes population and returns a promise. Useful for ES2015
+ * integration.
+ *
+ * ####Example:
+ *
+ * var promise = doc.
+ * populate('company').
+ * populate({
+ * path: 'notes',
+ * match: /airline/,
+ * select: 'text',
+ * model: 'modelName'
+ * options: opts
+ * }).
+ * execPopulate();
+ *
+ * // summary
+ * doc.execPopulate().then(resolve, reject);
+ *
+ *
+ * @see Document.populate #document_Document-populate
+ * @api public
+ * @param {Function} [callback] optional callback. If specified, a promise will **not** be returned
+ * @return {Promise} promise that resolves to the document when population is done
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.execPopulate = function(callback) {
+ return utils.promiseOrCallback(callback, cb => {
+ this.populate(cb);
+ }, this.constructor.events);
+};
+
+/**
+ * Gets _id(s) used during population of the given `path`.
+ *
+ * ####Example:
+ *
+ * Model.findOne().populate('author').exec(function (err, doc) {
+ * console.log(doc.author.name) // Dr.Seuss
+ * console.log(doc.populated('author')) // '5144cf8050f071d979c118a7'
+ * })
+ *
+ * If the path was not populated, undefined is returned.
+ *
+ * @param {String} path
+ * @return {Array|ObjectId|Number|Buffer|String|undefined}
+ * @memberOf Document
+ * @instance
+ * @api public
+ */
+
+Document.prototype.populated = function(path, val, options) {
+ // val and options are internal
+ if (val === null || val === void 0) {
+ if (!this.$__.populated) {
+ return undefined;
+ }
+ const v = this.$__.populated[path];
+ if (v) {
+ return v.value;
+ }
+ return undefined;
+ }
+
+ // internal
+
+ if (val === true) {
+ if (!this.$__.populated) {
+ return undefined;
+ }
+ return this.$__.populated[path];
+ }
+
+ this.$__.populated || (this.$__.populated = {});
+ this.$__.populated[path] = {value: val, options: options};
+ return val;
+};
+
+/**
+ * Takes a populated field and returns it to its unpopulated state.
+ *
+ * ####Example:
+ *
+ * Model.findOne().populate('author').exec(function (err, doc) {
+ * console.log(doc.author.name); // Dr.Seuss
+ * console.log(doc.depopulate('author'));
+ * console.log(doc.author); // '5144cf8050f071d979c118a7'
+ * })
+ *
+ * If the path was not populated, this is a no-op.
+ *
+ * @param {String} path
+ * @return {Document} this
+ * @see Document.populate #document_Document-populate
+ * @api public
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.depopulate = function(path) {
+ if (typeof path === 'string') {
+ path = path.split(' ');
+ }
+ let i;
+ let populatedIds;
+ const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : [];
+
+ if (arguments.length === 0) {
+ // Depopulate all
+ const keys = this.$__.populated ? Object.keys(this.$__.populated) : [];
+
+ for (i = 0; i < virtualKeys.length; i++) {
+ delete this.$$populatedVirtuals[virtualKeys[i]];
+ delete this._doc[virtualKeys[i]];
+ }
+
+ for (i = 0; i < keys.length; i++) {
+ populatedIds = this.populated(keys[i]);
+ if (!populatedIds) {
+ continue;
+ }
+ delete this.$__.populated[keys[i]];
+ this.$set(keys[i], populatedIds);
+ }
+ return this;
+ }
+
+ for (i = 0; i < path.length; i++) {
+ populatedIds = this.populated(path[i]);
+ if (!populatedIds) {
+ if (virtualKeys.indexOf(path[i]) === -1) {
+ continue;
+ } else {
+ delete this.$$populatedVirtuals[path[i]];
+ delete this._doc[path[i]];
+ continue;
+ }
+ }
+ delete this.$__.populated[path[i]];
+ this.$set(path[i], populatedIds);
+ }
+ return this;
+};
+
+
+/**
+ * Returns the full path to this document.
+ *
+ * @param {String} [path]
+ * @return {String}
+ * @api private
+ * @method $__fullPath
+ * @memberOf Document
+ * @instance
+ */
+
+Document.prototype.$__fullPath = function(path) {
+ // overridden in SubDocuments
+ return path || '';
+};
+
+/*!
+ * Module exports.
+ */
+
+Document.ValidationError = ValidationError;
+module.exports = exports = Document;
diff --git a/node_modules/mongoose/lib/document_provider.js b/node_modules/mongoose/lib/document_provider.js
new file mode 100644
index 0000000..1ace61f
--- /dev/null
+++ b/node_modules/mongoose/lib/document_provider.js
@@ -0,0 +1,30 @@
+'use strict';
+
+/* eslint-env browser */
+
+/*!
+ * Module dependencies.
+ */
+const Document = require('./document.js');
+const BrowserDocument = require('./browserDocument.js');
+
+let isBrowser = false;
+
+/**
+ * Returns the Document constructor for the current context
+ *
+ * @api private
+ */
+module.exports = function() {
+ if (isBrowser) {
+ return BrowserDocument;
+ }
+ return Document;
+};
+
+/*!
+ * ignore
+ */
+module.exports.setBrowser = function(flag) {
+ isBrowser = flag;
+};
diff --git a/node_modules/mongoose/lib/driver.js b/node_modules/mongoose/lib/driver.js
new file mode 100644
index 0000000..cf7ca3d
--- /dev/null
+++ b/node_modules/mongoose/lib/driver.js
@@ -0,0 +1,15 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+let driver = null;
+
+module.exports.get = function() {
+ return driver;
+};
+
+module.exports.set = function(v) {
+ driver = v;
+};
diff --git a/node_modules/mongoose/lib/drivers/SPEC.md b/node_modules/mongoose/lib/drivers/SPEC.md
new file mode 100644
index 0000000..6464693
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/SPEC.md
@@ -0,0 +1,4 @@
+
+# Driver Spec
+
+TODO
diff --git a/node_modules/mongoose/lib/drivers/browser/ReadPreference.js b/node_modules/mongoose/lib/drivers/browser/ReadPreference.js
new file mode 100644
index 0000000..1363570
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/ReadPreference.js
@@ -0,0 +1,7 @@
+/*!
+ * ignore
+ */
+
+'use strict';
+
+module.exports = function() {};
diff --git a/node_modules/mongoose/lib/drivers/browser/binary.js b/node_modules/mongoose/lib/drivers/browser/binary.js
new file mode 100644
index 0000000..4658f7b
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/binary.js
@@ -0,0 +1,14 @@
+
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const Binary = require('bson').Binary;
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = Binary;
diff --git a/node_modules/mongoose/lib/drivers/browser/decimal128.js b/node_modules/mongoose/lib/drivers/browser/decimal128.js
new file mode 100644
index 0000000..5668182
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/decimal128.js
@@ -0,0 +1,7 @@
+/*!
+ * ignore
+ */
+
+'use strict';
+
+module.exports = require('bson').Decimal128;
diff --git a/node_modules/mongoose/lib/drivers/browser/index.js b/node_modules/mongoose/lib/drivers/browser/index.js
new file mode 100644
index 0000000..56d0b8a
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/index.js
@@ -0,0 +1,13 @@
+/*!
+ * Module exports.
+ */
+
+'use strict';
+
+exports.Binary = require('./binary');
+exports.Collection = function() {
+ throw new Error('Cannot create a collection from browser library');
+};
+exports.Decimal128 = require('./decimal128');
+exports.ObjectId = require('./objectid');
+exports.ReadPreference = require('./ReadPreference');
diff --git a/node_modules/mongoose/lib/drivers/browser/objectid.js b/node_modules/mongoose/lib/drivers/browser/objectid.js
new file mode 100644
index 0000000..b1e603d
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/objectid.js
@@ -0,0 +1,28 @@
+
+/*!
+ * [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) ObjectId
+ * @constructor NodeMongoDbObjectId
+ * @see ObjectId
+ */
+
+'use strict';
+
+const ObjectId = require('bson').ObjectID;
+
+/*!
+ * Getter for convenience with populate, see gh-6115
+ */
+
+Object.defineProperty(ObjectId.prototype, '_id', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
+ return this;
+ }
+});
+
+/*!
+ * ignore
+ */
+
+module.exports = exports = ObjectId;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/ReadPreference.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/ReadPreference.js
new file mode 100644
index 0000000..024ee18
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/ReadPreference.js
@@ -0,0 +1,47 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const mongodb = require('mongodb');
+const ReadPref = mongodb.ReadPreference;
+
+/*!
+ * Converts arguments to ReadPrefs the driver
+ * can understand.
+ *
+ * @param {String|Array} pref
+ * @param {Array} [tags]
+ */
+
+module.exports = function readPref(pref, tags) {
+ if (Array.isArray(pref)) {
+ tags = pref[1];
+ pref = pref[0];
+ }
+
+ if (pref instanceof ReadPref) {
+ return pref;
+ }
+
+ switch (pref) {
+ case 'p':
+ pref = 'primary';
+ break;
+ case 'pp':
+ pref = 'primaryPreferred';
+ break;
+ case 's':
+ pref = 'secondary';
+ break;
+ case 'sp':
+ pref = 'secondaryPreferred';
+ break;
+ case 'n':
+ pref = 'nearest';
+ break;
+ }
+
+ return new ReadPref(pref, tags);
+};
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js
new file mode 100644
index 0000000..4e3c86f
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js
@@ -0,0 +1,10 @@
+
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const Binary = require('mongodb').Binary;
+
+module.exports = exports = Binary;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
new file mode 100644
index 0000000..deeaa73
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
@@ -0,0 +1,332 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const MongooseCollection = require('../../collection');
+const Collection = require('mongodb').Collection;
+const get = require('../../helpers/get');
+const sliced = require('sliced');
+const stream = require('stream');
+const util = require('util');
+
+/**
+ * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
+ *
+ * All methods methods from the [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver are copied and wrapped in queue management.
+ *
+ * @inherits Collection
+ * @api private
+ */
+
+function NativeCollection() {
+ this.collection = null;
+ MongooseCollection.apply(this, arguments);
+}
+
+/*!
+ * Inherit from abstract Collection.
+ */
+
+NativeCollection.prototype.__proto__ = MongooseCollection.prototype;
+
+/**
+ * Called when the connection opens.
+ *
+ * @api private
+ */
+
+NativeCollection.prototype.onOpen = function() {
+ const _this = this;
+
+ // always get a new collection in case the user changed host:port
+ // of parent db instance when re-opening the connection.
+
+ if (!_this.opts.capped.size) {
+ // non-capped
+ callback(null, _this.conn.db.collection(_this.name));
+ return _this.collection;
+ }
+
+ // capped
+ return _this.conn.db.collection(_this.name, function(err, c) {
+ if (err) return callback(err);
+
+ // discover if this collection exists and if it is capped
+ _this.conn.db.listCollections({name: _this.name}).toArray(function(err, docs) {
+ if (err) {
+ return callback(err);
+ }
+ const doc = docs[0];
+ const exists = !!doc;
+
+ if (exists) {
+ if (doc.options && doc.options.capped) {
+ callback(null, c);
+ } else {
+ const msg = 'A non-capped collection exists with the name: ' + _this.name + '\n\n'
+ + ' To use this collection as a capped collection, please '
+ + 'first convert it.\n'
+ + ' http://www.mongodb.org/display/DOCS/Capped+Collections#CappedCollections-Convertingacollectiontocapped';
+ err = new Error(msg);
+ callback(err);
+ }
+ } else {
+ // create
+ const opts = Object.assign({}, _this.opts.capped);
+ opts.capped = true;
+ _this.conn.db.createCollection(_this.name, opts, callback);
+ }
+ });
+ });
+
+ function callback(err, collection) {
+ if (err) {
+ // likely a strict mode error
+ _this.conn.emit('error', err);
+ } else {
+ _this.collection = collection;
+ MongooseCollection.prototype.onOpen.call(_this);
+ }
+ }
+};
+
+/**
+ * Called when the connection closes
+ *
+ * @api private
+ */
+
+NativeCollection.prototype.onClose = function(force) {
+ MongooseCollection.prototype.onClose.call(this, force);
+};
+
+/*!
+ * ignore
+ */
+
+const syncCollectionMethods = { watch: true };
+
+/*!
+ * Copy the collection methods and make them subject to queues
+ */
+
+function iter(i) {
+ NativeCollection.prototype[i] = function() {
+ const collection = this.collection;
+ const args = arguments;
+ const _this = this;
+ const debug = _this.conn.base.options.debug;
+
+ // If user force closed, queueing will hang forever. See #5664
+ if (this.opts.$wasForceClosed) {
+ return this.conn.db.collection(this.name)[i].apply(collection, args);
+ }
+ if (this.buffer) {
+ if (syncCollectionMethods[i]) {
+ throw new Error('Collection method ' + i + ' is synchronous');
+ }
+ this.addQueue(i, arguments);
+ return;
+ }
+
+ if (debug) {
+ if (typeof debug === 'function') {
+ debug.apply(_this,
+ [_this.name, i].concat(sliced(args, 0, args.length - 1)));
+ } else if (debug instanceof stream.Writable) {
+ this.$printToStream(_this.name, i, args, debug);
+ } else {
+ this.$print(_this.name, i, args);
+ }
+ }
+
+ try {
+ return collection[i].apply(collection, args);
+ } catch (error) {
+ // Collection operation may throw because of max bson size, catch it here
+ // See gh-3906
+ if (args.length > 0 &&
+ typeof args[args.length - 1] === 'function') {
+ args[args.length - 1](error);
+ } else {
+ throw error;
+ }
+ }
+ };
+}
+
+for (const i in Collection.prototype) {
+ // Janky hack to work around gh-3005 until we can get rid of the mongoose
+ // collection abstraction
+ try {
+ if (typeof Collection.prototype[i] !== 'function') {
+ continue;
+ }
+ } catch (e) {
+ continue;
+ }
+
+ iter(i);
+}
+
+/**
+ * Debug print helper
+ *
+ * @api public
+ * @method $print
+ */
+
+NativeCollection.prototype.$print = function(name, i, args) {
+ const moduleName = '\x1B[0;36mMongoose:\x1B[0m ';
+ const functionCall = [name, i].join('.');
+ const _args = [];
+ for (let j = args.length - 1; j >= 0; --j) {
+ if (this.$format(args[j]) || _args.length) {
+ _args.unshift(this.$format(args[j]));
+ }
+ }
+ const params = '(' + _args.join(', ') + ')';
+
+ console.info(moduleName + functionCall + params);
+};
+
+/**
+ * Debug print helper
+ *
+ * @api public
+ * @method $print
+ */
+
+NativeCollection.prototype.$printToStream = function(name, i, args, stream) {
+ const functionCall = [name, i].join('.');
+ const _args = [];
+ for (let j = args.length - 1; j >= 0; --j) {
+ if (this.$format(args[j]) || _args.length) {
+ _args.unshift(this.$format(args[j]));
+ }
+ }
+ const params = '(' + _args.join(', ') + ')';
+
+ stream.write(functionCall + params, 'utf8');
+};
+
+/**
+ * Formatter for debug print args
+ *
+ * @api public
+ * @method $format
+ */
+
+NativeCollection.prototype.$format = function(arg) {
+ const type = typeof arg;
+ if (type === 'function' || type === 'undefined') return '';
+ return format(arg);
+};
+
+/*!
+ * Debug print helper
+ */
+
+function inspectable(representation) {
+ const ret = {
+ inspect: function() { return representation; },
+ };
+ if (util.inspect.custom) {
+ ret[util.inspect.custom] = ret.inspect;
+ }
+ return ret;
+}
+function map(o) {
+ return format(o, true);
+}
+function formatObjectId(x, key) {
+ x[key] = inspectable('ObjectId("' + x[key].toHexString() + '")');
+}
+function formatDate(x, key) {
+ x[key] = inspectable('new Date("' + x[key].toUTCString() + '")');
+}
+function format(obj, sub) {
+ if (obj && typeof obj.toBSON === 'function') {
+ obj = obj.toBSON();
+ }
+ if (obj == null) {
+ return obj;
+ }
+
+ let x = require('../../utils').clone(obj, {transform: false});
+
+ if (x.constructor.name === 'Binary') {
+ x = 'BinData(' + x.sub_type + ', "' + x.toString('base64') + '")';
+ } else if (x.constructor.name === 'ObjectID') {
+ x = inspectable('ObjectId("' + x.toHexString() + '")');
+ } else if (x.constructor.name === 'Date') {
+ x = inspectable('new Date("' + x.toUTCString() + '")');
+ } else if (x.constructor.name === 'Object') {
+ const keys = Object.keys(x);
+ const numKeys = keys.length;
+ let key;
+ for (let i = 0; i < numKeys; ++i) {
+ key = keys[i];
+ if (x[key]) {
+ let error;
+ if (typeof x[key].toBSON === 'function') {
+ try {
+ // `session.toBSON()` throws an error. This means we throw errors
+ // in debug mode when using transactions, see gh-6712. As a
+ // workaround, catch `toBSON()` errors, try to serialize without
+ // `toBSON()`, and rethrow if serialization still fails.
+ x[key] = x[key].toBSON();
+ } catch (_error) {
+ error = _error;
+ }
+ }
+ if (x[key].constructor.name === 'Binary') {
+ x[key] = 'BinData(' + x[key].sub_type + ', "' +
+ x[key].buffer.toString('base64') + '")';
+ } else if (x[key].constructor.name === 'Object') {
+ x[key] = format(x[key], true);
+ } else if (x[key].constructor.name === 'ObjectID') {
+ formatObjectId(x, key);
+ } else if (x[key].constructor.name === 'Date') {
+ formatDate(x, key);
+ } else if (x[key].constructor.name === 'ClientSession') {
+ x[key] = inspectable('ClientSession("' +
+ get(x[key], 'id.id.buffer', '').toString('hex') + '")');
+ } else if (Array.isArray(x[key])) {
+ x[key] = x[key].map(map);
+ } else if (error != null) {
+ // If there was an error with `toBSON()` and the object wasn't
+ // already converted to a string representation, rethrow it.
+ // Open to better ideas on how to handle this.
+ throw error;
+ }
+ }
+ }
+ }
+ if (sub) {
+ return x;
+ }
+
+ return util.
+ inspect(x, false, 10, true).
+ replace(/\n/g, '').
+ replace(/\s{2,}/g, ' ');
+}
+
+/**
+ * Retrieves information about this collections indexes.
+ *
+ * @param {Function} callback
+ * @method getIndexes
+ * @api public
+ */
+
+NativeCollection.prototype.getIndexes = NativeCollection.prototype.indexInformation;
+
+/*!
+ * Module exports.
+ */
+
+module.exports = NativeCollection;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js
new file mode 100644
index 0000000..9fd4b82
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js
@@ -0,0 +1,181 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseConnection = require('../../connection');
+const STATES = require('../../connectionstate');
+
+/**
+ * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
+ *
+ * @inherits Connection
+ * @api private
+ */
+
+function NativeConnection() {
+ MongooseConnection.apply(this, arguments);
+ this._listening = false;
+}
+
+/**
+ * Expose the possible connection states.
+ * @api public
+ */
+
+NativeConnection.STATES = STATES;
+
+/*!
+ * Inherits from Connection.
+ */
+
+NativeConnection.prototype.__proto__ = MongooseConnection.prototype;
+
+/**
+ * Switches to a different database using the same connection pool.
+ *
+ * Returns a new connection object, with the new db.
+ *
+ * @param {String} name The database name
+ * @return {Connection} New Connection Object
+ * @api public
+ */
+
+NativeConnection.prototype.useDb = function(name, options) {
+ // Return immediately if cached
+ if (options && options.useCache && this.relatedDbs[name]) {
+ return this.relatedDbs[name];
+ }
+
+ // we have to manually copy all of the attributes...
+ const newConn = new this.constructor();
+ newConn.name = name;
+ newConn.base = this.base;
+ newConn.collections = {};
+ newConn.models = {};
+ newConn.replica = this.replica;
+ newConn.name = this.name;
+ newConn.options = this.options;
+ newConn._readyState = this._readyState;
+ newConn._closeCalled = this._closeCalled;
+ newConn._hasOpened = this._hasOpened;
+ newConn._listening = false;
+
+ newConn.host = this.host;
+ newConn.port = this.port;
+ newConn.user = this.user;
+ newConn.pass = this.pass;
+
+ // First, when we create another db object, we are not guaranteed to have a
+ // db object to work with. So, in the case where we have a db object and it
+ // is connected, we can just proceed with setting everything up. However, if
+ // we do not have a db or the state is not connected, then we need to wait on
+ // the 'open' event of the connection before doing the rest of the setup
+ // the 'connected' event is the first time we'll have access to the db object
+
+ const _this = this;
+
+ newConn.client = _this.client;
+
+ if (this.db && this._readyState === STATES.connected) {
+ wireup();
+ } else {
+ this.once('connected', wireup);
+ }
+
+ function wireup() {
+ newConn.client = _this.client;
+ newConn.db = _this.client.db(name);
+ newConn.onOpen();
+ // setup the events appropriately
+ listen(newConn);
+ }
+
+ newConn.name = name;
+
+ // push onto the otherDbs stack, this is used when state changes
+ this.otherDbs.push(newConn);
+ newConn.otherDbs.push(this);
+
+ // push onto the relatedDbs cache, this is used when state changes
+ if (options && options.useCache) {
+ this.relatedDbs[newConn.name] = newConn;
+ newConn.relatedDbs = this.relatedDbs;
+ }
+
+ return newConn;
+};
+
+/*!
+ * Register listeners for important events and bubble appropriately.
+ */
+
+function listen(conn) {
+ if (conn.db._listening) {
+ return;
+ }
+ conn.db._listening = true;
+
+ conn.db.on('close', function(force) {
+ if (conn._closeCalled) return;
+
+ // the driver never emits an `open` event. auto_reconnect still
+ // emits a `close` event but since we never get another
+ // `open` we can't emit close
+ if (conn.db.serverConfig.autoReconnect) {
+ conn.readyState = STATES.disconnected;
+ conn.emit('close');
+ return;
+ }
+ conn.onClose(force);
+ });
+ conn.db.on('error', function(err) {
+ conn.emit('error', err);
+ });
+ conn.db.on('reconnect', function() {
+ conn.readyState = STATES.connected;
+ conn.emit('reconnect');
+ conn.emit('reconnected');
+ conn.onOpen();
+ });
+ conn.db.on('timeout', function(err) {
+ conn.emit('timeout', err);
+ });
+ conn.db.on('open', function(err, db) {
+ if (STATES.disconnected === conn.readyState && db && db.databaseName) {
+ conn.readyState = STATES.connected;
+ conn.emit('reconnect');
+ conn.emit('reconnected');
+ }
+ });
+ conn.db.on('parseError', function(err) {
+ conn.emit('parseError', err);
+ });
+}
+
+/**
+ * Closes the connection
+ *
+ * @param {Boolean} [force]
+ * @param {Function} [fn]
+ * @return {Connection} this
+ * @api private
+ */
+
+NativeConnection.prototype.doClose = function(force, fn) {
+ this.client.close(force, (err, res) => {
+ // Defer because the driver will wait at least 1ms before finishing closing
+ // the pool, see https://github.com/mongodb-js/mongodb-core/blob/a8f8e4ce41936babc3b9112bf42d609779f03b39/lib/connection/pool.js#L1026-L1030.
+ // If there's queued operations, you may still get some background work
+ // after the callback is called.
+ setTimeout(() => fn(err, res), 1);
+ });
+ return this;
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = NativeConnection;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/decimal128.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/decimal128.js
new file mode 100644
index 0000000..c895f17
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/decimal128.js
@@ -0,0 +1,7 @@
+/*!
+ * ignore
+ */
+
+'use strict';
+
+module.exports = require('mongodb').Decimal128;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js
new file mode 100644
index 0000000..648207d
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js
@@ -0,0 +1,11 @@
+/*!
+ * Module exports.
+ */
+
+'use strict';
+
+exports.Binary = require('./binary');
+exports.Collection = require('./collection');
+exports.Decimal128 = require('./decimal128');
+exports.ObjectId = require('./objectid');
+exports.ReadPreference = require('./ReadPreference');
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js
new file mode 100644
index 0000000..6f432b7
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js
@@ -0,0 +1,16 @@
+
+/*!
+ * [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) ObjectId
+ * @constructor NodeMongoDbObjectId
+ * @see ObjectId
+ */
+
+'use strict';
+
+const ObjectId = require('mongodb').ObjectId;
+
+/*!
+ * ignore
+ */
+
+module.exports = exports = ObjectId;
diff --git a/node_modules/mongoose/lib/error/browserMissingSchema.js b/node_modules/mongoose/lib/error/browserMissingSchema.js
new file mode 100644
index 0000000..852f873
--- /dev/null
+++ b/node_modules/mongoose/lib/error/browserMissingSchema.js
@@ -0,0 +1,38 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/*!
+ * MissingSchema Error constructor.
+ *
+ * @inherits MongooseError
+ */
+
+function MissingSchemaError() {
+ const msg = 'Schema hasn\'t been registered for document.\n'
+ + 'Use mongoose.Document(name, schema)';
+ MongooseError.call(this, msg);
+ this.name = 'MissingSchemaError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+MissingSchemaError.prototype = Object.create(MongooseError.prototype);
+MissingSchemaError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = MissingSchemaError;
diff --git a/node_modules/mongoose/lib/error/cast.js b/node_modules/mongoose/lib/error/cast.js
new file mode 100644
index 0000000..1980d38
--- /dev/null
+++ b/node_modules/mongoose/lib/error/cast.js
@@ -0,0 +1,62 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const MongooseError = require('./mongooseError');
+const util = require('util');
+
+/**
+ * Casting Error constructor.
+ *
+ * @param {String} type
+ * @param {String} value
+ * @inherits MongooseError
+ * @api private
+ */
+
+function CastError(type, value, path, reason) {
+ let stringValue = util.inspect(value);
+ stringValue = stringValue.replace(/^'/, '"').replace(/'$/, '"');
+ if (stringValue.charAt(0) !== '"') {
+ stringValue = '"' + stringValue + '"';
+ }
+ MongooseError.call(this, 'Cast to ' + type + ' failed for value ' +
+ stringValue + ' at path "' + path + '"');
+ this.name = 'CastError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.stringValue = stringValue;
+ this.kind = type;
+ this.value = value;
+ this.path = path;
+ this.reason = reason;
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+CastError.prototype = Object.create(MongooseError.prototype);
+CastError.prototype.constructor = MongooseError;
+
+/*!
+ * ignore
+ */
+
+CastError.prototype.setModel = function(model) {
+ this.model = model;
+ this.message = 'Cast to ' + this.kind + ' failed for value ' +
+ this.stringValue + ' at path "' + this.path + '"' + ' for model "' +
+ model.modelName + '"';
+};
+
+/*!
+ * exports
+ */
+
+module.exports = CastError;
diff --git a/node_modules/mongoose/lib/error/disconnected.js b/node_modules/mongoose/lib/error/disconnected.js
new file mode 100644
index 0000000..f542459
--- /dev/null
+++ b/node_modules/mongoose/lib/error/disconnected.js
@@ -0,0 +1,42 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/**
+ * Casting Error constructor.
+ *
+ * @param {String} type
+ * @param {String} value
+ * @inherits MongooseError
+ * @api private
+ */
+
+function DisconnectedError(connectionString) {
+ MongooseError.call(this, 'Ran out of retries trying to reconnect to "' +
+ connectionString + '". Try setting `server.reconnectTries` and ' +
+ '`server.reconnectInterval` to something higher.');
+ this.name = 'DisconnectedError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+DisconnectedError.prototype = Object.create(MongooseError.prototype);
+DisconnectedError.prototype.constructor = MongooseError;
+
+
+/*!
+ * exports
+ */
+
+module.exports = DisconnectedError;
diff --git a/node_modules/mongoose/lib/error/divergentArray.js b/node_modules/mongoose/lib/error/divergentArray.js
new file mode 100644
index 0000000..872fd2b
--- /dev/null
+++ b/node_modules/mongoose/lib/error/divergentArray.js
@@ -0,0 +1,48 @@
+
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/*!
+ * DivergentArrayError constructor.
+ *
+ * @inherits MongooseError
+ */
+
+function DivergentArrayError(paths) {
+ const msg = 'For your own good, using `document.save()` to update an array '
+ + 'which was selected using an $elemMatch projection OR '
+ + 'populated using skip, limit, query conditions, or exclusion of '
+ + 'the _id field when the operation results in a $pop or $set of '
+ + 'the entire array is not supported. The following '
+ + 'path(s) would have been modified unsafely:\n'
+ + ' ' + paths.join('\n ') + '\n'
+ + 'Use Model.update() to update these arrays instead.';
+ // TODO write up a docs page (FAQ) and link to it
+
+ MongooseError.call(this, msg);
+ this.name = 'DivergentArrayError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+DivergentArrayError.prototype = Object.create(MongooseError.prototype);
+DivergentArrayError.prototype.constructor = MongooseError;
+
+
+/*!
+ * exports
+ */
+
+module.exports = DivergentArrayError;
diff --git a/node_modules/mongoose/lib/error/index.js b/node_modules/mongoose/lib/error/index.js
new file mode 100644
index 0000000..3ae007d
--- /dev/null
+++ b/node_modules/mongoose/lib/error/index.js
@@ -0,0 +1,104 @@
+'use strict';
+
+const MongooseError = require('./mongooseError');
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = MongooseError;
+
+/**
+ * The default built-in validator error messages.
+ *
+ * @see Error.messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+MongooseError.messages = require('./messages');
+
+// backward compat
+MongooseError.Messages = MongooseError.messages;
+
+/**
+ * An instance of this error class will be returned when `save()` fails
+ * because the underlying
+ * document was not found. The constructor takes one parameter, the
+ * conditions that mongoose passed to `update()` when trying to update
+ * the document.
+ *
+ * @api public
+ */
+
+MongooseError.DocumentNotFoundError = require('./notFound');
+
+/**
+ * An instance of this error class will be returned when mongoose failed to
+ * cast a value.
+ *
+ * @api public
+ */
+
+MongooseError.CastError = require('./cast');
+
+/**
+ * An instance of this error class will be returned when [validation](/docs/validation.html) failed.
+ *
+ * @api public
+ */
+
+MongooseError.ValidationError = require('./validation');
+
+/**
+ * A `ValidationError` has a hash of `errors` that contain individual `ValidatorError` instances
+ *
+ * @api public
+ */
+
+MongooseError.ValidatorError = require('./validator');
+
+/**
+ * An instance of this error class will be returned when you call `save()` after
+ * the document in the database was changed in a potentially unsafe way. See
+ * the [`versionKey` option](/docs/guide.html#versionKey) for more information.
+ *
+ * @api public
+ */
+
+MongooseError.VersionError = require('./version');
+
+/**
+ * An instance of this error class will be returned when you call `save()` multiple
+ * times on the same document in parallel. See the [FAQ](/docs/faq.html) for more
+ * information.
+ *
+ * @api public
+ */
+
+MongooseError.ParallelSaveError = require('./parallelSave');
+
+/**
+ * Thrown when a model with the given name was already registered on the connection.
+ * See [the FAQ about `OverwriteModelError`](/docs/faq.html#overwrite-model-error).
+ *
+ * @api public
+ */
+
+MongooseError.OverwriteModelError = require('./overwriteModel');
+
+/**
+ * Thrown when you try to access a model that has not been registered yet
+ *
+ * @api public
+ */
+
+MongooseError.MissingSchemaError = require('./missingSchema');
+
+/**
+ * An instance of this error will be returned if you used an array projection
+ * and then modified the array in an unsafe way.
+ *
+ * @api public
+ */
+
+MongooseError.DivergentArrayError = require('./divergentArray');
diff --git a/node_modules/mongoose/lib/error/messages.js b/node_modules/mongoose/lib/error/messages.js
new file mode 100644
index 0000000..4483a86
--- /dev/null
+++ b/node_modules/mongoose/lib/error/messages.js
@@ -0,0 +1,46 @@
+
+/**
+ * The default built-in validator error messages. These may be customized.
+ *
+ * // customize within each schema or globally like so
+ * var mongoose = require('mongoose');
+ * mongoose.Error.messages.String.enum = "Your custom message for {PATH}.";
+ *
+ * As you might have noticed, error messages support basic templating
+ *
+ * - `{PATH}` is replaced with the invalid document path
+ * - `{VALUE}` is replaced with the invalid value
+ * - `{TYPE}` is replaced with the validator type such as "regexp", "min", or "user defined"
+ * - `{MIN}` is replaced with the declared min value for the Number.min validator
+ * - `{MAX}` is replaced with the declared max value for the Number.max validator
+ *
+ * Click the "show code" link below to see all defaults.
+ *
+ * @static messages
+ * @receiver MongooseError
+ * @api public
+ */
+
+'use strict';
+
+const msg = module.exports = exports = {};
+
+msg.DocumentNotFoundError = null;
+
+msg.general = {};
+msg.general.default = 'Validator failed for path `{PATH}` with value `{VALUE}`';
+msg.general.required = 'Path `{PATH}` is required.';
+
+msg.Number = {};
+msg.Number.min = 'Path `{PATH}` ({VALUE}) is less than minimum allowed value ({MIN}).';
+msg.Number.max = 'Path `{PATH}` ({VALUE}) is more than maximum allowed value ({MAX}).';
+
+msg.Date = {};
+msg.Date.min = 'Path `{PATH}` ({VALUE}) is before minimum allowed value ({MIN}).';
+msg.Date.max = 'Path `{PATH}` ({VALUE}) is after maximum allowed value ({MAX}).';
+
+msg.String = {};
+msg.String.enum = '`{VALUE}` is not a valid enum value for path `{PATH}`.';
+msg.String.match = 'Path `{PATH}` is invalid ({VALUE}).';
+msg.String.minlength = 'Path `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).';
+msg.String.maxlength = 'Path `{PATH}` (`{VALUE}`) is longer than the maximum allowed length ({MAXLENGTH}).';
diff --git a/node_modules/mongoose/lib/error/missingSchema.js b/node_modules/mongoose/lib/error/missingSchema.js
new file mode 100644
index 0000000..3195158
--- /dev/null
+++ b/node_modules/mongoose/lib/error/missingSchema.js
@@ -0,0 +1,39 @@
+
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/*!
+ * MissingSchema Error constructor.
+ *
+ * @inherits MongooseError
+ */
+
+function MissingSchemaError(name) {
+ const msg = 'Schema hasn\'t been registered for model "' + name + '".\n'
+ + 'Use mongoose.model(name, schema)';
+ MongooseError.call(this, msg);
+ this.name = 'MissingSchemaError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+MissingSchemaError.prototype = Object.create(MongooseError.prototype);
+MissingSchemaError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = MissingSchemaError;
diff --git a/node_modules/mongoose/lib/error/mongooseError.js b/node_modules/mongoose/lib/error/mongooseError.js
new file mode 100644
index 0000000..398c31b
--- /dev/null
+++ b/node_modules/mongoose/lib/error/mongooseError.js
@@ -0,0 +1,28 @@
+/**
+ * MongooseError constructor
+ *
+ * @param {String} msg Error message
+ * @inherits Error https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error
+ */
+
+'use strict';
+
+function MongooseError(msg) {
+ Error.call(this);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.message = msg;
+ this.name = 'MongooseError';
+}
+
+/*!
+ * Inherits from Error.
+ */
+
+MongooseError.prototype = Object.create(Error.prototype);
+MongooseError.prototype.constructor = Error;
+
+module.exports = MongooseError;
diff --git a/node_modules/mongoose/lib/error/notFound.js b/node_modules/mongoose/lib/error/notFound.js
new file mode 100644
index 0000000..6b61242
--- /dev/null
+++ b/node_modules/mongoose/lib/error/notFound.js
@@ -0,0 +1,50 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const MongooseError = require('./');
+const util = require('util');
+
+/*!
+ * OverwriteModel Error constructor.
+ *
+ * @inherits MongooseError
+ */
+
+function DocumentNotFoundError(query) {
+ let msg;
+ const messages = MongooseError.messages;
+ if (messages.DocumentNotFoundError != null) {
+ msg = typeof messages.DocumentNotFoundError === 'function' ?
+ messages.DocumentNotFoundError(query) :
+ messages.DocumentNotFoundError;
+ } else {
+ msg = 'No document found for query "' + util.inspect(query) + '"';
+ }
+
+ MongooseError.call(this, msg);
+
+ this.name = 'DocumentNotFoundError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+
+ this.query = query;
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+DocumentNotFoundError.prototype = Object.create(MongooseError.prototype);
+DocumentNotFoundError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = DocumentNotFoundError;
diff --git a/node_modules/mongoose/lib/error/objectExpected.js b/node_modules/mongoose/lib/error/objectExpected.js
new file mode 100644
index 0000000..de54f4f
--- /dev/null
+++ b/node_modules/mongoose/lib/error/objectExpected.js
@@ -0,0 +1,37 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/**
+ * Strict mode error constructor
+ *
+ * @param {String} type
+ * @param {String} value
+ * @inherits MongooseError
+ * @api private
+ */
+
+function ObjectExpectedError(path, val) {
+ MongooseError.call(this, 'Tried to set nested object field `' + path +
+ '` to primitive value `' + val + '` and strict mode is set to throw.');
+ this.name = 'ObjectExpectedError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.path = path;
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+ObjectExpectedError.prototype = Object.create(MongooseError.prototype);
+ObjectExpectedError.prototype.constructor = MongooseError;
+
+module.exports = ObjectExpectedError;
diff --git a/node_modules/mongoose/lib/error/objectParameter.js b/node_modules/mongoose/lib/error/objectParameter.js
new file mode 100644
index 0000000..3a7f284
--- /dev/null
+++ b/node_modules/mongoose/lib/error/objectParameter.js
@@ -0,0 +1,38 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/**
+ * Constructor for errors that happen when a parameter that's expected to be
+ * an object isn't an object
+ *
+ * @param {Any} value
+ * @param {String} paramName
+ * @param {String} fnName
+ * @inherits MongooseError
+ * @api private
+ */
+
+function ObjectParameterError(value, paramName, fnName) {
+ MongooseError.call(this, 'Parameter "' + paramName + '" to ' + fnName +
+ '() must be an object, got ' + value.toString());
+ this.name = 'ObjectParameterError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+ObjectParameterError.prototype = Object.create(MongooseError.prototype);
+ObjectParameterError.prototype.constructor = MongooseError;
+
+module.exports = ObjectParameterError;
diff --git a/node_modules/mongoose/lib/error/overwriteModel.js b/node_modules/mongoose/lib/error/overwriteModel.js
new file mode 100644
index 0000000..21013b6
--- /dev/null
+++ b/node_modules/mongoose/lib/error/overwriteModel.js
@@ -0,0 +1,37 @@
+
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/*!
+ * OverwriteModel Error constructor.
+ *
+ * @inherits MongooseError
+ */
+
+function OverwriteModelError(name) {
+ MongooseError.call(this, 'Cannot overwrite `' + name + '` model once compiled.');
+ this.name = 'OverwriteModelError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+OverwriteModelError.prototype = Object.create(MongooseError.prototype);
+OverwriteModelError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = OverwriteModelError;
diff --git a/node_modules/mongoose/lib/error/parallelSave.js b/node_modules/mongoose/lib/error/parallelSave.js
new file mode 100644
index 0000000..c9a189c
--- /dev/null
+++ b/node_modules/mongoose/lib/error/parallelSave.js
@@ -0,0 +1,33 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const MongooseError = require('./');
+
+/**
+ * ParallelSave Error constructor.
+ *
+ * @inherits MongooseError
+ * @api private
+ */
+
+function ParallelSaveError(doc) {
+ const msg = 'Can\'t save() the same doc multiple times in parallel. Document: ';
+ MongooseError.call(this, msg + doc.id);
+ this.name = 'ParallelSaveError';
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+ParallelSaveError.prototype = Object.create(MongooseError.prototype);
+ParallelSaveError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = ParallelSaveError;
diff --git a/node_modules/mongoose/lib/error/strict.js b/node_modules/mongoose/lib/error/strict.js
new file mode 100644
index 0000000..2678174
--- /dev/null
+++ b/node_modules/mongoose/lib/error/strict.js
@@ -0,0 +1,38 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/**
+ * Strict mode error constructor
+ *
+ * @param {String} type
+ * @param {String} value
+ * @inherits MongooseError
+ * @api private
+ */
+
+function StrictModeError(path, msg) {
+ msg = msg || 'Field `' + path + '` is not in schema and strict ' +
+ 'mode is set to throw.';
+ MongooseError.call(this, msg);
+ this.name = 'StrictModeError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.path = path;
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+StrictModeError.prototype = Object.create(MongooseError.prototype);
+StrictModeError.prototype.constructor = MongooseError;
+
+module.exports = StrictModeError;
diff --git a/node_modules/mongoose/lib/error/validation.js b/node_modules/mongoose/lib/error/validation.js
new file mode 100644
index 0000000..85b733c
--- /dev/null
+++ b/node_modules/mongoose/lib/error/validation.js
@@ -0,0 +1,112 @@
+/*!
+ * Module requirements
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+const util = require('util');
+
+/**
+ * Document Validation Error
+ *
+ * @api private
+ * @param {Document} instance
+ * @inherits MongooseError
+ */
+
+function ValidationError(instance) {
+ this.errors = {};
+ this._message = '';
+ if (instance && instance.constructor.name === 'model') {
+ this._message = instance.constructor.modelName + ' validation failed';
+ MongooseError.call(this, this._message);
+ } else {
+ this._message = 'Validation failed';
+ MongooseError.call(this, this._message);
+ }
+ this.name = 'ValidationError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ if (instance) {
+ instance.errors = this.errors;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+ValidationError.prototype = Object.create(MongooseError.prototype);
+ValidationError.prototype.constructor = MongooseError;
+
+/**
+ * Console.log helper
+ */
+
+ValidationError.prototype.toString = function() {
+ return this.name + ': ' + _generateMessage(this);
+};
+
+/*!
+ * inspect helper
+ */
+
+ValidationError.prototype.inspect = function() {
+ return Object.assign(new Error(this.message), this);
+};
+
+if (util.inspect.custom) {
+ /*!
+ * Avoid Node deprecation warning DEP0079
+ */
+
+ ValidationError.prototype[util.inspect.custom] = ValidationError.prototype.inspect;
+}
+
+/*!
+ * Helper for JSON.stringify
+ */
+
+ValidationError.prototype.toJSON = function() {
+ return Object.assign({}, this, { message: this.message });
+};
+
+/*!
+ * add message
+ */
+
+ValidationError.prototype.addError = function(path, error) {
+ this.errors[path] = error;
+ this.message = this._message + ': ' + _generateMessage(this);
+};
+
+/*!
+ * ignore
+ */
+
+function _generateMessage(err) {
+ const keys = Object.keys(err.errors || {});
+ const len = keys.length;
+ const msgs = [];
+ let key;
+
+ for (let i = 0; i < len; ++i) {
+ key = keys[i];
+ if (err === err.errors[key]) {
+ continue;
+ }
+ msgs.push(key + ': ' + err.errors[key].message);
+ }
+
+ return msgs.join(', ');
+}
+
+/*!
+ * Module exports
+ */
+
+module.exports = exports = ValidationError;
diff --git a/node_modules/mongoose/lib/error/validator.js b/node_modules/mongoose/lib/error/validator.js
new file mode 100644
index 0000000..d07100c
--- /dev/null
+++ b/node_modules/mongoose/lib/error/validator.js
@@ -0,0 +1,89 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const MongooseError = require('./');
+
+/**
+ * Schema validator error
+ *
+ * @param {Object} properties
+ * @inherits MongooseError
+ * @api private
+ */
+
+function ValidatorError(properties) {
+ let msg = properties.message;
+ if (!msg) {
+ msg = MongooseError.messages.general.default;
+ }
+
+ const message = this.formatMessage(msg, properties);
+ MongooseError.call(this, message);
+
+ properties = Object.assign({}, properties, { message: message });
+ this.name = 'ValidatorError';
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.properties = properties;
+ this.kind = properties.type;
+ this.path = properties.path;
+ this.value = properties.value;
+ this.reason = properties.reason;
+}
+
+/*!
+ * Inherits from MongooseError
+ */
+
+ValidatorError.prototype = Object.create(MongooseError.prototype);
+ValidatorError.prototype.constructor = MongooseError;
+
+/*!
+ * The object used to define this validator. Not enumerable to hide
+ * it from `require('util').inspect()` output re: gh-3925
+ */
+
+Object.defineProperty(ValidatorError.prototype, 'properties', {
+ enumerable: false,
+ writable: true,
+ value: null
+});
+
+/*!
+ * Formats error messages
+ */
+
+ValidatorError.prototype.formatMessage = function(msg, properties) {
+ if (typeof msg === 'function') {
+ return msg(properties);
+ }
+ const propertyNames = Object.keys(properties);
+ for (let i = 0; i < propertyNames.length; ++i) {
+ const propertyName = propertyNames[i];
+ if (propertyName === 'message') {
+ continue;
+ }
+ msg = msg.replace('{' + propertyName.toUpperCase() + '}', properties[propertyName]);
+ }
+ return msg;
+};
+
+/*!
+ * toString helper
+ */
+
+ValidatorError.prototype.toString = function() {
+ return this.message;
+};
+
+/*!
+ * exports
+ */
+
+module.exports = ValidatorError;
diff --git a/node_modules/mongoose/lib/error/version.js b/node_modules/mongoose/lib/error/version.js
new file mode 100644
index 0000000..9fe9201
--- /dev/null
+++ b/node_modules/mongoose/lib/error/version.js
@@ -0,0 +1,36 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const MongooseError = require('./');
+
+/**
+ * Version Error constructor.
+ *
+ * @inherits MongooseError
+ * @api private
+ */
+
+function VersionError(doc, currentVersion, modifiedPaths) {
+ const modifiedPathsStr = modifiedPaths.join(', ');
+ MongooseError.call(this, 'No matching document found for id "' + doc._id +
+ '" version ' + currentVersion + ' modifiedPaths "' + modifiedPathsStr + '"');
+ this.name = 'VersionError';
+ this.version = currentVersion;
+ this.modifiedPaths = modifiedPaths;
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+VersionError.prototype = Object.create(MongooseError.prototype);
+VersionError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = VersionError;
diff --git a/node_modules/mongoose/lib/helpers/common.js b/node_modules/mongoose/lib/helpers/common.js
new file mode 100644
index 0000000..ac75317
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/common.js
@@ -0,0 +1,87 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const ObjectId = require('../types/objectid');
+const utils = require('../utils');
+
+exports.flatten = flatten;
+exports.modifiedPaths = modifiedPaths;
+
+/*!
+ * ignore
+ */
+
+function flatten(update, path, options) {
+ let keys;
+ if (update && utils.isMongooseObject(update) && !Buffer.isBuffer(update)) {
+ keys = Object.keys(update.toObject({ transform: false, virtuals: false }));
+ } else {
+ keys = Object.keys(update || {});
+ }
+
+ const numKeys = keys.length;
+ const result = {};
+ path = path ? path + '.' : '';
+
+ for (let i = 0; i < numKeys; ++i) {
+ const key = keys[i];
+ const val = update[key];
+ result[path + key] = val;
+ if (shouldFlatten(val)) {
+ if (options && options.skipArrays && Array.isArray(val)) {
+ continue;
+ }
+ const flat = flatten(val, path + key, options);
+ for (const k in flat) {
+ result[k] = flat[k];
+ }
+ if (Array.isArray(val)) {
+ result[path + key] = val;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*!
+ * ignore
+ */
+
+function modifiedPaths(update, path, result) {
+ const keys = Object.keys(update || {});
+ const numKeys = keys.length;
+ result = result || {};
+ path = path ? path + '.' : '';
+
+ for (let i = 0; i < numKeys; ++i) {
+ const key = keys[i];
+ let val = update[key];
+
+ result[path + key] = true;
+ if (utils.isMongooseObject(val) && !Buffer.isBuffer(val)) {
+ val = val.toObject({ transform: false, virtuals: false });
+ }
+ if (shouldFlatten(val)) {
+ modifiedPaths(val, path + key, result);
+ }
+ }
+
+ return result;
+}
+
+/*!
+ * ignore
+ */
+
+function shouldFlatten(val) {
+ return val &&
+ typeof val === 'object' &&
+ !(val instanceof Date) &&
+ !(val instanceof ObjectId) &&
+ (!Array.isArray(val) || val.length > 0) &&
+ !(val instanceof Buffer);
+}
diff --git a/node_modules/mongoose/lib/helpers/cursor/eachAsync.js b/node_modules/mongoose/lib/helpers/cursor/eachAsync.js
new file mode 100644
index 0000000..5ab0f57
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/cursor/eachAsync.js
@@ -0,0 +1,71 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const async = require('async');
+const utils = require('../../utils');
+
+/**
+ * Execute `fn` for every document in the cursor. If `fn` returns a promise,
+ * will wait for the promise to resolve before iterating on to the next one.
+ * Returns a promise that resolves when done.
+ *
+ * @param {Function} next the thunk to call to get the next document
+ * @param {Function} fn
+ * @param {Object} options
+ * @param {Function} [callback] executed when all docs have been processed
+ * @return {Promise}
+ * @api public
+ * @method eachAsync
+ */
+
+module.exports = function eachAsync(next, fn, options, callback) {
+ const parallel = options.parallel || 1;
+
+ const handleNextResult = function(doc, callback) {
+ const promise = fn(doc);
+ if (promise && typeof promise.then === 'function') {
+ promise.then(
+ function() { callback(null); },
+ function(error) { callback(error || new Error('`eachAsync()` promise rejected without error')); });
+ } else {
+ callback(null);
+ }
+ };
+
+ const iterate = function(callback) {
+ let drained = false;
+ const nextQueue = async.queue(function(task, cb) {
+ if (drained) return cb();
+ next(function(err, doc) {
+ if (err) return cb(err);
+ if (!doc) drained = true;
+ cb(null, doc);
+ });
+ }, 1);
+
+ const getAndRun = function(cb) {
+ nextQueue.push({}, function(err, doc) {
+ if (err) return cb(err);
+ if (!doc) return cb();
+ handleNextResult(doc, function(err) {
+ if (err) return cb(err);
+ // Make sure to clear the stack re: gh-4697
+ setTimeout(function() {
+ getAndRun(cb);
+ }, 0);
+ });
+ });
+ };
+
+ async.times(parallel, function(n, cb) {
+ getAndRun(cb);
+ }, callback);
+ };
+
+ return utils.promiseOrCallback(callback, cb => {
+ iterate(cb);
+ });
+};
diff --git a/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js b/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js
new file mode 100644
index 0000000..0405b5b
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js
@@ -0,0 +1,25 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function cleanModifiedSubpaths(doc, path, options) {
+ options = options || {};
+ const skipDocArrays = options.skipDocArrays;
+
+ let deleted = 0;
+ for (const modifiedPath of Object.keys(doc.$__.activePaths.states.modify)) {
+ if (skipDocArrays) {
+ const schemaType = doc.schema.path(modifiedPath);
+ if (schemaType && schemaType.$isMongooseDocumentArray) {
+ continue;
+ }
+ }
+ if (modifiedPath.indexOf(path + '.') === 0) {
+ delete doc.$__.activePaths.states.modify[modifiedPath];
+ ++deleted;
+ }
+ }
+ return deleted;
+};
diff --git a/node_modules/mongoose/lib/helpers/document/compile.js b/node_modules/mongoose/lib/helpers/document/compile.js
new file mode 100644
index 0000000..3644aa9
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/document/compile.js
@@ -0,0 +1,148 @@
+'use strict';
+
+const get = require('../../helpers/get');
+const getSymbol = require('../../helpers/symbols').getSymbol;
+const utils = require('../../utils');
+
+let Document;
+
+/*!
+ * exports
+ */
+
+exports.compile = compile;
+exports.defineKey = defineKey;
+
+/*!
+ * Compiles schemas.
+ */
+
+function compile(tree, proto, prefix, options) {
+ Document = Document || require('../../document');
+ const keys = Object.keys(tree);
+ const len = keys.length;
+ let limb;
+ let key;
+
+ for (let i = 0; i < len; ++i) {
+ key = keys[i];
+ limb = tree[key];
+
+ const hasSubprops = utils.getFunctionName(limb.constructor) === 'Object' &&
+ Object.keys(limb).length &&
+ (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type));
+ const subprops = hasSubprops ? limb : null;
+
+ defineKey(key, subprops, proto, prefix, keys, options);
+ }
+}
+
+/*!
+ * Defines the accessor named prop on the incoming prototype.
+ */
+
+function defineKey(prop, subprops, prototype, prefix, keys, options) {
+ Document = Document || require('../../document');
+ const path = (prefix ? prefix + '.' : '') + prop;
+ prefix = prefix || '';
+
+ if (subprops) {
+ Object.defineProperty(prototype, prop, {
+ enumerable: true,
+ configurable: true,
+ get: function() {
+ const _this = this;
+ if (!this.$__.getters) {
+ this.$__.getters = {};
+ }
+
+ if (!this.$__.getters[path]) {
+ const nested = Object.create(Document.prototype, getOwnPropertyDescriptors(this));
+
+ // save scope for nested getters/setters
+ if (!prefix) {
+ nested.$__.scope = this;
+ }
+ nested.$__.nestedPath = path;
+
+ Object.defineProperty(nested, 'schema', {
+ enumerable: false,
+ configurable: true,
+ writable: false,
+ value: prototype.schema
+ });
+
+ Object.defineProperty(nested, 'toObject', {
+ enumerable: false,
+ configurable: true,
+ writable: false,
+ value: function() {
+ return utils.clone(_this.get(path, null, {
+ virtuals: get(this, 'schema.options.toObject.virtuals', null)
+ }));
+ }
+ });
+
+ Object.defineProperty(nested, 'toJSON', {
+ enumerable: false,
+ configurable: true,
+ writable: false,
+ value: function() {
+ return _this.get(path, null, {
+ virtuals: get(_this, 'schema.options.toJSON.virtuals', null)
+ });
+ }
+ });
+
+ Object.defineProperty(nested, '$__isNested', {
+ enumerable: false,
+ configurable: true,
+ writable: false,
+ value: true
+ });
+
+ compile(subprops, nested, path, options);
+ this.$__.getters[path] = nested;
+ }
+
+ return this.$__.getters[path];
+ },
+ set: function(v) {
+ if (v instanceof Document) {
+ v = v.toObject({ transform: false });
+ }
+ const doc = this.$__.scope || this;
+ return doc.$set(path, v);
+ }
+ });
+ } else {
+ Object.defineProperty(prototype, prop, {
+ enumerable: true,
+ configurable: true,
+ get: function() {
+ return this[getSymbol].call(this.$__.scope || this, path);
+ },
+ set: function(v) {
+ return this.$set.call(this.$__.scope || this, path, v);
+ }
+ });
+ }
+}
+
+// gets descriptors for all properties of `object`
+// makes all properties non-enumerable to match previous behavior to #2211
+function getOwnPropertyDescriptors(object) {
+ const result = {};
+
+ Object.getOwnPropertyNames(object).forEach(function(key) {
+ result[key] = Object.getOwnPropertyDescriptor(object, key);
+ // Assume these are schema paths, ignore them re: #5470
+ if (result[key].get) {
+ delete result[key];
+ return;
+ }
+ result[key].enumerable = ['isNew', '$__', 'errors', '_doc'].indexOf(key) === -1;
+ });
+
+ return result;
+}
diff --git a/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js b/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js
new file mode 100644
index 0000000..0e115b0
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js
@@ -0,0 +1,43 @@
+'use strict';
+
+const get = require('../get');
+
+/*!
+ * Like `schema.path()`, except with a document, because impossible to
+ * determine path type without knowing the embedded discriminator key.
+ */
+
+module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) {
+ options = options || {};
+ const typeOnly = options.typeOnly;
+ const parts = path.split('.');
+ let schema = null;
+ let type = 'adhocOrUndefined';
+
+ for (let i = 0; i < parts.length; ++i) {
+ const subpath = parts.slice(0, i + 1).join('.');
+ schema = doc.schema.path(subpath);
+ if (schema == null) {
+ continue;
+ }
+ type = doc.schema.pathType(subpath);
+ if ((schema.$isSingleNested || schema.$isMongooseDocumentArrayElement) &&
+ schema.schema.discriminators != null) {
+ const discriminators = schema.schema.discriminators;
+ const discriminatorKey = doc.get(subpath + '.' +
+ get(schema, 'schema.options.discriminatorKey'));
+ if (discriminatorKey == null || discriminators[discriminatorKey] == null) {
+ continue;
+ }
+ const rest = parts.slice(i + 1).join('.');
+ schema = discriminators[discriminatorKey].path(rest);
+ if (schema != null) {
+ type = discriminators[discriminatorKey].pathType(rest);
+ break;
+ }
+ }
+ }
+
+ // Are we getting the whole schema or just the type, 'real', 'nested', etc.
+ return typeOnly ? type : schema;
+};
diff --git a/node_modules/mongoose/lib/helpers/get.js b/node_modules/mongoose/lib/helpers/get.js
new file mode 100644
index 0000000..68e71e1
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/get.js
@@ -0,0 +1,39 @@
+'use strict';
+
+/*!
+ * Simplified lodash.get to work around the annoying null quirk. See:
+ * https://github.com/lodash/lodash/issues/3659
+ */
+
+module.exports = function get(obj, path, def) {
+ const parts = path.split('.');
+ let rest = path;
+ let cur = obj;
+ for (const part of parts) {
+ if (cur == null) {
+ return def;
+ }
+
+ // `lib/cast.js` depends on being able to get dotted paths in updates,
+ // like `{ $set: { 'a.b': 42 } }`
+ if (cur[rest] != null) {
+ return cur[rest];
+ }
+
+ cur = getProperty(cur, part);
+
+ rest = rest.substr(part.length + 1);
+ }
+
+ return cur == null ? def : cur;
+};
+
+function getProperty(obj, prop) {
+ if (obj == null) {
+ return obj;
+ }
+ if (obj instanceof Map) {
+ return obj.get(prop);
+ }
+ return obj[prop];
+}
diff --git a/node_modules/mongoose/lib/helpers/immediate.js b/node_modules/mongoose/lib/helpers/immediate.js
new file mode 100644
index 0000000..ddb7060
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/immediate.js
@@ -0,0 +1,12 @@
+/*!
+ * Centralize this so we can more easily work around issues with people
+ * stubbing out `process.nextTick()` in tests using sinon:
+ * https://github.com/sinonjs/lolex#automatically-incrementing-mocked-time
+ * See gh-6074
+ */
+
+'use strict';
+
+module.exports = function immediate(cb) {
+ return process.nextTick(cb);
+};
diff --git a/node_modules/mongoose/lib/helpers/model/applyHooks.js b/node_modules/mongoose/lib/helpers/model/applyHooks.js
new file mode 100644
index 0000000..802d978
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/model/applyHooks.js
@@ -0,0 +1,122 @@
+'use strict';
+
+const symbols = require('../../schema/symbols');
+const utils = require('../../utils');
+
+/*!
+ * ignore
+ */
+
+module.exports = applyHooks;
+
+/*!
+ * ignore
+ */
+
+applyHooks.middlewareFunctions = [
+ 'save',
+ 'validate',
+ 'remove',
+ 'updateOne',
+ 'init'
+];
+
+/*!
+ * Register hooks for this model
+ *
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+
+function applyHooks(model, schema, options) {
+ options = options || {};
+
+ const kareemOptions = {
+ useErrorHandlers: true,
+ numCallbackParams: 1,
+ nullResultByDefault: true,
+ contextParameter: true
+ };
+ const objToDecorate = options.decorateDoc ? model : model.prototype;
+
+ model.$appliedHooks = true;
+ for (let i = 0; i < schema.childSchemas.length; ++i) {
+ const childModel = schema.childSchemas[i].model;
+ if (childModel.$appliedHooks) {
+ continue;
+ }
+ applyHooks(childModel, schema.childSchemas[i].schema, options);
+ if (childModel.discriminators != null) {
+ const keys = Object.keys(childModel.discriminators);
+ for (let j = 0; j < keys.length; ++j) {
+ applyHooks(childModel.discriminators[keys[j]],
+ childModel.discriminators[keys[j]].schema, options);
+ }
+ }
+ }
+
+ // Built-in hooks rely on hooking internal functions in order to support
+ // promises and make it so that `doc.save.toString()` provides meaningful
+ // information.
+
+ const middleware = schema.s.hooks.
+ filter(hook => {
+ if (hook.name === 'updateOne') {
+ return !!hook['document'];
+ }
+ if (hook.name === 'remove') {
+ return hook['document'] == null || !!hook['document'];
+ }
+ return true;
+ }).
+ filter(hook => {
+ // If user has overwritten the method, don't apply built-in middleware
+ if (schema.methods[hook.name]) {
+ return !hook.fn[symbols.builtInMiddleware];
+ }
+
+ return true;
+ });
+
+ model._middleware = middleware;
+
+ objToDecorate.$__save = middleware.
+ createWrapper('save', objToDecorate.$__save, null, kareemOptions);
+ objToDecorate.$__validate = middleware.
+ createWrapper('validate', objToDecorate.$__validate, null, kareemOptions);
+ objToDecorate.$__remove = middleware.
+ createWrapper('remove', objToDecorate.$__remove, null, kareemOptions);
+ objToDecorate.$__init = middleware.
+ createWrapperSync('init', objToDecorate.$__init, null, kareemOptions);
+
+ // Support hooks for custom methods
+ const customMethods = Object.keys(schema.methods);
+ const customMethodOptions = Object.assign({}, kareemOptions, {
+ // Only use `checkForPromise` for custom methods, because mongoose
+ // query thunks are not as consistent as I would like about returning
+ // a nullish value rather than the query. If a query thunk returns
+ // a query, `checkForPromise` causes infinite recursion
+ checkForPromise: true
+ });
+ for (const method of customMethods) {
+ if (!middleware.hasHooks(method)) {
+ // Don't wrap if there are no hooks for the custom method to avoid
+ // surprises. Also, `createWrapper()` enforces consistent async,
+ // so wrapping a sync method would break it.
+ continue;
+ }
+ const originalMethod = objToDecorate[method];
+ objToDecorate[method] = function() {
+ const args = Array.prototype.slice.call(arguments);
+ const cb = utils.last(args);
+ const argsWithoutCallback = cb == null ? args :
+ args.slice(0, args.length - 1);
+ return utils.promiseOrCallback(cb, callback => {
+ this[`$__${method}`].apply(this,
+ argsWithoutCallback.concat([callback]));
+ }, model.events);
+ };
+ objToDecorate[`$__${method}`] = middleware.
+ createWrapper(method, originalMethod, null, customMethodOptions);
+ }
+}
diff --git a/node_modules/mongoose/lib/helpers/model/applyMethods.js b/node_modules/mongoose/lib/helpers/model/applyMethods.js
new file mode 100644
index 0000000..5a9cf39
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/model/applyMethods.js
@@ -0,0 +1,53 @@
+'use strict';
+
+const get = require('../get');
+
+/*!
+ * Register methods for this model
+ *
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+
+module.exports = function applyMethods(model, schema) {
+ function apply(method, schema) {
+ Object.defineProperty(model.prototype, method, {
+ get: function() {
+ const h = {};
+ for (const k in schema.methods[method]) {
+ h[k] = schema.methods[method][k].bind(this);
+ }
+ return h;
+ },
+ configurable: true
+ });
+ }
+ for (const method of Object.keys(schema.methods)) {
+ const fn = schema.methods[method];
+ if (schema.tree.hasOwnProperty(method)) {
+ throw new Error('You have a method and a property in your schema both ' +
+ 'named "' + method + '"');
+ }
+ if (schema.reserved[method] &&
+ !get(schema, `methodOptions.${method}.suppressWarning`, false)) {
+ console.warn(`mongoose: the method name "${method}" is used by mongoose ` +
+ 'internally, overwriting it may cause bugs. If you\'re sure you know ' +
+ 'what you\'re doing, you can suppress this error by using ' +
+ `\`schema.method('${method}', fn, { suppressWarning: true })\`.`);
+ }
+ if (typeof fn === 'function') {
+ model.prototype[method] = fn;
+ } else {
+ apply(method, schema);
+ }
+ }
+
+ // Recursively call `applyMethods()` on child schemas
+ model.$appliedMethods = true;
+ for (let i = 0; i < schema.childSchemas.length; ++i) {
+ if (schema.childSchemas[i].model.$appliedMethods) {
+ continue;
+ }
+ applyMethods(schema.childSchemas[i].model, schema.childSchemas[i].schema);
+ }
+};
diff --git a/node_modules/mongoose/lib/helpers/model/applyStatics.js b/node_modules/mongoose/lib/helpers/model/applyStatics.js
new file mode 100644
index 0000000..3b9501e
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/model/applyStatics.js
@@ -0,0 +1,12 @@
+'use strict';
+
+/*!
+ * Register statics for this model
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+module.exports = function applyStatics(model, schema) {
+ for (const i in schema.statics) {
+ model[i] = schema.statics[i];
+ }
+};
diff --git a/node_modules/mongoose/lib/helpers/model/castBulkWrite.js b/node_modules/mongoose/lib/helpers/model/castBulkWrite.js
new file mode 100644
index 0000000..318f6f5
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/model/castBulkWrite.js
@@ -0,0 +1,136 @@
+'use strict';
+
+const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
+const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
+const cast = require('../../cast');
+const castUpdate = require('../query/castUpdate');
+const setDefaultsOnInsert = require('../setDefaultsOnInsert');
+
+/*!
+ * Given a model and a bulkWrite op, return a thunk that handles casting and
+ * validating the individual op.
+ */
+
+module.exports = function castBulkWrite(model, op) {
+ const now = model.base.now();
+
+ if (op['insertOne']) {
+ return (callback) => {
+ const doc = new model(op['insertOne']['document']);
+ if (model.schema.options.timestamps != null) {
+ doc.initializeTimestamps();
+ }
+
+ op['insertOne']['document'] = doc;
+ op['insertOne']['document'].validate({ __noPromise: true }, function(error) {
+ if (error) {
+ return callback(error, null);
+ }
+ callback(null);
+ });
+ };
+ } else if (op['updateOne']) {
+ op = op['updateOne'];
+ return (callback) => {
+ try {
+ op['filter'] = cast(model.schema, op['filter']);
+ op['update'] = castUpdate(model.schema, op['update'], {
+ strict: model.schema.options.strict,
+ overwrite: false
+ });
+ if (op.setDefaultsOnInsert) {
+ setDefaultsOnInsert(op['filter'], model.schema, op['update'], {
+ setDefaultsOnInsert: true,
+ upsert: op.upsert
+ });
+ }
+ if (model.schema.$timestamps != null) {
+ const createdAt = model.schema.$timestamps.createdAt;
+ const updatedAt = model.schema.$timestamps.updatedAt;
+ applyTimestampsToUpdate(now, createdAt, updatedAt, op['update'], {});
+ }
+ applyTimestampsToChildren(now, op['update'], model.schema);
+ } catch (error) {
+ return callback(error, null);
+ }
+
+ callback(null);
+ };
+ } else if (op['updateMany']) {
+ op = op['updateMany'];
+ return (callback) => {
+ try {
+ op['filter'] = cast(model.schema, op['filter']);
+ op['update'] = castUpdate(model.schema, op['update'], {
+ strict: model.schema.options.strict,
+ overwrite: false
+ });
+ if (op.setDefaultsOnInsert) {
+ setDefaultsOnInsert(op['filter'], model.schema, op['update'], {
+ setDefaultsOnInsert: true,
+ upsert: op.upsert
+ });
+ }
+ if (model.schema.$timestamps != null) {
+ const createdAt = model.schema.$timestamps.createdAt;
+ const updatedAt = model.schema.$timestamps.updatedAt;
+ applyTimestampsToUpdate(now, createdAt, updatedAt, op['update'], {});
+ }
+ applyTimestampsToChildren(now, op['update'], model.schema);
+ } catch (error) {
+ return callback(error, null);
+ }
+
+ callback(null);
+ };
+ } else if (op['replaceOne']) {
+ return (callback) => {
+ try {
+ op['replaceOne']['filter'] = cast(model.schema,
+ op['replaceOne']['filter']);
+ } catch (error) {
+ return callback(error, null);
+ }
+
+ // set `skipId`, otherwise we get "_id field cannot be changed"
+ const doc = new model(op['replaceOne']['replacement'], null, true);
+ if (model.schema.options.timestamps != null) {
+ doc.initializeTimestamps();
+ }
+ op['replaceOne']['replacement'] = doc;
+
+ op['replaceOne']['replacement'].validate({ __noPromise: true }, function(error) {
+ if (error) {
+ return callback(error, null);
+ }
+ callback(null);
+ });
+ };
+ } else if (op['deleteOne']) {
+ return (callback) => {
+ try {
+ op['deleteOne']['filter'] = cast(model.schema,
+ op['deleteOne']['filter']);
+ } catch (error) {
+ return callback(error, null);
+ }
+
+ callback(null);
+ };
+ } else if (op['deleteMany']) {
+ return (callback) => {
+ try {
+ op['deleteMany']['filter'] = cast(model.schema,
+ op['deleteMany']['filter']);
+ } catch (error) {
+ return callback(error, null);
+ }
+
+ callback(null);
+ };
+ } else {
+ return (callback) => {
+ callback(new Error('Invalid op passed to `bulkWrite()`'), null);
+ };
+ }
+};
diff --git a/node_modules/mongoose/lib/helpers/model/discriminator.js b/node_modules/mongoose/lib/helpers/model/discriminator.js
new file mode 100644
index 0000000..52bede1
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/model/discriminator.js
@@ -0,0 +1,168 @@
+'use strict';
+
+const defineKey = require('../document/compile').defineKey;
+const get = require('../../helpers/get');
+const utils = require('../../utils');
+
+const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
+ toJSON: true,
+ toObject: true,
+ _id: true,
+ id: true
+};
+
+/*!
+ * ignore
+ */
+
+module.exports = function discriminator(model, name, schema, tiedValue) {
+ if (!(schema && schema.instanceOfSchema)) {
+ throw new Error('You must pass a valid discriminator Schema');
+ }
+
+ const applyPluginsToDiscriminators = get(model.base,
+ 'options.applyPluginsToDiscriminators', false);
+ // Even if `applyPluginsToDiscriminators` isn't set, we should still apply
+ // global plugins to schemas embedded in the discriminator schema (gh-7370)
+ model.base._applyPlugins(schema, { skipTopLevel: !applyPluginsToDiscriminators });
+
+ if (model.schema.discriminatorMapping &&
+ !model.schema.discriminatorMapping.isRoot) {
+ throw new Error('Discriminator "' + name +
+ '" can only be a discriminator of the root model');
+ }
+
+ const key = model.schema.options.discriminatorKey;
+
+ const baseSchemaAddition = {};
+ baseSchemaAddition[key] = {
+ default: void 0,
+ select: true,
+ $skipDiscriminatorCheck: true
+ };
+ baseSchemaAddition[key][model.schema.options.typeKey] = String;
+ model.schema.add(baseSchemaAddition);
+ defineKey(key, null, model.prototype, null, [key], model.schema.options);
+
+ if (schema.path(key) && schema.path(key).options.$skipDiscriminatorCheck !== true) {
+ throw new Error('Discriminator "' + name +
+ '" cannot have field with name "' + key + '"');
+ }
+
+ let value = name;
+ if (typeof tiedValue == 'string' && tiedValue.length) {
+ value = tiedValue;
+ }
+
+ function merge(schema, baseSchema) {
+ // Retain original schema before merging base schema
+ schema._originalSchema = schema.clone();
+ if (baseSchema.paths._id &&
+ baseSchema.paths._id.options &&
+ !baseSchema.paths._id.options.auto) {
+ const originalSchema = schema;
+ utils.merge(schema, originalSchema);
+ delete schema.paths._id;
+ delete schema.tree._id;
+ }
+
+ // Find conflicting paths: if something is a path in the base schema
+ // and a nested path in the child schema, overwrite the base schema path.
+ // See gh-6076
+ const baseSchemaPaths = Object.keys(baseSchema.paths);
+ const conflictingPaths = [];
+ for (let i = 0; i < baseSchemaPaths.length; ++i) {
+ if (schema.nested[baseSchemaPaths[i]]) {
+ conflictingPaths.push(baseSchemaPaths[i]);
+ }
+ }
+
+ utils.merge(schema, baseSchema, {
+ omit: { discriminators: true, $parentSchema: true },
+ omitNested: conflictingPaths.reduce((cur, path) => {
+ cur['tree.' + path] = true;
+ return cur;
+ }, {})
+ });
+
+ // Clean up conflicting paths _after_ merging re: gh-6076
+ for (let i = 0; i < conflictingPaths.length; ++i) {
+ delete schema.paths[conflictingPaths[i]];
+ }
+
+ const obj = {};
+ obj[key] = {
+ default: value,
+ select: true,
+ set: function(newName) {
+ if (newName === value) {
+ return value;
+ }
+ throw new Error('Can\'t set discriminator key "' + key + '"');
+ },
+ $skipDiscriminatorCheck: true
+ };
+ obj[key][schema.options.typeKey] = String;
+ schema.add(obj);
+ schema.discriminatorMapping = {key: key, value: value, isRoot: false};
+
+ if (baseSchema.options.collection) {
+ schema.options.collection = baseSchema.options.collection;
+ }
+
+ const toJSON = schema.options.toJSON;
+ const toObject = schema.options.toObject;
+ const _id = schema.options._id;
+ const id = schema.options.id;
+
+ const keys = Object.keys(schema.options);
+ schema.options.discriminatorKey = baseSchema.options.discriminatorKey;
+
+ for (let i = 0; i < keys.length; ++i) {
+ const _key = keys[i];
+ if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
+ if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
+ throw new Error('Can\'t customize discriminator option ' + _key +
+ ' (can only modify ' +
+ Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
+ ')');
+ }
+ }
+ }
+
+ schema.options = utils.clone(baseSchema.options);
+ if (toJSON) schema.options.toJSON = toJSON;
+ if (toObject) schema.options.toObject = toObject;
+ if (typeof _id !== 'undefined') {
+ schema.options._id = _id;
+ }
+ schema.options.id = id;
+ schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks);
+
+ schema.plugins = Array.prototype.slice(baseSchema.plugins);
+ schema.callQueue = baseSchema.callQueue.concat(schema.callQueue);
+ delete schema._requiredpaths; // reset just in case Schema#requiredPaths() was called on either schema
+ }
+
+ // merges base schema into new discriminator schema and sets new type field.
+ merge(schema, model.schema);
+
+ if (!model.discriminators) {
+ model.discriminators = {};
+ }
+
+ if (!model.schema.discriminatorMapping) {
+ model.schema.discriminatorMapping = {key: key, value: null, isRoot: true};
+ }
+ if (!model.schema.discriminators) {
+ model.schema.discriminators = {};
+ }
+
+ model.schema.discriminators[name] = schema;
+
+ if (model.discriminators[name]) {
+ throw new Error('Discriminator with name "' + name + '" already exists');
+ }
+
+ return schema;
+};
diff --git a/node_modules/mongoose/lib/helpers/once.js b/node_modules/mongoose/lib/helpers/once.js
new file mode 100644
index 0000000..dfa5ee7
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/once.js
@@ -0,0 +1,12 @@
+'use strict';
+
+module.exports = function once(fn) {
+ let called = false;
+ return function() {
+ if (called) {
+ return;
+ }
+ called = true;
+ return fn.apply(null, arguments);
+ };
+};
diff --git a/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js b/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
new file mode 100644
index 0000000..f6a9fce
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
@@ -0,0 +1,85 @@
+'use strict';
+
+module.exports = assignRawDocsToIdStructure;
+
+/*!
+ * Assign `vals` returned by mongo query to the `rawIds`
+ * structure returned from utils.getVals() honoring
+ * query sort order if specified by user.
+ *
+ * This can be optimized.
+ *
+ * Rules:
+ *
+ * if the value of the path is not an array, use findOne rules, else find.
+ * for findOne the results are assigned directly to doc path (including null results).
+ * for find, if user specified sort order, results are assigned directly
+ * else documents are put back in original order of array if found in results
+ *
+ * @param {Array} rawIds
+ * @param {Array} vals
+ * @param {Boolean} sort
+ * @api private
+ */
+
+function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, recursed) {
+ // honor user specified sort order
+ const newOrder = [];
+ const sorting = options.sort && rawIds.length > 1;
+ let doc;
+ let sid;
+ let id;
+
+ for (let i = 0; i < rawIds.length; ++i) {
+ id = rawIds[i];
+
+ if (Array.isArray(id)) {
+ // handle [ [id0, id2], [id3] ]
+ assignRawDocsToIdStructure(id, resultDocs, resultOrder, options, true);
+ newOrder.push(id);
+ continue;
+ }
+
+ if (id === null && !sorting) {
+ // keep nulls for findOne unless sorting, which always
+ // removes them (backward compat)
+ newOrder.push(id);
+ continue;
+ }
+
+ sid = String(id);
+
+ doc = resultDocs[sid];
+ // If user wants separate copies of same doc, use this option
+ if (options.clone) {
+ doc = doc.constructor.hydrate(doc._doc);
+ }
+
+ if (recursed) {
+ if (doc) {
+ if (sorting) {
+ newOrder[resultOrder[sid]] = doc;
+ } else {
+ newOrder.push(doc);
+ }
+ } else {
+ newOrder.push(id);
+ }
+ } else {
+ // apply findOne behavior - if document in results, assign, else assign null
+ newOrder[i] = doc || null;
+ }
+ }
+
+ rawIds.length = 0;
+ if (newOrder.length) {
+ // reassign the documents based on corrected order
+
+ // forEach skips over sparse entries in arrays so we
+ // can safely use this to our advantage dealing with sorted
+ // result sets too.
+ newOrder.forEach(function(doc, i) {
+ rawIds[i] = doc;
+ });
+ }
+}
diff --git a/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js b/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
new file mode 100644
index 0000000..5231942
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
@@ -0,0 +1,181 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+const Mixed = require('../../schema/mixed');
+const get = require('../get');
+const leanPopulateMap = require('./leanPopulateMap');
+const mpath = require('mpath');
+
+/*!
+ * @param {Schema} schema
+ * @param {Object} doc POJO
+ * @param {string} path
+ */
+
+module.exports = function getSchemaTypes(schema, doc, path) {
+ const pathschema = schema.path(path);
+ const topLevelDoc = doc;
+
+ if (pathschema) {
+ return pathschema;
+ }
+
+ function search(parts, schema, subdoc, nestedPath) {
+ let p = parts.length + 1;
+ let foundschema;
+ let trypath;
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+
+ if (foundschema == null) {
+ continue;
+ }
+
+ if (foundschema.caster) {
+ // array of Mixed?
+ if (foundschema.caster instanceof Mixed) {
+ return foundschema.caster;
+ }
+
+ let schemas = null;
+ if (doc != null && foundschema.schema != null && foundschema.schema.discriminators != null) {
+ const discriminators = foundschema.schema.discriminators;
+ const discriminatorKeyPath = trypath + '.' +
+ foundschema.schema.options.discriminatorKey;
+ const keys = subdoc ? mpath.get(discriminatorKeyPath, subdoc) || [] : [];
+ schemas = Object.keys(discriminators).
+ reduce(function(cur, discriminator) {
+ if (keys.indexOf(discriminator) !== -1) {
+ cur.push(discriminators[discriminator]);
+ }
+ return cur;
+ }, []);
+ }
+
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ // If there is no foundschema.schema we are dealing with
+ // a path like array.$
+ if (p !== parts.length && foundschema.schema) {
+ let ret;
+ if (parts[p] === '$') {
+ if (p + 1 === parts.length) {
+ // comments.$
+ return foundschema;
+ }
+ // comments.$.comments.$.title
+ ret = search(
+ parts.slice(p + 1),
+ schema,
+ subdoc ? mpath.get(trypath, subdoc) : null,
+ nestedPath.concat(parts.slice(0, p))
+ );
+ if (ret) {
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
+ !foundschema.schema.$isSingleNested;
+ }
+ return ret;
+ }
+
+ if (schemas != null && schemas.length > 0) {
+ ret = [];
+ for (let i = 0; i < schemas.length; ++i) {
+ const _ret = search(
+ parts.slice(p),
+ schemas[i],
+ subdoc ? mpath.get(trypath, subdoc) : null,
+ nestedPath.concat(parts.slice(0, p))
+ );
+ if (_ret != null) {
+ _ret.$isUnderneathDocArray = _ret.$isUnderneathDocArray ||
+ !foundschema.schema.$isSingleNested;
+ if (_ret.$isUnderneathDocArray) {
+ ret.$isUnderneathDocArray = true;
+ }
+ ret.push(_ret);
+ }
+ }
+ return ret;
+ } else {
+ ret = search(
+ parts.slice(p),
+ foundschema.schema,
+ subdoc ? mpath.get(trypath, subdoc) : null,
+ nestedPath.concat(parts.slice(0, p))
+ );
+
+ if (ret) {
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
+ !foundschema.schema.$isSingleNested;
+ }
+
+ return ret;
+ }
+ }
+ }
+
+ const fullPath = nestedPath.concat([trypath]).join('.');
+ if (topLevelDoc.$__ && topLevelDoc.populated(fullPath) && p < parts.length) {
+ const schema = get(doc.$__.populated[fullPath], 'options.model.schema');
+ if (schema != null) {
+ const ret = search(
+ parts.slice(p),
+ schema,
+ subdoc ? mpath.get(trypath, subdoc) : null,
+ nestedPath.concat(parts.slice(0, p))
+ );
+
+ if (ret) {
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
+ !schema.$isSingleNested;
+ }
+
+ return ret;
+ }
+ }
+
+ const _val = get(topLevelDoc, trypath);
+ if (_val != null) {
+ const model = Array.isArray(_val) && _val.length > 0 ?
+ leanPopulateMap.get(_val[0]) :
+ leanPopulateMap.get(_val);
+ // Populated using lean, `leanPopulateMap` value is the foreign model
+ const schema = model != null ? model.schema : null;
+ if (schema != null) {
+ const ret = search(
+ parts.slice(p),
+ schema,
+ subdoc ? mpath.get(trypath, subdoc) : null,
+ nestedPath.concat(parts.slice(0, p))
+ );
+
+ if (ret) {
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
+ !schema.$isSingleNested;
+ }
+
+ return ret;
+ }
+ }
+
+ return foundschema;
+ }
+ }
+
+ // look for arrays
+ const parts = path.split('.');
+ for (let i = 0; i < parts.length; ++i) {
+ if (parts[i] === '$') {
+ // Re: gh-5628, because `schema.path()` doesn't take $ into account.
+ parts[i] = '0';
+ }
+ }
+ return search(parts, schema, doc, []);
+};
diff --git a/node_modules/mongoose/lib/helpers/populate/getVirtual.js b/node_modules/mongoose/lib/helpers/populate/getVirtual.js
new file mode 100644
index 0000000..dd2644d
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/getVirtual.js
@@ -0,0 +1,61 @@
+'use strict';
+
+module.exports = getVirtual;
+
+/*!
+ * ignore
+ */
+
+function getVirtual(schema, name) {
+ if (schema.virtuals[name]) {
+ return schema.virtuals[name];
+ }
+ const parts = name.split('.');
+ let cur = '';
+ let nestedSchemaPath = '';
+ for (let i = 0; i < parts.length; ++i) {
+ cur += (cur.length > 0 ? '.' : '') + parts[i];
+ if (schema.virtuals[cur]) {
+ if (i === parts.length - 1) {
+ schema.virtuals[cur].$nestedSchemaPath = nestedSchemaPath;
+ return schema.virtuals[cur];
+ }
+ continue;
+ }
+
+ if (schema.nested[cur]) {
+ continue;
+ }
+
+ if (schema.paths[cur] && schema.paths[cur].schema) {
+ schema = schema.paths[cur].schema;
+ const rest = parts.slice(i + 1).join('.');
+
+ if (schema.virtuals[rest]) {
+ if (i === parts.length - 2) {
+ schema.virtuals[rest].$nestedSchemaPath =
+ [nestedSchemaPath, cur].filter(v => !!v).join('.');
+ return schema.virtuals[rest];
+ }
+ continue;
+ }
+
+ if (i + 1 < parts.length && schema.discriminators) {
+ for (const key of Object.keys(schema.discriminators)) {
+ const _virtual = getVirtual(schema.discriminators[key], rest);
+ if (_virtual != null) {
+ _virtual.$nestedSchemaPath = [nestedSchemaPath, cur].
+ filter(v => !!v).join('.');
+ return _virtual;
+ }
+ }
+ }
+
+ nestedSchemaPath += (nestedSchemaPath.length > 0 ? '.' : '') + cur;
+ cur = '';
+ continue;
+ }
+
+ return null;
+ }
+}
diff --git a/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js b/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js
new file mode 100644
index 0000000..9ff9b13
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js
@@ -0,0 +1,7 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = new WeakMap();
diff --git a/node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js b/node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js
new file mode 100644
index 0000000..4287e7a
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js
@@ -0,0 +1,45 @@
+'use strict';
+
+module.exports = function normalizeRefPath(refPath, doc, populatedPath) {
+ if (refPath == null) {
+ return refPath;
+ }
+
+ if (typeof refPath === 'function') {
+ refPath = refPath.call(doc, doc, populatedPath);
+ }
+
+ // If populated path has numerics, the end `refPath` should too. For example,
+ // if populating `a.0.b` instead of `a.b` and `b` has `refPath = a.c`, we
+ // should return `a.0.c` for the refPath.
+ const hasNumericProp = /(\.\d+$|\.\d+\.)/g;
+
+ if (hasNumericProp.test(populatedPath)) {
+ const chunks = populatedPath.split(hasNumericProp);
+
+ if (chunks[chunks.length - 1] === '') {
+ throw new Error('Can\'t populate individual element in an array');
+ }
+
+ let _refPath = '';
+ let _remaining = refPath;
+ // 2nd, 4th, etc. will be numeric props. For example: `[ 'a', '.0.', 'b' ]`
+ for (let i = 0; i < chunks.length; i += 2) {
+ const chunk = chunks[i];
+ if (_remaining.startsWith(chunk + '.')) {
+ _refPath += _remaining.substr(0, chunk.length) + chunks[i + 1];
+ _remaining = _remaining.substr(chunk.length + 1);
+ } else if (i === chunks.length - 1) {
+ _refPath += _remaining;
+ _remaining = '';
+ break;
+ } else {
+ throw new Error('Could not normalize ref path, chunk ' + chunk + ' not in populated path');
+ }
+ }
+
+ return _refPath;
+ }
+
+ return refPath;
+};
diff --git a/node_modules/mongoose/lib/helpers/populate/validateRef.js b/node_modules/mongoose/lib/helpers/populate/validateRef.js
new file mode 100644
index 0000000..2b58e16
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/validateRef.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const MongooseError = require('../../error/mongooseError');
+const util = require('util');
+
+module.exports = validateRef;
+
+function validateRef(ref, path) {
+ if (typeof ref === 'string') {
+ return;
+ }
+
+ if (typeof ref === 'function') {
+ return;
+ }
+
+ throw new MongooseError('Invalid ref at path "' + path + '". Got ' +
+ util.inspect(ref, { depth: 0 }));
+}
diff --git a/node_modules/mongoose/lib/helpers/printJestWarning.js b/node_modules/mongoose/lib/helpers/printJestWarning.js
new file mode 100644
index 0000000..25d8831
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/printJestWarning.js
@@ -0,0 +1,8 @@
+'use strict';
+
+if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
+ console.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
+ 'with Jest\'s default jsdom test environment. Please make sure you read ' +
+ 'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
+ 'http://mongoosejs.com/docs/jest.html');
+}
diff --git a/node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js b/node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js
new file mode 100644
index 0000000..67dfb39
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js
@@ -0,0 +1,18 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function isDefiningProjection(val) {
+ if (val == null) {
+ // `undefined` or `null` become exclusive projections
+ return true;
+ }
+ if (typeof val === 'object') {
+ // Only cases where a value does **not** define whether the whole projection
+ // is inclusive or exclusive are `$meta` and `$slice`.
+ return !('$meta' in val) && !('$slice' in val);
+ }
+ return true;
+};
diff --git a/node_modules/mongoose/lib/helpers/projection/isExclusive.js b/node_modules/mongoose/lib/helpers/projection/isExclusive.js
new file mode 100644
index 0000000..8c64bc5
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/projection/isExclusive.js
@@ -0,0 +1,28 @@
+'use strict';
+
+const isDefiningProjection = require('./isDefiningProjection');
+
+/*!
+ * ignore
+ */
+
+module.exports = function isExclusive(projection) {
+ const keys = Object.keys(projection);
+ let ki = keys.length;
+ let exclude = null;
+
+ if (ki === 1 && keys[0] === '_id') {
+ exclude = !!projection[keys[ki]];
+ } else {
+ while (ki--) {
+ // Does this projection explicitly define inclusion/exclusion?
+ // Explicitly avoid `$meta` and `$slice`
+ if (keys[ki] !== '_id' && isDefiningProjection(projection[keys[ki]])) {
+ exclude = !projection[keys[ki]];
+ break;
+ }
+ }
+ }
+
+ return exclude;
+};
diff --git a/node_modules/mongoose/lib/helpers/projection/isInclusive.js b/node_modules/mongoose/lib/helpers/projection/isInclusive.js
new file mode 100644
index 0000000..7781d8c
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/projection/isInclusive.js
@@ -0,0 +1,34 @@
+'use strict';
+
+const isDefiningProjection = require('./isDefiningProjection');
+
+/*!
+ * ignore
+ */
+
+module.exports = function isInclusive(projection) {
+ if (projection == null) {
+ return false;
+ }
+
+ const props = Object.keys(projection);
+ const numProps = props.length;
+ if (numProps === 0) {
+ return false;
+ }
+
+ for (let i = 0; i < numProps; ++i) {
+ const prop = props[i];
+ // Plus paths can't define the projection (see gh-7050)
+ if (prop.charAt(0) === '+') {
+ continue;
+ }
+ // If field is truthy (1, true, etc.) and not an object, then this
+ // projection must be inclusive. If object, assume its $meta, $slice, etc.
+ if (isDefiningProjection(projection[prop]) && !!projection[prop]) {
+ return true;
+ }
+ }
+
+ return false;
+};
diff --git a/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js b/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js
new file mode 100644
index 0000000..fc2592c
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js
@@ -0,0 +1,35 @@
+'use strict';
+
+const isDefiningProjection = require('./isDefiningProjection');
+
+/*!
+ * Determines if `path` is excluded by `projection`
+ *
+ * @param {Object} projection
+ * @param {string} path
+ * @return {Boolean}
+ */
+
+module.exports = function isPathExcluded(projection, path) {
+ if (path === '_id') {
+ return projection._id === 0;
+ }
+
+ const paths = Object.keys(projection);
+ let type = null;
+
+ for (const _path of paths) {
+ if (isDefiningProjection(projection[_path])) {
+ type = projection[path] === 1 ? 'inclusive' : 'exclusive';
+ break;
+ }
+ }
+
+ if (type === 'inclusive') {
+ return projection[path] !== 1;
+ }
+ if (type === 'exclusive') {
+ return projection[path] === 0;
+ }
+ return false;
+};
diff --git a/node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js b/node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js
new file mode 100644
index 0000000..8a05fc9
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js
@@ -0,0 +1,28 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function isPathSelectedInclusive(fields, path) {
+ const chunks = path.split('.');
+ let cur = '';
+ let j;
+ let keys;
+ let numKeys;
+ for (let i = 0; i < chunks.length; ++i) {
+ cur += cur.length ? '.' : '' + chunks[i];
+ if (fields[cur]) {
+ keys = Object.keys(fields);
+ numKeys = keys.length;
+ for (j = 0; j < numKeys; ++j) {
+ if (keys[i].indexOf(cur + '.') === 0 && keys[i].indexOf(path) !== 0) {
+ continue;
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+};
diff --git a/node_modules/mongoose/lib/helpers/query/applyQueryMiddleware.js b/node_modules/mongoose/lib/helpers/query/applyQueryMiddleware.js
new file mode 100644
index 0000000..3a987c4
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/query/applyQueryMiddleware.js
@@ -0,0 +1,66 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = applyQueryMiddleware;
+
+/*!
+ * ignore
+ */
+
+applyQueryMiddleware.middlewareFunctions = [
+ 'count',
+ 'countDocuments',
+ 'deleteMany',
+ 'deleteOne',
+ 'estimatedDocumentCount',
+ 'find',
+ 'findOne',
+ 'findOneAndDelete',
+ 'findOneAndRemove',
+ 'findOneAndReplace',
+ 'findOneAndUpdate',
+ 'remove',
+ 'replaceOne',
+ 'update',
+ 'updateMany',
+ 'updateOne'
+];
+
+/*!
+ * Apply query middleware
+ *
+ * @param {Query} query constructor
+ * @param {Model} model
+ */
+
+function applyQueryMiddleware(Query, model) {
+ const kareemOptions = {
+ useErrorHandlers: true,
+ numCallbackParams: 1,
+ nullResultByDefault: true
+ };
+
+ const middleware = model.hooks.filter(hook => {
+ if (hook.name === 'updateOne') {
+ return hook.query == null || !!hook.query;
+ }
+ if (hook.name === 'remove') {
+ return !!hook.query;
+ }
+ return true;
+ });
+
+ // `update()` thunk has a different name because `_update` was already taken
+ Query.prototype._execUpdate = middleware.createWrapper('update',
+ Query.prototype._execUpdate, null, kareemOptions);
+
+ applyQueryMiddleware.middlewareFunctions.
+ filter(v => v !== 'update').
+ forEach(fn => {
+ Query.prototype[`_${fn}`] = middleware.createWrapper(fn,
+ Query.prototype[`_${fn}`], null, kareemOptions);
+ });
+}
diff --git a/node_modules/mongoose/lib/helpers/query/castUpdate.js b/node_modules/mongoose/lib/helpers/query/castUpdate.js
new file mode 100644
index 0000000..47cf6d4
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/query/castUpdate.js
@@ -0,0 +1,428 @@
+'use strict';
+
+const CastError = require('../../error/cast');
+const StrictModeError = require('../../error/strict');
+const ValidationError = require('../../error/validation');
+const castNumber = require('../../cast/number');
+const getEmbeddedDiscriminatorPath = require('./getEmbeddedDiscriminatorPath');
+const utils = require('../../utils');
+
+/*!
+ * Casts an update op based on the given schema
+ *
+ * @param {Schema} schema
+ * @param {Object} obj
+ * @param {Object} options
+ * @param {Boolean} [options.overwrite] defaults to false
+ * @param {Boolean|String} [options.strict] defaults to true
+ * @param {Query} context passed to setters
+ * @return {Boolean} true iff the update is non-empty
+ */
+
+module.exports = function castUpdate(schema, obj, options, context, filter) {
+ if (!obj) {
+ return undefined;
+ }
+
+ const ops = Object.keys(obj);
+ let i = ops.length;
+ const ret = {};
+ let hasKeys;
+ let val;
+ let hasDollarKey = false;
+ const overwrite = options.overwrite;
+
+ filter = filter || {};
+
+ while (i--) {
+ const op = ops[i];
+ // if overwrite is set, don't do any of the special $set stuff
+ if (op[0] !== '$' && !overwrite) {
+ // fix up $set sugar
+ if (!ret.$set) {
+ if (obj.$set) {
+ ret.$set = obj.$set;
+ } else {
+ ret.$set = {};
+ }
+ }
+ ret.$set[op] = obj[op];
+ ops.splice(i, 1);
+ if (!~ops.indexOf('$set')) ops.push('$set');
+ } else if (op === '$set') {
+ if (!ret.$set) {
+ ret[op] = obj[op];
+ }
+ } else {
+ ret[op] = obj[op];
+ }
+ }
+
+ // cast each value
+ i = ops.length;
+
+ // if we get passed {} for the update, we still need to respect that when it
+ // is an overwrite scenario
+ if (overwrite) {
+ hasKeys = true;
+ }
+
+ while (i--) {
+ const op = ops[i];
+ val = ret[op];
+ hasDollarKey = hasDollarKey || op.charAt(0) === '$';
+
+ if (val &&
+ typeof val === 'object' &&
+ !Buffer.isBuffer(val) &&
+ (!overwrite || hasDollarKey)) {
+ hasKeys |= walkUpdatePath(schema, val, op, options, context, filter);
+ } else if (overwrite && ret && typeof ret === 'object') {
+ // if we are just using overwrite, cast the query and then we will
+ // *always* return the value, even if it is an empty object. We need to
+ // set hasKeys above because we need to account for the case where the
+ // user passes {} and wants to clobber the whole document
+ // Also, _walkUpdatePath expects an operation, so give it $set since that
+ // is basically what we're doing
+ walkUpdatePath(schema, ret, '$set', options, context, filter);
+ } else {
+ const msg = 'Invalid atomic update value for ' + op + '. '
+ + 'Expected an object, received ' + typeof val;
+ throw new Error(msg);
+ }
+ }
+
+ return hasKeys && ret;
+};
+
+/*!
+ * Walk each path of obj and cast its values
+ * according to its schema.
+ *
+ * @param {Schema} schema
+ * @param {Object} obj - part of a query
+ * @param {String} op - the atomic operator ($pull, $set, etc)
+ * @param {Object} options
+ * @param {Boolean|String} [options.strict]
+ * @param {Boolean} [options.omitUndefined]
+ * @param {Query} context
+ * @param {String} pref - path prefix (internal only)
+ * @return {Bool} true if this path has keys to update
+ * @api private
+ */
+
+function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
+ const strict = options.strict;
+ const prefix = pref ? pref + '.' : '';
+ const keys = Object.keys(obj);
+ let i = keys.length;
+ let hasKeys = false;
+ let schematype;
+ let key;
+ let val;
+
+ let aggregatedError = null;
+
+ let useNestedStrict;
+ if (options.useNestedStrict === undefined) {
+ useNestedStrict = schema.options.useNestedStrict;
+ } else {
+ useNestedStrict = options.useNestedStrict;
+ }
+
+ while (i--) {
+ key = keys[i];
+ val = obj[key];
+
+ if (val && val.constructor.name === 'Object') {
+ // watch for embedded doc schemas
+ schematype = schema._getSchema(prefix + key);
+ if (schematype && schematype.caster && op in castOps) {
+ // embedded doc schema
+ if ('$each' in val) {
+ hasKeys = true;
+ try {
+ obj[key] = {
+ $each: castUpdateVal(schematype, val.$each, op, key, context, prefix + key)
+ };
+ } catch (error) {
+ aggregatedError = _handleCastError(error, context, key, aggregatedError);
+ }
+
+ if (val.$slice != null) {
+ obj[key].$slice = val.$slice | 0;
+ }
+
+ if (val.$sort) {
+ obj[key].$sort = val.$sort;
+ }
+
+ if (!!val.$position || val.$position === 0) {
+ obj[key].$position = val.$position;
+ }
+ } else {
+ try {
+ obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
+ } catch (error) {
+ aggregatedError = _handleCastError(error, context, key, aggregatedError);
+ }
+
+ if (options.omitUndefined && obj[key] === void 0) {
+ delete obj[key];
+ continue;
+ }
+
+ hasKeys = true;
+ }
+ } else if ((op === '$currentDate') || (op in castOps && schematype)) {
+ // $currentDate can take an object
+ try {
+ obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
+ } catch (error) {
+ aggregatedError = _handleCastError(error, context, key, aggregatedError);
+ }
+
+ if (options.omitUndefined && obj[key] === void 0) {
+ delete obj[key];
+ continue;
+ }
+
+ hasKeys = true;
+ } else {
+ const pathToCheck = (prefix + key);
+ const v = schema._getPathType(pathToCheck);
+ let _strict = strict;
+ if (useNestedStrict &&
+ v &&
+ v.schema &&
+ 'strict' in v.schema.options) {
+ _strict = v.schema.options.strict;
+ }
+
+ if (v.pathType === 'undefined') {
+ if (_strict === 'throw') {
+ throw new StrictModeError(pathToCheck);
+ } else if (_strict) {
+ delete obj[key];
+ continue;
+ }
+ }
+
+ // gh-2314
+ // we should be able to set a schema-less field
+ // to an empty object literal
+ hasKeys |= walkUpdatePath(schema, val, op, options, context, filter, prefix + key) ||
+ (utils.isObject(val) && Object.keys(val).length === 0);
+ }
+ } else {
+ const checkPath = (key === '$each' || key === '$or' || key === '$and' || key === '$in') ?
+ pref : prefix + key;
+ schematype = schema._getSchema(checkPath);
+ let pathDetails = schema._getPathType(checkPath);
+
+ // If no schema type, check for embedded discriminators
+ if (schematype == null) {
+ const _res = getEmbeddedDiscriminatorPath(schema, obj, filter, checkPath);
+ if (_res.schematype != null) {
+ schematype = _res.schematype;
+ pathDetails = _res.type;
+ }
+ }
+
+ let isStrict = strict;
+ if (useNestedStrict &&
+ pathDetails &&
+ pathDetails.schema &&
+ 'strict' in pathDetails.schema.options) {
+ isStrict = pathDetails.schema.options.strict;
+ }
+
+ const skip = isStrict &&
+ !schematype &&
+ !/real|nested/.test(pathDetails.pathType);
+
+ if (skip) {
+ // Even if strict is `throw`, avoid throwing an error because of
+ // virtuals because of #6731
+ if (isStrict === 'throw' && schema.virtuals[checkPath] == null) {
+ throw new StrictModeError(prefix + key);
+ } else {
+ delete obj[key];
+ }
+ } else {
+ // gh-1845 temporary fix: ignore $rename. See gh-3027 for tracking
+ // improving this.
+ if (op === '$rename') {
+ hasKeys = true;
+ continue;
+ }
+
+ try {
+ obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
+ } catch (error) {
+ aggregatedError = _handleCastError(error, context, key, aggregatedError);
+ }
+
+ if (Array.isArray(obj[key]) && (op === '$addToSet' || op === '$push') && key !== '$each') {
+ if (schematype && schematype.caster && !schematype.caster.$isMongooseArray) {
+ obj[key] = { $each: obj[key] };
+ }
+ }
+
+ if (options.omitUndefined && obj[key] === void 0) {
+ delete obj[key];
+ continue;
+ }
+
+ hasKeys = true;
+ }
+ }
+ }
+
+ if (aggregatedError != null) {
+ throw aggregatedError;
+ }
+
+ return hasKeys;
+}
+
+/*!
+ * ignore
+ */
+
+function _handleCastError(error, query, key, aggregatedError) {
+ if (typeof query !== 'object' || !query.options.multipleCastError) {
+ throw error;
+ }
+ aggregatedError = aggregatedError || new ValidationError();
+ aggregatedError.addError(key, error);
+ return aggregatedError;
+}
+
+/*!
+ * These operators should be cast to numbers instead
+ * of their path schema type.
+ */
+
+const numberOps = {
+ $pop: 1,
+ $inc: 1
+};
+
+/*!
+ * These ops require no casting because the RHS doesn't do anything.
+ */
+
+const noCastOps = {
+ $unset: 1
+};
+
+/*!
+ * These operators require casting docs
+ * to real Documents for Update operations.
+ */
+
+const castOps = {
+ $push: 1,
+ $addToSet: 1,
+ $set: 1,
+ $setOnInsert: 1
+};
+
+/*!
+ * ignore
+ */
+
+const overwriteOps = {
+ $set: 1,
+ $setOnInsert: 1
+};
+
+/*!
+ * Casts `val` according to `schema` and atomic `op`.
+ *
+ * @param {SchemaType} schema
+ * @param {Object} val
+ * @param {String} op - the atomic operator ($pull, $set, etc)
+ * @param {String} $conditional
+ * @param {Query} context
+ * @api private
+ */
+
+function castUpdateVal(schema, val, op, $conditional, context, path) {
+ if (!schema) {
+ // non-existing schema path
+ if (op in numberOps) {
+ try {
+ return castNumber(val);
+ } catch (err) {
+ throw new CastError('number', val, path);
+ }
+ }
+ return val;
+ }
+
+ const cond = schema.caster && op in castOps &&
+ (utils.isObject(val) || Array.isArray(val));
+ if (cond && op !== '$set') {
+ // Cast values for ops that add data to MongoDB.
+ // Ensures embedded documents get ObjectIds etc.
+ const tmp = schema.cast(val);
+ if (Array.isArray(val)) {
+ val = tmp;
+ } else if (Array.isArray(tmp)) {
+ val = tmp[0];
+ } else {
+ val = tmp;
+ }
+ return val;
+ } else if (cond && op === '$set') {
+ return schema.cast(val);
+ }
+
+ if (op in noCastOps) {
+ return val;
+ }
+ if (op in numberOps) {
+ // Null and undefined not allowed for $pop, $inc
+ if (val == null) {
+ throw new CastError('number', val, schema.path);
+ }
+ if (op === '$inc') {
+ // Support `$inc` with long, int32, etc. (gh-4283)
+ return schema.castForQueryWrapper({
+ val: val,
+ context: context
+ });
+ }
+ try {
+ return castNumber(val);
+ } catch (error) {
+ throw new CastError('number', val, schema.path);
+ }
+ }
+ if (op === '$currentDate') {
+ if (typeof val === 'object') {
+ return {$type: val.$type};
+ }
+ return Boolean(val);
+ }
+
+ if (/^\$/.test($conditional)) {
+ return schema.castForQueryWrapper({
+ $conditional: $conditional,
+ val: val,
+ context: context
+ });
+ }
+
+ if (overwriteOps[op]) {
+ return schema.castForQueryWrapper({
+ val: val,
+ context: context,
+ $skipQueryCastForUpdate: val != null && schema.$isMongooseArray && schema.$parentSchema
+ });
+ }
+
+ return schema.castForQueryWrapper({ val: val, context: context });
+}
diff --git a/node_modules/mongoose/lib/helpers/query/completeMany.js b/node_modules/mongoose/lib/helpers/query/completeMany.js
new file mode 100644
index 0000000..aabe596
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/query/completeMany.js
@@ -0,0 +1,47 @@
+'use strict';
+
+const helpers = require('../../queryhelpers');
+
+module.exports = completeMany;
+
+/*!
+ * Given a model and an array of docs, hydrates all the docs to be instances
+ * of the model. Used to initialize docs returned from the db from `find()`
+ *
+ * @param {Model} model
+ * @param {Array} docs
+ * @param {Object} fields the projection used, including `select` from schemas
+ * @param {Object} userProvidedFields the user-specified projection
+ * @param {Object} opts
+ * @param {Array} [opts.populated]
+ * @param {ClientSession} [opts.session]
+ * @param {Function} callback
+ */
+
+function completeMany(model, docs, fields, userProvidedFields, opts, callback) {
+ const arr = [];
+ let count = docs.length;
+ const len = count;
+ let error = null;
+
+ function init(_error) {
+ if (_error != null) {
+ error = error || _error;
+ }
+ if (error != null) {
+ --count || process.nextTick(() => callback(error));
+ return;
+ }
+ --count || process.nextTick(() => callback(error, arr));
+ }
+
+ for (let i = 0; i < len; ++i) {
+ arr[i] = helpers.createModel(model, docs[i], fields, userProvidedFields);
+ try {
+ arr[i].init(docs[i], opts, init);
+ } catch (error) {
+ init(error);
+ }
+ arr[i].$session(opts.session);
+ }
+}
diff --git a/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js b/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js
new file mode 100644
index 0000000..4d74864
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js
@@ -0,0 +1,53 @@
+'use strict';
+
+const get = require('../get');
+
+/*!
+ * Like `schema.path()`, except with a document, because impossible to
+ * determine path type without knowing the embedded discriminator key.
+ */
+
+module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, path) {
+ const parts = path.split('.');
+ let schematype = null;
+ let type = 'adhocOrUndefined';
+
+ filter = filter || {};
+ update = update || {};
+
+ for (let i = 0; i < parts.length; ++i) {
+ const subpath = parts.slice(0, i + 1).join('.').
+ replace(/\.\$\./i, '.0.').replace(/\.\$$/, '.0');
+ schematype = schema.path(subpath);
+ if (schematype == null) {
+ continue;
+ }
+ type = schema.pathType(subpath);
+ if ((schematype.$isSingleNested || schematype.$isMongooseDocumentArrayElement) &&
+ schematype.schema.discriminators != null) {
+ const discriminators = schematype.schema.discriminators;
+ const discriminatorValuePath = subpath + '.' +
+ get(schematype, 'schema.options.discriminatorKey');
+ const discriminatorFilterPath =
+ discriminatorValuePath.replace(/\.\d+\./, '.');
+ let discriminatorKey = null;
+ if (discriminatorValuePath in filter) {
+ discriminatorKey = filter[discriminatorValuePath];
+ }
+ if (discriminatorFilterPath in filter) {
+ discriminatorKey = filter[discriminatorFilterPath];
+ }
+ if (discriminatorKey == null || discriminators[discriminatorKey] == null) {
+ continue;
+ }
+ const rest = parts.slice(i + 1).join('.');
+ schematype = discriminators[discriminatorKey].path(rest);
+ if (schematype != null) {
+ type = discriminators[discriminatorKey]._getPathType(rest);
+ break;
+ }
+ }
+ }
+
+ return { type: type, schematype: schematype };
+};
diff --git a/node_modules/mongoose/lib/helpers/query/hasDollarKeys.js b/node_modules/mongoose/lib/helpers/query/hasDollarKeys.js
new file mode 100644
index 0000000..fb831da
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/query/hasDollarKeys.js
@@ -0,0 +1,16 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function(obj) {
+ const keys = Object.keys(obj);
+ const len = keys.length;
+ for (let i = 0; i < len; ++i) {
+ if (keys[i].charAt(0) === '$') {
+ return true;
+ }
+ }
+ return false;
+};
diff --git a/node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js b/node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js
new file mode 100644
index 0000000..15f7e99
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js
@@ -0,0 +1,46 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function selectPopulatedFields(query) {
+ const opts = query._mongooseOptions;
+
+ if (opts.populate != null) {
+ const paths = Object.keys(opts.populate);
+ const userProvidedFields = query._userProvidedFields || {};
+ if (query.selectedInclusively()) {
+ for (let i = 0; i < paths.length; ++i) {
+ if (!isPathInFields(userProvidedFields, paths[i])) {
+ query.select(paths[i]);
+ } else if (userProvidedFields[paths[i]] === 0) {
+ delete query._fields[paths[i]];
+ }
+ }
+ } else if (query.selectedExclusively()) {
+ for (let i = 0; i < paths.length; ++i) {
+ if (userProvidedFields[paths[i]] == null) {
+ delete query._fields[paths[i]];
+ }
+ }
+ }
+ }
+};
+
+/*!
+ * ignore
+ */
+
+function isPathInFields(userProvidedFields, path) {
+ const pieces = path.split('.');
+ const len = pieces.length;
+ let cur = pieces[0];
+ for (let i = 1; i < len; ++i) {
+ if (userProvidedFields[cur] != null) {
+ return true;
+ }
+ cur += '.' + pieces[i];
+ }
+ return userProvidedFields[cur] != null;
+}
diff --git a/node_modules/mongoose/lib/helpers/query/wrapThunk.js b/node_modules/mongoose/lib/helpers/query/wrapThunk.js
new file mode 100644
index 0000000..f72aee3
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/query/wrapThunk.js
@@ -0,0 +1,18 @@
+'use strict';
+
+/*!
+ * A query thunk is the function responsible for sending the query to MongoDB,
+ * like `Query#_findOne()` or `Query#_execUpdate()`. The `Query#exec()` function
+ * calls a thunk. The term "thunk" here is the traditional Node.js definition:
+ * a function that takes exactly 1 parameter, a callback.
+ *
+ * This function defines common behavior for all query thunks.
+ */
+
+module.exports = function wrapThunk(fn) {
+ return function _wrappedThunk(cb) {
+ ++this._executionCount;
+
+ fn.call(this, cb);
+ };
+};
diff --git a/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js b/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js
new file mode 100644
index 0000000..168156d
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js
@@ -0,0 +1,16 @@
+'use strict';
+
+const get = require('../get');
+
+module.exports = function applyWriteConcern(schema, options) {
+ const writeConcern = get(schema, 'options.writeConcern', {});
+ if (!('w' in options) && writeConcern.w != null) {
+ options.w = writeConcern.w;
+ }
+ if (!('j' in options) && writeConcern.j != null) {
+ options.j = writeConcern.j;
+ }
+ if (!('wtimeout' in options) && writeConcern.wtimeout != null) {
+ options.wtimeout = writeConcern.wtimeout;
+ }
+};
diff --git a/node_modules/mongoose/lib/helpers/schema/getIndexes.js b/node_modules/mongoose/lib/helpers/schema/getIndexes.js
new file mode 100644
index 0000000..43b82e7
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/schema/getIndexes.js
@@ -0,0 +1,124 @@
+'use strict';
+
+const get = require('../get');
+const utils = require('../../utils');
+
+/*!
+ * Gather all indexes defined in the schema, including single nested,
+ * document arrays, and embedded discriminators.
+ */
+
+module.exports = function getIndexes(schema) {
+ let indexes = [];
+ const schemaStack = new WeakMap();
+ const indexTypes = schema.constructor.indexTypes;
+
+ const collectIndexes = function(schema, prefix) {
+ // Ignore infinitely nested schemas, if we've already seen this schema
+ // along this path there must be a cycle
+ if (schemaStack.has(schema)) {
+ return;
+ }
+ schemaStack.set(schema, true);
+
+ prefix = prefix || '';
+ const keys = Object.keys(schema.paths);
+ const length = keys.length;
+
+ for (let i = 0; i < length; ++i) {
+ const key = keys[i];
+ const path = schema.paths[key];
+
+ if (path.$isMongooseDocumentArray || path.$isSingleNested) {
+ if (get(path, 'options.excludeIndexes') !== true &&
+ get(path, 'schemaOptions.excludeIndexes') !== true) {
+ collectIndexes(path.schema, prefix + key + '.');
+ }
+
+ if (path.schema.discriminators != null) {
+ const discriminators = path.schema.discriminators;
+ const discriminatorKeys = Object.keys(discriminators);
+ for (const discriminatorKey of discriminatorKeys) {
+ collectIndexes(discriminators[discriminatorKey]._originalSchema,
+ prefix + key + '.');
+ }
+ }
+
+ // Retained to minimize risk of backwards breaking changes due to
+ // gh-6113
+ if (path.$isMongooseDocumentArray) {
+ continue;
+ }
+ }
+
+ const index = path._index || (path.caster && path.caster._index);
+
+ if (index !== false && index !== null && index !== undefined) {
+ const field = {};
+ const isObject = utils.isObject(index);
+ const options = isObject ? index : {};
+ const type = typeof index === 'string' ? index :
+ isObject ? index.type :
+ false;
+
+ if (type && indexTypes.indexOf(type) !== -1) {
+ field[prefix + key] = type;
+ } else if (options.text) {
+ field[prefix + key] = 'text';
+ delete options.text;
+ } else {
+ field[prefix + key] = 1;
+ }
+
+ delete options.type;
+ if (!('background' in options)) {
+ options.background = true;
+ }
+
+ indexes.push([field, options]);
+ }
+ }
+
+ schemaStack.delete(schema);
+
+ if (prefix) {
+ fixSubIndexPaths(schema, prefix);
+ } else {
+ schema._indexes.forEach(function(index) {
+ if (!('background' in index[1])) {
+ index[1].background = true;
+ }
+ });
+ indexes = indexes.concat(schema._indexes);
+ }
+ };
+
+ collectIndexes(schema);
+ return indexes;
+
+ /*!
+ * Checks for indexes added to subdocs using Schema.index().
+ * These indexes need their paths prefixed properly.
+ *
+ * schema._indexes = [ [indexObj, options], [indexObj, options] ..]
+ */
+
+ function fixSubIndexPaths(schema, prefix) {
+ const subindexes = schema._indexes;
+ const len = subindexes.length;
+ for (let i = 0; i < len; ++i) {
+ const indexObj = subindexes[i][0];
+ const keys = Object.keys(indexObj);
+ const klen = keys.length;
+ const newindex = {};
+
+ // use forward iteration, order matters
+ for (let j = 0; j < klen; ++j) {
+ const key = keys[j];
+ newindex[prefix + key] = indexObj[key];
+ }
+
+ indexes.push([newindex, subindexes[i][1]]);
+ }
+ }
+};
diff --git a/node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js b/node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js
new file mode 100644
index 0000000..0f9c30c
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js
@@ -0,0 +1,24 @@
+'use strict';
+
+module.exports = handleTimestampOption;
+
+/*!
+ * ignore
+ */
+
+function handleTimestampOption(arg, prop) {
+ if (arg == null) {
+ return null;
+ }
+
+ if (typeof arg === 'boolean') {
+ return prop;
+ }
+ if (typeof arg[prop] === 'boolean') {
+ return arg[prop] ? prop : null;
+ }
+ if (!(prop in arg)) {
+ return prop;
+ }
+ return arg[prop];
+}
diff --git a/node_modules/mongoose/lib/helpers/schema/merge.js b/node_modules/mongoose/lib/helpers/schema/merge.js
new file mode 100644
index 0000000..321c78f
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/schema/merge.js
@@ -0,0 +1,19 @@
+'use strict';
+
+module.exports = function merge(s1, s2) {
+ s1.add(s2.obj);
+
+ s1.callQueue = s1.callQueue.concat(s2.callQueue);
+ s1.method(s2.methods);
+ s1.static(s2.statics);
+
+ for (const query in s2.query) {
+ s1.query[query] = s2.query[query];
+ }
+
+ for (const virtual in s2.virtuals) {
+ s1.virtual[virtual] = s2.virtual[virtual].clone();
+ }
+
+ s1.s.hooks.merge(s2.s.hooks, false);
+};
diff --git a/node_modules/mongoose/lib/helpers/schema/setParentPointers.js b/node_modules/mongoose/lib/helpers/schema/setParentPointers.js
new file mode 100644
index 0000000..094bdf0
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/schema/setParentPointers.js
@@ -0,0 +1,38 @@
+'use strict';
+
+/*!
+ * Set `$parentSchema` on all schema types, and `$schemaType` on single
+ * nested docs.
+ *
+ * This is a slow path function, should only run when model is compiled
+ */
+
+module.exports = function setParentPointers(schema, skipRecursion) {
+ for (const path of Object.keys(schema.paths)) {
+ const schemaType = schema.paths[path];
+ if (schemaType.schema != null) {
+ Object.defineProperty(schemaType.schema, '$schemaType', {
+ configurable: true,
+ writable: false,
+ enumerable: false,
+ value: schemaType
+ });
+ }
+ Object.defineProperty(schemaType, '$parentSchema', {
+ configurable: true,
+ writable: false,
+ enumerable: false,
+ value: schema
+ });
+ }
+
+ // `childSchemas` contains all descendant schemas, so no need to recurse
+ // further.
+ if (skipRecursion) {
+ return;
+ }
+
+ for (const obj of schema.childSchemas) {
+ setParentPointers(obj.schema, true);
+ }
+};
diff --git a/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js b/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js
new file mode 100644
index 0000000..43642ad
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js
@@ -0,0 +1,117 @@
+'use strict';
+
+const modifiedPaths = require('./common').modifiedPaths;
+
+/**
+ * Applies defaults to update and findOneAndUpdate operations.
+ *
+ * @param {Object} filter
+ * @param {Schema} schema
+ * @param {Object} castedDoc
+ * @param {Object} options
+ * @method setDefaultsOnInsert
+ * @api private
+ */
+
+module.exports = function(filter, schema, castedDoc, options) {
+ const keys = Object.keys(castedDoc || {});
+ const updatedKeys = {};
+ const updatedValues = {};
+ const numKeys = keys.length;
+ const modified = {};
+
+ let hasDollarUpdate = false;
+
+ options = options || {};
+
+ if (!options.upsert || !options.setDefaultsOnInsert) {
+ return castedDoc;
+ }
+
+ for (let i = 0; i < numKeys; ++i) {
+ if (keys[i].charAt(0) === '$') {
+ modifiedPaths(castedDoc[keys[i]], '', modified);
+ hasDollarUpdate = true;
+ }
+ }
+
+ if (!hasDollarUpdate) {
+ modifiedPaths(castedDoc, '', modified);
+ }
+
+ const paths = Object.keys(filter);
+ const numPaths = paths.length;
+ for (let i = 0; i < numPaths; ++i) {
+ const path = paths[i];
+ const condition = filter[path];
+ if (condition && typeof condition === 'object') {
+ const conditionKeys = Object.keys(condition);
+ const numConditionKeys = conditionKeys.length;
+ let hasDollarKey = false;
+ for (let j = 0; j < numConditionKeys; ++j) {
+ if (conditionKeys[j].charAt(0) === '$') {
+ hasDollarKey = true;
+ break;
+ }
+ }
+ if (hasDollarKey) {
+ continue;
+ }
+ }
+ updatedKeys[path] = true;
+ modified[path] = true;
+ }
+
+ if (options && options.overwrite && !hasDollarUpdate) {
+ // Defaults will be set later, since we're overwriting we'll cast
+ // the whole update to a document
+ return castedDoc;
+ }
+
+ schema.eachPath(function(path, schemaType) {
+ if (schemaType.$isSingleNested) {
+ // Only handle nested schemas 1-level deep to avoid infinite
+ // recursion re: https://github.com/mongodb-js/mongoose-autopopulate/issues/11
+ schemaType.schema.eachPath(function(_path, _schemaType) {
+ if (_path === '_id' && _schemaType.auto) {
+ // Ignore _id if auto id so we don't create subdocs
+ return;
+ }
+
+ const def = _schemaType.getDefault(null, true);
+ if (!isModified(modified, path + '.' + _path) &&
+ typeof def !== 'undefined') {
+ castedDoc = castedDoc || {};
+ castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
+ castedDoc.$setOnInsert[path + '.' + _path] = def;
+ updatedValues[path + '.' + _path] = def;
+ }
+ });
+ } else {
+ const def = schemaType.getDefault(null, true);
+ if (!isModified(modified, path) && typeof def !== 'undefined') {
+ castedDoc = castedDoc || {};
+ castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
+ castedDoc.$setOnInsert[path] = def;
+ updatedValues[path] = def;
+ }
+ }
+ });
+
+ return castedDoc;
+};
+
+function isModified(modified, path) {
+ if (modified[path]) {
+ return true;
+ }
+ const sp = path.split('.');
+ let cur = sp[0];
+ for (let i = 1; i < sp.length; ++i) {
+ if (modified[cur]) {
+ return true;
+ }
+ cur += '.' + sp[i];
+ }
+ return false;
+}
diff --git a/node_modules/mongoose/lib/helpers/symbols.js b/node_modules/mongoose/lib/helpers/symbols.js
new file mode 100644
index 0000000..319785e
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/symbols.js
@@ -0,0 +1,11 @@
+'use strict';
+
+exports.validatorErrorSymbol = Symbol.for('mongoose:validatorError');
+
+exports.documentArrayParent = Symbol.for('mongoose:documentArrayParent');
+
+exports.modelSymbol = Symbol.for('mongoose#Model');
+
+exports.getSymbol = Symbol.for('mongoose#Document#get');
+
+exports.objectIdSymbol = Symbol.for('mongoose#ObjectId');
diff --git a/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js b/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js
new file mode 100644
index 0000000..5f85313
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js
@@ -0,0 +1,173 @@
+'use strict';
+
+const handleTimestampOption = require('../schema/handleTimestampOption');
+
+module.exports = applyTimestampsToChildren;
+
+/*!
+ * ignore
+ */
+
+function applyTimestampsToChildren(now, update, schema) {
+ if (update == null) {
+ return;
+ }
+
+ const keys = Object.keys(update);
+ let key;
+ let createdAt;
+ let updatedAt;
+ let timestamps;
+ let path;
+
+ const hasDollarKey = keys.length && keys[0].charAt(0) === '$';
+
+ if (hasDollarKey) {
+ if (update.$push) {
+ for (key in update.$push) {
+ const $path = schema.path(key);
+ if (update.$push[key] &&
+ $path &&
+ $path.$isMongooseDocumentArray &&
+ $path.schema.options.timestamps) {
+ timestamps = $path.schema.options.timestamps;
+ createdAt = handleTimestampOption(timestamps, 'createdAt');
+ updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+ if (update.$push[key].$each) {
+ update.$push[key].$each.forEach(function(subdoc) {
+ if (updatedAt != null) {
+ subdoc[updatedAt] = now;
+ }
+ if (createdAt != null) {
+ subdoc[createdAt] = now;
+ }
+ });
+ } else {
+ if (updatedAt != null) {
+ update.$push[key][updatedAt] = now;
+ }
+ if (createdAt != null) {
+ update.$push[key][createdAt] = now;
+ }
+ }
+ }
+ }
+ }
+ if (update.$set != null) {
+ const keys = Object.keys(update.$set);
+ for (key of keys) {
+ // Replace positional operator `$` and array filters `$[]` and `$[.*]`
+ const keyToSearch = key.
+ replace(/\.\$(\[[^\]]*\])?\./g, '.0.').
+ replace(/\.(\[[^\]]*\])?\$$/, '.0');
+ path = schema.path(keyToSearch);
+ if (!path) {
+ continue;
+ }
+ if (Array.isArray(update.$set[key]) && path.$isMongooseDocumentArray) {
+ applyTimestampsToDocumentArray(update.$set[key], path, now);
+ } else if (update.$set[key] && path.$isSingleNested) {
+ applyTimestampsToSingleNested(update.$set[key], path, now);
+ } else if (path.$parentSchema !== schema && path.$parentSchema != null) {
+ const parentPath = path.$parentSchema.$schemaType;
+
+ if (parentPath == null) {
+ continue;
+ }
+
+ timestamps = parentPath.schema.options.timestamps;
+ createdAt = handleTimestampOption(timestamps, 'createdAt');
+ updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+
+ if (updatedAt == null) {
+ continue;
+ }
+
+ if (parentPath.$isSingleNested) {
+ // Single nested is easy
+ update.$set[parentPath.path + '.' + updatedAt] = now;
+ continue;
+ }
+
+ let childPath = key.substr(parentPath.path.length + 1);
+ const firstDot = childPath.indexOf('.');
+
+ // Shouldn't happen, but if it does ignore this path
+ if (firstDot === -1) {
+ continue;
+ }
+
+ childPath = childPath.substr(0, firstDot);
+
+ update.$set[parentPath.path + '.' + childPath + '.' + updatedAt] = now;
+ } else if (path.schema != null && path.schema != schema) {
+ timestamps = path.schema.options.timestamps;
+ createdAt = handleTimestampOption(timestamps, 'createdAt');
+ updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+
+ if (updatedAt != null) {
+ update.$set[key][updatedAt] = now;
+ }
+ if (createdAt != null) {
+ update.$set[key][createdAt] = now;
+ }
+ }
+ }
+ }
+ } else {
+ const keys = Object.keys(update).filter(key => !key.startsWith('$'));
+ for (key of keys) {
+ // Replace positional operator `$` and array filters `$[]` and `$[.*]`
+ const keyToSearch = key.
+ replace(/\.\$(\[[^\]]*\])?\./g, '.0.').
+ replace(/\.(\[[^\]]*\])?\$$/, '.0');
+ path = schema.path(keyToSearch);
+ if (!path) {
+ continue;
+ }
+
+ if (Array.isArray(update[key]) && path.$isMongooseDocumentArray) {
+ applyTimestampsToDocumentArray(update[key], path, now);
+ } else if (update[key] != null && path.$isSingleNested) {
+ applyTimestampsToSingleNested(update[key], path, now);
+ }
+ }
+ }
+}
+
+function applyTimestampsToDocumentArray(arr, schematype, now) {
+ const timestamps = schematype.schema.options.timestamps;
+
+ if (!timestamps) {
+ return;
+ }
+
+ const len = arr.length;
+
+ const createdAt = handleTimestampOption(timestamps, 'createdAt');
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+ for (let i = 0; i < len; ++i) {
+ if (updatedAt != null) {
+ arr[i][updatedAt] = now;
+ }
+ if (createdAt != null) {
+ arr[i][createdAt] = now;
+ }
+ }
+}
+
+function applyTimestampsToSingleNested(subdoc, schematype, now) {
+ const timestamps = schematype.schema.options.timestamps;
+ if (!timestamps) {
+ return;
+ }
+
+ const createdAt = handleTimestampOption(timestamps, 'createdAt');
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+ if (updatedAt != null) {
+ subdoc[updatedAt] = now;
+ }
+ if (createdAt != null) {
+ subdoc[createdAt] = now;
+ }
+}
diff --git a/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js b/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js
new file mode 100644
index 0000000..85cef1a
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js
@@ -0,0 +1,65 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+const get = require('../get');
+
+module.exports = applyTimestampsToUpdate;
+
+/*!
+ * ignore
+ */
+
+function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options) {
+ const updates = currentUpdate;
+ let _updates = updates;
+ const overwrite = get(options, 'overwrite', false);
+ const timestamps = get(options, 'timestamps', true);
+
+ // Support skipping timestamps at the query level, see gh-6980
+ if (!timestamps || updates == null) {
+ return currentUpdate;
+ }
+
+ if (overwrite) {
+ if (currentUpdate && currentUpdate.$set) {
+ currentUpdate = currentUpdate.$set;
+ updates.$set = {};
+ _updates = updates.$set;
+ }
+ if (updatedAt && !currentUpdate[updatedAt]) {
+ _updates[updatedAt] = now;
+ }
+ if (createdAt && !currentUpdate[createdAt]) {
+ _updates[createdAt] = now;
+ }
+ return updates;
+ }
+ updates.$set = updates.$set || {};
+ currentUpdate = currentUpdate || {};
+
+ if (updatedAt &&
+ (!currentUpdate.$currentDate || !currentUpdate.$currentDate[updatedAt])) {
+ updates.$set[updatedAt] = now;
+ }
+
+ if (createdAt) {
+ if (currentUpdate[createdAt]) {
+ delete currentUpdate[createdAt];
+ }
+ if (currentUpdate.$set && currentUpdate.$set[createdAt]) {
+ delete currentUpdate.$set[createdAt];
+ }
+
+ updates.$setOnInsert = updates.$setOnInsert || {};
+ updates.$setOnInsert[createdAt] = now;
+ }
+
+ if (Object.keys(updates.$set).length === 0) {
+ delete updates.$set;
+ }
+
+ return updates;
+}
diff --git a/node_modules/mongoose/lib/helpers/update/modifiedPaths.js b/node_modules/mongoose/lib/helpers/update/modifiedPaths.js
new file mode 100644
index 0000000..9cb567d
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/modifiedPaths.js
@@ -0,0 +1,33 @@
+'use strict';
+
+const _modifiedPaths = require('../common').modifiedPaths;
+
+/**
+ * Given an update document with potential update operators (`$set`, etc.)
+ * returns an object whose keys are the directly modified paths.
+ *
+ * If there are any top-level keys that don't start with `$`, we assume those
+ * will get wrapped in a `$set`. The Mongoose Query is responsible for wrapping
+ * top-level keys in `$set`.
+ *
+ * @param {Object} update
+ * @return {Object} modified
+ */
+
+module.exports = function modifiedPaths(update) {
+ const keys = Object.keys(update);
+ const res = {};
+
+ const withoutDollarKeys = {};
+ for (const key of keys) {
+ if (key.startsWith('$')) {
+ _modifiedPaths(update[key], '', res);
+ continue;
+ }
+ withoutDollarKeys[key] = update[key];
+ }
+
+ _modifiedPaths(withoutDollarKeys, '', res);
+
+ return res;
+};
diff --git a/node_modules/mongoose/lib/helpers/updateValidators.js b/node_modules/mongoose/lib/helpers/updateValidators.js
new file mode 100644
index 0000000..5c790fb
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/updateValidators.js
@@ -0,0 +1,227 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const Mixed = require('../schema/mixed');
+const ValidationError = require('../error/validation');
+const flatten = require('./common').flatten;
+const modifiedPaths = require('./common').modifiedPaths;
+const parallel = require('async/parallel');
+
+/**
+ * Applies validators and defaults to update and findOneAndUpdate operations,
+ * specifically passing a null doc as `this` to validators and defaults
+ *
+ * @param {Query} query
+ * @param {Schema} schema
+ * @param {Object} castedDoc
+ * @param {Object} options
+ * @method runValidatorsOnUpdate
+ * @api private
+ */
+
+module.exports = function(query, schema, castedDoc, options) {
+ let _keys;
+ const keys = Object.keys(castedDoc || {});
+ let updatedKeys = {};
+ let updatedValues = {};
+ const isPull = {};
+ const arrayAtomicUpdates = {};
+ const numKeys = keys.length;
+ let hasDollarUpdate = false;
+ const modified = {};
+ let currentUpdate;
+ let key;
+ let i;
+
+ for (i = 0; i < numKeys; ++i) {
+ if (keys[i].charAt(0) === '$') {
+ hasDollarUpdate = true;
+ if (keys[i] === '$push' || keys[i] === '$addToSet') {
+ _keys = Object.keys(castedDoc[keys[i]]);
+ for (let ii = 0; ii < _keys.length; ++ii) {
+ currentUpdate = castedDoc[keys[i]][_keys[ii]];
+ if (currentUpdate && currentUpdate.$each) {
+ arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
+ concat(currentUpdate.$each);
+ } else {
+ arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
+ concat([currentUpdate]);
+ }
+ }
+ continue;
+ }
+ modifiedPaths(castedDoc[keys[i]], '', modified);
+ const flat = flatten(castedDoc[keys[i]]);
+ const paths = Object.keys(flat);
+ const numPaths = paths.length;
+ for (let j = 0; j < numPaths; ++j) {
+ let updatedPath = paths[j].replace('.$.', '.0.');
+ updatedPath = updatedPath.replace(/\.\$$/, '.0');
+ key = keys[i];
+ // With `$pull` we might flatten `$in`. Skip stuff nested under `$in`
+ // for the rest of the logic, it will get handled later.
+ if (updatedPath.indexOf('$') !== -1) {
+ continue;
+ }
+ if (key === '$set' || key === '$setOnInsert' ||
+ key === '$pull' || key === '$pullAll') {
+ updatedValues[updatedPath] = flat[paths[j]];
+ isPull[updatedPath] = key === '$pull' || key === '$pullAll';
+ } else if (key === '$unset') {
+ updatedValues[updatedPath] = undefined;
+ }
+ updatedKeys[updatedPath] = true;
+ }
+ }
+ }
+
+ if (!hasDollarUpdate) {
+ modifiedPaths(castedDoc, '', modified);
+ updatedValues = flatten(castedDoc);
+ updatedKeys = Object.keys(updatedValues);
+ }
+
+ const updates = Object.keys(updatedValues);
+ const numUpdates = updates.length;
+ const validatorsToExecute = [];
+ const validationErrors = [];
+
+ const alreadyValidated = [];
+
+ const context = options && options.context === 'query' ? query : null;
+ function iter(i, v) {
+ const schemaPath = schema._getSchema(updates[i]);
+ if (schemaPath) {
+ // gh-4305: `_getSchema()` will report all sub-fields of a 'Mixed' path
+ // as 'Mixed', so avoid double validating them.
+ if (schemaPath instanceof Mixed && schemaPath.$fullPath !== updates[i]) {
+ return;
+ }
+
+ if (v && Array.isArray(v.$in)) {
+ v.$in.forEach((v, i) => {
+ validatorsToExecute.push(function(callback) {
+ schemaPath.doValidate(
+ v,
+ function(err) {
+ if (err) {
+ err.path = updates[i] + '.$in.' + i;
+ validationErrors.push(err);
+ }
+ callback(null);
+ },
+ context,
+ {updateValidator: true});
+ });
+ });
+ } else {
+ if (isPull[updates[i]] &&
+ !Array.isArray(v) &&
+ schemaPath.$isMongooseArray) {
+ v = [v];
+ }
+
+ if (schemaPath.$isMongooseDocumentArrayElement && v != null && v.$__ != null) {
+ alreadyValidated.push(updates[i]);
+ validatorsToExecute.push(function(callback) {
+ schemaPath.doValidate(v, function(err) {
+ if (err) {
+ err.path = updates[i];
+ validationErrors.push(err);
+ return callback(null);
+ }
+
+ v.validate(function(err) {
+ if (err) {
+ if (err.errors) {
+ for (const key of Object.keys(err.errors)) {
+ const _err = err.errors[key];
+ _err.path = updates[i] + '.' + key;
+ validationErrors.push(_err);
+ }
+ }
+ }
+ callback(null);
+ });
+ }, context, { updateValidator: true });
+ });
+ } else {
+ validatorsToExecute.push(function(callback) {
+ for (const path of alreadyValidated) {
+ if (updates[i].startsWith(path + '.')) {
+ return callback(null);
+ }
+ }
+ schemaPath.doValidate(v, function(err) {
+ if (err) {
+ err.path = updates[i];
+ validationErrors.push(err);
+ }
+ callback(null);
+ }, context, { updateValidator: true });
+ });
+ }
+ }
+ }
+ }
+ for (i = 0; i < numUpdates; ++i) {
+ iter(i, updatedValues[updates[i]]);
+ }
+
+ const arrayUpdates = Object.keys(arrayAtomicUpdates);
+ const numArrayUpdates = arrayUpdates.length;
+ for (i = 0; i < numArrayUpdates; ++i) {
+ (function(i) {
+ let schemaPath = schema._getSchema(arrayUpdates[i]);
+ if (schemaPath && schemaPath.$isMongooseDocumentArray) {
+ validatorsToExecute.push(function(callback) {
+ schemaPath.doValidate(
+ arrayAtomicUpdates[arrayUpdates[i]],
+ function(err) {
+ if (err) {
+ err.path = arrayUpdates[i];
+ validationErrors.push(err);
+ }
+ callback(null);
+ },
+ options && options.context === 'query' ? query : null);
+ });
+ } else {
+ schemaPath = schema._getSchema(arrayUpdates[i] + '.0');
+ for (let j = 0; j < arrayAtomicUpdates[arrayUpdates[i]].length; ++j) {
+ (function(j) {
+ validatorsToExecute.push(function(callback) {
+ schemaPath.doValidate(
+ arrayAtomicUpdates[arrayUpdates[i]][j],
+ function(err) {
+ if (err) {
+ err.path = arrayUpdates[i];
+ validationErrors.push(err);
+ }
+ callback(null);
+ },
+ options && options.context === 'query' ? query : null,
+ { updateValidator: true });
+ });
+ })(j);
+ }
+ }
+ })(i);
+ }
+
+ return function(callback) {
+ parallel(validatorsToExecute, function() {
+ if (validationErrors.length) {
+ const err = new ValidationError(null);
+ for (let i = 0; i < validationErrors.length; ++i) {
+ err.addError(validationErrors[i].path, validationErrors[i]);
+ }
+ return callback(err);
+ }
+ callback(null);
+ });
+ };
+};
diff --git a/node_modules/mongoose/lib/index.js b/node_modules/mongoose/lib/index.js
new file mode 100644
index 0000000..cb0deb3
--- /dev/null
+++ b/node_modules/mongoose/lib/index.js
@@ -0,0 +1,956 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+if (global.MONGOOSE_DRIVER_PATH) {
+ require('./driver').set(require(global.MONGOOSE_DRIVER_PATH));
+} else {
+ require('./driver').set(require('./drivers/node-mongodb-native'));
+}
+
+const Schema = require('./schema');
+const SchemaType = require('./schematype');
+const SchemaTypes = require('./schema/index');
+const VirtualType = require('./virtualtype');
+const STATES = require('./connectionstate');
+const Types = require('./types');
+const Query = require('./query');
+const Model = require('./model');
+const Document = require('./document');
+const legacyPluralize = require('mongoose-legacy-pluralize');
+const utils = require('./utils');
+const pkg = require('../package.json');
+
+const removeSubdocs = require('./plugins/removeSubdocs');
+const saveSubdocs = require('./plugins/saveSubdocs');
+const validateBeforeSave = require('./plugins/validateBeforeSave');
+
+const Aggregate = require('./aggregate');
+const PromiseProvider = require('./promise_provider');
+const shardingPlugin = require('./plugins/sharding');
+
+const defaultMongooseSymbol = Symbol.for('mongoose:default');
+
+require('./helpers/printJestWarning');
+
+/**
+ * Mongoose constructor.
+ *
+ * The exports object of the `mongoose` module is an instance of this class.
+ * Most apps will only use this one instance.
+ *
+ * @api public
+ */
+
+function Mongoose(options) {
+ this.connections = [];
+ this.models = {};
+ this.modelSchemas = {};
+ // default global options
+ this.options = {
+ pluralization: true
+ };
+ const conn = this.createConnection(); // default connection
+ conn.models = this.models;
+
+ this._pluralize = legacyPluralize;
+
+ // If a user creates their own Mongoose instance, give them a separate copy
+ // of the `Schema` constructor so they get separate custom types. (gh-6933)
+ if (!options || !options[defaultMongooseSymbol]) {
+ const _this = this;
+ this.Schema = function() {
+ this.base = _this;
+ return Schema.apply(this, arguments);
+ };
+ this.Schema.prototype = Object.create(Schema.prototype);
+
+ Object.assign(this.Schema, Schema);
+ this.Schema.base = this;
+ this.Schema.Types = Object.assign({}, Schema.Types);
+ } else {
+ // Hack to work around babel's strange behavior with
+ // `import mongoose, { Schema } from 'mongoose'`. Because `Schema` is not
+ // an own property of a Mongoose global, Schema will be undefined. See gh-5648
+ for (const key of ['Schema', 'model']) {
+ this[key] = Mongoose.prototype[key];
+ }
+ }
+ this.Schema.prototype.base = this;
+
+ Object.defineProperty(this, 'plugins', {
+ configurable: false,
+ enumerable: true,
+ writable: false,
+ value: [
+ [saveSubdocs, { deduplicate: true }],
+ [validateBeforeSave, { deduplicate: true }],
+ [shardingPlugin, { deduplicate: true }],
+ [removeSubdocs, { deduplicate: true }]
+ ]
+ });
+}
+
+/**
+ * Expose connection states for user-land
+ *
+ * @memberOf Mongoose
+ * @property STATES
+ * @api public
+ */
+Mongoose.prototype.STATES = STATES;
+
+/**
+ * Sets mongoose options
+ *
+ * ####Example:
+ *
+ * mongoose.set('test', value) // sets the 'test' option to `value`
+ *
+ * mongoose.set('debug', true) // enable logging collection methods + arguments to the console
+ *
+ * mongoose.set('debug', function(collectionName, methodName, arg1, arg2...) {}); // use custom function to log collection methods + arguments
+ *
+ * Currently supported options are:
+ * - 'debug': prints the operations mongoose sends to MongoDB to the console
+ * - 'bufferCommands': enable/disable mongoose's buffering mechanism for all connections and models
+ * - 'useCreateIndex': false by default. Set to `true` to make Mongoose's default index build use `createIndex()` instead of `ensureIndex()` to avoid deprecation warnings from the MongoDB driver.
+ * - 'useFindAndModify': true by default. Set to `false` to make `findOneAndUpdate()` and `findOneAndRemove()` use native `findOneAndUpdate()` rather than `findAndModify()`.
+ * - 'useNewUrlParser': false by default. Set to `true` to make all connections set the `useNewUrlParser` option by default
+ * - 'cloneSchemas': false by default. Set to `true` to `clone()` all schemas before compiling into a model.
+ * - 'applyPluginsToDiscriminators': false by default. Set to true to apply global plugins to discriminator schemas. This typically isn't necessary because plugins are applied to the base schema and discriminators copy all middleware, methods, statics, and properties from the base schema.
+ * - 'objectIdGetter': true by default. Mongoose adds a getter to MongoDB ObjectId's called `_id` that returns `this` for convenience with populate. Set this to false to remove the getter.
+ * - 'runValidators': false by default. Set to true to enable [update validators](/docs/validation.html#update-validators) for all validators by default.
+ * - 'toObject': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toObject()`](/docs/api.html#document_Document-toObject)
+ * - 'toJSON': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toJSON()`](/docs/api.html#document_Document-toJSON), for determining how Mongoose documents get serialized by `JSON.stringify()`
+ * - 'strict': true by default, may be `false`, `true`, or `'throw'`. Sets the default strict mode for schemas.
+ * - 'selectPopulatedPaths': true by default. Set to false to opt out of Mongoose adding all fields that you `populate()` to your `select()`. The schema-level option `selectPopulatedPaths` overwrites this one.
+ *
+ * @param {String} key
+ * @param {String|Function|Boolean} value
+ * @api public
+ */
+
+Mongoose.prototype.set = function(key, value) {
+ if (arguments.length === 1) {
+ return this.options[key];
+ }
+
+ this.options[key] = value;
+
+ if (key === 'objectIdGetter') {
+ if (value) {
+ Object.defineProperty(mongoose.Types.ObjectId.prototype, '_id', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
+ return this;
+ }
+ });
+ } else {
+ delete mongoose.Types.ObjectId.prototype._id;
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Gets mongoose options
+ *
+ * ####Example:
+ *
+ * mongoose.get('test') // returns the 'test' value
+ *
+ * @param {String} key
+ * @method get
+ * @api public
+ */
+
+Mongoose.prototype.get = Mongoose.prototype.set;
+
+/**
+ * Creates a Connection instance.
+ *
+ * Each `connection` instance maps to a single database. This method is helpful when mangaging multiple db connections.
+ *
+ *
+ * _Options passed take precedence over options included in connection strings._
+ *
+ * ####Example:
+ *
+ * // with mongodb:// URI
+ * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database');
+ *
+ * // and options
+ * var opts = { db: { native_parser: true }}
+ * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database', opts);
+ *
+ * // replica sets
+ * db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database');
+ *
+ * // and options
+ * var opts = { replset: { strategy: 'ping', rs_name: 'testSet' }}
+ * db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database', opts);
+ *
+ * // and options
+ * var opts = { server: { auto_reconnect: false }, user: 'username', pass: 'mypassword' }
+ * db = mongoose.createConnection('localhost', 'database', port, opts)
+ *
+ * // initialize now, connect later
+ * db = mongoose.createConnection();
+ * db.openUri('localhost', 'database', port, [opts]);
+ *
+ * @param {String} [uri] a mongodb:// URI
+ * @param {Object} [options] passed down to the [MongoDB driver's `connect()` function](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html), except for 4 mongoose-specific options explained below.
+ * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility.
+ * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
+ * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
+ * @param {Boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](http://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection.
+ * @return {Connection} the created Connection object. Connections are thenable, so you can do `await mongoose.createConnection()`
+ * @api public
+ */
+
+Mongoose.prototype.createConnection = function(uri, options, callback) {
+ const conn = new Connection(this);
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+ this.connections.push(conn);
+
+ if (arguments.length > 0) {
+ return conn.openUri(uri, options, callback);
+ }
+
+ return conn;
+};
+
+/**
+ * Opens the default mongoose connection.
+ *
+ * ####Example:
+ *
+ * mongoose.connect('mongodb://user:pass@localhost:port/database');
+ *
+ * // replica sets
+ * var uri = 'mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/mydatabase';
+ * mongoose.connect(uri);
+ *
+ * // with options
+ * mongoose.connect(uri, options);
+ *
+ * // optional callback that gets fired when initial connection completed
+ * var uri = 'mongodb://nonexistent.domain:27000';
+ * mongoose.connect(uri, function(error) {
+ * // if error is truthy, the initial connection failed.
+ * })
+ *
+ * @param {String} uri(s)
+ * @param {Object} [options] passed down to the [MongoDB driver's `connect()` function](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html), except for 4 mongoose-specific options explained below.
+ * @param {String} [options.dbName] The name of the database we want to use. If not provided, use database name from connection string.
+ * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility.
+ * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
+ * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
+ * @param {Boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](http://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection.
+ * @param {Boolean} [options.useCreateIndex=true] Mongoose-specific option. If `true`, this connection will use [`createIndex()` instead of `ensureIndex()`](/docs/deprecations.html#-ensureindex-) for automatic index builds via [`Model.init()`](/docs/api.html#model_Model.init).
+ * @param {Boolean} [options.useFindAndModify=true] True by default. Set to `false` to make `findOneAndUpdate()` and `findOneAndRemove()` use native `findOneAndUpdate()` rather than `findAndModify()`.
+ * @param {Boolean} [options.useNewUrlParser=false] False by default. Set to `true` to make all connections set the `useNewUrlParser` option by default.
+ * @param {Function} [callback]
+ * @see Mongoose#createConnection #index_Mongoose-createConnection
+ * @api public
+ * @return {Promise} resolves to `this` if connection succeeded
+ */
+
+Mongoose.prototype.connect = function() {
+ const _mongoose = this instanceof Mongoose ? this : mongoose;
+ const conn = _mongoose.connection;
+ return conn.openUri(arguments[0], arguments[1], arguments[2]).then(() => _mongoose);
+};
+
+/**
+ * Runs `.close()` on all connections in parallel.
+ *
+ * @param {Function} [callback] called after all connection close, or when first error occurred.
+ * @return {Promise} resolves when all connections are closed, or rejects with the first error that occurred.
+ * @api public
+ */
+
+Mongoose.prototype.disconnect = function(callback) {
+ return utils.promiseOrCallback(callback, cb => {
+ let remaining = this.connections.length;
+ if (remaining <= 0) {
+ return cb(null);
+ }
+ this.connections.forEach(conn => {
+ conn.close(function(error) {
+ if (error) {
+ return cb(error);
+ }
+ if (!--remaining) {
+ cb(null);
+ }
+ });
+ });
+ });
+};
+
+/**
+ * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://docs.mongodb.com/manual/release-notes/3.6/#client-sessions)
+ * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/),
+ * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html).
+ *
+ * Calling `mongoose.startSession()` is equivalent to calling `mongoose.connection.startSession()`.
+ * Sessions are scoped to a connection, so calling `mongoose.startSession()`
+ * starts a session on the [default mongoose connection](/docs/api.html#mongoose_Mongoose-connection).
+ *
+ * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#startSession)
+ * @param {Boolean} [options.causalConsistency=true] set to false to disable causal consistency
+ * @param {Function} [callback]
+ * @return {Promise} promise that resolves to a MongoDB driver `ClientSession`
+ * @api public
+ */
+
+Mongoose.prototype.startSession = function() {
+ return this.connection.startSession.apply(this.connection, arguments);
+};
+
+/**
+ * Getter/setter around function for pluralizing collection names.
+ *
+ * @param {Function|null} [fn] overwrites the function used to pluralize collection names
+ * @return {Function|null} the current function used to pluralize collection names, defaults to the legacy function from `mongoose-legacy-pluralize`.
+ * @api public
+ */
+
+Mongoose.prototype.pluralize = function(fn) {
+ if (arguments.length > 0) {
+ this._pluralize = fn;
+ }
+ return this._pluralize;
+};
+
+/**
+ * Defines a model or retrieves it.
+ *
+ * Models defined on the `mongoose` instance are available to all connection
+ * created by the same `mongoose` instance.
+ *
+ * If you call `mongoose.model()` with twice the same name but a different schema,
+ * you will get an `OverwriteModelError`. If you call `mongoose.model()` with
+ * the same name and same schema, you'll get the same schema back.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ *
+ * // define an Actor model with this mongoose instance
+ * const Schema = new Schema({ name: String });
+ * mongoose.model('Actor', schema);
+ *
+ * // create a new connection
+ * var conn = mongoose.createConnection(..);
+ *
+ * // create Actor model
+ * var Actor = conn.model('Actor', schema);
+ * conn.model('Actor') === Actor; // true
+ * conn.model('Actor', schema) === Actor; // true, same schema
+ * conn.model('Actor', schema, 'actors') === Actor; // true, same schema and collection name
+ *
+ * // This throws an `OverwriteModelError` because the schema is different.
+ * conn.model('Actor', new Schema({ name: String }));
+ *
+ * _When no `collection` argument is passed, Mongoose uses the model name. If you don't like this behavior, either pass a collection name, use `mongoose.pluralize()`, or set your schemas collection name option._
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ name: String }, { collection: 'actor' });
+ *
+ * // or
+ *
+ * schema.set('collection', 'actor');
+ *
+ * // or
+ *
+ * var collectionName = 'actor'
+ * var M = mongoose.model('Actor', schema, collectionName)
+ *
+ * @param {String|Function} name model name or class extending Model
+ * @param {Schema} [schema] the schema to use.
+ * @param {String} [collection] name (optional, inferred from model name)
+ * @param {Boolean} [skipInit] whether to skip initialization (defaults to false)
+ * @return {Model} The model associated with `name`. Mongoose will create the model if it doesn't already exist.
+ * @api public
+ */
+
+Mongoose.prototype.model = function(name, schema, collection, skipInit) {
+ const _mongoose = this instanceof Mongoose ? this : mongoose;
+
+ let model;
+ if (typeof name === 'function') {
+ model = name;
+ name = model.name;
+ if (!(model.prototype instanceof Model)) {
+ throw new _mongoose.Error('The provided class ' + name + ' must extend Model');
+ }
+ }
+
+ if (typeof schema === 'string') {
+ collection = schema;
+ schema = false;
+ }
+
+ if (utils.isObject(schema) && !(schema.instanceOfSchema)) {
+ schema = new Schema(schema);
+ }
+ if (schema && !schema.instanceOfSchema) {
+ throw new Error('The 2nd parameter to `mongoose.model()` should be a ' +
+ 'schema or a POJO');
+ }
+
+ if (typeof collection === 'boolean') {
+ skipInit = collection;
+ collection = null;
+ }
+
+ // handle internal options from connection.model()
+ let options;
+ if (skipInit && utils.isObject(skipInit)) {
+ options = skipInit;
+ skipInit = true;
+ } else {
+ options = {};
+ }
+
+ // look up schema for the collection.
+ if (!_mongoose.modelSchemas[name]) {
+ if (schema) {
+ // cache it so we only apply plugins once
+ _mongoose.modelSchemas[name] = schema;
+ } else {
+ throw new mongoose.Error.MissingSchemaError(name);
+ }
+ }
+
+ const originalSchema = schema;
+ if (schema) {
+ if (_mongoose.get('cloneSchemas')) {
+ schema = schema.clone();
+ }
+ _mongoose._applyPlugins(schema);
+ }
+
+ let sub;
+
+ // connection.model() may be passing a different schema for
+ // an existing model name. in this case don't read from cache.
+ if (_mongoose.models[name] && options.cache !== false) {
+ if (originalSchema &&
+ originalSchema.instanceOfSchema &&
+ originalSchema !== _mongoose.models[name].schema) {
+ throw new _mongoose.Error.OverwriteModelError(name);
+ }
+
+ if (collection && collection !== _mongoose.models[name].collection.name) {
+ // subclass current model with alternate collection
+ model = _mongoose.models[name];
+ schema = model.prototype.schema;
+ sub = model.__subclass(_mongoose.connection, schema, collection);
+ // do not cache the sub model
+ return sub;
+ }
+
+ return _mongoose.models[name];
+ }
+
+ // ensure a schema exists
+ if (!schema) {
+ schema = this.modelSchemas[name];
+ if (!schema) {
+ throw new mongoose.Error.MissingSchemaError(name);
+ }
+ }
+
+ // Apply relevant "global" options to the schema
+ if (!('pluralization' in schema.options)) {
+ schema.options.pluralization = _mongoose.options.pluralization;
+ }
+
+ if (!collection) {
+ collection = schema.get('collection') ||
+ utils.toCollectionName(name, _mongoose.pluralize());
+ }
+
+ const connection = options.connection || _mongoose.connection;
+ model = _mongoose.Model.compile(model || name, schema, collection, connection, _mongoose);
+
+ if (!skipInit) {
+ // Errors handled internally, so safe to ignore error
+ model.init(function $modelInitNoop() {});
+ }
+
+ if (options.cache === false) {
+ return model;
+ }
+
+ _mongoose.models[name] = model;
+ return _mongoose.models[name];
+};
+
+/**
+ * Removes the model named `name` from the default connection, if it exists.
+ * You can use this function to clean up any models you created in your tests to
+ * prevent OverwriteModelErrors.
+ *
+ * Equivalent to `mongoose.connection.deleteModel(name)`.
+ *
+ * ####Example:
+ *
+ * mongoose.model('User', new Schema({ name: String }));
+ * console.log(mongoose.model('User')); // Model object
+ * mongoose.deleteModel('User');
+ * console.log(mongoose.model('User')); // undefined
+ *
+ * // Usually useful in a Mocha `afterEach()` hook
+ * afterEach(function() {
+ * mongoose.deleteModel(/.+/); // Delete every model
+ * });
+ *
+ * @api public
+ * @param {String|RegExp} name if string, the name of the model to remove. If regexp, removes all models whose name matches the regexp.
+ * @return {Mongoose} this
+ */
+
+Mongoose.prototype.deleteModel = function(name) {
+ this.connection.deleteModel(name);
+ return this;
+};
+
+/**
+ * Returns an array of model names created on this instance of Mongoose.
+ *
+ * ####Note:
+ *
+ * _Does not include names of models created using `connection.model()`._
+ *
+ * @api public
+ * @return {Array}
+ */
+
+Mongoose.prototype.modelNames = function() {
+ const names = Object.keys(this.models);
+ return names;
+};
+
+/**
+ * Applies global plugins to `schema`.
+ *
+ * @param {Schema} schema
+ * @api private
+ */
+
+Mongoose.prototype._applyPlugins = function(schema, options) {
+ if (schema.$globalPluginsApplied) {
+ return;
+ }
+ let i;
+ let len;
+ if (!options || !options.skipTopLevel) {
+ for (i = 0, len = this.plugins.length; i < len; ++i) {
+ schema.plugin(this.plugins[i][0], this.plugins[i][1]);
+ }
+ schema.$globalPluginsApplied = true;
+ }
+ for (i = 0, len = schema.childSchemas.length; i < len; ++i) {
+ this._applyPlugins(schema.childSchemas[i].schema);
+ }
+};
+
+/**
+ * Declares a global plugin executed on all Schemas.
+ *
+ * Equivalent to calling `.plugin(fn)` on each Schema you create.
+ *
+ * @param {Function} fn plugin callback
+ * @param {Object} [opts] optional options
+ * @return {Mongoose} this
+ * @see plugins ./plugins.html
+ * @api public
+ */
+
+Mongoose.prototype.plugin = function(fn, opts) {
+ this.plugins.push([fn, opts]);
+ return this;
+};
+
+/**
+ * The default connection of the mongoose module.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * mongoose.connect(...);
+ * mongoose.connection.on('error', cb);
+ *
+ * This is the connection used by default for every model created using [mongoose.model](#index_Mongoose-model).
+ *
+ * @memberOf Mongoose
+ * @instance
+ * @property connection
+ * @return {Connection}
+ * @api public
+ */
+
+Mongoose.prototype.__defineGetter__('connection', function() {
+ return this.connections[0];
+});
+
+Mongoose.prototype.__defineSetter__('connection', function(v) {
+ if (v instanceof Connection) {
+ this.connections[0] = v;
+ this.models = v.models;
+ }
+});
+
+/*!
+ * Driver dependent APIs
+ */
+
+const driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
+
+/*!
+ * Connection
+ */
+
+const Connection = require(driver + '/connection');
+
+/*!
+ * Collection
+ */
+
+const Collection = require(driver + '/collection');
+
+/**
+ * The Mongoose Aggregate constructor
+ *
+ * @method Aggregate
+ * @api public
+ */
+
+Mongoose.prototype.Aggregate = Aggregate;
+
+/**
+ * The Mongoose Collection constructor
+ *
+ * @method Collection
+ * @api public
+ */
+
+Mongoose.prototype.Collection = Collection;
+
+/**
+ * The Mongoose [Connection](#connection_Connection) constructor
+ *
+ * @memberOf Mongoose
+ * @instance
+ * @method Connection
+ * @api public
+ */
+
+Mongoose.prototype.Connection = Connection;
+
+/**
+ * The Mongoose version
+ *
+ * #### Example
+ *
+ * console.log(mongoose.version); // '5.x.x'
+ *
+ * @property version
+ * @api public
+ */
+
+Mongoose.prototype.version = pkg.version;
+
+/**
+ * The Mongoose constructor
+ *
+ * The exports of the mongoose module is an instance of this class.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var mongoose2 = new mongoose.Mongoose();
+ *
+ * @method Mongoose
+ * @api public
+ */
+
+Mongoose.prototype.Mongoose = Mongoose;
+
+/**
+ * The Mongoose [Schema](#schema_Schema) constructor
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var Schema = mongoose.Schema;
+ * var CatSchema = new Schema(..);
+ *
+ * @method Schema
+ * @api public
+ */
+
+Mongoose.prototype.Schema = Schema;
+
+/**
+ * The Mongoose [SchemaType](#schematype_SchemaType) constructor
+ *
+ * @method SchemaType
+ * @api public
+ */
+
+Mongoose.prototype.SchemaType = SchemaType;
+
+/**
+ * The various Mongoose SchemaTypes.
+ *
+ * ####Note:
+ *
+ * _Alias of mongoose.Schema.Types for backwards compatibility._
+ *
+ * @property SchemaTypes
+ * @see Schema.SchemaTypes #schema_Schema.Types
+ * @api public
+ */
+
+Mongoose.prototype.SchemaTypes = Schema.Types;
+
+/**
+ * The Mongoose [VirtualType](#virtualtype_VirtualType) constructor
+ *
+ * @method VirtualType
+ * @api public
+ */
+
+Mongoose.prototype.VirtualType = VirtualType;
+
+/**
+ * The various Mongoose Types.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var array = mongoose.Types.Array;
+ *
+ * ####Types:
+ *
+ * - [ObjectId](#types-objectid-js)
+ * - [Buffer](#types-buffer-js)
+ * - [SubDocument](#types-embedded-js)
+ * - [Array](#types-array-js)
+ * - [DocumentArray](#types-documentarray-js)
+ *
+ * Using this exposed access to the `ObjectId` type, we can construct ids on demand.
+ *
+ * var ObjectId = mongoose.Types.ObjectId;
+ * var id1 = new ObjectId;
+ *
+ * @property Types
+ * @api public
+ */
+
+Mongoose.prototype.Types = Types;
+
+/**
+ * The Mongoose [Query](#query_Query) constructor.
+ *
+ * @method Query
+ * @api public
+ */
+
+Mongoose.prototype.Query = Query;
+
+/**
+ * The Mongoose [Promise](#promise_Promise) constructor.
+ *
+ * @memberOf Mongoose
+ * @instance
+ * @property Promise
+ * @api public
+ */
+
+Object.defineProperty(Mongoose.prototype, 'Promise', {
+ get: function() {
+ return PromiseProvider.get();
+ },
+ set: function(lib) {
+ PromiseProvider.set(lib);
+ }
+});
+
+/**
+ * Storage layer for mongoose promises
+ *
+ * @method PromiseProvider
+ * @api public
+ */
+
+Mongoose.prototype.PromiseProvider = PromiseProvider;
+
+/**
+ * The Mongoose [Model](#model_Model) constructor.
+ *
+ * @method Model
+ * @api public
+ */
+
+Mongoose.prototype.Model = Model;
+
+/**
+ * The Mongoose [Document](#document-js) constructor.
+ *
+ * @method Document
+ * @api public
+ */
+
+Mongoose.prototype.Document = Document;
+
+/**
+ * The Mongoose DocumentProvider constructor. Mongoose users should not have to
+ * use this directly
+ *
+ * @method DocumentProvider
+ * @api public
+ */
+
+Mongoose.prototype.DocumentProvider = require('./document_provider');
+
+/**
+ * The Mongoose ObjectId [SchemaType](/docs/schematypes.html). Used for
+ * declaring paths in your schema that should be
+ * [MongoDB ObjectIds](https://docs.mongodb.com/manual/reference/method/ObjectId/).
+ * Do not use this to create a new ObjectId instance, use `mongoose.Types.ObjectId`
+ * instead.
+ *
+ * ####Example:
+ *
+ * const childSchema = new Schema({ parentId: mongoose.ObjectId });
+ *
+ * @property ObjectId
+ * @api public
+ */
+
+Mongoose.prototype.ObjectId = SchemaTypes.ObjectId;
+
+/**
+ * The Mongoose Decimal128 [SchemaType](/docs/schematypes.html). Used for
+ * declaring paths in your schema that should be
+ * [128-bit decimal floating points](http://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-decimal.html).
+ * Do not use this to create a new Decimal128 instance, use `mongoose.Types.Decimal128`
+ * instead.
+ *
+ * ####Example:
+ *
+ * const vehicleSchema = new Schema({ fuelLevel: mongoose.Decimal128 });
+ *
+ * @property Decimal128
+ * @api public
+ */
+
+Mongoose.prototype.Decimal128 = SchemaTypes.Decimal128;
+
+/**
+ * The Mongoose Mixed [SchemaType](/docs/schematypes.html). Used for
+ * declaring paths in your schema that Mongoose's change tracking, casting,
+ * and validation should ignore.
+ *
+ * ####Example:
+ *
+ * const schema = new Schema({ arbitrary: mongoose.Mixed });
+ *
+ * @property Mixed
+ * @api public
+ */
+
+Mongoose.prototype.Mixed = SchemaTypes.Mixed;
+
+/**
+ * The Mongoose Number [SchemaType](/docs/schematypes.html). Used for
+ * declaring paths in your schema that Mongoose should cast to numbers.
+ *
+ * ####Example:
+ *
+ * const schema = new Schema({ num: mongoose.Number });
+ * // Equivalent to:
+ * const schema = new Schema({ num: 'number' });
+ *
+ * @property Number
+ * @api public
+ */
+
+Mongoose.prototype.Number = SchemaTypes.Number;
+
+/**
+ * The [MongooseError](#error_MongooseError) constructor.
+ *
+ * @method Error
+ * @api public
+ */
+
+Mongoose.prototype.Error = require('./error');
+
+/**
+ * Mongoose uses this function to get the current time when setting
+ * [timestamps](/docs/guide.html#timestamps). You may stub out this function
+ * using a tool like [Sinon](https://www.npmjs.com/package/sinon) for testing.
+ *
+ * @method now
+ * @returns Date the current time
+ * @api public
+ */
+
+Mongoose.prototype.now = function now() { return new Date(); };
+
+/**
+ * The Mongoose CastError constructor
+ *
+ * @method CastError
+ * @param {String} type The name of the type
+ * @param {Any} value The value that failed to cast
+ * @param {String} path The path `a.b.c` in the doc where this cast error occurred
+ * @param {Error} [reason] The original error that was thrown
+ * @api public
+ */
+
+Mongoose.prototype.CastError = require('./error/cast');
+
+/**
+ * The [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver Mongoose uses.
+ *
+ * @property mongo
+ * @api public
+ */
+
+Mongoose.prototype.mongo = require('mongodb');
+
+/**
+ * The [mquery](https://github.com/aheckmann/mquery) query builder Mongoose uses.
+ *
+ * @property mquery
+ * @api public
+ */
+
+Mongoose.prototype.mquery = require('mquery');
+
+/*!
+ * The exports object is an instance of Mongoose.
+ *
+ * @api public
+ */
+
+const mongoose = module.exports = exports = new Mongoose({
+ [defaultMongooseSymbol]: true
+});
diff --git a/node_modules/mongoose/lib/internal.js b/node_modules/mongoose/lib/internal.js
new file mode 100644
index 0000000..9364658
--- /dev/null
+++ b/node_modules/mongoose/lib/internal.js
@@ -0,0 +1,37 @@
+/*!
+ * Dependencies
+ */
+
+'use strict';
+
+const StateMachine = require('./statemachine');
+const ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default', 'ignore');
+
+module.exports = exports = InternalCache;
+
+function InternalCache() {
+ this.strictMode = undefined;
+ this.selected = undefined;
+ this.shardval = undefined;
+ this.saveError = undefined;
+ this.validationError = undefined;
+ this.adhocPaths = undefined;
+ this.removing = undefined;
+ this.inserting = undefined;
+ this.saving = undefined;
+ this.version = undefined;
+ this.getters = {};
+ this._id = undefined;
+ this.populate = undefined; // what we want to populate in this doc
+ this.populated = undefined;// the _ids that have been populated
+ this.wasPopulated = false; // if this doc was the result of a population
+ this.scope = undefined;
+ this.activePaths = new ActiveRoster;
+ this.pathsToScopes = {};
+ this.cachedRequired = {};
+ this.session = null;
+
+ // embedded docs
+ this.ownerDocument = undefined;
+ this.fullPath = undefined;
+}
diff --git a/node_modules/mongoose/lib/model.js b/node_modules/mongoose/lib/model.js
new file mode 100644
index 0000000..4fbf5fb
--- /dev/null
+++ b/node_modules/mongoose/lib/model.js
@@ -0,0 +1,4657 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const Aggregate = require('./aggregate');
+const ChangeStream = require('./cursor/ChangeStream');
+const Document = require('./document');
+const DocumentNotFoundError = require('./error').DocumentNotFoundError;
+const DivergentArrayError = require('./error').DivergentArrayError;
+const Error = require('./error');
+const EventEmitter = require('events').EventEmitter;
+const MongooseMap = require('./types/map');
+const OverwriteModelError = require('./error').OverwriteModelError;
+const PromiseProvider = require('./promise_provider');
+const Query = require('./query');
+const Schema = require('./schema');
+const VersionError = require('./error').VersionError;
+const ParallelSaveError = require('./error').ParallelSaveError;
+const applyQueryMiddleware = require('./helpers/query/applyQueryMiddleware');
+const applyHooks = require('./helpers/model/applyHooks');
+const applyMethods = require('./helpers/model/applyMethods');
+const applyStatics = require('./helpers/model/applyStatics');
+const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
+const assignRawDocsToIdStructure = require('./helpers/populate/assignRawDocsToIdStructure');
+const castBulkWrite = require('./helpers/model/castBulkWrite');
+const discriminator = require('./helpers/model/discriminator');
+const getDiscriminatorByValue = require('./queryhelpers').getDiscriminatorByValue;
+const immediate = require('./helpers/immediate');
+const internalToObjectOptions = require('./options').internalToObjectOptions;
+const isPathExcluded = require('./helpers/projection/isPathExcluded');
+const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');
+const get = require('./helpers/get');
+const getSchemaTypes = require('./helpers/populate/getSchemaTypes');
+const getVirtual = require('./helpers/populate/getVirtual');
+const leanPopulateMap = require('./helpers/populate/leanPopulateMap');
+const modifiedPaths = require('./helpers/update/modifiedPaths');
+const mpath = require('mpath');
+const normalizeRefPath = require('./helpers/populate/normalizeRefPath');
+const parallel = require('async/parallel');
+const parallelLimit = require('async/parallelLimit');
+const setParentPointers = require('./helpers/schema/setParentPointers');
+const util = require('util');
+const utils = require('./utils');
+
+const VERSION_WHERE = 1;
+const VERSION_INC = 2;
+const VERSION_ALL = VERSION_WHERE | VERSION_INC;
+
+const modelCollectionSymbol = Symbol.for('mongoose#Model#collection');
+const modelSymbol = require('./helpers/symbols').modelSymbol;
+const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
+
+/**
+ * A Model is a class that's your primary tool for interacting with MongoDB.
+ * An instance of a Model is called a [Document](./api.html#Document).
+ *
+ * In Mongoose, the term "Model" refers to subclasses of the `mongoose.Model`
+ * class. You should not use the `mongoose.Model` class directly. The
+ * [`mongoose.model()`](./api.html#mongoose_Mongoose-model) and
+ * [`connection.model()`](./api.html#connection_Connection-model) functions
+ * create subclasses of `mongoose.Model` as shown below.
+ *
+ * ####Example:
+ *
+ * // `UserModel` is a "Model", a subclass of `mongoose.Model`.
+ * const UserModel = mongoose.model('User', new Schema({ name: String }));
+ *
+ * // You can use a Model to create new documents using `new`:
+ * const userDoc = new UserModel({ name: 'Foo' });
+ * await userDoc.save();
+ *
+ * // You also use a model to create queries:
+ * const userFromDb = await UserModel.findOne({ name: 'Foo' });
+ *
+ * @param {Object} doc values for initial set
+ * @param [fields] optional object containing the fields that were selected in the query which returned this document. You do **not** need to set this parameter to ensure Mongoose handles your [query projetion](./api.html#query_Query-select).
+ * @inherits Document http://mongoosejs.com/docs/api.html#document-js
+ * @event `error`: If listening to this event, 'error' is emitted when a document was saved without passing a callback and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
+ * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
+ * @event `index-single-start`: Emitted when an individual index starts within `Model#ensureIndexes`. The fields and options being used to build the index are also passed with the event.
+ * @event `index-single-done`: Emitted when an individual index finishes within `Model#ensureIndexes`. If an error occurred it is passed with the event. The fields, options, and index name are also passed.
+ * @api public
+ */
+
+function Model(doc, fields, skipId) {
+ if (fields instanceof Schema) {
+ throw new TypeError('2nd argument to `Model` must be a POJO or string, ' +
+ '**not** a schema. Make sure you\'re calling `mongoose.model()`, not ' +
+ '`mongoose.Model()`.');
+ }
+ Document.call(this, doc, fields, skipId);
+}
+
+/*!
+ * Inherits from Document.
+ *
+ * All Model.prototype features are available on
+ * top level (non-sub) documents.
+ */
+
+Model.prototype.__proto__ = Document.prototype;
+Model.prototype.$isMongooseModelPrototype = true;
+
+/**
+ * Connection the model uses.
+ *
+ * @api public
+ * @property db
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.db;
+
+/**
+ * Collection the model uses.
+ *
+ * This property is read-only. Modifying this property is a no-op.
+ *
+ * @api public
+ * @property collection
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.collection;
+
+/**
+ * The name of the model
+ *
+ * @api public
+ * @property modelName
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.modelName;
+
+/**
+ * Additional properties to attach to the query when calling `save()` and
+ * `isNew` is false.
+ *
+ * @api public
+ * @property $where
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.$where;
+
+/**
+ * If this is a discriminator model, `baseModelName` is the name of
+ * the base model.
+ *
+ * @api public
+ * @property baseModelName
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.baseModelName;
+
+/**
+ * Event emitter that reports any errors that occurred. Useful for global error
+ * handling.
+ *
+ * ####Example:
+ *
+ * MyModel.events.on('error', err => console.log(err.message));
+ *
+ * // Prints a 'CastError' because of the above handler
+ * await MyModel.findOne({ _id: 'notanid' }).catch({} => {});
+ *
+ * @api public
+ * @fires error whenever any query or model function errors
+ * @property events
+ * @memberOf Model
+ * @static
+ */
+
+Model.events;
+
+/*!
+ * Compiled middleware for this model. Set in `applyHooks()`.
+ *
+ * @api private
+ * @property _middleware
+ * @memberOf Model
+ * @static
+ */
+
+Model._middleware;
+
+/*!
+ * ignore
+ */
+
+Model.prototype.$__handleSave = function(options, callback) {
+ const _this = this;
+ let i;
+ let keys;
+ let len;
+ let saveOptions = {};
+
+ if ('safe' in options) {
+ _handleSafe(options);
+ }
+ applyWriteConcern(this.schema, options);
+ if ('w' in options) {
+ saveOptions.w = options.w;
+ }
+ if ('j' in options) {
+ saveOptions.j = options.j;
+ }
+ if ('wtimeout' in options) {
+ saveOptions.wtimeout = options.wtimeout;
+ }
+ if ('checkKeys' in options) {
+ saveOptions.checkKeys = options.checkKeys;
+ }
+
+ const session = 'session' in options ? options.session : this.$session();
+ if (session != null) {
+ saveOptions.session = session;
+ this.$session(session);
+ }
+
+ if (Object.keys(saveOptions).length === 0) {
+ saveOptions = null;
+ }
+
+ if (this.isNew) {
+ // send entire doc
+ const obj = this.toObject(internalToObjectOptions);
+
+ if ((obj || {})._id === void 0) {
+ // documents must have an _id else mongoose won't know
+ // what to update later if more changes are made. the user
+ // wouldn't know what _id was generated by mongodb either
+ // nor would the ObjectId generated my mongodb necessarily
+ // match the schema definition.
+ setTimeout(function() {
+ callback(new Error('document must have an _id before saving'));
+ }, 0);
+ return;
+ }
+
+ this.$__version(true, obj);
+ this[modelCollectionSymbol].insertOne(obj, saveOptions, function(err, ret) {
+ if (err) {
+ _this.isNew = true;
+ _this.emit('isNew', true);
+ _this.constructor.emit('isNew', true);
+
+ callback(err, null);
+ return;
+ }
+
+ callback(null, ret);
+ });
+ this.$__reset();
+ this.isNew = false;
+ this.emit('isNew', false);
+ this.constructor.emit('isNew', false);
+ // Make it possible to retry the insert
+ this.$__.inserting = true;
+ } else {
+ // Make sure we don't treat it as a new object on error,
+ // since it already exists
+ this.$__.inserting = false;
+
+ const delta = this.$__delta();
+
+ if (delta) {
+ if (delta instanceof Error) {
+ callback(delta);
+ return;
+ }
+
+ const where = this.$__where(delta[0]);
+ if (where instanceof Error) {
+ callback(where);
+ return;
+ }
+
+ if (this.$where) {
+ keys = Object.keys(this.$where);
+ len = keys.length;
+ for (i = 0; i < len; ++i) {
+ where[keys[i]] = this.$where[keys[i]];
+ }
+ }
+
+ this[modelCollectionSymbol].updateOne(where, delta[1], saveOptions, function(err, ret) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ ret.$where = where;
+ callback(null, ret);
+ });
+ } else {
+ this.$__reset();
+ callback();
+ return;
+ }
+
+ this.emit('isNew', false);
+ this.constructor.emit('isNew', false);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+Model.prototype.$__save = function(options, callback) {
+ this.$__handleSave(options, (error, result) => {
+ if (error) {
+ return this.schema.s.hooks.execPost('save:error', this, [this], { error: error }, function(error) {
+ callback(error);
+ });
+ }
+
+ // store the modified paths before the document is reset
+ const modifiedPaths = this.modifiedPaths();
+
+ this.$__reset();
+
+ let numAffected = 0;
+ if (get(options, 'safe.w') !== 0 && get(options, 'w') !== 0) {
+ // Skip checking if write succeeded if writeConcern is set to
+ // unacknowledged writes, because otherwise `numAffected` will always be 0
+ if (result) {
+ if (Array.isArray(result)) {
+ numAffected = result.length;
+ } else if (result.result && result.result.n !== undefined) {
+ numAffected = result.result.n;
+ } else if (result.result && result.result.nModified !== undefined) {
+ numAffected = result.result.nModified;
+ } else {
+ numAffected = result;
+ }
+ }
+
+ // was this an update that required a version bump?
+ if (this.$__.version && !this.$__.inserting) {
+ const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version);
+ this.$__.version = undefined;
+
+ const key = this.schema.options.versionKey;
+ const version = this.getValue(key) || 0;
+
+ if (numAffected <= 0) {
+ // the update failed. pass an error back
+ const err = options.$versionError || new VersionError(this, version, modifiedPaths);
+ return callback(err);
+ }
+
+ // increment version if was successful
+ if (doIncrement) {
+ this.setValue(key, version + 1);
+ }
+ }
+
+ if (result != null && numAffected <= 0) {
+ error = new DocumentNotFoundError(result.$where);
+ return this.schema.s.hooks.execPost('save:error', this, [this], { error: error }, function(error) {
+ callback(error);
+ });
+ }
+ }
+ this.$__.saving = undefined;
+ this.emit('save', this, numAffected);
+ this.constructor.emit('save', this, numAffected);
+ callback(null, this);
+ });
+};
+
+/*!
+ * ignore
+ */
+
+function generateVersionError(doc, modifiedPaths) {
+ const key = doc.schema.options.versionKey;
+ if (!key) {
+ return null;
+ }
+ const version = doc.getValue(key) || 0;
+ return new VersionError(doc, version, modifiedPaths);
+}
+
+/**
+ * Saves this document.
+ *
+ * ####Example:
+ *
+ * product.sold = Date.now();
+ * product.save(function (err, product) {
+ * if (err) ..
+ * })
+ *
+ * The callback will receive two parameters
+ *
+ * 1. `err` if an error occurred
+ * 2. `product` which is the saved `product`
+ *
+ * As an extra measure of flow control, save will return a Promise.
+ * ####Example:
+ * product.save().then(function(product) {
+ * ...
+ * });
+ *
+ * @param {Object} [options] options optional options
+ * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
+ * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
+ * @param {Number|String} [options.w] set the [write concern](https://docs.mongodb.com/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern)
+ * @param {Boolean} [options.j] set to true for MongoDB to wait until this `save()` has been [journaled before resolving the returned promise](https://docs.mongodb.com/manual/reference/write-concern/#j-option). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern)
+ * @param {Number} [options.wtimeout] sets a [timeout for the write concern](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern).
+ * @param {Boolean} [options.checkKeys=true] the MongoDB driver prevents you from saving keys that start with '$' or contain '.' by default. Set this option to `false` to skip that check. See [restrictions on field names](https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names)
+ * @param {Function} [fn] optional callback
+ * @return {Promise|undefined} Returns undefined if used with callback or a Promise otherwise.
+ * @api public
+ * @see middleware http://mongoosejs.com/docs/middleware.html
+ */
+
+Model.prototype.save = function(options, fn) {
+ let parallelSave;
+
+ if (this.$__.saving) {
+ parallelSave = new ParallelSaveError(this);
+ } else {
+ this.$__.saving = new ParallelSaveError(this);
+ }
+
+ if (typeof options === 'function') {
+ fn = options;
+ options = undefined;
+ }
+
+ if (options != null) {
+ options = utils.clone(options);
+ } else {
+ options = {};
+ }
+
+ if (fn) {
+ fn = this.constructor.$wrapCallback(fn);
+ }
+
+ options.$versionError = generateVersionError(this, this.modifiedPaths());
+
+ return utils.promiseOrCallback(fn, cb => {
+ if (parallelSave) {
+ this.$__handleReject(parallelSave);
+ return cb(parallelSave);
+ }
+
+ this.$__save(options, error => {
+ this.$__.saving = undefined;
+
+ if (error) {
+ this.$__handleReject(error);
+ return cb(error);
+ }
+ cb(null, this);
+ });
+ }, this.constructor.events);
+};
+
+/*!
+ * Determines whether versioning should be skipped for the given path
+ *
+ * @param {Document} self
+ * @param {String} path
+ * @return {Boolean} true if versioning should be skipped for the given path
+ */
+function shouldSkipVersioning(self, path) {
+ const skipVersioning = self.schema.options.skipVersioning;
+ if (!skipVersioning) return false;
+
+ // Remove any array indexes from the path
+ path = path.replace(/\.\d+\./, '.');
+
+ return skipVersioning[path];
+}
+
+/*!
+ * Apply the operation to the delta (update) clause as
+ * well as track versioning for our where clause.
+ *
+ * @param {Document} self
+ * @param {Object} where
+ * @param {Object} delta
+ * @param {Object} data
+ * @param {Mixed} val
+ * @param {String} [operation]
+ */
+
+function operand(self, where, delta, data, val, op) {
+ // delta
+ op || (op = '$set');
+ if (!delta[op]) delta[op] = {};
+ delta[op][data.path] = val;
+
+ // disabled versioning?
+ if (self.schema.options.versionKey === false) return;
+
+ // path excluded from versioning?
+ if (shouldSkipVersioning(self, data.path)) return;
+
+ // already marked for versioning?
+ if (VERSION_ALL === (VERSION_ALL & self.$__.version)) return;
+
+ switch (op) {
+ case '$set':
+ case '$unset':
+ case '$pop':
+ case '$pull':
+ case '$pullAll':
+ case '$push':
+ case '$addToSet':
+ break;
+ default:
+ // nothing to do
+ return;
+ }
+
+ // ensure updates sent with positional notation are
+ // editing the correct array element.
+ // only increment the version if an array position changes.
+ // modifying elements of an array is ok if position does not change.
+ if (op === '$push' || op === '$addToSet' || op === '$pullAll' || op === '$pull') {
+ self.$__.version = VERSION_INC;
+ } else if (/^\$p/.test(op)) {
+ // potentially changing array positions
+ self.increment();
+ } else if (Array.isArray(val)) {
+ // $set an array
+ self.increment();
+ } else if (/\.\d+\.|\.\d+$/.test(data.path)) {
+ // now handling $set, $unset
+ // subpath of array
+ self.$__.version = VERSION_WHERE;
+ }
+}
+
+/*!
+ * Compiles an update and where clause for a `val` with _atomics.
+ *
+ * @param {Document} self
+ * @param {Object} where
+ * @param {Object} delta
+ * @param {Object} data
+ * @param {Array} value
+ */
+
+function handleAtomics(self, where, delta, data, value) {
+ if (delta.$set && delta.$set[data.path]) {
+ // $set has precedence over other atomics
+ return;
+ }
+
+ if (typeof value.$__getAtomics === 'function') {
+ value.$__getAtomics().forEach(function(atomic) {
+ const op = atomic[0];
+ const val = atomic[1];
+ operand(self, where, delta, data, val, op);
+ });
+ return;
+ }
+
+ // legacy support for plugins
+
+ const atomics = value._atomics;
+ const ops = Object.keys(atomics);
+ let i = ops.length;
+ let val;
+ let op;
+
+ if (i === 0) {
+ // $set
+
+ if (utils.isMongooseObject(value)) {
+ value = value.toObject({depopulate: 1, _isNested: true});
+ } else if (value.valueOf) {
+ value = value.valueOf();
+ }
+
+ return operand(self, where, delta, data, value);
+ }
+
+ function iter(mem) {
+ return utils.isMongooseObject(mem)
+ ? mem.toObject({depopulate: 1, _isNested: true})
+ : mem;
+ }
+
+ while (i--) {
+ op = ops[i];
+ val = atomics[op];
+
+ if (utils.isMongooseObject(val)) {
+ val = val.toObject({depopulate: true, transform: false, _isNested: true});
+ } else if (Array.isArray(val)) {
+ val = val.map(iter);
+ } else if (val.valueOf) {
+ val = val.valueOf();
+ }
+
+ if (op === '$addToSet') {
+ val = {$each: val};
+ }
+
+ operand(self, where, delta, data, val, op);
+ }
+}
+
+/**
+ * Produces a special query document of the modified properties used in updates.
+ *
+ * @api private
+ * @method $__delta
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.$__delta = function() {
+ const dirty = this.$__dirty();
+ if (!dirty.length && VERSION_ALL !== this.$__.version) {
+ return;
+ }
+
+ const where = {};
+ const delta = {};
+ const len = dirty.length;
+ const divergent = [];
+ let d = 0;
+
+ where._id = this._doc._id;
+ // If `_id` is an object, need to depopulate, but also need to be careful
+ // because `_id` can technically be null (see gh-6406)
+ if (get(where, '_id.$__', null) != null) {
+ where._id = where._id.toObject({ transform: false, depopulate: true });
+ }
+
+ for (; d < len; ++d) {
+ const data = dirty[d];
+ let value = data.value;
+
+ const match = checkDivergentArray(this, data.path, value);
+ if (match) {
+ divergent.push(match);
+ continue;
+ }
+
+ const pop = this.populated(data.path, true);
+ if (!pop && this.$__.selected) {
+ // If any array was selected using an $elemMatch projection, we alter the path and where clause
+ // NOTE: MongoDB only supports projected $elemMatch on top level array.
+ const pathSplit = data.path.split('.');
+ const top = pathSplit[0];
+ if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) {
+ // If the selected array entry was modified
+ if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') {
+ where[top] = this.$__.selected[top];
+ pathSplit[1] = '$';
+ data.path = pathSplit.join('.');
+ }
+ // if the selected array was modified in any other way throw an error
+ else {
+ divergent.push(data.path);
+ continue;
+ }
+ }
+ }
+
+ if (divergent.length) continue;
+
+ if (value === undefined) {
+ operand(this, where, delta, data, 1, '$unset');
+ } else if (value === null) {
+ operand(this, where, delta, data, null);
+ } else if (value._path && value._atomics) {
+ // arrays and other custom types (support plugins etc)
+ handleAtomics(this, where, delta, data, value);
+ } else if (value._path && Buffer.isBuffer(value)) {
+ // MongooseBuffer
+ value = value.toObject();
+ operand(this, where, delta, data, value);
+ } else {
+ value = utils.clone(value, {
+ depopulate: true,
+ transform: false,
+ virtuals: false,
+ _isNested: true
+ });
+ operand(this, where, delta, data, value);
+ }
+ }
+
+ if (divergent.length) {
+ return new DivergentArrayError(divergent);
+ }
+
+ if (this.$__.version) {
+ this.$__version(where, delta);
+ }
+
+ return [where, delta];
+};
+
+/*!
+ * Determine if array was populated with some form of filter and is now
+ * being updated in a manner which could overwrite data unintentionally.
+ *
+ * @see https://github.com/Automattic/mongoose/issues/1334
+ * @param {Document} doc
+ * @param {String} path
+ * @return {String|undefined}
+ */
+
+function checkDivergentArray(doc, path, array) {
+ // see if we populated this path
+ const pop = doc.populated(path, true);
+
+ if (!pop && doc.$__.selected) {
+ // If any array was selected using an $elemMatch projection, we deny the update.
+ // NOTE: MongoDB only supports projected $elemMatch on top level array.
+ const top = path.split('.')[0];
+ if (doc.$__.selected[top + '.$']) {
+ return top;
+ }
+ }
+
+ if (!(pop && array && array.isMongooseArray)) return;
+
+ // If the array was populated using options that prevented all
+ // documents from being returned (match, skip, limit) or they
+ // deselected the _id field, $pop and $set of the array are
+ // not safe operations. If _id was deselected, we do not know
+ // how to remove elements. $pop will pop off the _id from the end
+ // of the array in the db which is not guaranteed to be the
+ // same as the last element we have here. $set of the entire array
+ // would be similarily destructive as we never received all
+ // elements of the array and potentially would overwrite data.
+ const check = pop.options.match ||
+ pop.options.options && utils.object.hasOwnProperty(pop.options.options, 'limit') || // 0 is not permitted
+ pop.options.options && pop.options.options.skip || // 0 is permitted
+ pop.options.select && // deselected _id?
+ (pop.options.select._id === 0 ||
+ /\s?-_id\s?/.test(pop.options.select));
+
+ if (check) {
+ const atomics = array._atomics;
+ if (Object.keys(atomics).length === 0 || atomics.$set || atomics.$pop) {
+ return path;
+ }
+ }
+}
+
+/**
+ * Appends versioning to the where and update clauses.
+ *
+ * @api private
+ * @method $__version
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.$__version = function(where, delta) {
+ const key = this.schema.options.versionKey;
+
+ if (where === true) {
+ // this is an insert
+ if (key) this.setValue(key, delta[key] = 0);
+ return;
+ }
+
+ // updates
+
+ // only apply versioning if our versionKey was selected. else
+ // there is no way to select the correct version. we could fail
+ // fast here and force them to include the versionKey but
+ // thats a bit intrusive. can we do this automatically?
+ if (!this.isSelected(key)) {
+ return;
+ }
+
+ // $push $addToSet don't need the where clause set
+ if (VERSION_WHERE === (VERSION_WHERE & this.$__.version)) {
+ const value = this.getValue(key);
+ if (value != null) where[key] = value;
+ }
+
+ if (VERSION_INC === (VERSION_INC & this.$__.version)) {
+ if (get(delta.$set, key, null) != null) {
+ // Version key is getting set, means we'll increment the doc's version
+ // after a successful save, so we should set the incremented version so
+ // future saves don't fail (gh-5779)
+ ++delta.$set[key];
+ } else {
+ delta.$inc = delta.$inc || {};
+ delta.$inc[key] = 1;
+ }
+ }
+};
+
+/**
+ * Signal that we desire an increment of this documents version.
+ *
+ * ####Example:
+ *
+ * Model.findById(id, function (err, doc) {
+ * doc.increment();
+ * doc.save(function (err) { .. })
+ * })
+ *
+ * @see versionKeys http://mongoosejs.com/docs/guide.html#versionKey
+ * @api public
+ */
+
+Model.prototype.increment = function increment() {
+ this.$__.version = VERSION_ALL;
+ return this;
+};
+
+/**
+ * Returns a query object
+ *
+ * @api private
+ * @method $__where
+ * @memberOf Model
+ * @instance
+ */
+
+Model.prototype.$__where = function _where(where) {
+ where || (where = {});
+
+ if (!where._id) {
+ where._id = this._doc._id;
+ }
+
+ if (this._doc._id === void 0) {
+ return new Error('No _id found on document!');
+ }
+
+ return where;
+};
+
+/**
+ * Removes this document from the db.
+ *
+ * ####Example:
+ * product.remove(function (err, product) {
+ * if (err) return handleError(err);
+ * Product.findById(product._id, function (err, product) {
+ * console.log(product) // null
+ * })
+ * })
+ *
+ *
+ * As an extra measure of flow control, remove will return a Promise (bound to `fn` if passed) so it could be chained, or hooked to recieve errors
+ *
+ * ####Example:
+ * product.remove().then(function (product) {
+ * ...
+ * }).catch(function (err) {
+ * assert.ok(err)
+ * })
+ *
+ * @param {function(err,product)} [fn] optional callback
+ * @return {Promise} Promise
+ * @api public
+ */
+
+Model.prototype.remove = function remove(options, fn) {
+ if (typeof options === 'function') {
+ fn = options;
+ options = undefined;
+ }
+
+ if (!options) {
+ options = {};
+ }
+
+ if (fn) {
+ fn = this.constructor.$wrapCallback(fn);
+ }
+
+ return utils.promiseOrCallback(fn, cb => {
+ this.$__remove(options, cb);
+ }, this.constructor.events);
+};
+
+/**
+ * Alias for remove
+ */
+
+Model.prototype.delete = Model.prototype.remove;
+
+/*!
+ * ignore
+ */
+
+Model.prototype.$__remove = function $__remove(options, cb) {
+ if (this.$__.isDeleted) {
+ return immediate(() => cb(null, this));
+ }
+
+ const where = this.$__where();
+ if (where instanceof Error) {
+ return cb(where);
+ }
+
+ this[modelCollectionSymbol].deleteOne(where, options, err => {
+ if (!err) {
+ this.$__.isDeleted = true;
+ this.emit('remove', this);
+ this.constructor.emit('remove', this);
+ return cb(null, this);
+ }
+ this.$__.isDeleted = false;
+ cb(err);
+ });
+};
+
+/**
+ * Returns another Model instance.
+ *
+ * ####Example:
+ *
+ * var doc = new Tank;
+ * doc.model('User').findById(id, callback);
+ *
+ * @param {String} name model name
+ * @api public
+ */
+
+Model.prototype.model = function model(name) {
+ return this.db.model(name);
+};
+
+/**
+ * Adds a discriminator type.
+ *
+ * ####Example:
+ *
+ * function BaseSchema() {
+ * Schema.apply(this, arguments);
+ *
+ * this.add({
+ * name: String,
+ * createdAt: Date
+ * });
+ * }
+ * util.inherits(BaseSchema, Schema);
+ *
+ * var PersonSchema = new BaseSchema();
+ * var BossSchema = new BaseSchema({ department: String });
+ *
+ * var Person = mongoose.model('Person', PersonSchema);
+ * var Boss = Person.discriminator('Boss', BossSchema);
+ * new Boss().__t; // "Boss". `__t` is the default `discriminatorKey`
+ *
+ * var employeeSchema = new Schema({ boss: ObjectId });
+ * var Employee = Person.discriminator('Employee', employeeSchema, 'staff');
+ * new Employee().__t; // "staff" because of 3rd argument above
+ *
+ * @param {String} name discriminator model name
+ * @param {Schema} schema discriminator model schema
+ * @param {String} value the string stored in the `discriminatorKey` property
+ * @api public
+ */
+
+Model.discriminator = function(name, schema, value) {
+ let model;
+ if (typeof name === 'function') {
+ model = name;
+ name = utils.getFunctionName(model);
+ if (!(model.prototype instanceof Model)) {
+ throw new Error('The provided class ' + name + ' must extend Model');
+ }
+ }
+
+ schema = discriminator(this, name, schema, value);
+ if (this.db.models[name]) {
+ throw new OverwriteModelError(name);
+ }
+
+ schema.$isRootDiscriminator = true;
+
+ model = this.db.model(model || name, schema, this.collection.name);
+ this.discriminators[name] = model;
+ const d = this.discriminators[name];
+ d.prototype.__proto__ = this.prototype;
+ Object.defineProperty(d, 'baseModelName', {
+ value: this.modelName,
+ configurable: true,
+ writable: false
+ });
+
+ // apply methods and statics
+ applyMethods(d, schema);
+ applyStatics(d, schema);
+
+ return d;
+};
+
+// Model (class) features
+
+/*!
+ * Give the constructor the ability to emit events.
+ */
+
+for (const i in EventEmitter.prototype) {
+ Model[i] = EventEmitter.prototype[i];
+}
+
+/**
+ * This function is responsible for building [indexes](https://docs.mongodb.com/manual/indexes/),
+ * unless [`autoIndex`](http://mongoosejs.com/docs/guide.html#autoIndex) is turned off.
+ *
+ * Mongoose calls this function automatically when a model is created using
+ * [`mongoose.model()`](/docs/api.html#mongoose_Mongoose-model) or
+ * * [`connection.model()`](/docs/api.html#connection_Connection-model), so you
+ * don't need to call it. This function is also idempotent, so you may call it
+ * to get back a promise that will resolve when your indexes are finished
+ * building as an alternative to [`MyModel.on('index')`](/docs/guide.html#indexes)
+ *
+ * ####Example:
+ *
+ * var eventSchema = new Schema({ thing: { type: 'string', unique: true }})
+ * // This calls `Event.init()` implicitly, so you don't need to call
+ * // `Event.init()` on your own.
+ * var Event = mongoose.model('Event', eventSchema);
+ *
+ * Event.init().then(function(Event) {
+ * // You can also use `Event.on('index')` if you prefer event emitters
+ * // over promises.
+ * console.log('Indexes are done building!');
+ * });
+ *
+ * @api public
+ * @param {Function} [callback]
+ * @returns {Promise}
+ */
+
+Model.init = function init(callback) {
+ this.schema.emit('init', this);
+
+ if (this.$init != null) {
+ if (callback) {
+ this.$init.then(() => callback(), err => callback(err));
+ return null;
+ }
+ return this.$init;
+ }
+
+ // If `dropDatabase()` is called, this model's collection will not be
+ // init-ed. It is sufficiently common to call `dropDatabase()` after
+ // `mongoose.connect()` but before creating models that we want to
+ // support this. See gh-6967
+ this.db.$internalEmitter.once('dropDatabase', () => {
+ delete this.$init;
+ });
+
+ const Promise = PromiseProvider.get();
+ const autoIndex = this.schema.options.autoIndex == null ?
+ this.db.config.autoIndex :
+ this.schema.options.autoIndex;
+ const autoCreate = this.schema.options.autoCreate == null ?
+ this.db.config.autoCreate :
+ this.schema.options.autoCreate;
+
+ const _ensureIndexes = autoIndex ?
+ cb => this.ensureIndexes({ _automatic: true }, cb) :
+ cb => cb();
+ const _createCollection = autoCreate ?
+ cb => this.createCollection({}, cb) :
+ cb => cb();
+
+ this.$init = new Promise((resolve, reject) => {
+ _createCollection(error => {
+ if (error) {
+ return reject(error);
+ }
+ _ensureIndexes(error => {
+ if (error) {
+ return reject(error);
+ }
+ resolve(this);
+ });
+ });
+ });
+
+ if (callback) {
+ this.$init.then(() => callback(), err => callback(err));
+ this.$caught = true;
+ return null;
+ } else {
+ const _catch = this.$init.catch;
+ const _this = this;
+ this.$init.catch = function() {
+ this.$caught = true;
+ return _catch.apply(_this.$init, arguments);
+ };
+ }
+
+ return this.$init;
+};
+
+
+/**
+ * Create the collection for this model. By default, if no indexes are specified,
+ * mongoose will not create the collection for the model until any documents are
+ * created. Use this method to create the collection explicitly.
+ *
+ * Note 1: You may need to call this before starting a transaction
+ * See https://docs.mongodb.com/manual/core/transactions/#transactions-and-operations
+ *
+ * Note 2: You don't have to call this if your schema contains index or unique field.
+ * In that case, just use `Model.init()`
+ *
+ * ####Example:
+ *
+ * var userSchema = new Schema({ name: String })
+ * var User = mongoose.model('User', userSchema);
+ *
+ * User.createCollection().then(function(collection) {
+ * console.log('Collection is created!');
+ * });
+ *
+ * @api public
+ * @param {Object} [options] see [MongoDB driver docs](http://mongodb.github.io/node-mongodb-native/3.1/api/Db.html#createCollection)
+ * @param {Function} [callback]
+ * @returns {Promise}
+ */
+
+Model.createCollection = function createCollection(options, callback) {
+ if (typeof options === 'string') {
+ throw new Error('You can\'t specify a new collection name in Model.createCollection.' +
+ 'This is not like Connection.createCollection. Only options are accepted here.');
+ } else if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ const schemaCollation = get(this, 'schema.options.collation', null);
+ if (schemaCollation != null) {
+ options = Object.assign({ collation: schemaCollation }, options);
+ }
+
+ return utils.promiseOrCallback(callback, cb => {
+ this.db.createCollection(this.collection.collectionName, options, utils.tick((error) => {
+ if (error) {
+ return cb(error);
+ }
+ this.collection = this.db.collection(this.collection.collectionName, options);
+ cb(null, this.collection);
+ }));
+ }, this.events);
+};
+
+/**
+ * Makes the indexes in MongoDB match the indexes defined in this model's
+ * schema. This function will drop any indexes that are not defined in
+ * the model's schema except the `_id` index, and build any indexes that
+ * are in your schema but not in MongoDB.
+ *
+ * See the [introductory blog post](http://thecodebarbarian.com/whats-new-in-mongoose-5-2-syncindexes)
+ * for more information.
+ *
+ * ####Example:
+ *
+ * const schema = new Schema({ name: { type: String, unique: true } });
+ * const Customer = mongoose.model('Customer', schema);
+ * await Customer.createIndex({ age: 1 }); // Index is not in schema
+ * // Will drop the 'age' index and create an index on `name`
+ * await Customer.syncIndexes();
+ *
+ * @param {Object} [options] options to pass to `ensureIndexes()`
+ * @param {Function} [callback] optional callback
+ * @return {Promise|undefined} Returns `undefined` if callback is specified, returns a promise if no callback.
+ * @api public
+ */
+
+Model.syncIndexes = function syncIndexes(options, callback) {
+ callback = this.$wrapCallback(callback);
+
+ const dropNonSchemaIndexes = (cb) => {
+ this.listIndexes((err, indexes) => {
+ if (err != null) {
+ return cb(err);
+ }
+
+ const schemaIndexes = this.schema.indexes();
+ const toDrop = [];
+
+ for (const index of indexes) {
+ let found = false;
+ // Never try to drop `_id` index, MongoDB server doesn't allow it
+ if (index.key._id) {
+ continue;
+ }
+
+ for (const schemaIndex of schemaIndexes) {
+ const key = schemaIndex[0];
+ const options = _decorateDiscriminatorIndexOptions(this,
+ utils.clone(schemaIndex[1]));
+
+ // If these options are different, need to rebuild the index
+ const optionKeys = ['unique', 'partialFilterExpression', 'sparse', 'expireAfterSeconds'];
+ const indexCopy = Object.assign({}, index);
+ for (const key of optionKeys) {
+ if (!(key in options) && !(key in indexCopy)) {
+ continue;
+ }
+ indexCopy[key] = options[key];
+ }
+ if (utils.deepEqual(key, index.key) &&
+ utils.deepEqual(index, indexCopy)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ toDrop.push(index.name);
+ }
+ }
+
+ if (toDrop.length === 0) {
+ return cb(null, []);
+ }
+
+ dropIndexes(toDrop, cb);
+ });
+ };
+
+ const dropIndexes = (toDrop, cb) => {
+ let remaining = toDrop.length;
+ let error = false;
+ toDrop.forEach(indexName => {
+ this.collection.dropIndex(indexName, err => {
+ if (err != null) {
+ error = true;
+ return cb(err);
+ }
+ if (!error) {
+ --remaining || cb(null, toDrop);
+ }
+ });
+ });
+ };
+
+ return utils.promiseOrCallback(callback, cb => {
+ dropNonSchemaIndexes((err, dropped) => {
+ if (err != null) {
+ return cb(err);
+ }
+ this.createIndexes(options, err => {
+ if (err != null) {
+ return cb(err);
+ }
+ cb(null, dropped);
+ });
+ });
+ }, this.events);
+};
+
+/**
+ * Lists the indexes currently defined in MongoDB. This may or may not be
+ * the same as the indexes defined in your schema depending on whether you
+ * use the [`autoIndex` option](/docs/guide.html#autoIndex) and if you
+ * build indexes manually.
+ *
+ * @param {Function} [cb] optional callback
+ * @return {Promise|undefined} Returns `undefined` if callback is specified, returns a promise if no callback.
+ * @api public
+ */
+
+Model.listIndexes = function init(callback) {
+ callback = this.$wrapCallback(callback);
+
+ const _listIndexes = cb => {
+ this.collection.listIndexes().toArray(cb);
+ };
+
+ return utils.promiseOrCallback(callback, cb => {
+ // Buffering
+ if (this.collection.buffer) {
+ this.collection.addQueue(_listIndexes, [cb]);
+ } else {
+ _listIndexes(cb);
+ }
+ }, this.events);
+};
+
+/**
+ * Sends `createIndex` commands to mongo for each index declared in the schema.
+ * The `createIndex` commands are sent in series.
+ *
+ * ####Example:
+ *
+ * Event.ensureIndexes(function (err) {
+ * if (err) return handleError(err);
+ * });
+ *
+ * After completion, an `index` event is emitted on this `Model` passing an error if one occurred.
+ *
+ * ####Example:
+ *
+ * var eventSchema = new Schema({ thing: { type: 'string', unique: true }})
+ * var Event = mongoose.model('Event', eventSchema);
+ *
+ * Event.on('index', function (err) {
+ * if (err) console.error(err); // error occurred during index creation
+ * })
+ *
+ * _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._
+ *
+ * @param {Object} [options] internal options
+ * @param {Function} [cb] optional callback
+ * @return {Promise}
+ * @api public
+ */
+
+Model.ensureIndexes = function ensureIndexes(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return utils.promiseOrCallback(callback, cb => {
+ _ensureIndexes(this, options || {}, error => {
+ if (error) {
+ return cb(error);
+ }
+ cb(null);
+ });
+ }, this.events);
+};
+
+/**
+ * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#createIndex)
+ * function.
+ *
+ * @param {Object} [options] internal options
+ * @param {Function} [cb] optional callback
+ * @return {Promise}
+ * @api public
+ */
+
+Model.createIndexes = function createIndexes(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ options = options || {};
+ options.createIndex = true;
+ return this.ensureIndexes(options, callback);
+};
+
+/*!
+ * ignore
+ */
+
+function _ensureIndexes(model, options, callback) {
+ const indexes = model.schema.indexes();
+
+ options = options || {};
+
+ const done = function(err) {
+ if (err && !model.$caught) {
+ model.emit('error', err);
+ }
+ model.emit('index', err);
+ callback && callback(err);
+ };
+
+ for (const index of indexes) {
+ const keys = Object.keys(index[0]);
+ if (keys.length === 1 && keys[0] === '_id' && index[0]._id !== 'hashed') {
+ console.warn('mongoose: Cannot specify a custom index on `_id` for ' +
+ 'model name "' + model.modelName + '", ' +
+ 'MongoDB does not allow overwriting the default `_id` index. See ' +
+ 'http://bit.ly/mongodb-id-index');
+ }
+ }
+
+ if (!indexes.length) {
+ immediate(function() {
+ done();
+ });
+ return;
+ }
+ // Indexes are created one-by-one to support how MongoDB < 2.4 deals
+ // with background indexes.
+
+ const indexSingleDone = function(err, fields, options, name) {
+ model.emit('index-single-done', err, fields, options, name);
+ };
+ const indexSingleStart = function(fields, options) {
+ model.emit('index-single-start', fields, options);
+ };
+
+ const create = function() {
+ if (options._automatic) {
+ if (model.schema.options.autoIndex === false ||
+ (model.schema.options.autoIndex == null && model.db.config.autoIndex === false)) {
+ return done();
+ }
+ }
+
+ const index = indexes.shift();
+ if (!index) {
+ return done();
+ }
+
+ const indexFields = utils.clone(index[0]);
+ const indexOptions = utils.clone(index[1]);
+
+ _decorateDiscriminatorIndexOptions(model, indexOptions);
+ if ('safe' in options) {
+ _handleSafe(options);
+ }
+ applyWriteConcern(model.schema, indexOptions);
+
+ indexSingleStart(indexFields, options);
+ let useCreateIndex = !!model.base.options.useCreateIndex;
+ if ('useCreateIndex' in model.db.config) {
+ useCreateIndex = !!model.db.config.useCreateIndex;
+ }
+ if ('createIndex' in options) {
+ useCreateIndex = !!options.createIndex;
+ }
+
+ const methodName = useCreateIndex ? 'createIndex' : 'ensureIndex';
+ model.collection[methodName](indexFields, indexOptions, utils.tick(function(err, name) {
+ indexSingleDone(err, indexFields, indexOptions, name);
+ if (err) {
+ return done(err);
+ }
+ create();
+ }));
+ };
+
+ immediate(function() {
+ // If buffering is off, do this manually.
+ if (options._automatic && !model.collection.collection) {
+ model.collection.addQueue(create, []);
+ } else {
+ create();
+ }
+ });
+}
+
+function _decorateDiscriminatorIndexOptions(model, indexOptions) {
+ // If the model is a discriminator and it has a unique index, add a
+ // partialFilterExpression by default so the unique index will only apply
+ // to that discriminator.
+ if (model.baseModelName != null && indexOptions.unique &&
+ !('partialFilterExpression' in indexOptions) &&
+ !('sparse' in indexOptions)) {
+ indexOptions.partialFilterExpression = {
+ [model.schema.options.discriminatorKey]: model.modelName
+ };
+ }
+ return indexOptions;
+}
+
+const safeDeprecationWarning = 'Mongoose: the `safe` option for `save()` is ' +
+ 'deprecated. Use the `w` option instead: http://bit.ly/mongoose-save';
+
+const _handleSafe = util.deprecate(function _handleSafe(options) {
+ if (options.safe) {
+ if (typeof options.safe === 'boolean') {
+ options.w = options.safe;
+ delete options.safe;
+ }
+ if (typeof options.safe === 'object') {
+ options.w = options.safe.w;
+ options.j = options.safe.j;
+ options.wtimeout = options.safe.wtimeout;
+ delete options.safe;
+ }
+ }
+}, safeDeprecationWarning);
+
+/**
+ * Schema the model uses.
+ *
+ * @property schema
+ * @receiver Model
+ * @api public
+ * @memberOf Model
+ */
+
+Model.schema;
+
+/*!
+ * Connection instance the model uses.
+ *
+ * @property db
+ * @api public
+ * @memberOf Model
+ */
+
+Model.db;
+
+/*!
+ * Collection the model uses.
+ *
+ * @property collection
+ * @api public
+ * @memberOf Model
+ */
+
+Model.collection;
+
+/**
+ * Base Mongoose instance the model uses.
+ *
+ * @property base
+ * @api public
+ * @memberOf Model
+ */
+
+Model.base;
+
+/**
+ * Registered discriminators for this model.
+ *
+ * @property discriminators
+ * @api public
+ * @memberOf Model
+ */
+
+Model.discriminators;
+
+/**
+ * Translate any aliases fields/conditions so the final query or document object is pure
+ *
+ * ####Example:
+ *
+ * Character
+ * .find(Character.translateAliases({
+ * '名': 'Eddard Stark' // Alias for 'name'
+ * })
+ * .exec(function(err, characters) {})
+ *
+ * ####Note:
+ * Only translate arguments of object type anything else is returned raw
+ *
+ * @param {Object} raw fields/conditions that may contain aliased keys
+ * @return {Object} the translated 'pure' fields/conditions
+ */
+Model.translateAliases = function translateAliases(fields) {
+ const aliases = this.schema.aliases;
+
+ if (typeof fields === 'object') {
+ // Fields is an object (query conditions or document fields)
+ for (const key in fields) {
+ if (aliases[key]) {
+ fields[aliases[key]] = fields[key];
+ delete fields[key];
+ }
+ }
+
+ return fields;
+ } else {
+ // Don't know typeof fields
+ return fields;
+ }
+};
+
+/**
+ * Removes all documents that match `conditions` from the collection.
+ * To remove just the first document that matches `conditions`, set the `single`
+ * option to true.
+ *
+ * ####Example:
+ *
+ * Character.remove({ name: 'Eddard Stark' }, function (err) {});
+ *
+ * ####Note:
+ *
+ * This method sends a remove command directly to MongoDB, no Mongoose documents
+ * are involved. Because no Mongoose documents are involved, _no middleware
+ * (hooks) are executed_.
+ *
+ * @param {Object} conditions
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.remove = function remove(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+
+ // get the mongodb collection object
+ const mq = new this.Query({}, {}, this, this.collection);
+
+ callback = this.$wrapCallback(callback);
+
+ return mq.remove(conditions, callback);
+};
+
+/**
+ * Deletes the first document that matches `conditions` from the collection.
+ * Behaves like `remove()`, but deletes at most one document regardless of the
+ * `single` option.
+ *
+ * ####Example:
+ *
+ * Character.deleteOne({ name: 'Eddard Stark' }, function (err) {});
+ *
+ * ####Note:
+ *
+ * Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
+ *
+ * @param {Object} conditions
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.deleteOne = function deleteOne(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+
+ // get the mongodb collection object
+ const mq = new this.Query(conditions, {}, this, this.collection);
+
+ callback = this.$wrapCallback(callback);
+
+ return mq.deleteOne(callback);
+};
+
+/**
+ * Deletes all of the documents that match `conditions` from the collection.
+ * Behaves like `remove()`, but deletes all documents that match `conditions`
+ * regardless of the `single` option.
+ *
+ * ####Example:
+ *
+ * Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }, function (err) {});
+ *
+ * ####Note:
+ *
+ * Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
+ *
+ * @param {Object} conditions
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.deleteMany = function deleteMany(conditions, options, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ options = null;
+ }
+ else if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ // get the mongodb collection object
+ const mq = new this.Query(conditions, {}, this, this.collection);
+ mq.setOptions(options);
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return mq.deleteMany(callback);
+};
+
+/**
+ * Finds documents
+ *
+ * The `conditions` are cast to their respective SchemaTypes before the command is sent.
+ *
+ * ####Examples:
+ *
+ * // named john and at least 18
+ * MyModel.find({ name: 'john', age: { $gte: 18 }});
+ *
+ * // executes immediately, passing results to callback
+ * MyModel.find({ name: 'john', age: { $gte: 18 }}, function (err, docs) {});
+ *
+ * // name LIKE john and only selecting the "name" and "friends" fields, executing immediately
+ * MyModel.find({ name: /john/i }, 'name friends', function (err, docs) { })
+ *
+ * // passing options
+ * MyModel.find({ name: /john/i }, null, { skip: 10 })
+ *
+ * // passing options and executing immediately
+ * MyModel.find({ name: /john/i }, null, { skip: 10 }, function (err, docs) {});
+ *
+ * // executing a query explicitly
+ * var query = MyModel.find({ name: /john/i }, null, { skip: 10 })
+ * query.exec(function (err, docs) {});
+ *
+ * // using the promise returned from executing a query
+ * var query = MyModel.find({ name: /john/i }, null, { skip: 10 });
+ * var promise = query.exec();
+ * promise.addBack(function (err, docs) {});
+ *
+ * @param {Object} conditions
+ * @param {Object|String} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see field selection #query_Query-select
+ * @see promise #promise-js
+ * @api public
+ */
+
+Model.find = function find(conditions, projection, options, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ projection = null;
+ options = null;
+ } else if (typeof projection === 'function') {
+ callback = projection;
+ projection = null;
+ options = null;
+ } else if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ const mq = new this.Query({}, {}, this, this.collection);
+ mq.select(projection);
+
+ mq.setOptions(options);
+ if (this.schema.discriminatorMapping &&
+ this.schema.discriminatorMapping.isRoot &&
+ mq.selectedInclusively()) {
+ // Need to select discriminator key because original schema doesn't have it
+ mq.select(this.schema.options.discriminatorKey);
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return mq.find(conditions, callback);
+};
+
+/**
+ * Finds a single document by its _id field. `findById(id)` is almost*
+ * equivalent to `findOne({ _id: id })`. If you want to query by a document's
+ * `_id`, use `findById()` instead of `findOne()`.
+ *
+ * The `id` is cast based on the Schema before sending the command.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOne()`
+ *
+ * \* Except for how it treats `undefined`. If you use `findOne()`, you'll see
+ * that `findOne(undefined)` and `findOne({ _id: undefined })` are equivalent
+ * to `findOne({})` and return arbitrary documents. However, mongoose
+ * translates `findById(undefined)` into `findOne({ _id: null })`.
+ *
+ * ####Example:
+ *
+ * // find adventure by id and execute immediately
+ * Adventure.findById(id, function (err, adventure) {});
+ *
+ * // same as above
+ * Adventure.findById(id).exec(callback);
+ *
+ * // select only the adventures name and length
+ * Adventure.findById(id, 'name length', function (err, adventure) {});
+ *
+ * // same as above
+ * Adventure.findById(id, 'name length').exec(callback);
+ *
+ * // include all properties except for `length`
+ * Adventure.findById(id, '-length').exec(function (err, adventure) {});
+ *
+ * // passing options (in this case return the raw js objects, not mongoose documents by passing `lean`
+ * Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
+ *
+ * // same as above
+ * Adventure.findById(id, 'name').lean().exec(function (err, doc) {});
+ *
+ * @param {Object|String|Number} id value of `_id` to query by
+ * @param {Object|String} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see field selection #query_Query-select
+ * @see lean queries #query_Query-lean
+ * @api public
+ */
+
+Model.findById = function findById(id, projection, options, callback) {
+ if (typeof id === 'undefined') {
+ id = null;
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return this.findOne({_id: id}, projection, options, callback);
+};
+
+/**
+ * Finds one document.
+ *
+ * The `conditions` are cast to their respective SchemaTypes before the command is sent.
+ *
+ * *Note:* `conditions` is optional, and if `conditions` is null or undefined,
+ * mongoose will send an empty `findOne` command to MongoDB, which will return
+ * an arbitrary document. If you're querying by `_id`, use `findById()` instead.
+ *
+ * ####Example:
+ *
+ * // find one iphone adventures - iphone adventures??
+ * Adventure.findOne({ type: 'iphone' }, function (err, adventure) {});
+ *
+ * // same as above
+ * Adventure.findOne({ type: 'iphone' }).exec(function (err, adventure) {});
+ *
+ * // select only the adventures name
+ * Adventure.findOne({ type: 'iphone' }, 'name', function (err, adventure) {});
+ *
+ * // same as above
+ * Adventure.findOne({ type: 'iphone' }, 'name').exec(function (err, adventure) {});
+ *
+ * // specify options, in this case lean
+ * Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }, callback);
+ *
+ * // same as above
+ * Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }).exec(callback);
+ *
+ * // chaining findOne queries (same as above)
+ * Adventure.findOne({ type: 'iphone' }).select('name').lean().exec(callback);
+ *
+ * @param {Object} [conditions]
+ * @param {Object|String} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see field selection #query_Query-select
+ * @see lean queries #query_Query-lean
+ * @api public
+ */
+
+Model.findOne = function findOne(conditions, projection, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ } else if (typeof projection === 'function') {
+ callback = projection;
+ projection = null;
+ options = null;
+ } else if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ projection = null;
+ options = null;
+ }
+
+ // get the mongodb collection object
+ const mq = new this.Query({}, {}, this, this.collection);
+ mq.select(projection);
+
+ mq.setOptions(options);
+ if (this.schema.discriminatorMapping &&
+ this.schema.discriminatorMapping.isRoot &&
+ mq.selectedInclusively()) {
+ mq.select(this.schema.options.discriminatorKey);
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return mq.findOne(conditions, callback);
+};
+
+/**
+ * Estimates the number of documents in the MongoDB collection. Faster than
+ * using `countDocuments()` for large collections because
+ * `estimatedDocumentCount()` uses collection metadata rather than scanning
+ * the entire collection.
+ *
+ * ####Example:
+ *
+ * const numAdventures = Adventure.estimatedDocumentCount();
+ *
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.estimatedDocumentCount = function estimatedDocumentCount(options, callback) {
+ // get the mongodb collection object
+ const mq = new this.Query({}, {}, this, this.collection);
+
+ callback = this.$wrapCallback(callback);
+
+ return mq.estimatedDocumentCount(options, callback);
+};
+
+/**
+ * Counts number of documents matching `filter` in a database collection.
+ *
+ * ####Example:
+ *
+ * Adventure.countDocuments({ type: 'jungle' }, function (err, count) {
+ * console.log('there are %d jungle adventures', count);
+ * });
+ *
+ * If you want to count all documents in a large collection,
+ * use the [`estimatedDocumentCount()` function](/docs/api.html#model_Model.estimatedDocumentCount)
+ * instead. If you call `countDocuments({})`, MongoDB will always execute
+ * a full collection scan and **not** use any indexes.
+ *
+ * The `countDocuments()` function is similar to `count()`, but there are a
+ * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments).
+ * Below are the operators that `count()` supports but `countDocuments()` does not,
+ * and the suggested replacement:
+ *
+ * - `$where`: [`$expr`](https://docs.mongodb.com/manual/reference/operator/query/expr/)
+ * - `$near`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$center`](https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center)
+ * - `$nearSphere`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere)
+ *
+ * @param {Object} filter
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.countDocuments = function countDocuments(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+
+ // get the mongodb collection object
+ const mq = new this.Query({}, {}, this, this.collection);
+
+ callback = this.$wrapCallback(callback);
+
+ return mq.countDocuments(conditions, callback);
+};
+
+/**
+ * Counts number of documents that match `filter` in a database collection.
+ *
+ * This method is deprecated. If you want to count the number of documents in
+ * a collection, e.g. `count({})`, use the [`estimatedDocumentCount()` function](/docs/api.html#model_Model.estimatedDocumentCount)
+ * instead. Otherwise, use the [`countDocuments()`](/docs/api.html#model_Model.countDocuments) function instead.
+ *
+ * ####Example:
+ *
+ * Adventure.count({ type: 'jungle' }, function (err, count) {
+ * if (err) ..
+ * console.log('there are %d jungle adventures', count);
+ * });
+ *
+ * @deprecated
+ * @param {Object} filter
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.count = function count(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+
+ // get the mongodb collection object
+ const mq = new this.Query({}, {}, this, this.collection);
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return mq.count(conditions, callback);
+};
+
+/**
+ * Creates a Query for a `distinct` operation.
+ *
+ * Passing a `callback` immediately executes the query.
+ *
+ * ####Example
+ *
+ * Link.distinct('url', { clicks: {$gt: 100}}, function (err, result) {
+ * if (err) return handleError(err);
+ *
+ * assert(Array.isArray(result));
+ * console.log('unique urls with more than 100 clicks', result);
+ * })
+ *
+ * var query = Link.distinct('url');
+ * query.exec(callback);
+ *
+ * @param {String} field
+ * @param {Object} [conditions] optional
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.distinct = function distinct(field, conditions, callback) {
+ // get the mongodb collection object
+ const mq = new this.Query({}, {}, this, this.collection);
+
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return mq.distinct(field, conditions, callback);
+};
+
+/**
+ * Creates a Query, applies the passed conditions, and returns the Query.
+ *
+ * For example, instead of writing:
+ *
+ * User.find({age: {$gte: 21, $lte: 65}}, callback);
+ *
+ * we can instead write:
+ *
+ * User.where('age').gte(21).lte(65).exec(callback);
+ *
+ * Since the Query class also supports `where` you can continue chaining
+ *
+ * User
+ * .where('age').gte(21).lte(65)
+ * .where('name', /^b/i)
+ * ... etc
+ *
+ * @param {String} path
+ * @param {Object} [val] optional value
+ * @return {Query}
+ * @api public
+ */
+
+Model.where = function where(path, val) {
+ void val; // eslint
+ // get the mongodb collection object
+ const mq = new this.Query({}, {}, this, this.collection).find({});
+ return mq.where.apply(mq, arguments);
+};
+
+/**
+ * Creates a `Query` and specifies a `$where` condition.
+ *
+ * Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model.
+ *
+ * Blog.$where('this.username.indexOf("val") !== -1').exec(function (err, docs) {});
+ *
+ * @param {String|Function} argument is a javascript string or anonymous function
+ * @method $where
+ * @memberOf Model
+ * @return {Query}
+ * @see Query.$where #query_Query-%24where
+ * @api public
+ */
+
+Model.$where = function $where() {
+ const mq = new this.Query({}, {}, this, this.collection).find({});
+ return mq.$where.apply(mq, arguments);
+};
+
+/**
+ * Issues a mongodb findAndModify update command.
+ *
+ * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
+ *
+ * ####Options:
+ *
+ * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
+ * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
+ * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
+ *
+ * ####Examples:
+ *
+ * A.findOneAndUpdate(conditions, update, options, callback) // executes
+ * A.findOneAndUpdate(conditions, update, options) // returns Query
+ * A.findOneAndUpdate(conditions, update, callback) // executes
+ * A.findOneAndUpdate(conditions, update) // returns Query
+ * A.findOneAndUpdate() // returns Query
+ *
+ * ####Note:
+ *
+ * All top level update keys which are not `atomic` operation names are treated as set operations:
+ *
+ * ####Example:
+ *
+ * var query = { name: 'borne' };
+ * Model.findOneAndUpdate(query, { name: 'jason bourne' }, options, callback)
+ *
+ * // is sent as
+ * Model.findOneAndUpdate(query, { $set: { name: 'jason bourne' }}, options, callback)
+ *
+ * This helps prevent accidentally overwriting your document with `{ name: 'jason bourne' }`.
+ *
+ * ####Note:
+ *
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are not executed by default.
+ *
+ * - defaults. Use the `setDefaultsOnInsert` option to override.
+ *
+ * `findAndModify` helpers support limited validation. You can
+ * enable these by setting the `runValidators` options,
+ * respectively.
+ *
+ * If you need full-fledged validation, use the traditional approach of first
+ * retrieving the document.
+ *
+ * Model.findById(id, function (err, doc) {
+ * if (err) ..
+ * doc.name = 'jason bourne';
+ * doc.save(callback);
+ * });
+ *
+ * @param {Object} [conditions]
+ * @param {Object} [update]
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](http://mongoosejs.com/docs/api.html#query_Query-lean).
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @api public
+ */
+
+Model.findOneAndUpdate = function(conditions, update, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ } else if (arguments.length === 1) {
+ if (typeof conditions === 'function') {
+ const msg = 'Model.findOneAndUpdate(): First argument must not be a function.\n\n'
+ + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate(update)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate()\n';
+ throw new TypeError(msg);
+ }
+ update = conditions;
+ conditions = undefined;
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ let fields;
+ if (options) {
+ fields = options.fields || options.projection;
+ }
+
+ update = utils.clone(update, {
+ depopulate: true,
+ _isNested: true
+ });
+
+ _decorateUpdateWithVersionKey(update, options, this.schema.options.versionKey);
+
+ const mq = new this.Query({}, {}, this, this.collection);
+ mq.select(fields);
+
+ return mq.findOneAndUpdate(conditions, update, options, callback);
+};
+
+/*!
+ * Decorate the update with a version key, if necessary
+ */
+
+function _decorateUpdateWithVersionKey(update, options, versionKey) {
+ if (!versionKey || !get(options, 'upsert', false)) {
+ return;
+ }
+
+ const updatedPaths = modifiedPaths(update);
+ if (!updatedPaths[versionKey]) {
+ if (options.overwrite) {
+ update[versionKey] = 0;
+ } else {
+ if (!update.$setOnInsert) {
+ update.$setOnInsert = {};
+ }
+ update.$setOnInsert[versionKey] = 0;
+ }
+ }
+}
+
+/**
+ * Issues a mongodb findAndModify update command by a document's _id field.
+ * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`.
+ *
+ * Finds a matching document, updates it according to the `update` arg,
+ * passing any `options`, and returns the found document (if any) to the
+ * callback. The query executes immediately if `callback` is passed else a
+ * Query object is returned.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndUpdate()`
+ *
+ * ####Options:
+ *
+ * - `new`: bool - true to return the modified document rather than the original. defaults to false
+ * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `select`: sets the document fields to return
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
+ *
+ * ####Examples:
+ *
+ * A.findByIdAndUpdate(id, update, options, callback) // executes
+ * A.findByIdAndUpdate(id, update, options) // returns Query
+ * A.findByIdAndUpdate(id, update, callback) // executes
+ * A.findByIdAndUpdate(id, update) // returns Query
+ * A.findByIdAndUpdate() // returns Query
+ *
+ * ####Note:
+ *
+ * All top level update keys which are not `atomic` operation names are treated as set operations:
+ *
+ * ####Example:
+ *
+ * Model.findByIdAndUpdate(id, { name: 'jason bourne' }, options, callback)
+ *
+ * // is sent as
+ * Model.findByIdAndUpdate(id, { $set: { name: 'jason bourne' }}, options, callback)
+ *
+ * This helps prevent accidentally overwriting your document with `{ name: 'jason bourne' }`.
+ *
+ * ####Note:
+ *
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are not executed by default.
+ *
+ * - defaults. Use the `setDefaultsOnInsert` option to override.
+ *
+ * `findAndModify` helpers support limited validation. You can
+ * enable these by setting the `runValidators` options,
+ * respectively.
+ *
+ * If you need full-fledged validation, use the traditional approach of first
+ * retrieving the document.
+ *
+ * Model.findById(id, function (err, doc) {
+ * if (err) ..
+ * doc.name = 'jason bourne';
+ * doc.save(callback);
+ * });
+ *
+ * @param {Object|Number|String} id value of `_id` to query by
+ * @param {Object} [update]
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](http://mongoosejs.com/docs/api.html#query_Query-lean).
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see Model.findOneAndUpdate #model_Model.findOneAndUpdate
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @api public
+ */
+
+Model.findByIdAndUpdate = function(id, update, options, callback) {
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+ if (arguments.length === 1) {
+ if (typeof id === 'function') {
+ const msg = 'Model.findByIdAndUpdate(): First argument must not be a function.\n\n'
+ + ' ' + this.modelName + '.findByIdAndUpdate(id, callback)\n'
+ + ' ' + this.modelName + '.findByIdAndUpdate(id)\n'
+ + ' ' + this.modelName + '.findByIdAndUpdate()\n';
+ throw new TypeError(msg);
+ }
+ return this.findOneAndUpdate({_id: id}, undefined);
+ }
+
+ // if a model is passed in instead of an id
+ if (id instanceof Document) {
+ id = id._id;
+ }
+
+ return this.findOneAndUpdate.call(this, {_id: id}, update, options, callback);
+};
+
+/**
+ * Issue a MongoDB `findOneAndDelete()` command.
+ *
+ * Finds a matching document, removes it, and passes the found document
+ * (if any) to the callback.
+ *
+ * Executes immediately if `callback` is passed else a Query object is returned.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndDelete()`
+ *
+ * This function differs slightly from `Model.findOneAndRemove()` in that
+ * `findOneAndRemove()` becomes a [MongoDB `findAndModify()` command](https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/),
+ * as opposed to a `findOneAndDelete()` command. For most mongoose use cases,
+ * this distinction is purely pedantic. You should use `findOneAndDelete()`
+ * unless you have a good reason not to.
+ *
+ * ####Options:
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `select`: sets the document fields to return
+ * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
+ *
+ * ####Examples:
+ *
+ * A.findOneAndDelete(conditions, options, callback) // executes
+ * A.findOneAndDelete(conditions, options) // return Query
+ * A.findOneAndDelete(conditions, callback) // executes
+ * A.findOneAndDelete(conditions) // returns Query
+ * A.findOneAndDelete() // returns Query
+ *
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are not executed by default.
+ *
+ * - defaults. Use the `setDefaultsOnInsert` option to override.
+ *
+ * `findAndModify` helpers support limited validation. You can
+ * enable these by setting the `runValidators` options,
+ * respectively.
+ *
+ * If you need full-fledged validation, use the traditional approach of first
+ * retrieving the document.
+ *
+ * Model.findById(id, function (err, doc) {
+ * if (err) ..
+ * doc.name = 'jason bourne';
+ * doc.save(callback);
+ * });
+ *
+ * @param {Object} conditions
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.findOneAndDelete = function(conditions, options, callback) {
+ if (arguments.length === 1 && typeof conditions === 'function') {
+ const msg = 'Model.findOneAndDelete(): First argument must not be a function.\n\n'
+ + ' ' + this.modelName + '.findOneAndDelete(conditions, callback)\n'
+ + ' ' + this.modelName + '.findOneAndDelete(conditions)\n'
+ + ' ' + this.modelName + '.findOneAndDelete()\n';
+ throw new TypeError(msg);
+ }
+
+ if (typeof options === 'function') {
+ callback = options;
+ options = undefined;
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ let fields;
+ if (options) {
+ fields = options.select;
+ options.select = undefined;
+ }
+
+ const mq = new this.Query({}, {}, this, this.collection);
+ mq.select(fields);
+
+ return mq.findOneAndDelete(conditions, options, callback);
+};
+
+/**
+ * Issue a MongoDB `findOneAndDelete()` command by a document's _id field.
+ * In other words, `findByIdAndDelete(id)` is a shorthand for
+ * `findOneAndDelete({ _id: id })`.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndDelete()`
+ *
+ * @param {Object|Number|String} id value of `_id` to query by
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see Model.findOneAndRemove #model_Model.findOneAndRemove
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ */
+
+Model.findByIdAndDelete = function(id, options, callback) {
+ if (arguments.length === 1 && typeof id === 'function') {
+ const msg = 'Model.findByIdAndDelete(): First argument must not be a function.\n\n'
+ + ' ' + this.modelName + '.findByIdAndDelete(id, callback)\n'
+ + ' ' + this.modelName + '.findByIdAndDelete(id)\n'
+ + ' ' + this.modelName + '.findByIdAndDelete()\n';
+ throw new TypeError(msg);
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return this.findOneAndDelete({_id: id}, options, callback);
+};
+
+/**
+ * Issue a MongoDB `findOneAndReplace()` command.
+ *
+ * Finds a matching document, replaces it with the provided doc, and passes the
+ * returned doc to the callback.
+ *
+ * Executes immediately if `callback` is passed else a Query object is returned.
+ *
+ * This function triggers the following query middleware.
+ *
+ * - `findOneAndReplace()`
+ *
+ * ####Options:
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `select`: sets the document fields to return
+ * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
+ *
+ * ####Examples:
+ *
+ * A.findOneAndReplace(conditions, options, callback) // executes
+ * A.findOneAndReplace(conditions, options) // return Query
+ * A.findOneAndReplace(conditions, callback) // executes
+ * A.findOneAndReplace(conditions) // returns Query
+ * A.findOneAndReplace() // returns Query
+ *
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are not executed by default.
+ *
+ * - defaults. Use the `setDefaultsOnInsert` option to override.
+ *
+ * @param {Object} conditions
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.findOneAndReplace = function(conditions, options, callback) {
+ if (arguments.length === 1 && typeof conditions === 'function') {
+ const msg = 'Model.findOneAndDelete(): First argument must not be a function.\n\n'
+ + ' ' + this.modelName + '.findOneAndDelete(conditions, callback)\n'
+ + ' ' + this.modelName + '.findOneAndDelete(conditions)\n'
+ + ' ' + this.modelName + '.findOneAndDelete()\n';
+ throw new TypeError(msg);
+ }
+
+ if (typeof options === 'function') {
+ callback = options;
+ options = undefined;
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ let fields;
+ if (options) {
+ fields = options.select;
+ options.select = undefined;
+ }
+
+ const mq = new this.Query({}, {}, this, this.collection);
+ mq.select(fields);
+
+ return mq.findOneAndReplace(conditions, options, callback);
+};
+
+/**
+ * Issue a mongodb findAndModify remove command.
+ *
+ * Finds a matching document, removes it, passing the found document (if any) to the callback.
+ *
+ * Executes immediately if `callback` is passed else a Query object is returned.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndRemove()`
+ *
+ * ####Options:
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `select`: sets the document fields to return
+ * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
+ *
+ * ####Examples:
+ *
+ * A.findOneAndRemove(conditions, options, callback) // executes
+ * A.findOneAndRemove(conditions, options) // return Query
+ * A.findOneAndRemove(conditions, callback) // executes
+ * A.findOneAndRemove(conditions) // returns Query
+ * A.findOneAndRemove() // returns Query
+ *
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are not executed by default.
+ *
+ * - defaults. Use the `setDefaultsOnInsert` option to override.
+ *
+ * `findAndModify` helpers support limited validation. You can
+ * enable these by setting the `runValidators` options,
+ * respectively.
+ *
+ * If you need full-fledged validation, use the traditional approach of first
+ * retrieving the document.
+ *
+ * Model.findById(id, function (err, doc) {
+ * if (err) ..
+ * doc.name = 'jason bourne';
+ * doc.save(callback);
+ * });
+ *
+ * @param {Object} conditions
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @api public
+ */
+
+Model.findOneAndRemove = function(conditions, options, callback) {
+ if (arguments.length === 1 && typeof conditions === 'function') {
+ const msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n'
+ + ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n'
+ + ' ' + this.modelName + '.findOneAndRemove(conditions)\n'
+ + ' ' + this.modelName + '.findOneAndRemove()\n';
+ throw new TypeError(msg);
+ }
+
+ if (typeof options === 'function') {
+ callback = options;
+ options = undefined;
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ let fields;
+ if (options) {
+ fields = options.select;
+ options.select = undefined;
+ }
+
+ const mq = new this.Query({}, {}, this, this.collection);
+ mq.select(fields);
+
+ return mq.findOneAndRemove(conditions, options, callback);
+};
+
+/**
+ * Issue a mongodb findAndModify remove command by a document's _id field. `findByIdAndRemove(id, ...)` is equivalent to `findOneAndRemove({ _id: id }, ...)`.
+ *
+ * Finds a matching document, removes it, passing the found document (if any) to the callback.
+ *
+ * Executes immediately if `callback` is passed, else a `Query` object is returned.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndRemove()`
+ *
+ * ####Options:
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `select`: sets the document fields to return
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
+ *
+ * ####Examples:
+ *
+ * A.findByIdAndRemove(id, options, callback) // executes
+ * A.findByIdAndRemove(id, options) // return Query
+ * A.findByIdAndRemove(id, callback) // executes
+ * A.findByIdAndRemove(id) // returns Query
+ * A.findByIdAndRemove() // returns Query
+ *
+ * @param {Object|Number|String} id value of `_id` to query by
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @see Model.findOneAndRemove #model_Model.findOneAndRemove
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ */
+
+Model.findByIdAndRemove = function(id, options, callback) {
+ if (arguments.length === 1 && typeof id === 'function') {
+ const msg = 'Model.findByIdAndRemove(): First argument must not be a function.\n\n'
+ + ' ' + this.modelName + '.findByIdAndRemove(id, callback)\n'
+ + ' ' + this.modelName + '.findByIdAndRemove(id)\n'
+ + ' ' + this.modelName + '.findByIdAndRemove()\n';
+ throw new TypeError(msg);
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return this.findOneAndRemove({_id: id}, options, callback);
+};
+
+/**
+ * Shortcut for saving one or more documents to the database.
+ * `MyModel.create(docs)` does `new MyModel(doc).save()` for every doc in
+ * docs.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `save()`
+ *
+ * ####Example:
+ *
+ * // pass a spread of docs and a callback
+ * Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
+ * if (err) // ...
+ * });
+ *
+ * // pass an array of docs
+ * var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
+ * Candy.create(array, function (err, candies) {
+ * if (err) // ...
+ *
+ * var jellybean = candies[0];
+ * var snickers = candies[1];
+ * // ...
+ * });
+ *
+ * // callback is optional; use the returned promise if you like:
+ * var promise = Candy.create({ type: 'jawbreaker' });
+ * promise.then(function (jawbreaker) {
+ * // ...
+ * })
+ *
+ * @param {Array|Object} docs Documents to insert, as a spread or array
+ * @param {Object} [options] Options passed down to `save()`. To specify `options`, `docs` **must** be an array, not a spread.
+ * @param {Function} [callback] callback
+ * @return {Promise}
+ * @api public
+ */
+
+Model.create = function create(doc, options, callback) {
+ let args;
+ let cb;
+ const discriminatorKey = this.schema.options.discriminatorKey;
+
+ if (Array.isArray(doc)) {
+ args = doc;
+ cb = typeof options === 'function' ? options : callback;
+ options = options != null && typeof options === 'object' ? options : {};
+ } else {
+ const last = arguments[arguments.length - 1];
+ options = {};
+ // Handle falsy callbacks re: #5061
+ if (typeof last === 'function' || !last) {
+ cb = last;
+ args = utils.args(arguments, 0, arguments.length - 1);
+ } else {
+ args = utils.args(arguments);
+ }
+ }
+
+ if (cb) {
+ cb = this.$wrapCallback(cb);
+ }
+
+ return utils.promiseOrCallback(cb, cb => {
+ if (args.length === 0) {
+ return cb(null);
+ }
+
+ const toExecute = [];
+ let firstError;
+ args.forEach(doc => {
+ toExecute.push(callback => {
+ const Model = this.discriminators && doc[discriminatorKey] != null ?
+ this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this, doc[discriminatorKey]) :
+ this;
+ if (Model == null) {
+ throw new Error(`Discriminator "${doc[discriminatorKey]}" not ` +
+ `found for model "${this.modelName}"`);
+ }
+ let toSave = doc;
+ const callbackWrapper = (error, doc) => {
+ if (error) {
+ if (!firstError) {
+ firstError = error;
+ }
+ return callback(null, { error: error });
+ }
+ callback(null, { doc: doc });
+ };
+
+ if (!(toSave instanceof Model)) {
+ try {
+ toSave = new Model(toSave);
+ } catch (error) {
+ return callbackWrapper(error);
+ }
+ }
+
+ toSave.save(options, callbackWrapper);
+ });
+ });
+
+ parallel(toExecute, (error, res) => {
+ const savedDocs = [];
+ const len = res.length;
+ for (let i = 0; i < len; ++i) {
+ if (res[i].doc) {
+ savedDocs.push(res[i].doc);
+ }
+ }
+
+ if (firstError) {
+ return cb(firstError, savedDocs);
+ }
+
+ if (doc instanceof Array) {
+ cb(null, savedDocs);
+ } else {
+ cb.apply(this, [null].concat(savedDocs));
+ }
+ });
+ }, this.events);
+};
+
+/**
+ * _Requires a replica set running MongoDB >= 3.6.0._ Watches the
+ * underlying collection for changes using
+ * [MongoDB change streams](https://docs.mongodb.com/manual/changeStreams/).
+ *
+ * This function does **not** trigger any middleware. In particular, it
+ * does **not** trigger aggregate middleware.
+ *
+ * The ChangeStream object is an event emitter that emits the following events:
+ *
+ * - 'change': A change occurred, see below example
+ * - 'error': An unrecoverable error occurred. In particular, change streams currently error out if they lose connection to the replica set primary. Follow [this GitHub issue](https://github.com/Automattic/mongoose/issues/6799) for updates.
+ * - 'end': Emitted if the underlying stream is closed
+ * - 'close': Emitted if the underlying stream is closed
+ *
+ * ####Example:
+ *
+ * const doc = await Person.create({ name: 'Ned Stark' });
+ * const changeStream = Person.watch().on('change', change => console.log(change));
+ * // Will print from the above `console.log()`:
+ * // { _id: { _data: ... },
+ * // operationType: 'delete',
+ * // ns: { db: 'mydb', coll: 'Person' },
+ * // documentKey: { _id: 5a51b125c5500f5aa094c7bd } }
+ * await doc.remove();
+ *
+ * @param {Array} [pipeline]
+ * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#watch)
+ * @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter
+ * @api public
+ */
+
+Model.watch = function(pipeline, options) {
+ return new ChangeStream(this, pipeline, options);
+};
+
+/**
+ * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://docs.mongodb.com/manual/release-notes/3.6/#client-sessions)
+ * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/),
+ * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html).
+ *
+ * Calling `MyModel.startSession()` is equivalent to calling `MyModel.db.startSession()`.
+ *
+ * This function does not trigger any middleware.
+ *
+ * ####Example:
+ *
+ * const session = await Person.startSession();
+ * let doc = await Person.findOne({ name: 'Ned Stark' }, null, { session });
+ * await doc.remove();
+ * // `doc` will always be null, even if reading from a replica set
+ * // secondary. Without causal consistency, it is possible to
+ * // get a doc back from the below query if the query reads from a
+ * // secondary that is experiencing replication lag.
+ * doc = await Person.findOne({ name: 'Ned Stark' }, null, { session, readPreference: 'secondary' });
+ *
+ * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#startSession)
+ * @param {Boolean} [options.causalConsistency=true] set to false to disable causal consistency
+ * @param {Function} [callback]
+ * @return {Promise} promise that resolves to a MongoDB driver `ClientSession`
+ * @api public
+ */
+
+Model.startSession = function() {
+ return this.db.startSession.apply(this.db, arguments);
+};
+
+/**
+ * Shortcut for validating an array of documents and inserting them into
+ * MongoDB if they're all valid. This function is faster than `.create()`
+ * because it only sends one operation to the server, rather than one for each
+ * document.
+ *
+ * Mongoose always validates each document **before** sending `insertMany`
+ * to MongoDB. So if one document has a validation error, no documents will
+ * be saved, unless you set
+ * [the `ordered` option to false](https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/#error-handling).
+ *
+ * This function does **not** trigger save middleware.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `insertMany()`
+ *
+ * ####Example:
+ *
+ * var arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
+ * Movies.insertMany(arr, function(error, docs) {});
+ *
+ * @param {Array|Object|*} doc(s)
+ * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#insertMany)
+ * @param {Boolean} [options.ordered = true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
+ * @param {Boolean} [options.rawResult = false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~insertWriteOpCallback) with a `mongoose` property that contains `validationErrors` if this is an unordered `insertMany`.
+ * @param {Function} [callback] callback
+ * @return {Promise}
+ * @api public
+ */
+
+Model.insertMany = function(arr, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+ return utils.promiseOrCallback(callback, cb => {
+ this.$__insertMany(arr, options, cb);
+ }, this.events);
+};
+
+/*!
+ * ignore
+ */
+
+Model.$__insertMany = function(arr, options, callback) {
+ const _this = this;
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+ callback = callback || utils.noop;
+ options = options || {};
+ const limit = get(options, 'limit', 1000);
+ const rawResult = get(options, 'rawResult', false);
+ const ordered = get(options, 'ordered', true);
+
+ if (!Array.isArray(arr)) {
+ arr = [arr];
+ }
+
+ const toExecute = [];
+ const validationErrors = [];
+ arr.forEach(function(doc) {
+ toExecute.push(function(callback) {
+ if (!(doc instanceof _this)) {
+ doc = new _this(doc);
+ }
+ doc.validate({ __noPromise: true }, function(error) {
+ if (error) {
+ // Option `ordered` signals that insert should be continued after reaching
+ // a failing insert. Therefore we delegate "null", meaning the validation
+ // failed. It's up to the next function to filter out all failed models
+ if (ordered === false) {
+ validationErrors.push(error);
+ return callback(null, null);
+ }
+ return callback(error);
+ }
+ callback(null, doc);
+ });
+ });
+ });
+
+ parallelLimit(toExecute, limit, function(error, docs) {
+ if (error) {
+ callback(error, null);
+ return;
+ }
+ // We filter all failed pre-validations by removing nulls
+ const docAttributes = docs.filter(function(doc) {
+ return doc != null;
+ });
+ // Quickly escape while there aren't any valid docAttributes
+ if (docAttributes.length < 1) {
+ callback(null, []);
+ return;
+ }
+ const docObjects = docAttributes.map(function(doc) {
+ if (doc.schema.options.versionKey) {
+ doc[doc.schema.options.versionKey] = 0;
+ }
+ if (doc.initializeTimestamps) {
+ return doc.initializeTimestamps().toObject(internalToObjectOptions);
+ }
+ return doc.toObject(internalToObjectOptions);
+ });
+
+ _this.collection.insertMany(docObjects, options, function(error, res) {
+ if (error) {
+ callback(error, null);
+ return;
+ }
+ for (let i = 0; i < docAttributes.length; ++i) {
+ docAttributes[i].isNew = false;
+ docAttributes[i].emit('isNew', false);
+ docAttributes[i].constructor.emit('isNew', false);
+ }
+ if (rawResult) {
+ if (ordered === false) {
+ // Decorate with mongoose validation errors in case of unordered,
+ // because then still do `insertMany()`
+ res.mongoose = {
+ validationErrors: validationErrors
+ };
+ }
+ return callback(null, res);
+ }
+ callback(null, docAttributes);
+ });
+ });
+};
+
+/**
+ * Sends multiple `insertOne`, `updateOne`, `updateMany`, `replaceOne`,
+ * `deleteOne`, and/or `deleteMany` operations to the MongoDB server in one
+ * command. This is faster than sending multiple independent operations (like)
+ * if you use `create()`) because with `bulkWrite()` there is only one round
+ * trip to MongoDB.
+ *
+ * Mongoose will perform casting on all operations you provide.
+ *
+ * This function does **not** trigger any middleware, not `save()` nor `update()`.
+ * If you need to trigger
+ * `save()` middleware for every document use [`create()`](http://mongoosejs.com/docs/api.html#model_Model.create) instead.
+ *
+ * ####Example:
+ *
+ * Character.bulkWrite([
+ * {
+ * insertOne: {
+ * document: {
+ * name: 'Eddard Stark',
+ * title: 'Warden of the North'
+ * }
+ * }
+ * },
+ * {
+ * updateOne: {
+ * filter: { name: 'Eddard Stark' },
+ * // If you were using the MongoDB driver directly, you'd need to do
+ * // `update: { $set: { title: ... } }` but mongoose adds $set for
+ * // you.
+ * update: { title: 'Hand of the King' }
+ * }
+ * },
+ * {
+ * deleteOne: {
+ * {
+ * filter: { name: 'Eddard Stark' }
+ * }
+ * }
+ * }
+ * ]).then(res => {
+ * // Prints "1 1 1"
+ * console.log(res.insertedCount, res.modifiedCount, res.deletedCount);
+ * });
+ *
+ * @param {Array} ops
+ * @param {Object} [options]
+ * @param {Function} [callback] callback `function(error, bulkWriteOpResult) {}`
+ * @return {Promise} resolves to a [`BulkWriteOpResult`](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~BulkWriteOpResult) if the operation succeeds
+ * @api public
+ */
+
+Model.bulkWrite = function(ops, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+ options = options || {};
+
+ const validations = ops.map(op => castBulkWrite(this, op));
+
+ return utils.promiseOrCallback(callback, cb => {
+ parallel(validations, error => {
+ if (error) {
+ return cb(error);
+ }
+
+ this.collection.bulkWrite(ops, options, (error, res) => {
+ if (error) {
+ return cb(error);
+ }
+
+ cb(null, res);
+ });
+ });
+ }, this.events);
+};
+
+/**
+ * Shortcut for creating a new Document from existing raw data, pre-saved in the DB.
+ * The document returned has no paths marked as modified initially.
+ *
+ * ####Example:
+ *
+ * // hydrate previous data into a Mongoose document
+ * var mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' });
+ *
+ * @param {Object} obj
+ * @return {Model} document instance
+ * @api public
+ */
+
+Model.hydrate = function(obj) {
+ const model = require('./queryhelpers').createModel(this, obj);
+ model.init(obj);
+ return model;
+};
+
+/**
+ * Updates one document in the database without returning it.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `update()`
+ *
+ * ####Examples:
+ *
+ * MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
+ * MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function (err, raw) {
+ * if (err) return handleError(err);
+ * console.log('The raw response from Mongo was ', raw);
+ * });
+ *
+ * ####Valid options:
+ *
+ * - `safe` (boolean) safe mode (defaults to value set in schema (true))
+ * - `upsert` (boolean) whether to create the doc if it doesn't match (false)
+ * - `multi` (boolean) whether multiple documents should be updated (false)
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `strict` (boolean) overrides the `strict` option for this update
+ * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false)
+ *
+ * All `update` values are cast to their appropriate SchemaTypes before being sent.
+ *
+ * The `callback` function receives `(err, rawResponse)`.
+ *
+ * - `err` is the error if any occurred
+ * - `rawResponse` is the full response from Mongo
+ *
+ * ####Note:
+ *
+ * All top level keys which are not `atomic` operation names are treated as set operations:
+ *
+ * ####Example:
+ *
+ * var query = { name: 'borne' };
+ * Model.update(query, { name: 'jason bourne' }, options, callback)
+ *
+ * // is sent as
+ * Model.update(query, { $set: { name: 'jason bourne' }}, options, callback)
+ * // if overwrite option is false. If overwrite is true, sent without the $set wrapper.
+ *
+ * This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason bourne' }`.
+ *
+ * ####Note:
+ *
+ * Be careful to not use an existing model instance for the update clause (this won't work and can cause weird behavior like infinite loops). Also, ensure that the update clause does not have an _id property, which causes Mongo to return a "Mod on _id not allowed" error.
+ *
+ * ####Note:
+ *
+ * Although values are casted to their appropriate types when using update, the following are *not* applied:
+ *
+ * - defaults
+ * - setters
+ * - validators
+ * - middleware
+ *
+ * If you need those features, use the traditional approach of first retrieving the document.
+ *
+ * Model.findOne({ name: 'borne' }, function (err, doc) {
+ * if (err) ..
+ * doc.name = 'jason bourne';
+ * doc.save(callback);
+ * })
+ *
+ * @see strict http://mongoosejs.com/docs/guide.html#strict
+ * @see response http://docs.mongodb.org/v2.6/reference/command/update/#output
+ * @param {Object} conditions
+ * @param {Object} doc
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.update = function update(conditions, doc, options, callback) {
+ return _update(this, 'update', conditions, doc, options, callback);
+};
+
+/**
+ * Same as `update()`, except MongoDB will update _all_ documents that match
+ * `criteria` (as opposed to just the first one) regardless of the value of
+ * the `multi` option.
+ *
+ * **Note** updateMany will _not_ fire update middleware. Use `pre('updateMany')`
+ * and `post('updateMany')` instead.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `updateMany()`
+ *
+ * @param {Object} conditions
+ * @param {Object} doc
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.updateMany = function updateMany(conditions, doc, options, callback) {
+ return _update(this, 'updateMany', conditions, doc, options, callback);
+};
+
+/**
+ * Same as `update()`, except it does not support the `multi` or `overwrite`
+ * options.
+ *
+ * - MongoDB will update _only_ the first document that matches `criteria` regardless of the value of the `multi` option.
+ * - Use `replaceOne()` if you want to overwrite an entire document rather than using atomic operators like `$set`.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `updateOne()`
+ *
+ * @param {Object} conditions
+ * @param {Object} doc
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.updateOne = function updateOne(conditions, doc, options, callback) {
+ return _update(this, 'updateOne', conditions, doc, options, callback);
+};
+
+/**
+ * Same as `update()`, except MongoDB replace the existing document with the
+ * given document (no atomic operators like `$set`).
+ *
+ * This function triggers the following middleware.
+ *
+ * - `replaceOne()`
+ *
+ * @param {Object} conditions
+ * @param {Object} doc
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback]
+ * @return {Query}
+ * @api public
+ */
+
+Model.replaceOne = function replaceOne(conditions, doc, options, callback) {
+ const versionKey = get(this, 'schema.options.versionKey', null);
+ if (versionKey && !doc[versionKey]) {
+ doc[versionKey] = 0;
+ }
+
+ return _update(this, 'replaceOne', conditions, doc, options, callback);
+};
+
+/*!
+ * Common code for `updateOne()`, `updateMany()`, `replaceOne()`, and `update()`
+ * because they need to do the same thing
+ */
+
+function _update(model, op, conditions, doc, options, callback) {
+ const mq = new model.Query({}, {}, model, model.collection);
+ if (callback) {
+ callback = model.$wrapCallback(callback);
+ }
+ // gh-2406
+ // make local deep copy of conditions
+ if (conditions instanceof Document) {
+ conditions = conditions.toObject();
+ } else {
+ conditions = utils.clone(conditions);
+ }
+ options = typeof options === 'function' ? options : utils.clone(options);
+
+ const versionKey = get(model, 'schema.options.versionKey', null);
+ _decorateUpdateWithVersionKey(doc, options, versionKey);
+
+ return mq[op](conditions, doc, options, callback);
+}
+
+/**
+ * Executes a mapReduce command.
+ *
+ * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation. See [node-mongodb-native mapReduce() documentation](http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#mapreduce) for more detail about options.
+ *
+ * This function does not trigger any middleware.
+ *
+ * ####Example:
+ *
+ * var o = {};
+ * // `map()` and `reduce()` are run on the MongoDB server, not Node.js,
+ * // these functions are converted to strings
+ * o.map = function () { emit(this.name, 1) };
+ * o.reduce = function (k, vals) { return vals.length };
+ * User.mapReduce(o, function (err, results) {
+ * console.log(results)
+ * })
+ *
+ * ####Other options:
+ *
+ * - `query` {Object} query filter object.
+ * - `sort` {Object} sort input objects using this key
+ * - `limit` {Number} max number of documents
+ * - `keeptemp` {Boolean, default:false} keep temporary data
+ * - `finalize` {Function} finalize function
+ * - `scope` {Object} scope variables exposed to map/reduce/finalize during execution
+ * - `jsMode` {Boolean, default:false} it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X
+ * - `verbose` {Boolean, default:false} provide statistics on job execution time.
+ * - `readPreference` {String}
+ * - `out*` {Object, default: {inline:1}} sets the output target for the map reduce job.
+ *
+ * ####* out options:
+ *
+ * - `{inline:1}` the results are returned in an array
+ * - `{replace: 'collectionName'}` add the results to collectionName: the results replace the collection
+ * - `{reduce: 'collectionName'}` add the results to collectionName: if dups are detected, uses the reducer / finalize functions
+ * - `{merge: 'collectionName'}` add the results to collectionName: if dups exist the new docs overwrite the old
+ *
+ * If `options.out` is set to `replace`, `merge`, or `reduce`, a Model instance is returned that can be used for further querying. Queries run against this model are all executed with the `lean` option; meaning only the js object is returned and no Mongoose magic is applied (getters, setters, etc).
+ *
+ * ####Example:
+ *
+ * var o = {};
+ * // You can also define `map()` and `reduce()` as strings if your
+ * // linter complains about `emit()` not being defined
+ * o.map = 'function () { emit(this.name, 1) }';
+ * o.reduce = 'function (k, vals) { return vals.length }';
+ * o.out = { replace: 'createdCollectionNameForResults' }
+ * o.verbose = true;
+ *
+ * User.mapReduce(o, function (err, model, stats) {
+ * console.log('map reduce took %d ms', stats.processtime)
+ * model.find().where('value').gt(10).exec(function (err, docs) {
+ * console.log(docs);
+ * });
+ * })
+ *
+ * // `mapReduce()` returns a promise. However, ES6 promises can only
+ * // resolve to exactly one value,
+ * o.resolveToObject = true;
+ * var promise = User.mapReduce(o);
+ * promise.then(function (res) {
+ * var model = res.model;
+ * var stats = res.stats;
+ * console.log('map reduce took %d ms', stats.processtime)
+ * return model.find().where('value').gt(10).exec();
+ * }).then(function (docs) {
+ * console.log(docs);
+ * }).then(null, handleError).end()
+ *
+ * @param {Object} o an object specifying map-reduce options
+ * @param {Function} [callback] optional callback
+ * @see http://www.mongodb.org/display/DOCS/MapReduce
+ * @return {Promise}
+ * @api public
+ */
+
+Model.mapReduce = function mapReduce(o, callback) {
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+ return utils.promiseOrCallback(callback, cb => {
+ if (!Model.mapReduce.schema) {
+ const opts = {noId: true, noVirtualId: true, strict: false};
+ Model.mapReduce.schema = new Schema({}, opts);
+ }
+
+ if (!o.out) o.out = {inline: 1};
+ if (o.verbose !== false) o.verbose = true;
+
+ o.map = String(o.map);
+ o.reduce = String(o.reduce);
+
+ if (o.query) {
+ let q = new this.Query(o.query);
+ q.cast(this);
+ o.query = q._conditions;
+ q = undefined;
+ }
+
+ this.collection.mapReduce(null, null, o, (err, res) => {
+ if (err) {
+ return cb(err);
+ }
+ if (res.collection) {
+ // returned a collection, convert to Model
+ const model = Model.compile('_mapreduce_' + res.collection.collectionName,
+ Model.mapReduce.schema, res.collection.collectionName, this.db,
+ this.base);
+
+ model._mapreduce = true;
+ res.model = model;
+
+ return cb(null, res);
+ }
+
+ cb(null, res);
+ });
+ }, this.events);
+};
+
+/**
+ * Performs [aggregations](http://docs.mongodb.org/manual/applications/aggregation/) on the models collection.
+ *
+ * If a `callback` is passed, the `aggregate` is executed and a `Promise` is returned. If a callback is not passed, the `aggregate` itself is returned.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `aggregate()`
+ *
+ * ####Example:
+ *
+ * // Find the max balance of all accounts
+ * Users.aggregate([
+ * { $group: { _id: null, maxBalance: { $max: '$balance' }}},
+ * { $project: { _id: 0, maxBalance: 1 }}
+ * ]).
+ * then(function (res) {
+ * console.log(res); // [ { maxBalance: 98000 } ]
+ * });
+ *
+ * // Or use the aggregation pipeline builder.
+ * Users.aggregate().
+ * group({ _id: null, maxBalance: { $max: '$balance' } }).
+ * project('-id maxBalance').
+ * exec(function (err, res) {
+ * if (err) return handleError(err);
+ * console.log(res); // [ { maxBalance: 98 } ]
+ * });
+ *
+ * ####NOTE:
+ *
+ * - Arguments are not cast to the model's schema because `$project` operators allow redefining the "shape" of the documents at any stage of the pipeline, which may leave documents in an incompatible format.
+ * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
+ * - Requires MongoDB >= 2.1
+ *
+ * @see Aggregate #aggregate_Aggregate
+ * @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
+ * @param {Array} [pipeline] aggregation pipeline as an array of objects
+ * @param {Function} [callback]
+ * @return {Aggregate}
+ * @api public
+ */
+
+Model.aggregate = function aggregate(pipeline, callback) {
+ if (arguments.length > 2 || get(pipeline, 'constructor.name') === 'Object') {
+ throw new Error('Mongoose 5.x disallows passing a spread of operators ' +
+ 'to `Model.aggregate()`. Instead of ' +
+ '`Model.aggregate({ $match }, { $skip })`, do ' +
+ '`Model.aggregate([{ $match }, { $skip }])`');
+ }
+
+ if (typeof pipeline === 'function') {
+ callback = pipeline;
+ pipeline = [];
+ }
+
+ const aggregate = new Aggregate(pipeline || []);
+ aggregate.model(this);
+
+ if (typeof callback === 'undefined') {
+ return aggregate;
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ aggregate.exec(callback);
+ return aggregate;
+};
+
+/**
+ * Implements `$geoSearch` functionality for Mongoose
+ *
+ * This function does not trigger any middleware
+ *
+ * ####Example:
+ *
+ * var options = { near: [10, 10], maxDistance: 5 };
+ * Locations.geoSearch({ type : "house" }, options, function(err, res) {
+ * console.log(res);
+ * });
+ *
+ * ####Options:
+ * - `near` {Array} x,y point to search for
+ * - `maxDistance` {Number} the maximum distance from the point near that a result can be
+ * - `limit` {Number} The maximum number of results to return
+ * - `lean` {Boolean} return the raw object instead of the Mongoose Model
+ *
+ * @param {Object} conditions an object that specifies the match condition (required)
+ * @param {Object} options for the geoSearch, some (near, maxDistance) are required
+ * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](http://mongoosejs.com/docs/api.html#query_Query-lean).
+ * @param {Function} [callback] optional callback
+ * @return {Promise}
+ * @see http://docs.mongodb.org/manual/reference/command/geoSearch/
+ * @see http://docs.mongodb.org/manual/core/geohaystack/
+ * @api public
+ */
+
+Model.geoSearch = function(conditions, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return utils.promiseOrCallback(callback, cb => {
+ let error;
+ if (conditions === undefined || !utils.isObject(conditions)) {
+ error = new Error('Must pass conditions to geoSearch');
+ } else if (!options.near) {
+ error = new Error('Must specify the near option in geoSearch');
+ } else if (!Array.isArray(options.near)) {
+ error = new Error('near option must be an array [x, y]');
+ }
+
+ if (error) {
+ return cb(error);
+ }
+
+ // send the conditions in the options object
+ options.search = conditions;
+
+ this.collection.geoHaystackSearch(options.near[0], options.near[1], options, (err, res) => {
+ if (err) {
+ return cb(err);
+ }
+
+ let count = res.results.length;
+ if (options.lean || count === 0) {
+ return cb(null, res.results);
+ }
+
+ const errSeen = false;
+
+ function init(err) {
+ if (err && !errSeen) {
+ return cb(err);
+ }
+
+ if (!--count && !errSeen) {
+ cb(null, res.results);
+ }
+ }
+
+ for (let i = 0; i < res.results.length; ++i) {
+ const temp = res.results[i];
+ res.results[i] = new this();
+ res.results[i].init(temp, {}, init);
+ }
+ });
+ }, this.events);
+};
+
+/**
+ * Populates document references.
+ *
+ * ####Available top-level options:
+ *
+ * - path: space delimited path(s) to populate
+ * - select: optional fields to select
+ * - match: optional query conditions to match
+ * - model: optional name of the model to use for population
+ * - options: optional query options like sort, limit, etc
+ * - justOne: optional boolean, if true Mongoose will always set `path` to an array. Inferred from schema by default.
+ *
+ * ####Examples:
+ *
+ * // populates a single object
+ * User.findById(id, function (err, user) {
+ * var opts = [
+ * { path: 'company', match: { x: 1 }, select: 'name' }
+ * , { path: 'notes', options: { limit: 10 }, model: 'override' }
+ * ]
+ *
+ * User.populate(user, opts, function (err, user) {
+ * console.log(user);
+ * });
+ * });
+ *
+ * // populates an array of objects
+ * User.find(match, function (err, users) {
+ * var opts = [{ path: 'company', match: { x: 1 }, select: 'name' }]
+ *
+ * var promise = User.populate(users, opts);
+ * promise.then(console.log).end();
+ * })
+ *
+ * // imagine a Weapon model exists with two saved documents:
+ * // { _id: 389, name: 'whip' }
+ * // { _id: 8921, name: 'boomerang' }
+ * // and this schema:
+ * // new Schema({
+ * // name: String,
+ * // weapon: { type: ObjectId, ref: 'Weapon' }
+ * // });
+ *
+ * var user = { name: 'Indiana Jones', weapon: 389 }
+ * Weapon.populate(user, { path: 'weapon', model: 'Weapon' }, function (err, user) {
+ * console.log(user.weapon.name) // whip
+ * })
+ *
+ * // populate many plain objects
+ * var users = [{ name: 'Indiana Jones', weapon: 389 }]
+ * users.push({ name: 'Batman', weapon: 8921 })
+ * Weapon.populate(users, { path: 'weapon' }, function (err, users) {
+ * users.forEach(function (user) {
+ * console.log('%s uses a %s', users.name, user.weapon.name)
+ * // Indiana Jones uses a whip
+ * // Batman uses a boomerang
+ * });
+ * });
+ * // Note that we didn't need to specify the Weapon model because
+ * // it is in the schema's ref
+ *
+ * @param {Document|Array} docs Either a single document or array of documents to populate.
+ * @param {Object} options A hash of key/val (path, options) used for population.
+ * @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
+ * @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](/docs/schematypes.html#schematype-options).
+ * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
+ * @param {Function} [callback(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
+ * @return {Promise}
+ * @api public
+ */
+
+Model.populate = function(docs, paths, callback) {
+ const _this = this;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ // normalized paths
+ paths = utils.populate(paths);
+
+ // data that should persist across subPopulate calls
+ const cache = {};
+
+ return utils.promiseOrCallback(callback, cb => {
+ _populate(_this, docs, paths, cache, cb);
+ }, this.events);
+};
+
+/*!
+ * Populate helper
+ *
+ * @param {Model} model the model to use
+ * @param {Document|Array} docs Either a single document or array of documents to populate.
+ * @param {Object} paths
+ * @param {Function} [cb(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
+ * @return {Function}
+ * @api private
+ */
+
+function _populate(model, docs, paths, cache, callback) {
+ let pending = paths.length;
+
+ if (pending === 0) {
+ return callback(null, docs);
+ }
+
+ // each path has its own query options and must be executed separately
+ let i = pending;
+ let path;
+ while (i--) {
+ path = paths[i];
+ populate(model, docs, path, next);
+ }
+
+ function next(err) {
+ if (err) {
+ return callback(err, null);
+ }
+ if (--pending) {
+ return;
+ }
+ callback(null, docs);
+ }
+}
+
+/*!
+ * Populates `docs`
+ */
+const excludeIdReg = /\s?-_id\s?/;
+const excludeIdRegGlobal = /\s?-_id\s?/g;
+
+function populate(model, docs, options, callback) {
+ // normalize single / multiple docs passed
+ if (!Array.isArray(docs)) {
+ docs = [docs];
+ }
+
+ if (docs.length === 0 || docs.every(utils.isNullOrUndefined)) {
+ return callback();
+ }
+
+ const modelsMap = getModelsMapForPopulate(model, docs, options);
+
+ if (modelsMap instanceof Error) {
+ return immediate(function() {
+ callback(modelsMap);
+ });
+ }
+
+ const len = modelsMap.length;
+ let mod;
+ let match;
+ let select;
+ let vals = [];
+
+ function flatten(item) {
+ // no need to include undefined values in our query
+ return undefined !== item;
+ }
+
+ let _remaining = len;
+ let hasOne = false;
+ for (let i = 0; i < len; ++i) {
+ mod = modelsMap[i];
+ select = mod.options.select;
+
+ if (mod.options.match) {
+ match = utils.object.shallowCopy(mod.options.match);
+ } else if (get(mod, 'options.options.match')) {
+ match = utils.object.shallowCopy(mod.options.options.match);
+ delete mod.options.options.match;
+ } else {
+ match = {};
+ }
+
+ let ids = utils.array.flatten(mod.ids, flatten);
+ ids = utils.array.unique(ids);
+
+ if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
+ --_remaining;
+ continue;
+ }
+
+ hasOne = true;
+ if (mod.foreignField.size === 1) {
+ const foreignField = Array.from(mod.foreignField)[0];
+ if (foreignField !== '_id' || !match['_id']) {
+ match[foreignField] = { $in: ids };
+ }
+ } else {
+ match.$or = [];
+ for (const foreignField of mod.foreignField) {
+ if (foreignField !== '_id' || !match['_id']) {
+ match.$or.push({ [foreignField]: { $in: ids } });
+ }
+ }
+ }
+
+ const assignmentOpts = {};
+ assignmentOpts.sort = get(mod, 'options.options.sort', void 0);
+ assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
+
+ if (assignmentOpts.excludeId) {
+ // override the exclusion from the query so we can use the _id
+ // for document matching during assignment. we'll delete the
+ // _id back off before returning the result.
+ if (typeof select === 'string') {
+ select = select.replace(excludeIdRegGlobal, ' ');
+ } else {
+ // preserve original select conditions by copying
+ select = utils.object.shallowCopy(select);
+ delete select._id;
+ }
+ }
+
+ // If just setting count, skip everything else
+ if (mod.count) {
+ mod.model.countDocuments(match, function(err, count) {
+ if (err != null) {
+ return callback(err);
+ }
+
+ for (const doc of docs) {
+ try {
+ if (doc.$__ != null) {
+ doc.set(mod.options.path, count);
+ } else {
+ utils.setValue(mod.options.path, count, doc);
+ }
+ } catch (err) {
+ return callback(err);
+ }
+ }
+
+ callback(null);
+ });
+ continue;
+ }
+
+ if (mod.options.options && mod.options.options.limit) {
+ assignmentOpts.originalLimit = mod.options.options.limit;
+ mod.options.options.limit = mod.options.options.limit * ids.length;
+ }
+
+ const subPopulate = utils.clone(mod.options.populate);
+
+ const query = mod.model.find(match, select, mod.options.options);
+
+ // If we're doing virtual populate and projection is inclusive and foreign
+ // field is not selected, automatically select it because mongoose needs it.
+ // If projection is exclusive and client explicitly unselected the foreign
+ // field, that's the client's fault.
+ for (const foreignField of mod.foreignField) {
+ if (foreignField !== '_id' && query.selectedInclusively() &&
+ !isPathSelectedInclusive(query._fields, foreignField)) {
+ query.select(foreignField);
+ }
+ }
+
+ // If we need to sub-populate, call populate recursively
+ if (subPopulate) {
+ query.populate(subPopulate);
+ }
+
+ query.exec(next.bind(this, mod, assignmentOpts));
+ }
+
+ if (!hasOne) {
+ return callback();
+ }
+
+ function next(options, assignmentOpts, err, valsFromDb) {
+ if (mod.options.options && mod.options.options.limit) {
+ mod.options.options.limit = assignmentOpts.originalLimit;
+ }
+
+ if (err) return callback(err, null);
+ vals = vals.concat(valsFromDb);
+ _assign(null, vals, options, assignmentOpts);
+ if (--_remaining === 0) {
+ callback();
+ }
+ }
+
+ function _assign(err, vals, mod, assignmentOpts) {
+ if (err) {
+ return callback(err, null);
+ }
+
+ const options = mod.options;
+ const isVirtual = mod.isVirtual;
+ const justOne = mod.justOne;
+ let _val;
+ const lean = options.options && options.options.lean;
+ const len = vals.length;
+ const rawOrder = {};
+ const rawDocs = {};
+ let key;
+ let val;
+
+ // Clone because `assignRawDocsToIdStructure` will mutate the array
+ const allIds = utils.clone(mod.allIds);
+
+ // optimization:
+ // record the document positions as returned by
+ // the query result.
+ for (let i = 0; i < len; i++) {
+ val = vals[i];
+ if (val == null) {
+ continue;
+ }
+ for (const foreignField of mod.foreignField) {
+ _val = utils.getValue(foreignField, val);
+ if (Array.isArray(_val)) {
+ const _valLength = _val.length;
+ for (let j = 0; j < _valLength; ++j) {
+ let __val = _val[j];
+ if (__val instanceof Document) {
+ __val = __val._id;
+ }
+ key = String(__val);
+ if (rawDocs[key]) {
+ if (Array.isArray(rawDocs[key])) {
+ rawDocs[key].push(val);
+ rawOrder[key].push(i);
+ } else {
+ rawDocs[key] = [rawDocs[key], val];
+ rawOrder[key] = [rawOrder[key], i];
+ }
+ } else {
+ if (isVirtual && !justOne) {
+ rawDocs[key] = [val];
+ rawOrder[key] = [i];
+ } else {
+ rawDocs[key] = val;
+ rawOrder[key] = i;
+ }
+ }
+ }
+ } else {
+ if (_val instanceof Document) {
+ _val = _val._id;
+ }
+ key = String(_val);
+ if (rawDocs[key]) {
+ if (Array.isArray(rawDocs[key])) {
+ rawDocs[key].push(val);
+ rawOrder[key].push(i);
+ } else {
+ rawDocs[key] = [rawDocs[key], val];
+ rawOrder[key] = [rawOrder[key], i];
+ }
+ } else {
+ rawDocs[key] = val;
+ rawOrder[key] = i;
+ }
+ }
+ // flag each as result of population
+ if (lean) {
+ leanPopulateMap.set(val, mod.model);
+ } else {
+ val.$__.wasPopulated = true;
+ }
+ }
+ }
+
+ assignVals({
+ originalModel: model,
+ // If virtual, make sure to not mutate original field
+ rawIds: mod.isVirtual ? allIds : mod.allIds,
+ allIds: allIds,
+ foreignField: mod.foreignField,
+ rawDocs: rawDocs,
+ rawOrder: rawOrder,
+ docs: mod.docs,
+ path: options.path,
+ options: assignmentOpts,
+ justOne: mod.justOne,
+ isVirtual: mod.isVirtual,
+ allOptions: mod,
+ lean: lean,
+ virtual: mod.virtual
+ });
+ }
+}
+
+/*!
+ * Assigns documents returned from a population query back
+ * to the original document path.
+ */
+
+function assignVals(o) {
+ // Options that aren't explicitly listed in `populateOptions`
+ const userOptions = get(o, 'allOptions.options.options');
+ // `o.options` contains options explicitly listed in `populateOptions`, like
+ // `match` and `limit`.
+ const populateOptions = Object.assign({}, o.options, userOptions, {
+ justOne: o.justOne
+ });
+
+ // replace the original ids in our intermediate _ids structure
+ // with the documents found by query
+ assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, populateOptions);
+
+ // now update the original documents being populated using the
+ // result structure that contains real documents.
+ const docs = o.docs;
+ const rawIds = o.rawIds;
+ const options = o.options;
+
+ function setValue(val) {
+ return valueFilter(val, options, populateOptions);
+ }
+
+ for (let i = 0; i < docs.length; ++i) {
+ const existingVal = utils.getValue(o.path, docs[i]);
+ if (existingVal == null && !getVirtual(o.originalModel.schema, o.path)) {
+ continue;
+ }
+
+ // If we're populating a map, the existing value will be an object, so
+ // we need to transform again
+ const originalSchema = o.originalModel.schema;
+ let isMap = isModel(docs[i]) ?
+ existingVal instanceof Map :
+ utils.isPOJO(existingVal);
+ // If we pass the first check, also make sure the local field's schematype
+ // is map (re: gh-6460)
+ isMap = isMap && get(originalSchema._getSchema(o.path), '$isSchemaMap');
+ if (!o.isVirtual && isMap) {
+ const _keys = existingVal instanceof Map ?
+ Array.from(existingVal.keys()) :
+ Object.keys(existingVal);
+ rawIds[i] = rawIds[i].reduce((cur, v, i) => {
+ // Avoid casting because that causes infinite recursion
+ cur.$init(_keys[i], v);
+ return cur;
+ }, new MongooseMap({}, docs[i]));
+ }
+
+ if (o.isVirtual && docs[i] instanceof Model) {
+ // If virtual populate and doc is already init-ed, need to walk through
+ // the actual doc to set rather than setting `_doc` directly
+ mpath.set(o.path, rawIds[i], docs[i], setValue);
+ continue;
+ }
+
+ const parts = o.path.split('.');
+ let cur = docs[i];
+ for (let j = 0; j < parts.length - 1; ++j) {
+ if (cur[parts[j]] == null) {
+ cur[parts[j]] = {};
+ }
+ cur = cur[parts[j]];
+ }
+ if (docs[i].$__) {
+ docs[i].populated(o.path, o.allIds[i], o.allOptions);
+ }
+
+ // If lean, need to check that each individual virtual respects
+ // `justOne`, because you may have a populated virtual with `justOne`
+ // underneath an array. See gh-6867
+ utils.setValue(o.path, rawIds[i], docs[i], function(v) {
+ if (o.justOne === true && Array.isArray(v)) {
+ return setValue(v[0]);
+ } else if (o.justOne === false && !Array.isArray(v)) {
+ return setValue([v]);
+ }
+ return setValue(v);
+ }, false);
+ }
+}
+
+/*!
+ * Check if obj is a document
+ */
+
+function isModel(obj) {
+ return get(obj, '$__') != null;
+}
+
+function getModelsMapForPopulate(model, docs, options) {
+ let i;
+ let doc;
+ const len = docs.length;
+ const available = {};
+ const map = [];
+ const modelNameFromQuery = options.model && options.model.modelName || options.model;
+ let schema;
+ let refPath;
+ let Model;
+ let currentOptions;
+ let modelNames;
+ let modelName;
+ let discriminatorKey;
+ let modelForFindSchema;
+
+ const originalModel = options.model;
+ let isVirtual = false;
+ const modelSchema = model.schema;
+
+ for (i = 0; i < len; i++) {
+ doc = docs[i];
+
+ schema = getSchemaTypes(modelSchema, doc, options.path);
+ const isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
+ if (isUnderneathDocArray && get(options, 'options.sort') != null) {
+ return new Error('Cannot populate with `sort` on path ' + options.path +
+ ' because it is a subproperty of a document array');
+ }
+
+ modelNames = null;
+ let isRefPath = false;
+ if (Array.isArray(schema)) {
+ for (let j = 0; j < schema.length; ++j) {
+ let _modelNames;
+ try {
+ const res = _getModelNames(doc, schema[j]);
+ _modelNames = res.modelNames;
+ isRefPath = res.isRefPath;
+ } catch (error) {
+ return error;
+ }
+ if (!_modelNames) {
+ continue;
+ }
+ modelNames = modelNames || [];
+ for (let x = 0; x < _modelNames.length; ++x) {
+ if (modelNames.indexOf(_modelNames[x]) === -1) {
+ modelNames.push(_modelNames[x]);
+ }
+ }
+ }
+ } else {
+ try {
+ const res = _getModelNames(doc, schema);
+ modelNames = res.modelNames;
+ isRefPath = res.isRefPath;
+ } catch (error) {
+ return error;
+ }
+
+ if (!modelNames) {
+ continue;
+ }
+ }
+
+ const virtual = getVirtual(model.schema, options.path);
+ let localField;
+ let count = false;
+ if (virtual && virtual.options) {
+ const virtualPrefix = virtual.$nestedSchemaPath ?
+ virtual.$nestedSchemaPath + '.' : '';
+ if (typeof virtual.options.localField === 'function') {
+ localField = virtualPrefix + virtual.options.localField.call(doc, doc);
+ } else {
+ localField = virtualPrefix + virtual.options.localField;
+ }
+ count = virtual.options.count;
+ } else {
+ localField = options.path;
+ }
+ let foreignField = virtual && virtual.options ?
+ virtual.options.foreignField :
+ '_id';
+
+ // `justOne = null` means we don't know from the schema whether the end
+ // result should be an array or a single doc. This can result from
+ // populating a POJO using `Model.populate()`
+ let justOne = null;
+ if ('justOne' in options) {
+ justOne = options.justOne;
+ } else if (virtual && virtual.options && virtual.options.ref) {
+ let normalizedRef;
+ if (typeof virtual.options.ref === 'function') {
+ normalizedRef = virtual.options.ref.call(doc, doc);
+ } else {
+ normalizedRef = virtual.options.ref;
+ }
+ justOne = !!virtual.options.justOne;
+ isVirtual = true;
+ if (!modelNames) {
+ modelNames = [].concat(normalizedRef);
+ }
+ } else if (schema && !schema[schemaMixedSymbol]) {
+ // Skip Mixed types because we explicitly don't do casting on those.
+ justOne = !schema.$isMongooseArray;
+ }
+
+ if (!modelNames) {
+ continue;
+ }
+
+ if (virtual && (!localField || !foreignField)) {
+ return new Error('If you are populating a virtual, you must set the ' +
+ 'localField and foreignField options');
+ }
+
+ options.isVirtual = isVirtual;
+ options.virtual = virtual;
+ if (typeof localField === 'function') {
+ localField = localField.call(doc, doc);
+ }
+ if (typeof foreignField === 'function') {
+ foreignField = foreignField.call(doc);
+ }
+
+ const localFieldPath = modelSchema.paths[localField];
+ const localFieldGetters = localFieldPath ? localFieldPath.getters : [];
+ let ret;
+
+ const _populateOptions = get(options, 'options', {});
+
+ const getters = 'getters' in _populateOptions ?
+ _populateOptions.getters :
+ options.isVirtual && get(virtual, 'options.getters', false);
+ if (localFieldGetters.length > 0 && getters) {
+ const hydratedDoc = (doc.$__ != null) ? doc : model.hydrate(doc);
+ ret = localFieldPath.applyGetters(doc[localField], hydratedDoc);
+ } else {
+ ret = convertTo_id(utils.getValue(localField, doc));
+ }
+
+ const id = String(utils.getValue(foreignField, doc));
+ options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
+
+ let k = modelNames.length;
+ while (k--) {
+ modelName = modelNames[k];
+ if (modelName == null) {
+ continue;
+ }
+
+ try {
+ Model = originalModel && originalModel[modelSymbol] ?
+ originalModel :
+ modelName[modelSymbol] ? modelName : model.db.model(modelName);
+ } catch (error) {
+ return error;
+ }
+
+ let ids = ret;
+ const flat = Array.isArray(ret) ? utils.array.flatten(ret) : [];
+ if (isRefPath && Array.isArray(ret) && flat.length === modelNames.length) {
+ ids = flat.filter((val, i) => modelNames[i] === modelName);
+ }
+
+ if (!available[modelName]) {
+ currentOptions = {
+ model: Model
+ };
+
+ if (isVirtual && virtual.options && virtual.options.options) {
+ currentOptions.options = utils.clone(virtual.options.options);
+ }
+ utils.merge(currentOptions, options);
+ if (schema && !discriminatorKey) {
+ currentOptions.model = Model;
+ }
+ options.model = Model;
+
+ available[modelName] = {
+ model: Model,
+ options: currentOptions,
+ docs: [doc],
+ ids: [ids],
+ allIds: [ret],
+ localField: new Set([localField]),
+ foreignField: new Set([foreignField]),
+ justOne: justOne,
+ isVirtual: isVirtual,
+ virtual: virtual,
+ count: count
+ };
+ map.push(available[modelName]);
+ } else {
+ available[modelName].localField.add(localField);
+ available[modelName].foreignField.add(foreignField);
+ available[modelName].docs.push(doc);
+ available[modelName].ids.push(ids);
+ available[modelName].allIds.push(ret);
+ }
+ }
+ }
+
+ function _getModelNames(doc, schema) {
+ let modelNames;
+ let discriminatorKey;
+ let isRefPath = false;
+
+ if (schema && schema.caster) {
+ schema = schema.caster;
+ }
+ if (schema && schema.$isSchemaMap) {
+ schema = schema.$__schemaType;
+ }
+
+ if (!schema && model.discriminators) {
+ discriminatorKey = model.schema.discriminatorMapping.key;
+ }
+
+ refPath = schema && schema.options && schema.options.refPath;
+
+ const normalizedRefPath = normalizeRefPath(refPath, doc, options.path);
+
+ if (modelNameFromQuery) {
+ modelNames = [modelNameFromQuery]; // query options
+ } else if (normalizedRefPath) {
+ if (options._queryProjection != null && isPathExcluded(options._queryProjection, normalizedRefPath)) {
+ throw new Error('refPath `' + normalizedRefPath +
+ '` must not be excluded in projection, got ' +
+ util.inspect(options._queryProjection));
+ }
+ modelNames = utils.getValue(normalizedRefPath, doc);
+ if (Array.isArray(modelNames)) {
+ modelNames = utils.array.flatten(modelNames);
+ }
+
+ isRefPath = true;
+ } else {
+ let modelForCurrentDoc = model;
+ let schemaForCurrentDoc;
+
+ if (!schema && discriminatorKey) {
+ modelForFindSchema = utils.getValue(discriminatorKey, doc);
+
+ if (modelForFindSchema) {
+ try {
+ modelForCurrentDoc = model.db.model(modelForFindSchema);
+ } catch (error) {
+ return error;
+ }
+
+ schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path);
+
+ if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
+ schemaForCurrentDoc = schemaForCurrentDoc.caster;
+ }
+ }
+ } else {
+ schemaForCurrentDoc = schema;
+ }
+ const virtual = getVirtual(modelForCurrentDoc.schema, options.path);
+
+ let ref;
+ if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) {
+ modelNames = [ref];
+ } else if ((ref = get(virtual, 'options.ref')) != null) {
+ if (typeof ref === 'function') {
+ ref = ref.call(doc, doc);
+ }
+
+ // When referencing nested arrays, the ref should be an Array
+ // of modelNames.
+ if (Array.isArray(ref)) {
+ modelNames = ref;
+ } else {
+ modelNames = [ref];
+ }
+
+ isVirtual = true;
+ } else {
+ // We may have a discriminator, in which case we don't want to
+ // populate using the base model by default
+ modelNames = discriminatorKey ? null : [model.modelName];
+ }
+ }
+
+ if (!modelNames) {
+ return { modelNames: modelNames, isRefPath: isRefPath };
+ }
+
+ if (!Array.isArray(modelNames)) {
+ modelNames = [modelNames];
+ }
+
+ return { modelNames: modelNames, isRefPath: isRefPath };
+ }
+
+ return map;
+}
+
+/*!
+ * Retrieve the _id of `val` if a Document or Array of Documents.
+ *
+ * @param {Array|Document|Any} val
+ * @return {Array|Document|Any}
+ */
+
+function convertTo_id(val) {
+ if (val instanceof Model) return val._id;
+
+ if (Array.isArray(val)) {
+ for (let i = 0; i < val.length; ++i) {
+ if (val[i] instanceof Model) {
+ val[i] = val[i]._id;
+ }
+ }
+ if (val.isMongooseArray && val._schema) {
+ return val._schema.cast(val, val._parent);
+ }
+
+ return [].concat(val);
+ }
+
+ // `populate('map')` may be an object if populating on a doc that hasn't
+ // been hydrated yet
+ if (val != null && val.constructor.name === 'Object') {
+ const ret = [];
+ for (const key of Object.keys(val)) {
+ ret.push(val[key]);
+ }
+ return ret;
+ }
+ // If doc has already been hydrated, e.g. `doc.populate('map').execPopulate()`
+ // then `val` will already be a map
+ if (val instanceof Map) {
+ return Array.from(val.values());
+ }
+
+ return val;
+}
+
+/*!
+ * 1) Apply backwards compatible find/findOne behavior to sub documents
+ *
+ * find logic:
+ * a) filter out non-documents
+ * b) remove _id from sub docs when user specified
+ *
+ * findOne
+ * a) if no doc found, set to null
+ * b) remove _id from sub docs when user specified
+ *
+ * 2) Remove _ids when specified by users query.
+ *
+ * background:
+ * _ids are left in the query even when user excludes them so
+ * that population mapping can occur.
+ */
+
+function valueFilter(val, assignmentOpts, populateOptions) {
+ if (Array.isArray(val)) {
+ // find logic
+ const ret = [];
+ const numValues = val.length;
+ for (let i = 0; i < numValues; ++i) {
+ const subdoc = val[i];
+ if (!isPopulatedObject(subdoc) && (!populateOptions.retainNullValues || subdoc != null)) {
+ continue;
+ }
+ maybeRemoveId(subdoc, assignmentOpts);
+ ret.push(subdoc);
+ if (assignmentOpts.originalLimit &&
+ ret.length >= assignmentOpts.originalLimit) {
+ break;
+ }
+ }
+
+ // Since we don't want to have to create a new mongoosearray, make sure to
+ // modify the array in place
+ while (val.length > ret.length) {
+ Array.prototype.pop.apply(val, []);
+ }
+ for (let i = 0; i < ret.length; ++i) {
+ val[i] = ret[i];
+ }
+ return val;
+ }
+
+ // findOne
+ if (isPopulatedObject(val)) {
+ maybeRemoveId(val, assignmentOpts);
+ return val;
+ }
+
+ if (populateOptions.justOne === true) {
+ return (val == null ? val : null);
+ }
+ if (populateOptions.justOne === false) {
+ return [];
+ }
+ return val;
+}
+
+/*!
+ * Remove _id from `subdoc` if user specified "lean" query option
+ */
+
+function maybeRemoveId(subdoc, assignmentOpts) {
+ if (assignmentOpts.excludeId) {
+ if (typeof subdoc.setValue === 'function') {
+ delete subdoc._doc._id;
+ } else {
+ delete subdoc._id;
+ }
+ }
+}
+
+/*!
+ * Determine if `obj` is something we can set a populated path to. Can be a
+ * document, a lean document, or an array/map that contains docs.
+ */
+
+function isPopulatedObject(obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ return Array.isArray(obj) ||
+ obj.$isMongooseMap ||
+ obj.$__ != null ||
+ leanPopulateMap.has(obj);
+}
+
+/*!
+ * Compiler utility.
+ *
+ * @param {String|Function} name model name or class extending Model
+ * @param {Schema} schema
+ * @param {String} collectionName
+ * @param {Connection} connection
+ * @param {Mongoose} base mongoose instance
+ */
+
+Model.compile = function compile(name, schema, collectionName, connection, base) {
+ const versioningEnabled = schema.options.versionKey !== false;
+
+ setParentPointers(schema);
+
+ if (versioningEnabled && !schema.paths[schema.options.versionKey]) {
+ // add versioning to top level documents only
+ const o = {};
+ o[schema.options.versionKey] = Number;
+ schema.add(o);
+ }
+
+ let model;
+ if (typeof name === 'function' && name.prototype instanceof Model) {
+ model = name;
+ name = model.name;
+ schema.loadClass(model, false);
+ model.prototype.$isMongooseModelPrototype = true;
+ } else {
+ // generate new class
+ model = function model(doc, fields, skipId) {
+ model.hooks.execPreSync('createModel', doc);
+ if (!(this instanceof model)) {
+ return new model(doc, fields, skipId);
+ }
+ Model.call(this, doc, fields, skipId);
+ };
+ }
+
+ model.hooks = schema.s.hooks.clone();
+ model.base = base;
+ model.modelName = name;
+
+ if (!(model.prototype instanceof Model)) {
+ model.__proto__ = Model;
+ model.prototype.__proto__ = Model.prototype;
+ }
+ model.model = Model.prototype.model;
+ model.db = model.prototype.db = connection;
+ model.discriminators = model.prototype.discriminators = undefined;
+ model[modelSymbol] = true;
+ model.events = new EventEmitter();
+
+ model.prototype.$__setSchema(schema);
+
+ const _userProvidedOptions = schema._userProvidedOptions || {};
+
+ // `bufferCommands` is true by default...
+ let bufferCommands = true;
+ // First, take the global option
+ if (connection.base.get('bufferCommands') != null) {
+ bufferCommands = connection.base.get('bufferCommands');
+ }
+ // Connection-specific overrides the global option
+ if (connection.config.bufferCommands != null) {
+ bufferCommands = connection.config.bufferCommands;
+ }
+ // And schema options override global and connection
+ if (_userProvidedOptions.bufferCommands != null) {
+ bufferCommands = _userProvidedOptions.bufferCommands;
+ }
+
+ const collectionOptions = {
+ bufferCommands: bufferCommands,
+ capped: schema.options.capped
+ };
+
+ model.prototype.collection = connection.collection(
+ collectionName,
+ collectionOptions
+ );
+ model.prototype[modelCollectionSymbol] = model.prototype.collection;
+
+ // apply methods and statics
+ applyMethods(model, schema);
+ applyStatics(model, schema);
+ applyHooks(model, schema);
+
+ model.schema = model.prototype.schema;
+ model.collection = model.prototype.collection;
+
+ // Create custom query constructor
+ model.Query = function() {
+ Query.apply(this, arguments);
+ };
+ model.Query.prototype = Object.create(Query.prototype);
+ model.Query.base = Query.base;
+ applyQueryMiddleware(model.Query, model);
+ applyQueryMethods(model, schema.query);
+
+ const kareemOptions = {
+ useErrorHandlers: true,
+ numCallbackParams: 1
+ };
+ model.$__insertMany = model.hooks.createWrapper('insertMany',
+ model.$__insertMany, model, kareemOptions);
+
+ return model;
+};
+
+/*!
+ * Register custom query methods for this model
+ *
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+
+function applyQueryMethods(model, methods) {
+ for (const i in methods) {
+ model.Query.prototype[i] = methods[i];
+ }
+}
+
+/*!
+ * Subclass this model with `conn`, `schema`, and `collection` settings.
+ *
+ * @param {Connection} conn
+ * @param {Schema} [schema]
+ * @param {String} [collection]
+ * @return {Model}
+ */
+
+Model.__subclass = function subclass(conn, schema, collection) {
+ // subclass model using this connection and collection name
+ const _this = this;
+
+ const Model = function Model(doc, fields, skipId) {
+ if (!(this instanceof Model)) {
+ return new Model(doc, fields, skipId);
+ }
+ _this.call(this, doc, fields, skipId);
+ };
+
+ Model.__proto__ = _this;
+ Model.prototype.__proto__ = _this.prototype;
+ Model.db = Model.prototype.db = conn;
+
+ const s = schema && typeof schema !== 'string'
+ ? schema
+ : _this.prototype.schema;
+
+ const options = s.options || {};
+ const _userProvidedOptions = s._userProvidedOptions || {};
+
+ if (!collection) {
+ collection = _this.prototype.schema.get('collection') ||
+ utils.toCollectionName(_this.modelName, this.base.pluralize());
+ }
+
+ let bufferCommands = true;
+ if (s) {
+ if (conn.config.bufferCommands != null) {
+ bufferCommands = conn.config.bufferCommands;
+ }
+ if (_userProvidedOptions.bufferCommands != null) {
+ bufferCommands = _userProvidedOptions.bufferCommands;
+ }
+ }
+ const collectionOptions = {
+ bufferCommands: bufferCommands,
+ capped: s && options.capped
+ };
+
+ Model.prototype.collection = conn.collection(collection, collectionOptions);
+ Model.prototype[modelCollectionSymbol] = Model.prototype.collection;
+ Model.collection = Model.prototype.collection;
+ // Errors handled internally, so ignore
+ Model.init(() => {});
+ return Model;
+};
+
+Model.$wrapCallback = function(callback) {
+ if (callback == null) {
+ return callback;
+ }
+ if (typeof callback !== 'function') {
+ throw new Error('Callback must be a function, got ' + callback);
+ }
+ const _this = this;
+ return function() {
+ try {
+ callback.apply(null, arguments);
+ } catch (error) {
+ _this.emit('error', error);
+ }
+ };
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = Model;
diff --git a/node_modules/mongoose/lib/options.js b/node_modules/mongoose/lib/options.js
new file mode 100644
index 0000000..8e9fc29
--- /dev/null
+++ b/node_modules/mongoose/lib/options.js
@@ -0,0 +1,14 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+exports.internalToObjectOptions = {
+ transform: false,
+ virtuals: false,
+ getters: false,
+ _skipDepopulateTopLevel: true,
+ depopulate: true,
+ flattenDecimals: false
+};
diff --git a/node_modules/mongoose/lib/plugins/idGetter.js b/node_modules/mongoose/lib/plugins/idGetter.js
new file mode 100644
index 0000000..f4e6356
--- /dev/null
+++ b/node_modules/mongoose/lib/plugins/idGetter.js
@@ -0,0 +1,28 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function(schema) {
+ // ensure the documents receive an id getter unless disabled
+ const autoIdGetter = !schema.paths['id'] &&
+ (!schema.options.noVirtualId && schema.options.id);
+ if (!autoIdGetter) {
+ return;
+ }
+
+ schema.virtual('id').get(idGetter);
+};
+
+/*!
+ * Returns this documents _id cast to a string.
+ */
+
+function idGetter() {
+ if (this._id != null) {
+ return String(this._id);
+ }
+
+ return null;
+}
diff --git a/node_modules/mongoose/lib/plugins/removeSubdocs.js b/node_modules/mongoose/lib/plugins/removeSubdocs.js
new file mode 100644
index 0000000..c7b2d24
--- /dev/null
+++ b/node_modules/mongoose/lib/plugins/removeSubdocs.js
@@ -0,0 +1,38 @@
+'use strict';
+
+const each = require('async/each');
+
+/*!
+ * ignore
+ */
+
+module.exports = function(schema) {
+ const unshift = true;
+ schema.s.hooks.pre('remove', false, function(next) {
+ if (this.ownerDocument) {
+ next();
+ return;
+ }
+
+ const _this = this;
+ const subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length) {
+ next();
+ return;
+ }
+
+ each(subdocs, function(subdoc, cb) {
+ subdoc.$__remove(function(err) {
+ cb(err);
+ });
+ }, function(error) {
+ if (error) {
+ return _this.schema.s.hooks.execPost('remove:error', _this, [_this], { error: error }, function(error) {
+ next(error);
+ });
+ }
+ next();
+ });
+ }, null, unshift);
+};
diff --git a/node_modules/mongoose/lib/plugins/saveSubdocs.js b/node_modules/mongoose/lib/plugins/saveSubdocs.js
new file mode 100644
index 0000000..dc27213
--- /dev/null
+++ b/node_modules/mongoose/lib/plugins/saveSubdocs.js
@@ -0,0 +1,66 @@
+'use strict';
+
+const each = require('async/each');
+
+/*!
+ * ignore
+ */
+
+module.exports = function(schema) {
+ const unshift = true;
+ schema.s.hooks.pre('save', false, function(next) {
+ if (this.ownerDocument) {
+ next();
+ return;
+ }
+
+ const _this = this;
+ const subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length) {
+ next();
+ return;
+ }
+
+ each(subdocs, function(subdoc, cb) {
+ subdoc.schema.s.hooks.execPre('save', subdoc, function(err) {
+ cb(err);
+ });
+ }, function(error) {
+ if (error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
+ next(error);
+ });
+ }
+ next();
+ });
+ }, null, unshift);
+
+ schema.s.hooks.post('save', function(doc, next) {
+ if (this.ownerDocument) {
+ next();
+ return;
+ }
+
+ const _this = this;
+ const subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length) {
+ next();
+ return;
+ }
+
+ each(subdocs, function(subdoc, cb) {
+ subdoc.schema.s.hooks.execPost('save', subdoc, [subdoc], function(err) {
+ cb(err);
+ });
+ }, function(error) {
+ if (error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
+ next(error);
+ });
+ }
+ next();
+ });
+ }, null, unshift);
+};
diff --git a/node_modules/mongoose/lib/plugins/sharding.js b/node_modules/mongoose/lib/plugins/sharding.js
new file mode 100644
index 0000000..0ea9996
--- /dev/null
+++ b/node_modules/mongoose/lib/plugins/sharding.js
@@ -0,0 +1,79 @@
+'use strict';
+
+const objectIdSymbol = require('../helpers/symbols').objectIdSymbol;
+const utils = require('../utils');
+
+/*!
+ * ignore
+ */
+
+module.exports = function shardingPlugin(schema) {
+ schema.post('init', function() {
+ storeShard.call(this);
+ return this;
+ });
+ schema.pre('save', function(next) {
+ applyWhere.call(this);
+ next();
+ });
+ schema.post('save', function() {
+ storeShard.call(this);
+ });
+};
+
+/*!
+ * ignore
+ */
+
+function applyWhere() {
+ let paths;
+ let len;
+
+ if (this.$__.shardval) {
+ paths = Object.keys(this.$__.shardval);
+ len = paths.length;
+
+ this.$where = this.$where || {};
+ for (let i = 0; i < len; ++i) {
+ this.$where[paths[i]] = this.$__.shardval[paths[i]];
+ }
+ }
+}
+
+/*!
+ * ignore
+ */
+
+module.exports.storeShard = storeShard;
+
+/*!
+ * ignore
+ */
+
+function storeShard() {
+ // backwards compat
+ const key = this.schema.options.shardKey || this.schema.options.shardkey;
+ if (!(key && utils.getFunctionName(key.constructor) === 'Object')) {
+ return;
+ }
+
+ const orig = this.$__.shardval = {};
+ const paths = Object.keys(key);
+ const len = paths.length;
+ let val;
+
+ for (let i = 0; i < len; ++i) {
+ val = this.getValue(paths[i]);
+ if (val == null) {
+ orig[paths[i]] = val;
+ } else if (utils.isMongooseObject(val)) {
+ orig[paths[i]] = val.toObject({depopulate: true, _isNested: true});
+ } else if (val instanceof Date || val[objectIdSymbol]) {
+ orig[paths[i]] = val;
+ } else if (typeof val.valueOf === 'function') {
+ orig[paths[i]] = val.valueOf();
+ } else {
+ orig[paths[i]] = val;
+ }
+ }
+}
diff --git a/node_modules/mongoose/lib/plugins/validateBeforeSave.js b/node_modules/mongoose/lib/plugins/validateBeforeSave.js
new file mode 100644
index 0000000..2d8a8ac
--- /dev/null
+++ b/node_modules/mongoose/lib/plugins/validateBeforeSave.js
@@ -0,0 +1,38 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function(schema) {
+ const unshift = true;
+ schema.pre('save', false, function(next, options) {
+ const _this = this;
+ // Nested docs have their own presave
+ if (this.ownerDocument) {
+ return next();
+ }
+
+ const hasValidateBeforeSaveOption = options &&
+ (typeof options === 'object') &&
+ ('validateBeforeSave' in options);
+
+ let shouldValidate;
+ if (hasValidateBeforeSaveOption) {
+ shouldValidate = !!options.validateBeforeSave;
+ } else {
+ shouldValidate = this.schema.options.validateBeforeSave;
+ }
+
+ // Validate
+ if (shouldValidate) {
+ this.validate(function(error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [ _this], { error: error }, function(error) {
+ next(error);
+ });
+ });
+ } else {
+ next();
+ }
+ }, null, unshift);
+};
diff --git a/node_modules/mongoose/lib/promise_provider.js b/node_modules/mongoose/lib/promise_provider.js
new file mode 100644
index 0000000..3febf36
--- /dev/null
+++ b/node_modules/mongoose/lib/promise_provider.js
@@ -0,0 +1,49 @@
+/*!
+ * ignore
+ */
+
+'use strict';
+
+const assert = require('assert');
+const mquery = require('mquery');
+
+/**
+ * Helper for multiplexing promise implementations
+ *
+ * @api private
+ */
+
+const store = {
+ _promise: null
+};
+
+/**
+ * Get the current promise constructor
+ *
+ * @api private
+ */
+
+store.get = function() {
+ return store._promise;
+};
+
+/**
+ * Set the current promise constructor
+ *
+ * @api private
+ */
+
+store.set = function(lib) {
+ assert.ok(typeof lib === 'function',
+ `mongoose.Promise must be a function, got ${lib}`);
+ store._promise = lib;
+ mquery.Promise = lib;
+};
+
+/*!
+ * Use native promises by default
+ */
+
+store.set(global.Promise);
+
+module.exports = store;
diff --git a/node_modules/mongoose/lib/query.js b/node_modules/mongoose/lib/query.js
new file mode 100644
index 0000000..250f6bc
--- /dev/null
+++ b/node_modules/mongoose/lib/query.js
@@ -0,0 +1,4895 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const CastError = require('./error/cast');
+const Kareem = require('kareem');
+const ObjectParameterError = require('./error/objectParameter');
+const QueryCursor = require('./cursor/QueryCursor');
+const ReadPreference = require('./driver').get().ReadPreference;
+const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
+const cast = require('./cast');
+const castUpdate = require('./helpers/query/castUpdate');
+const completeMany = require('./helpers/query/completeMany');
+const get = require('./helpers/get');
+const hasDollarKeys = require('./helpers/query/hasDollarKeys');
+const helpers = require('./queryhelpers');
+const isInclusive = require('./helpers/projection/isInclusive');
+const mquery = require('mquery');
+const selectPopulatedFields = require('./helpers/query/selectPopulatedFields');
+const setDefaultsOnInsert = require('./helpers/setDefaultsOnInsert');
+const slice = require('sliced');
+const updateValidators = require('./helpers/updateValidators');
+const util = require('util');
+const utils = require('./utils');
+const wrapThunk = require('./helpers/query/wrapThunk');
+
+/**
+ * Query constructor used for building queries. You do not need
+ * to instantiate a `Query` directly. Instead use Model functions like
+ * [`Model.find()`](/docs/api.html#find_find).
+ *
+ * ####Example:
+ *
+ * const query = MyModel.find(); // `query` is an instance of `Query`
+ * query.setOptions({ lean : true });
+ * query.collection(MyModel.collection);
+ * query.where('age').gte(21).exec(callback);
+ *
+ * // You can instantiate a query directly. There is no need to do
+ * // this unless you're an advanced user with a very good reason to.
+ * const query = new mongoose.Query();
+ *
+ * @param {Object} [options]
+ * @param {Object} [model]
+ * @param {Object} [conditions]
+ * @param {Object} [collection] Mongoose collection
+ * @api public
+ */
+
+function Query(conditions, options, model, collection) {
+ // this stuff is for dealing with custom queries created by #toConstructor
+ if (!this._mongooseOptions) {
+ this._mongooseOptions = {};
+ }
+ options = options || {};
+
+ this._transforms = [];
+ this._hooks = new Kareem();
+ this._executionCount = 0;
+
+ // this is the case where we have a CustomQuery, we need to check if we got
+ // options passed in, and if we did, merge them in
+ const keys = Object.keys(options);
+ for (let i = 0; i < keys.length; ++i) {
+ const k = keys[i];
+ this._mongooseOptions[k] = options[k];
+ }
+
+ if (collection) {
+ this.mongooseCollection = collection;
+ }
+
+ if (model) {
+ this.model = model;
+ this.schema = model.schema;
+ }
+
+ // this is needed because map reduce returns a model that can be queried, but
+ // all of the queries on said model should be lean
+ if (this.model && this.model._mapreduce) {
+ this.lean();
+ }
+
+ // inherit mquery
+ mquery.call(this, this.mongooseCollection, options);
+
+ if (conditions) {
+ this.find(conditions);
+ }
+
+ this.options = this.options || {};
+
+ // For gh-6880. mquery still needs to support `fields` by default for old
+ // versions of MongoDB
+ this.$useProjection = true;
+
+ const collation = get(this, 'schema.options.collation', null);
+ if (collation != null) {
+ this.options.collation = collation;
+ }
+}
+
+/*!
+ * inherit mquery
+ */
+
+Query.prototype = new mquery;
+Query.prototype.constructor = Query;
+Query.base = mquery.prototype;
+
+/**
+ * Flag to opt out of using `$geoWithin`.
+ *
+ * mongoose.Query.use$geoWithin = false;
+ *
+ * MongoDB 2.4 deprecated the use of `$within`, replacing it with `$geoWithin`. Mongoose uses `$geoWithin` by default (which is 100% backward compatible with $within). If you are running an older version of MongoDB, set this flag to `false` so your `within()` queries continue to work.
+ *
+ * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
+ * @default true
+ * @property use$geoWithin
+ * @memberOf Query
+ * @receiver Query
+ * @api public
+ */
+
+Query.use$geoWithin = mquery.use$geoWithin;
+
+/**
+ * Converts this query to a customized, reusable query constructor with all arguments and options retained.
+ *
+ * ####Example
+ *
+ * // Create a query for adventure movies and read from the primary
+ * // node in the replica-set unless it is down, in which case we'll
+ * // read from a secondary node.
+ * var query = Movie.find({ tags: 'adventure' }).read('primaryPreferred');
+ *
+ * // create a custom Query constructor based off these settings
+ * var Adventure = query.toConstructor();
+ *
+ * // Adventure is now a subclass of mongoose.Query and works the same way but with the
+ * // default query parameters and options set.
+ * Adventure().exec(callback)
+ *
+ * // further narrow down our query results while still using the previous settings
+ * Adventure().where({ name: /^Life/ }).exec(callback);
+ *
+ * // since Adventure is a stand-alone constructor we can also add our own
+ * // helper methods and getters without impacting global queries
+ * Adventure.prototype.startsWith = function (prefix) {
+ * this.where({ name: new RegExp('^' + prefix) })
+ * return this;
+ * }
+ * Object.defineProperty(Adventure.prototype, 'highlyRated', {
+ * get: function () {
+ * this.where({ rating: { $gt: 4.5 }});
+ * return this;
+ * }
+ * })
+ * Adventure().highlyRated.startsWith('Life').exec(callback)
+ *
+ * New in 3.7.3
+ *
+ * @return {Query} subclass-of-Query
+ * @api public
+ */
+
+Query.prototype.toConstructor = function toConstructor() {
+ const model = this.model;
+ const coll = this.mongooseCollection;
+
+ const CustomQuery = function(criteria, options) {
+ if (!(this instanceof CustomQuery)) {
+ return new CustomQuery(criteria, options);
+ }
+ this._mongooseOptions = utils.clone(p._mongooseOptions);
+ Query.call(this, criteria, options || null, model, coll);
+ };
+
+ util.inherits(CustomQuery, model.Query);
+
+ // set inherited defaults
+ const p = CustomQuery.prototype;
+
+ p.options = {};
+
+ p.setOptions(this.options);
+
+ p.op = this.op;
+ p._conditions = utils.clone(this._conditions);
+ p._fields = utils.clone(this._fields);
+ p._update = utils.clone(this._update, {
+ flattenDecimals: false
+ });
+ p._path = this._path;
+ p._distinct = this._distinct;
+ p._collection = this._collection;
+ p._mongooseOptions = this._mongooseOptions;
+
+ return CustomQuery;
+};
+
+/**
+ * Specifies a javascript function or expression to pass to MongoDBs query system.
+ *
+ * ####Example
+ *
+ * query.$where('this.comments.length === 10 || this.name.length === 5')
+ *
+ * // or
+ *
+ * query.$where(function () {
+ * return this.comments.length === 10 || this.name.length === 5;
+ * })
+ *
+ * ####NOTE:
+ *
+ * Only use `$where` when you have a condition that cannot be met using other MongoDB operators like `$lt`.
+ * **Be sure to read about all of [its caveats](http://docs.mongodb.org/manual/reference/operator/where/) before using.**
+ *
+ * @see $where http://docs.mongodb.org/manual/reference/operator/where/
+ * @method $where
+ * @param {String|Function} js javascript string or function
+ * @return {Query} this
+ * @memberOf Query
+ * @instance
+ * @method $where
+ * @api public
+ */
+
+/**
+ * Specifies a `path` for use with chaining.
+ *
+ * ####Example
+ *
+ * // instead of writing:
+ * User.find({age: {$gte: 21, $lte: 65}}, callback);
+ *
+ * // we can instead write:
+ * User.where('age').gte(21).lte(65);
+ *
+ * // passing query conditions is permitted
+ * User.find().where({ name: 'vonderful' })
+ *
+ * // chaining
+ * User
+ * .where('age').gte(21).lte(65)
+ * .where('name', /^vonderful/i)
+ * .where('friends').slice(10)
+ * .exec(callback)
+ *
+ * @method where
+ * @memberOf Query
+ * @instance
+ * @param {String|Object} [path]
+ * @param {any} [val]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.slice = function() {
+ if (arguments.length === 0) {
+ return this;
+ }
+
+ this._validate('slice');
+
+ let path;
+ let val;
+
+ if (arguments.length === 1) {
+ const arg = arguments[0];
+ if (typeof arg === 'object' && !Array.isArray(arg)) {
+ const keys = Object.keys(arg);
+ const numKeys = keys.length;
+ for (let i = 0; i < numKeys; ++i) {
+ this.slice(keys[i], arg[keys[i]]);
+ }
+ return this;
+ }
+ this._ensurePath('slice');
+ path = this._path;
+ val = arguments[0];
+ } else if (arguments.length === 2) {
+ if ('number' === typeof arguments[0]) {
+ this._ensurePath('slice');
+ path = this._path;
+ val = slice(arguments);
+ } else {
+ path = arguments[0];
+ val = arguments[1];
+ }
+ } else if (arguments.length === 3) {
+ path = arguments[0];
+ val = slice(arguments, 1);
+ }
+
+ const p = {};
+ p[path] = { $slice: val };
+ this.select(p);
+
+ return this;
+};
+
+
+/**
+ * Specifies the complementary comparison value for paths specified with `where()`
+ *
+ * ####Example
+ *
+ * User.where('age').equals(49);
+ *
+ * // is the same as
+ *
+ * User.where('age', 49);
+ *
+ * @method equals
+ * @memberOf Query
+ * @instance
+ * @param {Object} val
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Specifies arguments for an `$or` condition.
+ *
+ * ####Example
+ *
+ * query.or([{ color: 'red' }, { status: 'emergency' }])
+ *
+ * @see $or http://docs.mongodb.org/manual/reference/operator/or/
+ * @method or
+ * @memberOf Query
+ * @instance
+ * @param {Array} array array of conditions
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Specifies arguments for a `$nor` condition.
+ *
+ * ####Example
+ *
+ * query.nor([{ color: 'green' }, { status: 'ok' }])
+ *
+ * @see $nor http://docs.mongodb.org/manual/reference/operator/nor/
+ * @method nor
+ * @memberOf Query
+ * @instance
+ * @param {Array} array array of conditions
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Specifies arguments for a `$and` condition.
+ *
+ * ####Example
+ *
+ * query.and([{ color: 'green' }, { status: 'ok' }])
+ *
+ * @method and
+ * @memberOf Query
+ * @instance
+ * @see $and http://docs.mongodb.org/manual/reference/operator/and/
+ * @param {Array} array array of conditions
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Specifies a $gt query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * ####Example
+ *
+ * Thing.find().where('age').gt(21)
+ *
+ * // or
+ * Thing.find().gt('age', 21)
+ *
+ * @method gt
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @see $gt http://docs.mongodb.org/manual/reference/operator/gt/
+ * @api public
+ */
+
+/**
+ * Specifies a $gte query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method gte
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @see $gte http://docs.mongodb.org/manual/reference/operator/gte/
+ * @api public
+ */
+
+/**
+ * Specifies a $lt query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method lt
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @see $lt http://docs.mongodb.org/manual/reference/operator/lt/
+ * @api public
+ */
+
+/**
+ * Specifies a $lte query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method lte
+ * @see $lte http://docs.mongodb.org/manual/reference/operator/lte/
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $ne query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @see $ne http://docs.mongodb.org/manual/reference/operator/ne/
+ * @method ne
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies an $in query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @see $in http://docs.mongodb.org/manual/reference/operator/in/
+ * @method in
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies an $nin query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @see $nin http://docs.mongodb.org/manual/reference/operator/nin/
+ * @method nin
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies an $all query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @see $all http://docs.mongodb.org/manual/reference/operator/all/
+ * @method all
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $size query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * ####Example
+ *
+ * MyModel.where('tags').size(0).exec(function (err, docs) {
+ * if (err) return handleError(err);
+ *
+ * assert(Array.isArray(docs));
+ * console.log('documents with 0 tags', docs);
+ * })
+ *
+ * @see $size http://docs.mongodb.org/manual/reference/operator/size/
+ * @method size
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $regex query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @see $regex http://docs.mongodb.org/manual/reference/operator/regex/
+ * @method regex
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {String|RegExp} val
+ * @api public
+ */
+
+/**
+ * Specifies a $maxDistance query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
+ * @method maxDistance
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a `$mod` condition, filters documents for documents whose
+ * `path` property is a number that is equal to `remainder` modulo `divisor`.
+ *
+ * ####Example
+ *
+ * // All find products whose inventory is odd
+ * Product.find().mod('inventory', [2, 1]);
+ * Product.find().where('inventory').mod([2, 1]);
+ * // This syntax is a little strange, but supported.
+ * Product.find().where('inventory').mod(2, 1);
+ *
+ * @method mod
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Array} val must be of length 2, first element is `divisor`, 2nd element is `remainder`.
+ * @return {Query} this
+ * @see $mod http://docs.mongodb.org/manual/reference/operator/mod/
+ * @api public
+ */
+
+Query.prototype.mod = function() {
+ let val;
+ let path;
+
+ if (arguments.length === 1) {
+ this._ensurePath('mod');
+ val = arguments[0];
+ path = this._path;
+ } else if (arguments.length === 2 && !Array.isArray(arguments[1])) {
+ this._ensurePath('mod');
+ val = slice(arguments);
+ path = this._path;
+ } else if (arguments.length === 3) {
+ val = slice(arguments, 1);
+ path = arguments[0];
+ } else {
+ val = arguments[1];
+ path = arguments[0];
+ }
+
+ const conds = this._conditions[path] || (this._conditions[path] = {});
+ conds.$mod = val;
+ return this;
+};
+
+/**
+ * Specifies an `$exists` condition
+ *
+ * ####Example
+ *
+ * // { name: { $exists: true }}
+ * Thing.where('name').exists()
+ * Thing.where('name').exists(true)
+ * Thing.find().exists('name')
+ *
+ * // { name: { $exists: false }}
+ * Thing.where('name').exists(false);
+ * Thing.find().exists('name', false);
+ *
+ * @method exists
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val
+ * @return {Query} this
+ * @see $exists http://docs.mongodb.org/manual/reference/operator/exists/
+ * @api public
+ */
+
+/**
+ * Specifies an `$elemMatch` condition
+ *
+ * ####Example
+ *
+ * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}})
+ *
+ * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})
+ *
+ * query.elemMatch('comment', function (elem) {
+ * elem.where('author').equals('autobot');
+ * elem.where('votes').gte(5);
+ * })
+ *
+ * query.where('comment').elemMatch(function (elem) {
+ * elem.where({ author: 'autobot' });
+ * elem.where('votes').gte(5);
+ * })
+ *
+ * @method elemMatch
+ * @memberOf Query
+ * @instance
+ * @param {String|Object|Function} path
+ * @param {Object|Function} criteria
+ * @return {Query} this
+ * @see $elemMatch http://docs.mongodb.org/manual/reference/operator/elemMatch/
+ * @api public
+ */
+
+/**
+ * Defines a `$within` or `$geoWithin` argument for geo-spatial queries.
+ *
+ * ####Example
+ *
+ * query.where(path).within().box()
+ * query.where(path).within().circle()
+ * query.where(path).within().geometry()
+ *
+ * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
+ * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
+ * query.where('loc').within({ polygon: [[],[],[],[]] });
+ *
+ * query.where('loc').within([], [], []) // polygon
+ * query.where('loc').within([], []) // box
+ * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
+ *
+ * **MUST** be used after `where()`.
+ *
+ * ####NOTE:
+ *
+ * As of Mongoose 3.7, `$geoWithin` is always used for queries. To change this behavior, see [Query.use$geoWithin](#query_Query-use%2524geoWithin).
+ *
+ * ####NOTE:
+ *
+ * In Mongoose 3.7, `within` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
+ *
+ * @method within
+ * @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
+ * @see $box http://docs.mongodb.org/manual/reference/operator/box/
+ * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
+ * @see $center http://docs.mongodb.org/manual/reference/operator/center/
+ * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
+ * @memberOf Query
+ * @instance
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Specifies a $slice projection for an array.
+ *
+ * ####Example
+ *
+ * query.slice('comments', 5)
+ * query.slice('comments', -5)
+ * query.slice('comments', [10, 5])
+ * query.where('comments').slice(5)
+ * query.where('comments').slice([-10, 5])
+ *
+ * @method slice
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Number} val number/range of elements to slice
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements
+ * @see $slice http://docs.mongodb.org/manual/reference/projection/slice/#prj._S_slice
+ * @api public
+ */
+
+/**
+ * Specifies the maximum number of documents the query will return.
+ *
+ * ####Example
+ *
+ * query.limit(20)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method limit
+ * @memberOf Query
+ * @instance
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies the number of documents to skip.
+ *
+ * ####Example
+ *
+ * query.skip(100).limit(20)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method skip
+ * @memberOf Query
+ * @instance
+ * @param {Number} val
+ * @see cursor.skip http://docs.mongodb.org/manual/reference/method/cursor.skip/
+ * @api public
+ */
+
+/**
+ * Specifies the maxScan option.
+ *
+ * ####Example
+ *
+ * query.maxScan(100)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method maxScan
+ * @memberOf Query
+ * @instance
+ * @param {Number} val
+ * @see maxScan http://docs.mongodb.org/manual/reference/operator/maxScan/
+ * @api public
+ */
+
+/**
+ * Specifies the batchSize option.
+ *
+ * ####Example
+ *
+ * query.batchSize(100)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method batchSize
+ * @memberOf Query
+ * @instance
+ * @param {Number} val
+ * @see batchSize http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
+ * @api public
+ */
+
+/**
+ * Specifies the `comment` option.
+ *
+ * ####Example
+ *
+ * query.comment('login query')
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method comment
+ * @memberOf Query
+ * @instance
+ * @param {Number} val
+ * @see comment http://docs.mongodb.org/manual/reference/operator/comment/
+ * @api public
+ */
+
+/**
+ * Specifies this query as a `snapshot` query.
+ *
+ * ####Example
+ *
+ * query.snapshot() // true
+ * query.snapshot(true)
+ * query.snapshot(false)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method snapshot
+ * @memberOf Query
+ * @instance
+ * @see snapshot http://docs.mongodb.org/manual/reference/operator/snapshot/
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Sets query hints.
+ *
+ * ####Example
+ *
+ * query.hint({ indexA: 1, indexB: -1})
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method hint
+ * @memberOf Query
+ * @instance
+ * @param {Object} val a hint object
+ * @return {Query} this
+ * @see $hint http://docs.mongodb.org/manual/reference/operator/hint/
+ * @api public
+ */
+
+/**
+ * Specifies which document fields to include or exclude (also known as the query "projection")
+ *
+ * When using string syntax, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. Lastly, if a path is prefixed with `+`, it forces inclusion of the path, which is useful for paths excluded at the [schema level](/docs/api.html#schematype_SchemaType-select).
+ *
+ * A projection _must_ be either inclusive or exclusive. In other words, you must
+ * either list the fields to include (which excludes all others), or list the fields
+ * to exclude (which implies all other fields are included). The [`_id` field is the only exception because MongoDB includes it by default](https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/#suppress-id-field).
+ *
+ * ####Example
+ *
+ * // include a and b, exclude other fields
+ * query.select('a b');
+ *
+ * // exclude c and d, include other fields
+ * query.select('-c -d');
+ *
+ * // Use `+` to override schema-level `select: false` without making the
+ * // projection inclusive.
+ * const schema = new Schema({
+ * foo: { type: String, select: false },
+ * bar: String
+ * });
+ * // ...
+ * query.select('+foo'); // Override foo's `select: false` without excluding `bar`
+ *
+ * // or you may use object notation, useful when
+ * // you have keys already prefixed with a "-"
+ * query.select({ a: 1, b: 1 });
+ * query.select({ c: 0, d: 0 });
+ *
+ *
+ * @method select
+ * @memberOf Query
+ * @instance
+ * @param {Object|String} arg
+ * @return {Query} this
+ * @see SchemaType
+ * @api public
+ */
+
+Query.prototype.select = function select() {
+ let arg = arguments[0];
+ if (!arg) return this;
+ let i;
+ let len;
+
+ if (arguments.length !== 1) {
+ throw new Error('Invalid select: select only takes 1 argument');
+ }
+
+ this._validate('select');
+
+ const fields = this._fields || (this._fields = {});
+ const userProvidedFields = this._userProvidedFields || (this._userProvidedFields = {});
+ const type = typeof arg;
+
+ if (('string' == type || Object.prototype.toString.call(arg) === '[object Arguments]') &&
+ 'number' == typeof arg.length || Array.isArray(arg)) {
+ if ('string' == type)
+ arg = arg.split(/\s+/);
+
+ for (i = 0, len = arg.length; i < len; ++i) {
+ let field = arg[i];
+ if (!field) continue;
+ const include = '-' == field[0] ? 0 : 1;
+ if (include === 0) field = field.substring(1);
+ fields[field] = include;
+ userProvidedFields[field] = include;
+ }
+ return this;
+ }
+
+ if (utils.isObject(arg)) {
+ const keys = Object.keys(arg);
+ for (i = 0; i < keys.length; ++i) {
+ fields[keys[i]] = arg[keys[i]];
+ userProvidedFields[keys[i]] = arg[keys[i]];
+ }
+ return this;
+ }
+
+ throw new TypeError('Invalid select() argument. Must be string or object.');
+};
+
+/**
+ * _DEPRECATED_ Sets the slaveOk option.
+ *
+ * **Deprecated** in MongoDB 2.2 in favor of [read preferences](#query_Query-read).
+ *
+ * ####Example:
+ *
+ * query.slaveOk() // true
+ * query.slaveOk(true)
+ * query.slaveOk(false)
+ *
+ * @method slaveOk
+ * @memberOf Query
+ * @instance
+ * @deprecated use read() preferences instead if on mongodb >= 2.2
+ * @param {Boolean} v defaults to true
+ * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
+ * @see slaveOk http://docs.mongodb.org/manual/reference/method/rs.slaveOk/
+ * @see read() #query_Query-read
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Determines the MongoDB nodes from which to read.
+ *
+ * ####Preferences:
+ *
+ * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags.
+ * secondary Read from secondary if available, otherwise error.
+ * primaryPreferred Read from primary if available, otherwise a secondary.
+ * secondaryPreferred Read from a secondary if available, otherwise read from the primary.
+ * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection.
+ *
+ * Aliases
+ *
+ * p primary
+ * pp primaryPreferred
+ * s secondary
+ * sp secondaryPreferred
+ * n nearest
+ *
+ * ####Example:
+ *
+ * new Query().read('primary')
+ * new Query().read('p') // same as primary
+ *
+ * new Query().read('primaryPreferred')
+ * new Query().read('pp') // same as primaryPreferred
+ *
+ * new Query().read('secondary')
+ * new Query().read('s') // same as secondary
+ *
+ * new Query().read('secondaryPreferred')
+ * new Query().read('sp') // same as secondaryPreferred
+ *
+ * new Query().read('nearest')
+ * new Query().read('n') // same as nearest
+ *
+ * // read from secondaries with matching tags
+ * new Query().read('s', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }])
+ *
+ * Read more about how to use read preferrences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences).
+ *
+ * @method read
+ * @memberOf Query
+ * @instance
+ * @param {String} pref one of the listed preference options or aliases
+ * @param {Array} [tags] optional tags for this query
+ * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
+ * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.read = function read(pref, tags) {
+ // first cast into a ReadPreference object to support tags
+ const read = new ReadPreference(pref, tags);
+ this.options.readPreference = read;
+ return this;
+};
+
+/**
+ * Sets the [MongoDB session](https://docs.mongodb.com/manual/reference/server-sessions/)
+ * associated with this query. Sessions are how you mark a query as part of a
+ * [transaction](/docs/transactions.html).
+ *
+ * Calling `session(null)` removes the session from this query.
+ *
+ * ####Example:
+ *
+ * const s = await mongoose.startSession();
+ * await mongoose.model('Person').findOne({ name: 'Axl Rose' }).session(s);
+ *
+ * @method session
+ * @memberOf Query
+ * @instance
+ * @param {ClientSession} [session] from `await conn.startSession()`
+ * @see Connection.prototype.startSession() /docs/api.html#connection_Connection-startSession
+ * @see mongoose.startSession() /docs/api.html#mongoose_Mongoose-startSession
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.session = function session(v) {
+ if (v == null) {
+ delete this.options.session;
+ }
+ this.options.session = v;
+ return this;
+};
+
+/**
+ * Sets the specified number of `mongod` servers, or tag set of `mongod` servers,
+ * that must acknowledge this write before this write is considered successful.
+ * This option is only valid for operations that write to the database:
+ *
+ * - `deleteOne()`
+ * - `deleteMany()`
+ * - `findOneAndDelete()`
+ * - `findOneAndReplace()`
+ * - `findOneAndUpdate()`
+ * - `remove()`
+ * - `update()`
+ * - `updateOne()`
+ * - `updateMany()`
+ *
+ * Defaults to the schema's [`writeConcern.w` option](/docs/guide.html#writeConcern)
+ *
+ * ####Example:
+ *
+ * // The 'majority' option means the `deleteOne()` promise won't resolve
+ * // until the `deleteOne()` has propagated to the majority of the replica set
+ * await mongoose.model('Person').
+ * deleteOne({ name: 'Ned Stark' }).
+ * w('majority');
+ *
+ * @method w
+ * @memberOf Query
+ * @instance
+ * @param {String|number} val 0 for fire-and-forget, 1 for acknowledged by one server, 'majority' for majority of the replica set, or [any of the more advanced options](https://docs.mongodb.com/manual/reference/write-concern/#w-option).
+ * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#w-option
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.w = function w(val) {
+ if (val == null) {
+ delete this.options.w;
+ }
+ this.options.w = val;
+ return this;
+};
+
+/**
+ * Requests acknowledgement that this operation has been persisted to MongoDB's
+ * on-disk journal.
+ * This option is only valid for operations that write to the database:
+ *
+ * - `deleteOne()`
+ * - `deleteMany()`
+ * - `findOneAndDelete()`
+ * - `findOneAndReplace()`
+ * - `findOneAndUpdate()`
+ * - `remove()`
+ * - `update()`
+ * - `updateOne()`
+ * - `updateMany()`
+ *
+ * Defaults to the schema's [`writeConcern.j` option](/docs/guide.html#writeConcern)
+ *
+ * ####Example:
+ *
+ * await mongoose.model('Person').deleteOne({ name: 'Ned Stark' }).j(true);
+ *
+ * @method j
+ * @memberOf Query
+ * @instance
+ * @param {boolean} val
+ * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#j-option
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.j = function j(val) {
+ if (val == null) {
+ delete this.options.j;
+ }
+ this.options.j = val;
+ return this;
+};
+
+/**
+ * If [`w > 1`](/docs/api.html#query_Query-w), the maximum amount of time to
+ * wait for this write to propagate through the replica set before this
+ * operation fails. The default is `0`, which means no timeout.
+ *
+ * This option is only valid for operations that write to the database:
+ *
+ * - `deleteOne()`
+ * - `deleteMany()`
+ * - `findOneAndDelete()`
+ * - `findOneAndReplace()`
+ * - `findOneAndUpdate()`
+ * - `remove()`
+ * - `update()`
+ * - `updateOne()`
+ * - `updateMany()`
+ *
+ * Defaults to the schema's [`writeConcern.wtimeout` option](/docs/guide.html#writeConcern)
+ *
+ * ####Example:
+ *
+ * // The `deleteOne()` promise won't resolve until this `deleteOne()` has
+ * // propagated to at least `w = 2` members of the replica set. If it takes
+ * // longer than 1 second, this `deleteOne()` will fail.
+ * await mongoose.model('Person').
+ * deleteOne({ name: 'Ned Stark' }).
+ * w(2).
+ * wtimeout(1000);
+ *
+ * @method wtimeout
+ * @memberOf Query
+ * @instance
+ * @param {number} ms number of milliseconds to wait
+ * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#wtimeout
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.wtimeout = function wtimeout(ms) {
+ if (ms == null) {
+ delete this.options.wtimeout;
+ }
+ this.options.wtimeout = ms;
+ return this;
+};
+
+/**
+ * Sets the readConcern option for the query.
+ *
+ * ####Example:
+ *
+ * new Query().readConcern('local')
+ * new Query().readConcern('l') // same as local
+ *
+ * new Query().readConcern('available')
+ * new Query().readConcern('a') // same as available
+ *
+ * new Query().readConcern('majority')
+ * new Query().readConcern('m') // same as majority
+ *
+ * new Query().readConcern('linearizable')
+ * new Query().readConcern('lz') // same as linearizable
+ *
+ * new Query().readConcern('snapshot')
+ * new Query().readConcern('s') // same as snapshot
+ *
+ *
+ * ####Read Concern Level:
+ *
+ * local MongoDB 3.2+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
+ * available MongoDB 3.6+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
+ * majority MongoDB 3.2+ The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure.
+ * linearizable MongoDB 3.4+ The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results.
+ * snapshot MongoDB 4.0+ Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data.
+ *
+ * Aliases
+ *
+ * l local
+ * a available
+ * m majority
+ * lz linearizable
+ * s snapshot
+ *
+ * Read more about how to use read concern [here](https://docs.mongodb.com/manual/reference/read-concern/).
+ *
+ * @memberOf Query
+ * @method readConcern
+ * @param {String} level one of the listed read concern level or their aliases
+ * @see mongodb https://docs.mongodb.com/manual/reference/read-concern/
+ * @return {Query} this
+ * @api public
+ */
+
+/**
+ * Merges another Query or conditions object into this one.
+ *
+ * When a Query is passed, conditions, field selection and options are merged.
+ *
+ * New in 3.7.0
+ *
+ * @method merge
+ * @memberOf Query
+ * @instance
+ * @param {Query|Object} source
+ * @return {Query} this
+ */
+
+/**
+ * Gets query options.
+ *
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.limit(10);
+ * query.setOptions({ maxTimeMS: 1000 })
+ * query.getOptions(); // { limit: 10, maxTimeMS: 1000 }
+ *
+ * @return {Object} the options
+ * @api public
+ */
+
+Query.prototype.getOptions = function() {
+ return this.options;
+};
+
+/**
+ * Sets query options. Some options only make sense for certain operations.
+ *
+ * ####Options:
+ *
+ * The following options are only for `find()`:
+ *
+ * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors)
+ * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D)
+ * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D)
+ * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D)
+ * - [maxscan](https://docs.mongodb.org/v3.2/reference/operator/meta/maxScan/#metaOp._S_maxScan)
+ * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D)
+ * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment)
+ * - [snapshot](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D)
+ * - [readPreference](http://docs.mongodb.org/manual/applications/replication/#read-preference)
+ * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint)
+ *
+ * The following options are only for write operations: `update()`, `updateOne()`, `updateMany()`, `replaceOne()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`:
+ *
+ * - [upsert](https://docs.mongodb.com/manual/reference/method/db.collection.update/)
+ * - [writeConcern](https://docs.mongodb.com/manual/reference/method/db.collection.update/)
+ * - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): If `timestamps` is set in the schema, set this option to `false` to skip timestamps for that particular update. Has no effect if `timestamps` is not enabled in the schema options.
+ *
+ * The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`:
+ *
+ * - [lean](./api.html#query_Query-lean)
+ *
+ * The following options are only for all operations **except** `update()`, `updateOne()`, `updateMany()`, `remove()`, `deleteOne()`, and `deleteMany()`:
+ *
+ * - [maxTimeMS](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/)
+ *
+ * The following options are for all operations:
+ *
+ * - [collation](https://docs.mongodb.com/manual/reference/collation/)
+ * - [session](https://docs.mongodb.com/manual/reference/server-sessions/)
+ *
+ * @param {Object} options
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.setOptions = function(options, overwrite) {
+ // overwrite is only for internal use
+ if (overwrite) {
+ // ensure that _mongooseOptions & options are two different objects
+ this._mongooseOptions = (options && utils.clone(options)) || {};
+ this.options = options || {};
+
+ if ('populate' in options) {
+ this.populate(this._mongooseOptions);
+ }
+ return this;
+ }
+
+ if (options == null) {
+ return this;
+ }
+ if (typeof options !== 'object') {
+ throw new Error('Options must be an object, got "' + options + '"');
+ }
+
+ if (Array.isArray(options.populate)) {
+ const populate = options.populate;
+ delete options.populate;
+ const _numPopulate = populate.length;
+ for (let i = 0; i < _numPopulate; ++i) {
+ this.populate(populate[i]);
+ }
+ }
+
+ if ('useFindAndModify' in options) {
+ this._mongooseOptions.useFindAndModify = options.useFindAndModify;
+ delete options.useFindAndModify;
+ }
+ if ('omitUndefined' in options) {
+ this._mongooseOptions.omitUndefined = options.omitUndefined;
+ delete options.omitUndefined;
+ }
+
+ return Query.base.setOptions.call(this, options);
+};
+
+/**
+ * Sets the [`explain` option](https://docs.mongodb.com/manual/reference/method/cursor.explain/),
+ * which makes this query return detailed execution stats instead of the actual
+ * query result. This method is useful for determining what index your queries
+ * use.
+ *
+ * Calling `query.explain(v)` is equivalent to `query.setOption({ explain: v })`
+ *
+ * ####Example:
+ *
+ * const query = new Query();
+ * const res = await query.find({ a: 1 }).explain('queryPlanner');
+ * console.log(res);
+ *
+ * @param {String} [verbose] The verbosity mode. Either 'queryPlanner', 'executionStats', or 'allPlansExecution'. The default is 'queryPlanner'
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.explain = function(verbose) {
+ if (arguments.length === 0) {
+ this.options.explain = true;
+ return this;
+ }
+ this.options.explain = verbose;
+ return this;
+};
+
+/**
+ * Sets the [maxTimeMS](https://docs.mongodb.com/manual/reference/method/cursor.maxTimeMS/)
+ * option. This will tell the MongoDB server to abort if the query or write op
+ * has been running for more than `ms` milliseconds.
+ *
+ * Calling `query.maxTimeMS(v)` is equivalent to `query.setOption({ maxTimeMS: v })`
+ *
+ * ####Example:
+ *
+ * const query = new Query();
+ * // Throws an error 'operation exceeded time limit' as long as there's
+ * // >= 1 doc in the queried collection
+ * const res = await query.find({ $where: 'sleep(1000) || true' }).maxTimeMS(100);
+ *
+ * @param {Number} [ms] The number of milliseconds
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.maxTimeMS = function(ms) {
+ this.options.maxTimeMS = ms;
+ return this;
+};
+
+/**
+ * Returns the current query conditions as a JSON object.
+ *
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.find({ a: 1 }).where('b').gt(2);
+ * query.getQuery(); // { a: 1, b: { $gt: 2 } }
+ *
+ * @return {Object} current query conditions
+ * @api public
+ */
+
+Query.prototype.getQuery = function() {
+ return this._conditions;
+};
+
+/**
+ * Sets the query conditions to the provided JSON object.
+ *
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.find({ a: 1 })
+ * query.setQuery({ a: 2 });
+ * query.getQuery(); // { a: 2 }
+ *
+ * @param {Object} new query conditions
+ * @return {undefined}
+ * @api public
+ */
+
+Query.prototype.setQuery = function(val) {
+ this._conditions = val;
+};
+
+/**
+ * Returns the current update operations as a JSON object.
+ *
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.update({}, { $set: { a: 5 } });
+ * query.getUpdate(); // { $set: { a: 5 } }
+ *
+ * @return {Object} current update operations
+ * @api public
+ */
+
+Query.prototype.getUpdate = function() {
+ return this._update;
+};
+
+/**
+ * Sets the current update operation to new value.
+ *
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.update({}, { $set: { a: 5 } });
+ * query.setUpdate({ $set: { b: 6 } });
+ * query.getUpdate(); // { $set: { b: 6 } }
+ *
+ * @param {Object} new update operation
+ * @return {undefined}
+ * @api public
+ */
+
+Query.prototype.setUpdate = function(val) {
+ this._update = val;
+};
+
+/**
+ * Returns fields selection for this query.
+ *
+ * @method _fieldsForExec
+ * @return {Object}
+ * @api private
+ * @receiver Query
+ */
+
+Query.prototype._fieldsForExec = function() {
+ return utils.clone(this._fields);
+};
+
+
+/**
+ * Return an update document with corrected $set operations.
+ *
+ * @method _updateForExec
+ * @api private
+ * @receiver Query
+ */
+
+Query.prototype._updateForExec = function() {
+ const update = utils.clone(this._update, {
+ transform: false,
+ depopulate: true
+ });
+ const ops = Object.keys(update);
+ let i = ops.length;
+ const ret = {};
+
+ while (i--) {
+ const op = ops[i];
+
+ if (this.options.overwrite) {
+ ret[op] = update[op];
+ continue;
+ }
+
+ if ('$' !== op[0]) {
+ // fix up $set sugar
+ if (!ret.$set) {
+ if (update.$set) {
+ ret.$set = update.$set;
+ } else {
+ ret.$set = {};
+ }
+ }
+ ret.$set[op] = update[op];
+ ops.splice(i, 1);
+ if (!~ops.indexOf('$set')) ops.push('$set');
+ } else if ('$set' === op) {
+ if (!ret.$set) {
+ ret[op] = update[op];
+ }
+ } else {
+ ret[op] = update[op];
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Makes sure _path is set.
+ *
+ * @method _ensurePath
+ * @param {String} method
+ * @api private
+ * @receiver Query
+ */
+
+/**
+ * Determines if `conds` can be merged using `mquery().merge()`
+ *
+ * @method canMerge
+ * @memberOf Query
+ * @instance
+ * @param {Object} conds
+ * @return {Boolean}
+ * @api private
+ */
+
+/**
+ * Returns default options for this query.
+ *
+ * @param {Model} model
+ * @api private
+ */
+
+Query.prototype._optionsForExec = function(model) {
+ const options = utils.clone(this.options);
+
+ delete options.populate;
+ model = model || this.model;
+
+ if (!model) {
+ return options;
+ }
+
+ const safe = get(model, 'schema.options.safe', null);
+ if (!('safe' in options) && safe != null) {
+ setSafe(options, safe);
+ }
+
+ // Apply schema-level `writeConcern` option
+ applyWriteConcern(model.schema, options);
+
+ const readPreference = get(model, 'schema.options.read');
+ if (!('readPreference' in options) && readPreference) {
+ options.readPreference = readPreference;
+ }
+
+ if (options.upsert !== void 0) {
+ options.upsert = !!options.upsert;
+ }
+
+ return options;
+};
+
+/*!
+ * ignore
+ */
+
+const safeDeprecationWarning = 'Mongoose: the `safe` option is deprecated. ' +
+ 'Use write concerns instead: http://bit.ly/mongoose-w';
+
+const setSafe = util.deprecate(function setSafe(options, safe) {
+ options.safe = safe;
+}, safeDeprecationWarning);
+
+/**
+ * Sets the lean option.
+ *
+ * Documents returned from queries with the `lean` option enabled are plain javascript objects, not [MongooseDocuments](#document-js). They have no `save` method, getters/setters or other Mongoose magic applied.
+ *
+ * ####Example:
+ *
+ * new Query().lean() // true
+ * new Query().lean(true)
+ * new Query().lean(false)
+ *
+ * Model.find().lean().exec(function (err, docs) {
+ * docs[0] instanceof mongoose.Document // false
+ * });
+ *
+ * This is a [great](https://groups.google.com/forum/#!topic/mongoose-orm/u2_DzDydcnA/discussion) option in high-performance read-only scenarios, especially when combined with [stream](#query_Query-stream).
+ *
+ * @param {Boolean|Object} bool defaults to true
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.lean = function(v) {
+ this._mongooseOptions.lean = arguments.length ? v : true;
+ return this;
+};
+
+/**
+ * Adds a `$set` to this query's update without changing the operation.
+ * This is useful for query middleware so you can add an update regardless
+ * of whether you use `updateOne()`, `updateMany()`, `findOneAndUpdate()`, etc.
+ *
+ * ####Example:
+ *
+ * // Updates `{ $set: { updatedAt: new Date() } }`
+ * new Query().updateOne({}, {}).set('updatedAt', new Date());
+ * new Query().updateMany({}, {}).set({ updatedAt: new Date() });
+ *
+ * @param {String|Object} path path or object of key/value pairs to set
+ * @param {Any} [val] the value to set
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.set = function(path, val) {
+ if (typeof path === 'object') {
+ const keys = Object.keys(path);
+ for (const key of keys) {
+ this.set(key, path[key]);
+ }
+ return this;
+ }
+
+ this._update = this._update || {};
+ this._update.$set = this._update.$set || {};
+ this._update.$set[path] = val;
+ return this;
+};
+
+/**
+ * Gets/sets the error flag on this query. If this flag is not null or
+ * undefined, the `exec()` promise will reject without executing.
+ *
+ * ####Example:
+ *
+ * Query().error(); // Get current error value
+ * Query().error(null); // Unset the current error
+ * Query().error(new Error('test')); // `exec()` will resolve with test
+ * Schema.pre('find', function() {
+ * if (!this.getQuery().userId) {
+ * this.error(new Error('Not allowed to query without setting userId'));
+ * }
+ * });
+ *
+ * Note that query casting runs **after** hooks, so cast errors will override
+ * custom errors.
+ *
+ * ####Example:
+ * var TestSchema = new Schema({ num: Number });
+ * var TestModel = db.model('Test', TestSchema);
+ * TestModel.find({ num: 'not a number' }).error(new Error('woops')).exec(function(error) {
+ * // `error` will be a cast error because `num` failed to cast
+ * });
+ *
+ * @param {Error|null} err if set, `exec()` will fail fast before sending the query to MongoDB
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.error = function error(err) {
+ if (arguments.length === 0) {
+ return this._error;
+ }
+
+ this._error = err;
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+Query.prototype._unsetCastError = function _unsetCastError() {
+ if (this._error != null && !(this._error instanceof CastError)) {
+ return;
+ }
+ return this.error(null);
+};
+
+/**
+ * Getter/setter around the current mongoose-specific options for this query
+ * Below are the current Mongoose-specific options.
+ *
+ * - `populate`: an array representing what paths will be populated. Should have one entry for each call to [`Query.prototype.populate()`](/docs/api.html#query_Query-populate)
+ * - `lean`: if truthy, Mongoose will not [hydrate](/docs/api.html#model_Model.hydrate) any documents that are returned from this query. See [`Query.prototype.lean()`](/docs/api.html#query_Query-lean) for more information.
+ * - `strict`: controls how Mongoose handles keys that aren't in the schema for updates. This option is `true` by default, which means Mongoose will silently strip any paths in the update that aren't in the schema. See the [`strict` mode docs](/docs/guide.html#strict) for more information.
+ * - `strictQuery`: controls how Mongoose handles keys that aren't in the schema for the query `filter`. This option is `false` by default for backwards compatibility, which means Mongoose will allow `Model.find({ foo: 'bar' })` even if `foo` is not in the schema. See the [`strictQuery` docs](/docs/guide.html#strictQuery) for more information.
+ * - `useFindAndModify`: used to work around the [`findAndModify()` deprecation warning](/docs/deprecations.html#-findandmodify-)
+ * - `omitUndefined`: delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
+ * - `nearSphere`: use `$nearSphere` instead of `near()`. See the [`Query.prototype.nearSphere()` docs](/docs/api.html#query_Query-nearSphere)
+ *
+ * Mongoose maintains a separate object for internal options because
+ * Mongoose sends `Query.prototype.options` to the MongoDB server, and the
+ * above options are not relevant for the MongoDB server.
+ *
+ * @param {Object} options if specified, overwrites the current options
+ * @return {Object} the options
+ * @api public
+ */
+
+Query.prototype.mongooseOptions = function(v) {
+ if (arguments.length > 0) {
+ this._mongooseOptions = v;
+ }
+ return this._mongooseOptions;
+};
+
+/*!
+ * ignore
+ */
+
+Query.prototype._castConditions = function() {
+ try {
+ this.cast(this.model);
+ this._unsetCastError();
+ } catch (err) {
+ this.error(err);
+ }
+};
+
+/**
+ * Thunk around find()
+ *
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api private
+ */
+Query.prototype._find = wrapThunk(function(callback) {
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return null;
+ }
+
+ callback = _wrapThunkCallback(this, callback);
+
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+
+ const fields = this._fieldsForExec();
+ const mongooseOptions = this._mongooseOptions;
+ const _this = this;
+ const userProvidedFields = _this._userProvidedFields || {};
+
+ // Separate options to pass down to `completeMany()` in case we need to
+ // set a session on the document
+ const completeManyOptions = Object.assign({}, {
+ session: get(this, 'options.session', null)
+ });
+
+ const cb = (err, docs) => {
+ if (err) {
+ return callback(err);
+ }
+
+ if (docs.length === 0) {
+ return callback(null, docs);
+ }
+ if (this.options.explain) {
+ return callback(null, docs);
+ }
+
+ if (!mongooseOptions.populate) {
+ return mongooseOptions.lean ?
+ callback(null, docs) :
+ completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
+ }
+
+ const pop = helpers.preparePopulationOptionsMQ(_this, mongooseOptions);
+ completeManyOptions.populated = pop;
+ _this.model.populate(docs, pop, function(err, docs) {
+ if (err) return callback(err);
+ return mongooseOptions.lean ?
+ callback(null, docs) :
+ completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
+ });
+ };
+
+ const options = this._optionsForExec();
+ options.projection = this._fieldsForExec();
+ const filter = this._conditions;
+
+ this._collection.find(filter, options, cb);
+ return null;
+});
+
+/**
+ * Find all documents that match `selector`. The result will be an array of documents.
+ *
+ * If there are too many documents in the result to fit in memory, use
+ * [`Query.prototype.cursor()`](api.html#query_Query-cursor)
+ *
+ * ####Example
+ *
+ * // Using async/await
+ * const arr = await Movie.find({ year: { $gte: 1980, $lte: 1989 } });
+ *
+ * // Using callbacks
+ * Movie.find({ year: { $gte: 1980, $lte: 1989 } }, function(err, arr) {});
+ *
+ * @param {Object} [filter] mongodb selector. If not specified, returns all documents.
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.find = function(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+
+ conditions = utils.toObject(conditions);
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+
+ prepareDiscriminatorCriteria(this);
+ } else if (conditions != null) {
+ this.error(new ObjectParameterError(conditions, 'filter', 'find'));
+ }
+
+ // if we don't have a callback, then just return the query object
+ if (!callback) {
+ return Query.base.find.call(this);
+ }
+
+ this._find(callback);
+
+ return this;
+};
+
+/**
+ * Merges another Query or conditions object into this one.
+ *
+ * When a Query is passed, conditions, field selection and options are merged.
+ *
+ * @param {Query|Object} source
+ * @return {Query} this
+ */
+
+Query.prototype.merge = function(source) {
+ if (!source) {
+ return this;
+ }
+
+ const opts = { overwrite: true };
+
+ if (source instanceof Query) {
+ // if source has a feature, apply it to ourselves
+
+ if (source._conditions) {
+ utils.merge(this._conditions, source._conditions, opts);
+ }
+
+ if (source._fields) {
+ this._fields || (this._fields = {});
+ utils.merge(this._fields, source._fields, opts);
+ }
+
+ if (source.options) {
+ this.options || (this.options = {});
+ utils.merge(this.options, source.options, opts);
+ }
+
+ if (source._update) {
+ this._update || (this._update = {});
+ utils.mergeClone(this._update, source._update);
+ }
+
+ if (source._distinct) {
+ this._distinct = source._distinct;
+ }
+
+ return this;
+ }
+
+ // plain object
+ utils.merge(this._conditions, source, opts);
+
+ return this;
+};
+
+/**
+ * Adds a collation to this op (MongoDB 3.4 and up)
+ *
+ * @param {Object} value
+ * @return {Query} this
+ * @see MongoDB docs https://docs.mongodb.com/manual/reference/method/cursor.collation/#cursor.collation
+ * @api public
+ */
+
+Query.prototype.collation = function(value) {
+ if (this.options == null) {
+ this.options = {};
+ }
+ this.options.collation = value;
+ return this;
+};
+
+/**
+ * Hydrate a single doc from `findOne()`, `findOneAndUpdate()`, etc.
+ *
+ * @api private
+ */
+
+Query.prototype._completeOne = function(doc, res, callback) {
+ if (!doc) {
+ return callback(null, null);
+ }
+
+ const model = this.model;
+ const projection = utils.clone(this._fields);
+ const userProvidedFields = this._userProvidedFields || {};
+ // `populate`, `lean`
+ const mongooseOptions = this._mongooseOptions;
+ // `rawResult`
+ const options = this.options;
+
+ if (options.explain) {
+ return callback(null, doc);
+ }
+
+ if (!mongooseOptions.populate) {
+ return mongooseOptions.lean ?
+ _completeOneLean(doc, res, options, callback) :
+ completeOne(model, doc, res, options, projection, userProvidedFields,
+ null, callback);
+ }
+
+ const pop = helpers.preparePopulationOptionsMQ(this, this._mongooseOptions);
+ model.populate(doc, pop, (err, doc) => {
+ if (err) {
+ return callback(err);
+ }
+ return mongooseOptions.lean ?
+ _completeOneLean(doc, res, options, callback) :
+ completeOne(model, doc, res, options, projection, userProvidedFields,
+ pop, callback);
+ });
+};
+
+/**
+ * Thunk around findOne()
+ *
+ * @param {Function} [callback]
+ * @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
+ * @api private
+ */
+
+Query.prototype._findOne = wrapThunk(function(callback) {
+ this._castConditions();
+
+ if (this.error()) {
+ callback(this.error());
+ return null;
+ }
+
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+
+ // don't pass in the conditions because we already merged them in
+ Query.base.findOne.call(this, {}, (err, doc) => {
+ if (err) {
+ callback(err);
+ return null;
+ }
+
+ this._completeOne(doc, null, _wrapThunkCallback(this, callback));
+ });
+});
+
+/**
+ * Declares the query a findOne operation. When executed, the first found document is passed to the callback.
+ *
+ * Passing a `callback` executes the query. The result of the query is a single document.
+ *
+ * * *Note:* `conditions` is optional, and if `conditions` is null or undefined,
+ * mongoose will send an empty `findOne` command to MongoDB, which will return
+ * an arbitrary document. If you're querying by `_id`, use `Model.findById()`
+ * instead.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOne()`
+ *
+ * ####Example
+ *
+ * var query = Kitten.where({ color: 'white' });
+ * query.findOne(function (err, kitten) {
+ * if (err) return handleError(err);
+ * if (kitten) {
+ * // doc may be null if no document matched
+ * }
+ * });
+ *
+ * @param {Object} [filter] mongodb selector
+ * @param {Object} [projection] optional fields to return
+ * @param {Object} [options] see [`setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
+ * @param {Function} [callback] optional params are (error, document)
+ * @return {Query} this
+ * @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
+ * @see Query.select #query_Query-select
+ * @api public
+ */
+
+Query.prototype.findOne = function(conditions, projection, options, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = null;
+ projection = null;
+ options = null;
+ } else if (typeof projection === 'function') {
+ callback = projection;
+ options = null;
+ projection = null;
+ } else if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ // make sure we don't send in the whole Document to merge()
+ conditions = utils.toObject(conditions);
+
+ this.op = 'findOne';
+
+ if (options) {
+ this.setOptions(options);
+ }
+
+ if (projection) {
+ this.select(projection);
+ }
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+
+ prepareDiscriminatorCriteria(this);
+ } else if (conditions != null) {
+ this.error(new ObjectParameterError(conditions, 'filter', 'findOne'));
+ }
+
+ if (!callback) {
+ // already merged in the conditions, don't need to send them in.
+ return Query.base.findOne.call(this);
+ }
+
+ this._findOne(callback);
+
+ return this;
+};
+
+/**
+ * Thunk around count()
+ *
+ * @param {Function} [callback]
+ * @see count http://docs.mongodb.org/manual/reference/method/db.collection.count/
+ * @api private
+ */
+
+Query.prototype._count = wrapThunk(function(callback) {
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ this.error(err);
+ }
+
+ if (this.error()) {
+ return callback(this.error());
+ }
+
+ const conds = this._conditions;
+ const options = this._optionsForExec();
+
+ this._collection.count(conds, options, utils.tick(callback));
+});
+
+/**
+ * Thunk around countDocuments()
+ *
+ * @param {Function} [callback]
+ * @see countDocuments http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments
+ * @api private
+ */
+
+Query.prototype._countDocuments = wrapThunk(function(callback) {
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ this.error(err);
+ }
+
+ if (this.error()) {
+ return callback(this.error());
+ }
+
+ const conds = this._conditions;
+ const options = this._optionsForExec();
+
+ this._collection.collection.countDocuments(conds, options, utils.tick(callback));
+});
+
+/**
+ * Thunk around estimatedDocumentCount()
+ *
+ * @param {Function} [callback]
+ * @see estimatedDocumentCount http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#estimatedDocumentCount
+ * @api private
+ */
+
+Query.prototype._estimatedDocumentCount = wrapThunk(function(callback) {
+ if (this.error()) {
+ return callback(this.error());
+ }
+
+ const options = this._optionsForExec();
+
+ this._collection.collection.estimatedDocumentCount(options, utils.tick(callback));
+});
+
+/**
+ * Specifies this query as a `count` query.
+ *
+ * This method is deprecated. If you want to count the number of documents in
+ * a collection, e.g. `count({})`, use the [`estimatedDocumentCount()` function](/docs/api.html#query_Query-estimatedDocumentCount)
+ * instead. Otherwise, use the [`countDocuments()`](/docs/api.html#query_Query-countDocuments) function instead.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `count()`
+ *
+ * ####Example:
+ *
+ * var countQuery = model.where({ 'color': 'black' }).count();
+ *
+ * query.count({ color: 'black' }).count(callback)
+ *
+ * query.count({ color: 'black' }, callback)
+ *
+ * query.where('color', 'black').count(function (err, count) {
+ * if (err) return handleError(err);
+ * console.log('there are %d kittens', count);
+ * })
+ *
+ * @deprecated
+ * @param {Object} [filter] count documents that match this object
+ * @param {Function} [callback] optional params are (error, count)
+ * @return {Query} this
+ * @see count http://docs.mongodb.org/manual/reference/method/db.collection.count/
+ * @api public
+ */
+
+Query.prototype.count = function(filter, callback) {
+ if (typeof filter === 'function') {
+ callback = filter;
+ filter = undefined;
+ }
+
+ filter = utils.toObject(filter);
+
+ if (mquery.canMerge(filter)) {
+ this.merge(filter);
+ }
+
+ this.op = 'count';
+ if (!callback) {
+ return this;
+ }
+
+ this._count(callback);
+
+ return this;
+};
+
+/**
+ * Specifies this query as a `estimatedDocumentCount()` query. Faster than
+ * using `countDocuments()` for large collections because
+ * `estimatedDocumentCount()` uses collection metadata rather than scanning
+ * the entire collection.
+ *
+ * `estimatedDocumentCount()` does **not** accept a filter. `Model.find({ foo: bar }).estimatedDocumentCount()`
+ * is equivalent to `Model.find().estimatedDocumentCount()`
+ *
+ * This function triggers the following middleware.
+ *
+ * - `estimatedDocumentCount()`
+ *
+ * ####Example:
+ *
+ * await Model.find().estimatedDocumentCount();
+ *
+ * @param {Object} [options] passed transparently to the [MongoDB driver](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#estimatedDocumentCount)
+ * @param {Function} [callback] optional params are (error, count)
+ * @return {Query} this
+ * @see estimatedDocumentCount http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#estimatedDocumentCount
+ * @api public
+ */
+
+Query.prototype.estimatedDocumentCount = function(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = undefined;
+ }
+
+ if (typeof options === 'object' && options != null) {
+ this.setOptions(options);
+ }
+
+ this.op = 'estimatedDocumentCount';
+ if (!callback) {
+ return this;
+ }
+
+ this._estimatedDocumentCount(callback);
+
+ return this;
+};
+
+/**
+ * Specifies this query as a `countDocuments()` query. Behaves like `count()`,
+ * except it always does a full collection scan when passed an empty filter `{}`.
+ *
+ * There are also minor differences in how `countDocuments()` handles
+ * [`$where` and a couple geospatial operators](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments).
+ * versus `count()`.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `countDocuments()`
+ *
+ * ####Example:
+ *
+ * const countQuery = model.where({ 'color': 'black' }).countDocuments();
+ *
+ * query.countDocuments({ color: 'black' }).count(callback);
+ *
+ * query.countDocuments({ color: 'black' }, callback);
+ *
+ * query.where('color', 'black').countDocuments(function(err, count) {
+ * if (err) return handleError(err);
+ * console.log('there are %d kittens', count);
+ * });
+ *
+ * The `countDocuments()` function is similar to `count()`, but there are a
+ * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments).
+ * Below are the operators that `count()` supports but `countDocuments()` does not,
+ * and the suggested replacement:
+ *
+ * - `$where`: [`$expr`](https://docs.mongodb.com/manual/reference/operator/query/expr/)
+ * - `$near`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$center`](https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center)
+ * - `$nearSphere`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere)
+ *
+ * @param {Object} [filter] mongodb selector
+ * @param {Function} [callback] optional params are (error, count)
+ * @return {Query} this
+ * @see countDocuments http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments
+ * @api public
+ */
+
+Query.prototype.countDocuments = function(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ }
+
+ conditions = utils.toObject(conditions);
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ this.op = 'countDocuments';
+ if (!callback) {
+ return this;
+ }
+
+ this._countDocuments(callback);
+
+ return this;
+};
+
+/**
+ * Declares or executes a distict() operation.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * This function does not trigger any middleware.
+ *
+ * ####Example
+ *
+ * distinct(field, conditions, callback)
+ * distinct(field, conditions)
+ * distinct(field, callback)
+ * distinct(field)
+ * distinct(callback)
+ * distinct()
+ *
+ * @param {String} [field]
+ * @param {Object|Query} [filter]
+ * @param {Function} [callback] optional params are (error, arr)
+ * @return {Query} this
+ * @see distinct http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
+ * @api public
+ */
+
+Query.prototype.distinct = function(field, conditions, callback) {
+ if (!callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ } else if (typeof field === 'function') {
+ callback = field;
+ field = undefined;
+ conditions = undefined;
+ }
+ }
+
+ conditions = utils.toObject(conditions);
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+
+ prepareDiscriminatorCriteria(this);
+ } else if (conditions != null) {
+ this.error(new ObjectParameterError(conditions, 'filter', 'distinct'));
+ }
+
+ if (callback != null) {
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return this;
+ }
+ }
+
+ return Query.base.distinct.call(this, {}, field, callback);
+};
+
+/**
+ * Sets the sort order
+ *
+ * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
+ *
+ * If a string is passed, it must be a space delimited list of path names. The
+ * sort order of each path is ascending unless the path name is prefixed with `-`
+ * which will be treated as descending.
+ *
+ * ####Example
+ *
+ * // sort by "field" ascending and "test" descending
+ * query.sort({ field: 'asc', test: -1 });
+ *
+ * // equivalent
+ * query.sort('field -test');
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @param {Object|String} arg
+ * @return {Query} this
+ * @see cursor.sort http://docs.mongodb.org/manual/reference/method/cursor.sort/
+ * @api public
+ */
+
+Query.prototype.sort = function(arg) {
+ if (arguments.length > 1) {
+ throw new Error('sort() only takes 1 Argument');
+ }
+
+ return Query.base.sort.call(this, arg);
+};
+
+/**
+ * Declare and/or execute this query as a remove() operation.
+ *
+ * This function does not trigger any middleware
+ *
+ * ####Example
+ *
+ * Model.remove({ artist: 'Anne Murray' }, callback)
+ *
+ * ####Note
+ *
+ * The operation is only executed when a callback is passed. To force execution without a callback, you must first call `remove()` and then execute it by using the `exec()` method.
+ *
+ * // not executed
+ * var query = Model.find().remove({ name: 'Anne Murray' })
+ *
+ * // executed
+ * query.remove({ name: 'Anne Murray' }, callback)
+ * query.remove({ name: 'Anne Murray' }).remove(callback)
+ *
+ * // executed without a callback
+ * query.exec()
+ *
+ * // summary
+ * query.remove(conds, fn); // executes
+ * query.remove(conds)
+ * query.remove(fn) // executes
+ * query.remove()
+ *
+ * @param {Object|Query} [filter] mongodb selector
+ * @param {Function} [callback] optional params are (error, writeOpResult)
+ * @return {Query} this
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @see remove http://docs.mongodb.org/manual/reference/method/db.collection.remove/
+ * @api public
+ */
+
+Query.prototype.remove = function(filter, callback) {
+ if (typeof filter === 'function') {
+ callback = filter;
+ filter = null;
+ }
+
+ filter = utils.toObject(filter);
+
+ if (mquery.canMerge(filter)) {
+ this.merge(filter);
+
+ prepareDiscriminatorCriteria(this);
+ } else if (filter != null) {
+ this.error(new ObjectParameterError(filter, 'filter', 'remove'));
+ }
+
+ if (!callback) {
+ return Query.base.remove.call(this);
+ }
+
+ this._remove(callback);
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+Query.prototype._remove = wrapThunk(function(callback) {
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return this;
+ }
+
+ callback = _wrapThunkCallback(this, callback);
+
+ return Query.base.remove.call(this, helpers.handleWriteOpResult(callback));
+});
+
+/**
+ * Declare and/or execute this query as a `deleteOne()` operation. Works like
+ * remove, except it deletes at most one document regardless of the `single`
+ * option.
+ *
+ * This function does not trigger any middleware.
+ *
+ * ####Example
+ *
+ * Character.deleteOne({ name: 'Eddard Stark' }, callback)
+ * Character.deleteOne({ name: 'Eddard Stark' }).then(next)
+ *
+ * @param {Object|Query} [filter] mongodb selector
+ * @param {Function} [callback] optional params are (error, writeOpResult)
+ * @return {Query} this
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @see remove http://docs.mongodb.org/manual/reference/method/db.collection.remove/
+ * @api public
+ */
+
+Query.prototype.deleteOne = function(filter, callback) {
+ if (typeof filter === 'function') {
+ callback = filter;
+ filter = null;
+ }
+
+ filter = utils.toObject(filter);
+
+ if (mquery.canMerge(filter)) {
+ this.merge(filter);
+
+ prepareDiscriminatorCriteria(this);
+ } else if (filter != null) {
+ this.error(new ObjectParameterError(filter, 'filter', 'deleteOne'));
+ }
+
+ if (!callback) {
+ return Query.base.deleteOne.call(this);
+ }
+
+ this._deleteOne.call(this, callback);
+
+ return this;
+};
+
+/*!
+ * Internal thunk for `deleteOne()`
+ */
+
+Query.prototype._deleteOne = wrapThunk(function(callback) {
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return this;
+ }
+
+ callback = _wrapThunkCallback(this, callback);
+
+ return Query.base.deleteOne.call(this, helpers.handleWriteOpResult(callback));
+});
+
+/**
+ * Declare and/or execute this query as a `deleteMany()` operation. Works like
+ * remove, except it deletes _every_ document that matches `criteria` in the
+ * collection, regardless of the value of `single`.
+ *
+ * This function does not trigger any middleware
+ *
+ * ####Example
+ *
+ * Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }, callback)
+ * Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }).then(next)
+ *
+ * @param {Object|Query} [filter] mongodb selector
+ * @param {Function} [callback] optional params are (error, writeOpResult)
+ * @return {Query} this
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @see remove http://docs.mongodb.org/manual/reference/method/db.collection.remove/
+ * @api public
+ */
+
+Query.prototype.deleteMany = function(filter, callback) {
+ if (typeof filter === 'function') {
+ callback = filter;
+ filter = null;
+ }
+
+ filter = utils.toObject(filter);
+
+ if (mquery.canMerge(filter)) {
+ this.merge(filter);
+
+ prepareDiscriminatorCriteria(this);
+ } else if (filter != null) {
+ this.error(new ObjectParameterError(filter, 'filter', 'deleteMany'));
+ }
+
+ if (!callback) {
+ return Query.base.deleteMany.call(this);
+ }
+
+ this._deleteMany.call(this, callback);
+
+ return this;
+};
+
+/*!
+ * Internal thunk around `deleteMany()`
+ */
+
+Query.prototype._deleteMany = wrapThunk(function(callback) {
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return this;
+ }
+
+ callback = _wrapThunkCallback(this, callback);
+
+ return Query.base.deleteMany.call(this, helpers.handleWriteOpResult(callback));
+});
+
+/*!
+ * hydrates a document
+ *
+ * @param {Model} model
+ * @param {Document} doc
+ * @param {Object} res 3rd parameter to callback
+ * @param {Object} fields
+ * @param {Query} self
+ * @param {Array} [pop] array of paths used in population
+ * @param {Function} callback
+ */
+
+function completeOne(model, doc, res, options, fields, userProvidedFields, pop, callback) {
+ const opts = pop ?
+ {populated: pop}
+ : undefined;
+
+ const casted = helpers.createModel(model, doc, fields, userProvidedFields);
+ try {
+ casted.init(doc, opts, _init);
+ } catch (error) {
+ _init(error);
+ }
+
+ function _init(err) {
+ if (err) {
+ return process.nextTick(() => callback(err));
+ }
+
+ casted.$session(options.session);
+
+ if (options.rawResult) {
+ res.value = casted;
+ return process.nextTick(() => callback(null, res));
+ }
+ process.nextTick(() => callback(null, casted));
+ }
+}
+
+/*!
+ * If the model is a discriminator type and not root, then add the key & value to the criteria.
+ */
+
+function prepareDiscriminatorCriteria(query) {
+ if (!query || !query.model || !query.model.schema) {
+ return;
+ }
+
+ const schema = query.model.schema;
+
+ if (schema && schema.discriminatorMapping && !schema.discriminatorMapping.isRoot) {
+ query._conditions[schema.discriminatorMapping.key] = schema.discriminatorMapping.value;
+ }
+}
+
+/**
+ * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) update command.
+ *
+ * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndUpdate()`
+ *
+ * ####Available options
+ *
+ * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
+ * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
+ * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `context` (string) if set to 'query' and `runValidators` is on, `this` will refer to the query in custom validator functions that update validation runs. Does nothing if `runValidators` is false.
+ *
+ * ####Callback Signature
+ * function(error, doc) {
+ * // error: any errors that occurred
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
+ * }
+ *
+ * ####Examples
+ *
+ * query.findOneAndUpdate(conditions, update, options, callback) // executes
+ * query.findOneAndUpdate(conditions, update, options) // returns Query
+ * query.findOneAndUpdate(conditions, update, callback) // executes
+ * query.findOneAndUpdate(conditions, update) // returns Query
+ * query.findOneAndUpdate(update, callback) // returns Query
+ * query.findOneAndUpdate(update) // returns Query
+ * query.findOneAndUpdate(callback) // executes
+ * query.findOneAndUpdate() // returns Query
+ *
+ * @method findOneAndUpdate
+ * @memberOf Query
+ * @instance
+ * @param {Object|Query} [query]
+ * @param {Object} [doc]
+ * @param {Object} [options]
+ * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
+ * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
+ * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](http://mongoosejs.com/docs/api.html#query_Query-lean).
+ * @param {Function} [callback] optional params are (error, doc), _unless_ `rawResult` is used, in which case params are (error, writeOpResult)
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.findOneAndUpdate = function(criteria, doc, options, callback) {
+ this.op = 'findOneAndUpdate';
+ this._validate();
+
+ switch (arguments.length) {
+ case 3:
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 2:
+ if (typeof doc === 'function') {
+ callback = doc;
+ doc = criteria;
+ criteria = undefined;
+ }
+ options = undefined;
+ break;
+ case 1:
+ if (typeof criteria === 'function') {
+ callback = criteria;
+ criteria = options = doc = undefined;
+ } else {
+ doc = criteria;
+ criteria = options = undefined;
+ }
+ }
+
+ if (mquery.canMerge(criteria)) {
+ this.merge(criteria);
+ }
+
+ // apply doc
+ if (doc) {
+ this._mergeUpdate(doc);
+ }
+
+ if (options) {
+ options = utils.clone(options);
+ if (options.projection) {
+ this.select(options.projection);
+ delete options.projection;
+ }
+ if (options.fields) {
+ this.select(options.fields);
+ delete options.fields;
+ }
+
+ this.setOptions(options);
+ }
+
+ if (!callback) {
+ return this;
+ }
+
+ this._findOneAndUpdate(callback);
+
+ return this;
+};
+
+/*!
+ * Thunk around findOneAndUpdate()
+ *
+ * @param {Function} [callback]
+ * @api private
+ */
+
+Query.prototype._findOneAndUpdate = wrapThunk(function(callback) {
+ if (this.error() != null) {
+ return callback(this.error());
+ }
+
+ this._findAndModify('update', callback);
+});
+
+/**
+ * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command.
+ *
+ * Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if `callback` is passed.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndRemove()`
+ *
+ * ####Available options
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ *
+ * ####Callback Signature
+ * function(error, doc) {
+ * // error: any errors that occurred
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
+ * }
+ *
+ * ####Examples
+ *
+ * A.where().findOneAndRemove(conditions, options, callback) // executes
+ * A.where().findOneAndRemove(conditions, options) // return Query
+ * A.where().findOneAndRemove(conditions, callback) // executes
+ * A.where().findOneAndRemove(conditions) // returns Query
+ * A.where().findOneAndRemove(callback) // executes
+ * A.where().findOneAndRemove() // returns Query
+ *
+ * @method findOneAndRemove
+ * @memberOf Query
+ * @instance
+ * @param {Object} [conditions]
+ * @param {Object} [options]
+ * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
+ * @param {Function} [callback] optional params are (error, document)
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @api public
+ */
+
+Query.prototype.findOneAndRemove = function(conditions, options, callback) {
+ this.op = 'findOneAndRemove';
+ this._validate();
+
+ switch (arguments.length) {
+ case 2:
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 1:
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ options = undefined;
+ }
+ break;
+ }
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ options && this.setOptions(options);
+
+ if (!callback) {
+ return this;
+ }
+
+ this._findOneAndRemove(callback);
+
+ return this;
+};
+
+/**
+ * Issues a MongoDB [findOneAndDelete](https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndDelete/) command.
+ *
+ * Finds a matching document, removes it, and passes the found document (if any) to the callback. Executes immediately if `callback` is passed.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndDelete()`
+ *
+ * This function differs slightly from `Model.findOneAndRemove()` in that
+ * `findOneAndRemove()` becomes a [MongoDB `findAndModify()` command](https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/),
+ * as opposed to a `findOneAndDelete()` command. For most mongoose use cases,
+ * this distinction is purely pedantic. You should use `findOneAndDelete()`
+ * unless you have a good reason not to.
+ *
+ * ####Available options
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ *
+ * ####Callback Signature
+ * function(error, doc) {
+ * // error: any errors that occurred
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
+ * }
+ *
+ * ####Examples
+ *
+ * A.where().findOneAndDelete(conditions, options, callback) // executes
+ * A.where().findOneAndDelete(conditions, options) // return Query
+ * A.where().findOneAndDelete(conditions, callback) // executes
+ * A.where().findOneAndDelete(conditions) // returns Query
+ * A.where().findOneAndDelete(callback) // executes
+ * A.where().findOneAndDelete() // returns Query
+ *
+ * @method findOneAndDelete
+ * @memberOf Query
+ * @param {Object} [conditions]
+ * @param {Object} [options]
+ * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
+ * @param {Function} [callback] optional params are (error, document)
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @api public
+ */
+
+Query.prototype.findOneAndDelete = function(conditions, options, callback) {
+ this.op = 'findOneAndDelete';
+ this._validate();
+
+ switch (arguments.length) {
+ case 2:
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 1:
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ options = undefined;
+ }
+ break;
+ }
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ options && this.setOptions(options);
+
+ if (!callback) {
+ return this;
+ }
+
+ this._findOneAndDelete(callback);
+
+ return this;
+};
+
+/*!
+ * Thunk around findOneAndDelete()
+ *
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api private
+ */
+Query.prototype._findOneAndDelete = wrapThunk(function(callback) {
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return null;
+ }
+
+ const filter = this._conditions;
+ const options = this._optionsForExec();
+ let fields = null;
+
+ if (this._fields != null) {
+ options.projection = this._castFields(utils.clone(this._fields));
+ fields = options.projection;
+ if (fields instanceof Error) {
+ callback(fields);
+ return null;
+ }
+ }
+
+ this._collection.collection.findOneAndDelete(filter, options, _wrapThunkCallback(this, (err, res) => {
+ if (err) {
+ return callback(err);
+ }
+
+ const doc = res.value;
+
+ return this._completeOne(doc, res, callback);
+ }));
+});
+
+/**
+ * Issues a MongoDB [findOneAndReplace](https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndReplace/) command.
+ *
+ * Finds a matching document, removes it, and passes the found document (if any) to the callback. Executes immediately if `callback` is passed.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `findOneAndReplace()`
+ *
+ * ####Available options
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ *
+ * ####Callback Signature
+ * function(error, doc) {
+ * // error: any errors that occurred
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
+ * }
+ *
+ * ####Examples
+ *
+ * A.where().findOneAndReplace(conditions, options, callback) // executes
+ * A.where().findOneAndReplace(conditions, options) // return Query
+ * A.where().findOneAndReplace(conditions, callback) // executes
+ * A.where().findOneAndReplace(conditions) // returns Query
+ * A.where().findOneAndReplace(callback) // executes
+ * A.where().findOneAndReplace() // returns Query
+ *
+ * @method findOneAndReplace
+ * @memberOf Query
+ * @param {Object} [conditions]
+ * @param {Object} [options]
+ * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
+ * @param {Function} [callback] optional params are (error, document)
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.findOneAndReplace = function(conditions, options, callback) {
+ this.op = 'findOneAndReplace';
+ this._validate();
+
+ switch (arguments.length) {
+ case 2:
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 1:
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ options = undefined;
+ }
+ break;
+ }
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ options && this.setOptions(options);
+
+ if (!callback) {
+ return this;
+ }
+
+ this._findOneAndReplace(callback);
+
+ return this;
+};
+
+/*!
+ * Thunk around findOneAndReplace()
+ *
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api private
+ */
+Query.prototype._findOneAndReplace = wrapThunk(function(callback) {
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return null;
+ }
+
+ const filter = this._conditions;
+ const options = this._optionsForExec();
+ let fields = null;
+
+ if (this._fields != null) {
+ options.projection = this._castFields(utils.clone(this._fields));
+ fields = options.projection;
+ if (fields instanceof Error) {
+ callback(fields);
+ return null;
+ }
+ }
+
+ this._collection.collection.findOneAndReplace(filter, options, (err, res) => {
+ if (err) {
+ return callback(err);
+ }
+
+ const doc = res.value;
+
+ return this._completeOne(doc, res, callback);
+ });
+});
+
+/*!
+ * Thunk around findOneAndRemove()
+ *
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api private
+ */
+Query.prototype._findOneAndRemove = wrapThunk(function(callback) {
+ if (this.error() != null) {
+ callback(this.error());
+ return;
+ }
+
+ this._findAndModify('remove', callback);
+});
+
+/*!
+ * Get options from query opts, falling back to the base mongoose object.
+ */
+
+function _getOption(query, option, def) {
+ const opts = query._optionsForExec(query.model);
+
+ if (option in opts) {
+ return opts[option];
+ }
+ if (option in query.model.base.options) {
+ return query.model.base.options[option];
+ }
+ return def;
+}
+
+/*!
+ * Override mquery.prototype._findAndModify to provide casting etc.
+ *
+ * @param {String} type - either "remove" or "update"
+ * @param {Function} callback
+ * @api private
+ */
+
+Query.prototype._findAndModify = function(type, callback) {
+ if (typeof callback !== 'function') {
+ throw new Error('Expected callback in _findAndModify');
+ }
+
+ const model = this.model;
+ const schema = model.schema;
+ const _this = this;
+ let castedDoc = this._update;
+ let fields;
+ let doValidate;
+
+ const castedQuery = castQuery(this);
+ if (castedQuery instanceof Error) {
+ return callback(castedQuery);
+ }
+
+ const opts = this._optionsForExec(model);
+
+ if ('strict' in opts) {
+ this._mongooseOptions.strict = opts.strict;
+ }
+
+ const isOverwriting = this.options.overwrite && !hasDollarKeys(castedDoc);
+ if (isOverwriting) {
+ castedDoc = new this.model(castedDoc, null, true);
+ }
+
+ if (type === 'remove') {
+ opts.remove = true;
+ } else {
+ if (!('new' in opts)) {
+ opts.new = false;
+ }
+ if (!('upsert' in opts)) {
+ opts.upsert = false;
+ }
+ if (opts.upsert || opts['new']) {
+ opts.remove = false;
+ }
+
+ if (isOverwriting) {
+ doValidate = function(callback) {
+ castedDoc.validate(callback);
+ };
+ } else {
+ castedDoc = castDoc(this, opts.overwrite);
+ castedDoc = setDefaultsOnInsert(this._conditions, schema, castedDoc, opts);
+ if (!castedDoc) {
+ if (opts.upsert) {
+ // still need to do the upsert to empty doc
+ const doc = utils.clone(castedQuery);
+ delete doc._id;
+ castedDoc = {$set: doc};
+ } else {
+ this.findOne(callback);
+ return this;
+ }
+ } else if (castedDoc instanceof Error) {
+ return callback(castedDoc);
+ } else {
+ // In order to make MongoDB 2.6 happy (see
+ // https://jira.mongodb.org/browse/SERVER-12266 and related issues)
+ // if we have an actual update document but $set is empty, junk the $set.
+ if (castedDoc.$set && Object.keys(castedDoc.$set).length === 0) {
+ delete castedDoc.$set;
+ }
+ }
+
+ doValidate = updateValidators(this, schema, castedDoc, opts);
+ }
+ }
+
+ this._applyPaths();
+
+ const options = this._mongooseOptions;
+
+ if (this._fields) {
+ fields = utils.clone(this._fields);
+ opts.projection = this._castFields(fields);
+ if (opts.projection instanceof Error) {
+ return callback(opts.projection);
+ }
+ }
+
+ if (opts.sort) convertSortToArray(opts);
+
+ const cb = function(err, doc, res) {
+ if (err) {
+ return callback(err);
+ }
+
+ _this._completeOne(doc, res, callback);
+ };
+
+ let _callback;
+
+ let useFindAndModify = true;
+ const runValidators = _getOption(this, 'runValidators', false);
+ const base = _this.model && _this.model.base;
+ const conn = get(model, 'collection.conn', {});
+ if ('useFindAndModify' in base.options) {
+ useFindAndModify = base.get('useFindAndModify');
+ }
+ if ('useFindAndModify' in conn.config) {
+ useFindAndModify = conn.config.useFindAndModify;
+ }
+ if ('useFindAndModify' in options) {
+ useFindAndModify = options.useFindAndModify;
+ }
+ if (useFindAndModify === false) {
+ // Bypass mquery
+ const collection = _this._collection.collection;
+ if ('new' in opts) {
+ opts.returnOriginal = !opts['new'];
+ delete opts['new'];
+ }
+
+ if (type === 'remove') {
+ collection.findOneAndDelete(castedQuery, opts, _wrapThunkCallback(_this, function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+
+ return this;
+ }
+
+ // honors legacy overwrite option for backward compatibility
+ const updateMethod = isOverwriting ? 'findOneAndReplace' : 'findOneAndUpdate';
+
+ if (runValidators && doValidate) {
+ _callback = function(error) {
+ if (error) {
+ return callback(error);
+ }
+ if (castedDoc && castedDoc.toBSON) {
+ castedDoc = castedDoc.toBSON();
+ }
+
+ collection[updateMethod](castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ };
+
+ try {
+ doValidate(_callback);
+ } catch (error) {
+ callback(error);
+ }
+ } else {
+ if (castedDoc && castedDoc.toBSON) {
+ castedDoc = castedDoc.toBSON();
+ }
+ collection[updateMethod](castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ }
+
+ return this;
+ }
+
+ if (runValidators && doValidate) {
+ _callback = function(error) {
+ if (error) {
+ return callback(error);
+ }
+ if (castedDoc && castedDoc.toBSON) {
+ castedDoc = castedDoc.toBSON();
+ }
+ _this._collection.findAndModify(castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ };
+
+ try {
+ doValidate(_callback);
+ } catch (error) {
+ callback(error);
+ }
+ } else {
+ if (castedDoc && castedDoc.toBSON) {
+ castedDoc = castedDoc.toBSON();
+ }
+ this._collection.findAndModify(castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ }
+
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+function _completeOneLean(doc, res, opts, callback) {
+ if (opts.rawResult) {
+ return callback(null, res);
+ }
+ return callback(null, doc);
+}
+
+/*!
+ * Override mquery.prototype._mergeUpdate to handle mongoose objects in
+ * updates.
+ *
+ * @param {Object} doc
+ * @api private
+ */
+
+Query.prototype._mergeUpdate = function(doc) {
+ if (!this._update) this._update = {};
+ if (doc instanceof Query) {
+ if (doc._update) {
+ utils.mergeClone(this._update, doc._update);
+ }
+ } else {
+ utils.mergeClone(this._update, doc);
+ }
+};
+
+/*!
+ * The mongodb driver 1.3.23 only supports the nested array sort
+ * syntax. We must convert it or sorting findAndModify will not work.
+ */
+
+function convertSortToArray(opts) {
+ if (Array.isArray(opts.sort)) {
+ return;
+ }
+ if (!utils.isObject(opts.sort)) {
+ return;
+ }
+
+ const sort = [];
+
+ for (const key in opts.sort) {
+ if (utils.object.hasOwnProperty(opts.sort, key)) {
+ sort.push([key, opts.sort[key]]);
+ }
+ }
+
+ opts.sort = sort;
+}
+
+/*!
+ * ignore
+ */
+
+function _updateThunk(op, callback) {
+ const schema = this.model.schema;
+ let doValidate;
+ const _this = this;
+
+ this._castConditions();
+
+ if (this.error() != null) {
+ callback(this.error());
+ return null;
+ }
+
+ callback = _wrapThunkCallback(this, callback);
+
+ const castedQuery = this._conditions;
+ let castedDoc;
+ const options = this._optionsForExec(this.model);
+
+ ++this._executionCount;
+
+ this._update = utils.clone(this._update, options);
+ const isOverwriting = this.options.overwrite && !hasDollarKeys(this._update);
+ if (isOverwriting) {
+ castedDoc = new this.model(this._update, null, true);
+ } else {
+ castedDoc = castDoc(this, options.overwrite);
+
+ if (castedDoc instanceof Error) {
+ callback(castedDoc);
+ return null;
+ }
+
+ if (castedDoc == null || Object.keys(castedDoc).length === 0) {
+ callback(null, 0);
+ return null;
+ }
+
+ castedDoc = setDefaultsOnInsert(this._conditions, this.model.schema,
+ castedDoc, options);
+ }
+
+ const runValidators = _getOption(this, 'runValidators', false);
+ if (runValidators) {
+ if (isOverwriting) {
+ doValidate = function(callback) {
+ castedDoc.validate(callback);
+ };
+ } else {
+ doValidate = updateValidators(this, schema, castedDoc, options);
+ }
+ const _callback = function(err) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (castedDoc.toBSON) {
+ castedDoc = castedDoc.toBSON();
+ }
+ _this._collection[op](castedQuery, castedDoc, options, callback);
+ };
+ try {
+ doValidate(_callback);
+ } catch (err) {
+ process.nextTick(function() {
+ callback(err);
+ });
+ }
+ return null;
+ }
+
+ if (castedDoc.toBSON) {
+ castedDoc = castedDoc.toBSON();
+ }
+
+ this._collection[op](castedQuery, castedDoc, options, callback);
+ return null;
+}
+
+/*!
+ * Internal thunk for .update()
+ *
+ * @param {Function} callback
+ * @see Model.update #model_Model.update
+ * @api private
+ */
+Query.prototype._execUpdate = wrapThunk(function(callback) {
+ return _updateThunk.call(this, 'update', callback);
+});
+
+/*!
+ * Internal thunk for .updateMany()
+ *
+ * @param {Function} callback
+ * @see Model.update #model_Model.update
+ * @api private
+ */
+Query.prototype._updateMany = wrapThunk(function(callback) {
+ return _updateThunk.call(this, 'updateMany', callback);
+});
+
+/*!
+ * Internal thunk for .updateOne()
+ *
+ * @param {Function} callback
+ * @see Model.update #model_Model.update
+ * @api private
+ */
+Query.prototype._updateOne = wrapThunk(function(callback) {
+ return _updateThunk.call(this, 'updateOne', callback);
+});
+
+/*!
+ * Internal thunk for .replaceOne()
+ *
+ * @param {Function} callback
+ * @see Model.replaceOne #model_Model.replaceOne
+ * @api private
+ */
+Query.prototype._replaceOne = wrapThunk(function(callback) {
+ return _updateThunk.call(this, 'replaceOne', callback);
+});
+
+/**
+ * Declare and/or execute this query as an update() operation.
+ *
+ * _All paths passed that are not $atomic operations will become $set ops._
+ *
+ * This function triggers the following middleware.
+ *
+ * - `update()`
+ *
+ * ####Example
+ *
+ * Model.where({ _id: id }).update({ title: 'words' })
+ *
+ * // becomes
+ *
+ * Model.where({ _id: id }).update({ $set: { title: 'words' }})
+ *
+ * ####Valid options:
+ *
+ * - `upsert` (boolean) whether to create the doc if it doesn't match (false)
+ * - `multi` (boolean) whether multiple documents should be updated (false)
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `strict` (boolean) overrides the `strict` option for this update
+ * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false)
+ * - `context` (string) if set to 'query' and `runValidators` is on, `this` will refer to the query in custom validator functions that update validation runs. Does nothing if `runValidators` is false.
+ * - `read`
+ * - `writeConcern`
+ *
+ * ####Note
+ *
+ * Passing an empty object `{}` as the doc will result in a no-op unless the `overwrite` option is passed. Without the `overwrite` option set, the update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting documents in the collection.
+ *
+ * ####Note
+ *
+ * The operation is only executed when a callback is passed. To force execution without a callback, we must first call update() and then execute it by using the `exec()` method.
+ *
+ * var q = Model.where({ _id: id });
+ * q.update({ $set: { name: 'bob' }}).update(); // not executed
+ *
+ * q.update({ $set: { name: 'bob' }}).exec(); // executed
+ *
+ * // keys that are not $atomic ops become $set.
+ * // this executes the same command as the previous example.
+ * q.update({ name: 'bob' }).exec();
+ *
+ * // overwriting with empty docs
+ * var q = Model.where({ _id: id }).setOptions({ overwrite: true })
+ * q.update({ }, callback); // executes
+ *
+ * // multi update with overwrite to empty doc
+ * var q = Model.where({ _id: id });
+ * q.setOptions({ multi: true, overwrite: true })
+ * q.update({ });
+ * q.update(callback); // executed
+ *
+ * // multi updates
+ * Model.where()
+ * .update({ name: /^match/ }, { $set: { arr: [] }}, { multi: true }, callback)
+ *
+ * // more multi updates
+ * Model.where()
+ * .setOptions({ multi: true })
+ * .update({ $set: { arr: [] }}, callback)
+ *
+ * // single update by default
+ * Model.where({ email: 'address@example.com' })
+ * .update({ $inc: { counter: 1 }}, callback)
+ *
+ * API summary
+ *
+ * update(criteria, doc, options, cb) // executes
+ * update(criteria, doc, options)
+ * update(criteria, doc, cb) // executes
+ * update(criteria, doc)
+ * update(doc, cb) // executes
+ * update(doc)
+ * update(cb) // executes
+ * update(true) // executes
+ * update()
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
+ * @param {Function} [callback] optional, params are (error, writeOpResult)
+ * @return {Query} this
+ * @see Model.update #model_Model.update
+ * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @api public
+ */
+
+Query.prototype.update = function(conditions, doc, options, callback) {
+ if (typeof options === 'function') {
+ // .update(conditions, doc, callback)
+ callback = options;
+ options = null;
+ } else if (typeof doc === 'function') {
+ // .update(doc, callback);
+ callback = doc;
+ doc = conditions;
+ conditions = {};
+ options = null;
+ } else if (typeof conditions === 'function') {
+ // .update(callback)
+ callback = conditions;
+ conditions = undefined;
+ doc = undefined;
+ options = undefined;
+ } else if (typeof conditions === 'object' && !doc && !options && !callback) {
+ // .update(doc)
+ doc = conditions;
+ conditions = undefined;
+ options = undefined;
+ callback = undefined;
+ }
+
+ return _update(this, 'update', conditions, doc, options, callback);
+};
+
+/**
+ * Declare and/or execute this query as an updateMany() operation. Same as
+ * `update()`, except MongoDB will update _all_ documents that match
+ * `criteria` (as opposed to just the first one) regardless of the value of
+ * the `multi` option.
+ *
+ * **Note** updateMany will _not_ fire update middleware. Use `pre('updateMany')`
+ * and `post('updateMany')` instead.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `updateMany()`
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
+ * @param {Function} [callback] optional params are (error, writeOpResult)
+ * @return {Query} this
+ * @see Model.update #model_Model.update
+ * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @api public
+ */
+
+Query.prototype.updateMany = function(conditions, doc, options, callback) {
+ if (typeof options === 'function') {
+ // .update(conditions, doc, callback)
+ callback = options;
+ options = null;
+ } else if (typeof doc === 'function') {
+ // .update(doc, callback);
+ callback = doc;
+ doc = conditions;
+ conditions = {};
+ options = null;
+ } else if (typeof conditions === 'function') {
+ // .update(callback)
+ callback = conditions;
+ conditions = undefined;
+ doc = undefined;
+ options = undefined;
+ } else if (typeof conditions === 'object' && !doc && !options && !callback) {
+ // .update(doc)
+ doc = conditions;
+ conditions = undefined;
+ options = undefined;
+ callback = undefined;
+ }
+
+ return _update(this, 'updateMany', conditions, doc, options, callback);
+};
+
+/**
+ * Declare and/or execute this query as an updateOne() operation. Same as
+ * `update()`, except it does not support the `multi` or `overwrite` options.
+ *
+ * - MongoDB will update _only_ the first document that matches `criteria` regardless of the value of the `multi` option.
+ * - Use `replaceOne()` if you want to overwrite an entire document rather than using atomic operators like `$set`.
+ *
+ * **Note** updateOne will _not_ fire update middleware. Use `pre('updateOne')`
+ * and `post('updateOne')` instead.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `updateOne()`
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
+ * @param {Function} [callback] params are (error, writeOpResult)
+ * @return {Query} this
+ * @see Model.update #model_Model.update
+ * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @api public
+ */
+
+Query.prototype.updateOne = function(conditions, doc, options, callback) {
+ if (typeof options === 'function') {
+ // .update(conditions, doc, callback)
+ callback = options;
+ options = null;
+ } else if (typeof doc === 'function') {
+ // .update(doc, callback);
+ callback = doc;
+ doc = conditions;
+ conditions = {};
+ options = null;
+ } else if (typeof conditions === 'function') {
+ // .update(callback)
+ callback = conditions;
+ conditions = undefined;
+ doc = undefined;
+ options = undefined;
+ } else if (typeof conditions === 'object' && !doc && !options && !callback) {
+ // .update(doc)
+ doc = conditions;
+ conditions = undefined;
+ options = undefined;
+ callback = undefined;
+ }
+
+ return _update(this, 'updateOne', conditions, doc, options, callback);
+};
+
+/**
+ * Declare and/or execute this query as a replaceOne() operation. Same as
+ * `update()`, except MongoDB will replace the existing document and will
+ * not accept any atomic operators (`$set`, etc.)
+ *
+ * **Note** replaceOne will _not_ fire update middleware. Use `pre('replaceOne')`
+ * and `post('replaceOne')` instead.
+ *
+ * This function triggers the following middleware.
+ *
+ * - `replaceOne()`
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ * @param {Function} [callback] optional params are (error, writeOpResult)
+ * @return {Query} this
+ * @see Model.update #model_Model.update
+ * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
+ * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
+ * @api public
+ */
+
+Query.prototype.replaceOne = function(conditions, doc, options, callback) {
+ if (typeof options === 'function') {
+ // .update(conditions, doc, callback)
+ callback = options;
+ options = null;
+ } else if (typeof doc === 'function') {
+ // .update(doc, callback);
+ callback = doc;
+ doc = conditions;
+ conditions = {};
+ options = null;
+ } else if (typeof conditions === 'function') {
+ // .update(callback)
+ callback = conditions;
+ conditions = undefined;
+ doc = undefined;
+ options = undefined;
+ } else if (typeof conditions === 'object' && !doc && !options && !callback) {
+ // .update(doc)
+ doc = conditions;
+ conditions = undefined;
+ options = undefined;
+ callback = undefined;
+ }
+
+ this.setOptions({ overwrite: true });
+ return _update(this, 'replaceOne', conditions, doc, options, callback);
+};
+
+/*!
+ * Internal helper for update, updateMany, updateOne, replaceOne
+ */
+
+function _update(query, op, filter, doc, options, callback) {
+ // make sure we don't send in the whole Document to merge()
+ query.op = op;
+ filter = utils.toObject(filter);
+ doc = doc || {};
+
+ const oldCb = callback;
+ if (oldCb) {
+ if (typeof oldCb === 'function') {
+ callback = function(error, result) {
+ oldCb(error, result ? result.result : {ok: 0, n: 0, nModified: 0});
+ };
+ } else {
+ throw new Error('Invalid callback() argument.');
+ }
+ }
+
+ // strict is an option used in the update checking, make sure it gets set
+ if (options) {
+ if ('strict' in options) {
+ query._mongooseOptions.strict = options.strict;
+ }
+ }
+
+ if (!(filter instanceof Query) &&
+ filter != null &&
+ filter.toString() !== '[object Object]') {
+ query.error(new ObjectParameterError(filter, 'filter', op));
+ } else {
+ query.merge(filter);
+ }
+
+ if (utils.isObject(options)) {
+ query.setOptions(options);
+ }
+
+ query._mergeUpdate(doc);
+
+ // Hooks
+ if (callback) {
+ if (op === 'update') {
+ query._execUpdate(callback);
+ return query;
+ }
+ query['_' + op](callback);
+ return query;
+ }
+
+ return Query.base[op].call(query, filter, doc, options, callback);
+}
+
+/**
+ * Runs a function `fn` and treats the return value of `fn` as the new value
+ * for the query to resolve to.
+ *
+ * Any functions you pass to `map()` will run **after** any post hooks.
+ *
+ * ####Example:
+ *
+ * const res = await MyModel.findOne().map(res => {
+ * // Sets a `loadedAt` property on the doc that tells you the time the
+ * // document was loaded.
+ * return res == null ?
+ * res :
+ * Object.assign(res, { loadedAt: new Date() });
+ * });
+ *
+ * @method map
+ * @memberOf Query
+ * @instance
+ * @param {Function} fn function to run to transform the query result
+ * @return {Query} this
+ */
+
+Query.prototype.map = function(fn) {
+ this._transforms.push(fn);
+ return this;
+};
+
+/**
+ * Make this query throw an error if no documents match the given `filter`.
+ * This is handy for integrating with async/await, because `orFail()` saves you
+ * an extra `if` statement to check if no document was found.
+ *
+ * ####Example:
+ *
+ * // Throws if no doc returned
+ * await Model.findOne({ foo: 'bar' }).orFail();
+ *
+ * // Throws if no document was updated
+ * await Model.updateOne({ foo: 'bar' }, { name: 'test' }).orFail();
+ *
+ * // Throws "No docs found!" error if no docs match `{ foo: 'bar' }`
+ * await Model.find({ foo: 'bar' }).orFail(new Error('No docs found!'));
+ *
+ * // Throws "Not found" error if no document was found
+ * await Model.findOneAndUpdate({ foo: 'bar' }, { name: 'test' }).
+ * orFail(() => Error('Not found'));
+ *
+ * @method orFail
+ * @memberOf Query
+ * @instance
+ * @param {Function|Error} [err] optional error to throw if no docs match `filter`
+ * @return {Query} this
+ */
+
+Query.prototype.orFail = function(err) {
+ this.map(res => {
+ switch (this.op) {
+ case 'find':
+ if (res.length === 0) {
+ err = typeof err === 'function' ? err.call(this) : err;
+ throw err;
+ }
+ break;
+ case 'findOne':
+ if (res == null) {
+ err = typeof err === 'function' ? err.call(this) : err;
+ throw err;
+ }
+ break;
+ case 'update':
+ case 'updateMany':
+ case 'updateOne':
+ if (get(res, 'result.nModified') === 0) {
+ err = typeof err === 'function' ? err.call(this) : err;
+ throw err;
+ }
+ break;
+ case 'findOneAndDelete':
+ if (get(res, 'lastErrorObject.n') === 0) {
+ err = typeof err === 'function' ? err.call(this) : err;
+ throw err;
+ }
+ break;
+ case 'findOneAndUpdate':
+ if (get(res, 'lastErrorObject.updatedExisting') === false) {
+ err = typeof err === 'function' ? err.call(this) : err;
+ throw err;
+ }
+ break;
+ case 'deleteMany':
+ case 'deleteOne':
+ case 'remove':
+ if (res.n === 0) {
+ err = typeof err === 'function' ? err.call(this) : err;
+ throw err;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return res;
+ });
+ return this;
+};
+
+/**
+ * Executes the query
+ *
+ * ####Examples:
+ *
+ * var promise = query.exec();
+ * var promise = query.exec('update');
+ *
+ * query.exec(callback);
+ * query.exec('find', callback);
+ *
+ * @param {String|Function} [operation]
+ * @param {Function} [callback] optional params depend on the function being called
+ * @return {Promise}
+ * @api public
+ */
+
+Query.prototype.exec = function exec(op, callback) {
+ const _this = this;
+
+ if (typeof op === 'function') {
+ callback = op;
+ op = null;
+ } else if (typeof op === 'string') {
+ this.op = op;
+ }
+
+ if (callback != null) {
+ callback = this.model.$wrapCallback(callback);
+ }
+
+ return utils.promiseOrCallback(callback, (cb) => {
+ if (!_this.op) {
+ cb();
+ return;
+ }
+
+ this._hooks.execPre('exec', this, [], (error) => {
+ if (error) {
+ return cb(error);
+ }
+ this[this.op].call(this, (error, res) => {
+ if (error) {
+ return cb(error);
+ }
+
+ this._hooks.execPost('exec', this, [], {}, (error) => {
+ if (error) {
+ return cb(error);
+ }
+
+ cb(null, res);
+ });
+ });
+ });
+ }, this.model.events);
+};
+
+/*!
+ * ignore
+ */
+
+function _wrapThunkCallback(query, cb) {
+ return function(error, res) {
+ if (error != null) {
+ return cb(error);
+ }
+
+ for (const fn of query._transforms) {
+ try {
+ res = fn(res);
+ } catch (error) {
+ return cb(error);
+ }
+ }
+
+ return cb(null, res);
+ };
+}
+
+/**
+ * Executes the query returning a `Promise` which will be
+ * resolved with either the doc(s) or rejected with the error.
+ *
+ * @param {Function} [resolve]
+ * @param {Function} [reject]
+ * @return {Promise}
+ * @api public
+ */
+
+Query.prototype.then = function(resolve, reject) {
+ return this.exec().then(resolve, reject);
+};
+
+/**
+ * Executes the query returning a `Promise` which will be
+ * resolved with either the doc(s) or rejected with the error.
+ * Like `.then()`, but only takes a rejection handler.
+ *
+ * @param {Function} [reject]
+ * @return {Promise}
+ * @api public
+ */
+
+Query.prototype.catch = function(reject) {
+ return this.exec().then(null, reject);
+};
+
+/*!
+ * ignore
+ */
+
+Query.prototype._pre = function(fn) {
+ this._hooks.pre('exec', fn);
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+Query.prototype._post = function(fn) {
+ this._hooks.post('exec', fn);
+ return this;
+};
+
+/*!
+ * Casts obj for an update command.
+ *
+ * @param {Object} obj
+ * @return {Object} obj after casting its values
+ * @api private
+ */
+
+Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
+ let strict;
+ if ('strict' in this._mongooseOptions) {
+ strict = this._mongooseOptions.strict;
+ } else if (this.schema && this.schema.options) {
+ strict = this.schema.options.strict;
+ } else {
+ strict = true;
+ }
+
+ let omitUndefined = false;
+ if ('omitUndefined' in this._mongooseOptions) {
+ omitUndefined = this._mongooseOptions.omitUndefined;
+ }
+
+ let useNestedStrict;
+ if ('useNestedStrict' in this.options) {
+ useNestedStrict = this.options.useNestedStrict;
+ }
+
+ return castUpdate(this.schema, obj, {
+ overwrite: overwrite,
+ strict: strict,
+ omitUndefined,
+ useNestedStrict: useNestedStrict
+ }, this, this._conditions);
+};
+
+/*!
+ * castQuery
+ * @api private
+ */
+
+function castQuery(query) {
+ try {
+ return query.cast(query.model);
+ } catch (err) {
+ return err;
+ }
+}
+
+/*!
+ * castDoc
+ * @api private
+ */
+
+function castDoc(query, overwrite) {
+ try {
+ return query._castUpdate(query._update, overwrite);
+ } catch (err) {
+ return err;
+ }
+}
+
+/**
+ * Specifies paths which should be populated with other documents.
+ *
+ * ####Example:
+ *
+ * Kitten.findOne().populate('owner').exec(function (err, kitten) {
+ * console.log(kitten.owner.name) // Max
+ * })
+ *
+ * Kitten.find().populate({
+ * path: 'owner'
+ * , select: 'name'
+ * , match: { color: 'black' }
+ * , options: { sort: { name: -1 }}
+ * }).exec(function (err, kittens) {
+ * console.log(kittens[0].owner.name) // Zoopa
+ * })
+ *
+ * // alternatively
+ * Kitten.find().populate('owner', 'name', null, {sort: { name: -1 }}).exec(function (err, kittens) {
+ * console.log(kittens[0].owner.name) // Zoopa
+ * })
+ *
+ * Paths are populated after the query executes and a response is received. A separate query is then executed for each path specified for population. After a response for each query has also been returned, the results are passed to the callback.
+ *
+ * @param {Object|String} path either the path to populate or an object specifying all parameters
+ * @param {Object|String} [select] Field selection for the population query
+ * @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field.
+ * @param {Object} [match] Conditions for the population query
+ * @param {Object} [options] Options for the population query (sort, etc)
+ * @see population ./populate.html
+ * @see Query#select #query_Query-select
+ * @see Model.populate #model_Model.populate
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.populate = function() {
+ if (arguments.length === 0) {
+ return this;
+ }
+
+ const res = utils.populate.apply(null, arguments);
+
+ // Propagate readConcern and readPreference and lean from parent query,
+ // unless one already specified
+ if (this.options != null) {
+ const readConcern = this.options.readConcern;
+ const readPref = this.options.readPreference;
+
+ for (let i = 0; i < res.length; ++i) {
+ if (readConcern != null && get(res[i], 'options.readConcern') == null) {
+ res[i].options = res[i].options || {};
+ res[i].options.readConcern = readConcern;
+ }
+ if (readPref != null && get(res[i], 'options.readPreference') == null) {
+ res[i].options = res[i].options || {};
+ res[i].options.readPreference = readPref;
+ }
+ }
+ }
+
+ const opts = this._mongooseOptions;
+
+ if (opts.lean != null) {
+ const lean = opts.lean;
+ for (let i = 0; i < res.length; ++i) {
+ if (get(res[i], 'options.lean') == null) {
+ res[i].options = res[i].options || {};
+ res[i].options.lean = lean;
+ }
+ }
+ }
+
+ if (!utils.isObject(opts.populate)) {
+ opts.populate = {};
+ }
+
+ const pop = opts.populate;
+
+ for (let i = 0; i < res.length; ++i) {
+ const path = res[i].path;
+ if (pop[path] && pop[path].populate && res[i].populate) {
+ res[i].populate = pop[path].populate.concat(res[i].populate);
+ }
+ pop[res[i].path] = res[i];
+ }
+
+ return this;
+};
+
+/**
+ * Gets a list of paths to be populated by this query
+ *
+ * ####Example:
+ * bookSchema.pre('findOne', function() {
+ * let keys = this.getPopulatedPaths(); // ['author']
+ * })
+ * ...
+ * Book.findOne({}).populate('author')
+ *
+ * @return {Array} an array of strings representing populated paths
+ * @api public
+ */
+
+Query.prototype.getPopulatedPaths = function getPopulatedPaths() {
+ const obj = this._mongooseOptions.populate || {};
+ return Object.keys(obj);
+};
+
+/**
+ * Casts this query to the schema of `model`
+ *
+ * ####Note
+ *
+ * If `obj` is present, it is cast instead of this query.
+ *
+ * @param {Model} [model] the model to cast to. If not set, defaults to `this.model`
+ * @param {Object} [obj]
+ * @return {Object}
+ * @api public
+ */
+
+Query.prototype.cast = function(model, obj) {
+ obj || (obj = this._conditions);
+
+ model = model || this.model;
+
+ try {
+ return cast(model.schema, obj, {
+ upsert: this.options && this.options.upsert,
+ strict: (this.options && 'strict' in this.options) ?
+ this.options.strict :
+ get(model, 'schema.options.strict', null),
+ strictQuery: (this.options && this.options.strictQuery) ||
+ get(model, 'schema.options.strictQuery', null)
+ }, this);
+ } catch (err) {
+ // CastError, assign model
+ if (typeof err.setModel === 'function') {
+ err.setModel(model);
+ }
+ throw err;
+ }
+};
+
+/**
+ * Casts selected field arguments for field selection with mongo 2.2
+ *
+ * query.select({ ids: { $elemMatch: { $in: [hexString] }})
+ *
+ * @param {Object} fields
+ * @see https://github.com/Automattic/mongoose/issues/1091
+ * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
+ * @api private
+ */
+
+Query.prototype._castFields = function _castFields(fields) {
+ let selected,
+ elemMatchKeys,
+ keys,
+ key,
+ out,
+ i;
+
+ if (fields) {
+ keys = Object.keys(fields);
+ elemMatchKeys = [];
+ i = keys.length;
+
+ // collect $elemMatch args
+ while (i--) {
+ key = keys[i];
+ if (fields[key].$elemMatch) {
+ selected || (selected = {});
+ selected[key] = fields[key];
+ elemMatchKeys.push(key);
+ }
+ }
+ }
+
+ if (selected) {
+ // they passed $elemMatch, cast em
+ try {
+ out = this.cast(this.model, selected);
+ } catch (err) {
+ return err;
+ }
+
+ // apply the casted field args
+ i = elemMatchKeys.length;
+ while (i--) {
+ key = elemMatchKeys[i];
+ fields[key] = out[key];
+ }
+ }
+
+ return fields;
+};
+
+/**
+ * Applies schematype selected options to this query.
+ * @api private
+ */
+
+Query.prototype._applyPaths = function applyPaths() {
+ this._fields = this._fields || {};
+ helpers.applyPaths(this._fields, this.model.schema);
+
+ let _selectPopulatedPaths = true;
+
+ if ('selectPopulatedPaths' in this.model.base.options) {
+ _selectPopulatedPaths = this.model.base.options.selectPopulatedPaths;
+ }
+ if ('selectPopulatedPaths' in this.model.schema.options) {
+ _selectPopulatedPaths = this.model.schema.options.selectPopulatedPaths;
+ }
+
+ if (_selectPopulatedPaths) {
+ selectPopulatedFields(this);
+ }
+};
+
+/**
+ * Returns a wrapper around a [mongodb driver cursor](http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html).
+ * A QueryCursor exposes a Streams3 interface, as well as a `.next()` function.
+ *
+ * The `.cursor()` function triggers pre find hooks, but **not** post find hooks.
+ *
+ * ####Example
+ *
+ * // There are 2 ways to use a cursor. First, as a stream:
+ * Thing.
+ * find({ name: /^hello/ }).
+ * cursor().
+ * on('data', function(doc) { console.log(doc); }).
+ * on('end', function() { console.log('Done!'); });
+ *
+ * // Or you can use `.next()` to manually get the next doc in the stream.
+ * // `.next()` returns a promise, so you can use promises or callbacks.
+ * var cursor = Thing.find({ name: /^hello/ }).cursor();
+ * cursor.next(function(error, doc) {
+ * console.log(doc);
+ * });
+ *
+ * // Because `.next()` returns a promise, you can use co
+ * // to easily iterate through all documents without loading them
+ * // all into memory.
+ * co(function*() {
+ * const cursor = Thing.find({ name: /^hello/ }).cursor();
+ * for (let doc = yield cursor.next(); doc != null; doc = yield cursor.next()) {
+ * console.log(doc);
+ * }
+ * });
+ *
+ * ####Valid options
+ *
+ * - `transform`: optional function which accepts a mongoose document. The return value of the function will be emitted on `data` and returned by `.next()`.
+ *
+ * @return {QueryCursor}
+ * @param {Object} [options]
+ * @see QueryCursor
+ * @api public
+ */
+
+Query.prototype.cursor = function cursor(opts) {
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+ this.setOptions({ projection: this._fieldsForExec() });
+ if (opts) {
+ this.setOptions(opts);
+ }
+
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ return (new QueryCursor(this, this.options))._markError(err);
+ }
+
+ return new QueryCursor(this, this.options);
+};
+
+// the rest of these are basically to support older Mongoose syntax with mquery
+
+/**
+ * _DEPRECATED_ Alias of `maxScan`
+ *
+ * @deprecated
+ * @see maxScan #query_Query-maxScan
+ * @method maxscan
+ * @memberOf Query
+ * @instance
+ */
+
+Query.prototype.maxscan = Query.base.maxScan;
+
+/**
+ * Sets the tailable option (for use with capped collections).
+ *
+ * ####Example
+ *
+ * query.tailable() // true
+ * query.tailable(true)
+ * query.tailable(false)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @param {Boolean} bool defaults to true
+ * @param {Object} [opts] options to set
+ * @param {Number} [opts.numberOfRetries] if cursor is exhausted, retry this many times before giving up
+ * @param {Number} [opts.tailableRetryInterval] if cursor is exhausted, wait this many milliseconds before retrying
+ * @see tailable http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/
+ * @api public
+ */
+
+Query.prototype.tailable = function(val, opts) {
+ // we need to support the tailable({ awaitdata : true }) as well as the
+ // tailable(true, {awaitdata :true}) syntax that mquery does not support
+ if (val && val.constructor.name === 'Object') {
+ opts = val;
+ val = true;
+ }
+
+ if (val === undefined) {
+ val = true;
+ }
+
+ if (opts && typeof opts === 'object') {
+ for (const key in opts) {
+ if (key === 'awaitdata') {
+ // For backwards compatibility
+ this.options[key] = !!opts[key];
+ } else {
+ this.options[key] = opts[key];
+ }
+ }
+ }
+
+ return Query.base.tailable.call(this, val);
+};
+
+/**
+ * Declares an intersects query for `geometry()`.
+ *
+ * ####Example
+ *
+ * query.where('path').intersects().geometry({
+ * type: 'LineString'
+ * , coordinates: [[180.0, 11.0], [180, 9.0]]
+ * })
+ *
+ * query.where('path').intersects({
+ * type: 'LineString'
+ * , coordinates: [[180.0, 11.0], [180, 9.0]]
+ * })
+ *
+ * ####NOTE:
+ *
+ * **MUST** be used after `where()`.
+ *
+ * ####NOTE:
+ *
+ * In Mongoose 3.7, `intersects` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
+ *
+ * @method intersects
+ * @memberOf Query
+ * @instance
+ * @param {Object} [arg]
+ * @return {Query} this
+ * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
+ * @see geoIntersects http://docs.mongodb.org/manual/reference/operator/geoIntersects/
+ * @api public
+ */
+
+/**
+ * Specifies a `$geometry` condition
+ *
+ * ####Example
+ *
+ * var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
+ * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })
+ *
+ * // or
+ * var polyB = [[ 0, 0 ], [ 1, 1 ]]
+ * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })
+ *
+ * // or
+ * var polyC = [ 0, 0 ]
+ * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })
+ *
+ * // or
+ * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })
+ *
+ * The argument is assigned to the most recent path passed to `where()`.
+ *
+ * ####NOTE:
+ *
+ * `geometry()` **must** come after either `intersects()` or `within()`.
+ *
+ * The `object` argument must contain `type` and `coordinates` properties.
+ * - type {String}
+ * - coordinates {Array}
+ *
+ * @method geometry
+ * @memberOf Query
+ * @instance
+ * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples.
+ * @return {Query} this
+ * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
+ * @see http://docs.mongodb.org/manual/release-notes/2.4/#new-geospatial-indexes-with-geojson-and-improved-spherical-geometry
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/**
+ * Specifies a `$near` or `$nearSphere` condition
+ *
+ * These operators return documents sorted by distance.
+ *
+ * ####Example
+ *
+ * query.where('loc').near({ center: [10, 10] });
+ * query.where('loc').near({ center: [10, 10], maxDistance: 5 });
+ * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true });
+ * query.near('loc', { center: [10, 10], maxDistance: 5 });
+ *
+ * @method near
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Object} val
+ * @return {Query} this
+ * @see $near http://docs.mongodb.org/manual/reference/operator/near/
+ * @see $nearSphere http://docs.mongodb.org/manual/reference/operator/nearSphere/
+ * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/*!
+ * Overwriting mquery is needed to support a couple different near() forms found in older
+ * versions of mongoose
+ * near([1,1])
+ * near(1,1)
+ * near(field, [1,2])
+ * near(field, 1, 2)
+ * In addition to all of the normal forms supported by mquery
+ */
+
+Query.prototype.near = function() {
+ const params = [];
+ const sphere = this._mongooseOptions.nearSphere;
+
+ // TODO refactor
+
+ if (arguments.length === 1) {
+ if (Array.isArray(arguments[0])) {
+ params.push({center: arguments[0], spherical: sphere});
+ } else if (typeof arguments[0] === 'string') {
+ // just passing a path
+ params.push(arguments[0]);
+ } else if (utils.isObject(arguments[0])) {
+ if (typeof arguments[0].spherical !== 'boolean') {
+ arguments[0].spherical = sphere;
+ }
+ params.push(arguments[0]);
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else if (arguments.length === 2) {
+ if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
+ params.push({center: [arguments[0], arguments[1]], spherical: sphere});
+ } else if (typeof arguments[0] === 'string' && Array.isArray(arguments[1])) {
+ params.push(arguments[0]);
+ params.push({center: arguments[1], spherical: sphere});
+ } else if (typeof arguments[0] === 'string' && utils.isObject(arguments[1])) {
+ params.push(arguments[0]);
+ if (typeof arguments[1].spherical !== 'boolean') {
+ arguments[1].spherical = sphere;
+ }
+ params.push(arguments[1]);
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else if (arguments.length === 3) {
+ if (typeof arguments[0] === 'string' && typeof arguments[1] === 'number'
+ && typeof arguments[2] === 'number') {
+ params.push(arguments[0]);
+ params.push({center: [arguments[1], arguments[2]], spherical: sphere});
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else {
+ throw new TypeError('invalid argument');
+ }
+
+ return Query.base.near.apply(this, params);
+};
+
+/**
+ * _DEPRECATED_ Specifies a `$nearSphere` condition
+ *
+ * ####Example
+ *
+ * query.where('loc').nearSphere({ center: [10, 10], maxDistance: 5 });
+ *
+ * **Deprecated.** Use `query.near()` instead with the `spherical` option set to `true`.
+ *
+ * ####Example
+ *
+ * query.where('loc').near({ center: [10, 10], spherical: true });
+ *
+ * @deprecated
+ * @see near() #query_Query-near
+ * @see $near http://docs.mongodb.org/manual/reference/operator/near/
+ * @see $nearSphere http://docs.mongodb.org/manual/reference/operator/nearSphere/
+ * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
+ */
+
+Query.prototype.nearSphere = function() {
+ this._mongooseOptions.nearSphere = true;
+ this.near.apply(this, arguments);
+ return this;
+};
+
+/**
+ * Returns an asyncIterator for use with [`for/await/of` loops](http://bit.ly/async-iterators)
+ * This function *only* works for `find()` queries.
+ * You do not need to call this function explicitly, the JavaScript runtime
+ * will call it for you.
+ *
+ * ####Example
+ *
+ * for await (const doc of Model.aggregate([{ $sort: { name: 1 } }])) {
+ * console.log(doc.name);
+ * }
+ *
+ * Node.js 10.x supports async iterators natively without any flags. You can
+ * enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
+ *
+ * **Note:** This function is not if `Symbol.asyncIterator` is undefined. If
+ * `Symbol.asyncIterator` is undefined, that means your Node.js version does not
+ * support async iterators.
+ *
+ * @method Symbol.asyncIterator
+ * @memberOf Query
+ * @instance
+ * @api public
+ */
+
+if (Symbol.asyncIterator != null) {
+ Query.prototype[Symbol.asyncIterator] = function() {
+ return this.cursor().transformNull().map(doc => {
+ return doc == null ? { done: true } : { value: doc, done: false };
+ });
+ };
+}
+
+/**
+ * Specifies a $polygon condition
+ *
+ * ####Example
+ *
+ * query.where('loc').within().polygon([10,20], [13, 25], [7,15])
+ * query.polygon('loc', [10,20], [13, 25], [7,15])
+ *
+ * @method polygon
+ * @memberOf Query
+ * @instance
+ * @param {String|Array} [path]
+ * @param {Array|Object} [coordinatePairs...]
+ * @return {Query} this
+ * @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/**
+ * Specifies a $box condition
+ *
+ * ####Example
+ *
+ * var lowerLeft = [40.73083, -73.99756]
+ * var upperRight= [40.741404, -73.988135]
+ *
+ * query.where('loc').within().box(lowerLeft, upperRight)
+ * query.box({ ll : lowerLeft, ur : upperRight })
+ *
+ * @method box
+ * @memberOf Query
+ * @instance
+ * @see $box http://docs.mongodb.org/manual/reference/operator/box/
+ * @see within() Query#within #query_Query-within
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @param {Object} val
+ * @param [Array] Upper Right Coords
+ * @return {Query} this
+ * @api public
+ */
+
+/*!
+ * this is needed to support the mongoose syntax of:
+ * box(field, { ll : [x,y], ur : [x2,y2] })
+ * box({ ll : [x,y], ur : [x2,y2] })
+ */
+
+Query.prototype.box = function(ll, ur) {
+ if (!Array.isArray(ll) && utils.isObject(ll)) {
+ ur = ll.ur;
+ ll = ll.ll;
+ }
+ return Query.base.box.call(this, ll, ur);
+};
+
+/**
+ * Specifies a $center or $centerSphere condition.
+ *
+ * ####Example
+ *
+ * var area = { center: [50, 50], radius: 10, unique: true }
+ * query.where('loc').within().circle(area)
+ * // alternatively
+ * query.circle('loc', area);
+ *
+ * // spherical calculations
+ * var area = { center: [50, 50], radius: 10, unique: true, spherical: true }
+ * query.where('loc').within().circle(area)
+ * // alternatively
+ * query.circle('loc', area);
+ *
+ * New in 3.7.0
+ *
+ * @method circle
+ * @memberOf Query
+ * @instance
+ * @param {String} [path]
+ * @param {Object} area
+ * @return {Query} this
+ * @see $center http://docs.mongodb.org/manual/reference/operator/center/
+ * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
+ * @see $geoWithin http://docs.mongodb.org/manual/reference/operator/geoWithin/
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/**
+ * _DEPRECATED_ Alias for [circle](#query_Query-circle)
+ *
+ * **Deprecated.** Use [circle](#query_Query-circle) instead.
+ *
+ * @deprecated
+ * @method center
+ * @memberOf Query
+ * @instance
+ * @api public
+ */
+
+Query.prototype.center = Query.base.circle;
+
+/**
+ * _DEPRECATED_ Specifies a $centerSphere condition
+ *
+ * **Deprecated.** Use [circle](#query_Query-circle) instead.
+ *
+ * ####Example
+ *
+ * var area = { center: [50, 50], radius: 10 };
+ * query.where('loc').within().centerSphere(area);
+ *
+ * @deprecated
+ * @param {String} [path]
+ * @param {Object} val
+ * @return {Query} this
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
+ * @api public
+ */
+
+Query.prototype.centerSphere = function() {
+ if (arguments[0] && arguments[0].constructor.name === 'Object') {
+ arguments[0].spherical = true;
+ }
+
+ if (arguments[1] && arguments[1].constructor.name === 'Object') {
+ arguments[1].spherical = true;
+ }
+
+ Query.base.circle.apply(this, arguments);
+};
+
+/**
+ * Determines if field selection has been made.
+ *
+ * @method selected
+ * @memberOf Query
+ * @instance
+ * @return {Boolean}
+ * @api public
+ */
+
+/**
+ * Determines if inclusive field selection has been made.
+ *
+ * query.selectedInclusively() // false
+ * query.select('name')
+ * query.selectedInclusively() // true
+ *
+ * @method selectedInclusively
+ * @memberOf Query
+ * @instance
+ * @return {Boolean}
+ * @api public
+ */
+
+Query.prototype.selectedInclusively = function selectedInclusively() {
+ return isInclusive(this._fields);
+};
+
+/**
+ * Determines if exclusive field selection has been made.
+ *
+ * query.selectedExclusively() // false
+ * query.select('-name')
+ * query.selectedExclusively() // true
+ * query.selectedInclusively() // false
+ *
+ * @method selectedExclusively
+ * @memberOf Query
+ * @instance
+ * @return {Boolean}
+ * @api public
+ */
+
+Query.prototype.selectedExclusively = function selectedExclusively() {
+ if (!this._fields) {
+ return false;
+ }
+
+ const keys = Object.keys(this._fields);
+ if (keys.length === 0) {
+ return false;
+ }
+
+ for (let i = 0; i < keys.length; ++i) {
+ const key = keys[i];
+ if (key === '_id') {
+ continue;
+ }
+ if (this._fields[key] === 0 || this._fields[key] === false) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/*!
+ * Export
+ */
+
+module.exports = Query;
diff --git a/node_modules/mongoose/lib/queryhelpers.js b/node_modules/mongoose/lib/queryhelpers.js
new file mode 100644
index 0000000..e5aa74f
--- /dev/null
+++ b/node_modules/mongoose/lib/queryhelpers.js
@@ -0,0 +1,306 @@
+'use strict';
+
+/*!
+ * Module dependencies
+ */
+
+const get = require('./helpers/get');
+const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
+const utils = require('./utils');
+
+/*!
+ * Prepare a set of path options for query population.
+ *
+ * @param {Query} query
+ * @param {Object} options
+ * @return {Array}
+ */
+
+exports.preparePopulationOptions = function preparePopulationOptions(query, options) {
+ const pop = utils.object.vals(query.options.populate);
+
+ // lean options should trickle through all queries
+ if (options.lean != null) {
+ pop.
+ filter(p => get(p, 'options.lean') == null).
+ forEach(makeLean(options.lean));
+ }
+
+ return pop;
+};
+
+/*!
+ * Prepare a set of path options for query population. This is the MongooseQuery
+ * version
+ *
+ * @param {Query} query
+ * @param {Object} options
+ * @return {Array}
+ */
+
+exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query, options) {
+ const pop = utils.object.vals(query._mongooseOptions.populate);
+
+ // lean options should trickle through all queries
+ if (options.lean != null) {
+ pop.
+ filter(p => get(p, 'options.lean') == null).
+ forEach(makeLean(options.lean));
+ }
+
+ const session = get(query, 'options.session', null);
+ if (session != null) {
+ pop.forEach(path => {
+ if (path.options == null) {
+ path.options = { session: session };
+ return;
+ }
+ if (!('session' in path.options)) {
+ path.options.session = session;
+ }
+ });
+ }
+
+ const projection = query._fieldsForExec();
+ pop.forEach(p => {
+ p._queryProjection = projection;
+ });
+
+ return pop;
+};
+
+
+/*!
+ * returns discriminator by discriminatorMapping.value
+ *
+ * @param {Model} model
+ * @param {string} value
+ */
+function getDiscriminatorByValue(model, value) {
+ let discriminator = null;
+ if (!model.discriminators) {
+ return discriminator;
+ }
+ for (const name in model.discriminators) {
+ const it = model.discriminators[name];
+ if (
+ it.schema &&
+ it.schema.discriminatorMapping &&
+ it.schema.discriminatorMapping.value == value
+ ) {
+ discriminator = it;
+ break;
+ }
+ }
+ return discriminator;
+}
+
+exports.getDiscriminatorByValue = getDiscriminatorByValue;
+
+/*!
+ * If the document is a mapped discriminator type, it returns a model instance for that type, otherwise,
+ * it returns an instance of the given model.
+ *
+ * @param {Model} model
+ * @param {Object} doc
+ * @param {Object} fields
+ *
+ * @return {Model}
+ */
+exports.createModel = function createModel(model, doc, fields, userProvidedFields) {
+ model.hooks.execPreSync('createModel', doc);
+ const discriminatorMapping = model.schema ?
+ model.schema.discriminatorMapping :
+ null;
+
+ const key = discriminatorMapping && discriminatorMapping.isRoot ?
+ discriminatorMapping.key :
+ null;
+
+ const value = doc[key];
+ if (key && value && model.discriminators) {
+ const discriminator = model.discriminators[value] || getDiscriminatorByValue(model, value);
+ if (discriminator) {
+ const _fields = utils.clone(userProvidedFields);
+ exports.applyPaths(_fields, discriminator.schema);
+ return new discriminator(undefined, _fields, true);
+ }
+ }
+
+ return new model(undefined, fields, {
+ skipId: true,
+ isNew: false,
+ willInit: true
+ });
+};
+
+/*!
+ * ignore
+ */
+
+exports.applyPaths = function applyPaths(fields, schema) {
+ // determine if query is selecting or excluding fields
+ let exclude;
+ let keys;
+ let ki;
+ let field;
+
+ if (fields) {
+ keys = Object.keys(fields);
+ ki = keys.length;
+
+ while (ki--) {
+ if (keys[ki][0] === '+') {
+ continue;
+ }
+ field = fields[keys[ki]];
+ // Skip `$meta` and `$slice`
+ if (!isDefiningProjection(field)) {
+ continue;
+ }
+ exclude = field === 0;
+ break;
+ }
+ }
+
+ // if selecting, apply default schematype select:true fields
+ // if excluding, apply schematype select:false fields
+
+ const selected = [];
+ const excluded = [];
+ const stack = [];
+
+ const analyzePath = function(path, type) {
+ const plusPath = '+' + path;
+ const hasPlusPath = fields && plusPath in fields;
+ if (hasPlusPath) {
+ // forced inclusion
+ delete fields[plusPath];
+ }
+
+ if (typeof type.selected !== 'boolean') return;
+
+ if (hasPlusPath) {
+ // forced inclusion
+ delete fields[plusPath];
+
+ // if there are other fields being included, add this one
+ // if no other included fields, leave this out (implied inclusion)
+ if (exclude === false && keys.length > 1 && !~keys.indexOf(path)) {
+ fields[path] = 1;
+ }
+
+ return;
+ }
+
+ // check for parent exclusions
+ const pieces = path.split('.');
+ const root = pieces[0];
+ if (~excluded.indexOf(root)) {
+ return;
+ }
+
+ // Special case: if user has included a parent path of a discriminator key,
+ // don't explicitly project in the discriminator key because that will
+ // project out everything else under the parent path
+ if (!exclude && get(type, 'options.$skipDiscriminatorCheck', false)) {
+ let cur = '';
+ for (let i = 0; i < pieces.length; ++i) {
+ cur += (cur.length === 0 ? '' : '.') + pieces[i];
+ const projection = get(fields, cur, false);
+ if (projection && typeof projection !== 'object') {
+ return;
+ }
+ }
+ }
+
+ (type.selected ? selected : excluded).push(path);
+ };
+
+ analyzeSchema(schema);
+
+ switch (exclude) {
+ case true:
+ for (let i = 0; i < excluded.length; ++i) {
+ fields[excluded[i]] = 0;
+ }
+ break;
+ case false:
+ if (schema &&
+ schema.paths['_id'] &&
+ schema.paths['_id'].options &&
+ schema.paths['_id'].options.select === false) {
+ fields._id = 0;
+ }
+ for (let i = 0; i < selected.length; ++i) {
+ fields[selected[i]] = 1;
+ }
+ break;
+ case undefined:
+ if (fields == null) {
+ break;
+ }
+ // Any leftover plus paths must in the schema, so delete them (gh-7017)
+ for (const key of Object.keys(fields || {})) {
+ if (key.charAt(0) === '+') {
+ delete fields[key];
+ }
+ }
+
+ // user didn't specify fields, implies returning all fields.
+ // only need to apply excluded fields and delete any plus paths
+ for (let i = 0; i < excluded.length; ++i) {
+ fields[excluded[i]] = 0;
+ }
+ break;
+ }
+
+ function analyzeSchema(schema, prefix) {
+ prefix || (prefix = '');
+
+ // avoid recursion
+ if (stack.indexOf(schema) !== -1) {
+ return;
+ }
+ stack.push(schema);
+
+ schema.eachPath(function(path, type) {
+ if (prefix) path = prefix + '.' + path;
+
+ analyzePath(path, type);
+
+ // array of subdocs?
+ if (type.schema) {
+ analyzeSchema(type.schema, path);
+ }
+ });
+
+ stack.pop();
+ }
+};
+
+/*!
+ * Set each path query option to lean
+ *
+ * @param {Object} option
+ */
+
+function makeLean(val) {
+ return function(option) {
+ option.options || (option.options = {});
+ option.options.lean = val;
+ };
+}
+
+/*!
+ * Handle the `WriteOpResult` from the server
+ */
+
+exports.handleWriteOpResult = function handleWriteOpResult(callback) {
+ return function _handleWriteOpResult(error, res) {
+ if (error) {
+ return callback(error);
+ }
+ return callback(null, res.result);
+ };
+};
diff --git a/node_modules/mongoose/lib/schema.js b/node_modules/mongoose/lib/schema.js
new file mode 100644
index 0000000..4adc3cd
--- /dev/null
+++ b/node_modules/mongoose/lib/schema.js
@@ -0,0 +1,1805 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const EventEmitter = require('events').EventEmitter;
+const Kareem = require('kareem');
+const SchemaType = require('./schematype');
+const VirtualType = require('./virtualtype');
+const applyTimestampsToChildren = require('./helpers/update/applyTimestampsToChildren');
+const applyTimestampsToUpdate = require('./helpers/update/applyTimestampsToUpdate');
+const get = require('./helpers/get');
+const getIndexes = require('./helpers/schema/getIndexes');
+const handleTimestampOption = require('./helpers/schema/handleTimestampOption');
+const merge = require('./helpers/schema/merge');
+const mpath = require('mpath');
+const readPref = require('./driver').get().ReadPreference;
+const symbols = require('./schema/symbols');
+const util = require('util');
+const utils = require('./utils');
+const validateRef = require('./helpers/populate/validateRef');
+
+let MongooseTypes;
+
+const allMiddleware = require('./helpers/query/applyQueryMiddleware').
+ middlewareFunctions.
+ concat(require('./helpers/model/applyHooks').middlewareFunctions);
+
+let id = 0;
+
+/**
+ * Schema constructor.
+ *
+ * ####Example:
+ *
+ * var child = new Schema({ name: String });
+ * var schema = new Schema({ name: String, age: Number, children: [child] });
+ * var Tree = mongoose.model('Tree', schema);
+ *
+ * // setting schema options
+ * new Schema({ name: String }, { _id: false, autoIndex: false })
+ *
+ * ####Options:
+ *
+ * - [autoIndex](/docs/guide.html#autoIndex): bool - defaults to null (which means use the connection's autoIndex option)
+ * - [autoCreate](/docs/guide.html#autoCreate): bool - defaults to null (which means use the connection's autoCreate option)
+ * - [bufferCommands](/docs/guide.html#bufferCommands): bool - defaults to true
+ * - [capped](/docs/guide.html#capped): bool - defaults to false
+ * - [collection](/docs/guide.html#collection): string - no default
+ * - [id](/docs/guide.html#id): bool - defaults to true
+ * - [_id](/docs/guide.html#_id): bool - defaults to true
+ * - `minimize`: bool - controls [document#toObject](#document_Document-toObject) behavior when called manually - defaults to true
+ * - [read](/docs/guide.html#read): string
+ * - [writeConcern](/docs/guide.html#writeConcern): object - defaults to null, use to override [the MongoDB server's default write concern settings](https://docs.mongodb.com/manual/reference/write-concern/)
+ * - [shardKey](/docs/guide.html#shardKey): bool - defaults to `null`
+ * - [strict](/docs/guide.html#strict): bool - defaults to true
+ * - [toJSON](/docs/guide.html#toJSON) - object - no default
+ * - [toObject](/docs/guide.html#toObject) - object - no default
+ * - [typeKey](/docs/guide.html#typeKey) - string - defaults to 'type'
+ * - [useNestedStrict](/docs/guide.html#useNestedStrict) - boolean - defaults to false
+ * - [validateBeforeSave](/docs/guide.html#validateBeforeSave) - bool - defaults to `true`
+ * - [versionKey](/docs/guide.html#versionKey): string - defaults to "__v"
+ * - [collation](/docs/guide.html#collation): object - defaults to null (which means use no collation)
+ * - [selectPopulatedPaths](/docs/guide.html#selectPopulatedPaths): boolean - defaults to `true`
+ *
+ * ####Note:
+ *
+ * _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into its parent._
+ *
+ * @param {Object|Schema|Array} [definition] Can be one of: object describing schema paths, or schema to copy, or array of objects and schemas
+ * @param {Object} [options]
+ * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
+ * @event `init`: Emitted after the schema is compiled into a `Model`.
+ * @api public
+ */
+
+function Schema(obj, options) {
+ if (!(this instanceof Schema)) {
+ return new Schema(obj, options);
+ }
+
+ this.obj = obj;
+ this.paths = {};
+ this.aliases = {};
+ this.subpaths = {};
+ this.virtuals = {};
+ this.singleNestedPaths = {};
+ this.nested = {};
+ this.inherits = {};
+ this.callQueue = [];
+ this._indexes = [];
+ this.methods = {};
+ this.methodOptions = {};
+ this.statics = {};
+ this.tree = {};
+ this.query = {};
+ this.childSchemas = [];
+ this.plugins = [];
+ // For internal debugging. Do not use this to try to save a schema in MDB.
+ this.$id = ++id;
+
+ this.s = {
+ hooks: new Kareem()
+ };
+
+ this.options = this.defaultOptions(options);
+
+ // build paths
+ if (Array.isArray(obj)) {
+ for (const definition of obj) {
+ this.add(definition);
+ }
+ } else if (obj) {
+ this.add(obj);
+ }
+
+ // check if _id's value is a subdocument (gh-2276)
+ const _idSubDoc = obj && obj._id && utils.isObject(obj._id);
+
+ // ensure the documents get an auto _id unless disabled
+ const auto_id = !this.paths['_id'] &&
+ (!this.options.noId && this.options._id) && !_idSubDoc;
+
+ if (auto_id) {
+ const _obj = {_id: {auto: true}};
+ _obj._id[this.options.typeKey] = Schema.ObjectId;
+ this.add(_obj);
+ }
+
+ this.setupTimestamp(this.options.timestamps);
+}
+
+/*!
+ * Create virtual properties with alias field
+ */
+function aliasFields(schema, paths) {
+ paths = paths || Object.keys(schema.paths);
+ for (const path of paths) {
+ const options = get(schema.paths[path], 'options');
+ if (options == null) {
+ continue;
+ }
+
+ const prop = schema.paths[path].path;
+ const alias = options.alias;
+
+ if (!alias) {
+ continue;
+ }
+
+ if (typeof alias !== 'string') {
+ throw new Error('Invalid value for alias option on ' + prop + ', got ' + alias);
+ }
+
+ schema.aliases[alias] = prop;
+
+ schema.
+ virtual(alias).
+ get((function(p) {
+ return function() {
+ if (typeof this.get === 'function') {
+ return this.get(p);
+ }
+ return this[p];
+ };
+ })(prop)).
+ set((function(p) {
+ return function(v) {
+ return this.set(p, v);
+ };
+ })(prop));
+ }
+}
+
+/*!
+ * Inherit from EventEmitter.
+ */
+Schema.prototype = Object.create(EventEmitter.prototype);
+Schema.prototype.constructor = Schema;
+Schema.prototype.instanceOfSchema = true;
+
+/*!
+ * ignore
+ */
+
+Object.defineProperty(Schema.prototype, '$schemaType', {
+ configurable: false,
+ enumerable: false,
+ writable: true
+});
+
+/**
+ * Array of child schemas (from document arrays and single nested subdocs)
+ * and their corresponding compiled models. Each element of the array is
+ * an object with 2 properties: `schema` and `model`.
+ *
+ * This property is typically only useful for plugin authors and advanced users.
+ * You do not need to interact with this property at all to use mongoose.
+ *
+ * @api public
+ * @property childSchemas
+ * @memberOf Schema
+ * @instance
+ */
+
+Object.defineProperty(Schema.prototype, 'childSchemas', {
+ configurable: false,
+ enumerable: true,
+ writable: true
+});
+
+/**
+ * The original object passed to the schema constructor
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ a: String }).add({ b: String });
+ * schema.obj; // { a: String }
+ *
+ * @api public
+ * @property obj
+ * @memberOf Schema
+ * @instance
+ */
+
+Schema.prototype.obj;
+
+/**
+ * Schema as flat paths
+ *
+ * ####Example:
+ * {
+ * '_id' : SchemaType,
+ * , 'nested.key' : SchemaType,
+ * }
+ *
+ * @api private
+ * @property paths
+ * @memberOf Schema
+ * @instance
+ */
+
+Schema.prototype.paths;
+
+/**
+ * Schema as a tree
+ *
+ * ####Example:
+ * {
+ * '_id' : ObjectId
+ * , 'nested' : {
+ * 'key' : String
+ * }
+ * }
+ *
+ * @api private
+ * @property tree
+ * @memberOf Schema
+ * @instance
+ */
+
+Schema.prototype.tree;
+
+/**
+ * Returns a deep copy of the schema
+ *
+ * @return {Schema} the cloned schema
+ * @api public
+ * @memberOf Schema
+ * @instance
+ */
+
+Schema.prototype.clone = function() {
+ const s = new Schema({}, this._userProvidedOptions);
+ s.base = this.base;
+ s.obj = this.obj;
+ s.options = utils.clone(this.options);
+ s.callQueue = this.callQueue.map(function(f) { return f; });
+ s.methods = utils.clone(this.methods);
+ s.methodOptions = utils.clone(this.methodOptions);
+ s.statics = utils.clone(this.statics);
+ s.query = utils.clone(this.query);
+ s.plugins = Array.prototype.slice.call(this.plugins);
+ s._indexes = utils.clone(this._indexes);
+ s.s.hooks = this.s.hooks.clone();
+ s._originalSchema = this._originalSchema == null ?
+ this._originalSchema :
+ this._originalSchema.clone();
+
+ s.tree = utils.clone(this.tree);
+ s.paths = utils.clone(this.paths);
+ s.nested = utils.clone(this.nested);
+ s.subpaths = utils.clone(this.subpaths);
+ s.childSchemas = this.childSchemas.slice();
+ s.singleNestedPaths = utils.clone(this.singleNestedPaths);
+
+ s.virtuals = utils.clone(this.virtuals);
+ s.$globalPluginsApplied = this.$globalPluginsApplied;
+ s.$isRootDiscriminator = this.$isRootDiscriminator;
+
+ if (this.discriminatorMapping != null) {
+ s.discriminatorMapping = Object.assign({}, this.discriminatorMapping);
+ }
+ if (s.discriminators != null) {
+ s.discriminators = Object.assign({}, this.discriminators);
+ }
+
+ s.aliases = Object.assign({}, this.aliases);
+
+ // Bubble up `init` for backwards compat
+ s.on('init', v => this.emit('init', v));
+
+ return s;
+};
+
+/**
+ * Returns default options for this schema, merged with `options`.
+ *
+ * @param {Object} options
+ * @return {Object}
+ * @api private
+ */
+
+Schema.prototype.defaultOptions = function(options) {
+ if (options && options.safe === false) {
+ options.safe = {w: 0};
+ }
+
+ if (options && options.safe && options.safe.w === 0) {
+ // if you turn off safe writes, then versioning goes off as well
+ options.versionKey = false;
+ }
+
+ this._userProvidedOptions = options == null ? {} : utils.clone(options);
+
+ const baseOptions = get(this, 'base.options', {});
+ options = utils.options({
+ strict: 'strict' in baseOptions ? baseOptions.strict : true,
+ bufferCommands: true,
+ capped: false, // { size, max, autoIndexId }
+ versionKey: '__v',
+ discriminatorKey: '__t',
+ minimize: true,
+ autoIndex: null,
+ shardKey: null,
+ read: null,
+ validateBeforeSave: true,
+ // the following are only applied at construction time
+ noId: false, // deprecated, use { _id: false }
+ _id: true,
+ noVirtualId: false, // deprecated, use { id: false }
+ id: true,
+ typeKey: 'type'
+ }, utils.clone(options));
+
+ if (options.read) {
+ options.read = readPref(options.read);
+ }
+
+ return options;
+};
+
+/**
+ * Adds key path / schema type pairs to this schema.
+ *
+ * ####Example:
+ *
+ * const ToySchema = new Schema();
+ * ToySchema.add({ name: 'string', color: 'string', price: 'number' });
+ *
+ * const TurboManSchema = new Schema();
+ * // You can also `add()` another schema and copy over all paths, virtuals,
+ * // getters, setters, indexes, methods, and statics.
+ * TurboManSchema.add(ToySchema).add({ year: Number });
+ *
+ * @param {Object|Schema} obj plain object with paths to add, or another schema
+ * @param {String} [prefix] path to prefix the newly added paths with
+ * @return {Schema} the Schema instance
+ * @api public
+ */
+
+Schema.prototype.add = function add(obj, prefix) {
+ if (obj instanceof Schema) {
+ merge(this, obj);
+ return;
+ }
+
+ prefix = prefix || '';
+ const keys = Object.keys(obj);
+
+ for (let i = 0; i < keys.length; ++i) {
+ const key = keys[i];
+
+ if (obj[key] == null) {
+ throw new TypeError('Invalid value for schema path `' + prefix + key + '`');
+ }
+
+ if (Array.isArray(obj[key]) && obj[key].length === 1 && obj[key][0] == null) {
+ throw new TypeError('Invalid value for schema Array path `' + prefix + key + '`');
+ }
+
+ if (utils.isObject(obj[key]) &&
+ (!obj[key].constructor || utils.getFunctionName(obj[key].constructor) === 'Object') &&
+ (!obj[key][this.options.typeKey] || (this.options.typeKey === 'type' && obj[key].type.type))) {
+ if (Object.keys(obj[key]).length) {
+ // nested object { last: { name: String }}
+ this.nested[prefix + key] = true;
+ this.add(obj[key], prefix + key + '.');
+ } else {
+ if (prefix) {
+ this.nested[prefix.substr(0, prefix.length - 1)] = true;
+ }
+ this.path(prefix + key, obj[key]); // mixed type
+ }
+ } else {
+ if (prefix) {
+ this.nested[prefix.substr(0, prefix.length - 1)] = true;
+ }
+ this.path(prefix + key, obj[key]);
+ }
+ }
+
+ const addedKeys = Object.keys(obj).
+ map(key => prefix ? prefix + key : key);
+ aliasFields(this, addedKeys);
+ return this;
+};
+
+/**
+ * Reserved document keys.
+ *
+ * Keys in this object are names that are rejected in schema declarations b/c they conflict with mongoose functionality. Using these key name will throw an error.
+ *
+ * on, emit, _events, db, get, set, init, isNew, errors, schema, options, modelName, collection, _pres, _posts, toObject
+ *
+ * _NOTE:_ Use of these terms as method names is permitted, but play at your own risk, as they may be existing mongoose document methods you are stomping on.
+ *
+ * var schema = new Schema(..);
+ * schema.methods.init = function () {} // potentially breaking
+ */
+
+Schema.reserved = Object.create(null);
+Schema.prototype.reserved = Schema.reserved;
+const reserved = Schema.reserved;
+// Core object
+reserved['prototype'] =
+// EventEmitter
+reserved.emit =
+reserved.on =
+reserved.once =
+reserved.listeners =
+reserved.removeListener =
+// document properties and functions
+reserved.collection =
+reserved.db =
+reserved.errors =
+reserved.init =
+reserved.isModified =
+reserved.isNew =
+reserved.get =
+reserved.modelName =
+reserved.save =
+reserved.schema =
+reserved.toObject =
+reserved.validate =
+reserved.remove =
+reserved.populated =
+// hooks.js
+reserved._pres = reserved._posts = 1;
+
+/*!
+ * Document keys to print warnings for
+ */
+
+const warnings = {};
+warnings.increment = '`increment` should not be used as a schema path name ' +
+ 'unless you have disabled versioning.';
+
+/**
+ * Gets/sets schema paths.
+ *
+ * Sets a path (if arity 2)
+ * Gets a path (if arity 1)
+ *
+ * ####Example
+ *
+ * schema.path('name') // returns a SchemaType
+ * schema.path('name', Number) // changes the schemaType of `name` to Number
+ *
+ * @param {String} path
+ * @param {Object} constructor
+ * @api public
+ */
+
+Schema.prototype.path = function(path, obj) {
+ if (obj === undefined) {
+ if (this.paths.hasOwnProperty(path)) {
+ return this.paths[path];
+ }
+ if (this.subpaths.hasOwnProperty(path)) {
+ return this.subpaths[path];
+ }
+ if (this.singleNestedPaths.hasOwnProperty(path)) {
+ return this.singleNestedPaths[path];
+ }
+
+ // Look for maps
+ for (const _path of Object.keys(this.paths)) {
+ if (!_path.includes('.$*')) {
+ continue;
+ }
+ const re = new RegExp('^' + _path.replace(/\.\$\*/g, '.[^.]+') + '$');
+ if (re.test(path)) {
+ return this.paths[_path];
+ }
+ }
+
+ // subpaths?
+ return /\.\d+\.?.*$/.test(path)
+ ? getPositionalPath(this, path)
+ : undefined;
+ }
+
+ // some path names conflict with document methods
+ if (reserved[path]) {
+ throw new Error('`' + path + '` may not be used as a schema pathname');
+ }
+
+ if (warnings[path]) {
+ console.log('WARN: ' + warnings[path]);
+ }
+
+ if (typeof obj === 'object' && 'ref' in obj) {
+ validateRef(obj.ref, path);
+ }
+
+ // update the tree
+ const subpaths = path.split(/\./);
+ const last = subpaths.pop();
+ let branch = this.tree;
+
+ subpaths.forEach(function(sub, i) {
+ if (!branch[sub]) {
+ branch[sub] = {};
+ }
+ if (typeof branch[sub] !== 'object') {
+ const msg = 'Cannot set nested path `' + path + '`. '
+ + 'Parent path `'
+ + subpaths.slice(0, i).concat([sub]).join('.')
+ + '` already set to type ' + branch[sub].name
+ + '.';
+ throw new Error(msg);
+ }
+ branch = branch[sub];
+ });
+
+ branch[last] = utils.clone(obj);
+
+ this.paths[path] = this.interpretAsType(path, obj, this.options);
+ const schemaType = this.paths[path];
+
+ if (schemaType.$isSchemaMap) {
+ // Maps can have arbitrary keys, so `$*` is internal shorthand for "any key"
+ // The '$' is to imply this path should never be stored in MongoDB so we
+ // can easily build a regexp out of this path, and '*' to imply "any key."
+ const mapPath = path + '.$*';
+ this.paths[path + '.$*'] = this.interpretAsType(mapPath,
+ obj.of || { type: {} }, this.options);
+ schemaType.$__schemaType = this.paths[path + '.$*'];
+ }
+
+ if (schemaType.$isSingleNested) {
+ for (const key in schemaType.schema.paths) {
+ this.singleNestedPaths[path + '.' + key] = schemaType.schema.paths[key];
+ }
+ for (const key in schemaType.schema.singleNestedPaths) {
+ this.singleNestedPaths[path + '.' + key] =
+ schemaType.schema.singleNestedPaths[key];
+ }
+
+ Object.defineProperty(schemaType.schema, 'base', {
+ configurable: true,
+ enumerable: false,
+ writable: false,
+ value: this.base
+ });
+
+ schemaType.caster.base = this.base;
+ this.childSchemas.push({
+ schema: schemaType.schema,
+ model: schemaType.caster
+ });
+ } else if (schemaType.$isMongooseDocumentArray) {
+ Object.defineProperty(schemaType.schema, 'base', {
+ configurable: true,
+ enumerable: false,
+ writable: false,
+ value: this.base
+ });
+
+ schemaType.casterConstructor.base = this.base;
+ this.childSchemas.push({
+ schema: schemaType.schema,
+ model: schemaType.casterConstructor
+ });
+ }
+
+ return this;
+};
+
+/**
+ * The Mongoose instance this schema is associated with
+ *
+ * @property base
+ * @api private
+ */
+
+Object.defineProperty(Schema.prototype, 'base', {
+ configurable: true,
+ enumerable: false,
+ writable: true,
+ value: null
+});
+
+/**
+ * Converts type arguments into Mongoose Types.
+ *
+ * @param {String} path
+ * @param {Object} obj constructor
+ * @api private
+ */
+
+Schema.prototype.interpretAsType = function(path, obj, options) {
+ if (obj instanceof SchemaType) {
+ return obj;
+ }
+
+ // If this schema has an associated Mongoose object, use the Mongoose object's
+ // copy of SchemaTypes re: gh-7158 gh-6933
+ const MongooseTypes = this.base != null ? this.base.Schema.Types : Schema.Types;
+
+ if (obj.constructor) {
+ const constructorName = utils.getFunctionName(obj.constructor);
+ if (constructorName !== 'Object') {
+ const oldObj = obj;
+ obj = {};
+ obj[options.typeKey] = oldObj;
+ }
+ }
+
+ // Get the type making sure to allow keys named "type"
+ // and default to mixed if not specified.
+ // { type: { type: String, default: 'freshcut' } }
+ let type = obj[options.typeKey] && (options.typeKey !== 'type' || !obj.type.type)
+ ? obj[options.typeKey]
+ : {};
+ let name;
+
+ if (utils.getFunctionName(type.constructor) === 'Object' || type === 'mixed') {
+ return new MongooseTypes.Mixed(path, obj);
+ }
+
+ if (Array.isArray(type) || Array === type || type === 'array') {
+ // if it was specified through { type } look for `cast`
+ let cast = (Array === type || type === 'array')
+ ? obj.cast
+ : type[0];
+
+ if (cast && cast.instanceOfSchema) {
+ return new MongooseTypes.DocumentArray(path, cast, obj);
+ }
+ if (cast &&
+ cast[options.typeKey] &&
+ cast[options.typeKey].instanceOfSchema) {
+ return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast);
+ }
+
+ if (Array.isArray(cast)) {
+ return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj);
+ }
+
+ if (typeof cast === 'string') {
+ cast = MongooseTypes[cast.charAt(0).toUpperCase() + cast.substring(1)];
+ } else if (cast && (!cast[options.typeKey] || (options.typeKey === 'type' && cast.type.type))
+ && utils.getFunctionName(cast.constructor) === 'Object') {
+ if (Object.keys(cast).length) {
+ // The `minimize` and `typeKey` options propagate to child schemas
+ // declared inline, like `{ arr: [{ val: { $type: String } }] }`.
+ // See gh-3560
+ const childSchemaOptions = {minimize: options.minimize};
+ if (options.typeKey) {
+ childSchemaOptions.typeKey = options.typeKey;
+ }
+ //propagate 'strict' option to child schema
+ if (options.hasOwnProperty('strict')) {
+ childSchemaOptions.strict = options.strict;
+ }
+ const childSchema = new Schema(cast, childSchemaOptions);
+ childSchema.$implicitlyCreated = true;
+ return new MongooseTypes.DocumentArray(path, childSchema, obj);
+ } else {
+ // Special case: empty object becomes mixed
+ return new MongooseTypes.Array(path, MongooseTypes.Mixed, obj);
+ }
+ }
+
+ if (cast) {
+ type = cast[options.typeKey] && (options.typeKey !== 'type' || !cast.type.type)
+ ? cast[options.typeKey]
+ : cast;
+
+ name = typeof type === 'string'
+ ? type
+ : type.schemaName || utils.getFunctionName(type);
+
+ if (!(name in MongooseTypes)) {
+ throw new TypeError('Invalid schema configuration: ' +
+ `\`${name}\` is not a valid type within the array \`${path}\`.` +
+ 'See http://bit.ly/mongoose-schematypes for a list of valid schema types.');
+ }
+ }
+
+ return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj, options);
+ }
+
+ if (type && type.instanceOfSchema) {
+ return new MongooseTypes.Embedded(type, path, obj);
+ }
+
+ if (Buffer.isBuffer(type)) {
+ name = 'Buffer';
+ } else if (typeof type === 'function' || typeof type === 'object') {
+ name = type.schemaName || utils.getFunctionName(type);
+ } else {
+ name = type == null ? '' + type : type.toString();
+ }
+
+ if (name) {
+ name = name.charAt(0).toUpperCase() + name.substring(1);
+ }
+ // Special case re: gh-7049 because the bson `ObjectID` class' capitalization
+ // doesn't line up with Mongoose's.
+ if (name === 'ObjectID') {
+ name = 'ObjectId';
+ }
+
+ if (MongooseTypes[name] == null) {
+ throw new TypeError(`Invalid schema configuration: \`${name}\` is not ` +
+ `a valid type at path \`${path}\`. See ` +
+ 'http://bit.ly/mongoose-schematypes for a list of valid schema types.');
+ }
+
+ return new MongooseTypes[name](path, obj);
+};
+
+/**
+ * Iterates the schemas paths similar to Array#forEach.
+ *
+ * The callback is passed the pathname and schemaType as arguments on each iteration.
+ *
+ * @param {Function} fn callback function
+ * @return {Schema} this
+ * @api public
+ */
+
+Schema.prototype.eachPath = function(fn) {
+ const keys = Object.keys(this.paths);
+ const len = keys.length;
+
+ for (let i = 0; i < len; ++i) {
+ fn(keys[i], this.paths[keys[i]]);
+ }
+
+ return this;
+};
+
+/**
+ * Returns an Array of path strings that are required by this schema.
+ *
+ * @api public
+ * @param {Boolean} invalidate refresh the cache
+ * @return {Array}
+ */
+
+Schema.prototype.requiredPaths = function requiredPaths(invalidate) {
+ if (this._requiredpaths && !invalidate) {
+ return this._requiredpaths;
+ }
+
+ const paths = Object.keys(this.paths);
+ let i = paths.length;
+ const ret = [];
+
+ while (i--) {
+ const path = paths[i];
+ if (this.paths[path].isRequired) {
+ ret.push(path);
+ }
+ }
+ this._requiredpaths = ret;
+ return this._requiredpaths;
+};
+
+/**
+ * Returns indexes from fields and schema-level indexes (cached).
+ *
+ * @api private
+ * @return {Array}
+ */
+
+Schema.prototype.indexedPaths = function indexedPaths() {
+ if (this._indexedpaths) {
+ return this._indexedpaths;
+ }
+ this._indexedpaths = this.indexes();
+ return this._indexedpaths;
+};
+
+/**
+ * Returns the pathType of `path` for this schema.
+ *
+ * Given a path, returns whether it is a real, virtual, nested, or ad-hoc/undefined path.
+ *
+ * @param {String} path
+ * @return {String}
+ * @api public
+ */
+
+Schema.prototype.pathType = function(path) {
+ if (path in this.paths) {
+ return 'real';
+ }
+ if (path in this.virtuals) {
+ return 'virtual';
+ }
+ if (path in this.nested) {
+ return 'nested';
+ }
+ if (path in this.subpaths) {
+ return 'real';
+ }
+ if (path in this.singleNestedPaths) {
+ return 'real';
+ }
+
+ // Look for maps
+ for (const _path of Object.keys(this.paths)) {
+ if (!_path.includes('.$*')) {
+ continue;
+ }
+ const re = new RegExp('^' + _path.replace(/\.\$\*/g, '.[^.]+') + '$');
+ if (re.test(path)) {
+ return this.paths[_path];
+ }
+ }
+
+ if (/\.\d+\.|\.\d+$/.test(path)) {
+ return getPositionalPathType(this, path);
+ }
+ return 'adhocOrUndefined';
+};
+
+/**
+ * Returns true iff this path is a child of a mixed schema.
+ *
+ * @param {String} path
+ * @return {Boolean}
+ * @api private
+ */
+
+Schema.prototype.hasMixedParent = function(path) {
+ const subpaths = path.split(/\./g);
+ path = '';
+ for (let i = 0; i < subpaths.length; ++i) {
+ path = i > 0 ? path + '.' + subpaths[i] : subpaths[i];
+ if (path in this.paths &&
+ this.paths[path] instanceof MongooseTypes.Mixed) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Setup updatedAt and createdAt timestamps to documents if enabled
+ *
+ * @param {Boolean|Object} timestamps timestamps options
+ * @api private
+ */
+Schema.prototype.setupTimestamp = function(timestamps) {
+ const childHasTimestamp = this.childSchemas.find(withTimestamp);
+
+ function withTimestamp(s) {
+ const ts = s.schema.options.timestamps;
+ return !!ts;
+ }
+
+ if (!timestamps && !childHasTimestamp) {
+ return;
+ }
+
+ const createdAt = handleTimestampOption(timestamps, 'createdAt');
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+ const schemaAdditions = {};
+
+ this.$timestamps = { createdAt: createdAt, updatedAt: updatedAt };
+
+ if (updatedAt && !this.paths[updatedAt]) {
+ schemaAdditions[updatedAt] = Date;
+ }
+
+ if (createdAt && !this.paths[createdAt]) {
+ schemaAdditions[createdAt] = Date;
+ }
+
+ this.add(schemaAdditions);
+
+ this.pre('save', function(next) {
+ const defaultTimestamp = (this.ownerDocument ? this.ownerDocument() : this).
+ constructor.base.now();
+ const auto_id = this._id && this._id.auto;
+
+ if (createdAt && !this.get(createdAt) && this.isSelected(createdAt)) {
+ this.set(createdAt, auto_id ? this._id.getTimestamp() : defaultTimestamp);
+ }
+
+ if (updatedAt && (this.isNew || this.isModified())) {
+ let ts = defaultTimestamp;
+ if (this.isNew) {
+ if (createdAt != null) {
+ ts = this.get(createdAt);
+ } else if (auto_id) {
+ ts = this._id.getTimestamp();
+ }
+ }
+ this.set(updatedAt, ts);
+ }
+
+ next();
+ });
+
+ this.methods.initializeTimestamps = function() {
+ if (createdAt && !this.get(createdAt)) {
+ this.set(createdAt, new Date());
+ }
+ if (updatedAt && !this.get(updatedAt)) {
+ this.set(updatedAt, new Date());
+ }
+ return this;
+ };
+
+ _setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
+
+ this.pre('findOneAndUpdate', _setTimestampsOnUpdate);
+ this.pre('replaceOne', _setTimestampsOnUpdate);
+ this.pre('update', _setTimestampsOnUpdate);
+ this.pre('updateOne', _setTimestampsOnUpdate);
+ this.pre('updateMany', _setTimestampsOnUpdate);
+
+ function _setTimestampsOnUpdate(next) {
+ const now = this.model.base.now();
+ applyTimestampsToUpdate(now, createdAt, updatedAt, this.getUpdate(),
+ this.options, this.schema);
+ applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
+ next();
+ }
+};
+
+/*!
+ * ignore
+ */
+
+function getPositionalPathType(self, path) {
+ const subpaths = path.split(/\.(\d+)\.|\.(\d+)$/).filter(Boolean);
+ if (subpaths.length < 2) {
+ return self.paths.hasOwnProperty(subpaths[0]) ? self.paths[subpath[0]] : null;
+ }
+
+ let val = self.path(subpaths[0]);
+ let isNested = false;
+ if (!val) {
+ return val;
+ }
+
+ const last = subpaths.length - 1;
+ let subpath;
+ let i = 1;
+
+ for (; i < subpaths.length; ++i) {
+ isNested = false;
+ subpath = subpaths[i];
+
+ if (i === last && val && !/\D/.test(subpath)) {
+ if (val.$isMongooseDocumentArray) {
+ const oldVal = val;
+ val = new SchemaType(subpath, {
+ required: get(val, 'schemaOptions.required', false)
+ });
+ val.cast = function(value, doc, init) {
+ return oldVal.cast(value, doc, init)[0];
+ };
+ val.$isMongooseDocumentArrayElement = true;
+ val.caster = oldVal.caster;
+ val.schema = oldVal.schema;
+ } else if (val instanceof MongooseTypes.Array) {
+ // StringSchema, NumberSchema, etc
+ val = val.caster;
+ } else {
+ val = undefined;
+ }
+ break;
+ }
+
+ // ignore if its just a position segment: path.0.subpath
+ if (!/\D/.test(subpath)) {
+ continue;
+ }
+
+ if (!(val && val.schema)) {
+ val = undefined;
+ break;
+ }
+
+ const type = val.schema.pathType(subpath);
+ isNested = (type === 'nested');
+ val = val.schema.path(subpath);
+ }
+
+ self.subpaths[path] = val;
+ if (val) {
+ return 'real';
+ }
+ if (isNested) {
+ return 'nested';
+ }
+ return 'adhocOrUndefined';
+}
+
+
+/*!
+ * ignore
+ */
+
+function getPositionalPath(self, path) {
+ getPositionalPathType(self, path);
+ return self.subpaths[path];
+}
+
+/**
+ * Adds a method call to the queue.
+ *
+ * @param {String} name name of the document method to call later
+ * @param {Array} args arguments to pass to the method
+ * @api public
+ */
+
+Schema.prototype.queue = function(name, args) {
+ this.callQueue.push([name, args]);
+ return this;
+};
+
+/**
+ * Defines a pre hook for the document.
+ *
+ * ####Example
+ *
+ * var toySchema = new Schema({ name: String, created: Date });
+ *
+ * toySchema.pre('save', function(next) {
+ * if (!this.created) this.created = new Date;
+ * next();
+ * });
+ *
+ * toySchema.pre('validate', function(next) {
+ * if (this.name !== 'Woody') this.name = 'Woody';
+ * next();
+ * });
+ *
+ * // Equivalent to calling `pre()` on `find`, `findOne`, `findOneAndUpdate`.
+ * toySchema.pre(/^find/, function(next) {
+ * console.log(this.getQuery());
+ * });
+ *
+ * @param {String|RegExp} method or regular expression to match method name
+ * @param {Object} [options]
+ * @param {Boolean} [options.document] If `name` is a hook for both document and query middleware, set to `true` to run on document middleware.
+ * @param {Boolean} [options.query] If `name` is a hook for both document and query middleware, set to `true` to run on query middleware.
+ * @param {Function} callback
+ * @see hooks.js https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3
+ * @api public
+ */
+
+Schema.prototype.pre = function(name) {
+ if (name instanceof RegExp) {
+ const remainingArgs = Array.prototype.slice.call(arguments, 1);
+ for (const fn of allMiddleware) {
+ if (name.test(fn)) {
+ this.pre.apply(this, [fn].concat(remainingArgs));
+ }
+ }
+ return this;
+ }
+ this.s.hooks.pre.apply(this.s.hooks, arguments);
+ return this;
+};
+
+/**
+ * Defines a post hook for the document
+ *
+ * var schema = new Schema(..);
+ * schema.post('save', function (doc) {
+ * console.log('this fired after a document was saved');
+ * });
+ *
+ * schema.post('find', function(docs) {
+ * console.log('this fired after you ran a find query');
+ * });
+ *
+ * schema.post(/Many$/, function(res) {
+ * console.log('this fired after you ran `updateMany()` or `deleteMany()`);
+ * });
+ *
+ * var Model = mongoose.model('Model', schema);
+ *
+ * var m = new Model(..);
+ * m.save(function(err) {
+ * console.log('this fires after the `post` hook');
+ * });
+ *
+ * m.find(function(err, docs) {
+ * console.log('this fires after the post find hook');
+ * });
+ *
+ * @param {String|RegExp} method or regular expression to match method name
+ * @param {Object} [options]
+ * @param {Boolean} [options.document] If `name` is a hook for both document and query middleware, set to `true` to run on document middleware.
+ * @param {Boolean} [options.query] If `name` is a hook for both document and query middleware, set to `true` to run on query middleware.
+ * @param {Function} fn callback
+ * @see middleware http://mongoosejs.com/docs/middleware.html
+ * @see kareem http://npmjs.org/package/kareem
+ * @api public
+ */
+
+Schema.prototype.post = function(name) {
+ if (name instanceof RegExp) {
+ const remainingArgs = Array.prototype.slice.call(arguments, 1);
+ for (const fn of allMiddleware) {
+ if (name.test(fn)) {
+ this.post.apply(this, [fn].concat(remainingArgs));
+ }
+ }
+ return this;
+ }
+ this.s.hooks.post.apply(this.s.hooks, arguments);
+ return this;
+};
+
+/**
+ * Registers a plugin for this schema.
+ *
+ * @param {Function} plugin callback
+ * @param {Object} [opts]
+ * @see plugins
+ * @api public
+ */
+
+Schema.prototype.plugin = function(fn, opts) {
+ if (typeof fn !== 'function') {
+ throw new Error('First param to `schema.plugin()` must be a function, ' +
+ 'got "' + (typeof fn) + '"');
+ }
+
+ if (opts &&
+ opts.deduplicate) {
+ for (let i = 0; i < this.plugins.length; ++i) {
+ if (this.plugins[i].fn === fn) {
+ return this;
+ }
+ }
+ }
+ this.plugins.push({ fn: fn, opts: opts });
+
+ fn(this, opts);
+ return this;
+};
+
+/**
+ * Adds an instance method to documents constructed from Models compiled from this schema.
+ *
+ * ####Example
+ *
+ * var schema = kittySchema = new Schema(..);
+ *
+ * schema.method('meow', function () {
+ * console.log('meeeeeoooooooooooow');
+ * })
+ *
+ * var Kitty = mongoose.model('Kitty', schema);
+ *
+ * var fizz = new Kitty;
+ * fizz.meow(); // meeeeeooooooooooooow
+ *
+ * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as methods.
+ *
+ * schema.method({
+ * purr: function () {}
+ * , scratch: function () {}
+ * });
+ *
+ * // later
+ * fizz.purr();
+ * fizz.scratch();
+ *
+ * NOTE: `Schema.method()` adds instance methods to the `Schema.methods` object. You can also add instance methods directly to the `Schema.methods` object as seen in the [guide](./guide.html#methods)
+ *
+ * @param {String|Object} method name
+ * @param {Function} [fn]
+ * @api public
+ */
+
+Schema.prototype.method = function(name, fn, options) {
+ if (typeof name !== 'string') {
+ for (const i in name) {
+ this.methods[i] = name[i];
+ this.methodOptions[i] = utils.clone(options);
+ }
+ } else {
+ this.methods[name] = fn;
+ this.methodOptions[name] = utils.clone(options);
+ }
+ return this;
+};
+
+/**
+ * Adds static "class" methods to Models compiled from this schema.
+ *
+ * ####Example
+ *
+ * var schema = new Schema(..);
+ * schema.static('findByName', function (name, callback) {
+ * return this.find({ name: name }, callback);
+ * });
+ *
+ * var Drink = mongoose.model('Drink', schema);
+ * Drink.findByName('sanpellegrino', function (err, drinks) {
+ * //
+ * });
+ *
+ * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as statics.
+ *
+ * @param {String|Object} name
+ * @param {Function} [fn]
+ * @api public
+ */
+
+Schema.prototype.static = function(name, fn) {
+ if (typeof name !== 'string') {
+ for (const i in name) {
+ this.statics[i] = name[i];
+ }
+ } else {
+ this.statics[name] = fn;
+ }
+ return this;
+};
+
+/**
+ * Defines an index (most likely compound) for this schema.
+ *
+ * ####Example
+ *
+ * schema.index({ first: 1, last: -1 })
+ *
+ * @param {Object} fields
+ * @param {Object} [options] Options to pass to [MongoDB driver's `createIndex()` function](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#createIndex)
+ * @param {String} [options.expires=null] Mongoose-specific syntactic sugar, uses [ms](https://www.npmjs.com/package/ms) to convert `expires` option into seconds for the `expireAfterSeconds` in the above link.
+ * @api public
+ */
+
+Schema.prototype.index = function(fields, options) {
+ fields || (fields = {});
+ options || (options = {});
+
+ if (options.expires) {
+ utils.expires(options);
+ }
+
+ this._indexes.push([fields, options]);
+ return this;
+};
+
+/**
+ * Sets/gets a schema option.
+ *
+ * ####Example
+ *
+ * schema.set('strict'); // 'true' by default
+ * schema.set('strict', false); // Sets 'strict' to false
+ * schema.set('strict'); // 'false'
+ *
+ * @param {String} key option name
+ * @param {Object} [value] if not passed, the current option value is returned
+ * @see Schema ./
+ * @api public
+ */
+
+Schema.prototype.set = function(key, value, _tags) {
+ if (arguments.length === 1) {
+ return this.options[key];
+ }
+
+ switch (key) {
+ case 'read':
+ this.options[key] = readPref(value, _tags);
+ this._userProvidedOptions[key] = this.options[key];
+ break;
+ case 'safe':
+ setSafe(this.options, value);
+ this._userProvidedOptions[key] = this.options[key];
+ break;
+ case 'timestamps':
+ this.setupTimestamp(value);
+ this.options[key] = value;
+ this._userProvidedOptions[key] = this.options[key];
+ break;
+ default:
+ this.options[key] = value;
+ this._userProvidedOptions[key] = this.options[key];
+ break;
+ }
+
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+const safeDeprecationWarning = 'Mongoose: The `safe` option for schemas is ' +
+ 'deprecated. Use the `writeConcern` option instead: ' +
+ 'http://bit.ly/mongoose-write-concern';
+
+const setSafe = util.deprecate(function setSafe(options, value) {
+ options.safe = value === false ?
+ {w: 0} :
+ value;
+}, safeDeprecationWarning);
+
+/**
+ * Gets a schema option.
+ *
+ * @param {String} key option name
+ * @api public
+ */
+
+Schema.prototype.get = function(key) {
+ return this.options[key];
+};
+
+/**
+ * The allowed index types
+ *
+ * @receiver Schema
+ * @static indexTypes
+ * @api public
+ */
+
+const indexTypes = '2d 2dsphere hashed text'.split(' ');
+
+Object.defineProperty(Schema, 'indexTypes', {
+ get: function() {
+ return indexTypes;
+ },
+ set: function() {
+ throw new Error('Cannot overwrite Schema.indexTypes');
+ }
+});
+
+/**
+ * Returns a list of indexes that this schema declares, via `schema.index()`
+ * or by `index: true` in a path's options.
+ *
+ * @api public
+ */
+
+Schema.prototype.indexes = function() {
+ return getIndexes(this);
+};
+
+/**
+ * Creates a virtual type with the given name.
+ *
+ * @param {String} name
+ * @param {Object} [options]
+ * @param {String|Model} [options.ref] model name or model instance. Marks this as a [populate virtual](populate.html#populate-virtuals).
+ * @param {String|Function} [options.localField] Required for populate virtuals. See [populate virtual docs](populate.html#populate-virtuals) for more information.
+ * @param {String|Function} [options.foreignField] Required for populate virtuals. See [populate virtual docs](populate.html#populate-virtuals) for more information.
+ * @param {Boolean|Function} [options.justOne=false] Only works with populate virtuals. If truthy, will be a single doc or `null`. Otherwise, the populate virtual will be an array.
+ * @param {Boolean} [options.count=false] Only works with populate virtuals. If truthy, this populate virtual will contain the number of documents rather than the documents themselves when you `populate()`.
+ * @return {VirtualType}
+ */
+
+Schema.prototype.virtual = function(name, options) {
+ if (options && options.ref) {
+ if (!options.localField) {
+ throw new Error('Reference virtuals require `localField` option');
+ }
+
+ if (!options.foreignField) {
+ throw new Error('Reference virtuals require `foreignField` option');
+ }
+
+ this.pre('init', function(obj) {
+ if (mpath.has(name, obj)) {
+ const _v = mpath.get(name, obj);
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+
+ if (options.justOne || options.count) {
+ this.$$populatedVirtuals[name] = Array.isArray(_v) ?
+ _v[0] :
+ _v;
+ } else {
+ this.$$populatedVirtuals[name] = Array.isArray(_v) ?
+ _v :
+ _v == null ? [] : [_v];
+ }
+
+ mpath.unset(name, obj);
+ }
+ });
+
+ const virtual = this.virtual(name);
+ virtual.options = options;
+ return virtual.
+ get(function() {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+ if (name in this.$$populatedVirtuals) {
+ return this.$$populatedVirtuals[name];
+ }
+ return null;
+ }).
+ set(function(_v) {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+
+ if (options.justOne || options.count) {
+ this.$$populatedVirtuals[name] = Array.isArray(_v) ?
+ _v[0] :
+ _v;
+
+ if (typeof this.$$populatedVirtuals[name] !== 'object') {
+ this.$$populatedVirtuals[name] = options.count ? _v : null;
+ }
+ } else {
+ this.$$populatedVirtuals[name] = Array.isArray(_v) ?
+ _v :
+ _v == null ? [] : [_v];
+
+ this.$$populatedVirtuals[name] = this.$$populatedVirtuals[name].filter(function(doc) {
+ return doc && typeof doc === 'object';
+ });
+ }
+ });
+ }
+
+ const virtuals = this.virtuals;
+ const parts = name.split('.');
+
+ if (this.pathType(name) === 'real') {
+ throw new Error('Virtual path "' + name + '"' +
+ ' conflicts with a real path in the schema');
+ }
+
+ virtuals[name] = parts.reduce(function(mem, part, i) {
+ mem[part] || (mem[part] = (i === parts.length - 1)
+ ? new VirtualType(options, name)
+ : {});
+ return mem[part];
+ }, this.tree);
+
+ return virtuals[name];
+};
+
+/**
+ * Returns the virtual type with the given `name`.
+ *
+ * @param {String} name
+ * @return {VirtualType}
+ */
+
+Schema.prototype.virtualpath = function(name) {
+ return this.virtuals.hasOwnProperty(name) ? this.virtuals[name] : null;
+};
+
+/**
+ * Removes the given `path` (or [`paths`]).
+ *
+ * @param {String|Array} path
+ * @return {Schema} the Schema instance
+ * @api public
+ */
+Schema.prototype.remove = function(path) {
+ if (typeof path === 'string') {
+ path = [path];
+ }
+ if (Array.isArray(path)) {
+ path.forEach(function(name) {
+ if (this.path(name)) {
+ delete this.paths[name];
+
+ const pieces = name.split('.');
+ const last = pieces.pop();
+ let branch = this.tree;
+ for (let i = 0; i < pieces.length; ++i) {
+ branch = branch[pieces[i]];
+ }
+ delete branch[last];
+ }
+ }, this);
+ }
+ return this;
+};
+
+/**
+ * Loads an ES6 class into a schema. Maps [setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) + [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get), [static methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static),
+ * and [instance methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Class_body_and_method_definitions)
+ * to schema [virtuals](http://mongoosejs.com/docs/guide.html#virtuals),
+ * [statics](http://mongoosejs.com/docs/guide.html#statics), and
+ * [methods](http://mongoosejs.com/docs/guide.html#methods).
+ *
+ * ####Example:
+ *
+ * ```javascript
+ * const md5 = require('md5');
+ * const userSchema = new Schema({ email: String });
+ * class UserClass {
+ * // `gravatarImage` becomes a virtual
+ * get gravatarImage() {
+ * const hash = md5(this.email.toLowerCase());
+ * return `https://www.gravatar.com/avatar/${hash}`;
+ * }
+ *
+ * // `getProfileUrl()` becomes a document method
+ * getProfileUrl() {
+ * return `https://mysite.com/${this.email}`;
+ * }
+ *
+ * // `findByEmail()` becomes a static
+ * static findByEmail(email) {
+ * return this.findOne({ email });
+ * }
+ * }
+ *
+ * // `schema` will now have a `gravatarImage` virtual, a `getProfileUrl()` method,
+ * // and a `findByEmail()` static
+ * userSchema.loadClass(UserClass);
+ * ```
+ *
+ * @param {Function} model
+ * @param {Boolean} [virtualsOnly] if truthy, only pulls virtuals from the class, not methods or statics
+ */
+Schema.prototype.loadClass = function(model, virtualsOnly) {
+ if (model === Object.prototype ||
+ model === Function.prototype ||
+ model.prototype.hasOwnProperty('$isMongooseModelPrototype')) {
+ return this;
+ }
+
+ this.loadClass(Object.getPrototypeOf(model));
+
+ // Add static methods
+ if (!virtualsOnly) {
+ Object.getOwnPropertyNames(model).forEach(function(name) {
+ if (name.match(/^(length|name|prototype)$/)) {
+ return;
+ }
+ const method = Object.getOwnPropertyDescriptor(model, name);
+ if (typeof method.value === 'function') {
+ this.static(name, method.value);
+ }
+ }, this);
+ }
+
+ // Add methods and virtuals
+ Object.getOwnPropertyNames(model.prototype).forEach(function(name) {
+ if (name.match(/^(constructor)$/)) {
+ return;
+ }
+ const method = Object.getOwnPropertyDescriptor(model.prototype, name);
+ if (!virtualsOnly) {
+ if (typeof method.value === 'function') {
+ this.method(name, method.value);
+ }
+ }
+ if (typeof method.get === 'function') {
+ this.virtual(name).get(method.get);
+ }
+ if (typeof method.set === 'function') {
+ this.virtual(name).set(method.set);
+ }
+ }, this);
+
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+Schema.prototype._getSchema = function(path) {
+ const _this = this;
+ const pathschema = _this.path(path);
+ const resultPath = [];
+
+ if (pathschema) {
+ pathschema.$fullPath = path;
+ return pathschema;
+ }
+
+ function search(parts, schema) {
+ let p = parts.length + 1;
+ let foundschema;
+ let trypath;
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+ if (foundschema) {
+ resultPath.push(trypath);
+
+ if (foundschema.caster) {
+ // array of Mixed?
+ if (foundschema.caster instanceof MongooseTypes.Mixed) {
+ foundschema.caster.$fullPath = resultPath.join('.');
+ return foundschema.caster;
+ }
+
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ // If there is no foundschema.schema we are dealing with
+ // a path like array.$
+ if (p !== parts.length && foundschema.schema) {
+ let ret;
+ if (parts[p] === '$' || isArrayFilter(parts[p])) {
+ if (p + 1 === parts.length) {
+ // comments.$
+ return foundschema;
+ }
+ // comments.$.comments.$.title
+ ret = search(parts.slice(p + 1), foundschema.schema);
+ if (ret) {
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
+ !foundschema.schema.$isSingleNested;
+ }
+ return ret;
+ }
+ // this is the last path of the selector
+ ret = search(parts.slice(p), foundschema.schema);
+ if (ret) {
+ ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
+ !foundschema.schema.$isSingleNested;
+ }
+ return ret;
+ }
+ }
+
+ foundschema.$fullPath = resultPath.join('.');
+
+ return foundschema;
+ }
+ }
+ }
+
+ // look for arrays
+ const parts = path.split('.');
+ for (let i = 0; i < parts.length; ++i) {
+ if (parts[i] === '$' || isArrayFilter(parts[i])) {
+ // Re: gh-5628, because `schema.path()` doesn't take $ into account.
+ parts[i] = '0';
+ }
+ }
+ return search(parts, _this);
+};
+
+/*!
+ * ignore
+ */
+
+Schema.prototype._getPathType = function(path) {
+ const _this = this;
+ const pathschema = _this.path(path);
+
+ if (pathschema) {
+ return 'real';
+ }
+
+ function search(parts, schema) {
+ let p = parts.length + 1,
+ foundschema,
+ trypath;
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+ if (foundschema) {
+ if (foundschema.caster) {
+ // array of Mixed?
+ if (foundschema.caster instanceof MongooseTypes.Mixed) {
+ return { schema: foundschema, pathType: 'mixed' };
+ }
+
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ // If there is no foundschema.schema we are dealing with
+ // a path like array.$
+ if (p !== parts.length && foundschema.schema) {
+ if (parts[p] === '$' || isArrayFilter(parts[p])) {
+ if (p === parts.length - 1) {
+ return { schema: foundschema, pathType: 'nested' };
+ }
+ // comments.$.comments.$.title
+ return search(parts.slice(p + 1), foundschema.schema);
+ }
+ // this is the last path of the selector
+ return search(parts.slice(p), foundschema.schema);
+ }
+ return {
+ schema: foundschema,
+ pathType: foundschema.$isSingleNested ? 'nested' : 'array'
+ };
+ }
+ return { schema: foundschema, pathType: 'real' };
+ } else if (p === parts.length && schema.nested[trypath]) {
+ return { schema: schema, pathType: 'nested' };
+ }
+ }
+ return { schema: foundschema || schema, pathType: 'undefined' };
+ }
+
+ // look for arrays
+ return search(path.split('.'), _this);
+};
+
+/*!
+ * ignore
+ */
+
+function isArrayFilter(piece) {
+ return piece.indexOf('$[') === 0 &&
+ piece.lastIndexOf(']') === piece.length - 1;
+}
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = Schema;
+
+// require down here because of reference issues
+
+/**
+ * The various built-in Mongoose Schema Types.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var ObjectId = mongoose.Schema.Types.ObjectId;
+ *
+ * ####Types:
+ *
+ * - [String](#schema-string-js)
+ * - [Number](#schema-number-js)
+ * - [Boolean](#schema-boolean-js) | Bool
+ * - [Array](#schema-array-js)
+ * - [Buffer](#schema-buffer-js)
+ * - [Date](#schema-date-js)
+ * - [ObjectId](#schema-objectid-js) | Oid
+ * - [Mixed](#schema-mixed-js)
+ *
+ * Using this exposed access to the `Mixed` SchemaType, we can use them in our schema.
+ *
+ * var Mixed = mongoose.Schema.Types.Mixed;
+ * new mongoose.Schema({ _user: Mixed })
+ *
+ * @api public
+ */
+
+Schema.Types = MongooseTypes = require('./schema/index');
+
+/*!
+ * ignore
+ */
+
+exports.ObjectId = MongooseTypes.ObjectId;
diff --git a/node_modules/mongoose/lib/schema/array.js b/node_modules/mongoose/lib/schema/array.js
new file mode 100644
index 0000000..f8e7b49
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/array.js
@@ -0,0 +1,401 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const $exists = require('./operators/exists');
+const $type = require('./operators/type');
+const MongooseError = require('../error/mongooseError');
+const SchemaType = require('../schematype');
+const CastError = SchemaType.CastError;
+const Types = {
+ Array: SchemaArray,
+ Boolean: require('./boolean'),
+ Date: require('./date'),
+ Number: require('./number'),
+ String: require('./string'),
+ ObjectId: require('./objectid'),
+ Buffer: require('./buffer'),
+ Map: require('./map')
+};
+const Mixed = require('./mixed');
+const cast = require('../cast');
+const get = require('../helpers/get');
+const util = require('util');
+const utils = require('../utils');
+const castToNumber = require('./operators/helpers').castToNumber;
+const geospatial = require('./operators/geospatial');
+const getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
+
+let MongooseArray;
+let EmbeddedDoc;
+
+/**
+ * Array SchemaType constructor
+ *
+ * @param {String} key
+ * @param {SchemaType} cast
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function SchemaArray(key, cast, options, schemaOptions) {
+ // lazy load
+ EmbeddedDoc || (EmbeddedDoc = require('../types').Embedded);
+
+ let typeKey = 'type';
+ if (schemaOptions && schemaOptions.typeKey) {
+ typeKey = schemaOptions.typeKey;
+ }
+
+ if (cast) {
+ let castOptions = {};
+
+ if (utils.getFunctionName(cast.constructor) === 'Object') {
+ if (cast[typeKey]) {
+ // support { type: Woot }
+ castOptions = utils.clone(cast); // do not alter user arguments
+ delete castOptions[typeKey];
+ cast = cast[typeKey];
+ } else {
+ cast = Mixed;
+ }
+ }
+
+ if (cast === Object) {
+ cast = Mixed;
+ }
+
+ // support { type: 'String' }
+ const name = typeof cast === 'string'
+ ? cast
+ : utils.getFunctionName(cast);
+
+ const caster = name in Types
+ ? Types[name]
+ : cast;
+
+ this.casterConstructor = caster;
+
+ if (typeof caster === 'function' &&
+ !caster.$isArraySubdocument &&
+ !caster.$isSchemaMap) {
+ this.caster = new caster(null, castOptions);
+ } else {
+ this.caster = caster;
+ }
+
+ if (!(this.caster instanceof EmbeddedDoc)) {
+ this.caster.path = key;
+ }
+ }
+
+ this.$isMongooseArray = true;
+
+ SchemaType.call(this, key, options, 'Array');
+
+ let defaultArr;
+ let fn;
+
+ if (this.defaultValue != null) {
+ defaultArr = this.defaultValue;
+ fn = typeof defaultArr === 'function';
+ }
+
+ if (!('defaultValue' in this) || this.defaultValue !== void 0) {
+ const defaultFn = function() {
+ let arr = [];
+ if (fn) {
+ arr = defaultArr.call(this);
+ } else if (defaultArr != null) {
+ arr = arr.concat(defaultArr);
+ }
+ // Leave it up to `cast()` to convert the array
+ return arr;
+ };
+ defaultFn.$runBeforeSetters = true;
+ this.default(defaultFn);
+ }
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaArray.schemaName = 'Array';
+
+/*!
+ * Inherits from SchemaType.
+ */
+SchemaArray.prototype = Object.create(SchemaType.prototype);
+SchemaArray.prototype.constructor = SchemaArray;
+
+/**
+ * Adds an enum validator if this is an array of strings. Equivalent to
+ * `SchemaString.prototype.enum()`
+ *
+ * @param {String|Object} [args...] enumeration values
+ * @return {SchemaType} this
+ */
+
+SchemaArray.prototype.enum = function() {
+ const instance = get(this, 'caster.instance');
+ if (instance !== 'String') {
+ throw new Error('`enum` can only be set on an array of strings, not ' + instance);
+ }
+ this.caster.enum.apply(this.caster, arguments);
+ return this;
+};
+
+/**
+ * Overrides the getters application for the population special-case
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @api private
+ */
+
+SchemaArray.prototype.applyGetters = function(value, scope) {
+ if (this.caster.options && this.caster.options.ref) {
+ // means the object id was populated
+ return value;
+ }
+
+ return SchemaType.prototype.applyGetters.call(this, value, scope);
+};
+
+/**
+ * Casts values for set().
+ *
+ * @param {Object} value
+ * @param {Document} doc document that triggers the casting
+ * @param {Boolean} init whether this is an initialization cast
+ * @api private
+ */
+
+SchemaArray.prototype.cast = function(value, doc, init) {
+ // lazy load
+ MongooseArray || (MongooseArray = require('../types').Array);
+
+ let i;
+ let l;
+
+ if (Array.isArray(value)) {
+ if (!value.length && doc) {
+ const indexes = doc.schema.indexedPaths();
+
+ for (i = 0, l = indexes.length; i < l; ++i) {
+ const pathIndex = indexes[i][0][this.path];
+ if (pathIndex === '2dsphere' || pathIndex === '2d') {
+ return;
+ }
+ }
+ }
+
+ if (!(value && value.isMongooseArray)) {
+ value = new MongooseArray(value, this.path, doc);
+ } else if (value && value.isMongooseArray) {
+ // We need to create a new array, otherwise change tracking will
+ // update the old doc (gh-4449)
+ value = new MongooseArray(value, this.path, doc);
+ }
+
+ if (this.caster && this.casterConstructor !== Mixed) {
+ try {
+ for (i = 0, l = value.length; i < l; i++) {
+ value[i] = this.caster.cast(value[i], doc, init);
+ }
+ } catch (e) {
+ // rethrow
+ throw new CastError('[' + e.kind + ']', util.inspect(value), this.path, e);
+ }
+ }
+
+ return value;
+ }
+ // gh-2442: if we're loading this from the db and its not an array, mark
+ // the whole array as modified.
+ if (!!doc && !!init) {
+ doc.markModified(this.path);
+ }
+ return this.cast([value], doc, init);
+};
+
+/*!
+ * Ignore
+ */
+
+SchemaArray.prototype.discriminator = function(name, schema) {
+ let arr = this; // eslint-disable-line consistent-this
+ while (arr.$isMongooseArray && !arr.$isMongooseDocumentArray) {
+ arr = arr.casterConstructor;
+ if (arr == null || typeof arr === 'function') {
+ throw new MongooseError('You can only add an embedded discriminator on ' +
+ 'a document array, ' + this.path + ' is a plain array');
+ }
+ }
+ return arr.discriminator(name, schema);
+};
+
+/**
+ * Casts values for queries.
+ *
+ * @param {String} $conditional
+ * @param {any} [value]
+ * @api private
+ */
+
+SchemaArray.prototype.castForQuery = function($conditional, value) {
+ let handler;
+ let val;
+
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with Array.');
+ }
+
+ val = handler.call(this, value);
+ } else {
+ val = $conditional;
+ let Constructor = this.casterConstructor;
+
+ if (val &&
+ Constructor.discriminators &&
+ Constructor.schema &&
+ Constructor.schema.options &&
+ Constructor.schema.options.discriminatorKey) {
+ if (typeof val[Constructor.schema.options.discriminatorKey] === 'string' &&
+ Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]]) {
+ Constructor = Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]];
+ } else {
+ const constructorByValue = getDiscriminatorByValue(Constructor, val[Constructor.schema.options.discriminatorKey]);
+ if (constructorByValue) {
+ Constructor = constructorByValue;
+ }
+ }
+ }
+
+ const proto = this.casterConstructor.prototype;
+ let method = proto && (proto.castForQuery || proto.cast);
+ if (!method && Constructor.castForQuery) {
+ method = Constructor.castForQuery;
+ }
+ const caster = this.caster;
+
+ if (Array.isArray(val)) {
+ this.setters.reverse().forEach(setter => {
+ val = setter.call(this, val, this);
+ });
+ val = val.map(function(v) {
+ if (utils.isObject(v) && v.$elemMatch) {
+ return v;
+ }
+ if (method) {
+ v = method.call(caster, v);
+ return v;
+ }
+ if (v != null) {
+ v = new Constructor(v);
+ return v;
+ }
+ return v;
+ });
+ } else if (method) {
+ val = method.call(caster, val);
+ } else if (val != null) {
+ val = new Constructor(val);
+ }
+ }
+
+ return val;
+};
+
+function cast$all(val) {
+ if (!Array.isArray(val)) {
+ val = [val];
+ }
+
+ val = val.map(function(v) {
+ if (utils.isObject(v)) {
+ const o = {};
+ o[this.path] = v;
+ return cast(this.casterConstructor.schema, o)[this.path];
+ }
+ return v;
+ }, this);
+
+ return this.castForQuery(val);
+}
+
+function cast$elemMatch(val) {
+ const keys = Object.keys(val);
+ const numKeys = keys.length;
+ let key;
+ let value;
+ for (let i = 0; i < numKeys; ++i) {
+ key = keys[i];
+ value = val[key];
+ if (key.indexOf('$') === 0 && value) {
+ val[key] = this.castForQuery(key, value);
+ }
+ }
+
+ return cast(this.casterConstructor.schema, val);
+}
+
+const handle = SchemaArray.prototype.$conditionalHandlers = {};
+
+handle.$all = cast$all;
+handle.$options = String;
+handle.$elemMatch = cast$elemMatch;
+handle.$geoIntersects = geospatial.cast$geoIntersects;
+handle.$or = handle.$and = function(val) {
+ if (!Array.isArray(val)) {
+ throw new TypeError('conditional $or/$and require array');
+ }
+
+ const ret = [];
+ for (let i = 0; i < val.length; ++i) {
+ ret.push(cast(this.casterConstructor.schema, val[i]));
+ }
+
+ return ret;
+};
+
+handle.$near =
+handle.$nearSphere = geospatial.cast$near;
+
+handle.$within =
+handle.$geoWithin = geospatial.cast$within;
+
+handle.$size =
+handle.$minDistance =
+handle.$maxDistance = castToNumber;
+
+handle.$exists = $exists;
+handle.$type = $type;
+
+handle.$eq =
+handle.$gt =
+handle.$gte =
+handle.$lt =
+handle.$lte =
+handle.$ne =
+handle.$nin =
+handle.$regex = SchemaArray.prototype.castForQuery;
+
+// `$in` is special because you can also include an empty array in the query
+// like `$in: [1, []]`, see gh-5913
+handle.$in = SchemaType.prototype.$conditionalHandlers.$in;
+
+/*!
+ * Module exports.
+ */
+
+module.exports = SchemaArray;
diff --git a/node_modules/mongoose/lib/schema/boolean.js b/node_modules/mongoose/lib/schema/boolean.js
new file mode 100644
index 0000000..4b2b3fa
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/boolean.js
@@ -0,0 +1,205 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const CastError = require('../error/cast');
+const SchemaType = require('../schematype');
+const castBoolean = require('../cast/boolean');
+const utils = require('../utils');
+
+/**
+ * Boolean SchemaType constructor.
+ *
+ * @param {String} path
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function SchemaBoolean(path, options) {
+ SchemaType.call(this, path, options, 'Boolean');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaBoolean.schemaName = 'Boolean';
+
+/*!
+ * Inherits from SchemaType.
+ */
+SchemaBoolean.prototype = Object.create(SchemaType.prototype);
+SchemaBoolean.prototype.constructor = SchemaBoolean;
+
+/*!
+ * ignore
+ */
+
+SchemaBoolean._cast = castBoolean;
+
+/**
+ * Get/set the function used to cast arbitrary values to booleans.
+ *
+ * ####Example:
+ *
+ * // Make Mongoose cast empty string '' to false.
+ * const original = mongoose.Schema.Boolean.cast();
+ * mongoose.Schema.Boolean.cast(v => {
+ * if (v === '') {
+ * return false;
+ * }
+ * return original(v);
+ * });
+ *
+ * // Or disable casting entirely
+ * mongoose.Schema.Boolean.cast(false);
+ *
+ * @param {Function} caster
+ * @return {Function}
+ * @function get
+ * @static
+ * @api public
+ */
+
+SchemaBoolean.cast = function cast(caster) {
+ if (arguments.length === 0) {
+ return this._cast;
+ }
+ if (caster === false) {
+ caster = v => {
+ if (v != null && typeof v !== 'boolean') {
+ throw new Error();
+ }
+ return v;
+ };
+ }
+ this._cast = caster;
+
+ return this._cast;
+};
+
+/*!
+ * ignore
+ */
+
+SchemaBoolean._checkRequired = v => v === true || v === false;
+
+/**
+ * Override the function the required validator uses to check whether a boolean
+ * passes the `required` check.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @function checkRequired
+ * @static
+ * @api public
+ */
+
+SchemaBoolean.checkRequired = SchemaType.checkRequired;
+
+/**
+ * Check if the given value satisfies a required validator. For a boolean
+ * to satisfy a required validator, it must be strictly equal to true or to
+ * false.
+ *
+ * @param {Any} value
+ * @return {Boolean}
+ * @api public
+ */
+
+SchemaBoolean.prototype.checkRequired = function(value) {
+ return this.constructor._checkRequired(value);
+};
+
+/**
+ * Configure which values get casted to `true`.
+ *
+ * ####Example:
+ *
+ * const M = mongoose.model('Test', new Schema({ b: Boolean }));
+ * new M({ b: 'affirmative' }).b; // undefined
+ * mongoose.Schema.Boolean.convertToTrue.add('affirmative');
+ * new M({ b: 'affirmative' }).b; // true
+ *
+ * @property convertToTrue
+ * @type Set
+ * @api public
+ */
+
+Object.defineProperty(SchemaBoolean, 'convertToTrue', {
+ get: () => castBoolean.convertToTrue,
+ set: v => { castBoolean.convertToTrue = v; }
+});
+
+/**
+ * Configure which values get casted to `false`.
+ *
+ * ####Example:
+ *
+ * const M = mongoose.model('Test', new Schema({ b: Boolean }));
+ * new M({ b: 'nay' }).b; // undefined
+ * mongoose.Schema.Types.Boolean.convertToFalse.add('nay');
+ * new M({ b: 'nay' }).b; // false
+ *
+ * @property convertToFalse
+ * @type Set
+ * @api public
+ */
+
+Object.defineProperty(SchemaBoolean, 'convertToFalse', {
+ get: () => castBoolean.convertToFalse,
+ set: v => { castBoolean.convertToFalse = v; }
+});
+
+/**
+ * Casts to boolean
+ *
+ * @param {Object} value
+ * @param {Object} model - this value is optional
+ * @api private
+ */
+
+SchemaBoolean.prototype.cast = function(value) {
+ try {
+ return this.constructor.cast()(value);
+ } catch (error) {
+ throw new CastError('Boolean', value, this.path);
+ }
+};
+
+SchemaBoolean.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {});
+
+/**
+ * Casts contents for queries.
+ *
+ * @param {String} $conditional
+ * @param {any} val
+ * @api private
+ */
+
+SchemaBoolean.prototype.castForQuery = function($conditional, val) {
+ let handler;
+ if (arguments.length === 2) {
+ handler = SchemaBoolean.$conditionalHandlers[$conditional];
+
+ if (handler) {
+ return handler.call(this, val);
+ }
+
+ return this._castForQuery(val);
+ }
+
+ return this._castForQuery($conditional);
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = SchemaBoolean;
diff --git a/node_modules/mongoose/lib/schema/buffer.js b/node_modules/mongoose/lib/schema/buffer.js
new file mode 100644
index 0000000..8bf6bc5
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/buffer.js
@@ -0,0 +1,250 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const handleBitwiseOperator = require('./operators/bitwise');
+const utils = require('../utils');
+
+const MongooseBuffer = require('../types/buffer');
+const SchemaType = require('../schematype');
+
+const Binary = MongooseBuffer.Binary;
+const CastError = SchemaType.CastError;
+let Document;
+
+/**
+ * Buffer SchemaType constructor
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function SchemaBuffer(key, options) {
+ SchemaType.call(this, key, options, 'Buffer');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaBuffer.schemaName = 'Buffer';
+
+/*!
+ * Inherits from SchemaType.
+ */
+SchemaBuffer.prototype = Object.create(SchemaType.prototype);
+SchemaBuffer.prototype.constructor = SchemaBuffer;
+
+/*!
+ * ignore
+ */
+
+SchemaBuffer._checkRequired = v => !!(v && v.length);
+
+/**
+ * Override the function the required validator uses to check whether a string
+ * passes the `required` check.
+ *
+ * ####Example:
+ *
+ * // Allow empty strings to pass `required` check
+ * mongoose.Schema.Types.String.checkRequired(v => v != null);
+ *
+ * const M = mongoose.model({ buf: { type: Buffer, required: true } });
+ * new M({ buf: Buffer.from('') }).validateSync(); // validation passes!
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @function checkRequired
+ * @static
+ * @api public
+ */
+
+SchemaBuffer.checkRequired = SchemaType.checkRequired;
+
+/**
+ * Check if the given value satisfies a required validator. To satisfy a
+ * required validator, a buffer must not be null or undefined and have
+ * non-zero length.
+ *
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
+ */
+
+SchemaBuffer.prototype.checkRequired = function(value, doc) {
+ if (SchemaType._isRef(this, value, doc, true)) {
+ return !!value;
+ }
+ return this.constructor._checkRequired(value);
+};
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @param {Document} doc document that triggers the casting
+ * @param {Boolean} init
+ * @api private
+ */
+
+SchemaBuffer.prototype.cast = function(value, doc, init) {
+ let ret;
+ if (SchemaType._isRef(this, value, doc, init)) {
+ // wait! we may need to cast this to a document
+
+ if (value === null || value === undefined) {
+ return value;
+ }
+
+ // lazy load
+ Document || (Document = require('./../document'));
+
+ if (value instanceof Document) {
+ value.$__.wasPopulated = true;
+ return value;
+ }
+
+ // setting a populated path
+ if (Buffer.isBuffer(value)) {
+ return value;
+ } else if (!utils.isObject(value)) {
+ throw new CastError('buffer', value, this.path);
+ }
+
+ // Handle the case where user directly sets a populated
+ // path to a plain object; cast to the Model used in
+ // the population query.
+ const path = doc.$__fullPath(this.path);
+ const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
+ const pop = owner.populated(path, true);
+ ret = new pop.options.model(value);
+ ret.$__.wasPopulated = true;
+ return ret;
+ }
+
+ // documents
+ if (value && value._id) {
+ value = value._id;
+ }
+
+ if (value && value.isMongooseBuffer) {
+ return value;
+ }
+
+ if (Buffer.isBuffer(value)) {
+ if (!value || !value.isMongooseBuffer) {
+ value = new MongooseBuffer(value, [this.path, doc]);
+ if (this.options.subtype != null) {
+ value._subtype = this.options.subtype;
+ }
+ }
+ return value;
+ }
+
+ if (value instanceof Binary) {
+ ret = new MongooseBuffer(value.value(true), [this.path, doc]);
+ if (typeof value.sub_type !== 'number') {
+ throw new CastError('buffer', value, this.path);
+ }
+ ret._subtype = value.sub_type;
+ return ret;
+ }
+
+ if (value === null) {
+ return value;
+ }
+
+
+ const type = typeof value;
+ if (
+ type === 'string' || type === 'number' || Array.isArray(value) ||
+ (type === 'object' && value.type === 'Buffer' && Array.isArray(value.data)) // gh-6863
+ ) {
+ if (type === 'number') {
+ value = [value];
+ }
+ ret = new MongooseBuffer(value, [this.path, doc]);
+ if (this.options.subtype != null) {
+ ret._subtype = this.options.subtype;
+ }
+ return ret;
+ }
+
+ throw new CastError('buffer', value, this.path);
+};
+
+/**
+ * Sets the default [subtype](https://studio3t.com/whats-new/best-practices-uuid-mongodb/)
+ * for this buffer. You can find a [list of allowed subtypes here](http://api.mongodb.com/python/current/api/bson/binary.html).
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ uuid: { type: Buffer, subtype: 4 });
+ * var M = db.model('M', s);
+ * var m = new M({ uuid: 'test string' });
+ * m.uuid._subtype; // 4
+ *
+ * @param {Number} subtype the default subtype
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaBuffer.prototype.subtype = function(subtype) {
+ this.options.subtype = subtype;
+ return this;
+};
+
+/*!
+ * ignore
+ */
+function handleSingle(val) {
+ return this.castForQuery(val);
+}
+
+SchemaBuffer.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $bitsAllClear: handleBitwiseOperator,
+ $bitsAnyClear: handleBitwiseOperator,
+ $bitsAllSet: handleBitwiseOperator,
+ $bitsAnySet: handleBitwiseOperator,
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle
+ });
+
+/**
+ * Casts contents for queries.
+ *
+ * @param {String} $conditional
+ * @param {any} [value]
+ * @api private
+ */
+
+SchemaBuffer.prototype.castForQuery = function($conditional, val) {
+ let handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with Buffer.');
+ }
+ return handler.call(this, val);
+ }
+ val = $conditional;
+ const casted = this._castForQuery(val);
+ return casted ? casted.toObject({ transform: false, virtuals: false }) : casted;
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = SchemaBuffer;
diff --git a/node_modules/mongoose/lib/schema/date.js b/node_modules/mongoose/lib/schema/date.js
new file mode 100644
index 0000000..6e06cb7
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/date.js
@@ -0,0 +1,346 @@
+/*!
+ * Module requirements.
+ */
+
+'use strict';
+
+const MongooseError = require('../error');
+const castDate = require('../cast/date');
+const utils = require('../utils');
+
+const SchemaType = require('../schematype');
+
+const CastError = SchemaType.CastError;
+
+/**
+ * Date SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function SchemaDate(key, options) {
+ SchemaType.call(this, key, options, 'Date');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaDate.schemaName = 'Date';
+
+/*!
+ * Inherits from SchemaType.
+ */
+SchemaDate.prototype = Object.create(SchemaType.prototype);
+SchemaDate.prototype.constructor = SchemaDate;
+
+/*!
+ * ignore
+ */
+
+SchemaDate._cast = castDate;
+
+/**
+ * Get/set the function used to cast arbitrary values to dates.
+ *
+ * ####Example:
+ *
+ * // Mongoose converts empty string '' into `null` for date types. You
+ * // can create a custom caster to disable it.
+ * const original = mongoose.Schema.Types.Date.cast();
+ * mongoose.Schema.Types.Date.cast(v => {
+ * assert.ok(v !== '');
+ * return original(v);
+ * });
+ *
+ * // Or disable casting entirely
+ * mongoose.Schema.Types.Date.cast(false);
+ *
+ * @param {Function} caster
+ * @return {Function}
+ * @function get
+ * @static
+ * @api public
+ */
+
+SchemaDate.cast = function cast(caster) {
+ if (arguments.length === 0) {
+ return this._cast;
+ }
+ if (caster === false) {
+ caster = v => {
+ if (v != null && !(v instanceof Date)) {
+ throw new Error();
+ }
+ return v;
+ };
+ }
+ this._cast = caster;
+
+ return this._cast;
+};
+
+/**
+ * Declares a TTL index (rounded to the nearest second) for _Date_ types only.
+ *
+ * This sets the `expireAfterSeconds` index option available in MongoDB >= 2.1.2.
+ * This index type is only compatible with Date types.
+ *
+ * ####Example:
+ *
+ * // expire in 24 hours
+ * new Schema({ createdAt: { type: Date, expires: 60*60*24 }});
+ *
+ * `expires` utilizes the `ms` module from [guille](https://github.com/guille/) allowing us to use a friendlier syntax:
+ *
+ * ####Example:
+ *
+ * // expire in 24 hours
+ * new Schema({ createdAt: { type: Date, expires: '24h' }});
+ *
+ * // expire in 1.5 hours
+ * new Schema({ createdAt: { type: Date, expires: '1.5h' }});
+ *
+ * // expire in 7 days
+ * var schema = new Schema({ createdAt: Date });
+ * schema.path('createdAt').expires('7d');
+ *
+ * @param {Number|String} when
+ * @added 3.0.0
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaDate.prototype.expires = function(when) {
+ if (!this._index || this._index.constructor.name !== 'Object') {
+ this._index = {};
+ }
+
+ this._index.expires = when;
+ utils.expires(this._index);
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+SchemaDate._checkRequired = v => v instanceof Date;
+
+/**
+ * Override the function the required validator uses to check whether a string
+ * passes the `required` check.
+ *
+ * ####Example:
+ *
+ * // Allow empty strings to pass `required` check
+ * mongoose.Schema.Types.String.checkRequired(v => v != null);
+ *
+ * const M = mongoose.model({ str: { type: String, required: true } });
+ * new M({ str: '' }).validateSync(); // `null`, validation passes!
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @function checkRequired
+ * @static
+ * @api public
+ */
+
+SchemaDate.checkRequired = SchemaType.checkRequired;
+
+/**
+ * Check if the given value satisfies a required validator. To satisfy
+ * a required validator, the given value must be an instance of `Date`.
+ *
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
+ */
+
+SchemaDate.prototype.checkRequired = function(value, doc) {
+ if (SchemaType._isRef(this, value, doc, true)) {
+ return !!value;
+ }
+ return this.constructor._checkRequired(value);
+};
+
+/**
+ * Sets a minimum date validator.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ d: { type: Date, min: Date('1970-01-01') })
+ * var M = db.model('M', s)
+ * var m = new M({ d: Date('1969-12-31') })
+ * m.save(function (err) {
+ * console.error(err) // validator error
+ * m.d = Date('2014-12-08');
+ * m.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MIN} token which will be replaced with the invalid value
+ * var min = [Date('1970-01-01'), 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).'];
+ * var schema = new Schema({ d: { type: Date, min: min })
+ * var M = mongoose.model('M', schema);
+ * var s= new M({ d: Date('1969-12-31') });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `d` (1969-12-31) is before the limit (1970-01-01).
+ * })
+ *
+ * @param {Date} value minimum date
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaDate.prototype.min = function(value, message) {
+ if (this.minValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.minValidator;
+ }, this);
+ }
+
+ if (value) {
+ let msg = message || MongooseError.messages.Date.min;
+ msg = msg.replace(/{MIN}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
+ const _this = this;
+ this.validators.push({
+ validator: this.minValidator = function(val) {
+ const min = (value === Date.now ? value() : _this.cast(value));
+ return val === null || val.valueOf() >= min.valueOf();
+ },
+ message: msg,
+ type: 'min',
+ min: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Sets a maximum date validator.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ d: { type: Date, max: Date('2014-01-01') })
+ * var M = db.model('M', s)
+ * var m = new M({ d: Date('2014-12-08') })
+ * m.save(function (err) {
+ * console.error(err) // validator error
+ * m.d = Date('2013-12-31');
+ * m.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MAX} token which will be replaced with the invalid value
+ * var max = [Date('2014-01-01'), 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).'];
+ * var schema = new Schema({ d: { type: Date, max: max })
+ * var M = mongoose.model('M', schema);
+ * var s= new M({ d: Date('2014-12-08') });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `d` (2014-12-08) exceeds the limit (2014-01-01).
+ * })
+ *
+ * @param {Date} maximum date
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaDate.prototype.max = function(value, message) {
+ if (this.maxValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.maxValidator;
+ }, this);
+ }
+
+ if (value) {
+ let msg = message || MongooseError.messages.Date.max;
+ msg = msg.replace(/{MAX}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
+ const _this = this;
+ this.validators.push({
+ validator: this.maxValidator = function(val) {
+ const max = (value === Date.now ? value() : _this.cast(value));
+ return val === null || val.valueOf() <= max.valueOf();
+ },
+ message: msg,
+ type: 'max',
+ max: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Casts to date
+ *
+ * @param {Object} value to cast
+ * @api private
+ */
+
+SchemaDate.prototype.cast = function(value) {
+ const _castDate = this.constructor.cast();
+ try {
+ return _castDate(value);
+ } catch (error) {
+ throw new CastError('date', value, this.path);
+ }
+};
+
+/*!
+ * Date Query casting.
+ *
+ * @api private
+ */
+
+function handleSingle(val) {
+ return this.cast(val);
+}
+
+SchemaDate.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle
+ });
+
+
+/**
+ * Casts contents for queries.
+ *
+ * @param {String} $conditional
+ * @param {any} [value]
+ * @api private
+ */
+
+SchemaDate.prototype.castForQuery = function($conditional, val) {
+ if (arguments.length !== 2) {
+ return this._castForQuery($conditional);
+ }
+
+ const handler = this.$conditionalHandlers[$conditional];
+
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with Date.');
+ }
+
+ return handler.call(this, val);
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = SchemaDate;
diff --git a/node_modules/mongoose/lib/schema/decimal128.js b/node_modules/mongoose/lib/schema/decimal128.js
new file mode 100644
index 0000000..0338905
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/decimal128.js
@@ -0,0 +1,201 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const SchemaType = require('../schematype');
+const CastError = SchemaType.CastError;
+const Decimal128Type = require('../types/decimal128');
+const castDecimal128 = require('../cast/decimal128');
+const utils = require('../utils');
+
+let Document;
+
+/**
+ * Decimal128 SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function Decimal128(key, options) {
+ SchemaType.call(this, key, options, 'Decimal128');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+Decimal128.schemaName = 'Decimal128';
+
+/*!
+ * Inherits from SchemaType.
+ */
+Decimal128.prototype = Object.create(SchemaType.prototype);
+Decimal128.prototype.constructor = Decimal128;
+
+/*!
+ * ignore
+ */
+
+Decimal128._cast = castDecimal128;
+
+/**
+ * Get/set the function used to cast arbitrary values to decimals.
+ *
+ * ####Example:
+ *
+ * // Make Mongoose only refuse to cast numbers as decimal128
+ * const original = mongoose.Schema.Types.Decimal128.cast();
+ * mongoose.Decimal128.cast(v => {
+ * assert.ok(typeof v !== 'number');
+ * return original(v);
+ * });
+ *
+ * // Or disable casting entirely
+ * mongoose.Decimal128.cast(false);
+ *
+ * @param {Function} [caster]
+ * @return {Function}
+ * @function get
+ * @static
+ * @api public
+ */
+
+Decimal128.cast = function cast(caster) {
+ if (arguments.length === 0) {
+ return this._cast;
+ }
+ if (caster === false) {
+ caster = v => {
+ if (v != null && !(v instanceof Decimal128Type)) {
+ throw new Error();
+ }
+ return v;
+ };
+ }
+ this._cast = caster;
+
+ return this._cast;
+};
+
+/*!
+ * ignore
+ */
+
+Decimal128._checkRequired = v => v instanceof Decimal128Type;
+
+/**
+ * Override the function the required validator uses to check whether a string
+ * passes the `required` check.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @function checkRequired
+ * @static
+ * @api public
+ */
+
+Decimal128.checkRequired = SchemaType.checkRequired;
+
+/**
+ * Check if the given value satisfies a required validator.
+ *
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
+ */
+
+Decimal128.prototype.checkRequired = function checkRequired(value, doc) {
+ if (SchemaType._isRef(this, value, doc, true)) {
+ return !!value;
+ }
+ return this.constructor._checkRequired(value);
+};
+
+/**
+ * Casts to Decimal128
+ *
+ * @param {Object} value
+ * @param {Object} doc
+ * @param {Boolean} init whether this is an initialization cast
+ * @api private
+ */
+
+Decimal128.prototype.cast = function(value, doc, init) {
+ if (SchemaType._isRef(this, value, doc, init)) {
+ // wait! we may need to cast this to a document
+
+ if (value === null || value === undefined) {
+ return value;
+ }
+
+ // lazy load
+ Document || (Document = require('./../document'));
+
+ if (value instanceof Document) {
+ value.$__.wasPopulated = true;
+ return value;
+ }
+
+ // setting a populated path
+ if (value instanceof Decimal128Type) {
+ return value;
+ } else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
+ throw new CastError('Decimal128', value, this.path);
+ }
+
+ // Handle the case where user directly sets a populated
+ // path to a plain object; cast to the Model used in
+ // the population query.
+ const path = doc.$__fullPath(this.path);
+ const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
+ const pop = owner.populated(path, true);
+ let ret = value;
+ if (!doc.$__.populated ||
+ !doc.$__.populated[path] ||
+ !doc.$__.populated[path].options ||
+ !doc.$__.populated[path].options.options ||
+ !doc.$__.populated[path].options.options.lean) {
+ ret = new pop.options.model(value);
+ ret.$__.wasPopulated = true;
+ }
+
+ return ret;
+ }
+
+ const _castDecimal128 = this.constructor.cast();
+ try {
+ return _castDecimal128(value);
+ } catch (error) {
+ throw new CastError('Decimal128', value, this.path);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+function handleSingle(val) {
+ return this.cast(val);
+}
+
+Decimal128.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle
+ });
+
+/*!
+ * Module exports.
+ */
+
+module.exports = Decimal128;
diff --git a/node_modules/mongoose/lib/schema/documentarray.js b/node_modules/mongoose/lib/schema/documentarray.js
new file mode 100644
index 0000000..ddb0713
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/documentarray.js
@@ -0,0 +1,460 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const ArrayType = require('./array');
+const CastError = require('../error/cast');
+const EventEmitter = require('events').EventEmitter;
+const SchemaType = require('../schematype');
+const discriminator = require('../helpers/model/discriminator');
+const util = require('util');
+const utils = require('../utils');
+const getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
+
+let MongooseDocumentArray;
+let Subdocument;
+
+/**
+ * SubdocsArray SchemaType constructor
+ *
+ * @param {String} key
+ * @param {Schema} schema
+ * @param {Object} options
+ * @inherits SchemaArray
+ * @api public
+ */
+
+function DocumentArray(key, schema, options, schemaOptions) {
+ const EmbeddedDocument = _createConstructor(schema, options);
+ EmbeddedDocument.prototype.$basePath = key;
+
+ ArrayType.call(this, key, EmbeddedDocument, options);
+
+ this.schema = schema;
+ this.schemaOptions = schemaOptions || {};
+ this.$isMongooseDocumentArray = true;
+ this.Constructor = EmbeddedDocument;
+
+ EmbeddedDocument.base = schema.base;
+
+ const fn = this.defaultValue;
+
+ if (!('defaultValue' in this) || fn !== void 0) {
+ this.default(function() {
+ let arr = fn.call(this);
+ if (!Array.isArray(arr)) {
+ arr = [arr];
+ }
+ // Leave it up to `cast()` to convert this to a documentarray
+ return arr;
+ });
+ }
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+DocumentArray.schemaName = 'DocumentArray';
+
+/*!
+ * Inherits from ArrayType.
+ */
+DocumentArray.prototype = Object.create(ArrayType.prototype);
+DocumentArray.prototype.constructor = DocumentArray;
+
+/*!
+ * Ignore
+ */
+
+function _createConstructor(schema, options) {
+ Subdocument || (Subdocument = require('../types/embedded'));
+
+ // compile an embedded document for this schema
+ function EmbeddedDocument() {
+ Subdocument.apply(this, arguments);
+
+ this.$session(this.ownerDocument().$session());
+ }
+
+ EmbeddedDocument.prototype = Object.create(Subdocument.prototype);
+ EmbeddedDocument.prototype.$__setSchema(schema);
+ EmbeddedDocument.schema = schema;
+ EmbeddedDocument.prototype.constructor = EmbeddedDocument;
+ EmbeddedDocument.$isArraySubdocument = true;
+ EmbeddedDocument.events = new EventEmitter();
+
+ // apply methods
+ for (const i in schema.methods) {
+ EmbeddedDocument.prototype[i] = schema.methods[i];
+ }
+
+ // apply statics
+ for (const i in schema.statics) {
+ EmbeddedDocument[i] = schema.statics[i];
+ }
+
+ for (const i in EventEmitter.prototype) {
+ EmbeddedDocument[i] = EventEmitter.prototype[i];
+ }
+
+ EmbeddedDocument.options = options;
+
+ return EmbeddedDocument;
+}
+
+/*!
+ * Ignore
+ */
+
+DocumentArray.prototype.discriminator = function(name, schema) {
+ if (typeof name === 'function') {
+ name = utils.getFunctionName(name);
+ }
+
+ schema = discriminator(this.casterConstructor, name, schema);
+
+ const EmbeddedDocument = _createConstructor(schema);
+ EmbeddedDocument.baseCasterConstructor = this.casterConstructor;
+
+ try {
+ Object.defineProperty(EmbeddedDocument, 'name', {
+ value: name
+ });
+ } catch (error) {
+ // Ignore error, only happens on old versions of node
+ }
+
+ this.casterConstructor.discriminators[name] = EmbeddedDocument;
+
+ return this.casterConstructor.discriminators[name];
+};
+
+/**
+ * Performs local validations first, then validations on each embedded doc
+ *
+ * @api private
+ */
+
+DocumentArray.prototype.doValidate = function(array, fn, scope, options) {
+ // lazy load
+ MongooseDocumentArray || (MongooseDocumentArray = require('../types/documentarray'));
+
+ const _this = this;
+ try {
+ SchemaType.prototype.doValidate.call(this, array, cb, scope);
+ } catch (err) {
+ err.$isArrayValidatorError = true;
+ return fn(err);
+ }
+
+ function cb(err) {
+ if (err) {
+ err.$isArrayValidatorError = true;
+ return fn(err);
+ }
+
+ let count = array && array.length;
+ let error;
+
+ if (!count) {
+ return fn();
+ }
+ if (options && options.updateValidator) {
+ return fn();
+ }
+ if (!array.isMongooseDocumentArray) {
+ array = new MongooseDocumentArray(array, _this.path, scope);
+ }
+
+ // handle sparse arrays, do not use array.forEach which does not
+ // iterate over sparse elements yet reports array.length including
+ // them :(
+
+ function callback(err) {
+ if (err != null) {
+ error = err;
+ if (error.name !== 'ValidationError') {
+ error.$isArrayValidatorError = true;
+ }
+ }
+ --count || fn(error);
+ }
+
+ for (let i = 0, len = count; i < len; ++i) {
+ // sidestep sparse entries
+ let doc = array[i];
+ if (doc == null) {
+ --count || fn(error);
+ continue;
+ }
+
+ // If you set the array index directly, the doc might not yet be
+ // a full fledged mongoose subdoc, so make it into one.
+ if (!(doc instanceof Subdocument)) {
+ doc = array[i] = new _this.casterConstructor(doc, array, undefined,
+ undefined, i);
+ }
+
+ doc.$__validate(callback);
+ }
+ }
+};
+
+/**
+ * Performs local validations first, then validations on each embedded doc.
+ *
+ * ####Note:
+ *
+ * This method ignores the asynchronous validators.
+ *
+ * @return {MongooseError|undefined}
+ * @api private
+ */
+
+DocumentArray.prototype.doValidateSync = function(array, scope) {
+ const schemaTypeError = SchemaType.prototype.doValidateSync.call(this, array, scope);
+ if (schemaTypeError != null) {
+ schemaTypeError.$isArrayValidatorError = true;
+ return schemaTypeError;
+ }
+
+ const count = array && array.length;
+ let resultError = null;
+
+ if (!count) {
+ return;
+ }
+
+ // handle sparse arrays, do not use array.forEach which does not
+ // iterate over sparse elements yet reports array.length including
+ // them :(
+
+ for (let i = 0, len = count; i < len; ++i) {
+ // sidestep sparse entries
+ let doc = array[i];
+ if (!doc) {
+ continue;
+ }
+
+ // If you set the array index directly, the doc might not yet be
+ // a full fledged mongoose subdoc, so make it into one.
+ if (!(doc instanceof Subdocument)) {
+ doc = array[i] = new this.casterConstructor(doc, array, undefined,
+ undefined, i);
+ }
+
+ const subdocValidateError = doc.validateSync();
+
+ if (subdocValidateError && resultError == null) {
+ resultError = subdocValidateError;
+ }
+ }
+
+ return resultError;
+};
+
+/*!
+ * ignore
+ */
+
+DocumentArray.prototype.getDefault = function(scope) {
+ let ret = typeof this.defaultValue === 'function'
+ ? this.defaultValue.call(scope)
+ : this.defaultValue;
+
+ if (ret == null) {
+ return ret;
+ }
+
+ // lazy load
+ MongooseDocumentArray || (MongooseDocumentArray = require('../types/documentarray'));
+
+ if (!Array.isArray(ret)) {
+ ret = [ret];
+ }
+
+ ret = new MongooseDocumentArray(ret, this.path, scope);
+ const _parent = ret._parent;
+ ret._parent = null;
+
+ for (let i = 0; i < ret.length; ++i) {
+ ret[i] = new this.Constructor(ret[i], ret, undefined,
+ undefined, i);
+ }
+
+ ret._parent = _parent;
+
+ return ret;
+};
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @param {Document} document that triggers the casting
+ * @api private
+ */
+
+DocumentArray.prototype.cast = function(value, doc, init, prev, options) {
+ // lazy load
+ MongooseDocumentArray || (MongooseDocumentArray = require('../types/documentarray'));
+
+ let selected;
+ let subdoc;
+ let i;
+ const _opts = { transform: false, virtuals: false };
+
+ if (!Array.isArray(value)) {
+ // gh-2442 mark whole array as modified if we're initializing a doc from
+ // the db and the path isn't an array in the document
+ if (!!doc && init) {
+ doc.markModified(this.path);
+ }
+ return this.cast([value], doc, init, prev);
+ }
+
+ if (!(value && value.isMongooseDocumentArray) &&
+ (!options || !options.skipDocumentArrayCast)) {
+ value = new MongooseDocumentArray(value, this.path, doc);
+ _clearListeners(prev);
+ } else if (value && value.isMongooseDocumentArray) {
+ // We need to create a new array, otherwise change tracking will
+ // update the old doc (gh-4449)
+ value = new MongooseDocumentArray(value, this.path, doc);
+ }
+
+ i = value.length;
+
+ while (i--) {
+ if (!value[i]) {
+ continue;
+ }
+
+ let Constructor = this.casterConstructor;
+ if (Constructor.discriminators &&
+ Constructor.schema &&
+ Constructor.schema.options &&
+ typeof value[i][Constructor.schema.options.discriminatorKey] === 'string') {
+ if (Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]]) {
+ Constructor = Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]];
+ } else {
+ const constructorByValue = getDiscriminatorByValue(Constructor, value[i][Constructor.schema.options.discriminatorKey]);
+ if (constructorByValue) {
+ Constructor = constructorByValue;
+ }
+ }
+ }
+
+ // Check if the document has a different schema (re gh-3701)
+ if ((value[i].$__) &&
+ value[i].schema !== Constructor.schema) {
+ value[i] = value[i].toObject({ transform: false, virtuals: false });
+ }
+ if (!(value[i] instanceof Subdocument) && value[i]) {
+ if (init) {
+ if (doc) {
+ selected || (selected = scopePaths(this, doc.$__.selected, init));
+ } else {
+ selected = true;
+ }
+
+ subdoc = new Constructor(null, value, true, selected, i);
+ value[i] = subdoc.init(value[i]);
+ } else {
+ if (prev && (subdoc = prev.id(value[i]._id))) {
+ subdoc = prev.id(value[i]._id);
+ }
+
+ if (prev && subdoc && utils.deepEqual(subdoc.toObject(_opts), value[i])) {
+ // handle resetting doc with existing id and same data
+ subdoc.set(value[i]);
+ // if set() is hooked it will have no return value
+ // see gh-746
+ value[i] = subdoc;
+ } else {
+ try {
+ subdoc = new Constructor(value[i], value, undefined,
+ undefined, i);
+ // if set() is hooked it will have no return value
+ // see gh-746
+ value[i] = subdoc;
+ } catch (error) {
+ // Make sure we don't leave listeners dangling because `value`
+ // won't get back up to the schema type. See gh-6723
+ _clearListeners(value);
+ const valueInErrorMessage = util.inspect(value[i]);
+ throw new CastError('embedded', valueInErrorMessage,
+ value._path, error);
+ }
+ }
+ }
+ }
+ }
+
+ return value;
+};
+
+/*!
+ * Removes listeners from parent
+ */
+
+function _clearListeners(arr) {
+ if (arr == null || arr._parent == null) {
+ return;
+ }
+
+ for (const key in arr._handlers) {
+ arr._parent.removeListener(key, arr._handlers[key]);
+ }
+}
+
+/*!
+ * Scopes paths selected in a query to this array.
+ * Necessary for proper default application of subdocument values.
+ *
+ * @param {DocumentArray} array - the array to scope `fields` paths
+ * @param {Object|undefined} fields - the root fields selected in the query
+ * @param {Boolean|undefined} init - if we are being created part of a query result
+ */
+
+function scopePaths(array, fields, init) {
+ if (!(init && fields)) {
+ return undefined;
+ }
+
+ const path = array.path + '.';
+ const keys = Object.keys(fields);
+ let i = keys.length;
+ const selected = {};
+ let hasKeys;
+ let key;
+ let sub;
+
+ while (i--) {
+ key = keys[i];
+ if (key.indexOf(path) === 0) {
+ sub = key.substring(path.length);
+ if (sub === '$') {
+ continue;
+ }
+ if (sub.indexOf('$.') === 0) {
+ sub = sub.substr(2);
+ }
+ hasKeys || (hasKeys = true);
+ selected[sub] = fields[key];
+ }
+ }
+
+ return hasKeys && selected || undefined;
+}
+
+/*!
+ * Module exports.
+ */
+
+module.exports = DocumentArray;
diff --git a/node_modules/mongoose/lib/schema/embedded.js b/node_modules/mongoose/lib/schema/embedded.js
new file mode 100644
index 0000000..ab08fd8
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/embedded.js
@@ -0,0 +1,320 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const CastError = require('../error/cast');
+const EventEmitter = require('events').EventEmitter;
+const ObjectExpectedError = require('../error/objectExpected');
+const SchemaType = require('../schematype');
+const $exists = require('./operators/exists');
+const castToNumber = require('./operators/helpers').castToNumber;
+const discriminator = require('../helpers/model/discriminator');
+const geospatial = require('./operators/geospatial');
+const get = require('../helpers/get');
+const getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
+const internalToObjectOptions = require('../options').internalToObjectOptions;
+
+let Subdocument;
+
+module.exports = Embedded;
+
+/**
+ * Sub-schema schematype constructor
+ *
+ * @param {Schema} schema
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function Embedded(schema, path, options) {
+ this.caster = _createConstructor(schema);
+ this.caster.path = path;
+ this.caster.prototype.$basePath = path;
+ this.schema = schema;
+ this.$isSingleNested = true;
+ SchemaType.call(this, path, options, 'Embedded');
+}
+
+/*!
+ * ignore
+ */
+
+Embedded.prototype = Object.create(SchemaType.prototype);
+
+/*!
+ * ignore
+ */
+
+function _createConstructor(schema) {
+ // lazy load
+ Subdocument || (Subdocument = require('../types/subdocument'));
+
+ const _embedded = function SingleNested(value, path, parent) {
+ const _this = this;
+
+ this.$parent = parent;
+ Subdocument.apply(this, arguments);
+
+ this.$session(this.ownerDocument().$session());
+
+ if (parent) {
+ parent.on('save', function() {
+ _this.emit('save', _this);
+ _this.constructor.emit('save', _this);
+ });
+
+ parent.on('isNew', function(val) {
+ _this.isNew = val;
+ _this.emit('isNew', val);
+ _this.constructor.emit('isNew', val);
+ });
+ }
+ };
+ _embedded.prototype = Object.create(Subdocument.prototype);
+ _embedded.prototype.$__setSchema(schema);
+ _embedded.prototype.constructor = _embedded;
+ _embedded.schema = schema;
+ _embedded.$isSingleNested = true;
+ _embedded.events = new EventEmitter();
+ _embedded.prototype.toBSON = function() {
+ return this.toObject(internalToObjectOptions);
+ };
+
+ // apply methods
+ for (const i in schema.methods) {
+ _embedded.prototype[i] = schema.methods[i];
+ }
+
+ // apply statics
+ for (const i in schema.statics) {
+ _embedded[i] = schema.statics[i];
+ }
+
+ for (const i in EventEmitter.prototype) {
+ _embedded[i] = EventEmitter.prototype[i];
+ }
+
+ return _embedded;
+}
+
+/*!
+ * Special case for when users use a common location schema to represent
+ * locations for use with $geoWithin.
+ * https://docs.mongodb.org/manual/reference/operator/query/geoWithin/
+ *
+ * @param {Object} val
+ * @api private
+ */
+
+Embedded.prototype.$conditionalHandlers.$geoWithin = function handle$geoWithin(val) {
+ return { $geometry: this.castForQuery(val.$geometry) };
+};
+
+/*!
+ * ignore
+ */
+
+Embedded.prototype.$conditionalHandlers.$near =
+Embedded.prototype.$conditionalHandlers.$nearSphere = geospatial.cast$near;
+
+Embedded.prototype.$conditionalHandlers.$within =
+Embedded.prototype.$conditionalHandlers.$geoWithin = geospatial.cast$within;
+
+Embedded.prototype.$conditionalHandlers.$geoIntersects =
+ geospatial.cast$geoIntersects;
+
+Embedded.prototype.$conditionalHandlers.$minDistance = castToNumber;
+Embedded.prototype.$conditionalHandlers.$maxDistance = castToNumber;
+
+Embedded.prototype.$conditionalHandlers.$exists = $exists;
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @api private
+ */
+
+Embedded.prototype.cast = function(val, doc, init, priorVal) {
+ if (val && val.$isSingleNested) {
+ return val;
+ }
+
+ if (val != null && (typeof val !== 'object' || Array.isArray(val))) {
+ throw new ObjectExpectedError(this.path, val);
+ }
+
+ let Constructor = this.caster;
+ const discriminatorKey = Constructor.schema.options.discriminatorKey;
+ if (val != null &&
+ Constructor.discriminators &&
+ typeof val[discriminatorKey] === 'string') {
+ if (Constructor.discriminators[val[discriminatorKey]]) {
+ Constructor = Constructor.discriminators[val[discriminatorKey]];
+ } else {
+ const constructorByValue = getDiscriminatorByValue(Constructor, val[discriminatorKey]);
+ if (constructorByValue) {
+ Constructor = constructorByValue;
+ }
+ }
+ }
+
+ let subdoc;
+
+ // Only pull relevant selected paths and pull out the base path
+ const parentSelected = get(doc, '$__.selected', {});
+ const path = this.path;
+ const selected = Object.keys(parentSelected).reduce((obj, key) => {
+ if (key.startsWith(path + '.')) {
+ obj[key.substr(path.length + 1)] = parentSelected[key];
+ }
+ return obj;
+ }, {});
+
+ if (init) {
+ subdoc = new Constructor(void 0, selected, doc);
+ subdoc.init(val);
+ } else {
+ if (Object.keys(val).length === 0) {
+ return new Constructor({}, selected, doc);
+ }
+
+ return new Constructor(val, selected, doc, undefined, { priorDoc: priorVal });
+ }
+
+ return subdoc;
+};
+
+/**
+ * Casts contents for query
+ *
+ * @param {string} [$conditional] optional query operator (like `$eq` or `$in`)
+ * @param {any} value
+ * @api private
+ */
+
+Embedded.prototype.castForQuery = function($conditional, val) {
+ let handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional);
+ }
+ return handler.call(this, val);
+ }
+ val = $conditional;
+ if (val == null) {
+ return val;
+ }
+
+ if (this.options.runSetters) {
+ val = this._applySetters(val);
+ }
+
+ let Constructor = this.caster;
+ const discriminatorKey = Constructor.schema.options.discriminatorKey;
+ if (val != null &&
+ Constructor.discriminators &&
+ typeof val[discriminatorKey] === 'string') {
+ if (Constructor.discriminators[val[discriminatorKey]]) {
+ Constructor = Constructor.discriminators[val[discriminatorKey]];
+ } else {
+ const constructorByValue = getDiscriminatorByValue(Constructor, val[discriminatorKey]);
+ if (constructorByValue) {
+ Constructor = constructorByValue;
+ }
+ }
+ }
+
+ try {
+ val = new Constructor(val);
+ } catch (error) {
+ // Make sure we always wrap in a CastError (gh-6803)
+ if (!(error instanceof CastError)) {
+ throw new CastError('Embedded', val, this.path, error);
+ }
+ throw error;
+ }
+ return val;
+};
+
+/**
+ * Async validation on this single nested doc.
+ *
+ * @api private
+ */
+
+Embedded.prototype.doValidate = function(value, fn, scope, options) {
+ let Constructor = this.caster;
+ const discriminatorKey = Constructor.schema.options.discriminatorKey;
+ if (value != null &&
+ Constructor.discriminators &&
+ typeof value[discriminatorKey] === 'string') {
+ if (Constructor.discriminators[value[discriminatorKey]]) {
+ Constructor = Constructor.discriminators[value[discriminatorKey]];
+ } else {
+ const constructorByValue = getDiscriminatorByValue(Constructor, value[discriminatorKey]);
+ if (constructorByValue) {
+ Constructor = constructorByValue;
+ }
+ }
+ }
+
+ if (options && options.skipSchemaValidators) {
+ if (!(value instanceof Constructor)) {
+ value = new Constructor(value, null, scope);
+ }
+
+ return value.validate(fn);
+ }
+
+ SchemaType.prototype.doValidate.call(this, value, function(error) {
+ if (error) {
+ return fn(error);
+ }
+ if (!value) {
+ return fn(null);
+ }
+
+ value.validate(fn);
+ }, scope);
+};
+
+/**
+ * Synchronously validate this single nested doc
+ *
+ * @api private
+ */
+
+Embedded.prototype.doValidateSync = function(value, scope, options) {
+ if (!options || !options.skipSchemaValidators) {
+ const schemaTypeError = SchemaType.prototype.doValidateSync.call(this, value, scope);
+ if (schemaTypeError) {
+ return schemaTypeError;
+ }
+ }
+ if (!value) {
+ return;
+ }
+ return value.validateSync();
+};
+
+/**
+ * Adds a discriminator to this property
+ *
+ * @param {String} name
+ * @param {Schema} schema fields to add to the schema for instances of this sub-class
+ * @api public
+ */
+
+Embedded.prototype.discriminator = function(name, schema) {
+ discriminator(this.caster, name, schema);
+
+ this.caster.discriminators[name] = _createConstructor(schema);
+
+ return this.caster.discriminators[name];
+};
diff --git a/node_modules/mongoose/lib/schema/index.js b/node_modules/mongoose/lib/schema/index.js
new file mode 100644
index 0000000..183a6e9
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/index.js
@@ -0,0 +1,36 @@
+
+/*!
+ * Module exports.
+ */
+
+'use strict';
+
+exports.String = require('./string');
+
+exports.Number = require('./number');
+
+exports.Boolean = require('./boolean');
+
+exports.DocumentArray = require('./documentarray');
+
+exports.Embedded = require('./embedded');
+
+exports.Array = require('./array');
+
+exports.Buffer = require('./buffer');
+
+exports.Date = require('./date');
+
+exports.ObjectId = require('./objectid');
+
+exports.Mixed = require('./mixed');
+
+exports.Decimal128 = exports.Decimal = require('./decimal128');
+
+exports.Map = require('./map');
+
+// alias
+
+exports.Oid = exports.ObjectId;
+exports.Object = exports.Mixed;
+exports.Bool = exports.Boolean;
diff --git a/node_modules/mongoose/lib/schema/map.js b/node_modules/mongoose/lib/schema/map.js
new file mode 100644
index 0000000..f50ac4d
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/map.js
@@ -0,0 +1,39 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+const MongooseMap = require('../types/map');
+const SchemaType = require('../schematype');
+
+/*!
+ * ignore
+ */
+
+class Map extends SchemaType {
+ constructor(key, options) {
+ super(key, options, 'Map');
+ this.$isSchemaMap = true;
+ }
+
+ cast(val, doc, init) {
+ if (val instanceof MongooseMap) {
+ return val;
+ }
+
+ if (init) {
+ const map = new MongooseMap({}, this.path, doc, this.$__schemaType);
+
+ for (const key of Object.keys(val)) {
+ map.$init(key, this.$__schemaType.cast(val[key], doc, true));
+ }
+
+ return map;
+ }
+
+ return new MongooseMap(val, this.path, doc, this.$__schemaType);
+ }
+}
+
+module.exports = Map;
diff --git a/node_modules/mongoose/lib/schema/mixed.js b/node_modules/mongoose/lib/schema/mixed.js
new file mode 100644
index 0000000..672cc51
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/mixed.js
@@ -0,0 +1,105 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const SchemaType = require('../schematype');
+const symbols = require('./symbols');
+const utils = require('../utils');
+
+/**
+ * Mixed SchemaType constructor.
+ *
+ * @param {String} path
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function Mixed(path, options) {
+ if (options && options.default) {
+ const def = options.default;
+ if (Array.isArray(def) && def.length === 0) {
+ // make sure empty array defaults are handled
+ options.default = Array;
+ } else if (!options.shared && utils.isObject(def) && Object.keys(def).length === 0) {
+ // prevent odd "shared" objects between documents
+ options.default = function() {
+ return {};
+ };
+ }
+ }
+
+ SchemaType.call(this, path, options, 'Mixed');
+
+ this[symbols.schemaMixedSymbol] = true;
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+Mixed.schemaName = 'Mixed';
+
+/*!
+ * Inherits from SchemaType.
+ */
+Mixed.prototype = Object.create(SchemaType.prototype);
+Mixed.prototype.constructor = Mixed;
+
+/**
+ * Attaches a getter for all Mixed paths.
+ *
+ * ####Example:
+ *
+ * // Hide the 'hidden' path
+ * mongoose.Schema.Mixed.get(v => Object.assign({}, v, { hidden: null }));
+ *
+ * const Model = mongoose.model('Test', new Schema({ test: {} }));
+ * new Model({ test: { hidden: 'Secret!' } }).test.hidden; // null
+ *
+ * @param {Function} getter
+ * @return {this}
+ * @function get
+ * @static
+ * @api public
+ */
+
+Mixed.get = SchemaType.get;
+
+/**
+ * Casts `val` for Mixed.
+ *
+ * _this is a no-op_
+ *
+ * @param {Object} value to cast
+ * @api private
+ */
+
+Mixed.prototype.cast = function(val) {
+ return val;
+};
+
+/**
+ * Casts contents for queries.
+ *
+ * @param {String} $cond
+ * @param {any} [val]
+ * @api private
+ */
+
+Mixed.prototype.castForQuery = function($cond, val) {
+ if (arguments.length === 2) {
+ return val;
+ }
+ return $cond;
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = Mixed;
diff --git a/node_modules/mongoose/lib/schema/number.js b/node_modules/mongoose/lib/schema/number.js
new file mode 100644
index 0000000..49dc631
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/number.js
@@ -0,0 +1,361 @@
+'use strict';
+
+/*!
+ * Module requirements.
+ */
+
+const MongooseError = require('../error');
+const SchemaType = require('../schematype');
+const castNumber = require('../cast/number');
+const handleBitwiseOperator = require('./operators/bitwise');
+const utils = require('../utils');
+
+const CastError = SchemaType.CastError;
+let Document;
+
+/**
+ * Number SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function SchemaNumber(key, options) {
+ SchemaType.call(this, key, options, 'Number');
+}
+
+/**
+ * Attaches a getter for all Number instances.
+ *
+ * ####Example:
+ *
+ * // Make all numbers round down
+ * mongoose.Number.get(function(v) { return Math.floor(v); });
+ *
+ * const Model = mongoose.model('Test', new Schema({ test: Number }));
+ * new Model({ test: 3.14 }).test; // 3
+ *
+ * @param {Function} getter
+ * @return {this}
+ * @function get
+ * @static
+ * @api public
+ */
+
+SchemaNumber.get = SchemaType.get;
+
+/*!
+ * ignore
+ */
+
+SchemaNumber._cast = castNumber;
+
+/**
+ * Get/set the function used to cast arbitrary values to numbers.
+ *
+ * ####Example:
+ *
+ * // Make Mongoose cast empty strings '' to 0 for paths declared as numbers
+ * const original = mongoose.Number.cast();
+ * mongoose.Number.cast(v => {
+ * if (v === '') { return 0; }
+ * return original(v);
+ * });
+ *
+ * // Or disable casting entirely
+ * mongoose.Number.cast(false);
+ *
+ * @param {Function} caster
+ * @return {Function}
+ * @function get
+ * @static
+ * @api public
+ */
+
+SchemaNumber.cast = function cast(caster) {
+ if (arguments.length === 0) {
+ return this._cast;
+ }
+ if (caster === false) {
+ caster = v => {
+ if (typeof v !== 'number') {
+ throw new Error();
+ }
+ return v;
+ };
+ }
+ this._cast = caster;
+
+ return this._cast;
+};
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaNumber.schemaName = 'Number';
+
+/*!
+ * Inherits from SchemaType.
+ */
+SchemaNumber.prototype = Object.create(SchemaType.prototype);
+SchemaNumber.prototype.constructor = SchemaNumber;
+
+/*!
+ * ignore
+ */
+
+SchemaNumber._checkRequired = v => typeof v === 'number' || v instanceof Number;
+
+/**
+ * Override the function the required validator uses to check whether a string
+ * passes the `required` check.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @function checkRequired
+ * @static
+ * @api public
+ */
+
+SchemaNumber.checkRequired = SchemaType.checkRequired;
+
+/**
+ * Check if the given value satisfies a required validator.
+ *
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
+ */
+
+SchemaNumber.prototype.checkRequired = function checkRequired(value, doc) {
+ if (SchemaType._isRef(this, value, doc, true)) {
+ return !!value;
+ }
+ return this.constructor._checkRequired(value);
+};
+
+/**
+ * Sets a minimum number validator.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ n: { type: Number, min: 10 })
+ * var M = db.model('M', s)
+ * var m = new M({ n: 9 })
+ * m.save(function (err) {
+ * console.error(err) // validator error
+ * m.n = 10;
+ * m.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MIN} token which will be replaced with the invalid value
+ * var min = [10, 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).'];
+ * var schema = new Schema({ n: { type: Number, min: min })
+ * var M = mongoose.model('Measurement', schema);
+ * var s= new M({ n: 4 });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `n` (4) is beneath the limit (10).
+ * })
+ *
+ * @param {Number} value minimum number
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaNumber.prototype.min = function(value, message) {
+ if (this.minValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.minValidator;
+ }, this);
+ }
+
+ if (value !== null && value !== undefined) {
+ let msg = message || MongooseError.messages.Number.min;
+ msg = msg.replace(/{MIN}/, value);
+ this.validators.push({
+ validator: this.minValidator = function(v) {
+ return v == null || v >= value;
+ },
+ message: msg,
+ type: 'min',
+ min: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Sets a maximum number validator.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ n: { type: Number, max: 10 })
+ * var M = db.model('M', s)
+ * var m = new M({ n: 11 })
+ * m.save(function (err) {
+ * console.error(err) // validator error
+ * m.n = 10;
+ * m.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MAX} token which will be replaced with the invalid value
+ * var max = [10, 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).'];
+ * var schema = new Schema({ n: { type: Number, max: max })
+ * var M = mongoose.model('Measurement', schema);
+ * var s= new M({ n: 4 });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `n` (4) exceeds the limit (10).
+ * })
+ *
+ * @param {Number} maximum number
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaNumber.prototype.max = function(value, message) {
+ if (this.maxValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.maxValidator;
+ }, this);
+ }
+
+ if (value !== null && value !== undefined) {
+ let msg = message || MongooseError.messages.Number.max;
+ msg = msg.replace(/{MAX}/, value);
+ this.validators.push({
+ validator: this.maxValidator = function(v) {
+ return v == null || v <= value;
+ },
+ message: msg,
+ type: 'max',
+ max: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Casts to number
+ *
+ * @param {Object} value value to cast
+ * @param {Document} doc document that triggers the casting
+ * @param {Boolean} init
+ * @api private
+ */
+
+SchemaNumber.prototype.cast = function(value, doc, init) {
+ if (SchemaType._isRef(this, value, doc, init)) {
+ // wait! we may need to cast this to a document
+
+ if (value === null || value === undefined) {
+ return value;
+ }
+
+ // lazy load
+ Document || (Document = require('./../document'));
+
+ if (value instanceof Document) {
+ value.$__.wasPopulated = true;
+ return value;
+ }
+
+ // setting a populated path
+ if (typeof value === 'number') {
+ return value;
+ } else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
+ throw new CastError('number', value, this.path);
+ }
+
+ // Handle the case where user directly sets a populated
+ // path to a plain object; cast to the Model used in
+ // the population query.
+ const path = doc.$__fullPath(this.path);
+ const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
+ const pop = owner.populated(path, true);
+ const ret = new pop.options.model(value);
+ ret.$__.wasPopulated = true;
+ return ret;
+ }
+
+ const val = value && typeof value._id !== 'undefined' ?
+ value._id : // documents
+ value;
+
+ try {
+ return this.constructor.cast()(val);
+ } catch (err) {
+ throw new CastError('number', val, this.path);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+function handleSingle(val) {
+ return this.cast(val);
+}
+
+function handleArray(val) {
+ const _this = this;
+ if (!Array.isArray(val)) {
+ return [this.cast(val)];
+ }
+ return val.map(function(m) {
+ return _this.cast(m);
+ });
+}
+
+SchemaNumber.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $bitsAllClear: handleBitwiseOperator,
+ $bitsAnyClear: handleBitwiseOperator,
+ $bitsAllSet: handleBitwiseOperator,
+ $bitsAnySet: handleBitwiseOperator,
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle,
+ $mod: handleArray
+ });
+
+/**
+ * Casts contents for queries.
+ *
+ * @param {String} $conditional
+ * @param {any} [value]
+ * @api private
+ */
+
+SchemaNumber.prototype.castForQuery = function($conditional, val) {
+ let handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler) {
+ throw new CastError('Can\'t use ' + $conditional + ' with Number.');
+ }
+ return handler.call(this, val);
+ }
+ val = this._castForQuery($conditional);
+ return val;
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = SchemaNumber;
diff --git a/node_modules/mongoose/lib/schema/objectid.js b/node_modules/mongoose/lib/schema/objectid.js
new file mode 100644
index 0000000..efe1eee
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/objectid.js
@@ -0,0 +1,282 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const castObjectId = require('../cast/objectid');
+const SchemaType = require('../schematype');
+const oid = require('../types/objectid');
+const utils = require('../utils');
+
+const CastError = SchemaType.CastError;
+let Document;
+
+/**
+ * ObjectId SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function ObjectId(key, options) {
+ const isKeyHexStr = typeof key === 'string' && key.length === 24 && /^[a-f0-9]+$/i.test(key);
+ const suppressWarning = options && options.suppressWarning;
+ if ((isKeyHexStr || typeof key === 'undefined') && !suppressWarning) {
+ console.warn('mongoose: To create a new ObjectId please try ' +
+ '`Mongoose.Types.ObjectId` instead of using ' +
+ '`Mongoose.Schema.ObjectId`. Set the `suppressWarning` option if ' +
+ 'you\'re trying to create a hex char path in your schema.');
+ console.trace();
+ }
+ SchemaType.call(this, key, options, 'ObjectID');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+ObjectId.schemaName = 'ObjectId';
+
+/*!
+ * Inherits from SchemaType.
+ */
+ObjectId.prototype = Object.create(SchemaType.prototype);
+ObjectId.prototype.constructor = ObjectId;
+
+/**
+ * Attaches a getter for all ObjectId instances
+ *
+ * ####Example:
+ *
+ * // Always convert to string when getting an ObjectId
+ * mongoose.ObjectId.get(v => v.toString());
+ *
+ * const Model = mongoose.model('Test', new Schema({}));
+ * typeof (new Model({})._id); // 'string'
+ *
+ * @param {Function} getter
+ * @return {this}
+ * @function get
+ * @static
+ * @api public
+ */
+
+ObjectId.get = SchemaType.get;
+
+/**
+ * Adds an auto-generated ObjectId default if turnOn is true.
+ * @param {Boolean} turnOn auto generated ObjectId defaults
+ * @api public
+ * @return {SchemaType} this
+ */
+
+ObjectId.prototype.auto = function(turnOn) {
+ if (turnOn) {
+ this.default(defaultId);
+ this.set(resetId);
+ }
+
+ return this;
+};
+
+/*!
+ * ignore
+ */
+
+ObjectId._checkRequired = v => v instanceof oid;
+
+/*!
+ * ignore
+ */
+
+ObjectId._cast = castObjectId;
+
+/**
+ * Get/set the function used to cast arbitrary values to objectids.
+ *
+ * ####Example:
+ *
+ * // Make Mongoose only try to cast length 24 strings. By default, any 12
+ * // char string is a valid ObjectId.
+ * const original = mongoose.ObjectId.cast();
+ * mongoose.ObjectId.cast(v => {
+ * assert.ok(typeof v !== 'string' || v.length === 24);
+ * return original(v);
+ * });
+ *
+ * // Or disable casting entirely
+ * mongoose.ObjectId.cast(false);
+ *
+ * @param {Function} caster
+ * @return {Function}
+ * @function get
+ * @static
+ * @api public
+ */
+
+ObjectId.cast = function cast(caster) {
+ if (arguments.length === 0) {
+ return this._cast;
+ }
+ if (caster === false) {
+ caster = v => {
+ if (!(v instanceof oid)) {
+ throw new Error();
+ }
+ return v;
+ };
+ }
+ this._cast = caster;
+
+ return this._cast;
+};
+
+/**
+ * Override the function the required validator uses to check whether a string
+ * passes the `required` check.
+ *
+ * ####Example:
+ *
+ * // Allow empty strings to pass `required` check
+ * mongoose.Schema.Types.String.checkRequired(v => v != null);
+ *
+ * const M = mongoose.model({ str: { type: String, required: true } });
+ * new M({ str: '' }).validateSync(); // `null`, validation passes!
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @function checkRequired
+ * @static
+ * @api public
+ */
+
+ObjectId.checkRequired = SchemaType.checkRequired;
+
+/**
+ * Check if the given value satisfies a required validator.
+ *
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
+ */
+
+ObjectId.prototype.checkRequired = function checkRequired(value, doc) {
+ if (SchemaType._isRef(this, value, doc, true)) {
+ return !!value;
+ }
+ return this.constructor._checkRequired(value);
+};
+
+/**
+ * Casts to ObjectId
+ *
+ * @param {Object} value
+ * @param {Object} doc
+ * @param {Boolean} init whether this is an initialization cast
+ * @api private
+ */
+
+ObjectId.prototype.cast = function(value, doc, init) {
+ if (SchemaType._isRef(this, value, doc, init)) {
+ // wait! we may need to cast this to a document
+
+ if (value === null || value === undefined) {
+ return value;
+ }
+
+ // lazy load
+ Document || (Document = require('./../document'));
+
+ if (value instanceof Document) {
+ value.$__.wasPopulated = true;
+ return value;
+ }
+
+ // setting a populated path
+ if (value instanceof oid) {
+ return value;
+ } else if ((value.constructor.name || '').toLowerCase() === 'objectid') {
+ return new oid(value.toHexString());
+ } else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
+ throw new CastError('ObjectId', value, this.path);
+ }
+
+ // Handle the case where user directly sets a populated
+ // path to a plain object; cast to the Model used in
+ // the population query.
+ const path = doc.$__fullPath(this.path);
+ const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
+ const pop = owner.populated(path, true);
+ let ret = value;
+ if (!doc.$__.populated ||
+ !doc.$__.populated[path] ||
+ !doc.$__.populated[path].options ||
+ !doc.$__.populated[path].options.options ||
+ !doc.$__.populated[path].options.options.lean) {
+ ret = new pop.options.model(value);
+ ret.$__.wasPopulated = true;
+ }
+
+ return ret;
+ }
+
+ try {
+ return this.constructor.cast()(value);
+ } catch (error) {
+ throw new CastError('ObjectId', value, this.path);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+function handleSingle(val) {
+ return this.cast(val);
+}
+
+ObjectId.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle
+ });
+
+/*!
+ * ignore
+ */
+
+function defaultId() {
+ return new oid();
+}
+
+defaultId.$runBeforeSetters = true;
+
+function resetId(v) {
+ Document || (Document = require('./../document'));
+
+ if (this instanceof Document) {
+ if (v === void 0) {
+ const _v = new oid;
+ this.$__._id = _v;
+ return _v;
+ }
+
+ this.$__._id = v;
+ }
+
+ return v;
+}
+
+/*!
+ * Module exports.
+ */
+
+module.exports = ObjectId;
diff --git a/node_modules/mongoose/lib/schema/operators/bitwise.js b/node_modules/mongoose/lib/schema/operators/bitwise.js
new file mode 100644
index 0000000..07e18cd
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/bitwise.js
@@ -0,0 +1,38 @@
+/*!
+ * Module requirements.
+ */
+
+'use strict';
+
+const CastError = require('../../error/cast');
+
+/*!
+ * ignore
+ */
+
+function handleBitwiseOperator(val) {
+ const _this = this;
+ if (Array.isArray(val)) {
+ return val.map(function(v) {
+ return _castNumber(_this.path, v);
+ });
+ } else if (Buffer.isBuffer(val)) {
+ return val;
+ }
+ // Assume trying to cast to number
+ return _castNumber(_this.path, val);
+}
+
+/*!
+ * ignore
+ */
+
+function _castNumber(path, num) {
+ const v = Number(num);
+ if (isNaN(v)) {
+ throw new CastError('number', num, path);
+ }
+ return v;
+}
+
+module.exports = handleBitwiseOperator;
diff --git a/node_modules/mongoose/lib/schema/operators/exists.js b/node_modules/mongoose/lib/schema/operators/exists.js
new file mode 100644
index 0000000..916b4cb
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/exists.js
@@ -0,0 +1,12 @@
+'use strict';
+
+const castBoolean = require('../../cast/boolean');
+
+/*!
+ * ignore
+ */
+
+module.exports = function(val) {
+ const path = this != null ? this.path : null;
+ return castBoolean(val, path);
+};
diff --git a/node_modules/mongoose/lib/schema/operators/geospatial.js b/node_modules/mongoose/lib/schema/operators/geospatial.js
new file mode 100644
index 0000000..73e38f8
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/geospatial.js
@@ -0,0 +1,102 @@
+/*!
+ * Module requirements.
+ */
+
+'use strict';
+
+const castArraysOfNumbers = require('./helpers').castArraysOfNumbers;
+const castToNumber = require('./helpers').castToNumber;
+
+/*!
+ * ignore
+ */
+
+exports.cast$geoIntersects = cast$geoIntersects;
+exports.cast$near = cast$near;
+exports.cast$within = cast$within;
+
+function cast$near(val) {
+ const SchemaArray = require('../array');
+
+ if (Array.isArray(val)) {
+ castArraysOfNumbers(val, this);
+ return val;
+ }
+
+ _castMinMaxDistance(this, val);
+
+ if (val && val.$geometry) {
+ return cast$geometry(val, this);
+ }
+
+ return SchemaArray.prototype.castForQuery.call(this, val);
+}
+
+function cast$geometry(val, self) {
+ switch (val.$geometry.type) {
+ case 'Polygon':
+ case 'LineString':
+ case 'Point':
+ castArraysOfNumbers(val.$geometry.coordinates, self);
+ break;
+ default:
+ // ignore unknowns
+ break;
+ }
+
+ _castMinMaxDistance(self, val);
+
+ return val;
+}
+
+function cast$within(val) {
+ _castMinMaxDistance(this, val);
+
+ if (val.$box || val.$polygon) {
+ const type = val.$box ? '$box' : '$polygon';
+ val[type].forEach(arr => {
+ if (!Array.isArray(arr)) {
+ const msg = 'Invalid $within $box argument. '
+ + 'Expected an array, received ' + arr;
+ throw new TypeError(msg);
+ }
+ arr.forEach((v, i) => {
+ arr[i] = castToNumber.call(this, v);
+ });
+ });
+ } else if (val.$center || val.$centerSphere) {
+ const type = val.$center ? '$center' : '$centerSphere';
+ val[type].forEach((item, i) => {
+ if (Array.isArray(item)) {
+ item.forEach((v, j) => {
+ item[j] = castToNumber.call(this, v);
+ });
+ } else {
+ val[type][i] = castToNumber.call(this, item);
+ }
+ });
+ } else if (val.$geometry) {
+ cast$geometry(val, this);
+ }
+
+ return val;
+}
+
+function cast$geoIntersects(val) {
+ const geo = val.$geometry;
+ if (!geo) {
+ return;
+ }
+
+ cast$geometry(val, this);
+ return val;
+}
+
+function _castMinMaxDistance(self, val) {
+ if (val.$maxDistance) {
+ val.$maxDistance = castToNumber.call(self, val.$maxDistance);
+ }
+ if (val.$minDistance) {
+ val.$minDistance = castToNumber.call(self, val.$minDistance);
+ }
+}
diff --git a/node_modules/mongoose/lib/schema/operators/helpers.js b/node_modules/mongoose/lib/schema/operators/helpers.js
new file mode 100644
index 0000000..a17951c
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/helpers.js
@@ -0,0 +1,32 @@
+'use strict';
+
+/*!
+ * Module requirements.
+ */
+
+const SchemaNumber = require('../number');
+
+/*!
+ * @ignore
+ */
+
+exports.castToNumber = castToNumber;
+exports.castArraysOfNumbers = castArraysOfNumbers;
+
+/*!
+ * @ignore
+ */
+
+function castToNumber(val) {
+ return SchemaNumber.cast()(val);
+}
+
+function castArraysOfNumbers(arr, self) {
+ arr.forEach(function(v, i) {
+ if (Array.isArray(v)) {
+ castArraysOfNumbers(v, self);
+ } else {
+ arr[i] = castToNumber.call(self, v);
+ }
+ });
+}
diff --git a/node_modules/mongoose/lib/schema/operators/text.js b/node_modules/mongoose/lib/schema/operators/text.js
new file mode 100644
index 0000000..4b95916
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/text.js
@@ -0,0 +1,39 @@
+'use strict';
+
+const CastError = require('../../error/cast');
+const castBoolean = require('../../cast/boolean');
+const castString = require('../../cast/string');
+
+/*!
+ * Casts val to an object suitable for `$text`. Throws an error if the object
+ * can't be casted.
+ *
+ * @param {Any} val value to cast
+ * @param {String} [path] path to associate with any errors that occured
+ * @return {Object} casted object
+ * @see https://docs.mongodb.com/manual/reference/operator/query/text/
+ * @api private
+ */
+
+module.exports = function(val, path) {
+ if (val == null || typeof val !== 'object') {
+ throw new CastError('$text', val, path);
+ }
+
+ if (val.$search != null) {
+ val.$search = castString(val.$search, path + '.$search');
+ }
+ if (val.$language != null) {
+ val.$language = castString(val.$language, path + '.$language');
+ }
+ if (val.$caseSensitive != null) {
+ val.$caseSensitive = castBoolean(val.$caseSensitive,
+ path + '.$castSensitive');
+ }
+ if (val.$diacriticSensitive != null) {
+ val.$diacriticSensitive = castBoolean(val.$diacriticSensitive,
+ path + '.$diacriticSensitive');
+ }
+
+ return val;
+};
diff --git a/node_modules/mongoose/lib/schema/operators/type.js b/node_modules/mongoose/lib/schema/operators/type.js
new file mode 100644
index 0000000..c8e391a
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/type.js
@@ -0,0 +1,13 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = function(val) {
+ if (typeof val !== 'number' && typeof val !== 'string') {
+ throw new Error('$type parameter must be number or string');
+ }
+
+ return val;
+};
diff --git a/node_modules/mongoose/lib/schema/string.js b/node_modules/mongoose/lib/schema/string.js
new file mode 100644
index 0000000..b93e824
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/string.js
@@ -0,0 +1,604 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const SchemaType = require('../schematype');
+const CastError = SchemaType.CastError;
+const MongooseError = require('../error');
+const castString = require('../cast/string');
+const utils = require('../utils');
+
+let Document;
+
+/**
+ * String SchemaType constructor.
+ *
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api public
+ */
+
+function SchemaString(key, options) {
+ this.enumValues = [];
+ this.regExp = null;
+ SchemaType.call(this, key, options, 'String');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaString.schemaName = 'String';
+
+/*!
+ * Inherits from SchemaType.
+ */
+SchemaString.prototype = Object.create(SchemaType.prototype);
+SchemaString.prototype.constructor = SchemaString;
+
+/*!
+ * ignore
+ */
+
+SchemaString._cast = castString;
+
+/**
+ * Get/set the function used to cast arbitrary values to strings.
+ *
+ * ####Example:
+ *
+ * // Throw an error if you pass in an object. Normally, Mongoose allows
+ * // objects with custom `toString()` functions.
+ * const original = mongoose.Schema.Types.String.cast();
+ * mongoose.Schema.Types.String.cast(v => {
+ * assert.ok(v == null || typeof v !== 'object');
+ * return original(v);
+ * });
+ *
+ * // Or disable casting entirely
+ * mongoose.Schema.Types.String.cast(false);
+ *
+ * @param {Function} caster
+ * @return {Function}
+ * @function get
+ * @static
+ * @api public
+ */
+
+SchemaString.cast = function cast(caster) {
+ if (arguments.length === 0) {
+ return this._cast;
+ }
+ if (caster === false) {
+ caster = v => {
+ if (v != null && typeof v !== 'string') {
+ throw new Error();
+ }
+ return v;
+ };
+ }
+ this._cast = caster;
+
+ return this._cast;
+};
+
+/**
+ * Attaches a getter for all String instances.
+ *
+ * ####Example:
+ *
+ * // Make all numbers round down
+ * mongoose.Schema.String.get(v => v.toLowerCase());
+ *
+ * const Model = mongoose.model('Test', new Schema({ test: String }));
+ * new Model({ test: 'FOO' }).test; // 'foo'
+ *
+ * @param {Function} getter
+ * @return {this}
+ * @function get
+ * @static
+ * @api public
+ */
+
+SchemaString.get = SchemaType.get;
+
+/*!
+ * ignore
+ */
+
+SchemaString._checkRequired = v => (v instanceof String || typeof v === 'string') && v.length;
+
+/**
+ * Override the function the required validator uses to check whether a string
+ * passes the `required` check.
+ *
+ * ####Example:
+ *
+ * // Allow empty strings to pass `required` check
+ * mongoose.Schema.Types.String.checkRequired(v => v != null);
+ *
+ * const M = mongoose.model({ str: { type: String, required: true } });
+ * new M({ str: '' }).validateSync(); // `null`, validation passes!
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @function checkRequired
+ * @static
+ * @api public
+ */
+
+SchemaString.checkRequired = SchemaType.checkRequired;
+
+/**
+ * Adds an enum validator
+ *
+ * ####Example:
+ *
+ * var states = ['opening', 'open', 'closing', 'closed']
+ * var s = new Schema({ state: { type: String, enum: states }})
+ * var M = db.model('M', s)
+ * var m = new M({ state: 'invalid' })
+ * m.save(function (err) {
+ * console.error(String(err)) // ValidationError: `invalid` is not a valid enum value for path `state`.
+ * m.state = 'open'
+ * m.save(callback) // success
+ * })
+ *
+ * // or with custom error messages
+ * var enum = {
+ * values: ['opening', 'open', 'closing', 'closed'],
+ * message: 'enum validator failed for path `{PATH}` with value `{VALUE}`'
+ * }
+ * var s = new Schema({ state: { type: String, enum: enum })
+ * var M = db.model('M', s)
+ * var m = new M({ state: 'invalid' })
+ * m.save(function (err) {
+ * console.error(String(err)) // ValidationError: enum validator failed for path `state` with value `invalid`
+ * m.state = 'open'
+ * m.save(callback) // success
+ * })
+ *
+ * @param {String|Object} [args...] enumeration values
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaString.prototype.enum = function() {
+ if (this.enumValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.enumValidator;
+ }, this);
+ this.enumValidator = false;
+ }
+
+ if (arguments[0] === void 0 || arguments[0] === false) {
+ return this;
+ }
+
+ let values;
+ let errorMessage;
+
+ if (utils.isObject(arguments[0])) {
+ values = arguments[0].values;
+ errorMessage = arguments[0].message;
+ } else {
+ values = arguments;
+ errorMessage = MongooseError.messages.String.enum;
+ }
+
+ for (let i = 0; i < values.length; i++) {
+ if (undefined !== values[i]) {
+ this.enumValues.push(this.cast(values[i]));
+ }
+ }
+
+ const vals = this.enumValues;
+ this.enumValidator = function(v) {
+ return undefined === v || ~vals.indexOf(v);
+ };
+ this.validators.push({
+ validator: this.enumValidator,
+ message: errorMessage,
+ type: 'enum',
+ enumValues: vals
+ });
+
+ return this;
+};
+
+/**
+ * Adds a lowercase [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ email: { type: String, lowercase: true }})
+ * var M = db.model('M', s);
+ * var m = new M({ email: 'SomeEmail@example.COM' });
+ * console.log(m.email) // someemail@example.com
+ * M.find({ email: 'SomeEmail@example.com' }); // Queries by 'someemail@example.com'
+ *
+ * @api public
+ * @return {SchemaType} this
+ */
+
+SchemaString.prototype.lowercase = function(shouldApply) {
+ if (arguments.length > 0 && !shouldApply) {
+ return this;
+ }
+ return this.set(function(v, self) {
+ if (typeof v !== 'string') {
+ v = self.cast(v);
+ }
+ if (v) {
+ return v.toLowerCase();
+ }
+ return v;
+ });
+};
+
+/**
+ * Adds an uppercase [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ caps: { type: String, uppercase: true }})
+ * var M = db.model('M', s);
+ * var m = new M({ caps: 'an example' });
+ * console.log(m.caps) // AN EXAMPLE
+ * M.find({ caps: 'an example' }) // Matches documents where caps = 'AN EXAMPLE'
+ *
+ * @api public
+ * @return {SchemaType} this
+ */
+
+SchemaString.prototype.uppercase = function(shouldApply) {
+ if (arguments.length > 0 && !shouldApply) {
+ return this;
+ }
+ return this.set(function(v, self) {
+ if (typeof v !== 'string') {
+ v = self.cast(v);
+ }
+ if (v) {
+ return v.toUpperCase();
+ }
+ return v;
+ });
+};
+
+/**
+ * Adds a trim [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
+ *
+ * The string value will be trimmed when set.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ name: { type: String, trim: true }})
+ * var M = db.model('M', s)
+ * var string = ' some name '
+ * console.log(string.length) // 11
+ * var m = new M({ name: string })
+ * console.log(m.name.length) // 9
+ *
+ * @api public
+ * @return {SchemaType} this
+ */
+
+SchemaString.prototype.trim = function(shouldTrim) {
+ if (arguments.length > 0 && !shouldTrim) {
+ return this;
+ }
+ return this.set(function(v, self) {
+ if (typeof v !== 'string') {
+ v = self.cast(v);
+ }
+ if (v) {
+ return v.trim();
+ }
+ return v;
+ });
+};
+
+/**
+ * Sets a minimum length validator.
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ postalCode: { type: String, minlength: 5 })
+ * var Address = db.model('Address', schema)
+ * var address = new Address({ postalCode: '9512' })
+ * address.save(function (err) {
+ * console.error(err) // validator error
+ * address.postalCode = '95125';
+ * address.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MINLENGTH} token which will be replaced with the minimum allowed length
+ * var minlength = [5, 'The value of path `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).'];
+ * var schema = new Schema({ postalCode: { type: String, minlength: minlength })
+ * var Address = mongoose.model('Address', schema);
+ * var address = new Address({ postalCode: '9512' });
+ * address.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512`) is shorter than the minimum length (5).
+ * })
+ *
+ * @param {Number} value minimum string length
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaString.prototype.minlength = function(value, message) {
+ if (this.minlengthValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.minlengthValidator;
+ }, this);
+ }
+
+ if (value !== null && value !== undefined) {
+ let msg = message || MongooseError.messages.String.minlength;
+ msg = msg.replace(/{MINLENGTH}/, value);
+ this.validators.push({
+ validator: this.minlengthValidator = function(v) {
+ return v === null || v.length >= value;
+ },
+ message: msg,
+ type: 'minlength',
+ minlength: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Sets a maximum length validator.
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ postalCode: { type: String, maxlength: 9 })
+ * var Address = db.model('Address', schema)
+ * var address = new Address({ postalCode: '9512512345' })
+ * address.save(function (err) {
+ * console.error(err) // validator error
+ * address.postalCode = '95125';
+ * address.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MAXLENGTH} token which will be replaced with the maximum allowed length
+ * var maxlength = [9, 'The value of path `{PATH}` (`{VALUE}`) exceeds the maximum allowed length ({MAXLENGTH}).'];
+ * var schema = new Schema({ postalCode: { type: String, maxlength: maxlength })
+ * var Address = mongoose.model('Address', schema);
+ * var address = new Address({ postalCode: '9512512345' });
+ * address.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512512345`) exceeds the maximum allowed length (9).
+ * })
+ *
+ * @param {Number} value maximum string length
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaString.prototype.maxlength = function(value, message) {
+ if (this.maxlengthValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.maxlengthValidator;
+ }, this);
+ }
+
+ if (value !== null && value !== undefined) {
+ let msg = message || MongooseError.messages.String.maxlength;
+ msg = msg.replace(/{MAXLENGTH}/, value);
+ this.validators.push({
+ validator: this.maxlengthValidator = function(v) {
+ return v === null || v.length <= value;
+ },
+ message: msg,
+ type: 'maxlength',
+ maxlength: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Sets a regexp validator.
+ *
+ * Any value that does not pass `regExp`.test(val) will fail validation.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ name: { type: String, match: /^a/ }})
+ * var M = db.model('M', s)
+ * var m = new M({ name: 'I am invalid' })
+ * m.validate(function (err) {
+ * console.error(String(err)) // "ValidationError: Path `name` is invalid (I am invalid)."
+ * m.name = 'apples'
+ * m.validate(function (err) {
+ * assert.ok(err) // success
+ * })
+ * })
+ *
+ * // using a custom error message
+ * var match = [ /\.html$/, "That file doesn't end in .html ({VALUE})" ];
+ * var s = new Schema({ file: { type: String, match: match }})
+ * var M = db.model('M', s);
+ * var m = new M({ file: 'invalid' });
+ * m.validate(function (err) {
+ * console.log(String(err)) // "ValidationError: That file doesn't end in .html (invalid)"
+ * })
+ *
+ * Empty strings, `undefined`, and `null` values always pass the match validator. If you require these values, enable the `required` validator also.
+ *
+ * var s = new Schema({ name: { type: String, match: /^a/, required: true }})
+ *
+ * @param {RegExp} regExp regular expression to test against
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaString.prototype.match = function match(regExp, message) {
+ // yes, we allow multiple match validators
+
+ const msg = message || MongooseError.messages.String.match;
+
+ const matchValidator = function(v) {
+ if (!regExp) {
+ return false;
+ }
+
+ const ret = ((v != null && v !== '')
+ ? regExp.test(v)
+ : true);
+ return ret;
+ };
+
+ this.validators.push({
+ validator: matchValidator,
+ message: msg,
+ type: 'regexp',
+ regexp: regExp
+ });
+ return this;
+};
+
+/**
+ * Check if the given value satisfies the `required` validator. The value is
+ * considered valid if it is a string (that is, not `null` or `undefined`) and
+ * has positive length. The `required` validator **will** fail for empty
+ * strings.
+ *
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
+ */
+
+SchemaString.prototype.checkRequired = function checkRequired(value, doc) {
+ if (SchemaType._isRef(this, value, doc, true)) {
+ return !!value;
+ }
+ return this.constructor._checkRequired(value, doc);
+};
+
+/**
+ * Casts to String
+ *
+ * @api private
+ */
+
+SchemaString.prototype.cast = function(value, doc, init) {
+ if (SchemaType._isRef(this, value, doc, init)) {
+ // wait! we may need to cast this to a document
+
+ if (value === null || value === undefined) {
+ return value;
+ }
+
+ // lazy load
+ Document || (Document = require('./../document'));
+
+ if (value instanceof Document) {
+ value.$__.wasPopulated = true;
+ return value;
+ }
+
+ // setting a populated path
+ if (typeof value === 'string') {
+ return value;
+ } else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
+ throw new CastError('string', value, this.path);
+ }
+
+ // Handle the case where user directly sets a populated
+ // path to a plain object; cast to the Model used in
+ // the population query.
+ const path = doc.$__fullPath(this.path);
+ const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
+ const pop = owner.populated(path, true);
+ const ret = new pop.options.model(value);
+ ret.$__.wasPopulated = true;
+ return ret;
+ }
+
+ try {
+ return this.constructor.cast()(value);
+ } catch (error) {
+ throw new CastError('string', value, this.path);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+function handleSingle(val) {
+ return this.castForQuery(val);
+}
+
+function handleArray(val) {
+ const _this = this;
+ if (!Array.isArray(val)) {
+ return [this.castForQuery(val)];
+ }
+ return val.map(function(m) {
+ return _this.castForQuery(m);
+ });
+}
+
+SchemaString.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $all: handleArray,
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle,
+ $options: handleSingle,
+ $regex: handleSingle,
+ $not: handleSingle
+ });
+
+/**
+ * Casts contents for queries.
+ *
+ * @param {String} $conditional
+ * @param {any} [val]
+ * @api private
+ */
+
+SchemaString.prototype.castForQuery = function($conditional, val) {
+ let handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with String.');
+ }
+ return handler.call(this, val);
+ }
+ val = $conditional;
+ if (Object.prototype.toString.call(val) === '[object RegExp]') {
+ return val;
+ }
+
+ return this._castForQuery(val);
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = SchemaString;
diff --git a/node_modules/mongoose/lib/schema/symbols.js b/node_modules/mongoose/lib/schema/symbols.js
new file mode 100644
index 0000000..7ae0e9e
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/symbols.js
@@ -0,0 +1,5 @@
+'use strict';
+
+exports.schemaMixedSymbol = Symbol.for('mongoose:schema_mixed');
+
+exports.builtInMiddleware = Symbol.for('mongoose:built-in-middleware');
diff --git a/node_modules/mongoose/lib/schematype.js b/node_modules/mongoose/lib/schematype.js
new file mode 100644
index 0000000..639de33
--- /dev/null
+++ b/node_modules/mongoose/lib/schematype.js
@@ -0,0 +1,1290 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const MongooseError = require('./error');
+const $exists = require('./schema/operators/exists');
+const $type = require('./schema/operators/type');
+const get = require('./helpers/get');
+const immediate = require('./helpers/immediate');
+const utils = require('./utils');
+const validatorErrorSymbol = require('./helpers/symbols').validatorErrorSymbol;
+
+const CastError = MongooseError.CastError;
+const ValidatorError = MongooseError.ValidatorError;
+
+/**
+ * SchemaType constructor. Do **not** instantiate `SchemaType` directly.
+ * Mongoose converts your schema paths into SchemaTypes automatically.
+ *
+ * ####Example:
+ *
+ * const schema = new Schema({ name: String });
+ * schema.path('name') instanceof SchemaType; // true
+ *
+ * @param {String} path
+ * @param {Object} [options]
+ * @param {String} [instance]
+ * @api public
+ */
+
+function SchemaType(path, options, instance) {
+ this.path = path;
+ this.instance = instance;
+ this.validators = [];
+ this.getters = this.constructor.hasOwnProperty('getters') ?
+ this.constructor.getters.slice() :
+ [];
+ this.setters = [];
+ this.options = options;
+ this._index = null;
+ this.selected;
+
+ for (const prop in options) {
+ if (this[prop] && typeof this[prop] === 'function') {
+ // { unique: true, index: true }
+ if (prop === 'index' && this._index) {
+ continue;
+ }
+
+ const val = options[prop];
+ // Special case so we don't screw up array defaults, see gh-5780
+ if (prop === 'default') {
+ this.default(val);
+ continue;
+ }
+
+ const opts = Array.isArray(val) ? val : [val];
+
+ this[prop].apply(this, opts);
+ }
+ }
+
+ Object.defineProperty(this, '$$context', {
+ enumerable: false,
+ configurable: false,
+ writable: true,
+ value: null
+ });
+}
+
+/**
+ * Get/set the function used to cast arbitrary values to this type.
+ *
+ * ####Example:
+ *
+ * // Disallow `null` for numbers, and don't try to cast any values to
+ * // numbers, so even strings like '123' will cause a CastError.
+ * mongoose.Number.cast(function(v) {
+ * assert.ok(v === undefined || typeof v === 'number');
+ * return v;
+ * });
+ *
+ * @param {Function|false} caster Function that casts arbitrary values to this type, or throws an error if casting failed
+ * @return {Function}
+ * @static
+ * @receiver SchemaType
+ * @function cast
+ * @api public
+ */
+
+SchemaType.cast = function cast(caster) {
+ if (arguments.length === 0) {
+ return this._cast;
+ }
+ if (caster === false) {
+ caster = v => v;
+ }
+ this._cast = caster;
+
+ return this._cast;
+};
+
+/**
+ * Attaches a getter for all instances of this schema type.
+ *
+ * ####Example:
+ *
+ * // Make all numbers round down
+ * mongoose.Number.get(function(v) { return Math.floor(v); });
+ *
+ * @param {Function} getter
+ * @return {this}
+ * @static
+ * @receiver SchemaType
+ * @function get
+ * @api public
+ */
+
+SchemaType.get = function(getter) {
+ this.getters = this.hasOwnProperty('getters') ? this.getters : [];
+ this.getters.push(getter);
+};
+
+/**
+ * Sets a default value for this SchemaType.
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ n: { type: Number, default: 10 })
+ * var M = db.model('M', schema)
+ * var m = new M;
+ * console.log(m.n) // 10
+ *
+ * Defaults can be either `functions` which return the value to use as the default or the literal value itself. Either way, the value will be cast based on its schema type before being set during document creation.
+ *
+ * ####Example:
+ *
+ * // values are cast:
+ * var schema = new Schema({ aNumber: { type: Number, default: 4.815162342 }})
+ * var M = db.model('M', schema)
+ * var m = new M;
+ * console.log(m.aNumber) // 4.815162342
+ *
+ * // default unique objects for Mixed types:
+ * var schema = new Schema({ mixed: Schema.Types.Mixed });
+ * schema.path('mixed').default(function () {
+ * return {};
+ * });
+ *
+ * // if we don't use a function to return object literals for Mixed defaults,
+ * // each document will receive a reference to the same object literal creating
+ * // a "shared" object instance:
+ * var schema = new Schema({ mixed: Schema.Types.Mixed });
+ * schema.path('mixed').default({});
+ * var M = db.model('M', schema);
+ * var m1 = new M;
+ * m1.mixed.added = 1;
+ * console.log(m1.mixed); // { added: 1 }
+ * var m2 = new M;
+ * console.log(m2.mixed); // { added: 1 }
+ *
+ * @param {Function|any} val the default value
+ * @return {defaultValue}
+ * @api public
+ */
+
+SchemaType.prototype.default = function(val) {
+ if (arguments.length === 1) {
+ if (val === void 0) {
+ this.defaultValue = void 0;
+ return void 0;
+ }
+ this.defaultValue = val;
+ return this.defaultValue;
+ } else if (arguments.length > 1) {
+ this.defaultValue = utils.args(arguments);
+ }
+ return this.defaultValue;
+};
+
+/**
+ * Declares the index options for this schematype.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ name: { type: String, index: true })
+ * var s = new Schema({ loc: { type: [Number], index: 'hashed' })
+ * var s = new Schema({ loc: { type: [Number], index: '2d', sparse: true })
+ * var s = new Schema({ loc: { type: [Number], index: { type: '2dsphere', sparse: true }})
+ * var s = new Schema({ date: { type: Date, index: { unique: true, expires: '1d' }})
+ * Schema.path('my.path').index(true);
+ * Schema.path('my.date').index({ expires: 60 });
+ * Schema.path('my.path').index({ unique: true, sparse: true });
+ *
+ * ####NOTE:
+ *
+ * _Indexes are created [in the background](https://docs.mongodb.com/manual/core/index-creation/#index-creation-background)
+ * by default. If `background` is set to `false`, MongoDB will not execute any
+ * read/write operations you send until the index build.
+ * Specify `background: false` to override Mongoose's default._
+ *
+ * @param {Object|Boolean|String} options
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.index = function(options) {
+ this._index = options;
+ utils.expires(this._index);
+ return this;
+};
+
+/**
+ * Declares an unique index.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ name: { type: String, unique: true }});
+ * Schema.path('name').index({ unique: true });
+ *
+ * _NOTE: violating the constraint returns an `E11000` error from MongoDB when saving, not a Mongoose validation error._
+ *
+ * @param {Boolean} bool
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.unique = function(bool) {
+ if (this._index === false) {
+ if (!bool) {
+ return;
+ }
+ throw new Error('Path "' + this.path + '" may not have `index` set to ' +
+ 'false and `unique` set to true');
+ }
+ if (this._index == null || this._index === true) {
+ this._index = {};
+ } else if (typeof this._index === 'string') {
+ this._index = {type: this._index};
+ }
+
+ this._index.unique = bool;
+ return this;
+};
+
+/**
+ * Declares a full text index.
+ *
+ * ###Example:
+ *
+ * var s = new Schema({name : {type: String, text : true })
+ * Schema.path('name').index({text : true});
+ * @param {Boolean} bool
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.text = function(bool) {
+ if (this._index === null || this._index === undefined ||
+ typeof this._index === 'boolean') {
+ this._index = {};
+ } else if (typeof this._index === 'string') {
+ this._index = {type: this._index};
+ }
+
+ this._index.text = bool;
+ return this;
+};
+
+/**
+ * Declares a sparse index.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ name: { type: String, sparse: true })
+ * Schema.path('name').index({ sparse: true });
+ *
+ * @param {Boolean} bool
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.sparse = function(bool) {
+ if (this._index === null || this._index === undefined ||
+ typeof this._index === 'boolean') {
+ this._index = {};
+ } else if (typeof this._index === 'string') {
+ this._index = {type: this._index};
+ }
+
+ this._index.sparse = bool;
+ return this;
+};
+
+/**
+ * Adds a setter to this schematype.
+ *
+ * ####Example:
+ *
+ * function capitalize (val) {
+ * if (typeof val !== 'string') val = '';
+ * return val.charAt(0).toUpperCase() + val.substring(1);
+ * }
+ *
+ * // defining within the schema
+ * var s = new Schema({ name: { type: String, set: capitalize }});
+ *
+ * // or with the SchemaType
+ * var s = new Schema({ name: String })
+ * s.path('name').set(capitalize);
+ *
+ * Setters allow you to transform the data before it gets to the raw mongodb
+ * document or query.
+ *
+ * Suppose you are implementing user registration for a website. Users provide
+ * an email and password, which gets saved to mongodb. The email is a string
+ * that you will want to normalize to lower case, in order to avoid one email
+ * having more than one account -- e.g., otherwise, avenue@q.com can be registered for 2 accounts via avenue@q.com and AvEnUe@Q.CoM.
+ *
+ * You can set up email lower case normalization easily via a Mongoose setter.
+ *
+ * function toLower(v) {
+ * return v.toLowerCase();
+ * }
+ *
+ * var UserSchema = new Schema({
+ * email: { type: String, set: toLower }
+ * });
+ *
+ * var User = db.model('User', UserSchema);
+ *
+ * var user = new User({email: 'AVENUE@Q.COM'});
+ * console.log(user.email); // 'avenue@q.com'
+ *
+ * // or
+ * var user = new User();
+ * user.email = 'Avenue@Q.com';
+ * console.log(user.email); // 'avenue@q.com'
+ * User.updateOne({ _id: _id }, { $set: { email: 'AVENUE@Q.COM' } }); // update to 'avenue@q.com'
+ *
+ * As you can see above, setters allow you to transform the data before it
+ * stored in MongoDB, or before executing a query.
+ *
+ * _NOTE: we could have also just used the built-in `lowercase: true` SchemaType option instead of defining our own function._
+ *
+ * new Schema({ email: { type: String, lowercase: true }})
+ *
+ * Setters are also passed a second argument, the schematype on which the setter was defined. This allows for tailored behavior based on options passed in the schema.
+ *
+ * function inspector (val, schematype) {
+ * if (schematype.options.required) {
+ * return schematype.path + ' is required';
+ * } else {
+ * return val;
+ * }
+ * }
+ *
+ * var VirusSchema = new Schema({
+ * name: { type: String, required: true, set: inspector },
+ * taxonomy: { type: String, set: inspector }
+ * })
+ *
+ * var Virus = db.model('Virus', VirusSchema);
+ * var v = new Virus({ name: 'Parvoviridae', taxonomy: 'Parvovirinae' });
+ *
+ * console.log(v.name); // name is required
+ * console.log(v.taxonomy); // Parvovirinae
+ *
+ * You can also use setters to modify other properties on the document. If
+ * you're setting a property `name` on a document, the setter will run with
+ * `this` as the document. Be careful, in mongoose 5 setters will also run
+ * when querying by `name` with `this` as the query.
+ *
+ * ```javascript
+ * const nameSchema = new Schema({ name: String, keywords: [String] });
+ * nameSchema.path('name').set(function(v) {
+ * // Need to check if `this` is a document, because in mongoose 5
+ * // setters will also run on queries, in which case `this` will be a
+ * // mongoose query object.
+ * if (this instanceof Document && v != null) {
+ * this.keywords = v.split(' ');
+ * }
+ * return v;
+ * });
+ * ```
+ *
+ * @param {Function} fn
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.set = function(fn) {
+ if (typeof fn !== 'function') {
+ throw new TypeError('A setter must be a function.');
+ }
+ this.setters.push(fn);
+ return this;
+};
+
+/**
+ * Adds a getter to this schematype.
+ *
+ * ####Example:
+ *
+ * function dob (val) {
+ * if (!val) return val;
+ * return (val.getMonth() + 1) + "/" + val.getDate() + "/" + val.getFullYear();
+ * }
+ *
+ * // defining within the schema
+ * var s = new Schema({ born: { type: Date, get: dob })
+ *
+ * // or by retreiving its SchemaType
+ * var s = new Schema({ born: Date })
+ * s.path('born').get(dob)
+ *
+ * Getters allow you to transform the representation of the data as it travels from the raw mongodb document to the value that you see.
+ *
+ * Suppose you are storing credit card numbers and you want to hide everything except the last 4 digits to the mongoose user. You can do so by defining a getter in the following way:
+ *
+ * function obfuscate (cc) {
+ * return '****-****-****-' + cc.slice(cc.length-4, cc.length);
+ * }
+ *
+ * var AccountSchema = new Schema({
+ * creditCardNumber: { type: String, get: obfuscate }
+ * });
+ *
+ * var Account = db.model('Account', AccountSchema);
+ *
+ * Account.findById(id, function (err, found) {
+ * console.log(found.creditCardNumber); // '****-****-****-1234'
+ * });
+ *
+ * Getters are also passed a second argument, the schematype on which the getter was defined. This allows for tailored behavior based on options passed in the schema.
+ *
+ * function inspector (val, schematype) {
+ * if (schematype.options.required) {
+ * return schematype.path + ' is required';
+ * } else {
+ * return schematype.path + ' is not';
+ * }
+ * }
+ *
+ * var VirusSchema = new Schema({
+ * name: { type: String, required: true, get: inspector },
+ * taxonomy: { type: String, get: inspector }
+ * })
+ *
+ * var Virus = db.model('Virus', VirusSchema);
+ *
+ * Virus.findById(id, function (err, virus) {
+ * console.log(virus.name); // name is required
+ * console.log(virus.taxonomy); // taxonomy is not
+ * })
+ *
+ * @param {Function} fn
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.get = function(fn) {
+ if (typeof fn !== 'function') {
+ throw new TypeError('A getter must be a function.');
+ }
+ this.getters.push(fn);
+ return this;
+};
+
+/**
+ * Adds validator(s) for this document path.
+ *
+ * Validators always receive the value to validate as their first argument and
+ * must return `Boolean`. Returning `false` or throwing an error means
+ * validation failed.
+ *
+ * The error message argument is optional. If not passed, the [default generic error message template](#error_messages_MongooseError-messages) will be used.
+ *
+ * ####Examples:
+ *
+ * // make sure every value is equal to "something"
+ * function validator (val) {
+ * return val == 'something';
+ * }
+ * new Schema({ name: { type: String, validate: validator }});
+ *
+ * // with a custom error message
+ *
+ * var custom = [validator, 'Uh oh, {PATH} does not equal "something".']
+ * new Schema({ name: { type: String, validate: custom }});
+ *
+ * // adding many validators at a time
+ *
+ * var many = [
+ * { validator: validator, msg: 'uh oh' }
+ * , { validator: anotherValidator, msg: 'failed' }
+ * ]
+ * new Schema({ name: { type: String, validate: many }});
+ *
+ * // or utilizing SchemaType methods directly:
+ *
+ * var schema = new Schema({ name: 'string' });
+ * schema.path('name').validate(validator, 'validation of `{PATH}` failed with value `{VALUE}`');
+ *
+ * ####Error message templates:
+ *
+ * From the examples above, you may have noticed that error messages support
+ * basic templating. There are a few other template keywords besides `{PATH}`
+ * and `{VALUE}` too. To find out more, details are available
+ * [here](#error_messages_MongooseError.messages).
+ *
+ * If Mongoose's built-in error message templating isn't enough, Mongoose
+ * supports setting the `message` property to a function.
+ *
+ * schema.path('name').validate({
+ * validator: function() { return v.length > 5; },
+ * // `errors['name']` will be "name must have length 5, got 'foo'"
+ * message: function(props) {
+ * return `${props.path} must have length 5, got '${props.value}'`;
+ * }
+ * });
+ *
+ * To bypass Mongoose's error messages and just copy the error message that
+ * the validator throws, do this:
+ *
+ * schema.path('name').validate({
+ * validator: function() { throw new Error('Oops!'); },
+ * // `errors['name']` will be "Oops!"
+ * message: function(props) { return props.reason.message; }
+ * });
+ *
+ * ####Asynchronous validation:
+ *
+ * Mongoose supports validators that return a promise. A validator that returns
+ * a promise is called an _async validator_. Async validators run in
+ * parallel, and `validate()` will wait until all async validators have settled.
+ *
+ * schema.path('name').validate({
+ * validator: function (value) {
+ * return new Promise(function (resolve, reject) {
+ * resolve(false); // validation failed
+ * });
+ * }
+ * });
+ *
+ * You might use asynchronous validators to retreive other documents from the database to validate against or to meet other I/O bound validation needs.
+ *
+ * Validation occurs `pre('save')` or whenever you manually execute [document#validate](#document_Document-validate).
+ *
+ * If validation fails during `pre('save')` and no callback was passed to receive the error, an `error` event will be emitted on your Models associated db [connection](#connection_Connection), passing the validation error object along.
+ *
+ * var conn = mongoose.createConnection(..);
+ * conn.on('error', handleError);
+ *
+ * var Product = conn.model('Product', yourSchema);
+ * var dvd = new Product(..);
+ * dvd.save(); // emits error on the `conn` above
+ *
+ * If you want to handle these errors at the Model level, add an `error`
+ * listener to your Model as shown below.
+ *
+ * // registering an error listener on the Model lets us handle errors more locally
+ * Product.on('error', handleError);
+ *
+ * @param {RegExp|Function|Object} obj validator function, or hash describing options
+ * @param {Function} [obj.validator] validator function. If the validator function returns `undefined` or a truthy value, validation succeeds. If it returns falsy (except `undefined`) or throws an error, validation fails.
+ * @param {String|Function} [obj.message] optional error message. If function, should return the error message as a string
+ * @param {Boolean} [obj.propsParameter=false] If true, Mongoose will pass the validator properties object (with the `validator` function, `message`, etc.) as the 2nd arg to the validator function. This is disabled by default because many validators [rely on positional args](https://github.com/chriso/validator.js#validators), so turning this on may cause unpredictable behavior in external validators.
+ * @param {String|Function} [errorMsg] optional error message. If function, should return the error message as a string
+ * @param {String} [type] optional validator type
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.validate = function(obj, message, type) {
+ if (typeof obj === 'function' || obj && utils.getFunctionName(obj.constructor) === 'RegExp') {
+ let properties;
+ if (message instanceof Object && !type) {
+ properties = utils.clone(message);
+ if (!properties.message) {
+ properties.message = properties.msg;
+ }
+ properties.validator = obj;
+ properties.type = properties.type || 'user defined';
+ } else {
+ if (!message) {
+ message = MongooseError.messages.general.default;
+ }
+ if (!type) {
+ type = 'user defined';
+ }
+ properties = {message: message, type: type, validator: obj};
+ }
+ this.validators.push(properties);
+ return this;
+ }
+
+ let i;
+ let length;
+ let arg;
+
+ for (i = 0, length = arguments.length; i < length; i++) {
+ arg = arguments[i];
+ if (!(arg && utils.getFunctionName(arg.constructor) === 'Object')) {
+ const msg = 'Invalid validator. Received (' + typeof arg + ') '
+ + arg
+ + '. See http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate';
+
+ throw new Error(msg);
+ }
+ this.validate(arg.validator, arg);
+ }
+
+ return this;
+};
+
+/**
+ * Adds a required validator to this SchemaType. The validator gets added
+ * to the front of this SchemaType's validators array using `unshift()`.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ born: { type: Date, required: true })
+ *
+ * // or with custom error message
+ *
+ * var s = new Schema({ born: { type: Date, required: '{PATH} is required!' })
+ *
+ * // or with a function
+ *
+ * var s = new Schema({
+ * userId: ObjectId,
+ * username: {
+ * type: String,
+ * required: function() { return this.userId != null; }
+ * }
+ * })
+ *
+ * // or with a function and a custom message
+ * var s = new Schema({
+ * userId: ObjectId,
+ * username: {
+ * type: String,
+ * required: [
+ * function() { return this.userId != null; },
+ * 'username is required if id is specified'
+ * ]
+ * }
+ * })
+ *
+ * // or through the path API
+ *
+ * Schema.path('name').required(true);
+ *
+ * // with custom error messaging
+ *
+ * Schema.path('name').required(true, 'grrr :( ');
+ *
+ * // or make a path conditionally required based on a function
+ * var isOver18 = function() { return this.age >= 18; };
+ * Schema.path('voterRegistrationId').required(isOver18);
+ *
+ * The required validator uses the SchemaType's `checkRequired` function to
+ * determine whether a given value satisfies the required validator. By default,
+ * a value satisfies the required validator if `val != null` (that is, if
+ * the value is not null nor undefined). However, most built-in mongoose schema
+ * types override the default `checkRequired` function:
+ *
+ * @param {Boolean|Function|Object} required enable/disable the validator, or function that returns required boolean, or options object
+ * @param {Boolean|Function} [options.isRequired] enable/disable the validator, or function that returns required boolean
+ * @param {Function} [options.ErrorConstructor] custom error constructor. The constructor receives 1 parameter, an object containing the validator properties.
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @see SchemaArray#checkRequired #schema_array_SchemaArray.checkRequired
+ * @see SchemaBoolean#checkRequired #schema_boolean_SchemaBoolean-checkRequired
+ * @see SchemaBuffer#checkRequired #schema_buffer_SchemaBuffer.schemaName
+ * @see SchemaNumber#checkRequired #schema_number_SchemaNumber-min
+ * @see SchemaObjectId#checkRequired #schema_objectid_ObjectId-auto
+ * @see SchemaString#checkRequired #schema_string_SchemaString-checkRequired
+ * @api public
+ */
+
+SchemaType.prototype.required = function(required, message) {
+ let customOptions = {};
+ if (typeof required === 'object') {
+ customOptions = required;
+ message = customOptions.message || message;
+ required = required.isRequired;
+ }
+
+ if (required === false) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.requiredValidator;
+ }, this);
+
+ this.isRequired = false;
+ delete this.originalRequiredValue;
+ return this;
+ }
+
+ const _this = this;
+ this.isRequired = true;
+
+ this.requiredValidator = function(v) {
+ const cachedRequired = get(this, '$__.cachedRequired');
+
+ // no validation when this path wasn't selected in the query.
+ if (cachedRequired != null && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
+ return true;
+ }
+
+ // `$cachedRequired` gets set in `_evaluateRequiredFunctions()` so we
+ // don't call required functions multiple times in one validate call
+ // See gh-6801
+ if (cachedRequired != null && _this.path in cachedRequired) {
+ const res = cachedRequired[_this.path] ?
+ _this.checkRequired(v, this) :
+ true;
+ delete cachedRequired[_this.path];
+ return res;
+ } else if (typeof required === 'function') {
+ return required.apply(this) ? _this.checkRequired(v, this) : true;
+ }
+
+ return _this.checkRequired(v, this);
+ };
+ this.originalRequiredValue = required;
+
+ if (typeof required === 'string') {
+ message = required;
+ required = undefined;
+ }
+
+ const msg = message || MongooseError.messages.general.required;
+ this.validators.unshift(Object.assign({}, customOptions, {
+ validator: this.requiredValidator,
+ message: msg,
+ type: 'required'
+ }));
+
+ return this;
+};
+
+/**
+ * Set the model that this path refers to. This is the option that [populate](https://mongoosejs.com/docs/populate.html)
+ * looks at to determine the foreign collection it should query.
+ *
+ * ####Example:
+ * const userSchema = new Schema({ name: String });
+ * const User = mongoose.model('User', userSchema);
+ *
+ * const postSchema = new Schema({ user: mongoose.ObjectId });
+ * postSchema.path('user').ref('User'); // By model name
+ * postSchema.path('user').ref(User); // Can pass the model as well
+ *
+ * // Or you can just declare the `ref` inline in your schema
+ * const postSchema2 = new Schema({
+ * user: { type: mongoose.ObjectId, ref: User }
+ * });
+ *
+ * @param {String|Model|Function} ref either a model name, a [Model](https://mongoosejs.com/docs/models.html), or a function that returns a model name or model.
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.ref = function(ref) {
+ this.options.ref = ref;
+ return this;
+};
+
+/**
+ * Gets the default value
+ *
+ * @param {Object} scope the scope which callback are executed
+ * @param {Boolean} init
+ * @api private
+ */
+
+SchemaType.prototype.getDefault = function(scope, init) {
+ let ret = typeof this.defaultValue === 'function'
+ ? this.defaultValue.call(scope)
+ : this.defaultValue;
+
+ if (ret !== null && ret !== undefined) {
+ if (typeof ret === 'object' && (!this.options || !this.options.shared)) {
+ ret = utils.clone(ret);
+ }
+
+ const casted = this.cast(ret, scope, init);
+ if (casted && casted.$isSingleNested) {
+ casted.$parent = scope;
+ }
+ return casted;
+ }
+ return ret;
+};
+
+/*!
+ * Applies setters without casting
+ *
+ * @api private
+ */
+
+SchemaType.prototype._applySetters = function(value, scope, init, priorVal) {
+ let v = value;
+ const setters = this.setters;
+ let len = setters.length;
+ const caster = this.caster;
+
+ while (len--) {
+ v = setters[len].call(scope, v, this);
+ }
+
+ if (Array.isArray(v) && caster && caster.setters) {
+ const newVal = [];
+ for (let i = 0; i < v.length; i++) {
+ newVal.push(caster.applySetters(v[i], scope, init, priorVal));
+ }
+ v = newVal;
+ }
+
+ return v;
+};
+
+/**
+ * Applies setters
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @param {Boolean} init
+ * @api private
+ */
+
+SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) {
+ let v = this._applySetters(value, scope, init, priorVal, options);
+
+ if (v == null) {
+ return v;
+ }
+
+ // do not cast until all setters are applied #665
+ v = this.cast(v, scope, init, priorVal, options);
+
+ return v;
+};
+
+/**
+ * Applies getters to a value
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @api private
+ */
+
+SchemaType.prototype.applyGetters = function(value, scope) {
+ let v = value;
+ const getters = this.getters;
+ const len = getters.length;
+
+ if (len === 0) {
+ return v;
+ }
+
+ for (let i = 0; i < len; ++i) {
+ v = getters[i].call(scope, v, this);
+ }
+
+ return v;
+};
+
+/**
+ * Sets default `select()` behavior for this path.
+ *
+ * Set to `true` if this path should always be included in the results, `false` if it should be excluded by default. This setting can be overridden at the query level.
+ *
+ * ####Example:
+ *
+ * T = db.model('T', new Schema({ x: { type: String, select: true }}));
+ * T.find(..); // field x will always be selected ..
+ * // .. unless overridden;
+ * T.find().select('-x').exec(callback);
+ *
+ * @param {Boolean} val
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.select = function select(val) {
+ this.selected = !!val;
+ return this;
+};
+
+/**
+ * Performs a validation of `value` using the validators declared for this SchemaType.
+ *
+ * @param {any} value
+ * @param {Function} callback
+ * @param {Object} scope
+ * @api private
+ */
+
+SchemaType.prototype.doValidate = function(value, fn, scope) {
+ let err = false;
+ const path = this.path;
+ let count = this.validators.length;
+
+ if (!count) {
+ return fn(null);
+ }
+
+ const validate = function(ok, validatorProperties) {
+ if (err) {
+ return;
+ }
+ if (ok === undefined || ok) {
+ if (--count <= 0) {
+ immediate(function() {
+ fn(null);
+ });
+ }
+ } else {
+ const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
+ err = new ErrorConstructor(validatorProperties);
+ err[validatorErrorSymbol] = true;
+ immediate(function() {
+ fn(err);
+ });
+ }
+ };
+
+ const _this = this;
+ this.validators.forEach(function(v) {
+ if (err) {
+ return;
+ }
+
+ const validator = v.validator;
+ let ok;
+
+ const validatorProperties = utils.clone(v);
+ validatorProperties.path = path;
+ validatorProperties.value = value;
+
+ if (validator instanceof RegExp) {
+ validate(validator.test(value), validatorProperties);
+ } else if (typeof validator === 'function') {
+ if (value === undefined && validator !== _this.requiredValidator) {
+ validate(true, validatorProperties);
+ return;
+ }
+ if (validatorProperties.isAsync) {
+ asyncValidate(validator, scope, value, validatorProperties, validate);
+ } else {
+ try {
+ if (validatorProperties.propsParameter) {
+ ok = validator.call(scope, value, validatorProperties);
+ } else {
+ ok = validator.call(scope, value);
+ }
+ } catch (error) {
+ ok = false;
+ validatorProperties.reason = error;
+ if (error.message) {
+ validatorProperties.message = error.message;
+ }
+ }
+ if (ok != null && typeof ok.then === 'function') {
+ ok.then(
+ function(ok) { validate(ok, validatorProperties); },
+ function(error) {
+ validatorProperties.reason = error;
+ ok = false;
+ validate(ok, validatorProperties);
+ });
+ } else {
+ validate(ok, validatorProperties);
+ }
+ }
+ }
+ });
+};
+
+/*!
+ * Handle async validators
+ */
+
+function asyncValidate(validator, scope, value, props, cb) {
+ let called = false;
+ const returnVal = validator.call(scope, value, function(ok, customMsg) {
+ if (called) {
+ return;
+ }
+ called = true;
+ if (customMsg) {
+ props.message = customMsg;
+ }
+ cb(ok, props);
+ });
+ if (typeof returnVal === 'boolean') {
+ called = true;
+ cb(returnVal, props);
+ } else if (returnVal && typeof returnVal.then === 'function') {
+ // Promise
+ returnVal.then(
+ function(ok) {
+ if (called) {
+ return;
+ }
+ called = true;
+ cb(ok, props);
+ },
+ function(error) {
+ if (called) {
+ return;
+ }
+ called = true;
+
+ props.reason = error;
+ cb(false, props);
+ });
+ }
+}
+
+/**
+ * Performs a validation of `value` using the validators declared for this SchemaType.
+ *
+ * ####Note:
+ *
+ * This method ignores the asynchronous validators.
+ *
+ * @param {any} value
+ * @param {Object} scope
+ * @return {MongooseError|undefined}
+ * @api private
+ */
+
+SchemaType.prototype.doValidateSync = function(value, scope) {
+ let err = null;
+ const path = this.path;
+ const count = this.validators.length;
+
+ if (!count) {
+ return null;
+ }
+
+ const validate = function(ok, validatorProperties) {
+ if (err) {
+ return;
+ }
+ if (ok !== undefined && !ok) {
+ const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
+ err = new ErrorConstructor(validatorProperties);
+ err[validatorErrorSymbol] = true;
+ }
+ };
+
+ let validators = this.validators;
+ if (value === void 0) {
+ if (this.validators.length > 0 && this.validators[0].type === 'required') {
+ validators = [this.validators[0]];
+ } else {
+ return null;
+ }
+ }
+
+ validators.forEach(function(v) {
+ if (err) {
+ return;
+ }
+
+ const validator = v.validator;
+ const validatorProperties = utils.clone(v);
+ validatorProperties.path = path;
+ validatorProperties.value = value;
+ let ok;
+
+ // Skip any explicit async validators. Validators that return a promise
+ // will still run, but won't trigger any errors.
+ if (validator.isAsync) {
+ return;
+ }
+
+ if (validator instanceof RegExp) {
+ validate(validator.test(value), validatorProperties);
+ } else if (typeof validator === 'function') {
+ try {
+ if (validatorProperties.propsParameter) {
+ ok = validator.call(scope, value, validatorProperties);
+ } else {
+ ok = validator.call(scope, value);
+ }
+ } catch (error) {
+ ok = false;
+ validatorProperties.reason = error;
+ }
+
+ // Skip any validators that return a promise, we can't handle those
+ // synchronously
+ if (ok != null && typeof ok.then === 'function') {
+ return;
+ }
+ validate(ok, validatorProperties);
+ }
+ });
+
+ return err;
+};
+
+/**
+ * Determines if value is a valid Reference.
+ *
+ * @param {SchemaType} self
+ * @param {Object} value
+ * @param {Document} doc
+ * @param {Boolean} init
+ * @return {Boolean}
+ * @api private
+ */
+
+SchemaType._isRef = function(self, value, doc, init) {
+ // fast path
+ let ref = init && self.options && (self.options.ref || self.options.refPath);
+
+ if (!ref && doc && doc.$__ != null) {
+ // checks for
+ // - this populated with adhoc model and no ref was set in schema OR
+ // - setting / pushing values after population
+ const path = doc.$__fullPath(self.path);
+ const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
+ ref = owner.populated(path);
+ }
+
+ if (ref) {
+ if (value == null) {
+ return true;
+ }
+ if (!Buffer.isBuffer(value) && // buffers are objects too
+ value._bsontype !== 'Binary' // raw binary value from the db
+ && utils.isObject(value) // might have deselected _id in population query
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/*!
+ * ignore
+ */
+
+function handleSingle(val) {
+ return this.castForQuery(val);
+}
+
+/*!
+ * ignore
+ */
+
+function handleArray(val) {
+ const _this = this;
+ if (!Array.isArray(val)) {
+ return [this.castForQuery(val)];
+ }
+ return val.map(function(m) {
+ return _this.castForQuery(m);
+ });
+}
+
+/*!
+ * Just like handleArray, except also allows `[]` because surprisingly
+ * `$in: [1, []]` works fine
+ */
+
+function handle$in(val) {
+ const _this = this;
+ if (!Array.isArray(val)) {
+ return [this.castForQuery(val)];
+ }
+ return val.map(function(m) {
+ if (Array.isArray(m) && m.length === 0) {
+ return m;
+ }
+ return _this.castForQuery(m);
+ });
+}
+
+/*!
+ * ignore
+ */
+
+SchemaType.prototype.$conditionalHandlers = {
+ $all: handleArray,
+ $eq: handleSingle,
+ $in: handle$in,
+ $ne: handleSingle,
+ $nin: handleArray,
+ $exists: $exists,
+ $type: $type
+};
+
+/*!
+ * Wraps `castForQuery` to handle context
+ */
+
+SchemaType.prototype.castForQueryWrapper = function(params) {
+ this.$$context = params.context;
+ if ('$conditional' in params) {
+ return this.castForQuery(params.$conditional, params.val);
+ }
+ if (params.$skipQueryCastForUpdate) {
+ return this._castForQuery(params.val);
+ }
+ return this.castForQuery(params.val);
+};
+
+/**
+ * Cast the given value with the given optional query operator.
+ *
+ * @param {String} [$conditional] query operator, like `$eq` or `$in`
+ * @param {any} val
+ * @api private
+ */
+
+SchemaType.prototype.castForQuery = function($conditional, val) {
+ let handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional);
+ }
+ return handler.call(this, val);
+ }
+ val = $conditional;
+ return this._castForQuery(val);
+};
+
+/*!
+ * Internal switch for runSetters
+ *
+ * @api private
+ */
+
+SchemaType.prototype._castForQuery = function(val) {
+ return this.applySetters(val, this.$$context);
+};
+
+/**
+ * Override the function the required validator uses to check whether a string
+ * passes the `required` check.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ * @static
+ * @receiver SchemaType
+ * @function checkRequired
+ * @api public
+ */
+
+SchemaType.checkRequired = function(fn) {
+ if (arguments.length > 0) {
+ this._checkRequired = fn;
+ }
+
+ return this._checkRequired;
+};
+
+/**
+ * Default check for if this path satisfies the `required` validator.
+ *
+ * @param {any} val
+ * @api private
+ */
+
+SchemaType.prototype.checkRequired = function(val) {
+ return val != null;
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = SchemaType;
+
+exports.CastError = CastError;
+
+exports.ValidatorError = ValidatorError;
diff --git a/node_modules/mongoose/lib/statemachine.js b/node_modules/mongoose/lib/statemachine.js
new file mode 100644
index 0000000..7e36dc1
--- /dev/null
+++ b/node_modules/mongoose/lib/statemachine.js
@@ -0,0 +1,180 @@
+
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const utils = require('./utils');
+
+/*!
+ * StateMachine represents a minimal `interface` for the
+ * constructors it builds via StateMachine.ctor(...).
+ *
+ * @api private
+ */
+
+const StateMachine = module.exports = exports = function StateMachine() {
+};
+
+/*!
+ * StateMachine.ctor('state1', 'state2', ...)
+ * A factory method for subclassing StateMachine.
+ * The arguments are a list of states. For each state,
+ * the constructor's prototype gets state transition
+ * methods named after each state. These transition methods
+ * place their path argument into the given state.
+ *
+ * @param {String} state
+ * @param {String} [state]
+ * @return {Function} subclass constructor
+ * @private
+ */
+
+StateMachine.ctor = function() {
+ const states = utils.args(arguments);
+
+ const ctor = function() {
+ StateMachine.apply(this, arguments);
+ this.paths = {};
+ this.states = {};
+ this.stateNames = states;
+
+ let i = states.length,
+ state;
+
+ while (i--) {
+ state = states[i];
+ this.states[state] = {};
+ }
+ };
+
+ ctor.prototype = new StateMachine();
+
+ states.forEach(function(state) {
+ // Changes the `path`'s state to `state`.
+ ctor.prototype[state] = function(path) {
+ this._changeState(path, state);
+ };
+ });
+
+ return ctor;
+};
+
+/*!
+ * This function is wrapped by the state change functions:
+ *
+ * - `require(path)`
+ * - `modify(path)`
+ * - `init(path)`
+ *
+ * @api private
+ */
+
+StateMachine.prototype._changeState = function _changeState(path, nextState) {
+ const prevBucket = this.states[this.paths[path]];
+ if (prevBucket) delete prevBucket[path];
+
+ this.paths[path] = nextState;
+ this.states[nextState][path] = true;
+};
+
+/*!
+ * ignore
+ */
+
+StateMachine.prototype.clear = function clear(state) {
+ const keys = Object.keys(this.states[state]);
+ let i = keys.length;
+ let path;
+
+ while (i--) {
+ path = keys[i];
+ delete this.states[state][path];
+ delete this.paths[path];
+ }
+};
+
+/*!
+ * Checks to see if at least one path is in the states passed in via `arguments`
+ * e.g., this.some('required', 'inited')
+ *
+ * @param {String} state that we want to check for.
+ * @private
+ */
+
+StateMachine.prototype.some = function some() {
+ const _this = this;
+ const what = arguments.length ? arguments : this.stateNames;
+ return Array.prototype.some.call(what, function(state) {
+ return Object.keys(_this.states[state]).length;
+ });
+};
+
+/*!
+ * This function builds the functions that get assigned to `forEach` and `map`,
+ * since both of those methods share a lot of the same logic.
+ *
+ * @param {String} iterMethod is either 'forEach' or 'map'
+ * @return {Function}
+ * @api private
+ */
+
+StateMachine.prototype._iter = function _iter(iterMethod) {
+ return function() {
+ const numArgs = arguments.length;
+ let states = utils.args(arguments, 0, numArgs - 1);
+ const callback = arguments[numArgs - 1];
+
+ if (!states.length) states = this.stateNames;
+
+ const _this = this;
+
+ const paths = states.reduce(function(paths, state) {
+ return paths.concat(Object.keys(_this.states[state]));
+ }, []);
+
+ return paths[iterMethod](function(path, i, paths) {
+ return callback(path, i, paths);
+ });
+ };
+};
+
+/*!
+ * Iterates over the paths that belong to one of the parameter states.
+ *
+ * The function profile can look like:
+ * this.forEach(state1, fn); // iterates over all paths in state1
+ * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2
+ * this.forEach(fn); // iterates over all paths in all states
+ *
+ * @param {String} [state]
+ * @param {String} [state]
+ * @param {Function} callback
+ * @private
+ */
+
+StateMachine.prototype.forEach = function forEach() {
+ this.forEach = this._iter('forEach');
+ return this.forEach.apply(this, arguments);
+};
+
+/*!
+ * Maps over the paths that belong to one of the parameter states.
+ *
+ * The function profile can look like:
+ * this.forEach(state1, fn); // iterates over all paths in state1
+ * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2
+ * this.forEach(fn); // iterates over all paths in all states
+ *
+ * @param {String} [state]
+ * @param {String} [state]
+ * @param {Function} callback
+ * @return {Array}
+ * @private
+ */
+
+StateMachine.prototype.map = function map() {
+ this.map = this._iter('map');
+ return this.map.apply(this, arguments);
+};
diff --git a/node_modules/mongoose/lib/types/array.js b/node_modules/mongoose/lib/types/array.js
new file mode 100644
index 0000000..26f03a5
--- /dev/null
+++ b/node_modules/mongoose/lib/types/array.js
@@ -0,0 +1,848 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const EmbeddedDocument = require('./embedded');
+const Document = require('../document');
+const ObjectId = require('./objectid');
+const cleanModifiedSubpaths = require('../helpers/document/cleanModifiedSubpaths');
+const get = require('../helpers/get');
+const internalToObjectOptions = require('../options').internalToObjectOptions;
+const utils = require('../utils');
+const util = require('util');
+
+const isMongooseObject = utils.isMongooseObject;
+
+/**
+ * Mongoose Array constructor.
+ *
+ * ####NOTE:
+ *
+ * _Values always have to be passed to the constructor to initialize, otherwise `MongooseArray#push` will mark the array as modified._
+ *
+ * @param {Array} values
+ * @param {String} path
+ * @param {Document} doc parent document
+ * @api private
+ * @inherits Array
+ * @see http://bit.ly/f6CnZU
+ */
+
+function MongooseArray(values, path, doc) {
+ const arr = [].concat(values);
+
+ const keysMA = Object.keys(MongooseArray.mixin);
+ const numKeys = keysMA.length;
+ for (let i = 0; i < numKeys; ++i) {
+ arr[keysMA[i]] = MongooseArray.mixin[keysMA[i]];
+ }
+
+ arr._path = path;
+ arr.isMongooseArray = true;
+ arr.validators = [];
+ arr._atomics = {};
+ arr._schema = void 0;
+ if (util.inspect.custom) {
+ arr[util.inspect.custom] = arr.inspect;
+ }
+
+ // Because doc comes from the context of another function, doc === global
+ // can happen if there was a null somewhere up the chain (see #3020)
+ // RB Jun 17, 2015 updated to check for presence of expected paths instead
+ // to make more proof against unusual node environments
+ if (doc && doc instanceof Document) {
+ arr._parent = doc;
+ arr._schema = doc.schema.path(path);
+ }
+
+ return arr;
+}
+
+MongooseArray.mixin = {
+ /*!
+ * ignore
+ */
+ toBSON: function() {
+ return this.toObject(internalToObjectOptions);
+ },
+
+ /**
+ * Stores a queue of atomic operations to perform
+ *
+ * @property _atomics
+ * @api private
+ */
+
+ _atomics: undefined,
+
+ /**
+ * Parent owner document
+ *
+ * @property _parent
+ * @api private
+ * @memberOf MongooseArray
+ */
+
+ _parent: undefined,
+
+ /**
+ * Casts a member based on this arrays schema.
+ *
+ * @param {any} value
+ * @return value the casted value
+ * @method _cast
+ * @api private
+ * @memberOf MongooseArray
+ */
+
+ _cast: function(value) {
+ let populated = false;
+ let Model;
+
+ if (this._parent) {
+ populated = this._parent.populated(this._path, true);
+ }
+
+ if (populated && value !== null && value !== undefined) {
+ // cast to the populated Models schema
+ Model = populated.options.model || populated.options.Model;
+
+ // only objects are permitted so we can safely assume that
+ // non-objects are to be interpreted as _id
+ if (Buffer.isBuffer(value) ||
+ value instanceof ObjectId || !utils.isObject(value)) {
+ value = {_id: value};
+ }
+
+ // gh-2399
+ // we should cast model only when it's not a discriminator
+ const isDisc = value.schema && value.schema.discriminatorMapping &&
+ value.schema.discriminatorMapping.key !== undefined;
+ if (!isDisc) {
+ value = new Model(value);
+ }
+ return this._schema.caster.applySetters(value, this._parent, true);
+ }
+
+ return this._schema.caster.applySetters(value, this._parent, false);
+ },
+
+ /**
+ * Marks this array as modified.
+ *
+ * If it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments)
+ *
+ * @param {EmbeddedDocument} embeddedDoc the embedded doc that invoked this method on the Array
+ * @param {String} embeddedPath the path which changed in the embeddedDoc
+ * @method _markModified
+ * @api private
+ * @memberOf MongooseArray
+ */
+
+ _markModified: function(elem, embeddedPath) {
+ const parent = this._parent;
+ let dirtyPath;
+
+ if (parent) {
+ dirtyPath = this._path;
+
+ if (arguments.length) {
+ if (embeddedPath != null) {
+ // an embedded doc bubbled up the change
+ dirtyPath = dirtyPath + '.' + this.indexOf(elem) + '.' + embeddedPath;
+ } else {
+ // directly set an index
+ dirtyPath = dirtyPath + '.' + elem;
+ }
+ }
+
+ parent.markModified(dirtyPath, arguments.length > 0 ? elem : parent);
+ }
+
+ return this;
+ },
+
+ /**
+ * Register an atomic operation with the parent.
+ *
+ * @param {Array} op operation
+ * @param {any} val
+ * @method _registerAtomic
+ * @api private
+ * @memberOf MongooseArray
+ */
+
+ _registerAtomic: function(op, val) {
+ if (op === '$set') {
+ // $set takes precedence over all other ops.
+ // mark entire array modified.
+ this._atomics = {$set: val};
+ return this;
+ }
+
+ const atomics = this._atomics;
+
+ // reset pop/shift after save
+ if (op === '$pop' && !('$pop' in atomics)) {
+ const _this = this;
+ this._parent.once('save', function() {
+ _this._popped = _this._shifted = null;
+ });
+ }
+
+ // check for impossible $atomic combos (Mongo denies more than one
+ // $atomic op on a single path
+ if (this._atomics.$set ||
+ Object.keys(atomics).length && !(op in atomics)) {
+ // a different op was previously registered.
+ // save the entire thing.
+ this._atomics = {$set: this};
+ return this;
+ }
+
+ let selector;
+
+ if (op === '$pullAll' || op === '$addToSet') {
+ atomics[op] || (atomics[op] = []);
+ atomics[op] = atomics[op].concat(val);
+ } else if (op === '$pullDocs') {
+ const pullOp = atomics['$pull'] || (atomics['$pull'] = {});
+ if (val[0] instanceof EmbeddedDocument) {
+ selector = pullOp['$or'] || (pullOp['$or'] = []);
+ Array.prototype.push.apply(selector, val.map(function(v) {
+ return v.toObject({transform: false, virtuals: false});
+ }));
+ } else {
+ selector = pullOp['_id'] || (pullOp['_id'] = {$in: []});
+ selector['$in'] = selector['$in'].concat(val);
+ }
+ } else if (op === '$push') {
+ atomics.$push = atomics.$push || { $each: [] };
+ atomics.$push.$each = atomics.$push.$each.concat(val);
+ } else {
+ atomics[op] = val;
+ }
+
+ return this;
+ },
+
+ /**
+ * Depopulates stored atomic operation values as necessary for direct insertion to MongoDB.
+ *
+ * If no atomics exist, we return all array values after conversion.
+ *
+ * @return {Array}
+ * @method $__getAtomics
+ * @memberOf MongooseArray
+ * @instance
+ * @api private
+ */
+
+ $__getAtomics: function() {
+ const ret = [];
+ const keys = Object.keys(this._atomics);
+ let i = keys.length;
+
+ const opts = Object.assign({}, internalToObjectOptions, { _isNested: true });
+
+ if (i === 0) {
+ ret[0] = ['$set', this.toObject(opts)];
+ return ret;
+ }
+
+ while (i--) {
+ const op = keys[i];
+ let val = this._atomics[op];
+
+ // the atomic values which are arrays are not MongooseArrays. we
+ // need to convert their elements as if they were MongooseArrays
+ // to handle populated arrays versus DocumentArrays properly.
+ if (isMongooseObject(val)) {
+ val = val.toObject(opts);
+ } else if (Array.isArray(val)) {
+ val = this.toObject.call(val, opts);
+ } else if (val != null && Array.isArray(val.$each)) {
+ val.$each = this.toObject.call(val.$each, opts);
+ } else if (val.valueOf) {
+ val = val.valueOf();
+ }
+
+ if (op === '$addToSet') {
+ val = {$each: val};
+ }
+
+ ret.push([op, val]);
+ }
+
+ return ret;
+ },
+
+ /**
+ * Returns the number of pending atomic operations to send to the db for this array.
+ *
+ * @api private
+ * @return {Number}
+ * @method hasAtomics
+ * @memberOf MongooseArray
+ */
+
+ hasAtomics: function hasAtomics() {
+ if (!(this._atomics && this._atomics.constructor.name === 'Object')) {
+ return 0;
+ }
+
+ return Object.keys(this._atomics).length;
+ },
+
+ /**
+ * Internal helper for .map()
+ *
+ * @api private
+ * @return {Number}
+ * @method _mapCast
+ * @memberOf MongooseArray
+ */
+ _mapCast: function(val, index) {
+ return this._cast(val, this.length + index);
+ },
+
+ /**
+ * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
+ *
+ * @param {Object} [args...]
+ * @api public
+ * @method push
+ * @memberOf MongooseArray
+ */
+
+ push: function() {
+ _checkManualPopulation(this, arguments);
+ let values = [].map.call(arguments, this._mapCast, this);
+ values = this._schema.applySetters(values, this._parent, undefined,
+ undefined, { skipDocumentArrayCast: true });
+ const ret = [].push.apply(this, values);
+
+ this._registerAtomic('$push', values);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Pushes items to the array non-atomically.
+ *
+ * ####NOTE:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @param {any} [args...]
+ * @api public
+ * @method nonAtomicPush
+ * @memberOf MongooseArray
+ */
+
+ nonAtomicPush: function() {
+ const values = [].map.call(arguments, this._mapCast, this);
+ const ret = [].push.apply(this, values);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Pops the array atomically at most one time per document `save()`.
+ *
+ * #### NOTE:
+ *
+ * _Calling this mulitple times on an array before saving sends the same command as calling it once._
+ * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
+ *
+ * doc.array = [1,2,3];
+ *
+ * var popped = doc.array.$pop();
+ * console.log(popped); // 3
+ * console.log(doc.array); // [1,2]
+ *
+ * // no affect
+ * popped = doc.array.$pop();
+ * console.log(doc.array); // [1,2]
+ *
+ * doc.save(function (err) {
+ * if (err) return handleError(err);
+ *
+ * // we saved, now $pop works again
+ * popped = doc.array.$pop();
+ * console.log(popped); // 2
+ * console.log(doc.array); // [1]
+ * })
+ *
+ * @api public
+ * @method $pop
+ * @memberOf MongooseArray
+ * @instance
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
+ * @method $pop
+ * @memberOf MongooseArray
+ */
+
+ $pop: function() {
+ this._registerAtomic('$pop', 1);
+ this._markModified();
+
+ // only allow popping once
+ if (this._popped) {
+ return;
+ }
+ this._popped = true;
+
+ return [].pop.call(this);
+ },
+
+ /**
+ * Wraps [`Array#pop`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking.
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified which will pass the entire thing to $set potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @see MongooseArray#$pop #types_array_MongooseArray-%24pop
+ * @api public
+ * @method pop
+ * @memberOf MongooseArray
+ */
+
+ pop: function() {
+ const ret = [].pop.call(this);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Atomically shifts the array at most one time per document `save()`.
+ *
+ * ####NOTE:
+ *
+ * _Calling this mulitple times on an array before saving sends the same command as calling it once._
+ * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
+ *
+ * doc.array = [1,2,3];
+ *
+ * var shifted = doc.array.$shift();
+ * console.log(shifted); // 1
+ * console.log(doc.array); // [2,3]
+ *
+ * // no affect
+ * shifted = doc.array.$shift();
+ * console.log(doc.array); // [2,3]
+ *
+ * doc.save(function (err) {
+ * if (err) return handleError(err);
+ *
+ * // we saved, now $shift works again
+ * shifted = doc.array.$shift();
+ * console.log(shifted ); // 2
+ * console.log(doc.array); // [3]
+ * })
+ *
+ * @api public
+ * @memberOf MongooseArray
+ * @instance
+ * @method $shift
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
+ */
+
+ $shift: function $shift() {
+ this._registerAtomic('$pop', -1);
+ this._markModified();
+
+ // only allow shifting once
+ if (this._shifted) {
+ return;
+ }
+ this._shifted = true;
+
+ return [].shift.call(this);
+ },
+
+ /**
+ * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
+ *
+ * ####Example:
+ *
+ * doc.array = [2,3];
+ * var res = doc.array.shift();
+ * console.log(res) // 2
+ * console.log(doc.array) // [3]
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method shift
+ * @memberOf MongooseArray
+ */
+
+ shift: function() {
+ const ret = [].shift.call(this);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Pulls items from the array atomically. Equality is determined by casting
+ * the provided value to an embedded document and comparing using
+ * [the `Document.equals()` function.](./api.html#document_Document-equals)
+ *
+ * ####Examples:
+ *
+ * doc.array.pull(ObjectId)
+ * doc.array.pull({ _id: 'someId' })
+ * doc.array.pull(36)
+ * doc.array.pull('tag 1', 'tag 2')
+ *
+ * To remove a document from a subdocument array we may pass an object with a matching `_id`.
+ *
+ * doc.subdocs.push({ _id: 4815162342 })
+ * doc.subdocs.pull({ _id: 4815162342 }) // removed
+ *
+ * Or we may passing the _id directly and let mongoose take care of it.
+ *
+ * doc.subdocs.push({ _id: 4815162342 })
+ * doc.subdocs.pull(4815162342); // works
+ *
+ * The first pull call will result in a atomic operation on the database, if pull is called repeatedly without saving the document, a $set operation is used on the complete array instead, overwriting possible changes that happened on the database in the meantime.
+ *
+ * @param {any} [args...]
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
+ * @api public
+ * @method pull
+ * @memberOf MongooseArray
+ */
+
+ pull: function() {
+ const values = [].map.call(arguments, this._cast, this);
+ const cur = this._parent.get(this._path);
+ let i = cur.length;
+ let mem;
+
+ while (i--) {
+ mem = cur[i];
+ if (mem instanceof Document) {
+ const some = values.some(function(v) {
+ return mem.equals(v);
+ });
+ if (some) {
+ [].splice.call(cur, i, 1);
+ }
+ } else if (~cur.indexOf.call(values, mem)) {
+ [].splice.call(cur, i, 1);
+ }
+ }
+
+ if (values[0] instanceof EmbeddedDocument) {
+ this._registerAtomic('$pullDocs', values.map(function(v) {
+ return v._id || v;
+ }));
+ } else {
+ this._registerAtomic('$pullAll', values);
+ }
+
+ this._markModified();
+
+ // Might have modified child paths and then pulled, like
+ // `doc.children[1].name = 'test';` followed by
+ // `doc.children.remove(doc.children[0]);`. In this case we fall back
+ // to a `$set` on the whole array. See #3511
+ if (cleanModifiedSubpaths(this._parent, this._path) > 0) {
+ this._registerAtomic('$set', this);
+ }
+
+ return this;
+ },
+
+ /**
+ * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method splice
+ * @memberOf MongooseArray
+ */
+
+ splice: function splice() {
+ let ret;
+
+ _checkManualPopulation(this, Array.prototype.slice.call(arguments, 2));
+
+ if (arguments.length) {
+ const vals = [];
+ for (let i = 0; i < arguments.length; ++i) {
+ vals[i] = i < 2 ?
+ arguments[i] :
+ this._cast(arguments[i], arguments[0] + (i - 2));
+ }
+ ret = [].splice.apply(this, vals);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ cleanModifiedSubpaths(this._parent, this._path);
+ }
+
+ return ret;
+ },
+
+ /**
+ * Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method unshift
+ * @memberOf MongooseArray
+ */
+
+ unshift: function() {
+ _checkManualPopulation(this, arguments);
+
+ let values = [].map.call(arguments, this._cast, this);
+ values = this._schema.applySetters(values, this._parent);
+ [].unshift.apply(this, values);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return this.length;
+ },
+
+ /**
+ * Wraps [`Array#sort`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort) with proper change tracking.
+ *
+ * ####NOTE:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method sort
+ * @memberOf MongooseArray
+ */
+
+ sort: function() {
+ const ret = [].sort.apply(this, arguments);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Adds values to the array if not already present.
+ *
+ * ####Example:
+ *
+ * console.log(doc.array) // [2,3,4]
+ * var added = doc.array.addToSet(4,5);
+ * console.log(doc.array) // [2,3,4,5]
+ * console.log(added) // [5]
+ *
+ * @param {any} [args...]
+ * @return {Array} the values that were added
+ * @memberOf MongooseArray
+ * @api public
+ * @method addToSet
+ */
+
+ addToSet: function addToSet() {
+ _checkManualPopulation(this, arguments);
+
+ let values = [].map.call(arguments, this._mapCast, this);
+ values = this._schema.applySetters(values, this._parent);
+ const added = [];
+ let type = '';
+ if (values[0] instanceof EmbeddedDocument) {
+ type = 'doc';
+ } else if (values[0] instanceof Date) {
+ type = 'date';
+ }
+
+ values.forEach(function(v) {
+ let found;
+ const val = +v;
+ switch (type) {
+ case 'doc':
+ found = this.some(function(doc) {
+ return doc.equals(v);
+ });
+ break;
+ case 'date':
+ found = this.some(function(d) {
+ return +d === val;
+ });
+ break;
+ default:
+ found = ~this.indexOf(v);
+ }
+
+ if (!found) {
+ [].push.call(this, v);
+ this._registerAtomic('$addToSet', v);
+ this._markModified();
+ [].push.call(added, v);
+ }
+ }, this);
+
+ return added;
+ },
+
+ /**
+ * Sets the casted `val` at index `i` and marks the array modified.
+ *
+ * ####Example:
+ *
+ * // given documents based on the following
+ * var Doc = mongoose.model('Doc', new Schema({ array: [Number] }));
+ *
+ * var doc = new Doc({ array: [2,3,4] })
+ *
+ * console.log(doc.array) // [2,3,4]
+ *
+ * doc.array.set(1,"5");
+ * console.log(doc.array); // [2,5,4] // properly cast to number
+ * doc.save() // the change is saved
+ *
+ * // VS not using array#set
+ * doc.array[1] = "5";
+ * console.log(doc.array); // [2,"5",4] // no casting
+ * doc.save() // change is not saved
+ *
+ * @return {Array} this
+ * @api public
+ * @method set
+ * @memberOf MongooseArray
+ */
+
+ set: function set(i, val) {
+ const value = this._cast(val, i);
+ this[i] = value;
+ this._markModified(i);
+ return this;
+ },
+
+ /**
+ * Returns a native js Array.
+ *
+ * @param {Object} options
+ * @return {Array}
+ * @api public
+ * @method toObject
+ * @memberOf MongooseArray
+ */
+
+ toObject: function(options) {
+ if (options && options.depopulate) {
+ options = utils.clone(options);
+ options._isNested = true;
+ return this.map(function(doc) {
+ return doc instanceof Document
+ ? doc.toObject(options)
+ : doc;
+ });
+ }
+
+ return this.slice();
+ },
+
+ /**
+ * Helper for console.log
+ *
+ * @api public
+ * @method inspect
+ * @memberOf MongooseArray
+ */
+
+ inspect: function() {
+ return JSON.stringify(this);
+ },
+
+ /**
+ * Return the index of `obj` or `-1` if not found.
+ *
+ * @param {Object} obj the item to look for
+ * @return {Number}
+ * @api public
+ * @method indexOf
+ * @memberOf MongooseArray
+ */
+
+ indexOf: function indexOf(obj) {
+ if (obj instanceof ObjectId) {
+ obj = obj.toString();
+ }
+ for (let i = 0, len = this.length; i < len; ++i) {
+ if (obj == this[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+};
+
+/**
+ * Alias of [pull](#types_array_MongooseArray-pull)
+ *
+ * @see MongooseArray#pull #types_array_MongooseArray-pull
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
+ * @api public
+ * @memberOf MongooseArray
+ * @instance
+ * @method remove
+ */
+
+MongooseArray.mixin.remove = MongooseArray.mixin.pull;
+
+/*!
+ * ignore
+ */
+
+function _isAllSubdocs(docs, ref) {
+ if (!ref) {
+ return false;
+ }
+ for (let i = 0; i < docs.length; ++i) {
+ const arg = docs[i];
+ if (arg == null) {
+ return false;
+ }
+ const model = arg.constructor;
+ if (!(arg instanceof Document) ||
+ (model.modelName !== ref && model.baseModelName !== ref)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*!
+ * ignore
+ */
+
+function _checkManualPopulation(arr, docs) {
+ const ref = get(arr, '_schema.caster.options.ref', null);
+ if (arr.length === 0 &&
+ docs.length > 0) {
+ if (_isAllSubdocs(docs, ref)) {
+ arr._parent.populated(arr._path, [], { model: docs[0].constructor });
+ }
+ }
+}
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = MongooseArray;
diff --git a/node_modules/mongoose/lib/types/buffer.js b/node_modules/mongoose/lib/types/buffer.js
new file mode 100644
index 0000000..98fcce0
--- /dev/null
+++ b/node_modules/mongoose/lib/types/buffer.js
@@ -0,0 +1,305 @@
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const Binary = require('../driver').get().Binary;
+const utils = require('../utils');
+const Buffer = require('safe-buffer').Buffer;
+
+// Yes this is weird. See https://github.com/feross/safe-buffer/pull/23
+const proto = Buffer.from('').constructor.prototype;
+
+/**
+ * Mongoose Buffer constructor.
+ *
+ * Values always have to be passed to the constructor to initialize.
+ *
+ * @param {Buffer} value
+ * @param {String} encode
+ * @param {Number} offset
+ * @api private
+ * @inherits Buffer
+ * @see http://bit.ly/f6CnZU
+ */
+
+function MongooseBuffer(value, encode, offset) {
+ const length = arguments.length;
+ let val;
+
+ if (length === 0 || arguments[0] === null || arguments[0] === undefined) {
+ val = 0;
+ } else {
+ val = value;
+ }
+
+ let encoding;
+ let path;
+ let doc;
+
+ if (Array.isArray(encode)) {
+ // internal casting
+ path = encode[0];
+ doc = encode[1];
+ } else {
+ encoding = encode;
+ }
+
+ let buf;
+ if (typeof val === 'number' || val instanceof Number) {
+ buf = Buffer.alloc(val);
+ } else { // string, array or object { type: 'Buffer', data: [...] }
+ buf = Buffer.from(val, encoding, offset);
+ }
+ utils.decorate(buf, MongooseBuffer.mixin);
+ buf.isMongooseBuffer = true;
+
+ // make sure these internal props don't show up in Object.keys()
+ Object.defineProperties(buf, {
+ validators: {
+ value: [],
+ enumerable: false
+ },
+ _path: {
+ value: path,
+ enumerable: false
+ },
+ _parent: {
+ value: doc,
+ enumerable: false
+ }
+ });
+
+ if (doc && typeof path === 'string') {
+ Object.defineProperty(buf, '_schema', {
+ value: doc.schema.path(path)
+ });
+ }
+
+ buf._subtype = 0;
+ return buf;
+}
+
+/*!
+ * Inherit from Buffer.
+ */
+
+// MongooseBuffer.prototype = Buffer.alloc(0);
+
+MongooseBuffer.mixin = {
+
+ /**
+ * Parent owner document
+ *
+ * @api private
+ * @property _parent
+ * @receiver MongooseBuffer
+ */
+
+ _parent: undefined,
+
+ /**
+ * Default subtype for the Binary representing this Buffer
+ *
+ * @api private
+ * @property _subtype
+ * @receiver MongooseBuffer
+ */
+
+ _subtype: undefined,
+
+ /**
+ * Marks this buffer as modified.
+ *
+ * @api private
+ * @method _markModified
+ * @receiver MongooseBuffer
+ */
+
+ _markModified: function() {
+ const parent = this._parent;
+
+ if (parent) {
+ parent.markModified(this._path);
+ }
+ return this;
+ },
+
+ /**
+ * Writes the buffer.
+ *
+ * @api public
+ * @method write
+ * @receiver MongooseBuffer
+ */
+
+ write: function() {
+ const written = proto.write.apply(this, arguments);
+
+ if (written > 0) {
+ this._markModified();
+ }
+
+ return written;
+ },
+
+ /**
+ * Copies the buffer.
+ *
+ * ####Note:
+ *
+ * `Buffer#copy` does not mark `target` as modified so you must copy from a `MongooseBuffer` for it to work as expected. This is a work around since `copy` modifies the target, not this.
+ *
+ * @return {Number} The number of bytes copied.
+ * @param {Buffer} target
+ * @method copy
+ * @receiver MongooseBuffer
+ */
+
+ copy: function(target) {
+ const ret = proto.copy.apply(this, arguments);
+
+ if (target && target.isMongooseBuffer) {
+ target._markModified();
+ }
+
+ return ret;
+ }
+};
+
+/*!
+ * Compile other Buffer methods marking this buffer as modified.
+ */
+
+(
+// node < 0.5
+ ('writeUInt8 writeUInt16 writeUInt32 writeInt8 writeInt16 writeInt32 ' +
+ 'writeFloat writeDouble fill ' +
+ 'utf8Write binaryWrite asciiWrite set ' +
+
+ // node >= 0.5
+ 'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' +
+ 'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' + 'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE')
+).split(' ').forEach(function(method) {
+ if (!proto[method]) {
+ return;
+ }
+ MongooseBuffer.mixin[method] = function() {
+ const ret = proto[method].apply(this, arguments);
+ this._markModified();
+ return ret;
+ };
+});
+
+/**
+ * Converts this buffer to its Binary type representation.
+ *
+ * ####SubTypes:
+ *
+ * var bson = require('bson')
+ * bson.BSON_BINARY_SUBTYPE_DEFAULT
+ * bson.BSON_BINARY_SUBTYPE_FUNCTION
+ * bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY
+ * bson.BSON_BINARY_SUBTYPE_UUID
+ * bson.BSON_BINARY_SUBTYPE_MD5
+ * bson.BSON_BINARY_SUBTYPE_USER_DEFINED
+ *
+ * doc.buffer.toObject(bson.BSON_BINARY_SUBTYPE_USER_DEFINED);
+ *
+ * @see http://bsonspec.org/#/specification
+ * @param {Hex} [subtype]
+ * @return {Binary}
+ * @api public
+ * @method toObject
+ * @receiver MongooseBuffer
+ */
+
+MongooseBuffer.mixin.toObject = function(options) {
+ const subtype = typeof options === 'number'
+ ? options
+ : (this._subtype || 0);
+ return new Binary(this, subtype);
+};
+
+/**
+ * Converts this buffer for storage in MongoDB, including subtype
+ *
+ * @return {Binary}
+ * @api public
+ * @method toBSON
+ * @receiver MongooseBuffer
+ */
+
+MongooseBuffer.mixin.toBSON = function() {
+ return new Binary(this, this._subtype || 0);
+};
+
+/**
+ * Determines if this buffer is equals to `other` buffer
+ *
+ * @param {Buffer} other
+ * @return {Boolean}
+ * @method equals
+ * @receiver MongooseBuffer
+ */
+
+MongooseBuffer.mixin.equals = function(other) {
+ if (!Buffer.isBuffer(other)) {
+ return false;
+ }
+
+ if (this.length !== other.length) {
+ return false;
+ }
+
+ for (let i = 0; i < this.length; ++i) {
+ if (this[i] !== other[i]) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+/**
+ * Sets the subtype option and marks the buffer modified.
+ *
+ * ####SubTypes:
+ *
+ * var bson = require('bson')
+ * bson.BSON_BINARY_SUBTYPE_DEFAULT
+ * bson.BSON_BINARY_SUBTYPE_FUNCTION
+ * bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY
+ * bson.BSON_BINARY_SUBTYPE_UUID
+ * bson.BSON_BINARY_SUBTYPE_MD5
+ * bson.BSON_BINARY_SUBTYPE_USER_DEFINED
+ *
+ * doc.buffer.subtype(bson.BSON_BINARY_SUBTYPE_UUID);
+ *
+ * @see http://bsonspec.org/#/specification
+ * @param {Hex} subtype
+ * @api public
+ * @method subtype
+ * @receiver MongooseBuffer
+ */
+
+MongooseBuffer.mixin.subtype = function(subtype) {
+ if (typeof subtype !== 'number') {
+ throw new TypeError('Invalid subtype. Expected a number');
+ }
+
+ if (this._subtype !== subtype) {
+ this._markModified();
+ }
+
+ this._subtype = subtype;
+};
+
+/*!
+ * Module exports.
+ */
+
+MongooseBuffer.Binary = Binary;
+
+module.exports = MongooseBuffer;
diff --git a/node_modules/mongoose/lib/types/decimal128.js b/node_modules/mongoose/lib/types/decimal128.js
new file mode 100644
index 0000000..f0bae2a
--- /dev/null
+++ b/node_modules/mongoose/lib/types/decimal128.js
@@ -0,0 +1,13 @@
+/**
+ * ObjectId type constructor
+ *
+ * ####Example
+ *
+ * var id = new mongoose.Types.ObjectId;
+ *
+ * @constructor ObjectId
+ */
+
+'use strict';
+
+module.exports = require('../driver').get().Decimal128;
diff --git a/node_modules/mongoose/lib/types/documentarray.js b/node_modules/mongoose/lib/types/documentarray.js
new file mode 100644
index 0000000..1899980
--- /dev/null
+++ b/node_modules/mongoose/lib/types/documentarray.js
@@ -0,0 +1,354 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const Document = require('../document');
+const MongooseArray = require('./array');
+const ObjectId = require('./objectid');
+const castObjectId = require('../cast/objectid');
+const get = require('../helpers/get');
+const getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
+const internalToObjectOptions = require('../options').internalToObjectOptions;
+const util = require('util');
+const utils = require('../utils');
+
+const documentArrayParent = require('../helpers/symbols').documentArrayParent;
+
+/*!
+ * ignore
+ */
+
+class CoreMongooseArray extends Array {
+ get isMongooseArray() {
+ return true;
+ }
+
+ remove() {}
+}
+
+/**
+ * DocumentArray constructor
+ *
+ * @param {Array} values
+ * @param {String} path the path to this array
+ * @param {Document} doc parent document
+ * @api private
+ * @return {MongooseDocumentArray}
+ * @inherits MongooseArray
+ * @see http://bit.ly/f6CnZU
+ */
+
+function MongooseDocumentArray(values, path, doc) {
+ // TODO: replace this with `new CoreMongooseArray().concat()` when we remove
+ // support for node 4.x and 5.x, see https://i.imgur.com/UAAHk4S.png
+ const arr = new CoreMongooseArray();
+ if (Array.isArray(values)) {
+ values.forEach(v => {
+ arr.push(v);
+ });
+ }
+ arr._path = path;
+
+ const props = {
+ isMongooseDocumentArray: true,
+ validators: [],
+ _atomics: {},
+ _schema: void 0,
+ _handlers: void 0
+ };
+
+ // Values always have to be passed to the constructor to initialize, since
+ // otherwise MongooseArray#push will mark the array as modified to the parent.
+ const keysMA = Object.keys(MongooseArray.mixin);
+ let numKeys = keysMA.length;
+ for (let j = 0; j < numKeys; ++j) {
+ arr[keysMA[j]] = MongooseArray.mixin[keysMA[j]];
+ }
+
+ const keysMDA = Object.keys(MongooseDocumentArray.mixin);
+ numKeys = keysMDA.length;
+ for (let i = 0; i < numKeys; ++i) {
+ arr[keysMDA[i]] = MongooseDocumentArray.mixin[keysMDA[i]];
+ }
+ if (util.inspect.custom) {
+ props[util.inspect.custom] = arr.inspect;
+ }
+
+ const keysP = Object.keys(props);
+ numKeys = keysP.length;
+ for (let k = 0; k < numKeys; ++k) {
+ arr[keysP[k]] = props[keysP[k]];
+ }
+
+ // Because doc comes from the context of another function, doc === global
+ // can happen if there was a null somewhere up the chain (see #3020 && #3034)
+ // RB Jun 17, 2015 updated to check for presence of expected paths instead
+ // to make more proof against unusual node environments
+ if (doc && doc instanceof Document) {
+ arr._parent = doc;
+ arr._schema = doc.schema.path(path);
+
+ // `schema.path()` doesn't drill into nested arrays properly yet, see
+ // gh-6398, gh-6602. This is a workaround because nested arrays are
+ // always plain non-document arrays, so once you get to a document array
+ // nesting is done. Matryoshka code.
+ while (get(arr, '_schema.$isMongooseArray') &&
+ !get(arr, '_schema.$isMongooseDocumentArray')) {
+ arr._schema = arr._schema.casterConstructor;
+ }
+
+ // Tricky but this may be a document array embedded in a normal array,
+ // in which case `path` would point to the embedded array. See #6405, #6398
+ if (arr._schema && !arr._schema.$isMongooseDocumentArray) {
+ arr._schema = arr._schema.casterConstructor;
+ }
+
+ arr._handlers = {
+ isNew: arr.notify('isNew'),
+ save: arr.notify('save')
+ };
+
+ doc.on('save', arr._handlers.save);
+ doc.on('isNew', arr._handlers.isNew);
+ }
+
+ return arr;
+}
+
+/*!
+ * Inherits from MongooseArray
+ */
+
+MongooseDocumentArray.mixin = {
+ /*!
+ * ignore
+ */
+ toBSON: function() {
+ return this.toObject(internalToObjectOptions);
+ },
+
+ /**
+ * Overrides MongooseArray#cast
+ *
+ * @method _cast
+ * @api private
+ * @receiver MongooseDocumentArray
+ */
+
+ _cast: function(value, index) {
+ let Constructor = this._schema.casterConstructor;
+ const isInstance = Constructor.$isMongooseDocumentArray ?
+ value && value.isMongooseDocumentArray :
+ value instanceof Constructor;
+ if (isInstance ||
+ // Hack re: #5001, see #5005
+ (value && value.constructor && value.constructor.baseCasterConstructor === Constructor)) {
+ if (!(value[documentArrayParent] && value.__parentArray)) {
+ // value may have been created using array.create()
+ value[documentArrayParent] = this._parent;
+ value.__parentArray = this;
+ }
+ value.$setIndex(index);
+ return value;
+ }
+
+ if (value === undefined || value === null) {
+ return null;
+ }
+
+ // handle cast('string') or cast(ObjectId) etc.
+ // only objects are permitted so we can safely assume that
+ // non-objects are to be interpreted as _id
+ if (Buffer.isBuffer(value) ||
+ value instanceof ObjectId || !utils.isObject(value)) {
+ value = {_id: value};
+ }
+
+ if (value &&
+ Constructor.discriminators &&
+ Constructor.schema &&
+ Constructor.schema.options &&
+ Constructor.schema.options.discriminatorKey) {
+ if (typeof value[Constructor.schema.options.discriminatorKey] === 'string' &&
+ Constructor.discriminators[value[Constructor.schema.options.discriminatorKey]]) {
+ Constructor = Constructor.discriminators[value[Constructor.schema.options.discriminatorKey]];
+ } else {
+ const constructorByValue = getDiscriminatorByValue(Constructor, value[Constructor.schema.options.discriminatorKey]);
+ if (constructorByValue) {
+ Constructor = constructorByValue;
+ }
+ }
+ }
+
+ if (Constructor.$isMongooseDocumentArray) {
+ return Constructor.cast(value, this, undefined, undefined, index);
+ }
+ return new Constructor(value, this, undefined, undefined, index);
+ },
+
+ /**
+ * Searches array items for the first document with a matching _id.
+ *
+ * ####Example:
+ *
+ * var embeddedDoc = m.array.id(some_id);
+ *
+ * @return {EmbeddedDocument|null} the subdocument or null if not found.
+ * @param {ObjectId|String|Number|Buffer} id
+ * @TODO cast to the _id based on schema for proper comparison
+ * @method id
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ id: function(id) {
+ let casted;
+ let sid;
+ let _id;
+
+ try {
+ casted = castObjectId(id).toString();
+ } catch (e) {
+ casted = null;
+ }
+
+ for (let i = 0, l = this.length; i < l; i++) {
+ if (!this[i]) {
+ continue;
+ }
+ _id = this[i].get('_id');
+
+ if (_id === null || typeof _id === 'undefined') {
+ continue;
+ } else if (_id instanceof Document) {
+ sid || (sid = String(id));
+ if (sid == _id._id) {
+ return this[i];
+ }
+ } else if (!(id instanceof ObjectId) && !(_id instanceof ObjectId)) {
+ if (utils.deepEqual(id, _id)) {
+ return this[i];
+ }
+ } else if (casted == _id) {
+ return this[i];
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Returns a native js Array of plain js objects
+ *
+ * ####NOTE:
+ *
+ * _Each sub-document is converted to a plain object by calling its `#toObject` method._
+ *
+ * @param {Object} [options] optional options to pass to each documents `toObject` method call during conversion
+ * @return {Array}
+ * @method toObject
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ toObject: function(options) {
+ return this.map(function(doc) {
+ try {
+ return doc.toObject(options);
+ } catch (e) {
+ return doc || null;
+ }
+ });
+ },
+
+ /**
+ * Helper for console.log
+ *
+ * @method inspect
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ inspect: function() {
+ return this.toObject();
+ },
+
+ /**
+ * Creates a subdocument casted to this schema.
+ *
+ * This is the same subdocument constructor used for casting.
+ *
+ * @param {Object} obj the value to cast to this arrays SubDocument schema
+ * @method create
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ create: function(obj) {
+ let Constructor = this._schema.casterConstructor;
+ if (obj &&
+ Constructor.discriminators &&
+ Constructor.schema &&
+ Constructor.schema.options &&
+ Constructor.schema.options.discriminatorKey) {
+ if (typeof obj[Constructor.schema.options.discriminatorKey] === 'string' &&
+ Constructor.discriminators[obj[Constructor.schema.options.discriminatorKey]]) {
+ Constructor = Constructor.discriminators[obj[Constructor.schema.options.discriminatorKey]];
+ } else {
+ const constructorByValue = getDiscriminatorByValue(Constructor, obj[Constructor.schema.options.discriminatorKey]);
+ if (constructorByValue) {
+ Constructor = constructorByValue;
+ }
+ }
+ }
+
+ return new Constructor(obj);
+ },
+
+ /**
+ * Creates a fn that notifies all child docs of `event`.
+ *
+ * @param {String} event
+ * @return {Function}
+ * @method notify
+ * @api private
+ * @receiver MongooseDocumentArray
+ */
+
+ notify: function notify(event) {
+ const _this = this;
+ return function notify(val, _arr) {
+ _arr = _arr || _this;
+ let i = _arr.length;
+ while (i--) {
+ if (_arr[i] == null) {
+ continue;
+ }
+ switch (event) {
+ // only swap for save event for now, we may change this to all event types later
+ case 'save':
+ val = _this[i];
+ break;
+ default:
+ // NO-OP
+ break;
+ }
+
+ if (_arr[i].isMongooseArray) {
+ notify(val, _arr[i]);
+ } else if (_arr[i]) {
+ _arr[i].emit(event, val);
+ }
+ }
+ };
+ }
+
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = MongooseDocumentArray;
diff --git a/node_modules/mongoose/lib/types/embedded.js b/node_modules/mongoose/lib/types/embedded.js
new file mode 100644
index 0000000..1558c81
--- /dev/null
+++ b/node_modules/mongoose/lib/types/embedded.js
@@ -0,0 +1,446 @@
+/* eslint no-func-assign: 1 */
+
+/*!
+ * Module dependencies.
+ */
+
+'use strict';
+
+const Document = require('../document_provider')();
+const EventEmitter = require('events').EventEmitter;
+const immediate = require('../helpers/immediate');
+const internalToObjectOptions = require('../options').internalToObjectOptions;
+const get = require('../helpers/get');
+const utils = require('../utils');
+const util = require('util');
+
+const documentArrayParent = require('../helpers/symbols').documentArrayParent;
+const validatorErrorSymbol = require('../helpers/symbols').validatorErrorSymbol;
+
+/**
+ * EmbeddedDocument constructor.
+ *
+ * @param {Object} obj js object returned from the db
+ * @param {MongooseDocumentArray} parentArr the parent array of this document
+ * @param {Boolean} skipId
+ * @inherits Document
+ * @api private
+ */
+
+function EmbeddedDocument(obj, parentArr, skipId, fields, index) {
+ if (parentArr) {
+ this.__parentArray = parentArr;
+ this[documentArrayParent] = parentArr._parent;
+ } else {
+ this.__parentArray = undefined;
+ this[documentArrayParent] = undefined;
+ }
+ this.$setIndex(index);
+ this.$isDocumentArrayElement = true;
+
+ Document.call(this, obj, fields, skipId);
+
+ const _this = this;
+ this.on('isNew', function(val) {
+ _this.isNew = val;
+ });
+
+ _this.on('save', function() {
+ _this.constructor.emit('save', _this);
+ });
+}
+
+/*!
+ * Inherit from Document
+ */
+EmbeddedDocument.prototype = Object.create(Document.prototype);
+EmbeddedDocument.prototype.constructor = EmbeddedDocument;
+
+for (const i in EventEmitter.prototype) {
+ EmbeddedDocument[i] = EventEmitter.prototype[i];
+}
+
+EmbeddedDocument.prototype.toBSON = function() {
+ return this.toObject(internalToObjectOptions);
+};
+
+/*!
+ * ignore
+ */
+
+EmbeddedDocument.prototype.$setIndex = function(index) {
+ this.__index = index;
+
+ if (get(this, '$__.validationError', null) != null) {
+ const keys = Object.keys(this.$__.validationError.errors);
+ for (const key of keys) {
+ this.invalidate(key, this.$__.validationError.errors[key]);
+ }
+ }
+};
+
+/**
+ * Marks the embedded doc modified.
+ *
+ * ####Example:
+ *
+ * var doc = blogpost.comments.id(hexstring);
+ * doc.mixed.type = 'changed';
+ * doc.markModified('mixed.type');
+ *
+ * @param {String} path the path which changed
+ * @api public
+ * @receiver EmbeddedDocument
+ */
+
+EmbeddedDocument.prototype.markModified = function(path) {
+ this.$__.activePaths.modify(path);
+ if (!this.__parentArray) {
+ return;
+ }
+
+ if (this.isNew) {
+ // Mark the WHOLE parent array as modified
+ // if this is a new document (i.e., we are initializing
+ // a document),
+ this.__parentArray._markModified();
+ } else {
+ this.__parentArray._markModified(this, path);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+EmbeddedDocument.prototype.populate = function() {
+ throw new Error('Mongoose does not support calling populate() on nested ' +
+ 'docs. Instead of `doc.arr[0].populate("path")`, use ' +
+ '`doc.populate("arr.0.path")`');
+};
+
+/**
+ * Used as a stub for [hooks.js](https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3)
+ *
+ * ####NOTE:
+ *
+ * _This is a no-op. Does not actually save the doc to the db._
+ *
+ * @param {Function} [fn]
+ * @return {Promise} resolved Promise
+ * @api private
+ */
+
+EmbeddedDocument.prototype.save = function(options, fn) {
+ if (typeof options === 'function') {
+ fn = options;
+ options = {};
+ }
+ options = options || {};
+
+ if (!options.suppressWarning) {
+ console.warn('mongoose: calling `save()` on a subdoc does **not** save ' +
+ 'the document to MongoDB, it only runs save middleware. ' +
+ 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' +
+ 'if you\'re sure this behavior is right for your app.');
+ }
+
+ return utils.promiseOrCallback(fn, cb => {
+ this.$__save(cb);
+ });
+};
+
+/**
+ * Used as a stub for middleware
+ *
+ * ####NOTE:
+ *
+ * _This is a no-op. Does not actually save the doc to the db._
+ *
+ * @param {Function} [fn]
+ * @method $__save
+ * @api private
+ */
+
+EmbeddedDocument.prototype.$__save = function(fn) {
+ return immediate(() => fn(null, this));
+};
+
+/*!
+ * Registers remove event listeners for triggering
+ * on subdocuments.
+ *
+ * @param {EmbeddedDocument} sub
+ * @api private
+ */
+
+function registerRemoveListener(sub) {
+ let owner = sub.ownerDocument();
+
+ function emitRemove() {
+ owner.removeListener('save', emitRemove);
+ owner.removeListener('remove', emitRemove);
+ sub.emit('remove', sub);
+ sub.constructor.emit('remove', sub);
+ owner = sub = null;
+ }
+
+ owner.on('save', emitRemove);
+ owner.on('remove', emitRemove);
+}
+
+/*!
+ * no-op for hooks
+ */
+
+EmbeddedDocument.prototype.$__remove = function(cb) {
+ return cb(null, this);
+};
+
+/**
+ * Removes the subdocument from its parent array.
+ *
+ * @param {Object} [options]
+ * @param {Function} [fn]
+ * @api public
+ */
+
+EmbeddedDocument.prototype.remove = function(options, fn) {
+ if ( typeof options === 'function' && !fn ) {
+ fn = options;
+ options = undefined;
+ }
+ if (!this.__parentArray || (options && options.noop)) {
+ fn && fn(null);
+ return this;
+ }
+
+ let _id;
+ if (!this.willRemove) {
+ _id = this._doc._id;
+ if (!_id) {
+ throw new Error('For your own good, Mongoose does not know ' +
+ 'how to remove an EmbeddedDocument that has no _id');
+ }
+ this.__parentArray.pull({_id: _id});
+ this.willRemove = true;
+ registerRemoveListener(this);
+ }
+
+ if (fn) {
+ fn(null);
+ }
+
+ return this;
+};
+
+/**
+ * Override #update method of parent documents.
+ * @api private
+ */
+
+EmbeddedDocument.prototype.update = function() {
+ throw new Error('The #update method is not available on EmbeddedDocuments');
+};
+
+/**
+ * Helper for console.log
+ *
+ * @api public
+ */
+
+EmbeddedDocument.prototype.inspect = function() {
+ return this.toObject({
+ transform: false,
+ virtuals: false,
+ flattenDecimals: false
+ });
+};
+
+if (util.inspect.custom) {
+ /*!
+ * Avoid Node deprecation warning DEP0079
+ */
+
+ EmbeddedDocument.prototype[util.inspect.custom] = EmbeddedDocument.prototype.inspect;
+}
+
+/**
+ * Marks a path as invalid, causing validation to fail.
+ *
+ * @param {String} path the field to invalidate
+ * @param {String|Error} err error which states the reason `path` was invalid
+ * @return {Boolean}
+ * @api public
+ */
+
+EmbeddedDocument.prototype.invalidate = function(path, err, val) {
+ Document.prototype.invalidate.call(this, path, err, val);
+
+ if (!this[documentArrayParent]) {
+ if (err[validatorErrorSymbol] || err.name === 'ValidationError') {
+ return true;
+ }
+ throw err;
+ }
+
+ const index = this.__index;
+ if (typeof index !== 'undefined') {
+ const parentPath = this.__parentArray._path;
+ const fullPath = [parentPath, index, path].join('.');
+ this[documentArrayParent].invalidate(fullPath, err, val);
+ }
+
+ return true;
+};
+
+/**
+ * Marks a path as valid, removing existing validation errors.
+ *
+ * @param {String} path the field to mark as valid
+ * @api private
+ * @method $markValid
+ * @receiver EmbeddedDocument
+ */
+
+EmbeddedDocument.prototype.$markValid = function(path) {
+ if (!this[documentArrayParent]) {
+ return;
+ }
+
+ const index = this.__index;
+ if (typeof index !== 'undefined') {
+ const parentPath = this.__parentArray._path;
+ const fullPath = [parentPath, index, path].join('.');
+ this[documentArrayParent].$markValid(fullPath);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+EmbeddedDocument.prototype.$ignore = function(path) {
+ Document.prototype.$ignore.call(this, path);
+
+ if (!this[documentArrayParent]) {
+ return;
+ }
+
+ const index = this.__index;
+ if (typeof index !== 'undefined') {
+ const parentPath = this.__parentArray._path;
+ const fullPath = [parentPath, index, path].join('.');
+ this[documentArrayParent].$ignore(fullPath);
+ }
+};
+
+/**
+ * Checks if a path is invalid
+ *
+ * @param {String} path the field to check
+ * @api private
+ * @method $isValid
+ * @receiver EmbeddedDocument
+ */
+
+EmbeddedDocument.prototype.$isValid = function(path) {
+ const index = this.__index;
+ if (typeof index !== 'undefined' && this[documentArrayParent]) {
+ return !this[documentArrayParent].$__.validationError ||
+ !this[documentArrayParent].$__.validationError.errors[this.$__fullPath(path)];
+ }
+
+ return true;
+};
+
+/**
+ * Returns the top level document of this sub-document.
+ *
+ * @return {Document}
+ */
+
+EmbeddedDocument.prototype.ownerDocument = function() {
+ if (this.$__.ownerDocument) {
+ return this.$__.ownerDocument;
+ }
+
+ let parent = this[documentArrayParent];
+ if (!parent) {
+ return this;
+ }
+
+ while (parent[documentArrayParent] || parent.$parent) {
+ parent = parent[documentArrayParent] || parent.$parent;
+ }
+
+ this.$__.ownerDocument = parent;
+ return this.$__.ownerDocument;
+};
+
+/**
+ * Returns the full path to this document. If optional `path` is passed, it is appended to the full path.
+ *
+ * @param {String} [path]
+ * @return {String}
+ * @api private
+ * @method $__fullPath
+ * @memberOf EmbeddedDocument
+ * @instance
+ */
+
+EmbeddedDocument.prototype.$__fullPath = function(path) {
+ if (!this.$__.fullPath) {
+ let parent = this; // eslint-disable-line consistent-this
+ if (!parent[documentArrayParent]) {
+ return path;
+ }
+
+ const paths = [];
+ while (parent[documentArrayParent] || parent.$parent) {
+ if (parent[documentArrayParent]) {
+ paths.unshift(parent.__parentArray._path);
+ } else {
+ paths.unshift(parent.$basePath);
+ }
+ parent = parent[documentArrayParent] || parent.$parent;
+ }
+
+ this.$__.fullPath = paths.join('.');
+
+ if (!this.$__.ownerDocument) {
+ // optimization
+ this.$__.ownerDocument = parent;
+ }
+ }
+
+ return path
+ ? this.$__.fullPath + '.' + path
+ : this.$__.fullPath;
+};
+
+/**
+ * Returns this sub-documents parent document.
+ *
+ * @api public
+ */
+
+EmbeddedDocument.prototype.parent = function() {
+ return this[documentArrayParent];
+};
+
+/**
+ * Returns this sub-documents parent array.
+ *
+ * @api public
+ */
+
+EmbeddedDocument.prototype.parentArray = function() {
+ return this.__parentArray;
+};
+
+/*!
+ * Module exports.
+ */
+
+module.exports = EmbeddedDocument;
diff --git a/node_modules/mongoose/lib/types/index.js b/node_modules/mongoose/lib/types/index.js
new file mode 100644
index 0000000..a1945a0
--- /dev/null
+++ b/node_modules/mongoose/lib/types/index.js
@@ -0,0 +1,20 @@
+
+/*!
+ * Module exports.
+ */
+
+'use strict';
+
+exports.Array = require('./array');
+exports.Buffer = require('./buffer');
+
+exports.Document = // @deprecate
+exports.Embedded = require('./embedded');
+
+exports.DocumentArray = require('./documentarray');
+exports.Decimal128 = require('./decimal128');
+exports.ObjectId = require('./objectid');
+
+exports.Map = require('./map');
+
+exports.Subdocument = require('./subdocument');
diff --git a/node_modules/mongoose/lib/types/map.js b/node_modules/mongoose/lib/types/map.js
new file mode 100644
index 0000000..bea39bf
--- /dev/null
+++ b/node_modules/mongoose/lib/types/map.js
@@ -0,0 +1,192 @@
+'use strict';
+
+const Mixed = require('../schema/mixed');
+const get = require('../helpers/get');
+const util = require('util');
+
+/*!
+ * ignore
+ */
+
+class MongooseMap extends Map {
+ constructor(v, path, doc, schemaType) {
+ if (v != null && v.constructor.name === 'Object') {
+ v = Object.keys(v).reduce((arr, key) => arr.concat([[key, v[key]]]), []);
+ }
+ super(v);
+
+ this.$__parent = doc != null && doc.$__ != null ? doc : null;
+ this.$__path = path;
+ this.$__schemaType = schemaType == null ? new Mixed(path) : schemaType;
+
+ this.$__runDeferred();
+ }
+
+ $init(key, value) {
+ checkValidKey(key);
+
+ super.set(key, value);
+
+ if (value != null && value.$isSingleNested) {
+ value.$basePath = this.$__path + '.' + key;
+ }
+ }
+
+ $__set(key, value) {
+ super.set(key, value);
+ }
+
+ set(key, value) {
+ checkValidKey(key);
+
+ // Weird, but because you can't assign to `this` before calling `super()`
+ // you can't get access to `$__schemaType` to cast in the initial call to
+ // `set()` from the `super()` constructor.
+
+ if (this.$__schemaType == null) {
+ this.$__deferred = this.$__deferred || [];
+ this.$__deferred.push({ key: key, value: value });
+ return;
+ }
+
+ const fullPath = this.$__path + '.' + key;
+ const populated = this.$__parent != null && this.$__parent.$__ ?
+ this.$__parent.populated(fullPath) || this.$__parent.populated(this.$__path) :
+ null;
+
+ if (populated != null) {
+ if (value.$__ == null) {
+ value = new populated.options.model(value);
+ }
+ value.$__.wasPopulated = true;
+ } else {
+ try {
+ value = this.$__schemaType.
+ applySetters(value, this.$__parent, false, this.get(key));
+ } catch (error) {
+ if (this.$__parent != null && this.$__parent.$__ != null) {
+ this.$__parent.invalidate(fullPath, error);
+ return;
+ }
+ throw error;
+ }
+ }
+
+ super.set(key, value);
+
+ if (value != null && value.$isSingleNested) {
+ value.$basePath = this.$__path + '.' + key;
+ }
+
+ if (this.$__parent != null && this.$__parent.$__) {
+ this.$__parent.markModified(this.$__path + '.' + key);
+ }
+ }
+
+ toBSON() {
+ return new Map(this);
+ }
+
+ toObject(options) {
+ if (get(options, 'flattenMaps')) {
+ const ret = {};
+ const keys = this.keys();
+ for (const key of keys) {
+ ret[key] = this.get(key);
+ }
+ return ret;
+ }
+
+ return new Map(this);
+ }
+
+ toJSON() {
+ const ret = {};
+ const keys = this.keys();
+ for (const key of keys) {
+ ret[key] = this.get(key);
+ }
+ return ret;
+ }
+
+ inspect() {
+ return new Map(this);
+ }
+
+ $__runDeferred() {
+ if (!this.$__deferred) {
+ return;
+ }
+ for (let i = 0; i < this.$__deferred.length; ++i) {
+ this.set(this.$__deferred[i].key, this.$__deferred[i].value);
+ }
+ this.$__deferred = null;
+ }
+}
+
+if (util.inspect.custom) {
+ Object.defineProperty(MongooseMap.prototype, util.inspect.custom, {
+ enumerable: false,
+ writable: false,
+ configurable: false,
+ value: MongooseMap.prototype.inspect
+ });
+}
+
+Object.defineProperty(MongooseMap.prototype, '$__set', {
+ enumerable: false,
+ writable: true,
+ configurable: false
+});
+
+Object.defineProperty(MongooseMap.prototype, '$__parent', {
+ enumerable: false,
+ writable: true,
+ configurable: false
+});
+
+Object.defineProperty(MongooseMap.prototype, '$__path', {
+ enumerable: false,
+ writable: true,
+ configurable: false
+});
+
+Object.defineProperty(MongooseMap.prototype, '$__schemaType', {
+ enumerable: false,
+ writable: true,
+ configurable: false
+});
+
+Object.defineProperty(MongooseMap.prototype, '$isMongooseMap', {
+ enumerable: false,
+ writable: false,
+ configurable: false,
+ value: true
+});
+
+Object.defineProperty(MongooseMap.prototype, '$__deferredCalls', {
+ enumerable: false,
+ writable: false,
+ configurable: false,
+ value: true
+});
+
+/*!
+ * Since maps are stored as objects under the hood, keys must be strings
+ * and can't contain any invalid characters
+ */
+
+function checkValidKey(key) {
+ const keyType = typeof key;
+ if (keyType !== 'string') {
+ throw new TypeError(`Mongoose maps only support string keys, got ${keyType}`);
+ }
+ if (key.startsWith('$')) {
+ throw new Error(`Mongoose maps do not support keys that start with "$", got "${key}"`);
+ }
+ if (key.includes('.')) {
+ throw new Error(`Mongoose maps do not support keys that contain ".", got "${key}"`);
+ }
+}
+
+module.exports = MongooseMap;
diff --git a/node_modules/mongoose/lib/types/objectid.js b/node_modules/mongoose/lib/types/objectid.js
new file mode 100644
index 0000000..4c3f8b4
--- /dev/null
+++ b/node_modules/mongoose/lib/types/objectid.js
@@ -0,0 +1,30 @@
+/**
+ * ObjectId type constructor
+ *
+ * ####Example
+ *
+ * var id = new mongoose.Types.ObjectId;
+ *
+ * @constructor ObjectId
+ */
+
+'use strict';
+
+const ObjectId = require('../driver').get().ObjectId;
+const objectIdSymbol = require('../helpers/symbols').objectIdSymbol;
+
+/*!
+ * Getter for convenience with populate, see gh-6115
+ */
+
+Object.defineProperty(ObjectId.prototype, '_id', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
+ return this;
+ }
+});
+
+ObjectId.prototype[objectIdSymbol] = true;
+
+module.exports = ObjectId;
diff --git a/node_modules/mongoose/lib/types/subdocument.js b/node_modules/mongoose/lib/types/subdocument.js
new file mode 100644
index 0000000..182d84e
--- /dev/null
+++ b/node_modules/mongoose/lib/types/subdocument.js
@@ -0,0 +1,236 @@
+'use strict';
+
+const Document = require('../document');
+const immediate = require('../helpers/immediate');
+const internalToObjectOptions = require('../options').internalToObjectOptions;
+const utils = require('../utils');
+
+const documentArrayParent = require('../helpers/symbols').documentArrayParent;
+
+module.exports = Subdocument;
+
+/**
+ * Subdocument constructor.
+ *
+ * @inherits Document
+ * @api private
+ */
+
+function Subdocument(value, fields, parent, skipId, options) {
+ this.$isSingleNested = true;
+ if (parent != null) {
+ // If setting a nested path, should copy isNew from parent re: gh-7048
+ options = Object.assign({}, options, { isNew: parent.isNew });
+ }
+ Document.call(this, value, fields, skipId, options);
+
+ delete this.$__.$options.priorDoc;
+}
+
+Subdocument.prototype = Object.create(Document.prototype);
+
+Subdocument.prototype.toBSON = function() {
+ return this.toObject(internalToObjectOptions);
+};
+
+/**
+ * Used as a stub for middleware
+ *
+ * ####NOTE:
+ *
+ * _This is a no-op. Does not actually save the doc to the db._
+ *
+ * @param {Function} [fn]
+ * @return {Promise} resolved Promise
+ * @api private
+ */
+
+Subdocument.prototype.save = function(options, fn) {
+ if (typeof options === 'function') {
+ fn = options;
+ options = {};
+ }
+ options = options || {};
+
+ if (!options.suppressWarning) {
+ console.warn('mongoose: calling `save()` on a subdoc does **not** save ' +
+ 'the document to MongoDB, it only runs save middleware. ' +
+ 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' +
+ 'if you\'re sure this behavior is right for your app.');
+ }
+
+ return utils.promiseOrCallback(fn, cb => {
+ this.$__save(cb);
+ });
+};
+
+/**
+ * Used as a stub for middleware
+ *
+ * ####NOTE:
+ *
+ * _This is a no-op. Does not actually save the doc to the db._
+ *
+ * @param {Function} [fn]
+ * @method $__save
+ * @api private
+ */
+
+Subdocument.prototype.$__save = function(fn) {
+ return immediate(() => fn(null, this));
+};
+
+Subdocument.prototype.$isValid = function(path) {
+ if (this.$parent && this.$basePath) {
+ return this.$parent.$isValid([this.$basePath, path].join('.'));
+ }
+ return Document.prototype.$isValid.call(this, path);
+};
+
+Subdocument.prototype.markModified = function(path) {
+ Document.prototype.markModified.call(this, path);
+
+ if (this.$parent && this.$basePath) {
+ if (this.$parent.isDirectModified(this.$basePath)) {
+ return;
+ }
+ this.$parent.markModified([this.$basePath, path].join('.'), this);
+ }
+};
+
+Subdocument.prototype.$markValid = function(path) {
+ Document.prototype.$markValid.call(this, path);
+ if (this.$parent && this.$basePath) {
+ this.$parent.$markValid([this.$basePath, path].join('.'));
+ }
+};
+
+/*!
+ * ignore
+ */
+
+Subdocument.prototype.invalidate = function(path, err, val) {
+ // Hack: array subdocuments' validationError is equal to the owner doc's,
+ // so validating an array subdoc gives the top-level doc back. Temporary
+ // workaround for #5208 so we don't have circular errors.
+ if (err !== this.ownerDocument().$__.validationError) {
+ Document.prototype.invalidate.call(this, path, err, val);
+ }
+
+ if (this.$parent && this.$basePath) {
+ this.$parent.invalidate([this.$basePath, path].join('.'), err, val);
+ } else if (err.kind === 'cast' || err.name === 'CastError') {
+ throw err;
+ }
+};
+
+/*!
+ * ignore
+ */
+
+Subdocument.prototype.$ignore = function(path) {
+ Document.prototype.$ignore.call(this, path);
+ if (this.$parent && this.$basePath) {
+ this.$parent.$ignore([this.$basePath, path].join('.'));
+ }
+};
+
+/**
+ * Returns the top level document of this sub-document.
+ *
+ * @return {Document}
+ */
+
+Subdocument.prototype.ownerDocument = function() {
+ if (this.$__.ownerDocument) {
+ return this.$__.ownerDocument;
+ }
+
+ let parent = this.$parent;
+ if (!parent) {
+ return this;
+ }
+
+ while (parent.$parent || parent[documentArrayParent]) {
+ parent = parent.$parent || parent[documentArrayParent];
+ }
+
+ this.$__.ownerDocument = parent;
+ return this.$__.ownerDocument;
+};
+
+/**
+ * Returns this sub-documents parent document.
+ *
+ * @api public
+ */
+
+Subdocument.prototype.parent = function() {
+ return this.$parent;
+};
+
+/*!
+ * no-op for hooks
+ */
+
+Subdocument.prototype.$__remove = function(cb) {
+ return cb(null, this);
+};
+
+/**
+ * Null-out this subdoc
+ *
+ * @param {Object} [options]
+ * @param {Function} [callback] optional callback for compatibility with Document.prototype.remove
+ */
+
+Subdocument.prototype.remove = function(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ registerRemoveListener(this);
+
+ // If removing entire doc, no need to remove subdoc
+ if (!options || !options.noop) {
+ this.$parent.set(this.$basePath, null);
+ }
+
+ if (typeof callback === 'function') {
+ callback(null);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+Subdocument.prototype.populate = function() {
+ throw new Error('Mongoose does not support calling populate() on nested ' +
+ 'docs. Instead of `doc.nested.populate("path")`, use ' +
+ '`doc.populate("nested.path")`');
+};
+
+/*!
+ * Registers remove event listeners for triggering
+ * on subdocuments.
+ *
+ * @param {EmbeddedDocument} sub
+ * @api private
+ */
+
+function registerRemoveListener(sub) {
+ let owner = sub.ownerDocument();
+
+ function emitRemove() {
+ owner.removeListener('save', emitRemove);
+ owner.removeListener('remove', emitRemove);
+ sub.emit('remove', sub);
+ sub.constructor.emit('remove', sub);
+ owner = sub = null;
+ }
+
+ owner.on('save', emitRemove);
+ owner.on('remove', emitRemove);
+}
diff --git a/node_modules/mongoose/lib/utils.js b/node_modules/mongoose/lib/utils.js
new file mode 100644
index 0000000..5c919f8
--- /dev/null
+++ b/node_modules/mongoose/lib/utils.js
@@ -0,0 +1,962 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+const Decimal = require('./types/decimal128');
+const ObjectId = require('./types/objectid');
+const PromiseProvider = require('./promise_provider');
+const cloneRegExp = require('regexp-clone');
+const get = require('./helpers/get');
+const sliced = require('sliced');
+const mpath = require('mpath');
+const ms = require('ms');
+const Buffer = require('safe-buffer').Buffer;
+
+const emittedSymbol = Symbol.for('mongoose:emitted');
+
+let MongooseBuffer;
+let MongooseArray;
+let Document;
+
+const specialProperties = new Set(['__proto__', 'constructor', 'prototype']);
+
+exports.specialProperties = specialProperties;
+
+/*!
+ * Produces a collection name from model `name`. By default, just returns
+ * the model name
+ *
+ * @param {String} name a model name
+ * @param {Function} pluralize function that pluralizes the collection name
+ * @return {String} a collection name
+ * @api private
+ */
+
+exports.toCollectionName = function(name, pluralize) {
+ if (name === 'system.profile') {
+ return name;
+ }
+ if (name === 'system.indexes') {
+ return name;
+ }
+ if (typeof pluralize === 'function') {
+ return pluralize(name);
+ }
+ return name;
+};
+
+/*!
+ * Determines if `a` and `b` are deep equal.
+ *
+ * Modified from node/lib/assert.js
+ *
+ * @param {any} a a value to compare to `b`
+ * @param {any} b a value to compare to `a`
+ * @return {Boolean}
+ * @api private
+ */
+
+exports.deepEqual = function deepEqual(a, b) {
+ if (a === b) {
+ return true;
+ }
+
+ if (a instanceof Date && b instanceof Date) {
+ return a.getTime() === b.getTime();
+ }
+
+ if ((isBsonType(a, 'ObjectID') && isBsonType(b, 'ObjectID')) ||
+ (isBsonType(a, 'Decimal128') && isBsonType(b, 'Decimal128'))) {
+ return a.toString() === b.toString();
+ }
+
+ if (a instanceof RegExp && b instanceof RegExp) {
+ return a.source === b.source &&
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline &&
+ a.global === b.global;
+ }
+
+ if (typeof a !== 'object' && typeof b !== 'object') {
+ return a == b;
+ }
+
+ if (a === null || b === null || a === undefined || b === undefined) {
+ return false;
+ }
+
+ if (a.prototype !== b.prototype) {
+ return false;
+ }
+
+ // Handle MongooseNumbers
+ if (a instanceof Number && b instanceof Number) {
+ return a.valueOf() === b.valueOf();
+ }
+
+ if (Buffer.isBuffer(a)) {
+ return exports.buffer.areEqual(a, b);
+ }
+
+ if (isMongooseObject(a)) {
+ a = a.toObject();
+ }
+ if (isMongooseObject(b)) {
+ b = b.toObject();
+ }
+
+ let ka;
+ let kb;
+ let key;
+ let i;
+ try {
+ ka = Object.keys(a);
+ kb = Object.keys(b);
+ } catch (e) {
+ // happens when one is a string literal and the other isn't
+ return false;
+ }
+
+ // having the same number of owned properties (keys incorporates
+ // hasOwnProperty)
+ if (ka.length !== kb.length) {
+ return false;
+ }
+
+ // the same set of keys (although not necessarily the same order),
+ ka.sort();
+ kb.sort();
+
+ // ~~~cheap key test
+ for (i = ka.length - 1; i >= 0; i--) {
+ if (ka[i] !== kb[i]) {
+ return false;
+ }
+ }
+
+ // equivalent values for every corresponding key, and
+ // ~~~possibly expensive deep test
+ for (i = ka.length - 1; i >= 0; i--) {
+ key = ka[i];
+ if (!deepEqual(a[key], b[key])) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+/*!
+ * Get the bson type, if it exists
+ */
+
+function isBsonType(obj, typename) {
+ return get(obj, '_bsontype', void 0) === typename;
+}
+
+/*!
+ * Get the last element of an array
+ */
+
+exports.last = function(arr) {
+ if (arr.length > 0) {
+ return arr[arr.length - 1];
+ }
+ return void 0;
+};
+
+/*!
+ * Object clone with Mongoose natives support.
+ *
+ * If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible.
+ *
+ * Functions are never cloned.
+ *
+ * @param {Object} obj the object to clone
+ * @param {Object} options
+ * @return {Object} the cloned object
+ * @api private
+ */
+
+exports.clone = function clone(obj, options) {
+ if (obj === undefined || obj === null) {
+ return obj;
+ }
+
+ if (Array.isArray(obj)) {
+ return cloneArray(obj, options);
+ }
+
+ if (isMongooseObject(obj)) {
+ if (options && options.json && typeof obj.toJSON === 'function') {
+ return obj.toJSON(options);
+ }
+ return obj.toObject(options);
+ }
+
+ if (obj.constructor) {
+ switch (exports.getFunctionName(obj.constructor)) {
+ case 'Object':
+ return cloneObject(obj, options);
+ case 'Date':
+ return new obj.constructor(+obj);
+ case 'RegExp':
+ return cloneRegExp(obj);
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ if (obj instanceof ObjectId) {
+ return new ObjectId(obj.id);
+ }
+ if (isBsonType(obj, 'Decimal128')) {
+ if (options && options.flattenDecimals) {
+ return obj.toJSON();
+ }
+ return Decimal.fromString(obj.toString());
+ }
+
+ if (!obj.constructor && exports.isObject(obj)) {
+ // object created with Object.create(null)
+ return cloneObject(obj, options);
+ }
+
+ if (obj.valueOf) {
+ return obj.valueOf();
+ }
+};
+const clone = exports.clone;
+
+/*!
+ * ignore
+ */
+
+exports.promiseOrCallback = function promiseOrCallback(callback, fn, ee) {
+ if (typeof callback === 'function') {
+ return fn(function(error) {
+ if (error != null) {
+ if (ee != null && ee.listeners('error').length > 0 && !error[emittedSymbol]) {
+ error[emittedSymbol] = true;
+ ee.emit('error', error);
+ }
+ try {
+ callback(error);
+ } catch (error) {
+ return process.nextTick(() => {
+ throw error;
+ });
+ }
+ return;
+ }
+ callback.apply(this, arguments);
+ });
+ }
+
+ const Promise = PromiseProvider.get();
+
+ return new Promise((resolve, reject) => {
+ fn(function(error, res) {
+ if (error != null) {
+ if (ee != null && ee.listeners('error').length > 0 && !error[emittedSymbol]) {
+ error[emittedSymbol] = true;
+ ee.emit('error', error);
+ }
+ return reject(error);
+ }
+ if (arguments.length > 2) {
+ return resolve(Array.prototype.slice.call(arguments, 1));
+ }
+ resolve(res);
+ });
+ });
+};
+
+/*!
+ * ignore
+ */
+
+function cloneObject(obj, options) {
+ const minimize = options && options.minimize;
+ const ret = {};
+ let hasKeys;
+ let val;
+ let k;
+
+ for (k in obj) {
+ if (specialProperties.has(k)) {
+ continue;
+ }
+
+ val = clone(obj[k], options);
+
+ if (!minimize || (typeof val !== 'undefined')) {
+ hasKeys || (hasKeys = true);
+ ret[k] = val;
+ }
+ }
+
+ return minimize ? hasKeys && ret : ret;
+}
+
+function cloneArray(arr, options) {
+ const ret = [];
+ for (let i = 0, l = arr.length; i < l; i++) {
+ ret.push(clone(arr[i], options));
+ }
+ return ret;
+}
+
+/*!
+ * Shallow copies defaults into options.
+ *
+ * @param {Object} defaults
+ * @param {Object} options
+ * @return {Object} the merged object
+ * @api private
+ */
+
+exports.options = function(defaults, options) {
+ const keys = Object.keys(defaults);
+ let i = keys.length;
+ let k;
+
+ options = options || {};
+
+ while (i--) {
+ k = keys[i];
+ if (!(k in options)) {
+ options[k] = defaults[k];
+ }
+ }
+
+ return options;
+};
+
+/*!
+ * Generates a random string
+ *
+ * @api private
+ */
+
+exports.random = function() {
+ return Math.random().toString().substr(3);
+};
+
+/*!
+ * Merges `from` into `to` without overwriting existing properties.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ * @api private
+ */
+
+exports.merge = function merge(to, from, options, path) {
+ options = options || {};
+
+ const keys = Object.keys(from);
+ let i = 0;
+ const len = keys.length;
+ let key;
+
+ path = path || '';
+ const omitNested = options.omitNested || {};
+
+ while (i < len) {
+ key = keys[i++];
+ if (options.omit && options.omit[key]) {
+ continue;
+ }
+ if (omitNested[path]) {
+ continue;
+ }
+ if (specialProperties.has(key)) {
+ continue;
+ }
+ if (to[key] == null) {
+ to[key] = from[key];
+ } else if (exports.isObject(from[key])) {
+ if (!exports.isObject(to[key])) {
+ to[key] = {};
+ }
+ if (from[key] != null) {
+ if (from[key].instanceOfSchema) {
+ to[key] = from[key].clone();
+ continue;
+ } else if (from[key] instanceof ObjectId) {
+ to[key] = new ObjectId(from[key]);
+ continue;
+ }
+ }
+ merge(to[key], from[key], options, path ? path + '.' + key : key);
+ } else if (options.overwrite) {
+ to[key] = from[key];
+ }
+ }
+};
+
+/*!
+ * Applies toObject recursively.
+ *
+ * @param {Document|Array|Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+exports.toObject = function toObject(obj) {
+ Document || (Document = require('./document'));
+ let ret;
+
+ if (obj == null) {
+ return obj;
+ }
+
+ if (obj instanceof Document) {
+ return obj.toObject();
+ }
+
+ if (Array.isArray(obj)) {
+ ret = [];
+
+ for (let i = 0, len = obj.length; i < len; ++i) {
+ ret.push(toObject(obj[i]));
+ }
+
+ return ret;
+ }
+
+ if ((obj.constructor && exports.getFunctionName(obj.constructor) === 'Object') ||
+ (!obj.constructor && exports.isObject(obj))) {
+ ret = {};
+
+ for (const k in obj) {
+ if (specialProperties.has(k)) {
+ continue;
+ }
+ ret[k] = toObject(obj[k]);
+ }
+
+ return ret;
+ }
+
+ return obj;
+};
+
+/*!
+ * Determines if `arg` is an object.
+ *
+ * @param {Object|Array|String|Function|RegExp|any} arg
+ * @api private
+ * @return {Boolean}
+ */
+
+exports.isObject = function(arg) {
+ if (Buffer.isBuffer(arg)) {
+ return true;
+ }
+ return Object.prototype.toString.call(arg) === '[object Object]';
+};
+
+/*!
+ * Determines if `arg` is a plain object.
+ *
+ * @param {Object|Array|String|Function|RegExp|any} arg
+ * @api private
+ * @return {Boolean}
+ */
+
+exports.isPOJO = function(arg) {
+ return arg instanceof Object && arg.constructor.name === 'Object';
+};
+
+/*!
+ * A faster Array.prototype.slice.call(arguments) alternative
+ * @api private
+ */
+
+exports.args = sliced;
+
+/*!
+ * process.nextTick helper.
+ *
+ * Wraps `callback` in a try/catch + nextTick.
+ *
+ * node-mongodb-native has a habit of state corruption when an error is immediately thrown from within a collection callback.
+ *
+ * @param {Function} callback
+ * @api private
+ */
+
+exports.tick = function tick(callback) {
+ if (typeof callback !== 'function') {
+ return;
+ }
+ return function() {
+ try {
+ callback.apply(this, arguments);
+ } catch (err) {
+ // only nextTick on err to get out of
+ // the event loop and avoid state corruption.
+ process.nextTick(function() {
+ throw err;
+ });
+ }
+ };
+};
+
+/*!
+ * Returns if `v` is a mongoose object that has a `toObject()` method we can use.
+ *
+ * This is for compatibility with libs like Date.js which do foolish things to Natives.
+ *
+ * @param {any} v
+ * @api private
+ */
+
+exports.isMongooseObject = function(v) {
+ Document || (Document = require('./document'));
+ MongooseArray || (MongooseArray = require('./types').Array);
+ MongooseBuffer || (MongooseBuffer = require('./types').Buffer);
+
+ if (v == null) {
+ return false;
+ }
+
+ return v.$__ != null || // Document
+ v.isMongooseArray || // Array or Document Array
+ v.isMongooseBuffer || // Buffer
+ v.$isMongooseMap; // Map
+};
+
+const isMongooseObject = exports.isMongooseObject;
+
+/*!
+ * Converts `expires` options of index objects to `expiresAfterSeconds` options for MongoDB.
+ *
+ * @param {Object} object
+ * @api private
+ */
+
+exports.expires = function expires(object) {
+ if (!(object && object.constructor.name === 'Object')) {
+ return;
+ }
+ if (!('expires' in object)) {
+ return;
+ }
+
+ let when;
+ if (typeof object.expires !== 'string') {
+ when = object.expires;
+ } else {
+ when = Math.round(ms(object.expires) / 1000);
+ }
+ object.expireAfterSeconds = when;
+ delete object.expires;
+};
+
+/*!
+ * Populate options constructor
+ */
+
+function PopulateOptions(obj) {
+ this.path = obj.path;
+ this.match = obj.match;
+ this.select = obj.select;
+ this.options = obj.options;
+ this.model = obj.model;
+ if (typeof obj.subPopulate === 'object') {
+ this.populate = obj.subPopulate;
+ }
+ if (obj.justOne != null) {
+ this.justOne = obj.justOne;
+ }
+ if (obj.count != null) {
+ this.count = obj.count;
+ }
+ this._docs = {};
+}
+
+// make it compatible with utils.clone
+PopulateOptions.prototype.constructor = Object;
+
+// expose
+exports.PopulateOptions = PopulateOptions;
+
+/*!
+ * populate helper
+ */
+
+exports.populate = function populate(path, select, model, match, options, subPopulate, justOne, count) {
+ // The order of select/conditions args is opposite Model.find but
+ // necessary to keep backward compatibility (select could be
+ // an array, string, or object literal).
+ function makeSingles(arr) {
+ const ret = [];
+ arr.forEach(function(obj) {
+ if (/[\s]/.test(obj.path)) {
+ const paths = obj.path.split(' ');
+ paths.forEach(function(p) {
+ const copy = Object.assign({}, obj);
+ copy.path = p;
+ ret.push(copy);
+ });
+ } else {
+ ret.push(obj);
+ }
+ });
+
+ return ret;
+ }
+
+ // might have passed an object specifying all arguments
+ if (arguments.length === 1) {
+ if (path instanceof PopulateOptions) {
+ return [path];
+ }
+
+ if (Array.isArray(path)) {
+ const singles = makeSingles(path);
+ return singles.map(function(o) {
+ if (o.populate && !(o.match || o.options)) {
+ return exports.populate(o)[0];
+ } else {
+ return exports.populate(o)[0];
+ }
+ });
+ }
+
+ if (exports.isObject(path)) {
+ match = path.match;
+ options = path.options;
+ select = path.select;
+ model = path.model;
+ subPopulate = path.populate;
+ justOne = path.justOne;
+ path = path.path;
+ count = path.count;
+ }
+ } else if (typeof model === 'object') {
+ options = match;
+ match = model;
+ model = undefined;
+ }
+
+ if (typeof path !== 'string') {
+ throw new TypeError('utils.populate: invalid path. Expected string. Got typeof `' + typeof path + '`');
+ }
+
+ if (Array.isArray(subPopulate)) {
+ const ret = [];
+ subPopulate.forEach(function(obj) {
+ if (/[\s]/.test(obj.path)) {
+ const copy = Object.assign({}, obj);
+ const paths = copy.path.split(' ');
+ paths.forEach(function(p) {
+ copy.path = p;
+ ret.push(exports.populate(copy)[0]);
+ });
+ } else {
+ ret.push(exports.populate(obj)[0]);
+ }
+ });
+ subPopulate = exports.populate(ret);
+ } else if (typeof subPopulate === 'object') {
+ subPopulate = exports.populate(subPopulate);
+ }
+
+ const ret = [];
+ const paths = path.split(' ');
+ options = exports.clone(options);
+ for (let i = 0; i < paths.length; ++i) {
+ ret.push(new PopulateOptions({
+ path: paths[i],
+ select: select,
+ match: match,
+ options: options,
+ model: model,
+ subPopulate: subPopulate,
+ justOne: justOne,
+ count: count
+ }));
+ }
+
+ return ret;
+};
+
+/*!
+ * Return the value of `obj` at the given `path`.
+ *
+ * @param {String} path
+ * @param {Object} obj
+ */
+
+exports.getValue = function(path, obj, map) {
+ return mpath.get(path, obj, '_doc', map);
+};
+
+/*!
+ * Sets the value of `obj` at the given `path`.
+ *
+ * @param {String} path
+ * @param {Anything} val
+ * @param {Object} obj
+ */
+
+exports.setValue = function(path, val, obj, map, _copying) {
+ mpath.set(path, val, obj, '_doc', map, _copying);
+};
+
+/*!
+ * Returns an array of values from object `o`.
+ *
+ * @param {Object} o
+ * @return {Array}
+ * @private
+ */
+
+exports.object = {};
+exports.object.vals = function vals(o) {
+ const keys = Object.keys(o);
+ let i = keys.length;
+ const ret = [];
+
+ while (i--) {
+ ret.push(o[keys[i]]);
+ }
+
+ return ret;
+};
+
+/*!
+ * @see exports.options
+ */
+
+exports.object.shallowCopy = exports.options;
+
+/*!
+ * Safer helper for hasOwnProperty checks
+ *
+ * @param {Object} obj
+ * @param {String} prop
+ */
+
+const hop = Object.prototype.hasOwnProperty;
+exports.object.hasOwnProperty = function(obj, prop) {
+ return hop.call(obj, prop);
+};
+
+/*!
+ * Determine if `val` is null or undefined
+ *
+ * @return {Boolean}
+ */
+
+exports.isNullOrUndefined = function(val) {
+ return val === null || val === undefined;
+};
+
+/*!
+ * ignore
+ */
+
+exports.array = {};
+
+/*!
+ * Flattens an array.
+ *
+ * [ 1, [ 2, 3, [4] ]] -> [1,2,3,4]
+ *
+ * @param {Array} arr
+ * @param {Function} [filter] If passed, will be invoked with each item in the array. If `filter` returns a falsey value, the item will not be included in the results.
+ * @return {Array}
+ * @private
+ */
+
+exports.array.flatten = function flatten(arr, filter, ret) {
+ ret || (ret = []);
+
+ arr.forEach(function(item) {
+ if (Array.isArray(item)) {
+ flatten(item, filter, ret);
+ } else {
+ if (!filter || filter(item)) {
+ ret.push(item);
+ }
+ }
+ });
+
+ return ret;
+};
+
+/*!
+ * Removes duplicate values from an array
+ *
+ * [1, 2, 3, 3, 5] => [1, 2, 3, 5]
+ * [ ObjectId("550988ba0c19d57f697dc45e"), ObjectId("550988ba0c19d57f697dc45e") ]
+ * => [ObjectId("550988ba0c19d57f697dc45e")]
+ *
+ * @param {Array} arr
+ * @return {Array}
+ * @private
+ */
+
+exports.array.unique = function(arr) {
+ const primitives = {};
+ const ids = {};
+ const ret = [];
+ const length = arr.length;
+ for (let i = 0; i < length; ++i) {
+ if (typeof arr[i] === 'number' || typeof arr[i] === 'string' || arr[i] == null) {
+ if (primitives[arr[i]]) {
+ continue;
+ }
+ ret.push(arr[i]);
+ primitives[arr[i]] = true;
+ } else if (arr[i] instanceof ObjectId) {
+ if (ids[arr[i].toString()]) {
+ continue;
+ }
+ ret.push(arr[i]);
+ ids[arr[i].toString()] = true;
+ } else {
+ ret.push(arr[i]);
+ }
+ }
+
+ return ret;
+};
+
+/*!
+ * Determines if two buffers are equal.
+ *
+ * @param {Buffer} a
+ * @param {Object} b
+ */
+
+exports.buffer = {};
+exports.buffer.areEqual = function(a, b) {
+ if (!Buffer.isBuffer(a)) {
+ return false;
+ }
+ if (!Buffer.isBuffer(b)) {
+ return false;
+ }
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (let i = 0, len = a.length; i < len; ++i) {
+ if (a[i] !== b[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+exports.getFunctionName = function(fn) {
+ if (fn.name) {
+ return fn.name;
+ }
+ return (fn.toString().trim().match(/^function\s*([^\s(]+)/) || [])[1];
+};
+
+/*!
+ * Decorate buffers
+ */
+
+exports.decorate = function(destination, source) {
+ for (const key in source) {
+ if (specialProperties.has(key)) {
+ continue;
+ }
+ destination[key] = source[key];
+ }
+};
+
+/**
+ * merges to with a copy of from
+ *
+ * @param {Object} to
+ * @param {Object} fromObj
+ * @api private
+ */
+
+exports.mergeClone = function(to, fromObj) {
+ if (isMongooseObject(fromObj)) {
+ fromObj = fromObj.toObject({
+ transform: false,
+ virtuals: false,
+ depopulate: true,
+ getters: false,
+ flattenDecimals: false
+ });
+ }
+ const keys = Object.keys(fromObj);
+ const len = keys.length;
+ let i = 0;
+ let key;
+
+ while (i < len) {
+ key = keys[i++];
+ if (specialProperties.has(key)) {
+ continue;
+ }
+ if (typeof to[key] === 'undefined') {
+ to[key] = exports.clone(fromObj[key], {
+ transform: false,
+ virtuals: false,
+ depopulate: true,
+ getters: false,
+ flattenDecimals: false
+ });
+ } else {
+ let val = fromObj[key];
+ if (val != null && val.valueOf && !(val instanceof Date)) {
+ val = val.valueOf();
+ }
+ if (exports.isObject(val)) {
+ let obj = val;
+ if (isMongooseObject(val) && !val.isMongooseBuffer) {
+ obj = obj.toObject({
+ transform: false,
+ virtuals: false,
+ depopulate: true,
+ getters: false,
+ flattenDecimals: false
+ });
+ }
+ if (val.isMongooseBuffer) {
+ obj = Buffer.from(obj);
+ }
+ exports.mergeClone(to[key], obj);
+ } else {
+ to[key] = exports.clone(val, {
+ flattenDecimals: false
+ });
+ }
+ }
+ }
+};
+
+/**
+ * Executes a function on each element of an array (like _.each)
+ *
+ * @param {Array} arr
+ * @param {Function} fn
+ * @api private
+ */
+
+exports.each = function(arr, fn) {
+ for (let i = 0; i < arr.length; ++i) {
+ fn(arr[i]);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+exports.noop = function() {};
diff --git a/node_modules/mongoose/lib/virtualtype.js b/node_modules/mongoose/lib/virtualtype.js
new file mode 100644
index 0000000..4612273
--- /dev/null
+++ b/node_modules/mongoose/lib/virtualtype.js
@@ -0,0 +1,143 @@
+'use strict';
+
+/**
+ * VirtualType constructor
+ *
+ * This is what mongoose uses to define virtual attributes via `Schema.prototype.virtual`.
+ *
+ * ####Example:
+ *
+ * const fullname = schema.virtual('fullname');
+ * fullname instanceof mongoose.VirtualType // true
+ *
+ * @param {Object} options
+ * @param {string|function} [options.ref] if `ref` is not nullish, this becomes a [populated virtual](/docs/populate.html#populate-virtuals)
+ * @param {string|function} [options.localField] the local field to populate on if this is a populated virtual.
+ * @param {string|function} [options.foreignField] the foreign field to populate on if this is a populated virtual.
+ * @param {boolean} [options.justOne=false] by default, a populated virtual is an array. If you set `justOne`, the populated virtual will be a single doc or `null`.
+ * @param {boolean} [options.getters=false] if you set this to `true`, Mongoose will call any custom getters you defined on this virtual
+ * @api public
+ */
+
+function VirtualType(options, name) {
+ this.path = name;
+ this.getters = [];
+ this.setters = [];
+ this.options = Object.assign({}, options);
+}
+
+/**
+ * If no getters/getters, add a default
+ *
+ * @param {Function} fn
+ * @return {VirtualType} this
+ * @api public
+ */
+
+VirtualType.prototype._applyDefaultGetters = function() {
+ if (this.getters.length > 0 || this.setters.length > 0) {
+ return;
+ }
+
+ const path = this.path;
+ const internalProperty = '$' + path;
+ this.getters.push(function() {
+ return this[internalProperty];
+ });
+ this.setters.push(function(v) {
+ this[internalProperty] = v;
+ });
+};
+
+/*!
+ * ignore
+ */
+
+VirtualType.prototype.clone = function() {
+ const clone = new VirtualType(this.name, this.options);
+ clone.getters = [].concat(this.getters);
+ clone.setters = [].concat(this.setters);
+ return clone;
+};
+
+/**
+ * Defines a getter.
+ *
+ * ####Example:
+ *
+ * var virtual = schema.virtual('fullname');
+ * virtual.get(function () {
+ * return this.name.first + ' ' + this.name.last;
+ * });
+ *
+ * @param {Function} fn
+ * @return {VirtualType} this
+ * @api public
+ */
+
+VirtualType.prototype.get = function(fn) {
+ this.getters.push(fn);
+ return this;
+};
+
+/**
+ * Defines a setter.
+ *
+ * ####Example:
+ *
+ * var virtual = schema.virtual('fullname');
+ * virtual.set(function (v) {
+ * var parts = v.split(' ');
+ * this.name.first = parts[0];
+ * this.name.last = parts[1];
+ * });
+ *
+ * @param {Function} fn
+ * @return {VirtualType} this
+ * @api public
+ */
+
+VirtualType.prototype.set = function(fn) {
+ this.setters.push(fn);
+ return this;
+};
+
+/**
+ * Applies getters to `value` using optional `scope`.
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @return {any} the value after applying all getters
+ * @api public
+ */
+
+VirtualType.prototype.applyGetters = function(value, scope) {
+ let v = value;
+ for (let l = this.getters.length - 1; l >= 0; l--) {
+ v = this.getters[l].call(scope, v, this);
+ }
+ return v;
+};
+
+/**
+ * Applies setters to `value` using optional `scope`.
+ *
+ * @param {Object} value
+ * @param {Object} scope
+ * @return {any} the value after applying all setters
+ * @api public
+ */
+
+VirtualType.prototype.applySetters = function(value, scope) {
+ let v = value;
+ for (let l = this.setters.length - 1; l >= 0; l--) {
+ v = this.setters[l].call(scope, v, this);
+ }
+ return v;
+};
+
+/*!
+ * exports
+ */
+
+module.exports = VirtualType;
diff --git a/node_modules/mongoose/migrating_to_5.md b/node_modules/mongoose/migrating_to_5.md
new file mode 100644
index 0000000..7b6377c
--- /dev/null
+++ b/node_modules/mongoose/migrating_to_5.md
@@ -0,0 +1,432 @@
+### Version Requirements
+
+Mongoose now requires node.js >= 4.0.0 and MongoDB >= 3.0.0. [MongoDB 2.6](https://www.mongodb.com/blog/post/mongodb-2-6-end-of-life) and [Node.js < 4](https://github.com/nodejs/Release) where both EOL-ed in 2016.
+
+### Query Middleware
+
+Query middleware is now compiled when you call `mongoose.model()` or `db.model()`. If you add query middleware after calling `mongoose.model()`, that middleware will **not** get called.
+
+```javascript
+const schema = new Schema({ name: String });
+const MyModel = mongoose.model('Test', schema);
+schema.pre('find', () => { console.log('find!'); });
+
+MyModel.find().exec(function() {
+ // In mongoose 4.x, the above `.find()` will print "find!"
+ // In mongoose 5.x, "find!" will **not** be printed.
+ // Call `pre('find')` **before** calling `mongoose.model()` to make the middleware apply.
+});
+```
+
+### Promises and Callbacks for `mongoose.connect()`
+
+`mongoose.connect()` and `mongoose.disconnect()` now return a promise if no callback specified, or `null` otherwise. It does **not** return the mongoose singleton.
+
+```javascript
+// Worked in mongoose 4. Does **not** work in mongoose 5, `mongoose.connect()`
+// now returns a promise consistently. This is to avoid the horrible things
+// we've done to allow mongoose to be a thenable that resolves to itself.
+mongoose.connect('mongodb://localhost:27017/test').model('Test', new Schema({}));
+
+// Do this instead
+mongoose.connect('mongodb://localhost:27017/test');
+mongoose.model('Test', new Schema({}));
+```
+
+### Connection Logic and `useMongoClient`
+
+The [`useMongoClient` option](http://mongoosejs.com/docs/4.x/docs/connections.html#use-mongo-client) was
+removed in Mongoose 5, it is now always `true`. As a consequence, Mongoose 5
+no longer supports several function signatures for `mongoose.connect()` that
+worked in Mongoose 4.x if the `useMongoClient` option was off. Below are some
+examples of `mongoose.connect()` calls that do **not** work in Mongoose 5.x.
+
+* `mongoose.connect('localhost', 27017);`
+* `mongoose.connect('localhost', 'mydb', 27017);`
+* `mongoose.connect('mongodb://host1:27017,mongodb://host2:27017');`
+
+In Mongoose 5.x, the first parameter to `mongoose.connect()` and `mongoose.createConnection()`, if specified, **must** be a [MongoDB connection string](https://docs.mongodb.com/manual/reference/connection-string/). The
+connection string and options are then passed down to [the MongoDB Node.js driver's `MongoClient.connect()` function](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#.connect). Mongoose does not modify the connection string, although `mongoose.connect()` and `mongoose.createConnection()` support a [few additional options in addition to the ones the MongoDB driver supports](http://mongoosejs.com/docs/connections.html#options).
+
+### Setter Order
+
+Setters run in reverse order in 4.x:
+
+```javascript
+const schema = new Schema({ name: String });
+schema.path('name').
+ get(() => console.log('This will print 2nd')).
+ get(() => console.log('This will print first'));
+```
+
+In 5.x, setters run in the order they're declared.
+
+```javascript
+const schema = new Schema({ name: String });
+schema.path('name').
+ get(() => console.log('This will print first')).
+ get(() => console.log('This will print 2nd'));
+```
+
+### Checking if a path is populated
+
+Mongoose 5.1.0 introduced an `_id` getter to ObjectIds that lets you get an ObjectId regardless of whether a path
+is populated.
+
+```javascript
+const blogPostSchema = new Schema({
+ title: String,
+ author: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Author'
+ }
+});
+const BlogPost = mongoose.model('BlogPost', blogPostSchema);
+
+await BlogPost.create({ title: 'test', author: author._id });
+const blogPost = await BlogPost.findOne();
+
+console.log(blogPost.author); // '5b207f84e8061d1d2711b421'
+// New in Mongoose 5.1.0: this will print '5b207f84e8061d1d2711b421' as well
+console.log(blogPost.author._id);
+
+await blogPost.populate('author');
+console.log(blogPost.author._id); '5b207f84e8061d1d2711b421'
+```
+
+As a consequence, checking whether `blogPost.author._id` is [no longer viable as a way to check whether `author` is populated](https://github.com/Automattic/mongoose/issues/6415#issuecomment-388579185). Use `blogPost.populated('author') != null` or `blogPost.author instanceof mongoose.Types.ObjectId` to check whether `author` is populated instead.
+
+Note that you can call `mongoose.set('objectIdGetter', false)` to change this behavior.
+
+### Return Values for `remove()` and `deleteX()`
+
+`deleteOne()`, `deleteMany()`, and `remove()` now resolve to the result object
+rather than the full [driver `WriteOpResult` object](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~writeOpCallback).
+
+```javascript
+// In 4.x, this is how you got the number of documents deleted
+MyModel.deleteMany().then(res => console.log(res.result.n));
+// In 5.x this is how you get the number of documents deleted
+MyModel.deleteMany().then(res => res.n);
+```
+
+### Aggregation Cursors
+
+The `useMongooseAggCursor` option from 4.x is now always on. This is the new syntax for aggregation cursors in mongoose 5:
+
+```javascript
+// When you call `.cursor()`, `.exec()` will now return a mongoose aggregation
+// cursor.
+const cursor = MyModel.aggregate([{ $match: { name: 'Val' } }]).cursor().exec();
+// No need to `await` on the cursor or wait for a promise to resolve
+cursor.eachAsync(doc => console.log(doc));
+
+// Can also pass options to `cursor()`
+const cursorWithOptions = MyModel.
+ aggregate([{ $match: { name: 'Val' } }]).
+ cursor({ batchSize: 10 }).
+ exec();
+```
+
+### geoNear
+
+`Model.geoNear()` has been removed because the [MongoDB driver no longer supports it](https://github.com/mongodb/node-mongodb-native/blob/master/CHANGES_3.0.0.md#geonear-command-helper)
+
+### Required URI encoding of connection strings
+
+Due to changes in the MongoDB driver, connection strings must be URI encoded.
+
+If they are not, connections may fail with an illegal character message.
+
+#### Passwords which contain certain characters
+
+See a [full list of affected characters](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding).
+
+If your app is used by a lot of different connection strings, it's possible
+that your test cases will pass, but production passwords will fail. Encode all your connection
+strings to be safe.
+
+If you want to continue to use unencoded connection strings, the easiest fix is to use
+the `mongodb-uri` module to parse the connection strings, and then produce the properly encoded
+versions. You can use a function like this:
+
+```javascript
+const uriFormat = require('mongodb-uri')
+function encodeMongoURI (urlString) {
+ if (urlString) {
+ let parsed = uriFormat.parse(urlString)
+ urlString = uriFormat.format(parsed);
+ }
+ return urlString;
+ }
+}
+
+// Your un-encoded string.
+const mongodbConnectString = "mongodb://...";
+mongoose.connect(encodeMongoURI(mongodbConnectString))
+```
+
+The function above is safe to use whether the existing string is already encoded or not.
+
+#### Domain sockets
+
+Domain sockets must be URI encoded. For example:
+
+```javascript
+// Works in mongoose 4. Does **not** work in mongoose 5 because of more
+// stringent URI parsing.
+const host = '/tmp/mongodb-27017.sock';
+mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`);
+
+// Do this instead
+const host = encodeURIComponent('/tmp/mongodb-27017.sock');
+mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`);
+```
+
+### `toObject()` Options
+
+The `options` parameter to `toObject()` and `toJSON()` merge defaults rather than overwriting them.
+
+```javascript
+// Note the `toObject` option below
+const schema = new Schema({ name: String }, { toObject: { virtuals: true } });
+schema.virtual('answer').get(() => 42);
+const MyModel = db.model('MyModel', schema);
+
+const doc = new MyModel({ name: 'test' });
+// In mongoose 4.x this prints "undefined", because `{ minimize: false }`
+// overwrites the entire schema-defined options object.
+// In mongoose 5.x this prints "42", because `{ minimize: false }` gets
+// merged with the schema-defined options.
+console.log(doc.toJSON({ minimize: false }).answer);
+```
+
+### Aggregate Parameters
+
+`aggregate()` no longer accepts a spread, you **must** pass your aggregation pipeline as an array. The below code worked in 4.x:
+
+```javascript
+MyModel.aggregate({ $match: { isDeleted: false } }, { $skip: 10 }).exec(cb);
+```
+
+The above code does **not** work in 5.x, you **must** wrap the `$match` and `$skip` stages in an array.
+
+```javascript
+MyModel.aggregate([{ $match: { isDeleted: false } }, { $skip: 10 }]).exec(cb);
+```
+
+### Boolean Casting
+
+By default, mongoose 4 would coerce any value to a boolean without error.
+
+```javascript
+// Fine in mongoose 4, would save a doc with `boolField = true`
+const MyModel = mongoose.model('Test', new Schema({
+ boolField: Boolean
+}));
+
+MyModel.create({ boolField: 'not a boolean' });
+```
+
+Mongoose 5 only casts the following values to `true`:
+
+* `true`
+* `'true'`
+* `1`
+* `'1'`
+* `'yes'`
+
+And the following values to `false`:
+
+* `false`
+* `'false'`
+* `0`
+* `'0'`
+* `'no'`
+
+All other values will cause a `CastError`
+### Query Casting
+
+Casting for `update()`, `updateOne()`, `updateMany()`, `replaceOne()`,
+`remove()`, `deleteOne()`, and `deleteMany()` doesn't happen until `exec()`.
+This makes it easier for hooks and custom query helpers to modify data, because
+mongoose won't restructure the data you passed in until after your hooks and
+query helpers have ran. It also makes it possible to set the `overwrite` option
+_after_ passing in an update.
+
+```javascript
+// In mongoose 4.x, this becomes `{ $set: { name: 'Baz' } }` despite the `overwrite`
+// In mongoose 5.x, this overwrite is respected and the first document with
+// `name = 'Bar'` will be replaced with `{ name: 'Baz' }`
+User.where({ name: 'Bar' }).update({ name: 'Baz' }).setOptions({ overwrite: true });
+```
+
+### Post Save Hooks Get Flow Control
+
+Post hooks now get flow control, which means async post save hooks and child document post save hooks execute **before** your `save()` callback.
+
+```javsscript
+const ChildModelSchema = new mongoose.Schema({
+ text: {
+ type: String
+ }
+});
+ChildModelSchema.post('save', function(doc) {
+ // In mongoose 5.x this will print **before** the `console.log()`
+ // in the `save()` callback. In mongoose 4.x this was reversed.
+ console.log('Child post save');
+});
+const ParentModelSchema = new mongoose.Schema({
+ children: [ChildModelSchema]
+});
+
+const Model = mongoose.model('Parent', ParentModelSchema);
+const m = new Model({ children: [{ text: 'test' }] });
+m.save(function() {
+ // In mongoose 5.xm this prints **after** the "Child post save" message.
+ console.log('Save callback');
+});
+```
+
+### The `$pushAll` Operator
+
+`$pushAll` is no longer supported and no longer used internally for `save()`, since it has been [deprecated since MongoDB 2.4](https://docs.mongodb.com/manual/reference/operator/update/pushAll/). Use `$push` with `$each` instead.
+
+### Always Use Forward Key Order
+
+The `retainKeyOrder` option was removed, mongoose will now always retain the same key position when cloning objects. If you have queries or indexes that rely on reverse key order, you will have to change them.
+
+### Run setters on queries
+
+Setters now run on queries by default, and the old `runSettersOnQuery` option
+has been removed.
+
+```javascript
+const schema = new Schema({
+ email: { type: String, lowercase: true }
+});
+const Model = mongoose.model('Test', schema);
+Model.find({ email: 'FOO@BAR.BAZ' }); // Converted to `find({ email: 'foo@bar.baz' })`
+```
+
+### Pre-compiled Browser Bundle
+
+We no longer have a pre-compiled version of mongoose for the browser. If you want to use mongoose schemas in the browser, you need to build your own bundle with browserify/webpack.
+
+### Save Errors
+
+The `saveErrorIfNotFound` option was removed, mongoose will now always error out from `save()` if the underlying document was not found
+
+### Init hook signatures
+
+`init` hooks are now fully synchronous and do not receive `next()` as a parameter.
+
+`Document.prototype.init()` no longer takes a callback as a parameter. It
+was always synchronous, just had a callback for legacy reasons.
+
+### `numAffected` and `save()`
+
+`doc.save()` no longer passes `numAffected` as a 3rd param to its callback.
+
+### `remove()` and debouncing
+
+`doc.remove()` no longer debounces
+
+### `getPromiseConstructor()`
+
+`getPromiseConstructor()` is gone, just use `mongoose.Promise`.
+
+### Passing Parameters from Pre Hooks
+
+You cannot pass parameters to the next pre middleware in the chain using `next()` in mongoose 5.x. In mongoose 4, `next('Test')` in pre middleware would call the
+next middleware with 'Test' as a parameter. Mongoose 5.x has removed support for this.
+
+### `required` validator for arrays
+
+In mongoose 5 the `required` validator only verifies if the value is an array. That is, it will **not** fail for _empty_ arrays as it would in mongoose 4.
+
+### debug output defaults to stdout instead of stderr
+
+In mongoose 5 the default debug function uses `console.info()` to display messages instead of `console.error()`.
+
+### Overwriting filter properties
+
+In Mongoose 4.x, overwriting a filter property that's a primitive with one that is an object would silently fail. For example, the below code would ignore the `where()` and be equivalent to `Sport.find({ name: 'baseball' })`
+
+```javascript
+Sport.find({ name: 'baseball' }).where({name: {$ne: 'softball'}});
+```
+
+In Mongoose 5.x, the above code will correctly overwrite `'baseball'` with `{ $ne: 'softball' }`
+
+### `bulkWrite()` results
+
+Mongoose 5.x uses version 3.x of the [MongoDB Node.js driver](http://npmjs.com/package/mongodb). MongoDB driver 3.x changed the format of
+the result of [`bulkWrite()` calls](http://localhost:8088/docs/api.html#model_Model.bulkWrite) so there is no longer a top-level `nInserted`, `nModified`, etc. property. The new result object structure is [described here](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~BulkWriteOpResult).
+
+```javascript
+const Model = mongoose.model('Test', new Schema({ name: String }));
+
+const res = await Model.bulkWrite([{ insertOne: { document: { name: 'test' } } }]);
+
+console.log(res);
+```
+
+In Mongoose 4.x, the above will print:
+
+```
+BulkWriteResult {
+ ok: [Getter],
+ nInserted: [Getter],
+ nUpserted: [Getter],
+ nMatched: [Getter],
+ nModified: [Getter],
+ nRemoved: [Getter],
+ getInsertedIds: [Function],
+ getUpsertedIds: [Function],
+ getUpsertedIdAt: [Function],
+ getRawResponse: [Function],
+ hasWriteErrors: [Function],
+ getWriteErrorCount: [Function],
+ getWriteErrorAt: [Function],
+ getWriteErrors: [Function],
+ getLastOp: [Function],
+ getWriteConcernError: [Function],
+ toJSON: [Function],
+ toString: [Function],
+ isOk: [Function],
+ insertedCount: 1,
+ matchedCount: 0,
+ modifiedCount: 0,
+ deletedCount: 0,
+ upsertedCount: 0,
+ upsertedIds: {},
+ insertedIds: { '0': 5be9a3101638a066702a0d38 },
+ n: 1 }
+```
+
+In Mongoose 5.x, the script will print:
+
+```
+BulkWriteResult {
+ result:
+ { ok: 1,
+ writeErrors: [],
+ writeConcernErrors: [],
+ insertedIds: [ [Object] ],
+ nInserted: 1,
+ nUpserted: 0,
+ nMatched: 0,
+ nModified: 0,
+ nRemoved: 0,
+ upserted: [],
+ lastOp: { ts: [Object], t: 1 } },
+ insertedCount: 1,
+ matchedCount: 0,
+ modifiedCount: 0,
+ deletedCount: 0,
+ upsertedCount: 0,
+ upsertedIds: {},
+ insertedIds: { '0': 5be9a1c87decfc6443dd9f18 },
+ n: 1 }
+```
\ No newline at end of file
diff --git a/node_modules/mongoose/package.json b/node_modules/mongoose/package.json
new file mode 100644
index 0000000..2260528
--- /dev/null
+++ b/node_modules/mongoose/package.json
@@ -0,0 +1,171 @@
+{
+ "_from": "mongoose",
+ "_id": "mongoose@5.4.3",
+ "_inBundle": false,
+ "_integrity": "sha512-zhfypQm63CPYHi43O/3lTcEPVucY3h6CqONbzreMKZIEXtwgkB9HCm4wn441Z9/CpM94G6/NA3wcaRb0N2kPYQ==",
+ "_location": "/mongoose",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "tag",
+ "registry": true,
+ "raw": "mongoose",
+ "name": "mongoose",
+ "escapedName": "mongoose",
+ "rawSpec": "",
+ "saveSpec": null,
+ "fetchSpec": "latest"
+ },
+ "_requiredBy": [
+ "#USER",
+ "/"
+ ],
+ "_resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.4.3.tgz",
+ "_shasum": "9cbc689ca0c529c2d7dd5a84dcfbc9b041084367",
+ "_spec": "mongoose",
+ "_where": "D:\\Gitterra\\om",
+ "author": {
+ "name": "Guillermo Rauch",
+ "email": "guillermo@learnboost.com"
+ },
+ "browser": "./browser.js",
+ "bugs": {
+ "url": "https://github.com/Automattic/mongoose/issues/new"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "async": "2.6.1",
+ "bson": "~1.1.0",
+ "kareem": "2.3.0",
+ "mongodb": "3.1.10",
+ "mongodb-core": "3.1.9",
+ "mongoose-legacy-pluralize": "1.0.2",
+ "mpath": "0.5.1",
+ "mquery": "3.2.0",
+ "ms": "2.0.0",
+ "regexp-clone": "0.0.1",
+ "safe-buffer": "5.1.2",
+ "sliced": "1.0.1"
+ },
+ "deprecated": false,
+ "description": "Mongoose MongoDB ODM",
+ "devDependencies": {
+ "acorn": "5.7.3",
+ "acquit": "1.0.2",
+ "acquit-ignore": "0.1.0",
+ "acquit-require": "0.1.1",
+ "babel-loader": "7.1.4",
+ "babel-preset-es2015": "6.24.1",
+ "benchmark": "2.1.2",
+ "bluebird": "3.5.0",
+ "chalk": "2.4.1",
+ "co": "4.6.0",
+ "dox": "0.3.1",
+ "eslint": "5.3.0",
+ "highlight.js": "9.1.0",
+ "jade": "1.11.0",
+ "lodash": "4.17.5",
+ "markdown": "0.5.0",
+ "marked": "0.3.9",
+ "mocha": "5.2.0",
+ "mongodb-topology-manager": "1.0.11",
+ "mongoose-long": "0.2.1",
+ "node-static": "0.7.10",
+ "nyc": "11.8.0",
+ "power-assert": "1.4.1",
+ "promise-debug": "0.1.1",
+ "q": "1.5.1",
+ "semver": "5.5.0",
+ "uuid": "2.0.3",
+ "uuid-parse": "1.0.0",
+ "validator": "10.8.0",
+ "webpack": "4.16.4"
+ },
+ "directories": {
+ "lib": "./lib/mongoose"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ },
+ "eslintConfig": {
+ "extends": [
+ "eslint:recommended"
+ ],
+ "parserOptions": {
+ "ecmaVersion": 2015
+ },
+ "env": {
+ "node": true,
+ "mocha": true,
+ "es6": true
+ },
+ "rules": {
+ "comma-style": "error",
+ "consistent-this": [
+ "error",
+ "_this"
+ ],
+ "indent": [
+ "error",
+ 2,
+ {
+ "SwitchCase": 1,
+ "VariableDeclarator": 2
+ }
+ ],
+ "keyword-spacing": "error",
+ "no-buffer-constructor": "warn",
+ "no-console": "off",
+ "no-multi-spaces": "error",
+ "func-call-spacing": "error",
+ "no-trailing-spaces": "error",
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "semi": "error",
+ "space-before-blocks": "error",
+ "space-before-function-paren": [
+ "error",
+ "never"
+ ],
+ "space-infix-ops": "error",
+ "space-unary-ops": "error",
+ "no-var": "warn",
+ "prefer-const": "warn",
+ "strict": [
+ "error",
+ "global"
+ ]
+ }
+ },
+ "homepage": "http://mongoosejs.com",
+ "keywords": [
+ "mongodb",
+ "document",
+ "model",
+ "schema",
+ "database",
+ "odm",
+ "data",
+ "datastore",
+ "query",
+ "nosql",
+ "orm",
+ "db"
+ ],
+ "license": "MIT",
+ "main": "./index.js",
+ "name": "mongoose",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/Automattic/mongoose.git"
+ },
+ "scripts": {
+ "lint": "eslint .",
+ "release": "git pull && git push origin master --tags && npm publish",
+ "release-legacy": "git pull origin 4.x && git push origin 4.x --tags && npm publish --tag legacy",
+ "test": "mocha --exit test/*.test.js test/**/*.test.js",
+ "test-cov": "nyc --reporter=html --reporter=text npm test"
+ },
+ "version": "5.4.3"
+}
diff --git a/node_modules/mongoose/tools/auth.js b/node_modules/mongoose/tools/auth.js
new file mode 100644
index 0000000..967f5e1
--- /dev/null
+++ b/node_modules/mongoose/tools/auth.js
@@ -0,0 +1,30 @@
+'use strict';
+
+const Server = require('mongodb-topology-manager').Server;
+const co = require('co');
+const mongodb = require('mongodb');
+
+co(function*() {
+ // Create new instance
+ var server = new Server('mongod', {
+ auth: null,
+ dbpath: '/data/db/27017'
+ });
+
+ // Purge the directory
+ yield server.purge();
+
+ // Start process
+ yield server.start();
+
+ const db = yield mongodb.MongoClient.connect('mongodb://localhost:27017/admin');
+
+ yield db.addUser('passwordIsTaco', 'taco', {
+ roles: ['dbOwner']
+ });
+
+ console.log('done');
+}).catch(error => {
+ console.error(error);
+ process.exit(-1);
+});
diff --git a/node_modules/mongoose/tools/repl.js b/node_modules/mongoose/tools/repl.js
new file mode 100644
index 0000000..ca3b6de
--- /dev/null
+++ b/node_modules/mongoose/tools/repl.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const co = require('co');
+
+co(function*() {
+ var ReplSet = require('mongodb-topology-manager').ReplSet;
+
+ // Create new instance
+ var topology = new ReplSet('mongod', [{
+ // mongod process options
+ options: {
+ bind_ip: 'localhost', port: 31000, dbpath: `/data/db/31000`
+ }
+ }, {
+ // mongod process options
+ options: {
+ bind_ip: 'localhost', port: 31001, dbpath: `/data/db/31001`
+ }
+ }, {
+ // Type of node
+ arbiterOnly: true,
+ // mongod process options
+ options: {
+ bind_ip: 'localhost', port: 31002, dbpath: `/data/db/31002`
+ }
+ }], {
+ replSet: 'rs'
+ });
+
+ yield topology.start();
+
+ console.log('done');
+}).catch(error => {
+ console.error(error);
+ process.exit(-1);
+});
diff --git a/node_modules/mongoose/tools/sharded.js b/node_modules/mongoose/tools/sharded.js
new file mode 100644
index 0000000..82cad14
--- /dev/null
+++ b/node_modules/mongoose/tools/sharded.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const co = require('co');
+
+co(function*() {
+ var Sharded = require('mongodb-topology-manager').Sharded;
+
+ // Create new instance
+ var topology = new Sharded({
+ mongod: 'mongod',
+ mongos: 'mongos'
+ });
+
+ yield topology.addShard([{
+ options: {
+ bind_ip: 'localhost', port: 31000, dbpath: `/data/db/31000`, shardsvr: null
+ }
+ }], { replSet: 'rs1' });
+
+ yield topology.addConfigurationServers([{
+ options: {
+ bind_ip: 'localhost', port: 35000, dbpath: `/data/db/35000`
+ }
+ }], { replSet: 'rs0' });
+
+ yield topology.addProxies([{
+ bind_ip: 'localhost', port: 51000, configdb: 'localhost:35000'
+ }], {
+ binary: 'mongos'
+ });
+
+ console.log('Start...');
+ // Start up topology
+ yield topology.start();
+
+ console.log('Started');
+
+ // Shard db
+ yield topology.enableSharding('test');
+
+ console.log('done');
+}).catch(error => {
+ console.error(error);
+ process.exit(-1);
+});
diff --git a/node_modules/mpath/.travis.yml b/node_modules/mpath/.travis.yml
new file mode 100644
index 0000000..0743746
--- /dev/null
+++ b/node_modules/mpath/.travis.yml
@@ -0,0 +1,11 @@
+language: node_js
+node_js:
+ - "0.10"
+ - "0.12"
+ - "4"
+ - "5"
+ - "6"
+ - "7"
+ - "8"
+ - "9"
+ - "10"
diff --git a/node_modules/mpath/History.md b/node_modules/mpath/History.md
new file mode 100644
index 0000000..b7ab531
--- /dev/null
+++ b/node_modules/mpath/History.md
@@ -0,0 +1,51 @@
+0.5.1 / 2018-08-30
+==================
+ * fix: prevent writing to constructor and prototype as well as __proto__
+
+0.5.0 / 2018-08-30
+==================
+ * BREAKING CHANGE: disallow setting/unsetting __proto__ properties
+ * feat: re-add support for Node < 4 for this release
+
+0.4.1 / 2018-04-08
+==================
+ * fix: allow opting out of weird `$` set behavior re: Automattic/mongoose#6273
+
+0.4.0 / 2018-03-27
+==================
+ * feat: add support for ES6 maps
+ * BREAKING CHANGE: drop support for Node < 4
+
+0.3.0 / 2017-06-05
+==================
+ * feat: add has() and unset() functions
+
+0.2.1 / 2013-03-22
+==================
+
+ * test; added for #5
+ * fix typo that breaks set #5 [Contra](https://github.com/Contra)
+
+0.2.0 / 2013-03-15
+==================
+
+ * added; adapter support for set
+ * added; adapter support for get
+ * add basic benchmarks
+ * add support for using module as a component #2 [Contra](https://github.com/Contra)
+
+0.1.1 / 2012-12-21
+==================
+
+ * added; map support
+
+0.1.0 / 2012-12-13
+==================
+
+ * added; set('array.property', val, object) support
+ * added; get('array.property', object) support
+
+0.0.1 / 2012-11-03
+==================
+
+ * initial release
diff --git a/node_modules/mpath/LICENSE b/node_modules/mpath/LICENSE
new file mode 100644
index 0000000..38c529d
--- /dev/null
+++ b/node_modules/mpath/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012 [Aaron Heckmann](aaron.heckmann+github@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/mpath/Makefile b/node_modules/mpath/Makefile
new file mode 100644
index 0000000..8d6a79c
--- /dev/null
+++ b/node_modules/mpath/Makefile
@@ -0,0 +1,4 @@
+bench:
+ node bench.js
+
+.PHONY: test
diff --git a/node_modules/mpath/README.md b/node_modules/mpath/README.md
new file mode 100644
index 0000000..9831dd0
--- /dev/null
+++ b/node_modules/mpath/README.md
@@ -0,0 +1,278 @@
+#mpath
+
+{G,S}et javascript object values using MongoDB-like path notation.
+
+###Getting
+
+```js
+var mpath = require('mpath');
+
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.get('comments.1.title', obj) // 'exciting!'
+```
+
+`mpath.get` supports array property notation as well.
+
+```js
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.get('comments.title', obj) // ['funny', 'exciting!']
+```
+
+Array property and indexing syntax, when used together, are very powerful.
+
+```js
+var obj = {
+ array: [
+ { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }}
+ , { o: { array: [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: 'Turkey Day' }] }}
+ , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }}
+ , { o: { array: [{x: null }] }}
+ , { o: { array: [{y: 3 }] }}
+ , { o: { array: [3, 0, null] }}
+ , { o: { name: 'ha' }}
+ ];
+}
+
+var found = mpath.get('array.o.array.x.b.1', obj);
+
+console.log(found); // prints..
+
+ [ [6, undefined]
+ , [2, undefined, undefined]
+ , [null, 1]
+ , [null]
+ , [undefined]
+ , [undefined, undefined, undefined]
+ , undefined
+ ]
+
+```
+
+#####Field selection rules:
+
+The following rules are iteratively applied to each `segment` in the passed `path`. For example:
+
+```js
+var path = 'one.two.14'; // path
+'one' // segment 0
+'two' // segment 1
+14 // segment 2
+```
+
+- 1) when value of the segment parent is not an array, return the value of `parent.segment`
+- 2) when value of the segment parent is an array
+ - a) if the segment is an integer, replace the parent array with the value at `parent[segment]`
+ - b) if not an integer, keep the array but replace each array `item` with the value returned from calling `get(remainingSegments, item)` or undefined if falsey.
+
+#####Maps
+
+`mpath.get` also accepts an optional `map` argument which receives each individual found value. The value returned from the `map` function will be used in the original found values place.
+
+```js
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.get('comments.title', obj, function (val) {
+ return 'funny' == val
+ ? 'amusing'
+ : val;
+});
+// ['amusing', 'exciting!']
+```
+
+###Setting
+
+```js
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.set('comments.1.title', 'hilarious', obj)
+console.log(obj.comments[1].title) // 'hilarious'
+```
+
+`mpath.set` supports the same array property notation as `mpath.get`.
+
+```js
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.set('comments.title', ['hilarious', 'fruity'], obj);
+
+console.log(obj); // prints..
+
+ { comments: [
+ { title: 'hilarious' },
+ { title: 'fruity' }
+ ]}
+```
+
+Array property and indexing syntax can be used together also when setting.
+
+```js
+var obj = {
+ array: [
+ { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }}
+ , { o: { array: [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: 'Turkey Day' }] }}
+ , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }}
+ , { o: { array: [{x: null }] }}
+ , { o: { array: [{y: 3 }] }}
+ , { o: { array: [3, 0, null] }}
+ , { o: { name: 'ha' }}
+ ]
+}
+
+mpath.set('array.1.o', 'this was changed', obj);
+
+console.log(require('util').inspect(obj, false, 1000)); // prints..
+
+{
+ array: [
+ { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }}
+ , { o: 'this was changed' }
+ , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }}
+ , { o: { array: [{x: null }] }}
+ , { o: { array: [{y: 3 }] }}
+ , { o: { array: [3, 0, null] }}
+ , { o: { name: 'ha' }}
+ ];
+}
+
+mpath.set('array.o.array.x', 'this was changed too', obj);
+
+console.log(require('util').inspect(obj, false, 1000)); // prints..
+
+{
+ array: [
+ { o: { array: [{x: 'this was changed too'}, { y: 10, x: 'this was changed too'} ] }}
+ , { o: 'this was changed' }
+ , { o: { array: [{x: 'this was changed too'}, { x: 'this was changed too'}] }}
+ , { o: { array: [{x: 'this was changed too'}] }}
+ , { o: { array: [{x: 'this was changed too', y: 3 }] }}
+ , { o: { array: [3, 0, null] }}
+ , { o: { name: 'ha' }}
+ ];
+}
+```
+
+####Setting arrays
+
+By default, setting a property within an array to another array results in each element of the new array being set to the item in the destination array at the matching index. An example is helpful.
+
+```js
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.set('comments.title', ['hilarious', 'fruity'], obj);
+
+console.log(obj); // prints..
+
+ { comments: [
+ { title: 'hilarious' },
+ { title: 'fruity' }
+ ]}
+```
+
+If we do not desire this destructuring-like assignment behavior we may instead specify the `$` operator in the path being set to force the array to be copied directly.
+
+```js
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.set('comments.$.title', ['hilarious', 'fruity'], obj);
+
+console.log(obj); // prints..
+
+ { comments: [
+ { title: ['hilarious', 'fruity'] },
+ { title: ['hilarious', 'fruity'] }
+ ]}
+```
+
+####Field assignment rules
+
+The rules utilized mirror those used on `mpath.get`, meaning we can take values returned from `mpath.get`, update them, and reassign them using `mpath.set`. Note that setting nested arrays of arrays can get unweildy quickly. Check out the [tests](https://github.com/aheckmann/mpath/blob/master/test/index.js) for more extreme examples.
+
+#####Maps
+
+`mpath.set` also accepts an optional `map` argument which receives each individual value being set. The value returned from the `map` function will be used in the original values place.
+
+```js
+var obj = {
+ comments: [
+ { title: 'funny' },
+ { title: 'exciting!' }
+ ]
+}
+
+mpath.set('comments.title', ['hilarious', 'fruity'], obj, function (val) {
+ return val.length;
+});
+
+console.log(obj); // prints..
+
+ { comments: [
+ { title: 9 },
+ { title: 6 }
+ ]}
+```
+
+### Custom object types
+
+Sometimes you may want to enact the same functionality on custom object types that store all their real data internally, say for an ODM type object. No fear, `mpath` has you covered. Simply pass the name of the property being used to store the internal data and it will be traversed instead:
+
+```js
+var mpath = require('mpath');
+
+var obj = {
+ comments: [
+ { title: 'exciting!', _doc: { title: 'great!' }}
+ ]
+}
+
+mpath.get('comments.0.title', obj, '_doc') // 'great!'
+mpath.set('comments.0.title', 'nov 3rd', obj, '_doc')
+mpath.get('comments.0.title', obj, '_doc') // 'nov 3rd'
+mpath.get('comments.0.title', obj) // 'exciting'
+```
+
+When used with a `map`, the `map` argument comes last.
+
+```js
+mpath.get(path, obj, '_doc', map);
+mpath.set(path, val, obj, '_doc', map);
+```
+
+[LICENSE](https://github.com/aheckmann/mpath/blob/master/LICENSE)
+
diff --git a/node_modules/mpath/bench.js b/node_modules/mpath/bench.js
new file mode 100644
index 0000000..7ec6a87
--- /dev/null
+++ b/node_modules/mpath/bench.js
@@ -0,0 +1,109 @@
+
+var mpath = require('./')
+var Bench = require('benchmark');
+var sha = require('child_process').exec("git log --pretty=format:'%h' -n 1", function (err, sha) {
+ if (err) throw err;
+
+ var fs = require('fs')
+ var filename = __dirname + '/bench.out';
+ var out = fs.createWriteStream(filename, { flags: 'a', encoding: 'utf8' });
+
+ /**
+ * test doc creator
+ */
+
+ function doc () {
+ var o = { first: { second: { third: [3,{ name: 'aaron' }, 9] }}};
+ o.comments = [
+ { name: 'one' }
+ , { name: 'two', _doc: { name: '2' }}
+ , { name: 'three'
+ , comments: [{},{ comments: [{val: 'twoo'}]}]
+ , _doc: { name: '3', comments: [{},{ _doc: { comments: [{ val: 2 }] }}] }}
+ ];
+ o.name = 'jiro';
+ o.array = [
+ { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }}
+ , { o: { array: [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: {b: 'hi'}}] }}
+ , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }}
+ , { o: { array: [{x: null }] }}
+ , { o: { array: [{y: 3 }] }}
+ , { o: { array: [3, 0, null] }}
+ , { o: { name: 'ha' }}
+ ];
+ o.arr = [
+ { arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true }
+ ]
+ return o;
+ }
+
+ var o = doc();
+
+ var s = new Bench.Suite;
+ s.add('mpath.get("first", obj)', function () {
+ mpath.get('first', o);
+ })
+ s.add('mpath.get("first.second", obj)', function () {
+ mpath.get('first.second', o);
+ })
+ s.add('mpath.get("first.second.third.1.name", obj)', function () {
+ mpath.get('first.second.third.1.name', o);
+ })
+ s.add('mpath.get("comments", obj)', function () {
+ mpath.get('comments', o);
+ })
+ s.add('mpath.get("comments.1", obj)', function () {
+ mpath.get('comments.1', o);
+ })
+ s.add('mpath.get("comments.2.name", obj)', function () {
+ mpath.get('comments.2.name', o);
+ })
+ s.add('mpath.get("comments.2.comments.1.comments.0.val", obj)', function () {
+ mpath.get('comments.2.comments.1.comments.0.val', o);
+ })
+ s.add('mpath.get("comments.name", obj)', function () {
+ mpath.get('comments.name', o);
+ })
+
+ s.add('mpath.set("first", obj, val)', function () {
+ mpath.set('first', o, 1);
+ })
+ s.add('mpath.set("first.second", obj, val)', function () {
+ mpath.set('first.second', o, 1);
+ })
+ s.add('mpath.set("first.second.third.1.name", obj, val)', function () {
+ mpath.set('first.second.third.1.name', o, 1);
+ })
+ s.add('mpath.set("comments", obj, val)', function () {
+ mpath.set('comments', o, 1);
+ })
+ s.add('mpath.set("comments.1", obj, val)', function () {
+ mpath.set('comments.1', o, 1);
+ })
+ s.add('mpath.set("comments.2.name", obj, val)', function () {
+ mpath.set('comments.2.name', o, 1);
+ })
+ s.add('mpath.set("comments.2.comments.1.comments.0.val", obj, val)', function () {
+ mpath.set('comments.2.comments.1.comments.0.val', o, 1);
+ })
+ s.add('mpath.set("comments.name", obj, val)', function () {
+ mpath.set('comments.name', o, 1);
+ })
+
+ s.on('start', function () {
+ console.log('starting...');
+ out.write('*' + sha + ': ' + String(new Date()) + '\n');
+ });
+ s.on('cycle', function (e) {
+ var s = String(e.target);
+ console.log(s);
+ out.write(s + '\n');
+ })
+ s.on('complete', function () {
+ console.log('done')
+ out.end('');
+ })
+ s.run()
+})
+
diff --git a/node_modules/mpath/bench.log b/node_modules/mpath/bench.log
new file mode 100644
index 0000000..e69de29
diff --git a/node_modules/mpath/bench.out b/node_modules/mpath/bench.out
new file mode 100644
index 0000000..b9b0371
--- /dev/null
+++ b/node_modules/mpath/bench.out
@@ -0,0 +1,52 @@
+*b566c26: Fri Mar 15 2013 14:14:04 GMT-0700 (PDT)
+mpath.get("first", obj) x 3,827,405 ops/sec ±0.91% (90 runs sampled)
+mpath.get("first.second", obj) x 4,930,222 ops/sec ±1.92% (91 runs sampled)
+mpath.get("first.second.third.1.name", obj) x 3,070,837 ops/sec ±1.45% (97 runs sampled)
+mpath.get("comments", obj) x 3,649,771 ops/sec ±1.71% (93 runs sampled)
+mpath.get("comments.1", obj) x 3,846,728 ops/sec ±0.86% (94 runs sampled)
+mpath.get("comments.2.name", obj) x 3,527,680 ops/sec ±0.95% (96 runs sampled)
+mpath.get("comments.2.comments.1.comments.0.val", obj) x 2,046,982 ops/sec ±0.80% (96 runs sampled)
+mpath.get("comments.name", obj) x 625,546 ops/sec ±2.02% (82 runs sampled)
+*e42bdb1: Fri Mar 15 2013 14:19:28 GMT-0700 (PDT)
+mpath.get("first", obj) x 3,700,783 ops/sec ±1.30% (95 runs sampled)
+mpath.get("first.second", obj) x 4,621,795 ops/sec ±0.86% (95 runs sampled)
+mpath.get("first.second.third.1.name", obj) x 3,012,671 ops/sec ±1.21% (100 runs sampled)
+mpath.get("comments", obj) x 3,677,694 ops/sec ±0.80% (96 runs sampled)
+mpath.get("comments.1", obj) x 3,798,862 ops/sec ±0.81% (91 runs sampled)
+mpath.get("comments.2.name", obj) x 3,489,356 ops/sec ±0.66% (98 runs sampled)
+mpath.get("comments.2.comments.1.comments.0.val", obj) x 2,004,076 ops/sec ±0.85% (99 runs sampled)
+mpath.get("comments.name", obj) x 613,270 ops/sec ±1.33% (83 runs sampled)
+*0521aac: Fri Mar 15 2013 16:37:16 GMT-0700 (PDT)
+mpath.get("first", obj) x 3,834,755 ops/sec ±0.70% (100 runs sampled)
+mpath.get("first.second", obj) x 4,999,965 ops/sec ±1.01% (98 runs sampled)
+mpath.get("first.second.third.1.name", obj) x 3,125,953 ops/sec ±0.97% (100 runs sampled)
+mpath.get("comments", obj) x 3,759,233 ops/sec ±0.81% (97 runs sampled)
+mpath.get("comments.1", obj) x 3,894,893 ops/sec ±0.76% (96 runs sampled)
+mpath.get("comments.2.name", obj) x 3,576,929 ops/sec ±0.68% (98 runs sampled)
+mpath.get("comments.2.comments.1.comments.0.val", obj) x 2,149,610 ops/sec ±0.67% (97 runs sampled)
+mpath.get("comments.name", obj) x 629,259 ops/sec ±1.30% (87 runs sampled)
+mpath.set("first", obj, val) x 2,869,477 ops/sec ±0.63% (97 runs sampled)
+mpath.set("first.second", obj, val) x 2,418,751 ops/sec ±0.62% (98 runs sampled)
+mpath.set("first.second.third.1.name", obj, val) x 2,313,099 ops/sec ±0.69% (94 runs sampled)
+mpath.set("comments", obj, val) x 2,680,882 ops/sec ±0.76% (99 runs sampled)
+mpath.set("comments.1", obj, val) x 2,401,829 ops/sec ±0.68% (98 runs sampled)
+mpath.set("comments.2.name", obj, val) x 2,335,081 ops/sec ±1.07% (96 runs sampled)
+mpath.set("comments.2.comments.1.comments.0.val", obj, val) x 2,245,436 ops/sec ±0.76% (92 runs sampled)
+mpath.set("comments.name", obj, val) x 2,356,278 ops/sec ±1.15% (100 runs sampled)
+*97e85d3: Fri Mar 15 2013 16:39:21 GMT-0700 (PDT)
+mpath.get("first", obj) x 3,837,614 ops/sec ±0.74% (99 runs sampled)
+mpath.get("first.second", obj) x 4,991,779 ops/sec ±1.01% (94 runs sampled)
+mpath.get("first.second.third.1.name", obj) x 3,078,455 ops/sec ±1.17% (96 runs sampled)
+mpath.get("comments", obj) x 3,770,961 ops/sec ±0.45% (101 runs sampled)
+mpath.get("comments.1", obj) x 3,832,814 ops/sec ±0.67% (92 runs sampled)
+mpath.get("comments.2.name", obj) x 3,536,436 ops/sec ±0.49% (100 runs sampled)
+mpath.get("comments.2.comments.1.comments.0.val", obj) x 2,141,947 ops/sec ±0.72% (98 runs sampled)
+mpath.get("comments.name", obj) x 667,898 ops/sec ±1.62% (85 runs sampled)
+mpath.set("first", obj, val) x 2,642,517 ops/sec ±0.72% (98 runs sampled)
+mpath.set("first.second", obj, val) x 2,502,124 ops/sec ±1.28% (99 runs sampled)
+mpath.set("first.second.third.1.name", obj, val) x 2,426,804 ops/sec ±0.55% (99 runs sampled)
+mpath.set("comments", obj, val) x 2,699,478 ops/sec ±0.85% (98 runs sampled)
+mpath.set("comments.1", obj, val) x 2,494,454 ops/sec ±1.05% (96 runs sampled)
+mpath.set("comments.2.name", obj, val) x 2,463,894 ops/sec ±0.86% (98 runs sampled)
+mpath.set("comments.2.comments.1.comments.0.val", obj, val) x 2,320,398 ops/sec ±0.82% (95 runs sampled)
+mpath.set("comments.name", obj, val) x 2,512,408 ops/sec ±0.77% (95 runs sampled)
diff --git a/node_modules/mpath/component.json b/node_modules/mpath/component.json
new file mode 100644
index 0000000..53c2846
--- /dev/null
+++ b/node_modules/mpath/component.json
@@ -0,0 +1,8 @@
+{
+ "name": "mpath",
+ "version": "0.2.1",
+ "main": "lib/index.js",
+ "scripts": [
+ "lib/index.js"
+ ]
+}
diff --git a/node_modules/mpath/index.js b/node_modules/mpath/index.js
new file mode 100644
index 0000000..f7b65dd
--- /dev/null
+++ b/node_modules/mpath/index.js
@@ -0,0 +1 @@
+module.exports = exports = require('./lib');
diff --git a/node_modules/mpath/lib/index.js b/node_modules/mpath/lib/index.js
new file mode 100644
index 0000000..c8ac5b6
--- /dev/null
+++ b/node_modules/mpath/lib/index.js
@@ -0,0 +1,299 @@
+// Make sure Map exists for old Node.js versions
+var Map = global.Map != null ? global.Map : function() {};
+
+// These properties are special and can open client libraries to security
+// issues
+var ignoreProperties = ['__proto__', 'constructor', 'prototype'];
+
+/**
+ * Returns the value of object `o` at the given `path`.
+ *
+ * ####Example:
+ *
+ * var obj = {
+ * comments: [
+ * { title: 'exciting!', _doc: { title: 'great!' }}
+ * , { title: 'number dos' }
+ * ]
+ * }
+ *
+ * mpath.get('comments.0.title', o) // 'exciting!'
+ * mpath.get('comments.0.title', o, '_doc') // 'great!'
+ * mpath.get('comments.title', o) // ['exciting!', 'number dos']
+ *
+ * // summary
+ * mpath.get(path, o)
+ * mpath.get(path, o, special)
+ * mpath.get(path, o, map)
+ * mpath.get(path, o, special, map)
+ *
+ * @param {String} path
+ * @param {Object} o
+ * @param {String} [special] When this property name is present on any object in the path, walking will continue on the value of this property.
+ * @param {Function} [map] Optional function which receives each individual found value. The value returned from `map` is used in the original values place.
+ */
+
+exports.get = function (path, o, special, map) {
+ var lookup;
+
+ if ('function' == typeof special) {
+ if (special.length < 2) {
+ map = special;
+ special = undefined;
+ } else {
+ lookup = special;
+ special = undefined;
+ }
+ }
+
+ map || (map = K);
+
+ var parts = 'string' == typeof path
+ ? path.split('.')
+ : path
+
+ if (!Array.isArray(parts)) {
+ throw new TypeError('Invalid `path`. Must be either string or array');
+ }
+
+ var obj = o
+ , part;
+
+ for (var i = 0; i < parts.length; ++i) {
+ part = parts[i];
+
+ if (Array.isArray(obj) && !/^\d+$/.test(part)) {
+ // reading a property from the array items
+ var paths = parts.slice(i);
+
+ return obj.map(function (item) {
+ return item
+ ? exports.get(paths, item, special || lookup, map)
+ : map(undefined);
+ });
+ }
+
+ if (lookup) {
+ obj = lookup(obj, part);
+ } else {
+ var _from = special && obj[special] ? obj[special] : obj;
+ obj = _from instanceof Map ?
+ _from.get(part) :
+ _from[part];
+ }
+
+ if (!obj) return map(obj);
+ }
+
+ return map(obj);
+};
+
+/**
+ * Returns true if `in` returns true for every piece of the path
+ *
+ * @param {String} path
+ * @param {Object} o
+ */
+
+exports.has = function (path, o) {
+ var parts = typeof path === 'string' ?
+ path.split('.') :
+ path;
+
+ if (!Array.isArray(parts)) {
+ throw new TypeError('Invalid `path`. Must be either string or array');
+ }
+
+ var len = parts.length;
+ var cur = o;
+ for (var i = 0; i < len; ++i) {
+ if (cur == null || typeof cur !== 'object' || !(parts[i] in cur)) {
+ return false;
+ }
+ cur = cur[parts[i]];
+ }
+
+ return true;
+};
+
+/**
+ * Deletes the last piece of `path`
+ *
+ * @param {String} path
+ * @param {Object} o
+ */
+
+exports.unset = function (path, o) {
+ var parts = typeof path === 'string' ?
+ path.split('.') :
+ path;
+
+ if (!Array.isArray(parts)) {
+ throw new TypeError('Invalid `path`. Must be either string or array');
+ }
+
+ var len = parts.length;
+ var cur = o;
+ for (var i = 0; i < len; ++i) {
+ if (cur == null || typeof cur !== 'object' || !(parts[i] in cur)) {
+ return false;
+ }
+ // Disallow any updates to __proto__ or special properties.
+ if (ignoreProperties.indexOf(parts[i]) !== -1) {
+ return false;
+ }
+ if (i === len - 1) {
+ delete cur[parts[i]];
+ return true;
+ }
+ cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]];
+ }
+
+ return true;
+};
+
+/**
+ * Sets the `val` at the given `path` of object `o`.
+ *
+ * @param {String} path
+ * @param {Anything} val
+ * @param {Object} o
+ * @param {String} [special] When this property name is present on any object in the path, walking will continue on the value of this property.
+ * @param {Function} [map] Optional function which is passed each individual value before setting it. The value returned from `map` is used in the original values place.
+ */
+
+exports.set = function (path, val, o, special, map, _copying) {
+ var lookup;
+
+ if ('function' == typeof special) {
+ if (special.length < 2) {
+ map = special;
+ special = undefined;
+ } else {
+ lookup = special;
+ special = undefined;
+ }
+ }
+
+ map || (map = K);
+
+ var parts = 'string' == typeof path
+ ? path.split('.')
+ : path
+
+ if (!Array.isArray(parts)) {
+ throw new TypeError('Invalid `path`. Must be either string or array');
+ }
+
+ if (null == o) return;
+
+ for (var i = 0; i < parts.length; ++i) {
+ // Silently ignore any updates to `__proto__`, these are potentially
+ // dangerous if using mpath with unsanitized data.
+ if (ignoreProperties.indexOf(parts[i]) !== -1) {
+ return;
+ }
+ }
+
+ // the existance of $ in a path tells us if the user desires
+ // the copying of an array instead of setting each value of
+ // the array to the one by one to matching positions of the
+ // current array. Unless the user explicitly opted out by passing
+ // false, see Automattic/mongoose#6273
+ var copy = _copying || (/\$/.test(path) && _copying !== false)
+ , obj = o
+ , part
+
+ for (var i = 0, len = parts.length - 1; i < len; ++i) {
+ part = parts[i];
+
+ if ('$' == part) {
+ if (i == len - 1) {
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (Array.isArray(obj) && !/^\d+$/.test(part)) {
+ var paths = parts.slice(i);
+ if (!copy && Array.isArray(val)) {
+ for (var j = 0; j < obj.length && j < val.length; ++j) {
+ // assignment of single values of array
+ exports.set(paths, val[j], obj[j], special || lookup, map, copy);
+ }
+ } else {
+ for (var j = 0; j < obj.length; ++j) {
+ // assignment of entire value
+ exports.set(paths, val, obj[j], special || lookup, map, copy);
+ }
+ }
+ return;
+ }
+
+ if (lookup) {
+ obj = lookup(obj, part);
+ } else {
+ var _to = special && obj[special] ? obj[special] : obj;
+ obj = _to instanceof Map ?
+ _to.get(part) :
+ _to[part];
+ }
+
+ if (!obj) return;
+ }
+
+ // process the last property of the path
+
+ part = parts[len];
+
+ // use the special property if exists
+ if (special && obj[special]) {
+ obj = obj[special];
+ }
+
+ // set the value on the last branch
+ if (Array.isArray(obj) && !/^\d+$/.test(part)) {
+ if (!copy && Array.isArray(val)) {
+ for (var item, j = 0; j < obj.length && j < val.length; ++j) {
+ item = obj[j];
+ if (item) {
+ if (lookup) {
+ lookup(item, part, map(val[j]));
+ } else {
+ if (item[special]) item = item[special];
+ item[part] = map(val[j]);
+ }
+ }
+ }
+ } else {
+ for (var j = 0; j < obj.length; ++j) {
+ item = obj[j];
+ if (item) {
+ if (lookup) {
+ lookup(item, part, map(val));
+ } else {
+ if (item[special]) item = item[special];
+ item[part] = map(val);
+ }
+ }
+ }
+ }
+ } else {
+ if (lookup) {
+ lookup(obj, part, map(val));
+ } else if (obj instanceof Map) {
+ obj.set(part, map(val));
+ } else {
+ obj[part] = map(val);
+ }
+ }
+}
+
+/*!
+ * Returns the value passed to it.
+ */
+
+function K (v) {
+ return v;
+}
diff --git a/node_modules/mpath/package.json b/node_modules/mpath/package.json
new file mode 100644
index 0000000..301629d
--- /dev/null
+++ b/node_modules/mpath/package.json
@@ -0,0 +1,60 @@
+{
+ "_from": "mpath@0.5.1",
+ "_id": "mpath@0.5.1",
+ "_inBundle": false,
+ "_integrity": "sha512-H8OVQ+QEz82sch4wbODFOz+3YQ61FYz/z3eJ5pIdbMEaUzDqA268Wd+Vt4Paw9TJfvDgVKaayC0gBzMIw2jhsg==",
+ "_location": "/mpath",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "mpath@0.5.1",
+ "name": "mpath",
+ "escapedName": "mpath",
+ "rawSpec": "0.5.1",
+ "saveSpec": null,
+ "fetchSpec": "0.5.1"
+ },
+ "_requiredBy": [
+ "/mongoose"
+ ],
+ "_resolved": "https://registry.npmjs.org/mpath/-/mpath-0.5.1.tgz",
+ "_shasum": "17131501f1ff9e6e4fbc8ffa875aa7065b5775ab",
+ "_spec": "mpath@0.5.1",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mongoose",
+ "author": {
+ "name": "Aaron Heckmann",
+ "email": "aaron.heckmann+github@gmail.com"
+ },
+ "bugs": {
+ "url": "https://github.com/aheckmann/mpath/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "{G,S}et object values using MongoDB-like path notation",
+ "devDependencies": {
+ "benchmark": "~1.0.0",
+ "mocha": "1.8.1"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ },
+ "homepage": "https://github.com/aheckmann/mpath#readme",
+ "keywords": [
+ "mongodb",
+ "path",
+ "get",
+ "set"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "mpath",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aheckmann/mpath.git"
+ },
+ "scripts": {
+ "test": "mocha test/*"
+ },
+ "version": "0.5.1"
+}
diff --git a/node_modules/mpath/test/index.js b/node_modules/mpath/test/index.js
new file mode 100644
index 0000000..4ad4e5d
--- /dev/null
+++ b/node_modules/mpath/test/index.js
@@ -0,0 +1,1857 @@
+
+/**
+ * Test dependencies.
+ */
+
+var mpath = require('../')
+var assert = require('assert')
+
+/**
+ * logging helper
+ */
+
+function log (o) {
+ console.log();
+ console.log(require('util').inspect(o, false, 1000));
+}
+
+/**
+ * special path for override tests
+ */
+
+var special = '_doc';
+
+/**
+ * Tests
+ */
+
+describe('mpath', function(){
+
+ /**
+ * test doc creator
+ */
+
+ function doc () {
+ var o = { first: { second: { third: [3,{ name: 'aaron' }, 9] }}};
+ o.comments = [
+ { name: 'one' }
+ , { name: 'two', _doc: { name: '2' }}
+ , { name: 'three'
+ , comments: [{},{ comments: [{val: 'twoo'}]}]
+ , _doc: { name: '3', comments: [{},{ _doc: { comments: [{ val: 2 }] }}] }}
+ ];
+ o.name = 'jiro';
+ o.array = [
+ { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }}
+ , { o: { array: [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: {b: 'hi'}}] }}
+ , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }}
+ , { o: { array: [{x: null }] }}
+ , { o: { array: [{y: 3 }] }}
+ , { o: { array: [3, 0, null] }}
+ , { o: { name: 'ha' }}
+ ];
+ o.arr = [
+ { arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true }
+ ]
+ return o;
+ }
+
+ describe('get', function(){
+ var o = doc();
+
+ it('`path` must be a string or array', function(done){
+ assert.throws(function () {
+ mpath.get({}, o);
+ }, /Must be either string or array/);
+ assert.throws(function () {
+ mpath.get(4, o);
+ }, /Must be either string or array/);
+ assert.throws(function () {
+ mpath.get(function(){}, o);
+ }, /Must be either string or array/);
+ assert.throws(function () {
+ mpath.get(/asdf/, o);
+ }, /Must be either string or array/);
+ assert.throws(function () {
+ mpath.get(Math, o);
+ }, /Must be either string or array/);
+ assert.throws(function () {
+ mpath.get(Buffer, o);
+ }, /Must be either string or array/);
+ assert.doesNotThrow(function () {
+ mpath.get('string', o);
+ });
+ assert.doesNotThrow(function () {
+ mpath.get([], o);
+ });
+ done();
+ })
+
+ describe('without `special`', function(){
+ it('works', function(done){
+ assert.equal('jiro', mpath.get('name', o));
+
+ assert.deepEqual(
+ { second: { third: [3,{ name: 'aaron' }, 9] }}
+ , mpath.get('first', o)
+ );
+
+ assert.deepEqual(
+ { third: [3,{ name: 'aaron' }, 9] }
+ , mpath.get('first.second', o)
+ );
+
+ assert.deepEqual(
+ [3,{ name: 'aaron' }, 9]
+ , mpath.get('first.second.third', o)
+ );
+
+ assert.deepEqual(
+ 3
+ , mpath.get('first.second.third.0', o)
+ );
+
+ assert.deepEqual(
+ 9
+ , mpath.get('first.second.third.2', o)
+ );
+
+ assert.deepEqual(
+ { name: 'aaron' }
+ , mpath.get('first.second.third.1', o)
+ );
+
+ assert.deepEqual(
+ 'aaron'
+ , mpath.get('first.second.third.1.name', o)
+ );
+
+ assert.deepEqual([
+ { name: 'one' }
+ , { name: 'two', _doc: { name: '2' }}
+ , { name: 'three'
+ , comments: [{},{ comments: [{val: 'twoo'}]}]
+ , _doc: { name: '3', comments: [{},{ _doc: { comments: [{ val: 2 }] }}]}}],
+ mpath.get('comments', o));
+
+ assert.deepEqual({ name: 'one' }, mpath.get('comments.0', o));
+ assert.deepEqual('one', mpath.get('comments.0.name', o));
+ assert.deepEqual('two', mpath.get('comments.1.name', o));
+ assert.deepEqual('three', mpath.get('comments.2.name', o));
+
+ assert.deepEqual([{},{ comments: [{val: 'twoo'}]}]
+ , mpath.get('comments.2.comments', o));
+
+ assert.deepEqual({ comments: [{val: 'twoo'}]}
+ , mpath.get('comments.2.comments.1', o));
+
+ assert.deepEqual('twoo', mpath.get('comments.2.comments.1.comments.0.val', o));
+
+ done();
+ })
+
+ it('handles array.property dot-notation', function(done){
+ assert.deepEqual(
+ ['one', 'two', 'three']
+ , mpath.get('comments.name', o)
+ );
+ done();
+ })
+
+ it('handles array.array notation', function(done){
+ assert.deepEqual(
+ [undefined, undefined, [{}, {comments:[{val:'twoo'}]}]]
+ , mpath.get('comments.comments', o)
+ );
+ done();
+ })
+
+ it('handles prop.prop.prop.arrayProperty notation', function(done){
+ assert.deepEqual(
+ [undefined, 'aaron', undefined]
+ , mpath.get('first.second.third.name', o)
+ );
+ assert.deepEqual(
+ [1, 'aaron', 1]
+ , mpath.get('first.second.third.name', o, function (v) {
+ return undefined === v ? 1 : v;
+ })
+ );
+ done();
+ })
+
+ it('handles array.prop.array', function(done){
+ assert.deepEqual(
+ [ [{x: {b: [4,6,8]}}, { y: 10} ]
+ , [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: {b: 'hi'}}]
+ , [{x: {b: null }}, { x: { b: [null, 1]}}]
+ , [{x: null }]
+ , [{y: 3 }]
+ , [3, 0, null]
+ , undefined
+ ]
+ , mpath.get('array.o.array', o)
+ );
+ done();
+ })
+
+ it('handles array.prop.array.index', function(done){
+ assert.deepEqual(
+ [ {x: {b: [4,6,8]}}
+ , {x: {b: [1,2,3]}}
+ , {x: {b: null }}
+ , {x: null }
+ , {y: 3 }
+ , 3
+ , undefined
+ ]
+ , mpath.get('array.o.array.0', o)
+ );
+ done();
+ })
+
+ it('handles array.prop.array.index.prop', function(done){
+ assert.deepEqual(
+ [ {b: [4,6,8]}
+ , {b: [1,2,3]}
+ , {b: null }
+ , null
+ , undefined
+ , undefined
+ , undefined
+ ]
+ , mpath.get('array.o.array.0.x', o)
+ );
+ done();
+ })
+
+ it('handles array.prop.array.prop', function(done){
+ assert.deepEqual(
+ [ [undefined, 10 ]
+ , [undefined, undefined, undefined]
+ , [undefined, undefined]
+ , [undefined]
+ , [3]
+ , [undefined, undefined, undefined]
+ , undefined
+ ]
+ , mpath.get('array.o.array.y', o)
+ );
+ assert.deepEqual(
+ [ [{b: [4,6,8]}, undefined]
+ , [{b: [1,2,3]}, {z: 10 }, {b: 'hi'}]
+ , [{b: null }, { b: [null, 1]}]
+ , [null]
+ , [undefined]
+ , [undefined, undefined, undefined]
+ , undefined
+ ]
+ , mpath.get('array.o.array.x', o)
+ );
+ done();
+ })
+
+ it('handles array.prop.array.prop.prop', function(done){
+ assert.deepEqual(
+ [ [[4,6,8], undefined]
+ , [[1,2,3], undefined, 'hi']
+ , [null, [null, 1]]
+ , [null]
+ , [undefined]
+ , [undefined, undefined, undefined]
+ , undefined
+ ]
+ , mpath.get('array.o.array.x.b', o)
+ );
+ done();
+ })
+
+ it('handles array.prop.array.prop.prop.index', function(done){
+ assert.deepEqual(
+ [ [6, undefined]
+ , [2, undefined, 'i'] // undocumented feature (string indexing)
+ , [null, 1]
+ , [null]
+ , [undefined]
+ , [undefined, undefined, undefined]
+ , undefined
+ ]
+ , mpath.get('array.o.array.x.b.1', o)
+ );
+ assert.deepEqual(
+ [ [6, 0]
+ , [2, 0, 'i'] // undocumented feature (string indexing)
+ , [null, 1]
+ , [null]
+ , [0]
+ , [0, 0, 0]
+ , 0
+ ]
+ , mpath.get('array.o.array.x.b.1', o, function (v) {
+ return undefined === v ? 0 : v;
+ })
+ );
+ done();
+ })
+
+ it('handles array.index.prop.prop', function(done){
+ assert.deepEqual(
+ [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: {b: 'hi'}}]
+ , mpath.get('array.1.o.array', o)
+ );
+ assert.deepEqual(
+ ['hi','hi','hi']
+ , mpath.get('array.1.o.array', o, function (v) {
+ if (Array.isArray(v)) {
+ return v.map(function (val) {
+ return 'hi';
+ })
+ }
+ return v;
+ })
+ );
+ done();
+ })
+
+ it('handles array.array.index', function(done){
+ assert.deepEqual(
+ [{ a: { c: 48 }}, undefined]
+ , mpath.get('arr.arr.1', o)
+ );
+ assert.deepEqual(
+ ['woot', undefined]
+ , mpath.get('arr.arr.1', o, function (v) {
+ if (v && v.a && v.a.c) return 'woot';
+ return v;
+ })
+ );
+ done();
+ })
+
+ it('handles array.array.index.prop', function(done){
+ assert.deepEqual(
+ [{ c: 48 }, 'woot']
+ , mpath.get('arr.arr.1.a', o, function (v) {
+ if (undefined === v) return 'woot';
+ return v;
+ })
+ );
+ assert.deepEqual(
+ [{ c: 48 }, undefined]
+ , mpath.get('arr.arr.1.a', o)
+ );
+ mpath.set('arr.arr.1.a', [{c:49},undefined], o)
+ assert.deepEqual(
+ [{ c: 49 }, undefined]
+ , mpath.get('arr.arr.1.a', o)
+ );
+ mpath.set('arr.arr.1.a', [{c:48},undefined], o)
+ done();
+ })
+
+ it('handles array.array.index.prop.prop', function(done){
+ assert.deepEqual(
+ [48, undefined]
+ , mpath.get('arr.arr.1.a.c', o)
+ );
+ assert.deepEqual(
+ [48, 'woot']
+ , mpath.get('arr.arr.1.a.c', o, function (v) {
+ if (undefined === v) return 'woot';
+ return v;
+ })
+ );
+ done();
+ })
+
+ })
+
+ describe('with `special`', function(){
+ describe('that is a string', function(){
+ it('works', function(done){
+ assert.equal('jiro', mpath.get('name', o, special));
+
+ assert.deepEqual(
+ { second: { third: [3,{ name: 'aaron' }, 9] }}
+ , mpath.get('first', o, special)
+ );
+
+ assert.deepEqual(
+ { third: [3,{ name: 'aaron' }, 9] }
+ , mpath.get('first.second', o, special)
+ );
+
+ assert.deepEqual(
+ [3,{ name: 'aaron' }, 9]
+ , mpath.get('first.second.third', o, special)
+ );
+
+ assert.deepEqual(
+ 3
+ , mpath.get('first.second.third.0', o, special)
+ );
+
+ assert.deepEqual(
+ 4
+ , mpath.get('first.second.third.0', o, special, function (v) {
+ return 3 === v ? 4 : v;
+ })
+ );
+
+ assert.deepEqual(
+ 9
+ , mpath.get('first.second.third.2', o, special)
+ );
+
+ assert.deepEqual(
+ { name: 'aaron' }
+ , mpath.get('first.second.third.1', o, special)
+ );
+
+ assert.deepEqual(
+ 'aaron'
+ , mpath.get('first.second.third.1.name', o, special)
+ );
+
+ assert.deepEqual([
+ { name: 'one' }
+ , { name: 'two', _doc: { name: '2' }}
+ , { name: 'three'
+ , comments: [{},{ comments: [{val: 'twoo'}]}]
+ , _doc: { name: '3', comments: [{},{ _doc: { comments: [{ val: 2 }] }}]}}],
+ mpath.get('comments', o, special));
+
+ assert.deepEqual({ name: 'one' }, mpath.get('comments.0', o, special));
+ assert.deepEqual('one', mpath.get('comments.0.name', o, special));
+ assert.deepEqual('2', mpath.get('comments.1.name', o, special));
+ assert.deepEqual('3', mpath.get('comments.2.name', o, special));
+ assert.deepEqual('nice', mpath.get('comments.2.name', o, special, function (v) {
+ return '3' === v ? 'nice' : v;
+ }));
+
+ assert.deepEqual([{},{ _doc: { comments: [{ val: 2 }] }}]
+ , mpath.get('comments.2.comments', o, special));
+
+ assert.deepEqual({ _doc: { comments: [{val: 2}]}}
+ , mpath.get('comments.2.comments.1', o, special));
+
+ assert.deepEqual(2, mpath.get('comments.2.comments.1.comments.0.val', o, special));
+ done();
+ })
+
+ it('handles array.property dot-notation', function(done){
+ assert.deepEqual(
+ ['one', '2', '3']
+ , mpath.get('comments.name', o, special)
+ );
+ assert.deepEqual(
+ ['one', 2, '3']
+ , mpath.get('comments.name', o, special, function (v) {
+ return '2' === v ? 2 : v
+ })
+ );
+ done();
+ })
+
+ it('handles array.array notation', function(done){
+ assert.deepEqual(
+ [undefined, undefined, [{}, {_doc: { comments:[{val:2}]}}]]
+ , mpath.get('comments.comments', o, special)
+ );
+ done();
+ })
+
+ it('handles array.array.index.array', function(done){
+ assert.deepEqual(
+ [undefined, undefined, [{val:2}]]
+ , mpath.get('comments.comments.1.comments', o, special)
+ );
+ done();
+ })
+
+ it('handles array.array.index.array.prop', function(done){
+ assert.deepEqual(
+ [undefined, undefined, [2]]
+ , mpath.get('comments.comments.1.comments.val', o, special)
+ );
+ assert.deepEqual(
+ ['nil', 'nil', [2]]
+ , mpath.get('comments.comments.1.comments.val', o, special, function (v) {
+ return undefined === v ? 'nil' : v;
+ })
+ );
+ done();
+ })
+ })
+
+ describe('that is a function', function(){
+ var special = function (obj, key) {
+ return obj[key]
+ }
+
+ it('works', function(done){
+ assert.equal('jiro', mpath.get('name', o, special));
+
+ assert.deepEqual(
+ { second: { third: [3,{ name: 'aaron' }, 9] }}
+ , mpath.get('first', o, special)
+ );
+
+ assert.deepEqual(
+ { third: [3,{ name: 'aaron' }, 9] }
+ , mpath.get('first.second', o, special)
+ );
+
+ assert.deepEqual(
+ [3,{ name: 'aaron' }, 9]
+ , mpath.get('first.second.third', o, special)
+ );
+
+ assert.deepEqual(
+ 3
+ , mpath.get('first.second.third.0', o, special)
+ );
+
+ assert.deepEqual(
+ 4
+ , mpath.get('first.second.third.0', o, special, function (v) {
+ return 3 === v ? 4 : v;
+ })
+ );
+
+ assert.deepEqual(
+ 9
+ , mpath.get('first.second.third.2', o, special)
+ );
+
+ assert.deepEqual(
+ { name: 'aaron' }
+ , mpath.get('first.second.third.1', o, special)
+ );
+
+ assert.deepEqual(
+ 'aaron'
+ , mpath.get('first.second.third.1.name', o, special)
+ );
+
+ assert.deepEqual([
+ { name: 'one' }
+ , { name: 'two', _doc: { name: '2' }}
+ , { name: 'three'
+ , comments: [{},{ comments: [{val: 'twoo'}]}]
+ , _doc: { name: '3', comments: [{},{ _doc: { comments: [{ val: 2 }] }}]}}],
+ mpath.get('comments', o, special));
+
+ assert.deepEqual({ name: 'one' }, mpath.get('comments.0', o, special));
+ assert.deepEqual('one', mpath.get('comments.0.name', o, special));
+ assert.deepEqual('two', mpath.get('comments.1.name', o, special));
+ assert.deepEqual('three', mpath.get('comments.2.name', o, special));
+ assert.deepEqual('nice', mpath.get('comments.2.name', o, special, function (v) {
+ return 'three' === v ? 'nice' : v;
+ }));
+
+ assert.deepEqual([{},{ comments: [{ val: 'twoo' }] }]
+ , mpath.get('comments.2.comments', o, special));
+
+ assert.deepEqual({ comments: [{val: 'twoo'}]}
+ , mpath.get('comments.2.comments.1', o, special));
+
+ assert.deepEqual('twoo', mpath.get('comments.2.comments.1.comments.0.val', o, special));
+
+ var overide = false;
+ assert.deepEqual('twoo', mpath.get('comments.8.comments.1.comments.0.val', o, function (obj, path) {
+ if (Array.isArray(obj) && 8 == path) {
+ overide = true;
+ return obj[2];
+ }
+ return obj[path];
+ }));
+ assert.ok(overide);
+
+ done();
+ })
+
+ it('in combination with map', function(done){
+ var special = function (obj, key) {
+ if (Array.isArray(obj)) return obj[key];
+ return obj.mpath;
+ }
+ var map = function (val) {
+ return 'convert' == val
+ ? 'mpath'
+ : val;
+ }
+ var o = { mpath: [{ mpath: 'converse' }, { mpath: 'convert' }] }
+
+ assert.equal('mpath', mpath.get('something.1.kewl', o, special, map));
+ done();
+ })
+ })
+ })
+ })
+
+ describe('set', function() {
+ it('prevents writing to __proto__', function() {
+ var obj = {};
+ mpath.set('__proto__.x', 'foobar', obj);
+ assert.ok(!({}.x));
+
+ mpath.set('constructor.prototype.x', 'foobar', obj);
+ assert.ok(!({}.x));
+ });
+
+ describe('without `special`', function() {
+ var o = doc();
+
+ it('works', function(done) {
+ mpath.set('name', 'a new val', o, function(v) {
+ return 'a new val' === v ? 'changed' : v;
+ });
+ assert.deepEqual('changed', o.name);
+
+ mpath.set('name', 'changed', o);
+ assert.deepEqual('changed', o.name);
+
+ mpath.set('first.second.third', [1,{name:'x'},9], o);
+ assert.deepEqual([1,{name:'x'},9], o.first.second.third);
+
+ mpath.set('first.second.third.1.name', 'y', o)
+ assert.deepEqual([1,{name:'y'},9], o.first.second.third);
+
+ mpath.set('comments.1.name', 'ttwwoo', o);
+ assert.deepEqual({ name: 'ttwwoo', _doc: { name: '2' }}, o.comments[1]);
+
+ mpath.set('comments.2.comments.1.comments.0.expand', 'added', o);
+ assert.deepEqual(
+ { val: 'twoo', expand: 'added'}
+ , o.comments[2].comments[1].comments[0]);
+
+ mpath.set('comments.2.comments.1.comments.2', 'added', o);
+ assert.equal(3, o.comments[2].comments[1].comments.length);
+ assert.deepEqual(
+ { val: 'twoo', expand: 'added'}
+ , o.comments[2].comments[1].comments[0]);
+ assert.deepEqual(
+ undefined
+ , o.comments[2].comments[1].comments[1]);
+ assert.deepEqual(
+ 'added'
+ , o.comments[2].comments[1].comments[2]);
+
+ done();
+ })
+
+ describe('array.path', function(){
+ describe('with single non-array value', function(){
+ it('works', function(done){
+ mpath.set('arr.yep', false, o, function (v) {
+ return false === v ? true: v;
+ });
+ assert.deepEqual([
+ { yep: true, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true }
+ ], o.arr);
+
+ mpath.set('arr.yep', false, o);
+
+ assert.deepEqual([
+ { yep: false, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: false }
+ ], o.arr);
+
+ done();
+ })
+ })
+ describe('with array of values', function(){
+ it('that are equal in length', function(done){
+ mpath.set('arr.yep', ['one',2], o, function (v) {
+ return 'one' === v ? 1 : v;
+ });
+ assert.deepEqual([
+ { yep: 1, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 2 }
+ ], o.arr);
+ mpath.set('arr.yep', ['one',2], o);
+
+ assert.deepEqual([
+ { yep: 'one', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 2 }
+ ], o.arr);
+
+ done();
+ })
+
+ it('that is less than length', function(done){
+ mpath.set('arr.yep', [47], o, function (v) {
+ return 47 === v ? 4 : v;
+ });
+ assert.deepEqual([
+ { yep: 4, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 2 }
+ ], o.arr);
+
+ mpath.set('arr.yep', [47], o);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 2 }
+ ], o.arr);
+
+ done();
+ })
+
+ it('that is greater than length', function(done){
+ mpath.set('arr.yep', [5,6,7], o, function (v) {
+ return 5 === v ? 'five' : v;
+ });
+ assert.deepEqual([
+ { yep: 'five', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 6 }
+ ], o.arr);
+
+ mpath.set('arr.yep', [5,6,7], o);
+ assert.deepEqual([
+ { yep: 5, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 6 }
+ ], o.arr);
+
+ done();
+ })
+ })
+ })
+
+ describe('array.$.path', function(){
+ describe('with single non-array value', function(){
+ it('copies the value to each item in array', function(done){
+ mpath.set('arr.$.yep', {xtra: 'double good'}, o, function (v) {
+ return v && v.xtra ? 'hi' : v;
+ });
+ assert.deepEqual([
+ { yep: 'hi', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 'hi'}
+ ], o.arr);
+
+ mpath.set('arr.$.yep', {xtra: 'double good'}, o);
+ assert.deepEqual([
+ { yep: {xtra:'double good'}, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: {xtra:'double good'}}
+ ], o.arr);
+
+ done();
+ })
+ })
+ describe('with array of values', function(){
+ it('copies the value to each item in array', function(done){
+ mpath.set('arr.$.yep', [15], o, function (v) {
+ return v.length === 1 ? [] : v;
+ });
+ assert.deepEqual([
+ { yep: [], arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: []}
+ ], o.arr);
+
+ mpath.set('arr.$.yep', [15], o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: [15]}
+ ], o.arr);
+
+ done();
+ })
+ })
+ })
+
+ describe('array.index.path', function(){
+ it('works', function(done){
+ mpath.set('arr.1.yep', 0, o, function (v) {
+ return 0 === v ? 'zero' : v;
+ });
+ assert.deepEqual([
+ { yep: [15] , arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 'zero' }
+ ], o.arr);
+
+ mpath.set('arr.1.yep', 0, o);
+ assert.deepEqual([
+ { yep: [15] , arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.index.array.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.0.arr.e', 35, o, function (v) {
+ return 35 === v ? 3 : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 47 }, e: 3}, { a: { c: 48 }, e: 3}, { d: 'yep', e: 3 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.0.arr.e', 35, o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 47 }, e: 35}, { a: { c: 48 }, e: 35}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.0.arr.e', ['a','b'], o, function (v) {
+ return 'a' === v ? 'x' : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 47 }, e: 'x'}, { a: { c: 48 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.0.arr.e', ['a','b'], o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 47 }, e: 'a'}, { a: { c: 48 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.index.array.path.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.0.arr.a.b', 36, o, function (v) {
+ return 36 === v ? 3 : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 3 }, e: 'a'}, { a: { c: 48, b: 3 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.0.arr.a.b', 36, o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 36 }, e: 'a'}, { a: { c: 48, b: 36 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.0.arr.a.b', [1,2,3,4], o, function (v) {
+ return 2 === v ? 'two' : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 1 }, e: 'a'}, { a: { c: 48, b: 'two' }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.0.arr.a.b', [1,2,3,4], o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 1 }, e: 'a'}, { a: { c: 48, b: 2 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.index.array.$.path.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.0.arr.$.a.b', '$', o, function (v) {
+ return '$' === v ? 'dolla billz' : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: 'dolla billz' }, e: 'a'}, { a: { c: 48, b: 'dolla billz' }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.0.arr.$.a.b', '$', o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: '$' }, e: 'a'}, { a: { c: 48, b: '$' }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.0.arr.$.a.b', [1], o, function (v) {
+ return Array.isArray(v) ? {} : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: {} }, e: 'a'}, { a: { c: 48, b: {} }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.0.arr.$.a.b', [1], o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: { b: [1] }, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.array.index.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.arr.0.a', 'single', o, function (v) {
+ return 'single' === v ? 'double' : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 'double', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.arr.0.a', 'single', o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 'single', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.arr.0.a', [4,8,15,16,23,42], o, function (v) {
+ return 4 === v ? 3 : v;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 3, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: false }
+ ], o.arr);
+
+ mpath.set('arr.arr.0.a', [4,8,15,16,23,42], o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 4, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: false }
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.array.$.index.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.arr.$.0.a', 'singles', o, function (v) {
+ return 0;
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 0, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('arr.arr.$.0.a', 'singles', o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 'singles', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ mpath.set('$.arr.arr.0.a', 'single', o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 'single', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0 }
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.arr.$.0.a', [4,8,15,16,23,42], o, function (v) {
+ return 'nope'
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: 'nope', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0}
+ ], o.arr);
+
+ mpath.set('arr.arr.$.0.a', [4,8,15,16,23,42], o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: [4,8,15,16,23,42], e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0}
+ ], o.arr);
+
+ mpath.set('arr.$.arr.0.a', [4,8,15,16,23,42,108], o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: [4,8,15,16,23,42,108], e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.array.path.index', function(){
+ it('with single value', function(done){
+ mpath.set('arr.arr.a.7', 47, o, function (v) {
+ return 1
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: [4,8,15,16,23,42,108,1], e: 'a'}, { a: { c: 48, b: [1], '7': 1 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0}
+ ], o.arr);
+
+ mpath.set('arr.arr.a.7', 47, o);
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: [4,8,15,16,23,42,108,47], e: 'a'}, { a: { c: 48, b: [1], '7': 47 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0}
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ o.arr[1].arr = [{ a: [] }, { a: [] }, { a: null }];
+ mpath.set('arr.arr.a.7', [[null,46], [undefined, 'woot']], o);
+
+ var a1 = [];
+ var a2 = [];
+ a1[7] = undefined;
+ a2[7] = 'woot';
+
+ assert.deepEqual([
+ { yep: [15], arr: [{ a: [4,8,15,16,23,42,108,null], e: 'a'}, { a: { c: 48, b: [1], '7': 46 }, e: 'b'}, { d: 'yep', e: 35 }] }
+ , { yep: 0, arr: [{a:a1},{a:a2},{a:null}] }
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('handles array.array.path', function(){
+ it('with single', function(done){
+ o.arr[1].arr = [{},{}];
+ assert.deepEqual([{},{}], o.arr[1].arr);
+ o.arr.push({ arr: 'something else' });
+ o.arr.push({ arr: ['something else'] });
+ o.arr.push({ arr: [[]] });
+ o.arr.push({ arr: [5] });
+
+ var weird = [];
+ weird.e = 'xmas';
+
+ // test
+ mpath.set('arr.arr.e', 47, o, function (v) {
+ return 'xmas'
+ });
+ assert.deepEqual([
+ { yep: [15], arr: [
+ { a: [4,8,15,16,23,42,108,null], e: 'xmas'}
+ , { a: { c: 48, b: [1], '7': 46 }, e: 'xmas'}
+ , { d: 'yep', e: 'xmas' }
+ ]
+ }
+ , { yep: 0, arr: [{e: 'xmas'}, {e:'xmas'}] }
+ , { arr: 'something else' }
+ , { arr: ['something else'] }
+ , { arr: [weird] }
+ , { arr: [5] }
+ ]
+ , o.arr);
+
+ weird.e = 47;
+
+ mpath.set('arr.arr.e', 47, o);
+ assert.deepEqual([
+ { yep: [15], arr: [
+ { a: [4,8,15,16,23,42,108,null], e: 47}
+ , { a: { c: 48, b: [1], '7': 46 }, e: 47}
+ , { d: 'yep', e: 47 }
+ ]
+ }
+ , { yep: 0, arr: [{e: 47}, {e:47}] }
+ , { arr: 'something else' }
+ , { arr: ['something else'] }
+ , { arr: [weird] }
+ , { arr: [5] }
+ ]
+ , o.arr);
+
+ done();
+ })
+ it('with arrays', function(done){
+ mpath.set('arr.arr.e', [[1,2,3],[4,5],null,[],[6], [7,8,9]], o, function (v) {
+ return 10;
+ });
+
+ var weird = [];
+ weird.e = 10;
+
+ assert.deepEqual([
+ { yep: [15], arr: [
+ { a: [4,8,15,16,23,42,108,null], e: 10}
+ , { a: { c: 48, b: [1], '7': 46 }, e: 10}
+ , { d: 'yep', e: 10 }
+ ]
+ }
+ , { yep: 0, arr: [{e: 10}, {e:10}] }
+ , { arr: 'something else' }
+ , { arr: ['something else'] }
+ , { arr: [weird] }
+ , { arr: [5] }
+ ]
+ , o.arr);
+
+ mpath.set('arr.arr.e', [[1,2,3],[4,5],null,[],[6], [7,8,9]], o);
+
+ weird.e = 6;
+
+ assert.deepEqual([
+ { yep: [15], arr: [
+ { a: [4,8,15,16,23,42,108,null], e: 1}
+ , { a: { c: 48, b: [1], '7': 46 }, e: 2}
+ , { d: 'yep', e: 3 }
+ ]
+ }
+ , { yep: 0, arr: [{e: 4}, {e:5}] }
+ , { arr: 'something else' }
+ , { arr: ['something else'] }
+ , { arr: [weird] }
+ , { arr: [5] }
+ ]
+ , o.arr);
+
+ done();
+ })
+ })
+ })
+
+ describe('with `special`', function(){
+ var o = doc();
+
+ it('works', function(done){
+ mpath.set('name', 'chan', o, special, function (v) {
+ return 'hi';
+ });
+ assert.deepEqual('hi', o.name);
+
+ mpath.set('name', 'changer', o, special);
+ assert.deepEqual('changer', o.name);
+
+ mpath.set('first.second.third', [1,{name:'y'},9], o, special);
+ assert.deepEqual([1,{name:'y'},9], o.first.second.third);
+
+ mpath.set('first.second.third.1.name', 'z', o, special)
+ assert.deepEqual([1,{name:'z'},9], o.first.second.third);
+
+ mpath.set('comments.1.name', 'ttwwoo', o, special);
+ assert.deepEqual({ name: 'two', _doc: { name: 'ttwwoo' }}, o.comments[1]);
+
+ mpath.set('comments.2.comments.1.comments.0.expander', 'adder', o, special, function (v) {
+ return 'super'
+ });
+ assert.deepEqual(
+ { val: 2, expander: 'super'}
+ , o.comments[2]._doc.comments[1]._doc.comments[0]);
+
+ mpath.set('comments.2.comments.1.comments.0.expander', 'adder', o, special);
+ assert.deepEqual(
+ { val: 2, expander: 'adder'}
+ , o.comments[2]._doc.comments[1]._doc.comments[0]);
+
+ mpath.set('comments.2.comments.1.comments.2', 'set', o, special);
+ assert.equal(3, o.comments[2]._doc.comments[1]._doc.comments.length);
+ assert.deepEqual(
+ { val: 2, expander: 'adder'}
+ , o.comments[2]._doc.comments[1]._doc.comments[0]);
+ assert.deepEqual(
+ undefined
+ , o.comments[2]._doc.comments[1]._doc.comments[1]);
+ assert.deepEqual(
+ 'set'
+ , o.comments[2]._doc.comments[1]._doc.comments[2]);
+ done();
+ })
+
+ describe('array.path', function(){
+ describe('with single non-array value', function(){
+ it('works', function(done){
+ o.arr[1]._doc = { special: true }
+
+ mpath.set('arr.yep', false, o, special, function (v) {
+ return 'yes';
+ });
+ assert.deepEqual([
+ { yep: 'yes', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true, _doc: { special: true, yep: 'yes'}}
+ ], o.arr);
+
+ mpath.set('arr.yep', false, o, special);
+ assert.deepEqual([
+ { yep: false, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true, _doc: { special: true, yep: false }}
+ ], o.arr);
+
+ done();
+ })
+ })
+ describe('with array of values', function(){
+ it('that are equal in length', function(done){
+ mpath.set('arr.yep', ['one',2], o, special, function (v) {
+ return 2 === v ? 20 : v;
+ });
+ assert.deepEqual([
+ { yep: 'one', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true, _doc: { special: true, yep: 20}}
+ ], o.arr);
+
+ mpath.set('arr.yep', ['one',2], o, special);
+ assert.deepEqual([
+ { yep: 'one', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true, _doc: { special: true, yep: 2}}
+ ], o.arr);
+
+ done();
+ })
+
+ it('that is less than length', function(done){
+ mpath.set('arr.yep', [47], o, special, function (v) {
+ return 80;
+ });
+ assert.deepEqual([
+ { yep: 80, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true, _doc: { special: true, yep: 2}}
+ ], o.arr);
+
+ mpath.set('arr.yep', [47], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+ , { yep: true, _doc: { special: true, yep: 2}}
+ ], o.arr);
+
+ // add _doc to first element
+ o.arr[0]._doc = { yep: 46, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] }
+
+ mpath.set('arr.yep', [20], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }], _doc: { yep: 20, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: 2}}
+ ], o.arr);
+
+ done();
+ })
+
+ it('that is greater than length', function(done){
+ mpath.set('arr.yep', [5,6,7], o, special, function () {
+ return 'x';
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }], _doc: { yep: 'x', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: 'x'}}
+ ], o.arr);
+
+ mpath.set('arr.yep', [5,6,7], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }], _doc: { yep: 5, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: 6}}
+ ], o.arr);
+
+ done();
+ })
+ })
+ })
+
+ describe('array.$.path', function(){
+ describe('with single non-array value', function(){
+ it('copies the value to each item in array', function(done){
+ mpath.set('arr.$.yep', {xtra: 'double good'}, o, special, function (v) {
+ return 9;
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: 9, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: 9}}
+ ], o.arr);
+
+ mpath.set('arr.$.yep', {xtra: 'double good'}, o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: {xtra:'double good'}, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: {xtra:'double good'}}}
+ ], o.arr);
+
+ done();
+ })
+ })
+ describe('with array of values', function(){
+ it('copies the value to each item in array', function(done){
+ mpath.set('arr.$.yep', [15], o, special, function (v) {
+ return 'array'
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: 'array', arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: 'array'}}
+ ], o.arr);
+
+ mpath.set('arr.$.yep', [15], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: [15]}}
+ ], o.arr);
+
+ done();
+ })
+ })
+ })
+
+ describe('array.index.path', function(){
+ it('works', function(done){
+ mpath.set('arr.1.yep', 0, o, special, function (v) {
+ return 1;
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: 1}}
+ ], o.arr);
+
+ mpath.set('arr.1.yep', 0, o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.index.array.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.0.arr.e', 35, o, special, function (v) {
+ return 30
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 30}, { a: { c: 48 }, e: 30}, { d: 'yep', e: 30 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.0.arr.e', 35, o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 35}, { a: { c: 48 }, e: 35}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.0.arr.e', ['a','b'], o, special, function (v) {
+ return 'a' === v ? 'A' : v;
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 'A'}, { a: { c: 48 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.0.arr.e', ['a','b'], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 'a'}, { a: { c: 48 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.index.array.path.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.0.arr.a.b', 36, o, special, function (v) {
+ return 20
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 20 }, e: 'a'}, { a: { c: 48, b: 20 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.0.arr.a.b', 36, o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 36 }, e: 'a'}, { a: { c: 48, b: 36 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.0.arr.a.b', [1,2,3,4], o, special, function (v) {
+ return v*2;
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 2 }, e: 'a'}, { a: { c: 48, b: 4 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.0.arr.a.b', [1,2,3,4], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 1 }, e: 'a'}, { a: { c: 48, b: 2 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.index.array.$.path.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.0.arr.$.a.b', '$', o, special, function (v) {
+ return 'dollaz'
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: 'dollaz' }, e: 'a'}, { a: { c: 48, b: 'dollaz' }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.0.arr.$.a.b', '$', o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: '$' }, e: 'a'}, { a: { c: 48, b: '$' }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.0.arr.$.a.b', [1], o, special, function (v) {
+ return {};
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: {} }, e: 'a'}, { a: { c: 48, b: {} }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.0.arr.$.a.b', [1], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: { b: [1] }, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.array.index.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.arr.0.a', 'single', o, special, function (v) {
+ return 88;
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: 88, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.arr.0.a', 'single', o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: 'single', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.arr.0.a', [4,8,15,16,23,42], o, special, function (v) {
+ return v*2;
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: 8, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.arr.0.a', [4,8,15,16,23,42], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: 4, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.array.$.index.path', function(){
+ it('with single value', function(done){
+ mpath.set('arr.arr.$.0.a', 'singles', o, special, function (v) {
+ return v.toUpperCase();
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: 'SINGLES', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.arr.$.0.a', 'singles', o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: 'singles', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('$.arr.arr.0.a', 'single', o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: 'single', e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ mpath.set('arr.arr.$.0.a', [4,8,15,16,23,42], o, special, function (v) {
+ return Array
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: Array, e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.arr.$.0.a', [4,8,15,16,23,42], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: [4,8,15,16,23,42], e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.$.arr.0.a', [4,8,15,16,23,42,108], o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: [4,8,15,16,23,42,108], e: 'a'}, { a: { c: 48, b: [1] }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('array.array.path.index', function(){
+ it('with single value', function(done){
+ mpath.set('arr.arr.a.7', 47, o, special, function (v) {
+ return Object;
+ });
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: [4,8,15,16,23,42,108,Object], e: 'a'}, { a: { c: 48, b: [1], '7': Object }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.arr.a.7', 47, o, special);
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: [4,8,15,16,23,42,108,47], e: 'a'}, { a: { c: 48, b: [1], '7': 47 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ it('with array', function(done){
+ o.arr[1]._doc.arr = [{ a: [] }, { a: [] }, { a: null }];
+ mpath.set('arr.arr.a.7', [[null,46], [undefined, 'woot']], o, special, function (v) {
+ return undefined === v ? 'nope' : v;
+ });
+
+ var a1 = [];
+ var a2 = [];
+ a1[7] = 'nope';
+ a2[7] = 'woot';
+
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: [4,8,15,16,23,42,108,null], e: 'a'}, { a: { c: 48, b: [1], '7': 46 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { arr: [{a:a1},{a:a2},{a:null}], special: true, yep: 0}}
+ ], o.arr);
+
+ mpath.set('arr.arr.a.7', [[null,46], [undefined, 'woot']], o, special);
+
+ a1[7] = undefined;
+
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: { yep: [15], arr: [{ a: [4,8,15,16,23,42,108,null], e: 'a'}, { a: { c: 48, b: [1], '7': 46 }, e: 'b'}, { d: 'yep', e: 35 }] } }
+ , { yep: true, _doc: { arr: [{a:a1},{a:a2},{a:null}], special: true, yep: 0}}
+ ], o.arr);
+
+ done();
+ })
+ })
+
+ describe('handles array.array.path', function(){
+ it('with single', function(done){
+ o.arr[1]._doc.arr = [{},{}];
+ assert.deepEqual([{},{}], o.arr[1]._doc.arr);
+ o.arr.push({ _doc: { arr: 'something else' }});
+ o.arr.push({ _doc: { arr: ['something else'] }});
+ o.arr.push({ _doc: { arr: [[]] }});
+ o.arr.push({ _doc: { arr: [5] }});
+
+ // test
+ mpath.set('arr.arr.e', 47, o, special);
+
+ var weird = [];
+ weird.e = 47;
+
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: {
+ yep: [15]
+ , arr: [
+ { a: [4,8,15,16,23,42,108,null], e: 47}
+ , { a: { c: 48, b: [1], '7': 46 }, e: 47}
+ , { d: 'yep', e: 47 }
+ ]
+ }
+ }
+ , { yep: true
+ , _doc: {
+ arr: [
+ {e:47}
+ , {e:47}
+ ]
+ , special: true
+ , yep: 0
+ }
+ }
+ , { _doc: { arr: 'something else' }}
+ , { _doc: { arr: ['something else'] }}
+ , { _doc: { arr: [weird] }}
+ , { _doc: { arr: [5] }}
+ ]
+ , o.arr);
+
+ done();
+ })
+ it('with arrays', function(done){
+ mpath.set('arr.arr.e', [[1,2,3],[4,5],null,[],[6], [7,8,9]], o, special);
+
+ var weird = [];
+ weird.e = 6;
+
+ assert.deepEqual([
+ { yep: 47, arr: [{ a: { b: 47 }}, { a: { c: 48 }}, { d: 'yep' }]
+ , _doc: {
+ yep: [15]
+ , arr: [
+ { a: [4,8,15,16,23,42,108,null], e: 1}
+ , { a: { c: 48, b: [1], '7': 46 }, e: 2}
+ , { d: 'yep', e: 3 }
+ ]
+ }
+ }
+ , { yep: true
+ , _doc: {
+ arr: [
+ {e:4}
+ , {e:5}
+ ]
+ , special: true
+ , yep: 0
+ }
+ }
+ , { _doc: { arr: 'something else' }}
+ , { _doc: { arr: ['something else'] }}
+ , { _doc: { arr: [weird] }}
+ , { _doc: { arr: [5] }}
+ ]
+ , o.arr);
+
+ done();
+ })
+ })
+
+ describe('that is a function', function(){
+ describe('without map', function(){
+ it('works on array value', function(done){
+ var o = { hello: { world: [{ how: 'are' }, { you: '?' }] }};
+ var special = function (obj, key, val) {
+ if (val) {
+ obj[key] = val;
+ } else {
+ return 'thing' == key
+ ? obj.world
+ : obj[key]
+ }
+ }
+ mpath.set('hello.thing.how', 'arrrr', o, special);
+ assert.deepEqual(o, { hello: { world: [{ how: 'arrrr' }, { you: '?', how: 'arrrr' }] }});
+ done();
+ })
+ it('works on non-array value', function(done){
+ var o = { hello: { world: { how: 'are you' }}};
+ var special = function (obj, key, val) {
+ if (val) {
+ obj[key] = val;
+ } else {
+ return 'thing' == key
+ ? obj.world
+ : obj[key]
+ }
+ }
+ mpath.set('hello.thing.how', 'RU', o, special);
+ assert.deepEqual(o, { hello: { world: { how: 'RU' }}});
+ done();
+ })
+ })
+ it('works with map', function(done){
+ var o = { hello: { world: [{ how: 'are' }, { you: '?' }] }};
+ var special = function (obj, key, val) {
+ if (val) {
+ obj[key] = val;
+ } else {
+ return 'thing' == key
+ ? obj.world
+ : obj[key]
+ }
+ }
+ var map = function (val) {
+ return 'convert' == val
+ ? 'ºº'
+ : val
+ }
+ mpath.set('hello.thing.how', 'convert', o, special, map);
+ assert.deepEqual(o, { hello: { world: [{ how: 'ºº' }, { you: '?', how: 'ºº' }] }});
+ done();
+ })
+ })
+
+ })
+
+ describe('get/set integration', function(){
+ var o = doc();
+
+ it('works', function(done){
+ var vals = mpath.get('array.o.array.x.b', o);
+
+ vals[0][0][2] = 10;
+ vals[1][0][1] = 0;
+ vals[1][1] = 'Rambaldi';
+ vals[1][2] = [12,14];
+ vals[2] = [{changed:true}, [null, ['changed','to','array']]];
+
+ mpath.set('array.o.array.x.b', vals, o);
+
+ var t = [
+ { o: { array: [{x: {b: [4,6,10]}}, { y: 10} ] }}
+ , { o: { array: [{x: {b: [1,0,3]}}, { x: {b:'Rambaldi',z: 10 }}, { x: {b: [12,14]}}] }}
+ , { o: { array: [{x: {b: {changed:true}}}, { x: { b: [null, ['changed','to','array']]}}]}}
+ , { o: { array: [{x: null }] }}
+ , { o: { array: [{y: 3 }] }}
+ , { o: { array: [3, 0, null] }}
+ , { o: { name: 'ha' }}
+ ];
+ assert.deepEqual(t, o.array);
+ done();
+ })
+
+ it('array.prop', function(done){
+ mpath.set('comments.name', ['this', 'was', 'changed'], o);
+
+ assert.deepEqual([
+ { name: 'this' }
+ , { name: 'was', _doc: { name: '2' }}
+ , { name: 'changed'
+ , comments: [{},{ comments: [{val: 'twoo'}]}]
+ , _doc: { name: '3', comments: [{},{ _doc: { comments: [{ val: 2 }] }}] }}
+ ], o.comments);
+
+ mpath.set('comments.name', ['also', 'changed', 'this'], o, special);
+
+ assert.deepEqual([
+ { name: 'also' }
+ , { name: 'was', _doc: { name: 'changed' }}
+ , { name: 'changed'
+ , comments: [{},{ comments: [{val: 'twoo'}]}]
+ , _doc: { name: 'this', comments: [{},{ _doc: { comments: [{ val: 2 }] }}] }}
+ ], o.comments);
+
+ done();
+ })
+
+ })
+
+ describe('multiple $ use', function(){
+ var o = doc();
+ it('is ok', function(done){
+ assert.doesNotThrow(function () {
+ mpath.set('arr.$.arr.$.a', 35, o);
+ });
+ done();
+ })
+ })
+
+ it('has', function(done) {
+ assert.ok(mpath.has('a', { a: 1 }));
+ assert.ok(mpath.has('a', { a: undefined }));
+ assert.ok(!mpath.has('a', {}));
+ assert.ok(!mpath.has('a', null));
+
+ assert.ok(mpath.has('a.b', { a: { b: 1 } }));
+ assert.ok(mpath.has('a.b', { a: { b: undefined } }));
+ assert.ok(!mpath.has('a.b', { a: 1 }));
+ assert.ok(!mpath.has('a.b', { a: null }));
+
+ done();
+ });
+
+ it('underneath a map', function(done) {
+ if (!global.Map) {
+ done();
+ return;
+ }
+ assert.equal(mpath.get('a.b', { a: new Map([['b', 1]]) }), 1);
+
+ var m = new Map([['b', 1]]);
+ var obj = { a: m };
+ mpath.set('a.c', 2, obj);
+ assert.equal(m.get('c'), 2);
+
+ done();
+ });
+
+ it('unset', function(done) {
+ var o = { a: 1 };
+ mpath.unset('a', o);
+ assert.deepEqual(o, {});
+
+ o = { a: { b: 1 } };
+ mpath.unset('a.b', o);
+ assert.deepEqual(o, { a: {} });
+
+ o = { a: null };
+ mpath.unset('a.b', o);
+ assert.deepEqual(o, { a: null });
+
+ done();
+ });
+
+ it('unset with __proto__', function(done) {
+ // Should refuse to set __proto__
+ function Clazz() {}
+ Clazz.prototype.foobar = true;
+
+ mpath.unset('__proto__.foobar', new Clazz());
+ assert.ok(Clazz.prototype.foobar);
+
+ mpath.unset('constructor.prototype.foobar', new Clazz());
+ assert.ok(Clazz.prototype.foobar);
+
+ done();
+ });
+
+ it('ignores setting a nested path that doesnt exist', function(done){
+ var o = doc();
+ assert.doesNotThrow(function(){
+ mpath.set('thing.that.is.new', 10, o);
+ })
+ done();
+ });
+ });
+});
diff --git a/node_modules/mquery/.eslintignore b/node_modules/mquery/.eslintignore
new file mode 100644
index 0000000..4b4d863
--- /dev/null
+++ b/node_modules/mquery/.eslintignore
@@ -0,0 +1 @@
+coverage/
\ No newline at end of file
diff --git a/node_modules/mquery/.travis.yml b/node_modules/mquery/.travis.yml
new file mode 100644
index 0000000..de18290
--- /dev/null
+++ b/node_modules/mquery/.travis.yml
@@ -0,0 +1,14 @@
+language: node_js
+node_js:
+ - "4"
+ - "5"
+ - "6"
+ - "7"
+ - "8"
+ - "9"
+ - "10"
+script:
+ - npm test
+ - npm run lint
+services:
+ - mongodb
diff --git a/node_modules/mquery/History.md b/node_modules/mquery/History.md
new file mode 100644
index 0000000..9238276
--- /dev/null
+++ b/node_modules/mquery/History.md
@@ -0,0 +1,334 @@
+3.2.0 / 2018-08-24
+==================
+ * feat: add $useProjection to opt in to using `projection` instead of `fields` re: MongoDB deprecation warnings Automattic/mongoose#6880
+
+3.1.2 / 2018-08-01
+==================
+ * chore: move eslint to devDependencies #110 [jakesjews](https://github.com/jakesjews)
+
+3.1.1 / 2018-07-30
+==================
+ * chore: add eslint #107 [Fonger](https://github.com/Fonger)
+ * docs: clean up readConcern docs #106 [Fonger](https://github.com/Fonger)
+
+3.1.0 / 2018-07-29
+==================
+ * feat: add `readConcern()` helper #105 [Fonger](https://github.com/Fonger)
+ * feat: add `maxTimeMS()` as alias of `maxTime()` #105 [Fonger](https://github.com/Fonger)
+ * feat: add `collation()` helper #105 [Fonger](https://github.com/Fonger)
+
+3.0.1 / 2018-07-02
+==================
+ * fix: parse sort array options correctly #103 #102 [Fonger](https://github.com/Fonger)
+
+3.0.0 / 2018-01-20
+==================
+ * chore: upgrade deps and add nsp
+
+3.0.0-rc0 / 2017-12-06
+======================
+ * BREAKING CHANGE: remove support for node < 4
+ * BREAKING CHANGE: remove support for retainKeyOrder, will always be true by default re: Automattic/mongoose#2749
+
+2.3.3 / 2017-11-19
+==================
+ * fixed; catch sync errors in cursor.toArray() re: Automattic/mongoose#5812
+
+2.3.2 / 2017-09-27
+==================
+ * fixed; bumped debug -> 2.6.9 re: #89
+
+2.3.1 / 2017-05-22
+==================
+ * fixed; bumped debug -> 2.6.7 re: #86
+
+2.3.0 / 2017-03-05
+==================
+ * added; replaceOne function
+ * added; deleteOne and deleteMany functions
+
+2.2.3 / 2017-01-31
+==================
+ * fixed; throw correct error when passing incorrectly formatted array to sort()
+
+2.2.2 / 2017-01-31
+==================
+ * fixed; allow passing maps to sort()
+
+2.2.1 / 2017-01-29
+==================
+ * fixed; allow passing string to hint()
+
+2.2.0 / 2017-01-08
+==================
+ * added; updateOne and updateMany functions
+
+2.1.0 / 2016-12-22
+==================
+ * added; ability to pass an array to select() #81 [dciccale](https://github.com/dciccale)
+
+2.0.0 / 2016-09-25
+==================
+ * added; support for mongodb driver 2.0 streams
+
+1.12.0 / 2016-09-25
+===================
+ * added; `retainKeyOrder` option re: Automattic/mongoose#4542
+
+1.11.0 / 2016-06-04
+===================
+ * added; `.minDistance()` helper and minDistance for `.near()` Automattic/mongoose#4179
+
+1.10.1 / 2016-04-26
+===================
+ * fixed; ensure conditions is an object before assigning #75
+
+1.10.0 / 2016-03-16
+==================
+
+ * updated; bluebird to latest 2.10.2 version #74 [matskiv](https://github.com/matskiv)
+
+1.9.0 / 2016-03-15
+==================
+ * added; `.eq` as a shortcut for `.equals` #72 [Fonger](https://github.com/Fonger)
+ * added; ability to use array syntax for sort re: https://jira.mongodb.org/browse/NODE-578 #67
+
+1.8.0 / 2016-03-01
+==================
+ * fixed; dont throw an error if count used with sort or select Automattic/mongoose#3914
+
+1.7.0 / 2016-02-23
+==================
+ * fixed; don't treat objects with a length property as argument objects #70
+ * added; `.findCursor()` method #69 [nswbmw](https://github.com/nswbmw)
+ * added; `_compiledUpdate` property #68 [nswbmw](https://github.com/nswbmw)
+
+1.6.2 / 2015-07-12
+==================
+
+ * fixed; support exec cb being called synchronously #66
+
+1.6.1 / 2015-06-16
+==================
+
+ * fixed; do not treat $meta projection as inclusive [vkarpov15](https://github.com/vkarpov15)
+
+1.6.0 / 2015-05-27
+==================
+
+ * update dependencies #65 [bachp](https://github.com/bachp)
+
+1.5.0 / 2015-03-31
+==================
+
+ * fixed; debug output
+ * fixed; allow hint usage with count #61 [trueinsider](https://github.com/trueinsider)
+
+1.4.0 / 2015-03-29
+==================
+
+ * added; object support to slice() #60 [vkarpov15](https://github.com/vkarpov15)
+ * debug; improved output #57 [flyvictor](https://github.com/flyvictor)
+
+1.3.0 / 2014-11-06
+==================
+
+ * added; setTraceFunction() #53 from [jlai](https://github.com/jlai)
+
+1.2.1 / 2014-09-26
+==================
+
+ * fixed; distinct assignment in toConstructor() #51 from [esco](https://github.com/esco)
+
+1.2.0 / 2014-09-18
+==================
+
+ * added; stream() support for find()
+
+1.1.0 / 2014-09-15
+==================
+
+ * add #then for co / koa support
+ * start checking code coverage
+
+1.0.0 / 2014-07-07
+==================
+
+ * Remove broken require() calls until they're actually implemented #48 [vkarpov15](https://github.com/vkarpov15)
+
+0.9.0 / 2014-05-22
+==================
+
+ * added; thunk() support
+ * release 0.8.0
+
+0.8.0 / 2014-05-15
+==================
+
+ * added; support for maxTimeMS #44 [yoitsro](https://github.com/yoitsro)
+ * updated; devDependency (driver to 1.4.4)
+
+0.7.0 / 2014-05-02
+==================
+
+ * fixed; pass $maxDistance in $near object as described in docs #43 [vkarpov15](https://github.com/vkarpov15)
+ * fixed; cloning buffers #42 [gjohnson](https://github.com/gjohnson)
+ * tests; a little bit more `mongodb` agnostic #34 [refack](https://github.com/refack)
+
+0.6.0 / 2014-04-01
+==================
+
+ * fixed; Allow $meta args in sort() so text search sorting works #37 [vkarpov15](https://github.com/vkarpov15)
+
+0.5.3 / 2014-02-22
+==================
+
+ * fixed; cloning mongodb.Binary
+
+0.5.2 / 2014-01-30
+==================
+
+ * fixed; cloning ObjectId constructors
+ * fixed; cloning of ReadPreferences #30 [ashtuchkin](https://github.com/ashtuchkin)
+ * tests; use specific mongodb version #29 [AvianFlu](https://github.com/AvianFlu)
+ * tests; remove dependency on ObjectId #28 [refack](https://github.com/refack)
+ * tests; add failing ReadPref test
+
+0.5.1 / 2014-01-17
+==================
+
+ * added; deprecation notice to tags parameter #27 [ashtuchkin](https://github.com/ashtuchkin)
+ * readme; add links
+
+0.5.0 / 2014-01-16
+==================
+
+ * removed; mongodb driver dependency #26 [ashtuchkin](https://github.com/ashtuchkin)
+ * removed; first class support of read preference tags #26 (still supported though) [ashtuchkin](https://github.com/ashtuchkin)
+ * added; better ObjectId clone support
+ * fixed; cloning objects that have no constructor #21
+ * docs; cleaned up [ashtuchkin](https://github.com/ashtuchkin)
+
+0.4.2 / 2014-01-08
+==================
+
+ * updated; debug module 0.7.4 [refack](https://github.com/refack)
+
+0.4.1 / 2014-01-07
+==================
+
+ * fixed; inclusive/exclusive logic
+
+0.4.0 / 2014-01-06
+==================
+
+ * added; selected()
+ * added; selectedInclusively()
+ * added; selectedExclusively()
+
+0.3.3 / 2013-11-14
+==================
+
+ * Fix Mongo DB Dependency #20 [rschmukler](https://github.com/rschmukler)
+
+0.3.2 / 2013-09-06
+==================
+
+ * added; geometry support for near()
+
+0.3.1 / 2013-08-22
+==================
+
+ * fixed; update retains key order #19
+
+0.3.0 / 2013-08-22
+==================
+
+ * less hardcoded isNode env detection #18 [vshulyak](https://github.com/vshulyak)
+ * added; validation of findAndModify varients
+ * clone update doc before execution
+ * stricter env checks
+
+0.2.7 / 2013-08-2
+==================
+
+ * Now support GeoJSON point values for Query#near
+
+0.2.6 / 2013-07-30
+==================
+
+ * internally, 'asc' and 'desc' for sorts are now converted into 1 and -1, respectively
+
+0.2.5 / 2013-07-30
+==================
+
+ * updated docs
+ * changed internal representation of `sort` to use objects instead of arrays
+
+0.2.4 / 2013-07-25
+==================
+
+ * updated; sliced to 0.0.5
+
+0.2.3 / 2013-07-09
+==================
+
+ * now using a callback in collection.find instead of directly calling toArray() on the cursor [ebensing](https://github.com/ebensing)
+
+0.2.2 / 2013-07-09
+==================
+
+ * now exposing mongodb export to allow for better testing [ebensing](https://github.com/ebensing)
+
+0.2.1 / 2013-07-08
+==================
+
+ * select no longer accepts arrays as parameters [ebensing](https://github.com/ebensing)
+
+0.2.0 / 2013-07-05
+==================
+
+ * use $geoWithin by default
+
+0.1.2 / 2013-07-02
+==================
+
+ * added use$geoWithin flag [ebensing](https://github.com/ebensing)
+ * fix read preferences typo [ebensing](https://github.com/ebensing)
+ * fix reference to old param name in exists() [ebensing](https://github.com/ebensing)
+
+0.1.1 / 2013-06-24
+==================
+
+ * fixed; $intersects -> $geoIntersects #14 [ebensing](https://github.com/ebensing)
+ * fixed; Retain key order when copying objects #15 [ebensing](https://github.com/ebensing)
+ * bump mongodb dev dep
+
+0.1.0 / 2013-05-06
+==================
+
+ * findAndModify; return the query
+ * move mquery.proto.canMerge to mquery.canMerge
+ * overwrite option now works with non-empty objects
+ * use strict mode
+ * validate count options
+ * validate distinct options
+ * add aggregate to base collection methods
+ * clone merge arguments
+ * clone merged update arguments
+ * move subclass to mquery.prototype.toConstructor
+ * fixed; maxScan casing
+ * use regexp-clone
+ * added; geometry/intersects support
+ * support $and
+ * near: do not use "radius"
+ * callbacks always fire on next turn of loop
+ * defined collection interface
+ * remove time from tests
+ * clarify goals
+ * updated docs;
+
+0.0.1 / 2012-12-15
+==================
+
+ * initial release
diff --git a/node_modules/mquery/LICENSE b/node_modules/mquery/LICENSE
new file mode 100644
index 0000000..38c529d
--- /dev/null
+++ b/node_modules/mquery/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012 [Aaron Heckmann](aaron.heckmann+github@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/mquery/Makefile b/node_modules/mquery/Makefile
new file mode 100644
index 0000000..587655d
--- /dev/null
+++ b/node_modules/mquery/Makefile
@@ -0,0 +1,26 @@
+
+test:
+ @NODE_ENV=test ./node_modules/.bin/mocha $(T) $(TESTS)
+
+test-cov:
+ @NODE_ENV=test node \
+ node_modules/.bin/istanbul cover \
+ ./node_modules/.bin/_mocha \
+ -- -u exports \
+
+open-cov:
+ open coverage/lcov-report/index.html
+
+lint:
+ @NODE_ENV=test node ./node_modules/eslint/bin/eslint.js .
+
+test-travis:
+ @NODE_ENV=test node \
+ node_modules/.bin/istanbul cover \
+ ./node_modules/.bin/_mocha \
+ --report lcovonly \
+ --bail
+ @NODE_ENV=test node \
+ ./node_modules/eslint/bin/eslint.js .
+
+.PHONY: test test-cov open-cov lint test-travis
diff --git a/node_modules/mquery/README.md b/node_modules/mquery/README.md
new file mode 100644
index 0000000..58d6322
--- /dev/null
+++ b/node_modules/mquery/README.md
@@ -0,0 +1,1375 @@
+# mquery
+
+`mquery` is a fluent mongodb query builder designed to run in multiple environments.
+
+[](https://travis-ci.org/aheckmann/mquery)
+[](http://badge.fury.io/js/mquery)
+
+[](https://www.npmjs.com/package/mquery)
+
+## Features
+
+ - fluent query builder api
+ - custom base query support
+ - MongoDB 2.4 geoJSON support
+ - method + option combinations validation
+ - node.js driver compatibility
+ - environment detection
+ - [debug](https://github.com/visionmedia/debug) support
+ - separated collection implementations for maximum flexibility
+
+## Use
+
+```js
+require('mongodb').connect(uri, function (err, db) {
+ if (err) return handleError(err);
+
+ // get a collection
+ var collection = db.collection('artists');
+
+ // pass it to the constructor
+ mquery(collection).find({..}, callback);
+
+ // or pass it to the collection method
+ mquery().find({..}).collection(collection).exec(callback)
+
+ // or better yet, create a custom query constructor that has it always set
+ var Artist = mquery(collection).toConstructor();
+ Artist().find(..).where(..).exec(callback)
+})
+```
+
+`mquery` requires a collection object to work with. In the example above we just pass the collection object created using the official [MongoDB driver](https://github.com/mongodb/node-mongodb-native).
+
+
+## Fluent API
+
+- [find](#find)
+- [findOne](#findOne)
+- [count](#count)
+- [remove](#remove)
+- [update](#update)
+- [findOneAndUpdate](#findoneandupdate)
+- [findOneAndDelete, findOneAndRemove](#findoneandremove)
+- [distinct](#distinct)
+- [exec](#exec)
+- [stream](#stream)
+- [all](#all)
+- [and](#and)
+- [box](#box)
+- [circle](#circle)
+- [elemMatch](#elemmatch)
+- [equals](#equals)
+- [exists](#exists)
+- [geometry](#geometry)
+- [gt](#gt)
+- [gte](#gte)
+- [in](#in)
+- [intersects](#intersects)
+- [lt](#lt)
+- [lte](#lte)
+- [maxDistance](#maxdistance)
+- [mod](#mod)
+- [ne](#ne)
+- [nin](#nin)
+- [nor](#nor)
+- [near](#near)
+- [or](#or)
+- [polygon](#polygon)
+- [regex](#regex)
+- [select](#select)
+- [selected](#selected)
+- [selectedInclusively](#selectedinclusively)
+- [selectedExclusively](#selectedexclusively)
+- [size](#size)
+- [slice](#slice)
+- [within](#within)
+- [where](#where)
+- [$where](#where-1)
+- [batchSize](#batchsize)
+- [collation](#collation)
+- [comment](#comment)
+- [hint](#hint)
+- [j](#j)
+- [limit](#limit)
+- [maxScan](#maxscan)
+- [maxTime, maxTimeMS](#maxtime)
+- [skip](#skip)
+- [sort](#sort)
+- [read, setReadPreference](#read)
+- [readConcern, r](#readconcern)
+- [slaveOk](#slaveok)
+- [snapshot](#snapshot)
+- [tailable](#tailable)
+- [writeConcern, w](#writeconcern)
+- [wtimeout, wTimeout](#wtimeout)
+
+## Helpers
+
+- [collection](#collection)
+- [then](#then)
+- [thunk](#thunk)
+- [merge](#mergeobject)
+- [setOptions](#setoptionsoptions)
+- [setTraceFunction](#settracefunctionfunc)
+- [mquery.setGlobalTraceFunction](#mquerysetglobaltracefunctionfunc)
+- [mquery.canMerge](#mquerycanmerge)
+- [mquery.use$geoWithin](#mqueryusegeowithin)
+
+### find()
+
+Declares this query a _find_ query. Optionally pass a match clause and / or callback. If a callback is passed the query is executed.
+
+```js
+mquery().find()
+mquery().find(match)
+mquery().find(callback)
+mquery().find(match, function (err, docs) {
+ assert(Array.isArray(docs));
+})
+```
+
+### findOne()
+
+Declares this query a _findOne_ query. Optionally pass a match clause and / or callback. If a callback is passed the query is executed.
+
+```js
+mquery().findOne()
+mquery().findOne(match)
+mquery().findOne(callback)
+mquery().findOne(match, function (err, doc) {
+ if (doc) {
+ // the document may not be found
+ console.log(doc);
+ }
+})
+```
+
+### count()
+
+Declares this query a _count_ query. Optionally pass a match clause and / or callback. If a callback is passed the query is executed.
+
+```js
+mquery().count()
+mquery().count(match)
+mquery().count(callback)
+mquery().count(match, function (err, number){
+ console.log('we found %d matching documents', number);
+})
+```
+
+### remove()
+
+Declares this query a _remove_ query. Optionally pass a match clause and / or callback. If a callback is passed the query is executed.
+
+```js
+mquery().remove()
+mquery().remove(match)
+mquery().remove(callback)
+mquery().remove(match, function (err){})
+```
+
+### update()
+
+Declares this query an _update_ query. Optionally pass an update document, match clause, options or callback. If a callback is passed, the query is executed. To force execution without passing a callback, run `update(true)`.
+
+```js
+mquery().update()
+mquery().update(match, updateDocument)
+mquery().update(match, updateDocument, options)
+
+// the following all execute the command
+mquery().update(callback)
+mquery().update({$set: updateDocument, callback)
+mquery().update(match, updateDocument, callback)
+mquery().update(match, updateDocument, options, function (err, result){})
+mquery().update(true) // executes (unsafe write)
+```
+
+##### the update document
+
+All paths passed that are not `$atomic` operations will become `$set` ops. For example:
+
+```js
+mquery(collection).where({ _id: id }).update({ title: 'words' }, callback)
+```
+
+becomes
+
+```js
+collection.update({ _id: id }, { $set: { title: 'words' }}, callback)
+```
+
+This behavior can be overridden using the `overwrite` option (see below).
+
+##### options
+
+Options are passed to the `setOptions()` method.
+
+- overwrite
+
+Passing an empty object `{ }` as the update document will result in a no-op unless the `overwrite` option is passed. Without the `overwrite` option, the update operation will be ignored and the callback executed without sending the command to MongoDB to prevent accidently overwritting documents in the collection.
+
+```js
+var q = mquery(collection).where({ _id: id }).setOptions({ overwrite: true });
+q.update({ }, callback); // overwrite with an empty doc
+```
+
+The `overwrite` option isn't just for empty objects, it also provides a means to override the default `$set` conversion and send the update document as is.
+
+```js
+// create a base query
+var base = mquery({ _id: 108 }).collection(collection).toConstructor();
+
+base().findOne(function (err, doc) {
+ console.log(doc); // { _id: 108, name: 'cajon' })
+
+ base().setOptions({ overwrite: true }).update({ changed: true }, function (err) {
+ base.findOne(function (err, doc) {
+ console.log(doc); // { _id: 108, changed: true }) - the doc was overwritten
+ });
+ });
+})
+```
+
+- multi
+
+Updates only modify a single document by default. To update multiple documents, set the `multi` option to `true`.
+
+```js
+mquery()
+ .collection(coll)
+ .update({ name: /^match/ }, { $addToSet: { arr: 4 }}, { multi: true }, callback)
+
+// another way of doing it
+mquery({ name: /^match/ })
+ .collection(coll)
+ .setOptions({ multi: true })
+ .update({ $addToSet: { arr: 4 }}, callback)
+
+// update multiple documents with an empty doc
+var q = mquery(collection).where({ name: /^match/ });
+q.setOptions({ multi: true, overwrite: true })
+q.update({ });
+q.update(function (err, result) {
+ console.log(arguments);
+});
+```
+
+### findOneAndUpdate()
+
+Declares this query a _findAndModify_ with update query. Optionally pass a match clause, update document, options, or callback. If a callback is passed, the query is executed.
+
+When executed, the first matching document (if found) is modified according to the update document and passed back to the callback.
+
+##### options
+
+Options are passed to the `setOptions()` method.
+
+- `new`: boolean - true to return the modified document rather than the original. defaults to true
+- `upsert`: boolean - creates the object if it doesn't exist. defaults to false
+- `sort`: if multiple docs are found by the match condition, sets the sort order to choose which doc to update
+
+```js
+query.findOneAndUpdate()
+query.findOneAndUpdate(updateDocument)
+query.findOneAndUpdate(match, updateDocument)
+query.findOneAndUpdate(match, updateDocument, options)
+
+// the following all execute the command
+query.findOneAndUpdate(callback)
+query.findOneAndUpdate(updateDocument, callback)
+query.findOneAndUpdate(match, updateDocument, callback)
+query.findOneAndUpdate(match, updateDocument, options, function (err, doc) {
+ if (doc) {
+ // the document may not be found
+ console.log(doc);
+ }
+})
+ ```
+
+### findOneAndRemove()
+
+Declares this query a _findAndModify_ with remove query. Alias of findOneAndDelete.
+Optionally pass a match clause, options, or callback. If a callback is passed, the query is executed.
+
+When executed, the first matching document (if found) is modified according to the update document, removed from the collection and passed to the callback.
+
+##### options
+
+Options are passed to the `setOptions()` method.
+
+- `sort`: if multiple docs are found by the condition, sets the sort order to choose which doc to modify and remove
+
+```js
+A.where().findOneAndDelete()
+A.where().findOneAndRemove()
+A.where().findOneAndRemove(match)
+A.where().findOneAndRemove(match, options)
+
+// the following all execute the command
+A.where().findOneAndRemove(callback)
+A.where().findOneAndRemove(match, callback)
+A.where().findOneAndRemove(match, options, function (err, doc) {
+ if (doc) {
+ // the document may not be found
+ console.log(doc);
+ }
+})
+ ```
+
+### distinct()
+
+Declares this query a _distinct_ query. Optionally pass the distinct field, a match clause or callback. If a callback is passed the query is executed.
+
+```js
+mquery().distinct()
+mquery().distinct(match)
+mquery().distinct(match, field)
+mquery().distinct(field)
+
+// the following all execute the command
+mquery().distinct(callback)
+mquery().distinct(field, callback)
+mquery().distinct(match, callback)
+mquery().distinct(match, field, function (err, result) {
+ console.log(result);
+})
+```
+
+### exec()
+
+Executes the query.
+
+```js
+mquery().findOne().where('route').intersects(polygon).exec(function (err, docs){})
+```
+
+### stream()
+
+Executes the query and returns a stream.
+
+```js
+var stream = mquery().find().stream(options);
+stream.on('data', cb);
+stream.on('close', fn);
+```
+
+Note: this only works with `find()` operations.
+
+Note: returns the stream object directly from the node-mongodb-native driver. (currently streams1 type stream). Any options will be passed along to the [driver method](http://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#stream).
+
+-------------
+
+### all()
+
+Specifies an `$all` query condition
+
+```js
+mquery().where('permission').all(['read', 'write'])
+```
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/all/)
+
+### and()
+
+Specifies arguments for an `$and` condition
+
+```js
+mquery().and([{ color: 'green' }, { status: 'ok' }])
+```
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/and/)
+
+### box()
+
+Specifies a `$box` condition
+
+```js
+var lowerLeft = [40.73083, -73.99756]
+var upperRight= [40.741404, -73.988135]
+
+mquery().where('location').within().box(lowerLeft, upperRight)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/box/)
+
+### circle()
+
+Specifies a `$center` or `$centerSphere` condition.
+
+```js
+var area = { center: [50, 50], radius: 10, unique: true }
+query.where('loc').within().circle(area)
+query.circle('loc', area);
+
+// for spherical calculations
+var area = { center: [50, 50], radius: 10, unique: true, spherical: true }
+query.where('loc').within().circle(area)
+query.circle('loc', area);
+```
+
+- [MongoDB Documentation - center](http://docs.mongodb.org/manual/reference/operator/center/)
+- [MongoDB Documentation - centerSphere](http://docs.mongodb.org/manual/reference/operator/centerSphere/)
+
+### elemMatch()
+
+Specifies an `$elemMatch` condition
+
+```js
+query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})
+
+query.elemMatch('comment', function (elem) {
+ elem.where('author').equals('autobot');
+ elem.where('votes').gte(5);
+})
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/elemMatch/)
+
+### equals()
+
+Specifies the complementary comparison value for the path specified with `where()`.
+
+```js
+mquery().where('age').equals(49);
+
+// is the same as
+
+mquery().where({ 'age': 49 });
+```
+
+### exists()
+
+Specifies an `$exists` condition
+
+```js
+// { name: { $exists: true }}
+mquery().where('name').exists()
+mquery().where('name').exists(true)
+mquery().exists('name')
+
+// { name: { $exists: false }}
+mquery().where('name').exists(false);
+mquery().exists('name', false);
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/exists/)
+
+### geometry()
+
+Specifies a `$geometry` condition
+
+```js
+var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
+query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })
+
+// or
+var polyB = [[ 0, 0 ], [ 1, 1 ]]
+query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })
+
+// or
+var polyC = [ 0, 0 ]
+query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })
+
+// or
+query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })
+
+// or
+query.where('loc').near().geometry({ type: 'Point', coordinates: [3,5] })
+```
+
+`geometry()` **must** come after `intersects()`, `within()`, or `near()`.
+
+The `object` argument must contain `type` and `coordinates` properties.
+
+- type `String`
+- coordinates `Array`
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/geometry/)
+
+### gt()
+
+Specifies a `$gt` query condition.
+
+```js
+mquery().where('clicks').gt(999)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/gt/)
+
+### gte()
+
+Specifies a `$gte` query condition.
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/gte/)
+
+```js
+mquery().where('clicks').gte(1000)
+```
+
+### in()
+
+Specifies an `$in` query condition.
+
+```js
+mquery().where('author_id').in([3, 48901, 761])
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/in/)
+
+### intersects()
+
+Declares an `$geoIntersects` query for `geometry()`.
+
+```js
+query.where('path').intersects().geometry({
+ type: 'LineString'
+ , coordinates: [[180.0, 11.0], [180, 9.0]]
+})
+
+// geometry arguments are supported
+query.where('path').intersects({
+ type: 'LineString'
+ , coordinates: [[180.0, 11.0], [180, 9.0]]
+})
+```
+
+**Must** be used after `where()`.
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/geoIntersects/)
+
+### lt()
+
+Specifies a `$lt` query condition.
+
+```js
+mquery().where('clicks').lt(50)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/lt/)
+
+### lte()
+
+Specifies a `$lte` query condition.
+
+```js
+mquery().where('clicks').lte(49)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/lte/)
+
+### maxDistance()
+
+Specifies a `$maxDistance` query condition.
+
+```js
+mquery().where('location').near({ center: [139, 74.3] }).maxDistance(5)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/maxDistance/)
+
+### mod()
+
+Specifies a `$mod` condition
+
+```js
+mquery().where('count').mod(2, 0)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/mod/)
+
+### ne()
+
+Specifies a `$ne` query condition.
+
+```js
+mquery().where('status').ne('ok')
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/ne/)
+
+### nin()
+
+Specifies an `$nin` query condition.
+
+```js
+mquery().where('author_id').nin([3, 48901, 761])
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/nin/)
+
+### nor()
+
+Specifies arguments for an `$nor` condition.
+
+```js
+mquery().nor([{ color: 'green' }, { status: 'ok' }])
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/nor/)
+
+### near()
+
+Specifies arguments for a `$near` or `$nearSphere` condition.
+
+These operators return documents sorted by distance.
+
+#### Example
+
+```js
+query.where('loc').near({ center: [10, 10] });
+query.where('loc').near({ center: [10, 10], maxDistance: 5 });
+query.near('loc', { center: [10, 10], maxDistance: 5 });
+
+// GeoJSON
+query.where('loc').near({ center: { type: 'Point', coordinates: [10, 10] }});
+query.where('loc').near({ center: { type: 'Point', coordinates: [10, 10] }, maxDistance: 5, spherical: true });
+query.where('loc').near().geometry({ type: 'Point', coordinates: [10, 10] });
+
+// For a $nearSphere condition, pass the `spherical` option.
+query.near({ center: [10, 10], maxDistance: 5, spherical: true });
+```
+
+[MongoDB Documentation](http://www.mongodb.org/display/DOCS/Geospatial+Indexing)
+
+### or()
+
+Specifies arguments for an `$or` condition.
+
+```js
+mquery().or([{ color: 'red' }, { status: 'emergency' }])
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/or/)
+
+### polygon()
+
+Specifies a `$polygon` condition
+
+```js
+mquery().where('loc').within().polygon([10,20], [13, 25], [7,15])
+mquery().polygon('loc', [10,20], [13, 25], [7,15])
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/polygon/)
+
+### regex()
+
+Specifies a `$regex` query condition.
+
+```js
+mquery().where('name').regex(/^sixstepsrecords/)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/regex/)
+
+### select()
+
+Specifies which document fields to include or exclude
+
+```js
+// 1 means include, 0 means exclude
+mquery().select({ name: 1, address: 1, _id: 0 })
+
+// or
+
+mquery().select('name address -_id')
+```
+
+##### String syntax
+
+When passing a string, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included.
+
+```js
+// include a and b, exclude c
+query.select('a b -c');
+
+// or you may use object notation, useful when
+// you have keys already prefixed with a "-"
+query.select({a: 1, b: 1, c: 0});
+```
+
+_Cannot be used with `distinct()`._
+
+### selected()
+
+Determines if the query has selected any fields.
+
+```js
+var query = mquery();
+query.selected() // false
+query.select('-name');
+query.selected() // true
+```
+
+### selectedInclusively()
+
+Determines if the query has selected any fields inclusively.
+
+```js
+var query = mquery().select('name');
+query.selectedInclusively() // true
+
+var query = mquery();
+query.selected() // false
+query.select('-name');
+query.selectedInclusively() // false
+query.selectedExclusively() // true
+```
+
+### selectedExclusively()
+
+Determines if the query has selected any fields exclusively.
+
+```js
+var query = mquery().select('-name');
+query.selectedExclusively() // true
+
+var query = mquery();
+query.selected() // false
+query.select('name');
+query.selectedExclusively() // false
+query.selectedInclusively() // true
+```
+
+### size()
+
+Specifies a `$size` query condition.
+
+```js
+mquery().where('someArray').size(6)
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/size/)
+
+### slice()
+
+Specifies a `$slice` projection for a `path`
+
+```js
+mquery().where('comments').slice(5)
+mquery().where('comments').slice(-5)
+mquery().where('comments').slice([-10, 5])
+```
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/projection/slice/)
+
+### within()
+
+Sets a `$geoWithin` or `$within` argument for geo-spatial queries.
+
+```js
+mquery().within().box()
+mquery().within().circle()
+mquery().within().geometry()
+
+mquery().where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
+mquery().where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
+mquery().where('loc').within({ polygon: [[],[],[],[]] });
+
+mquery().where('loc').within([], [], []) // polygon
+mquery().where('loc').within([], []) // box
+mquery().where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
+```
+
+As of mquery 2.0, `$geoWithin` is used by default. This impacts you if running MongoDB < 2.4. To alter this behavior, see [mquery.use$geoWithin](#mqueryusegeowithin).
+
+**Must** be used after `where()`.
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/geoWithin/)
+
+### where()
+
+Specifies a `path` for use with chaining
+
+```js
+// instead of writing:
+mquery().find({age: {$gte: 21, $lte: 65}});
+
+// we can instead write:
+mquery().where('age').gte(21).lte(65);
+
+// passing query conditions is permitted too
+mquery().find().where({ name: 'vonderful' })
+
+// chaining
+mquery()
+.where('age').gte(21).lte(65)
+.where({ 'name': /^vonderful/i })
+.where('friends').slice(10)
+.exec(callback)
+```
+
+### $where()
+
+Specifies a `$where` condition.
+
+Use `$where` when you need to select documents using a JavaScript expression.
+
+```js
+query.$where('this.comments.length > 10 || this.name.length > 5').exec(callback)
+
+query.$where(function () {
+ return this.comments.length > 10 || this.name.length > 5;
+})
+```
+
+Only use `$where` when you have a condition that cannot be met using other MongoDB operators like `$lt`. Be sure to read about all of [its caveats](http://docs.mongodb.org/manual/reference/operator/where/) before using.
+
+-----------
+
+### batchSize()
+
+Specifies the batchSize option.
+
+```js
+query.batchSize(100)
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.batchSize/)
+
+### collation()
+
+Specifies the collation option.
+
+```js
+query.collation({ locale: "en_US", strength: 1 })
+```
+
+[MongoDB documentation](https://docs.mongodb.com/manual/reference/method/cursor.collation/#cursor.collation)
+
+### comment()
+
+Specifies the comment option.
+
+```js
+query.comment('login query');
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/)
+
+### hint()
+
+Sets query hints.
+
+```js
+mquery().hint({ indexA: 1, indexB: -1 })
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/hint/)
+
+### j()
+
+Requests acknowledgement that this operation has been persisted to MongoDB's on-disk journal.
+
+This option is only valid for operations that write to the database:
+
+- `deleteOne()`
+- `deleteMany()`
+- `findOneAndDelete()`
+- `findOneAndUpdate()`
+- `remove()`
+- `update()`
+- `updateOne()`
+- `updateMany()`
+
+Defaults to the `j` value if it is specified in [writeConcern](#writeconcern)
+
+```js
+mquery().j(true);
+```
+
+### limit()
+
+Specifies the limit option.
+
+```js
+query.limit(20)
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.limit/)
+
+### maxScan()
+
+Specifies the maxScan option.
+
+```js
+query.maxScan(100)
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/maxScan/)
+
+### maxTime()
+
+Specifies the maxTimeMS option.
+
+```js
+query.maxTime(100)
+query.maxTimeMS(100)
+```
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.maxTimeMS/)
+
+
+### skip()
+
+Specifies the skip option.
+
+```js
+query.skip(100).limit(20)
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.skip/)
+
+### sort()
+
+Sets the query sort order.
+
+If an object is passed, key values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
+
+If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending.
+
+```js
+// these are equivalent
+query.sort({ field: 'asc', test: -1 });
+query.sort('field -test');
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.sort/)
+
+### read()
+
+Sets the readPreference option for the query.
+
+```js
+mquery().read('primary')
+mquery().read('p') // same as primary
+
+mquery().read('primaryPreferred')
+mquery().read('pp') // same as primaryPreferred
+
+mquery().read('secondary')
+mquery().read('s') // same as secondary
+
+mquery().read('secondaryPreferred')
+mquery().read('sp') // same as secondaryPreferred
+
+mquery().read('nearest')
+mquery().read('n') // same as nearest
+
+mquery().setReadPreference('primary') // alias of .read()
+```
+
+##### Preferences:
+
+- `primary` - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags.
+- `secondary` - Read from secondary if available, otherwise error.
+- `primaryPreferred` - Read from primary if available, otherwise a secondary.
+- `secondaryPreferred` - Read from a secondary if available, otherwise read from the primary.
+- `nearest` - All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection.
+
+Aliases
+
+- `p` primary
+- `pp` primaryPreferred
+- `s` secondary
+- `sp` secondaryPreferred
+- `n` nearest
+
+##### Preference Tags:
+
+To keep the separation of concerns between `mquery` and your driver
+clean, `mquery#read()` no longer handles specifying a second `tags` argument as of version 0.5.
+If you need to specify tags, pass any non-string argument as the first argument.
+`mquery` will pass this argument untouched to your collections methods later.
+For example:
+
+```js
+// example of specifying tags using the Node.js driver
+var ReadPref = require('mongodb').ReadPreference;
+var preference = new ReadPref('secondary', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }]);
+mquery(..).read(preference).exec();
+```
+
+Read more about how to use read preferences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences).
+
+
+### readConcern()
+
+Sets the readConcern option for the query.
+
+```js
+// local
+mquery().readConcern('local')
+mquery().readConcern('l')
+mquery().r('l')
+
+// available
+mquery().readConcern('available')
+mquery().readConcern('a')
+mquery().r('a')
+
+// majority
+mquery().readConcern('majority')
+mquery().readConcern('m')
+mquery().r('m')
+
+// linearizable
+mquery().readConcern('linearizable')
+mquery().readConcern('lz')
+mquery().r('lz')
+
+// snapshot
+mquery().readConcern('snapshot')
+mquery().readConcern('s')
+mquery().r('s')
+```
+
+##### Read Concern Level:
+
+- `local` - The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). (MongoDB 3.2+)
+- `available` - The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). (MongoDB 3.6+)
+- `majority` - The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure. (MongoDB 3.2+)
+- `linearizable` - The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results. (MongoDB 3.4+)
+- `snapshot` - Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data. (MongoDB 4.0+)
+
+Aliases
+
+- `l` local
+- `a` available
+- `m` majority
+- `lz` linearizable
+- `s` snapshot
+
+Read more about how to use read concern [here](https://docs.mongodb.com/manual/reference/read-concern/).
+
+### writeConcern()
+
+Sets the writeConcern option for the query.
+
+This option is only valid for operations that write to the database:
+
+- `deleteOne()`
+- `deleteMany()`
+- `findOneAndDelete()`
+- `findOneAndUpdate()`
+- `remove()`
+- `update()`
+- `updateOne()`
+- `updateMany()`
+
+```js
+mquery().writeConcern(0)
+mquery().writeConcern(1)
+mquery().writeConcern({ w: 1, j: true, wtimeout: 2000 })
+mquery().writeConcern('majority')
+mquery().writeConcern('m') // same as majority
+mquery().writeConcern('tagSetName') // if the tag set is 'm', use .writeConcern({ w: 'm' }) instead
+mquery().w(1) // w is alias of writeConcern
+```
+
+##### Write Concern:
+
+writeConcern({ w: ``, j: ``, wtimeout: `` }`)
+
+- the w option to request acknowledgement that the write operation has propagated to a specified number of mongod instances or to mongod instances with specified tags
+- the j option to request acknowledgement that the write operation has been written to the journal
+- the wtimeout option to specify a time limit to prevent write operations from blocking indefinitely
+
+Can be break down to use the following syntax:
+
+mquery().w(``).j(``).wtimeout(``)
+
+Read more about how to use write concern [here](https://docs.mongodb.com/manual/reference/write-concern/)
+
+### slaveOk()
+
+Sets the slaveOk option. `true` allows reading from secondaries.
+
+**deprecated** use [read()](#read) preferences instead if on mongodb >= 2.2
+
+```js
+query.slaveOk() // true
+query.slaveOk(true)
+query.slaveOk(false)
+```
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/rs.slaveOk/)
+
+### snapshot()
+
+Specifies this query as a snapshot query.
+
+```js
+mquery().snapshot() // true
+mquery().snapshot(true)
+mquery().snapshot(false)
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/snapshot/)
+
+### tailable()
+
+Sets tailable option.
+
+```js
+mquery().tailable() <== true
+mquery().tailable(true)
+mquery().tailable(false)
+```
+
+_Cannot be used with `distinct()`._
+
+[MongoDB Documentation](http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/)
+
+### wtimeout()
+
+Specifies a time limit, in milliseconds, for the write concern. If `w > 1`, it is maximum amount of time to
+wait for this write to propagate through the replica set before this operation fails. The default is `0`, which means no timeout.
+
+This option is only valid for operations that write to the database:
+
+- `deleteOne()`
+- `deleteMany()`
+- `findOneAndDelete()`
+- `findOneAndUpdate()`
+- `remove()`
+- `update()`
+- `updateOne()`
+- `updateMany()`
+
+Defaults to `wtimeout` value if it is specified in [writeConcern](#writeconcern)
+
+```js
+mquery().wtimeout(2000)
+mquery().wTimeout(2000)
+```
+
+## Helpers
+
+### collection()
+
+Sets the querys collection.
+
+```js
+mquery().collection(aCollection)
+```
+
+### then()
+
+Executes the query and returns a promise which will be resolved with the query results or rejected if the query responds with an error.
+
+```js
+mquery().find(..).then(success, error);
+```
+
+This is very useful when combined with [co](https://github.com/visionmedia/co) or [koa](https://github.com/koajs/koa), which automatically resolve promise-like objects for you.
+
+```js
+co(function*(){
+ var doc = yield mquery().findOne({ _id: 499 });
+ console.log(doc); // { _id: 499, name: 'amazing', .. }
+})();
+```
+
+_NOTE_:
+The returned promise is a [bluebird](https://github.com/petkaantonov/bluebird/) promise but this is customizable. If you want to
+use your favorite promise library, simply set `mquery.Promise = YourPromiseConstructor`.
+Your `Promise` must be [promises A+](http://promisesaplus.com/) compliant.
+
+### thunk()
+
+Returns a thunk which when called runs the query's `exec` method passing the results to the callback.
+
+```js
+var thunk = mquery(collection).find({..}).thunk();
+
+thunk(function(err, results) {
+
+})
+```
+
+### merge(object)
+
+Merges other mquery or match condition objects into this one. When an mquery instance is passed, its match conditions, field selection and options are merged.
+
+```js
+var drum = mquery({ type: 'drum' }).collection(instruments);
+var redDrum = mquery({ color: 'red' }).merge(drum);
+redDrum.count(function (err, n) {
+ console.log('there are %d red drums', n);
+})
+```
+
+Internally uses `mquery.canMerge` to determine validity.
+
+### setOptions(options)
+
+Sets query options.
+
+```js
+mquery().setOptions({ collection: coll, limit: 20 })
+```
+
+##### options
+
+- [tailable](#tailable) *
+- [sort](#sort) *
+- [limit](#limit) *
+- [skip](#skip) *
+- [maxScan](#maxscan) *
+- [maxTime](#maxtime) *
+- [batchSize](#batchSize) *
+- [comment](#comment) *
+- [snapshot](#snapshot) *
+- [hint](#hint) *
+- [slaveOk](#slaveOk) *
+- [safe](http://docs.mongodb.org/manual/reference/write-concern/): Boolean - passed through to the collection. Setting to `true` is equivalent to `{ w: 1 }`
+- [collection](#collection): the collection to query against
+
+_* denotes a query helper method is also available_
+
+### setTraceFunction(func)
+
+Set a function to trace this query. Useful for profiling or logging.
+
+```js
+function traceFunction (method, queryInfo, query) {
+ console.log('starting ' + method + ' query');
+
+ return function (err, result, millis) {
+ console.log('finished ' + method + ' query in ' + millis + 'ms');
+ };
+}
+
+mquery().setTraceFunction(traceFunction).findOne({name: 'Joe'}, cb);
+```
+
+The trace function is passed (method, queryInfo, query)
+
+- method is the name of the method being called (e.g. findOne)
+- queryInfo contains information about the query:
+ - conditions: query conditions/criteria
+ - options: options such as sort, fields, etc
+ - doc: document being updated
+- query is the query object
+
+The trace function should return a callback function which accepts:
+- err: error, if any
+- result: result, if any
+- millis: time spent waiting for query result
+
+NOTE: stream requests are not traced.
+
+### mquery.setGlobalTraceFunction(func)
+
+Similar to `setTraceFunction()` but automatically applied to all queries.
+
+```js
+mquery.setTraceFunction(traceFunction);
+```
+
+### mquery.canMerge(conditions)
+
+Determines if `conditions` can be merged using `mquery().merge()`.
+
+```js
+var query = mquery({ type: 'drum' });
+var okToMerge = mquery.canMerge(anObject)
+if (okToMerge) {
+ query.merge(anObject);
+}
+```
+
+## mquery.use$geoWithin
+
+MongoDB 2.4 introduced the `$geoWithin` operator which replaces and is 100% backward compatible with `$within`. As of mquery 0.2, we default to using `$geoWithin` for all `within()` calls.
+
+If you are running MongoDB < 2.4 this will be problematic. To force `mquery` to be backward compatible and always use `$within`, set the `mquery.use$geoWithin` flag to `false`.
+
+```js
+mquery.use$geoWithin = false;
+```
+
+## Custom Base Queries
+
+Often times we want custom base queries that encapsulate predefined criteria. With `mquery` this is easy. First create the query you want to reuse and call its `toConstructor()` method which returns a new subclass of `mquery` that retains all options and criteria of the original.
+
+```js
+var greatMovies = mquery(movieCollection).where('rating').gte(4.5).toConstructor();
+
+// use it!
+greatMovies().count(function (err, n) {
+ console.log('There are %d great movies', n);
+});
+
+greatMovies().where({ name: /^Life/ }).select('name').find(function (err, docs) {
+ console.log(docs);
+});
+```
+
+## Validation
+
+Method and options combinations are checked for validity at runtime to prevent creation of invalid query constructs. For example, a `distinct` query does not support specifying options like `hint` or field selection. In this case an error will be thrown so you can catch these mistakes in development.
+
+## Debug support
+
+Debug mode is provided through the use of the [debug](https://github.com/visionmedia/debug) module. To enable:
+
+ DEBUG=mquery node yourprogram.js
+
+Read the debug module documentation for more details.
+
+## General compatibility
+
+#### ObjectIds
+
+`mquery` clones query arguments before passing them to a `collection` method for execution.
+This prevents accidental side-affects to the objects you pass.
+To clone `ObjectIds` we need to make some assumptions.
+
+First, to check if an object is an `ObjectId`, we check its constructors name. If it matches either
+`ObjectId` or `ObjectID` we clone it.
+
+To clone `ObjectIds`, we call its optional `clone` method. If a `clone` method does not exist, we fall
+back to calling `new obj.constructor(obj.id)`. We assume, for compatibility with the
+Node.js driver, that the `ObjectId` instance has a public `id` property and that
+when creating an `ObjectId` instance we can pass that `id` as an argument.
+
+#### Read Preferences
+
+`mquery` supports specifying [Read Preferences]() to control from which MongoDB node your query will read.
+The Read Preferences spec also support specifying tags. To pass tags, some
+drivers (Node.js driver) require passing a special constructor that handles both the read preference and its tags.
+If you need to specify tags, pass an instance of your drivers ReadPreference constructor or roll your own. `mquery` will store whatever you provide and pass later to your collection during execution.
+
+## Future goals
+
+ - mongo shell compatibility
+ - browser compatibility
+
+## Installation
+
+ $ npm install mquery
+
+## License
+
+[MIT](https://github.com/aheckmann/mquery/blob/master/LICENSE)
+
diff --git a/node_modules/mquery/lib/collection/collection.js b/node_modules/mquery/lib/collection/collection.js
new file mode 100644
index 0000000..0f39c76
--- /dev/null
+++ b/node_modules/mquery/lib/collection/collection.js
@@ -0,0 +1,46 @@
+'use strict';
+
+/**
+ * methods a collection must implement
+ */
+
+var methods = [
+ 'find',
+ 'findOne',
+ 'update',
+ 'updateMany',
+ 'updateOne',
+ 'replaceOne',
+ 'remove',
+ 'count',
+ 'distinct',
+ 'findAndModify',
+ 'aggregate',
+ 'findStream',
+ 'deleteOne',
+ 'deleteMany'
+];
+
+/**
+ * Collection base class from which implementations inherit
+ */
+
+function Collection() {}
+
+for (var i = 0, len = methods.length; i < len; ++i) {
+ var method = methods[i];
+ Collection.prototype[method] = notImplemented(method);
+}
+
+module.exports = exports = Collection;
+Collection.methods = methods;
+
+/**
+ * creates a function which throws an implementation error
+ */
+
+function notImplemented(method) {
+ return function() {
+ throw new Error('collection.' + method + ' not implemented');
+ };
+}
diff --git a/node_modules/mquery/lib/collection/index.js b/node_modules/mquery/lib/collection/index.js
new file mode 100644
index 0000000..1992e20
--- /dev/null
+++ b/node_modules/mquery/lib/collection/index.js
@@ -0,0 +1,13 @@
+'use strict';
+
+var env = require('../env');
+
+if ('unknown' == env.type) {
+ throw new Error('Unknown environment');
+}
+
+module.exports =
+ env.isNode ? require('./node') :
+ env.isMongo ? require('./collection') :
+ require('./collection');
+
diff --git a/node_modules/mquery/lib/collection/node.js b/node_modules/mquery/lib/collection/node.js
new file mode 100644
index 0000000..cc07d60
--- /dev/null
+++ b/node_modules/mquery/lib/collection/node.js
@@ -0,0 +1,151 @@
+'use strict';
+
+/**
+ * Module dependencies
+ */
+
+var Collection = require('./collection');
+var utils = require('../utils');
+
+function NodeCollection(col) {
+ this.collection = col;
+ this.collectionName = col.collectionName;
+}
+
+/**
+ * inherit from collection base class
+ */
+
+utils.inherits(NodeCollection, Collection);
+
+/**
+ * find(match, options, function(err, docs))
+ */
+
+NodeCollection.prototype.find = function(match, options, cb) {
+ this.collection.find(match, options, function(err, cursor) {
+ if (err) return cb(err);
+
+ try {
+ cursor.toArray(cb);
+ } catch (error) {
+ cb(error);
+ }
+ });
+};
+
+/**
+ * findOne(match, options, function(err, doc))
+ */
+
+NodeCollection.prototype.findOne = function(match, options, cb) {
+ this.collection.findOne(match, options, cb);
+};
+
+/**
+ * count(match, options, function(err, count))
+ */
+
+NodeCollection.prototype.count = function(match, options, cb) {
+ this.collection.count(match, options, cb);
+};
+
+/**
+ * distinct(prop, match, options, function(err, count))
+ */
+
+NodeCollection.prototype.distinct = function(prop, match, options, cb) {
+ this.collection.distinct(prop, match, options, cb);
+};
+
+/**
+ * update(match, update, options, function(err[, result]))
+ */
+
+NodeCollection.prototype.update = function(match, update, options, cb) {
+ this.collection.update(match, update, options, cb);
+};
+
+/**
+ * update(match, update, options, function(err[, result]))
+ */
+
+NodeCollection.prototype.updateMany = function(match, update, options, cb) {
+ this.collection.updateMany(match, update, options, cb);
+};
+
+/**
+ * update(match, update, options, function(err[, result]))
+ */
+
+NodeCollection.prototype.updateOne = function(match, update, options, cb) {
+ this.collection.updateOne(match, update, options, cb);
+};
+
+/**
+ * replaceOne(match, update, options, function(err[, result]))
+ */
+
+NodeCollection.prototype.replaceOne = function(match, update, options, cb) {
+ this.collection.replaceOne(match, update, options, cb);
+};
+
+/**
+ * deleteOne(match, options, function(err[, result])
+ */
+
+NodeCollection.prototype.deleteOne = function(match, options, cb) {
+ this.collection.deleteOne(match, options, cb);
+};
+
+/**
+ * deleteMany(match, options, function(err[, result])
+ */
+
+NodeCollection.prototype.deleteMany = function(match, options, cb) {
+ this.collection.deleteMany(match, options, cb);
+};
+
+/**
+ * remove(match, options, function(err[, result])
+ */
+
+NodeCollection.prototype.remove = function(match, options, cb) {
+ this.collection.remove(match, options, cb);
+};
+
+/**
+ * findAndModify(match, update, options, function(err, doc))
+ */
+
+NodeCollection.prototype.findAndModify = function(match, update, options, cb) {
+ var sort = Array.isArray(options.sort) ? options.sort : [];
+ this.collection.findAndModify(match, sort, update, options, cb);
+};
+
+/**
+ * var stream = findStream(match, findOptions, streamOptions)
+ */
+
+NodeCollection.prototype.findStream = function(match, findOptions, streamOptions) {
+ return this.collection.find(match, findOptions).stream(streamOptions);
+};
+
+/**
+ * var cursor = findCursor(match, findOptions)
+ */
+
+NodeCollection.prototype.findCursor = function(match, findOptions) {
+ return this.collection.find(match, findOptions);
+};
+
+/**
+ * aggregation(operators..., function(err, doc))
+ * TODO
+ */
+
+/**
+ * Expose
+ */
+
+module.exports = exports = NodeCollection;
diff --git a/node_modules/mquery/lib/env.js b/node_modules/mquery/lib/env.js
new file mode 100644
index 0000000..d3d225b
--- /dev/null
+++ b/node_modules/mquery/lib/env.js
@@ -0,0 +1,22 @@
+'use strict';
+
+exports.isNode = 'undefined' != typeof process
+ && 'object' == typeof module
+ && 'object' == typeof global
+ && 'function' == typeof Buffer
+ && process.argv;
+
+exports.isMongo = !exports.isNode
+ && 'function' == typeof printjson
+ && 'function' == typeof ObjectId
+ && 'function' == typeof rs
+ && 'function' == typeof sh;
+
+exports.isBrowser = !exports.isNode
+ && !exports.isMongo
+ && 'undefined' != typeof window;
+
+exports.type = exports.isNode ? 'node'
+ : exports.isMongo ? 'mongo'
+ : exports.isBrowser ? 'browser'
+ : 'unknown';
diff --git a/node_modules/mquery/lib/mquery.js b/node_modules/mquery/lib/mquery.js
new file mode 100644
index 0000000..9d82cff
--- /dev/null
+++ b/node_modules/mquery/lib/mquery.js
@@ -0,0 +1,3253 @@
+'use strict';
+
+/**
+ * Dependencies
+ */
+
+var slice = require('sliced');
+var assert = require('assert');
+var util = require('util');
+var utils = require('./utils');
+var debug = require('debug')('mquery');
+
+/* global Map */
+
+/**
+ * Query constructor used for building queries.
+ *
+ * ####Example:
+ *
+ * var query = new Query({ name: 'mquery' });
+ * query.setOptions({ collection: moduleCollection })
+ * query.where('age').gte(21).exec(callback);
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [options]
+ * @api public
+ */
+
+function Query(criteria, options) {
+ if (!(this instanceof Query))
+ return new Query(criteria, options);
+
+ var proto = this.constructor.prototype;
+
+ this.op = proto.op || undefined;
+
+ this.options = {};
+ this.setOptions(proto.options);
+
+ this._conditions = proto._conditions
+ ? utils.clone(proto._conditions)
+ : {};
+
+ this._fields = proto._fields
+ ? utils.clone(proto._fields)
+ : undefined;
+
+ this._update = proto._update
+ ? utils.clone(proto._update)
+ : undefined;
+
+ this._path = proto._path || undefined;
+ this._distinct = proto._distinct || undefined;
+ this._collection = proto._collection || undefined;
+ this._traceFunction = proto._traceFunction || undefined;
+
+ if (options) {
+ this.setOptions(options);
+ }
+
+ if (criteria) {
+ if (criteria.find && criteria.remove && criteria.update) {
+ // quack quack!
+ this.collection(criteria);
+ } else {
+ this.find(criteria);
+ }
+ }
+}
+
+/**
+ * This is a parameter that the user can set which determines if mquery
+ * uses $within or $geoWithin for queries. It defaults to true which
+ * means $geoWithin will be used. If using MongoDB < 2.4 you should
+ * set this to false.
+ *
+ * @api public
+ * @property use$geoWithin
+ */
+
+var $withinCmd = '$geoWithin';
+Object.defineProperty(Query, 'use$geoWithin', {
+ get: function( ) { return $withinCmd == '$geoWithin'; },
+ set: function(v) {
+ if (true === v) {
+ // mongodb >= 2.4
+ $withinCmd = '$geoWithin';
+ } else {
+ $withinCmd = '$within';
+ }
+ }
+});
+
+/**
+ * Converts this query to a constructor function with all arguments and options retained.
+ *
+ * ####Example
+ *
+ * // Create a query that will read documents with a "video" category from
+ * // `aCollection` on the primary node in the replica-set unless it is down,
+ * // in which case we'll read from a secondary node.
+ * var query = mquery({ category: 'video' })
+ * query.setOptions({ collection: aCollection, read: 'primaryPreferred' });
+ *
+ * // create a constructor based off these settings
+ * var Video = query.toConstructor();
+ *
+ * // Video is now a subclass of mquery() and works the same way but with the
+ * // default query parameters and options set.
+ *
+ * // run a query with the previous settings but filter for movies with names
+ * // that start with "Life".
+ * Video().where({ name: /^Life/ }).exec(cb);
+ *
+ * @return {Query} new Query
+ * @api public
+ */
+
+Query.prototype.toConstructor = function toConstructor() {
+ function CustomQuery(criteria, options) {
+ if (!(this instanceof CustomQuery))
+ return new CustomQuery(criteria, options);
+ Query.call(this, criteria, options);
+ }
+
+ utils.inherits(CustomQuery, Query);
+
+ // set inherited defaults
+ var p = CustomQuery.prototype;
+
+ p.options = {};
+ p.setOptions(this.options);
+
+ p.op = this.op;
+ p._conditions = utils.clone(this._conditions);
+ p._fields = utils.clone(this._fields);
+ p._update = utils.clone(this._update);
+ p._path = this._path;
+ p._distinct = this._distinct;
+ p._collection = this._collection;
+ p._traceFunction = this._traceFunction;
+
+ return CustomQuery;
+};
+
+/**
+ * Sets query options.
+ *
+ * ####Options:
+ *
+ * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors) *
+ * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D) *
+ * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D) *
+ * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D) *
+ * - [maxScan](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24maxScan) *
+ * - [maxTime](http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/#op._S_maxTimeMS) *
+ * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D) *
+ * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment) *
+ * - [snapshot](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D) *
+ * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint) *
+ * - [slaveOk](http://docs.mongodb.org/manual/applications/replication/#read-preference) *
+ * - [safe](http://www.mongodb.org/display/DOCS/getLastError+Command)
+ * - collection the collection to query against
+ *
+ * _* denotes a query helper method is also available_
+ *
+ * @param {Object} options
+ * @api public
+ */
+
+Query.prototype.setOptions = function(options) {
+ if (!(options && utils.isObject(options)))
+ return this;
+
+ // set arbitrary options
+ var methods = utils.keys(options),
+ method;
+
+ for (var i = 0; i < methods.length; ++i) {
+ method = methods[i];
+
+ // use methods if exist (safer option manipulation)
+ if ('function' == typeof this[method]) {
+ var args = utils.isArray(options[method])
+ ? options[method]
+ : [options[method]];
+ this[method].apply(this, args);
+ } else {
+ this.options[method] = options[method];
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Sets this Querys collection.
+ *
+ * @param {Collection} coll
+ * @return {Query} this
+ */
+
+Query.prototype.collection = function collection(coll) {
+ this._collection = new Query.Collection(coll);
+
+ return this;
+};
+
+/**
+ * Adds a collation to this op (MongoDB 3.4 and up)
+ *
+ * ####Example
+ *
+ * query.find().collation({ locale: "en_US", strength: 1 })
+ *
+ * @param {Object} value
+ * @return {Query} this
+ * @see MongoDB docs https://docs.mongodb.com/manual/reference/method/cursor.collation/#cursor.collation
+ * @api public
+ */
+
+Query.prototype.collation = function(value) {
+ this.options.collation = value;
+ return this;
+};
+
+/**
+ * Specifies a `$where` condition
+ *
+ * Use `$where` when you need to select documents using a JavaScript expression.
+ *
+ * ####Example
+ *
+ * query.$where('this.comments.length > 10 || this.name.length > 5')
+ *
+ * query.$where(function () {
+ * return this.comments.length > 10 || this.name.length > 5;
+ * })
+ *
+ * @param {String|Function} js javascript string or function
+ * @return {Query} this
+ * @memberOf Query
+ * @method $where
+ * @api public
+ */
+
+Query.prototype.$where = function(js) {
+ this._conditions.$where = js;
+ return this;
+};
+
+/**
+ * Specifies a `path` for use with chaining.
+ *
+ * ####Example
+ *
+ * // instead of writing:
+ * User.find({age: {$gte: 21, $lte: 65}}, callback);
+ *
+ * // we can instead write:
+ * User.where('age').gte(21).lte(65);
+ *
+ * // passing query conditions is permitted
+ * User.find().where({ name: 'vonderful' })
+ *
+ * // chaining
+ * User
+ * .where('age').gte(21).lte(65)
+ * .where('name', /^vonderful/i)
+ * .where('friends').slice(10)
+ * .exec(callback)
+ *
+ * @param {String} [path]
+ * @param {Object} [val]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.where = function() {
+ if (!arguments.length) return this;
+ if (!this.op) this.op = 'find';
+
+ var type = typeof arguments[0];
+
+ if ('string' == type) {
+ this._path = arguments[0];
+
+ if (2 === arguments.length) {
+ this._conditions[this._path] = arguments[1];
+ }
+
+ return this;
+ }
+
+ if ('object' == type && !Array.isArray(arguments[0])) {
+ return this.merge(arguments[0]);
+ }
+
+ throw new TypeError('path must be a string or object');
+};
+
+/**
+ * Specifies the complementary comparison value for paths specified with `where()`
+ *
+ * ####Example
+ *
+ * User.where('age').equals(49);
+ *
+ * // is the same as
+ *
+ * User.where('age', 49);
+ *
+ * @param {Object} val
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.equals = function equals(val) {
+ this._ensurePath('equals');
+ var path = this._path;
+ this._conditions[path] = val;
+ return this;
+};
+
+/**
+ * Specifies the complementary comparison value for paths specified with `where()`
+ * This is alias of `equals`
+ *
+ * ####Example
+ *
+ * User.where('age').eq(49);
+ *
+ * // is the same as
+ *
+ * User.shere('age').equals(49);
+ *
+ * // is the same as
+ *
+ * User.where('age', 49);
+ *
+ * @param {Object} val
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.eq = function eq(val) {
+ this._ensurePath('eq');
+ var path = this._path;
+ this._conditions[path] = val;
+ return this;
+};
+
+/**
+ * Specifies arguments for an `$or` condition.
+ *
+ * ####Example
+ *
+ * query.or([{ color: 'red' }, { status: 'emergency' }])
+ *
+ * @param {Array} array array of conditions
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.or = function or(array) {
+ var or = this._conditions.$or || (this._conditions.$or = []);
+ if (!utils.isArray(array)) array = [array];
+ or.push.apply(or, array);
+ return this;
+};
+
+/**
+ * Specifies arguments for a `$nor` condition.
+ *
+ * ####Example
+ *
+ * query.nor([{ color: 'green' }, { status: 'ok' }])
+ *
+ * @param {Array} array array of conditions
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.nor = function nor(array) {
+ var nor = this._conditions.$nor || (this._conditions.$nor = []);
+ if (!utils.isArray(array)) array = [array];
+ nor.push.apply(nor, array);
+ return this;
+};
+
+/**
+ * Specifies arguments for a `$and` condition.
+ *
+ * ####Example
+ *
+ * query.and([{ color: 'green' }, { status: 'ok' }])
+ *
+ * @see $and http://docs.mongodb.org/manual/reference/operator/and/
+ * @param {Array} array array of conditions
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.and = function and(array) {
+ var and = this._conditions.$and || (this._conditions.$and = []);
+ if (!Array.isArray(array)) array = [array];
+ and.push.apply(and, array);
+ return this;
+};
+
+/**
+ * Specifies a $gt query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * ####Example
+ *
+ * Thing.find().where('age').gt(21)
+ *
+ * // or
+ * Thing.find().gt('age', 21)
+ *
+ * @method gt
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $gte query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method gte
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $lt query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method lt
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $lte query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method lte
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $ne query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method ne
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies an $in query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method in
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies an $nin query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method nin
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies an $all query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method all
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $size query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method size
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/**
+ * Specifies a $regex query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method regex
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {String|RegExp} val
+ * @api public
+ */
+
+/**
+ * Specifies a $maxDistance query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
+ *
+ * @method maxDistance
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val
+ * @api public
+ */
+
+/*!
+ * gt, gte, lt, lte, ne, in, nin, all, regex, size, maxDistance
+ *
+ * Thing.where('type').nin(array)
+ */
+
+'gt gte lt lte ne in nin all regex size maxDistance minDistance'.split(' ').forEach(function($conditional) {
+ Query.prototype[$conditional] = function() {
+ var path, val;
+
+ if (1 === arguments.length) {
+ this._ensurePath($conditional);
+ val = arguments[0];
+ path = this._path;
+ } else {
+ val = arguments[1];
+ path = arguments[0];
+ }
+
+ var conds = this._conditions[path] === null || typeof this._conditions[path] === 'object' ?
+ this._conditions[path] :
+ (this._conditions[path] = {});
+ conds['$' + $conditional] = val;
+ return this;
+ };
+});
+
+/**
+ * Specifies a `$mod` condition
+ *
+ * @param {String} [path]
+ * @param {Number} val
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.mod = function() {
+ var val, path;
+
+ if (1 === arguments.length) {
+ this._ensurePath('mod');
+ val = arguments[0];
+ path = this._path;
+ } else if (2 === arguments.length && !utils.isArray(arguments[1])) {
+ this._ensurePath('mod');
+ val = slice(arguments);
+ path = this._path;
+ } else if (3 === arguments.length) {
+ val = slice(arguments, 1);
+ path = arguments[0];
+ } else {
+ val = arguments[1];
+ path = arguments[0];
+ }
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds.$mod = val;
+ return this;
+};
+
+/**
+ * Specifies an `$exists` condition
+ *
+ * ####Example
+ *
+ * // { name: { $exists: true }}
+ * Thing.where('name').exists()
+ * Thing.where('name').exists(true)
+ * Thing.find().exists('name')
+ *
+ * // { name: { $exists: false }}
+ * Thing.where('name').exists(false);
+ * Thing.find().exists('name', false);
+ *
+ * @param {String} [path]
+ * @param {Number} val
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.exists = function() {
+ var path, val;
+
+ if (0 === arguments.length) {
+ this._ensurePath('exists');
+ path = this._path;
+ val = true;
+ } else if (1 === arguments.length) {
+ if ('boolean' === typeof arguments[0]) {
+ this._ensurePath('exists');
+ path = this._path;
+ val = arguments[0];
+ } else {
+ path = arguments[0];
+ val = true;
+ }
+ } else if (2 === arguments.length) {
+ path = arguments[0];
+ val = arguments[1];
+ }
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds.$exists = val;
+ return this;
+};
+
+/**
+ * Specifies an `$elemMatch` condition
+ *
+ * ####Example
+ *
+ * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}})
+ *
+ * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})
+ *
+ * query.elemMatch('comment', function (elem) {
+ * elem.where('author').equals('autobot');
+ * elem.where('votes').gte(5);
+ * })
+ *
+ * query.where('comment').elemMatch(function (elem) {
+ * elem.where({ author: 'autobot' });
+ * elem.where('votes').gte(5);
+ * })
+ *
+ * @param {String|Object|Function} path
+ * @param {Object|Function} criteria
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.elemMatch = function() {
+ if (null == arguments[0])
+ throw new TypeError('Invalid argument');
+
+ var fn, path, criteria;
+
+ if ('function' === typeof arguments[0]) {
+ this._ensurePath('elemMatch');
+ path = this._path;
+ fn = arguments[0];
+ } else if (utils.isObject(arguments[0])) {
+ this._ensurePath('elemMatch');
+ path = this._path;
+ criteria = arguments[0];
+ } else if ('function' === typeof arguments[1]) {
+ path = arguments[0];
+ fn = arguments[1];
+ } else if (arguments[1] && utils.isObject(arguments[1])) {
+ path = arguments[0];
+ criteria = arguments[1];
+ } else {
+ throw new TypeError('Invalid argument');
+ }
+
+ if (fn) {
+ criteria = new Query;
+ fn(criteria);
+ criteria = criteria._conditions;
+ }
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds.$elemMatch = criteria;
+ return this;
+};
+
+// Spatial queries
+
+/**
+ * Sugar for geo-spatial queries.
+ *
+ * ####Example
+ *
+ * query.within().box()
+ * query.within().circle()
+ * query.within().geometry()
+ *
+ * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
+ * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
+ * query.where('loc').within({ polygon: [[],[],[],[]] });
+ *
+ * query.where('loc').within([], [], []) // polygon
+ * query.where('loc').within([], []) // box
+ * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
+ *
+ * ####NOTE:
+ *
+ * Must be used after `where()`.
+ *
+ * @memberOf Query
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.within = function within() {
+ // opinionated, must be used after where
+ this._ensurePath('within');
+ this._geoComparison = $withinCmd;
+
+ if (0 === arguments.length) {
+ return this;
+ }
+
+ if (2 === arguments.length) {
+ return this.box.apply(this, arguments);
+ } else if (2 < arguments.length) {
+ return this.polygon.apply(this, arguments);
+ }
+
+ var area = arguments[0];
+
+ if (!area)
+ throw new TypeError('Invalid argument');
+
+ if (area.center)
+ return this.circle(area);
+
+ if (area.box)
+ return this.box.apply(this, area.box);
+
+ if (area.polygon)
+ return this.polygon.apply(this, area.polygon);
+
+ if (area.type && area.coordinates)
+ return this.geometry(area);
+
+ throw new TypeError('Invalid argument');
+};
+
+/**
+ * Specifies a $box condition
+ *
+ * ####Example
+ *
+ * var lowerLeft = [40.73083, -73.99756]
+ * var upperRight= [40.741404, -73.988135]
+ *
+ * query.where('loc').within().box(lowerLeft, upperRight)
+ * query.box('loc', lowerLeft, upperRight )
+ *
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @see Query#within #query_Query-within
+ * @param {String} path
+ * @param {Object} val
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.box = function() {
+ var path, box;
+
+ if (3 === arguments.length) {
+ // box('loc', [], [])
+ path = arguments[0];
+ box = [arguments[1], arguments[2]];
+ } else if (2 === arguments.length) {
+ // box([], [])
+ this._ensurePath('box');
+ path = this._path;
+ box = [arguments[0], arguments[1]];
+ } else {
+ throw new TypeError('Invalid argument');
+ }
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds[this._geoComparison || $withinCmd] = { '$box': box };
+ return this;
+};
+
+/**
+ * Specifies a $polygon condition
+ *
+ * ####Example
+ *
+ * query.where('loc').within().polygon([10,20], [13, 25], [7,15])
+ * query.polygon('loc', [10,20], [13, 25], [7,15])
+ *
+ * @param {String|Array} [path]
+ * @param {Array|Object} [val]
+ * @return {Query} this
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+Query.prototype.polygon = function() {
+ var val, path;
+
+ if ('string' == typeof arguments[0]) {
+ // polygon('loc', [],[],[])
+ path = arguments[0];
+ val = slice(arguments, 1);
+ } else {
+ // polygon([],[],[])
+ this._ensurePath('polygon');
+ path = this._path;
+ val = slice(arguments);
+ }
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds[this._geoComparison || $withinCmd] = { '$polygon': val };
+ return this;
+};
+
+/**
+ * Specifies a $center or $centerSphere condition.
+ *
+ * ####Example
+ *
+ * var area = { center: [50, 50], radius: 10, unique: true }
+ * query.where('loc').within().circle(area)
+ * query.center('loc', area);
+ *
+ * // for spherical calculations
+ * var area = { center: [50, 50], radius: 10, unique: true, spherical: true }
+ * query.where('loc').within().circle(area)
+ * query.center('loc', area);
+ *
+ * @param {String} [path]
+ * @param {Object} area
+ * @return {Query} this
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+Query.prototype.circle = function() {
+ var path, val;
+
+ if (1 === arguments.length) {
+ this._ensurePath('circle');
+ path = this._path;
+ val = arguments[0];
+ } else if (2 === arguments.length) {
+ path = arguments[0];
+ val = arguments[1];
+ } else {
+ throw new TypeError('Invalid argument');
+ }
+
+ if (!('radius' in val && val.center))
+ throw new Error('center and radius are required');
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+
+ var type = val.spherical
+ ? '$centerSphere'
+ : '$center';
+
+ var wKey = this._geoComparison || $withinCmd;
+ conds[wKey] = {};
+ conds[wKey][type] = [val.center, val.radius];
+
+ if ('unique' in val)
+ conds[wKey].$uniqueDocs = !!val.unique;
+
+ return this;
+};
+
+/**
+ * Specifies a `$near` or `$nearSphere` condition
+ *
+ * These operators return documents sorted by distance.
+ *
+ * ####Example
+ *
+ * query.where('loc').near({ center: [10, 10] });
+ * query.where('loc').near({ center: [10, 10], maxDistance: 5 });
+ * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true });
+ * query.near('loc', { center: [10, 10], maxDistance: 5 });
+ * query.near({ center: { type: 'Point', coordinates: [..] }})
+ * query.near().geometry({ type: 'Point', coordinates: [..] })
+ *
+ * @param {String} [path]
+ * @param {Object} val
+ * @return {Query} this
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+Query.prototype.near = function near() {
+ var path, val;
+
+ this._geoComparison = '$near';
+
+ if (0 === arguments.length) {
+ return this;
+ } else if (1 === arguments.length) {
+ this._ensurePath('near');
+ path = this._path;
+ val = arguments[0];
+ } else if (2 === arguments.length) {
+ path = arguments[0];
+ val = arguments[1];
+ } else {
+ throw new TypeError('Invalid argument');
+ }
+
+ if (!val.center) {
+ throw new Error('center is required');
+ }
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+
+ var type = val.spherical
+ ? '$nearSphere'
+ : '$near';
+
+ // center could be a GeoJSON object or an Array
+ if (Array.isArray(val.center)) {
+ conds[type] = val.center;
+
+ var radius = 'maxDistance' in val
+ ? val.maxDistance
+ : null;
+
+ if (null != radius) {
+ conds.$maxDistance = radius;
+ }
+ if (null != val.minDistance) {
+ conds.$minDistance = val.minDistance;
+ }
+ } else {
+ // GeoJSON?
+ if (val.center.type != 'Point' || !Array.isArray(val.center.coordinates)) {
+ throw new Error(util.format('Invalid GeoJSON specified for %s', type));
+ }
+ conds[type] = { $geometry : val.center };
+
+ // MongoDB 2.6 insists on maxDistance being in $near / $nearSphere
+ if ('maxDistance' in val) {
+ conds[type]['$maxDistance'] = val.maxDistance;
+ }
+ if ('minDistance' in val) {
+ conds[type]['$minDistance'] = val.minDistance;
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Declares an intersects query for `geometry()`.
+ *
+ * ####Example
+ *
+ * query.where('path').intersects().geometry({
+ * type: 'LineString'
+ * , coordinates: [[180.0, 11.0], [180, 9.0]]
+ * })
+ *
+ * query.where('path').intersects({
+ * type: 'LineString'
+ * , coordinates: [[180.0, 11.0], [180, 9.0]]
+ * })
+ *
+ * @param {Object} [arg]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.intersects = function intersects() {
+ // opinionated, must be used after where
+ this._ensurePath('intersects');
+
+ this._geoComparison = '$geoIntersects';
+
+ if (0 === arguments.length) {
+ return this;
+ }
+
+ var area = arguments[0];
+
+ if (null != area && area.type && area.coordinates)
+ return this.geometry(area);
+
+ throw new TypeError('Invalid argument');
+};
+
+/**
+ * Specifies a `$geometry` condition
+ *
+ * ####Example
+ *
+ * var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
+ * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })
+ *
+ * // or
+ * var polyB = [[ 0, 0 ], [ 1, 1 ]]
+ * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })
+ *
+ * // or
+ * var polyC = [ 0, 0 ]
+ * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })
+ *
+ * // or
+ * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })
+ *
+ * ####NOTE:
+ *
+ * `geometry()` **must** come after either `intersects()` or `within()`.
+ *
+ * The `object` argument must contain `type` and `coordinates` properties.
+ * - type {String}
+ * - coordinates {Array}
+ *
+ * The most recent path passed to `where()` is used.
+ *
+ * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples.
+ * @return {Query} this
+ * @see http://docs.mongodb.org/manual/release-notes/2.4/#new-geospatial-indexes-with-geojson-and-improved-spherical-geometry
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
+ * @api public
+ */
+
+Query.prototype.geometry = function geometry() {
+ if (!('$within' == this._geoComparison ||
+ '$geoWithin' == this._geoComparison ||
+ '$near' == this._geoComparison ||
+ '$geoIntersects' == this._geoComparison)) {
+ throw new Error('geometry() must come after `within()`, `intersects()`, or `near()');
+ }
+
+ var val, path;
+
+ if (1 === arguments.length) {
+ this._ensurePath('geometry');
+ path = this._path;
+ val = arguments[0];
+ } else {
+ throw new TypeError('Invalid argument');
+ }
+
+ if (!(val.type && Array.isArray(val.coordinates))) {
+ throw new TypeError('Invalid argument');
+ }
+
+ var conds = this._conditions[path] || (this._conditions[path] = {});
+ conds[this._geoComparison] = { $geometry: val };
+
+ return this;
+};
+
+// end spatial
+
+/**
+ * Specifies which document fields to include or exclude
+ *
+ * ####String syntax
+ *
+ * When passing a string, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included.
+ *
+ * ####Example
+ *
+ * // include a and b, exclude c
+ * query.select('a b -c');
+ *
+ * // or you may use object notation, useful when
+ * // you have keys already prefixed with a "-"
+ * query.select({a: 1, b: 1, c: 0});
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @param {Object|String} arg
+ * @return {Query} this
+ * @see SchemaType
+ * @api public
+ */
+
+Query.prototype.select = function select() {
+ var arg = arguments[0];
+ if (!arg) return this;
+
+ if (arguments.length !== 1) {
+ throw new Error('Invalid select: select only takes 1 argument');
+ }
+
+ this._validate('select');
+
+ var fields = this._fields || (this._fields = {});
+ var type = typeof arg;
+ var i, len;
+
+ if (('string' == type || utils.isArgumentsObject(arg)) &&
+ 'number' == typeof arg.length || Array.isArray(arg)) {
+ if ('string' == type)
+ arg = arg.split(/\s+/);
+
+ for (i = 0, len = arg.length; i < len; ++i) {
+ var field = arg[i];
+ if (!field) continue;
+ var include = '-' == field[0] ? 0 : 1;
+ if (include === 0) field = field.substring(1);
+ fields[field] = include;
+ }
+
+ return this;
+ }
+
+ if (utils.isObject(arg)) {
+ var keys = utils.keys(arg);
+ for (i = 0; i < keys.length; ++i) {
+ fields[keys[i]] = arg[keys[i]];
+ }
+ return this;
+ }
+
+ throw new TypeError('Invalid select() argument. Must be string or object.');
+};
+
+/**
+ * Specifies a $slice condition for a `path`
+ *
+ * ####Example
+ *
+ * query.slice('comments', 5)
+ * query.slice('comments', -5)
+ * query.slice('comments', [10, 5])
+ * query.where('comments').slice(5)
+ * query.where('comments').slice([-10, 5])
+ *
+ * @param {String} [path]
+ * @param {Number} val number/range of elements to slice
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements
+ * @api public
+ */
+
+Query.prototype.slice = function() {
+ if (0 === arguments.length)
+ return this;
+
+ this._validate('slice');
+
+ var path, val;
+
+ if (1 === arguments.length) {
+ var arg = arguments[0];
+ if (typeof arg === 'object' && !Array.isArray(arg)) {
+ var keys = Object.keys(arg);
+ var numKeys = keys.length;
+ for (var i = 0; i < numKeys; ++i) {
+ this.slice(keys[i], arg[keys[i]]);
+ }
+ return this;
+ }
+ this._ensurePath('slice');
+ path = this._path;
+ val = arguments[0];
+ } else if (2 === arguments.length) {
+ if ('number' === typeof arguments[0]) {
+ this._ensurePath('slice');
+ path = this._path;
+ val = slice(arguments);
+ } else {
+ path = arguments[0];
+ val = arguments[1];
+ }
+ } else if (3 === arguments.length) {
+ path = arguments[0];
+ val = slice(arguments, 1);
+ }
+
+ var myFields = this._fields || (this._fields = {});
+ myFields[path] = { '$slice': val };
+ return this;
+};
+
+/**
+ * Sets the sort order
+ *
+ * If an object is passed, values allowed are 'asc', 'desc', 'ascending', 'descending', 1, and -1.
+ *
+ * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending.
+ *
+ * ####Example
+ *
+ * // these are equivalent
+ * query.sort({ field: 'asc', test: -1 });
+ * query.sort('field -test');
+ * query.sort([['field', 1], ['test', -1]]);
+ *
+ * ####Note
+ *
+ * - The array syntax `.sort([['field', 1], ['test', -1]])` can only be used with [mongodb driver >= 2.0.46](https://github.com/mongodb/node-mongodb-native/blob/2.1/HISTORY.md#2046-2015-10-15).
+ * - Cannot be used with `distinct()`
+ *
+ * @param {Object|String|Array} arg
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.sort = function(arg) {
+ if (!arg) return this;
+ var i, len, field;
+
+ this._validate('sort');
+
+ var type = typeof arg;
+
+ // .sort([['field', 1], ['test', -1]])
+ if (Array.isArray(arg)) {
+ len = arg.length;
+ for (i = 0; i < arg.length; ++i) {
+ if (!Array.isArray(arg[i])) {
+ throw new Error('Invalid sort() argument, must be array of arrays');
+ }
+ _pushArr(this.options, arg[i][0], arg[i][1]);
+ }
+ return this;
+ }
+
+ // .sort('field -test')
+ if (1 === arguments.length && 'string' == type) {
+ arg = arg.split(/\s+/);
+ len = arg.length;
+ for (i = 0; i < len; ++i) {
+ field = arg[i];
+ if (!field) continue;
+ var ascend = '-' == field[0] ? -1 : 1;
+ if (ascend === -1) field = field.substring(1);
+ push(this.options, field, ascend);
+ }
+
+ return this;
+ }
+
+ // .sort({ field: 1, test: -1 })
+ if (utils.isObject(arg)) {
+ var keys = utils.keys(arg);
+ for (i = 0; i < keys.length; ++i) {
+ field = keys[i];
+ push(this.options, field, arg[field]);
+ }
+
+ return this;
+ }
+
+ if (typeof Map !== 'undefined' && arg instanceof Map) {
+ _pushMap(this.options, arg);
+ return this;
+ }
+ throw new TypeError('Invalid sort() argument. Must be a string, object, or array.');
+};
+
+/*!
+ * @ignore
+ */
+
+var _validSortValue = {
+ '1': 1,
+ '-1': -1,
+ 'asc': 1,
+ 'ascending': 1,
+ 'desc': -1,
+ 'descending': -1
+};
+
+function push(opts, field, value) {
+ if (Array.isArray(opts.sort)) {
+ throw new TypeError('Can\'t mix sort syntaxes. Use either array or object:' +
+ '\n- `.sort([[\'field\', 1], [\'test\', -1]])`' +
+ '\n- `.sort({ field: 1, test: -1 })`');
+ }
+
+ var s;
+ if (value && value.$meta) {
+ s = opts.sort || (opts.sort = {});
+ s[field] = { $meta : value.$meta };
+ return;
+ }
+
+ s = opts.sort || (opts.sort = {});
+ var val = String(value || 1).toLowerCase();
+ val = _validSortValue[val];
+ if (!val) throw new TypeError('Invalid sort value: { ' + field + ': ' + value + ' }');
+
+ s[field] = val;
+}
+
+function _pushArr(opts, field, value) {
+ opts.sort = opts.sort || [];
+ if (!Array.isArray(opts.sort)) {
+ throw new TypeError('Can\'t mix sort syntaxes. Use either array or object:' +
+ '\n- `.sort([[\'field\', 1], [\'test\', -1]])`' +
+ '\n- `.sort({ field: 1, test: -1 })`');
+ }
+
+ var val = String(value || 1).toLowerCase();
+ val = _validSortValue[val];
+ if (!val) throw new TypeError('Invalid sort value: [ ' + field + ', ' + value + ' ]');
+
+ opts.sort.push([field, val]);
+}
+
+function _pushMap(opts, map) {
+ opts.sort = opts.sort || new Map();
+ if (!(opts.sort instanceof Map)) {
+ throw new TypeError('Can\'t mix sort syntaxes. Use either array or ' +
+ 'object or map consistently');
+ }
+ map.forEach(function(value, key) {
+ var val = String(value || 1).toLowerCase();
+ val = _validSortValue[val];
+ if (!val) throw new TypeError('Invalid sort value: < ' + key + ': ' + value + ' >');
+
+ opts.sort.set(key, val);
+ });
+}
+
+
+
+/**
+ * Specifies the limit option.
+ *
+ * ####Example
+ *
+ * query.limit(20)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method limit
+ * @memberOf Query
+ * @param {Number} val
+ * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D
+ * @api public
+ */
+/**
+ * Specifies the skip option.
+ *
+ * ####Example
+ *
+ * query.skip(100).limit(20)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method skip
+ * @memberOf Query
+ * @param {Number} val
+ * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D
+ * @api public
+ */
+/**
+ * Specifies the maxScan option.
+ *
+ * ####Example
+ *
+ * query.maxScan(100)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method maxScan
+ * @memberOf Query
+ * @param {Number} val
+ * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24maxScan
+ * @api public
+ */
+/**
+ * Specifies the batchSize option.
+ *
+ * ####Example
+ *
+ * query.batchSize(100)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method batchSize
+ * @memberOf Query
+ * @param {Number} val
+ * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D
+ * @api public
+ */
+/**
+ * Specifies the `comment` option.
+ *
+ * ####Example
+ *
+ * query.comment('login query')
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method comment
+ * @memberOf Query
+ * @param {Number} val
+ * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment
+ * @api public
+ */
+
+/*!
+ * limit, skip, maxScan, batchSize, comment
+ *
+ * Sets these associated options.
+ *
+ * query.comment('feed query');
+ */
+
+['limit', 'skip', 'maxScan', 'batchSize', 'comment'].forEach(function(method) {
+ Query.prototype[method] = function(v) {
+ this._validate(method);
+ this.options[method] = v;
+ return this;
+ };
+});
+
+/**
+ * Specifies the maxTimeMS option.
+ *
+ * ####Example
+ *
+ * query.maxTime(100)
+ * query.maxTimeMS(100)
+ *
+ * @method maxTime
+ * @memberOf Query
+ * @param {Number} ms
+ * @see mongodb http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/#op._S_maxTimeMS
+ * @api public
+ */
+
+Query.prototype.maxTime = Query.prototype.maxTimeMS = function(ms) {
+ this._validate('maxTime');
+ this.options.maxTimeMS = ms;
+ return this;
+};
+
+/**
+ * Specifies this query as a `snapshot` query.
+ *
+ * ####Example
+ *
+ * mquery().snapshot() // true
+ * mquery().snapshot(true)
+ * mquery().snapshot(false)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.snapshot = function() {
+ this._validate('snapshot');
+
+ this.options.snapshot = arguments.length
+ ? !!arguments[0]
+ : true;
+
+ return this;
+};
+
+/**
+ * Sets query hints.
+ *
+ * ####Example
+ *
+ * query.hint({ indexA: 1, indexB: -1});
+ * query.hint('indexA_1_indexB_1');
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @param {Object|string} val a hint object or the index name
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint
+ * @api public
+ */
+
+Query.prototype.hint = function() {
+ if (0 === arguments.length) return this;
+
+ this._validate('hint');
+
+ var arg = arguments[0];
+ if (utils.isObject(arg)) {
+ var hint = this.options.hint || (this.options.hint = {});
+
+ // must keep object keys in order so don't use Object.keys()
+ for (var k in arg) {
+ hint[k] = arg[k];
+ }
+
+ return this;
+ }
+ if (typeof arg === 'string') {
+ this.options.hint = arg;
+ return this;
+ }
+
+ throw new TypeError('Invalid hint. ' + arg);
+};
+
+/**
+ * Requests acknowledgement that this operation has been persisted to MongoDB's
+ * on-disk journal.
+ * This option is only valid for operations that write to the database:
+ *
+ * - `deleteOne()`
+ * - `deleteMany()`
+ * - `findOneAndDelete()`
+ * - `findOneAndUpdate()`
+ * - `remove()`
+ * - `update()`
+ * - `updateOne()`
+ * - `updateMany()`
+ *
+ * Defaults to the `j` value if it is specified in writeConcern options
+ *
+ * ####Example:
+ *
+ * mquery().w(2).j(true).wtimeout(2000);
+ *
+ * @method j
+ * @memberOf Query
+ * @instance
+ * @param {boolean} val
+ * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#j-option
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.j = function j(val) {
+ this.options.j = val;
+ return this;
+};
+
+/**
+ * Sets the slaveOk option. _Deprecated_ in MongoDB 2.2 in favor of read preferences.
+ *
+ * ####Example:
+ *
+ * query.slaveOk() // true
+ * query.slaveOk(true)
+ * query.slaveOk(false)
+ *
+ * @deprecated use read() preferences instead if on mongodb >= 2.2
+ * @param {Boolean} v defaults to true
+ * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
+ * @see read()
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.slaveOk = function(v) {
+ this.options.slaveOk = arguments.length ? !!v : true;
+ return this;
+};
+
+/**
+ * Sets the readPreference option for the query.
+ *
+ * ####Example:
+ *
+ * new Query().read('primary')
+ * new Query().read('p') // same as primary
+ *
+ * new Query().read('primaryPreferred')
+ * new Query().read('pp') // same as primaryPreferred
+ *
+ * new Query().read('secondary')
+ * new Query().read('s') // same as secondary
+ *
+ * new Query().read('secondaryPreferred')
+ * new Query().read('sp') // same as secondaryPreferred
+ *
+ * new Query().read('nearest')
+ * new Query().read('n') // same as nearest
+ *
+ * // you can also use mongodb.ReadPreference class to also specify tags
+ * new Query().read(mongodb.ReadPreference('secondary', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }]))
+ *
+ * new Query().setReadPreference('primary') // alias of .read()
+ *
+ * ####Preferences:
+ *
+ * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags.
+ * secondary Read from secondary if available, otherwise error.
+ * primaryPreferred Read from primary if available, otherwise a secondary.
+ * secondaryPreferred Read from a secondary if available, otherwise read from the primary.
+ * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection.
+ *
+ * Aliases
+ *
+ * p primary
+ * pp primaryPreferred
+ * s secondary
+ * sp secondaryPreferred
+ * n nearest
+ *
+ * Read more about how to use read preferences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences).
+ *
+ * @param {String|ReadPreference} pref one of the listed preference options or their aliases
+ * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
+ * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.read = Query.prototype.setReadPreference = function(pref) {
+ if (arguments.length > 1 && !Query.prototype.read.deprecationWarningIssued) {
+ console.error('Deprecation warning: \'tags\' argument is not supported anymore in Query.read() method. Please use mongodb.ReadPreference object instead.');
+ Query.prototype.read.deprecationWarningIssued = true;
+ }
+ this.options.readPreference = utils.readPref(pref);
+ return this;
+};
+
+/**
+ * Sets the readConcern option for the query.
+ *
+ * ####Example:
+ *
+ * new Query().readConcern('local')
+ * new Query().readConcern('l') // same as local
+ *
+ * new Query().readConcern('available')
+ * new Query().readConcern('a') // same as available
+ *
+ * new Query().readConcern('majority')
+ * new Query().readConcern('m') // same as majority
+ *
+ * new Query().readConcern('linearizable')
+ * new Query().readConcern('lz') // same as linearizable
+ *
+ * new Query().readConcern('snapshot')
+ * new Query().readConcern('s') // same as snapshot
+ *
+ * new Query().r('s') // r is alias of readConcern
+ *
+ *
+ * ####Read Concern Level:
+ *
+ * local MongoDB 3.2+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
+ * available MongoDB 3.6+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
+ * majority MongoDB 3.2+ The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure.
+ * linearizable MongoDB 3.4+ The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results.
+ * snapshot MongoDB 4.0+ Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data.
+
+
+ *
+ *
+ * Aliases
+ *
+ * l local
+ * a available
+ * m majority
+ * lz linearizable
+ * s snapshot
+ *
+ * Read more about how to use read concern [here](https://docs.mongodb.com/manual/reference/read-concern/).
+ *
+ * @param {String} level one of the listed read concern level or their aliases
+ * @see mongodb https://docs.mongodb.com/manual/reference/read-concern/
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.readConcern = Query.prototype.r = function(level) {
+ this.options.readConcern = utils.readConcern(level);
+ return this;
+};
+
+/**
+ * Sets tailable option.
+ *
+ * ####Example
+ *
+ * query.tailable() <== true
+ * query.tailable(true)
+ * query.tailable(false)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @param {Boolean} v defaults to true
+ * @see mongodb http://www.mongodb.org/display/DOCS/Tailable+Cursors
+ * @api public
+ */
+
+Query.prototype.tailable = function() {
+ this._validate('tailable');
+
+ this.options.tailable = arguments.length
+ ? !!arguments[0]
+ : true;
+
+ return this;
+};
+
+/**
+ * Sets the specified number of `mongod` servers, or tag set of `mongod` servers,
+ * that must acknowledge this write before this write is considered successful.
+ * This option is only valid for operations that write to the database:
+ *
+ * - `deleteOne()`
+ * - `deleteMany()`
+ * - `findOneAndDelete()`
+ * - `findOneAndUpdate()`
+ * - `remove()`
+ * - `update()`
+ * - `updateOne()`
+ * - `updateMany()`
+ *
+ * Defaults to the `w` value if it is specified in writeConcern options
+ *
+ * ####Example:
+ *
+ * mquery().writeConcern(0)
+ * mquery().writeConcern(1)
+ * mquery().writeConcern({ w: 1, j: true, wtimeout: 2000 })
+ * mquery().writeConcern('majority')
+ * mquery().writeConcern('m') // same as majority
+ * mquery().writeConcern('tagSetName') // if the tag set is 'm', use .writeConcern({ w: 'm' }) instead
+ * mquery().w(1) // w is alias of writeConcern
+ *
+ * @method writeConcern
+ * @memberOf Query
+ * @instance
+ * @param {String|number|object} concern 0 for fire-and-forget, 1 for acknowledged by one server, 'majority' for majority of the replica set, or [any of the more advanced options](https://docs.mongodb.com/manual/reference/write-concern/#w-option).
+ * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#w-option
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.writeConcern = Query.prototype.w = function writeConcern(concern) {
+ if ('object' === typeof concern) {
+ if ('undefined' !== typeof concern.j) this.options.j = concern.j;
+ if ('undefined' !== typeof concern.w) this.options.w = concern.w;
+ if ('undefined' !== typeof concern.wtimeout) this.options.wtimeout = concern.wtimeout;
+ } else {
+ this.options.w = 'm' === concern ? 'majority' : concern;
+ }
+ return this;
+};
+
+/**
+ * Specifies a time limit, in milliseconds, for the write concern.
+ * If `ms > 1`, it is maximum amount of time to wait for this write
+ * to propagate through the replica set before this operation fails.
+ * The default is `0`, which means no timeout.
+ *
+ * This option is only valid for operations that write to the database:
+ *
+ * - `deleteOne()`
+ * - `deleteMany()`
+ * - `findOneAndDelete()`
+ * - `findOneAndUpdate()`
+ * - `remove()`
+ * - `update()`
+ * - `updateOne()`
+ * - `updateMany()`
+ *
+ * Defaults to `wtimeout` value if it is specified in writeConcern
+ *
+ * ####Example:
+ *
+ * mquery().w(2).j(true).wtimeout(2000)
+ *
+ * @method wtimeout
+ * @memberOf Query
+ * @instance
+ * @param {number} ms number of milliseconds to wait
+ * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#wtimeout
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.wtimeout = Query.prototype.wTimeout = function wtimeout(ms) {
+ this.options.wtimeout = ms;
+ return this;
+};
+
+/**
+ * Merges another Query or conditions object into this one.
+ *
+ * When a Query is passed, conditions, field selection and options are merged.
+ *
+ * @param {Query|Object} source
+ * @return {Query} this
+ */
+
+Query.prototype.merge = function(source) {
+ if (!source)
+ return this;
+
+ if (!Query.canMerge(source))
+ throw new TypeError('Invalid argument. Expected instanceof mquery or plain object');
+
+ if (source instanceof Query) {
+ // if source has a feature, apply it to ourselves
+
+ if (source._conditions) {
+ utils.merge(this._conditions, source._conditions);
+ }
+
+ if (source._fields) {
+ this._fields || (this._fields = {});
+ utils.merge(this._fields, source._fields);
+ }
+
+ if (source.options) {
+ this.options || (this.options = {});
+ utils.merge(this.options, source.options);
+ }
+
+ if (source._update) {
+ this._update || (this._update = {});
+ utils.mergeClone(this._update, source._update);
+ }
+
+ if (source._distinct) {
+ this._distinct = source._distinct;
+ }
+
+ return this;
+ }
+
+ // plain object
+ utils.merge(this._conditions, source);
+
+ return this;
+};
+
+/**
+ * Finds documents.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * ####Example
+ *
+ * query.find()
+ * query.find(callback)
+ * query.find({ name: 'Burning Lights' }, callback)
+ *
+ * @param {Object} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.find = function(criteria, callback) {
+ this.op = 'find';
+
+ if ('function' === typeof criteria) {
+ callback = criteria;
+ criteria = undefined;
+ } else if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ }
+
+ if (!callback) return this;
+
+ var conds = this._conditions;
+ var options = this._optionsForExec();
+
+ if (this.$useProjection) {
+ options.projection = this._fieldsForExec();
+ } else {
+ options.fields = this._fieldsForExec();
+ }
+
+ debug('find', this._collection.collectionName, conds, options);
+ callback = this._wrapCallback('find', callback, {
+ conditions: conds,
+ options: options
+ });
+
+ this._collection.find(conds, options, utils.tick(callback));
+ return this;
+};
+
+/**
+ * Returns the query cursor
+ *
+ * ####Examples
+ *
+ * query.find().cursor();
+ * query.cursor({ name: 'Burning Lights' });
+ *
+ * @param {Object} [criteria] mongodb selector
+ * @return {Object} cursor
+ * @api public
+ */
+
+Query.prototype.cursor = function cursor(criteria) {
+ if (this.op) {
+ if (this.op !== 'find') {
+ throw new TypeError('.cursor only support .find method');
+ }
+ } else {
+ this.find(criteria);
+ }
+
+ var conds = this._conditions;
+ var options = this._optionsForExec();
+
+ if (this.$useProjection) {
+ options.projection = this._fieldsForExec();
+ } else {
+ options.fields = this._fieldsForExec();
+ }
+
+ debug('findCursor', this._collection.collectionName, conds, options);
+ return this._collection.findCursor(conds, options);
+};
+
+/**
+ * Executes the query as a findOne() operation.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * ####Example
+ *
+ * query.findOne().where('name', /^Burning/);
+ *
+ * query.findOne({ name: /^Burning/ })
+ *
+ * query.findOne({ name: /^Burning/ }, callback); // executes
+ *
+ * query.findOne(function (err, doc) {
+ * if (err) return handleError(err);
+ * if (doc) {
+ * // doc may be null if no document matched
+ *
+ * }
+ * });
+ *
+ * @param {Object|Query} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.findOne = function(criteria, callback) {
+ this.op = 'findOne';
+
+ if ('function' === typeof criteria) {
+ callback = criteria;
+ criteria = undefined;
+ } else if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ }
+
+ if (!callback) return this;
+
+ var conds = this._conditions;
+ var options = this._optionsForExec();
+
+ if (this.$useProjection) {
+ options.projection = this._fieldsForExec();
+ } else {
+ options.fields = this._fieldsForExec();
+ }
+
+ debug('findOne', this._collection.collectionName, conds, options);
+ callback = this._wrapCallback('findOne', callback, {
+ conditions: conds,
+ options: options
+ });
+
+ this._collection.findOne(conds, options, utils.tick(callback));
+
+ return this;
+};
+
+/**
+ * Exectues the query as a count() operation.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * ####Example
+ *
+ * query.count().where('color', 'black').exec(callback);
+ *
+ * query.count({ color: 'black' }).count(callback)
+ *
+ * query.count({ color: 'black' }, callback)
+ *
+ * query.where('color', 'black').count(function (err, count) {
+ * if (err) return handleError(err);
+ * console.log('there are %d kittens', count);
+ * })
+ *
+ * @param {Object} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Count
+ * @api public
+ */
+
+Query.prototype.count = function(criteria, callback) {
+ this.op = 'count';
+ this._validate();
+
+ if ('function' === typeof criteria) {
+ callback = criteria;
+ criteria = undefined;
+ } else if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ }
+
+ if (!callback) return this;
+
+ var conds = this._conditions,
+ options = this._optionsForExec();
+
+ debug('count', this._collection.collectionName, conds, options);
+ callback = this._wrapCallback('count', callback, {
+ conditions: conds,
+ options: options
+ });
+
+ this._collection.count(conds, options, utils.tick(callback));
+ return this;
+};
+
+/**
+ * Declares or executes a distinct() operation.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * ####Example
+ *
+ * distinct(criteria, field, fn)
+ * distinct(criteria, field)
+ * distinct(field, fn)
+ * distinct(field)
+ * distinct(fn)
+ * distinct()
+ *
+ * @param {Object|Query} [criteria]
+ * @param {String} [field]
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Distinct
+ * @api public
+ */
+
+Query.prototype.distinct = function(criteria, field, callback) {
+ this.op = 'distinct';
+ this._validate();
+
+ if (!callback) {
+ switch (typeof field) {
+ case 'function':
+ callback = field;
+ if ('string' == typeof criteria) {
+ field = criteria;
+ criteria = undefined;
+ }
+ break;
+ case 'undefined':
+ case 'string':
+ break;
+ default:
+ throw new TypeError('Invalid `field` argument. Must be string or function');
+ }
+
+ switch (typeof criteria) {
+ case 'function':
+ callback = criteria;
+ criteria = field = undefined;
+ break;
+ case 'string':
+ field = criteria;
+ criteria = undefined;
+ break;
+ }
+ }
+
+ if ('string' == typeof field) {
+ this._distinct = field;
+ }
+
+ if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ }
+
+ if (!callback) {
+ return this;
+ }
+
+ if (!this._distinct) {
+ throw new Error('No value for `distinct` has been declared');
+ }
+
+ var conds = this._conditions,
+ options = this._optionsForExec();
+
+ debug('distinct', this._collection.collectionName, conds, options);
+ callback = this._wrapCallback('distinct', callback, {
+ conditions: conds,
+ options: options
+ });
+
+ this._collection.distinct(this._distinct, conds, options, utils.tick(callback));
+
+ return this;
+};
+
+/**
+ * Declare and/or execute this query as an update() operation. By default,
+ * `update()` only modifies the _first_ document that matches `criteria`.
+ *
+ * _All paths passed that are not $atomic operations will become $set ops._
+ *
+ * ####Example
+ *
+ * mquery({ _id: id }).update({ title: 'words' }, ...)
+ *
+ * becomes
+ *
+ * collection.update({ _id: id }, { $set: { title: 'words' }}, ...)
+ *
+ * ####Note
+ *
+ * Passing an empty object `{}` as the doc will result in a no-op unless the `overwrite` option is passed. Without the `overwrite` option set, the update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting documents in the collection.
+ *
+ * ####Note
+ *
+ * The operation is only executed when a callback is passed. To force execution without a callback (which would be an unsafe write), we must first call update() and then execute it by using the `exec()` method.
+ *
+ * var q = mquery(collection).where({ _id: id });
+ * q.update({ $set: { name: 'bob' }}).update(); // not executed
+ *
+ * var q = mquery(collection).where({ _id: id });
+ * q.update({ $set: { name: 'bob' }}).exec(); // executed as unsafe
+ *
+ * // keys that are not $atomic ops become $set.
+ * // this executes the same command as the previous example.
+ * q.update({ name: 'bob' }).where({ _id: id }).exec();
+ *
+ * var q = mquery(collection).update(); // not executed
+ *
+ * // overwriting with empty docs
+ * var q.where({ _id: id }).setOptions({ overwrite: true })
+ * q.update({ }, callback); // executes
+ *
+ * // multi update with overwrite to empty doc
+ * var q = mquery(collection).where({ _id: id });
+ * q.setOptions({ multi: true, overwrite: true })
+ * q.update({ });
+ * q.update(callback); // executed
+ *
+ * // multi updates
+ * mquery()
+ * .collection(coll)
+ * .update({ name: /^match/ }, { $set: { arr: [] }}, { multi: true }, callback)
+ * // more multi updates
+ * mquery({ })
+ * .collection(coll)
+ * .setOptions({ multi: true })
+ * .update({ $set: { arr: [] }}, callback)
+ *
+ * // single update by default
+ * mquery({ email: 'address@example.com' })
+ * .collection(coll)
+ * .update({ $inc: { counter: 1 }}, callback)
+ *
+ * // summary
+ * update(criteria, doc, opts, cb) // executes
+ * update(criteria, doc, opts)
+ * update(criteria, doc, cb) // executes
+ * update(criteria, doc)
+ * update(doc, cb) // executes
+ * update(doc)
+ * update(cb) // executes
+ * update(true) // executes (unsafe write)
+ * update()
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.update = function update(criteria, doc, options, callback) {
+ var force;
+
+ switch (arguments.length) {
+ case 3:
+ if ('function' == typeof options) {
+ callback = options;
+ options = undefined;
+ }
+ break;
+ case 2:
+ if ('function' == typeof doc) {
+ callback = doc;
+ doc = criteria;
+ criteria = undefined;
+ }
+ break;
+ case 1:
+ switch (typeof criteria) {
+ case 'function':
+ callback = criteria;
+ criteria = options = doc = undefined;
+ break;
+ case 'boolean':
+ // execution with no callback (unsafe write)
+ force = criteria;
+ criteria = undefined;
+ break;
+ default:
+ doc = criteria;
+ criteria = options = undefined;
+ break;
+ }
+ }
+
+ return _update(this, 'update', criteria, doc, options, force, callback);
+};
+
+/**
+ * Declare and/or execute this query as an `updateMany()` operation. Identical
+ * to `update()` except `updateMany()` will update _all_ documents that match
+ * `criteria`, rather than just the first one.
+ *
+ * _All paths passed that are not $atomic operations will become $set ops._
+ *
+ * ####Example
+ *
+ * // Update every document whose `title` contains 'test'
+ * mquery().updateMany({ title: /test/ }, { year: 2017 })
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.updateMany = function updateMany(criteria, doc, options, callback) {
+ var force;
+
+ switch (arguments.length) {
+ case 3:
+ if ('function' == typeof options) {
+ callback = options;
+ options = undefined;
+ }
+ break;
+ case 2:
+ if ('function' == typeof doc) {
+ callback = doc;
+ doc = criteria;
+ criteria = undefined;
+ }
+ break;
+ case 1:
+ switch (typeof criteria) {
+ case 'function':
+ callback = criteria;
+ criteria = options = doc = undefined;
+ break;
+ case 'boolean':
+ // execution with no callback (unsafe write)
+ force = criteria;
+ criteria = undefined;
+ break;
+ default:
+ doc = criteria;
+ criteria = options = undefined;
+ break;
+ }
+ }
+
+ return _update(this, 'updateMany', criteria, doc, options, force, callback);
+};
+
+/**
+ * Declare and/or execute this query as an `updateOne()` operation. Identical
+ * to `update()` except `updateOne()` will _always_ update just one document,
+ * regardless of the `multi` option.
+ *
+ * _All paths passed that are not $atomic operations will become $set ops._
+ *
+ * ####Example
+ *
+ * // Update the first document whose `title` contains 'test'
+ * mquery().updateMany({ title: /test/ }, { year: 2017 })
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.updateOne = function updateOne(criteria, doc, options, callback) {
+ var force;
+
+ switch (arguments.length) {
+ case 3:
+ if ('function' == typeof options) {
+ callback = options;
+ options = undefined;
+ }
+ break;
+ case 2:
+ if ('function' == typeof doc) {
+ callback = doc;
+ doc = criteria;
+ criteria = undefined;
+ }
+ break;
+ case 1:
+ switch (typeof criteria) {
+ case 'function':
+ callback = criteria;
+ criteria = options = doc = undefined;
+ break;
+ case 'boolean':
+ // execution with no callback (unsafe write)
+ force = criteria;
+ criteria = undefined;
+ break;
+ default:
+ doc = criteria;
+ criteria = options = undefined;
+ break;
+ }
+ }
+
+ return _update(this, 'updateOne', criteria, doc, options, force, callback);
+};
+
+/**
+ * Declare and/or execute this query as an `replaceOne()` operation. Similar
+ * to `updateOne()`, except `replaceOne()` is not allowed to use atomic
+ * modifiers (`$set`, `$push`, etc.). Calling `replaceOne()` will always
+ * replace the existing doc.
+ *
+ * ####Example
+ *
+ * // Replace the document with `_id` 1 with `{ _id: 1, year: 2017 }`
+ * mquery().replaceOne({ _id: 1 }, { year: 2017 })
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.replaceOne = function replaceOne(criteria, doc, options, callback) {
+ var force;
+
+ switch (arguments.length) {
+ case 3:
+ if ('function' == typeof options) {
+ callback = options;
+ options = undefined;
+ }
+ break;
+ case 2:
+ if ('function' == typeof doc) {
+ callback = doc;
+ doc = criteria;
+ criteria = undefined;
+ }
+ break;
+ case 1:
+ switch (typeof criteria) {
+ case 'function':
+ callback = criteria;
+ criteria = options = doc = undefined;
+ break;
+ case 'boolean':
+ // execution with no callback (unsafe write)
+ force = criteria;
+ criteria = undefined;
+ break;
+ default:
+ doc = criteria;
+ criteria = options = undefined;
+ break;
+ }
+ }
+
+ this.setOptions({ overwrite: true });
+ return _update(this, 'replaceOne', criteria, doc, options, force, callback);
+};
+
+
+/*!
+ * Internal helper for update, updateMany, updateOne
+ */
+
+function _update(query, op, criteria, doc, options, force, callback) {
+ query.op = op;
+
+ if (Query.canMerge(criteria)) {
+ query.merge(criteria);
+ }
+
+ if (doc) {
+ query._mergeUpdate(doc);
+ }
+
+ if (utils.isObject(options)) {
+ // { overwrite: true }
+ query.setOptions(options);
+ }
+
+ // we are done if we don't have callback and they are
+ // not forcing an unsafe write.
+ if (!(force || callback)) {
+ return query;
+ }
+
+ if (!query._update ||
+ !query.options.overwrite && 0 === utils.keys(query._update).length) {
+ callback && utils.soon(callback.bind(null, null, 0));
+ return query;
+ }
+
+ options = query._optionsForExec();
+ if (!callback) options.safe = false;
+
+ criteria = query._conditions;
+ doc = query._updateForExec();
+
+ debug('update', query._collection.collectionName, criteria, doc, options);
+ callback = query._wrapCallback(op, callback, {
+ conditions: criteria,
+ doc: doc,
+ options: options
+ });
+
+ query._collection[op](criteria, doc, options, utils.tick(callback));
+
+ return query;
+}
+
+/**
+ * Declare and/or execute this query as a remove() operation.
+ *
+ * ####Example
+ *
+ * mquery(collection).remove({ artist: 'Anne Murray' }, callback)
+ *
+ * ####Note
+ *
+ * The operation is only executed when a callback is passed. To force execution without a callback (which would be an unsafe write), we must first call remove() and then execute it by using the `exec()` method.
+ *
+ * // not executed
+ * var query = mquery(collection).remove({ name: 'Anne Murray' })
+ *
+ * // executed
+ * mquery(collection).remove({ name: 'Anne Murray' }, callback)
+ * mquery(collection).remove({ name: 'Anne Murray' }).remove(callback)
+ *
+ * // executed without a callback (unsafe write)
+ * query.exec()
+ *
+ * // summary
+ * query.remove(conds, fn); // executes
+ * query.remove(conds)
+ * query.remove(fn) // executes
+ * query.remove()
+ *
+ * @param {Object|Query} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.remove = function(criteria, callback) {
+ this.op = 'remove';
+ var force;
+
+ if ('function' === typeof criteria) {
+ callback = criteria;
+ criteria = undefined;
+ } else if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ } else if (true === criteria) {
+ force = criteria;
+ criteria = undefined;
+ }
+
+ if (!(force || callback))
+ return this;
+
+ var options = this._optionsForExec();
+ if (!callback) options.safe = false;
+
+ var conds = this._conditions;
+
+ debug('remove', this._collection.collectionName, conds, options);
+ callback = this._wrapCallback('remove', callback, {
+ conditions: conds,
+ options: options
+ });
+
+ this._collection.remove(conds, options, utils.tick(callback));
+
+ return this;
+};
+
+/**
+ * Declare and/or execute this query as a `deleteOne()` operation. Behaves like
+ * `remove()`, except for ignores the `justOne` option and always deletes at
+ * most one document.
+ *
+ * ####Example
+ *
+ * mquery(collection).deleteOne({ artist: 'Anne Murray' }, callback)
+ *
+ * @param {Object|Query} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.deleteOne = function(criteria, callback) {
+ this.op = 'deleteOne';
+ var force;
+
+ if ('function' === typeof criteria) {
+ callback = criteria;
+ criteria = undefined;
+ } else if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ } else if (true === criteria) {
+ force = criteria;
+ criteria = undefined;
+ }
+
+ if (!(force || callback))
+ return this;
+
+ var options = this._optionsForExec();
+ if (!callback) options.safe = false;
+ delete options.justOne;
+
+ var conds = this._conditions;
+
+ debug('deleteOne', this._collection.collectionName, conds, options);
+ callback = this._wrapCallback('deleteOne', callback, {
+ conditions: conds,
+ options: options
+ });
+
+ this._collection.deleteOne(conds, options, utils.tick(callback));
+
+ return this;
+};
+
+/**
+ * Declare and/or execute this query as a `deleteMany()` operation. Behaves like
+ * `remove()`, except for ignores the `justOne` option and always deletes
+ * _every_ document that matches `criteria`.
+ *
+ * ####Example
+ *
+ * mquery(collection).deleteMany({ artist: 'Anne Murray' }, callback)
+ *
+ * @param {Object|Query} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.deleteMany = function(criteria, callback) {
+ this.op = 'deleteMany';
+ var force;
+
+ if ('function' === typeof criteria) {
+ callback = criteria;
+ criteria = undefined;
+ } else if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ } else if (true === criteria) {
+ force = criteria;
+ criteria = undefined;
+ }
+
+ if (!(force || callback))
+ return this;
+
+ var options = this._optionsForExec();
+ if (!callback) options.safe = false;
+ delete options.justOne;
+
+ var conds = this._conditions;
+
+ debug('deleteOne', this._collection.collectionName, conds, options);
+ callback = this._wrapCallback('deleteOne', callback, {
+ conditions: conds,
+ options: options
+ });
+
+ this._collection.deleteMany(conds, options, utils.tick(callback));
+
+ return this;
+};
+
+/**
+ * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) update command.
+ *
+ * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed.
+ *
+ * ####Available options
+ *
+ * - `new`: bool - true to return the modified document rather than the original. defaults to true
+ * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ *
+ * ####Examples
+ *
+ * query.findOneAndUpdate(conditions, update, options, callback) // executes
+ * query.findOneAndUpdate(conditions, update, options) // returns Query
+ * query.findOneAndUpdate(conditions, update, callback) // executes
+ * query.findOneAndUpdate(conditions, update) // returns Query
+ * query.findOneAndUpdate(update, callback) // returns Query
+ * query.findOneAndUpdate(update) // returns Query
+ * query.findOneAndUpdate(callback) // executes
+ * query.findOneAndUpdate() // returns Query
+ *
+ * @param {Object|Query} [query]
+ * @param {Object} [doc]
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.findOneAndUpdate = function(criteria, doc, options, callback) {
+ this.op = 'findOneAndUpdate';
+ this._validate();
+
+ switch (arguments.length) {
+ case 3:
+ if ('function' == typeof options) {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 2:
+ if ('function' == typeof doc) {
+ callback = doc;
+ doc = criteria;
+ criteria = undefined;
+ }
+ options = undefined;
+ break;
+ case 1:
+ if ('function' == typeof criteria) {
+ callback = criteria;
+ criteria = options = doc = undefined;
+ } else {
+ doc = criteria;
+ criteria = options = undefined;
+ }
+ }
+
+ if (Query.canMerge(criteria)) {
+ this.merge(criteria);
+ }
+
+ // apply doc
+ if (doc) {
+ this._mergeUpdate(doc);
+ }
+
+ options && this.setOptions(options);
+
+ if (!callback) return this;
+ return this._findAndModify('update', callback);
+};
+
+/**
+ * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command.
+ *
+ * Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if `callback` is passed.
+ *
+ * ####Available options
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ *
+ * ####Examples
+ *
+ * A.where().findOneAndRemove(conditions, options, callback) // executes
+ * A.where().findOneAndRemove(conditions, options) // return Query
+ * A.where().findOneAndRemove(conditions, callback) // executes
+ * A.where().findOneAndRemove(conditions) // returns Query
+ * A.where().findOneAndRemove(callback) // executes
+ * A.where().findOneAndRemove() // returns Query
+ * A.where().findOneAndDelete() // alias of .findOneAndRemove()
+ *
+ * @param {Object} [conditions]
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @api public
+ */
+
+Query.prototype.findOneAndRemove = Query.prototype.findOneAndDelete = function(conditions, options, callback) {
+ this.op = 'findOneAndRemove';
+ this._validate();
+
+ if ('function' == typeof options) {
+ callback = options;
+ options = undefined;
+ } else if ('function' == typeof conditions) {
+ callback = conditions;
+ conditions = undefined;
+ }
+
+ // apply conditions
+ if (Query.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ // apply options
+ options && this.setOptions(options);
+
+ if (!callback) return this;
+
+ return this._findAndModify('remove', callback);
+};
+
+/**
+ * _findAndModify
+ *
+ * @param {String} type - either "remove" or "update"
+ * @param {Function} callback
+ * @api private
+ */
+
+Query.prototype._findAndModify = function(type, callback) {
+ assert.equal('function', typeof callback);
+
+ var options = this._optionsForExec();
+ var fields;
+ var doc;
+
+ if ('remove' == type) {
+ options.remove = true;
+ } else {
+ if (!('new' in options)) options.new = true;
+ if (!('upsert' in options)) options.upsert = false;
+
+ doc = this._updateForExec();
+ if (!doc) {
+ if (options.upsert) {
+ // still need to do the upsert to empty doc
+ doc = { $set: {} };
+ } else {
+ return this.findOne(callback);
+ }
+ }
+ }
+
+ fields = this._fieldsForExec();
+ if (fields != null) {
+ if (this.$useProjection) {
+ options.projection = this._fieldsForExec();
+ } else {
+ options.fields = this._fieldsForExec();
+ }
+ }
+
+ var conds = this._conditions;
+
+ debug('findAndModify', this._collection.collectionName, conds, doc, options);
+ callback = this._wrapCallback('findAndModify', callback, {
+ conditions: conds,
+ doc: doc,
+ options: options
+ });
+
+ this._collection.findAndModify(conds, doc, options, utils.tick(callback));
+
+ return this;
+};
+
+/**
+ * Wrap callback to add tracing
+ *
+ * @param {Function} callback
+ * @param {Object} [queryInfo]
+ * @api private
+ */
+Query.prototype._wrapCallback = function(method, callback, queryInfo) {
+ var traceFunction = this._traceFunction || Query.traceFunction;
+
+ if (traceFunction) {
+ queryInfo.collectionName = this._collection.collectionName;
+
+ var traceCallback = traceFunction &&
+ traceFunction.call(null, method, queryInfo, this);
+
+ var startTime = new Date().getTime();
+
+ return function wrapperCallback(err, result) {
+ if (traceCallback) {
+ var millis = new Date().getTime() - startTime;
+ traceCallback.call(null, err, result, millis);
+ }
+
+ if (callback) {
+ callback.apply(null, arguments);
+ }
+ };
+ }
+
+ return callback;
+};
+
+/**
+ * Add trace function that gets called when the query is executed.
+ * The function will be called with (method, queryInfo, query) and
+ * should return a callback function which will be called
+ * with (err, result, millis) when the query is complete.
+ *
+ * queryInfo is an object containing: {
+ * collectionName: ,
+ * conditions: ,
+ * options: ,
+ * doc: [document to update, if applicable]
+ * }
+ *
+ * NOTE: Does not trace stream queries.
+ *
+ * @param {Function} traceFunction
+ * @return {Query} this
+ * @api public
+ */
+Query.prototype.setTraceFunction = function(traceFunction) {
+ this._traceFunction = traceFunction;
+ return this;
+};
+
+/**
+ * Executes the query
+ *
+ * ####Examples
+ *
+ * query.exec();
+ * query.exec(callback);
+ * query.exec('update');
+ * query.exec('find', callback);
+ *
+ * @param {String|Function} [operation]
+ * @param {Function} [callback]
+ * @api public
+ */
+
+Query.prototype.exec = function exec(op, callback) {
+ switch (typeof op) {
+ case 'function':
+ callback = op;
+ op = null;
+ break;
+ case 'string':
+ this.op = op;
+ break;
+ }
+
+ assert.ok(this.op, 'Missing query type: (find, update, etc)');
+
+ if ('update' == this.op || 'remove' == this.op) {
+ callback || (callback = true);
+ }
+
+ var _this = this;
+
+ if ('function' == typeof callback) {
+ this[this.op](callback);
+ } else {
+ return new Query.Promise(function(success, error) {
+ _this[_this.op](function(err, val) {
+ if (err) error(err);
+ else success(val);
+ success = error = null;
+ });
+ });
+ }
+};
+
+/**
+ * Returns a thunk which when called runs this.exec()
+ *
+ * The thunk receives a callback function which will be
+ * passed to `this.exec()`
+ *
+ * @return {Function}
+ * @api public
+ */
+
+Query.prototype.thunk = function() {
+ var _this = this;
+ return function(cb) {
+ _this.exec(cb);
+ };
+};
+
+/**
+ * Executes the query returning a `Promise` which will be
+ * resolved with either the doc(s) or rejected with the error.
+ *
+ * @param {Function} [resolve]
+ * @param {Function} [reject]
+ * @return {Promise}
+ * @api public
+ */
+
+Query.prototype.then = function(resolve, reject) {
+ var _this = this;
+ var promise = new Query.Promise(function(success, error) {
+ _this.exec(function(err, val) {
+ if (err) error(err);
+ else success(val);
+ success = error = null;
+ });
+ });
+ return promise.then(resolve, reject);
+};
+
+/**
+ * Returns a stream for the given find query.
+ *
+ * @throws Error if operation is not a find
+ * @returns {Stream} Node 0.8 style
+ */
+
+Query.prototype.stream = function(streamOptions) {
+ if ('find' != this.op)
+ throw new Error('stream() is only available for find');
+
+ var conds = this._conditions;
+
+ var options = this._optionsForExec();
+ if (this.$useProjection) {
+ options.projection = this._fieldsForExec();
+ } else {
+ options.fields = this._fieldsForExec();
+ }
+
+ debug('stream', this._collection.collectionName, conds, options, streamOptions);
+
+ return this._collection.findStream(conds, options, streamOptions);
+};
+
+/**
+ * Determines if field selection has been made.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+Query.prototype.selected = function selected() {
+ return !!(this._fields && Object.keys(this._fields).length > 0);
+};
+
+/**
+ * Determines if inclusive field selection has been made.
+ *
+ * query.selectedInclusively() // false
+ * query.select('name')
+ * query.selectedInclusively() // true
+ * query.selectedExlusively() // false
+ *
+ * @returns {Boolean}
+ */
+
+Query.prototype.selectedInclusively = function selectedInclusively() {
+ if (!this._fields) return false;
+
+ var keys = Object.keys(this._fields);
+ if (0 === keys.length) return false;
+
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (0 === this._fields[key]) return false;
+ if (this._fields[key] &&
+ typeof this._fields[key] === 'object' &&
+ this._fields[key].$meta) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+/**
+ * Determines if exclusive field selection has been made.
+ *
+ * query.selectedExlusively() // false
+ * query.select('-name')
+ * query.selectedExlusively() // true
+ * query.selectedInclusively() // false
+ *
+ * @returns {Boolean}
+ */
+
+Query.prototype.selectedExclusively = function selectedExclusively() {
+ if (!this._fields) return false;
+
+ var keys = Object.keys(this._fields);
+ if (0 === keys.length) return false;
+
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (0 === this._fields[key]) return true;
+ }
+
+ return false;
+};
+
+/**
+ * Merges `doc` with the current update object.
+ *
+ * @param {Object} doc
+ */
+
+Query.prototype._mergeUpdate = function(doc) {
+ if (!this._update) this._update = {};
+ if (doc instanceof Query) {
+ if (doc._update) {
+ utils.mergeClone(this._update, doc._update);
+ }
+ } else {
+ utils.mergeClone(this._update, doc);
+ }
+};
+
+/**
+ * Returns default options.
+ *
+ * @return {Object}
+ * @api private
+ */
+
+Query.prototype._optionsForExec = function() {
+ var options = utils.clone(this.options);
+ return options;
+};
+
+/**
+ * Returns fields selection for this query.
+ *
+ * @return {Object}
+ * @api private
+ */
+
+Query.prototype._fieldsForExec = function() {
+ return utils.clone(this._fields);
+};
+
+/**
+ * Return an update document with corrected $set operations.
+ *
+ * @api private
+ */
+
+Query.prototype._updateForExec = function() {
+ var update = utils.clone(this._update),
+ ops = utils.keys(update),
+ i = ops.length,
+ ret = {};
+
+ while (i--) {
+ var op = ops[i];
+
+ if (this.options.overwrite) {
+ ret[op] = update[op];
+ continue;
+ }
+
+ if ('$' !== op[0]) {
+ // fix up $set sugar
+ if (!ret.$set) {
+ if (update.$set) {
+ ret.$set = update.$set;
+ } else {
+ ret.$set = {};
+ }
+ }
+ ret.$set[op] = update[op];
+ ops.splice(i, 1);
+ if (!~ops.indexOf('$set')) ops.push('$set');
+ } else if ('$set' === op) {
+ if (!ret.$set) {
+ ret[op] = update[op];
+ }
+ } else {
+ ret[op] = update[op];
+ }
+ }
+
+ this._compiledUpdate = ret;
+ return ret;
+};
+
+/**
+ * Make sure _path is set.
+ *
+ * @parmam {String} method
+ */
+
+Query.prototype._ensurePath = function(method) {
+ if (!this._path) {
+ var msg = method + '() must be used after where() '
+ + 'when called with these arguments';
+ throw new Error(msg);
+ }
+};
+
+/*!
+ * Permissions
+ */
+
+Query.permissions = require('./permissions');
+
+Query._isPermitted = function(a, b) {
+ var denied = Query.permissions[b];
+ if (!denied) return true;
+ return true !== denied[a];
+};
+
+Query.prototype._validate = function(action) {
+ var fail;
+ var validator;
+
+ if (undefined === action) {
+
+ validator = Query.permissions[this.op];
+ if ('function' != typeof validator) return true;
+
+ fail = validator(this);
+
+ } else if (!Query._isPermitted(action, this.op)) {
+ fail = action;
+ }
+
+ if (fail) {
+ throw new Error(fail + ' cannot be used with ' + this.op);
+ }
+};
+
+/**
+ * Determines if `conds` can be merged using `mquery().merge()`
+ *
+ * @param {Object} conds
+ * @return {Boolean}
+ */
+
+Query.canMerge = function(conds) {
+ return conds instanceof Query || utils.isObject(conds);
+};
+
+/**
+ * Set a trace function that will get called whenever a
+ * query is executed.
+ *
+ * See `setTraceFunction()` for details.
+ *
+ * @param {Object} conds
+ * @return {Boolean}
+ */
+Query.setGlobalTraceFunction = function(traceFunction) {
+ Query.traceFunction = traceFunction;
+};
+
+/*!
+ * Exports.
+ */
+
+Query.utils = utils;
+Query.env = require('./env');
+Query.Collection = require('./collection');
+Query.BaseCollection = require('./collection/collection');
+Query.Promise = require('bluebird');
+module.exports = exports = Query;
+
+// TODO
+// test utils
diff --git a/node_modules/mquery/lib/permissions.js b/node_modules/mquery/lib/permissions.js
new file mode 100644
index 0000000..c306f3a
--- /dev/null
+++ b/node_modules/mquery/lib/permissions.js
@@ -0,0 +1,88 @@
+'use strict';
+
+var denied = exports;
+
+denied.distinct = function(self) {
+ if (self._fields && Object.keys(self._fields).length > 0) {
+ return 'field selection and slice';
+ }
+
+ var keys = Object.keys(denied.distinct);
+ var err;
+
+ keys.every(function(option) {
+ if (self.options[option]) {
+ err = option;
+ return false;
+ }
+ return true;
+ });
+
+ return err;
+};
+denied.distinct.select =
+denied.distinct.slice =
+denied.distinct.sort =
+denied.distinct.limit =
+denied.distinct.skip =
+denied.distinct.batchSize =
+denied.distinct.comment =
+denied.distinct.maxScan =
+denied.distinct.snapshot =
+denied.distinct.hint =
+denied.distinct.tailable = true;
+
+
+// aggregation integration
+
+
+denied.findOneAndUpdate =
+denied.findOneAndRemove = function(self) {
+ var keys = Object.keys(denied.findOneAndUpdate);
+ var err;
+
+ keys.every(function(option) {
+ if (self.options[option]) {
+ err = option;
+ return false;
+ }
+ return true;
+ });
+
+ return err;
+};
+denied.findOneAndUpdate.limit =
+denied.findOneAndUpdate.skip =
+denied.findOneAndUpdate.batchSize =
+denied.findOneAndUpdate.maxScan =
+denied.findOneAndUpdate.snapshot =
+denied.findOneAndUpdate.hint =
+denied.findOneAndUpdate.tailable =
+denied.findOneAndUpdate.comment = true;
+
+
+denied.count = function(self) {
+ if (self._fields && Object.keys(self._fields).length > 0) {
+ return 'field selection and slice';
+ }
+
+ var keys = Object.keys(denied.count);
+ var err;
+
+ keys.every(function(option) {
+ if (self.options[option]) {
+ err = option;
+ return false;
+ }
+ return true;
+ });
+
+ return err;
+};
+
+denied.count.slice =
+denied.count.batchSize =
+denied.count.comment =
+denied.count.maxScan =
+denied.count.snapshot =
+denied.count.tailable = true;
diff --git a/node_modules/mquery/lib/utils.js b/node_modules/mquery/lib/utils.js
new file mode 100644
index 0000000..ef51712
--- /dev/null
+++ b/node_modules/mquery/lib/utils.js
@@ -0,0 +1,356 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+var Buffer = require('safe-buffer').Buffer;
+var RegExpClone = require('regexp-clone');
+
+/**
+ * Clones objects
+ *
+ * @param {Object} obj the object to clone
+ * @param {Object} options
+ * @return {Object} the cloned object
+ * @api private
+ */
+
+var clone = exports.clone = function clone(obj, options) {
+ if (obj === undefined || obj === null)
+ return obj;
+
+ if (Array.isArray(obj))
+ return exports.cloneArray(obj, options);
+
+ if (obj.constructor) {
+ if (/ObjectI[dD]$/.test(obj.constructor.name)) {
+ return 'function' == typeof obj.clone
+ ? obj.clone()
+ : new obj.constructor(obj.id);
+ }
+
+ if (obj.constructor.name === 'ReadPreference') {
+ return new obj.constructor(obj.mode, clone(obj.tags, options));
+ }
+
+ if ('Binary' == obj._bsontype && obj.buffer && obj.value) {
+ return 'function' == typeof obj.clone
+ ? obj.clone()
+ : new obj.constructor(obj.value(true), obj.sub_type);
+ }
+
+ if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
+ return new obj.constructor(+obj);
+
+ if ('RegExp' === obj.constructor.name)
+ return RegExpClone(obj);
+
+ if ('Buffer' === obj.constructor.name)
+ return exports.cloneBuffer(obj);
+ }
+
+ if (isObject(obj))
+ return exports.cloneObject(obj, options);
+
+ if (obj.valueOf)
+ return obj.valueOf();
+};
+
+/*!
+ * ignore
+ */
+
+exports.cloneObject = function cloneObject(obj, options) {
+ var minimize = options && options.minimize;
+ var ret = {};
+ var hasKeys;
+ var val;
+ var k;
+
+ for (k in obj) {
+ val = clone(obj[k], options);
+
+ if (!minimize || ('undefined' !== typeof val)) {
+ hasKeys || (hasKeys = true);
+ ret[k] = val;
+ }
+ }
+
+ return minimize
+ ? hasKeys && ret
+ : ret;
+};
+
+exports.cloneArray = function cloneArray(arr, options) {
+ var ret = [];
+ for (var i = 0, l = arr.length; i < l; i++)
+ ret.push(clone(arr[i], options));
+ return ret;
+};
+
+/**
+ * process.nextTick helper.
+ *
+ * Wraps the given `callback` in a try/catch. If an error is
+ * caught it will be thrown on nextTick.
+ *
+ * node-mongodb-native had a habit of state corruption when
+ * an error was immediately thrown from within a collection
+ * method (find, update, etc) callback.
+ *
+ * @param {Function} [callback]
+ * @api private
+ */
+
+exports.tick = function tick(callback) {
+ if ('function' !== typeof callback) return;
+ return function() {
+ // callbacks should always be fired on the next
+ // turn of the event loop. A side benefit is
+ // errors thrown from executing the callback
+ // will not cause drivers state to be corrupted
+ // which has historically been a problem.
+ var args = arguments;
+ soon(function() {
+ callback.apply(this, args);
+ });
+ };
+};
+
+/**
+ * Merges `from` into `to` without overwriting existing properties.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ * @api private
+ */
+
+exports.merge = function merge(to, from) {
+ var keys = Object.keys(from),
+ i = keys.length,
+ key;
+
+ while (i--) {
+ key = keys[i];
+ if ('undefined' === typeof to[key]) {
+ to[key] = from[key];
+ } else {
+ if (exports.isObject(from[key])) {
+ merge(to[key], from[key]);
+ } else {
+ to[key] = from[key];
+ }
+ }
+ }
+};
+
+/**
+ * Same as merge but clones the assigned values.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ * @api private
+ */
+
+exports.mergeClone = function mergeClone(to, from) {
+ var keys = Object.keys(from),
+ i = keys.length,
+ key;
+
+ while (i--) {
+ key = keys[i];
+ if ('undefined' === typeof to[key]) {
+ to[key] = clone(from[key]);
+ } else {
+ if (exports.isObject(from[key])) {
+ mergeClone(to[key], from[key]);
+ } else {
+ to[key] = clone(from[key]);
+ }
+ }
+ }
+};
+
+/**
+ * Read pref helper (mongo 2.2 drivers support this)
+ *
+ * Allows using aliases instead of full preference names:
+ *
+ * p primary
+ * pp primaryPreferred
+ * s secondary
+ * sp secondaryPreferred
+ * n nearest
+ *
+ * @param {String} pref
+ */
+
+exports.readPref = function readPref(pref) {
+ switch (pref) {
+ case 'p':
+ pref = 'primary';
+ break;
+ case 'pp':
+ pref = 'primaryPreferred';
+ break;
+ case 's':
+ pref = 'secondary';
+ break;
+ case 'sp':
+ pref = 'secondaryPreferred';
+ break;
+ case 'n':
+ pref = 'nearest';
+ break;
+ }
+
+ return pref;
+};
+
+
+/**
+ * Read Concern helper (mongo 3.2 drivers support this)
+ *
+ * Allows using string to specify read concern level:
+ *
+ * local 3.2+
+ * available 3.6+
+ * majority 3.2+
+ * linearizable 3.4+
+ * snapshot 4.0+
+ *
+ * @param {String|Object} concern
+ */
+
+exports.readConcern = function readConcern(concern) {
+ if ('string' === typeof concern) {
+ switch (concern) {
+ case 'l':
+ concern = 'local';
+ break;
+ case 'a':
+ concern = 'available';
+ break;
+ case 'm':
+ concern = 'majority';
+ break;
+ case 'lz':
+ concern = 'linearizable';
+ break;
+ case 's':
+ concern = 'snapshot';
+ break;
+ }
+ concern = { level: concern };
+ }
+ return concern;
+};
+
+/**
+ * Object.prototype.toString.call helper
+ */
+
+var _toString = Object.prototype.toString;
+exports.toString = function(arg) {
+ return _toString.call(arg);
+};
+
+/**
+ * Determines if `arg` is an object.
+ *
+ * @param {Object|Array|String|Function|RegExp|any} arg
+ * @return {Boolean}
+ */
+
+var isObject = exports.isObject = function(arg) {
+ return '[object Object]' == exports.toString(arg);
+};
+
+/**
+ * Determines if `arg` is an array.
+ *
+ * @param {Object}
+ * @return {Boolean}
+ * @see nodejs utils
+ */
+
+exports.isArray = function(arg) {
+ return Array.isArray(arg) ||
+ 'object' == typeof arg && '[object Array]' == exports.toString(arg);
+};
+
+/**
+ * Object.keys helper
+ */
+
+exports.keys = Object.keys || function(obj) {
+ var keys = [];
+ for (var k in obj) if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ return keys;
+};
+
+/**
+ * Basic Object.create polyfill.
+ * Only one argument is supported.
+ *
+ * Based on https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
+ */
+
+exports.create = 'function' == typeof Object.create
+ ? Object.create
+ : create;
+
+function create(proto) {
+ if (arguments.length > 1) {
+ throw new Error('Adding properties is not supported');
+ }
+
+ function F() {}
+ F.prototype = proto;
+ return new F;
+}
+
+/**
+ * inheritance
+ */
+
+exports.inherits = function(ctor, superCtor) {
+ ctor.prototype = exports.create(superCtor.prototype);
+ ctor.prototype.constructor = ctor;
+};
+
+/**
+ * nextTick helper
+ * compat with node 0.10 which behaves differently than previous versions
+ */
+
+var soon = exports.soon = 'function' == typeof setImmediate
+ ? setImmediate
+ : process.nextTick;
+
+/**
+ * Clones the contents of a buffer.
+ *
+ * @param {Buffer} buff
+ * @return {Buffer}
+ */
+
+exports.cloneBuffer = function(buff) {
+ var dupe = Buffer.alloc(buff.length);
+ buff.copy(dupe, 0, 0, buff.length);
+ return dupe;
+};
+
+/**
+ * Check if this object is an arguments object
+ *
+ * @param {Any} v
+ * @return {Boolean}
+ */
+
+exports.isArgumentsObject = function(v) {
+ return Object.prototype.toString.call(v) === '[object Arguments]';
+};
diff --git a/node_modules/mquery/node_modules/debug/.coveralls.yml b/node_modules/mquery/node_modules/debug/.coveralls.yml
new file mode 100644
index 0000000..20a7068
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/.coveralls.yml
@@ -0,0 +1 @@
+repo_token: SIAeZjKYlHK74rbcFvNHMUzjRiMpflxve
diff --git a/node_modules/mquery/node_modules/debug/.eslintrc b/node_modules/mquery/node_modules/debug/.eslintrc
new file mode 100644
index 0000000..146371e
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/.eslintrc
@@ -0,0 +1,14 @@
+{
+ "env": {
+ "browser": true,
+ "node": true
+ },
+ "globals": {
+ "chrome": true
+ },
+ "rules": {
+ "no-console": 0,
+ "no-empty": [1, { "allowEmptyCatch": true }]
+ },
+ "extends": "eslint:recommended"
+}
diff --git a/node_modules/mquery/node_modules/debug/.npmignore b/node_modules/mquery/node_modules/debug/.npmignore
new file mode 100644
index 0000000..5f60eec
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/.npmignore
@@ -0,0 +1,9 @@
+support
+test
+examples
+example
+*.sock
+dist
+yarn.lock
+coverage
+bower.json
diff --git a/node_modules/mquery/node_modules/debug/.travis.yml b/node_modules/mquery/node_modules/debug/.travis.yml
new file mode 100644
index 0000000..a764300
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/.travis.yml
@@ -0,0 +1,20 @@
+sudo: false
+
+language: node_js
+
+node_js:
+ - "4"
+ - "6"
+ - "8"
+
+install:
+ - make install
+
+script:
+ - make lint
+ - make test
+
+matrix:
+ include:
+ - node_js: '8'
+ env: BROWSER=1
diff --git a/node_modules/mquery/node_modules/debug/CHANGELOG.md b/node_modules/mquery/node_modules/debug/CHANGELOG.md
new file mode 100644
index 0000000..820d21e
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/CHANGELOG.md
@@ -0,0 +1,395 @@
+
+3.1.0 / 2017-09-26
+==================
+
+ * Add `DEBUG_HIDE_DATE` env var (#486)
+ * Remove ReDoS regexp in %o formatter (#504)
+ * Remove "component" from package.json
+ * Remove `component.json`
+ * Ignore package-lock.json
+ * Examples: fix colors printout
+ * Fix: browser detection
+ * Fix: spelling mistake (#496, @EdwardBetts)
+
+3.0.1 / 2017-08-24
+==================
+
+ * Fix: Disable colors in Edge and Internet Explorer (#489)
+
+3.0.0 / 2017-08-08
+==================
+
+ * Breaking: Remove DEBUG_FD (#406)
+ * Breaking: Use `Date#toISOString()` instead to `Date#toUTCString()` when output is not a TTY (#418)
+ * Breaking: Make millisecond timer namespace specific and allow 'always enabled' output (#408)
+ * Addition: document `enabled` flag (#465)
+ * Addition: add 256 colors mode (#481)
+ * Addition: `enabled()` updates existing debug instances, add `destroy()` function (#440)
+ * Update: component: update "ms" to v2.0.0
+ * Update: separate the Node and Browser tests in Travis-CI
+ * Update: refactor Readme, fixed documentation, added "Namespace Colors" section, redid screenshots
+ * Update: separate Node.js and web browser examples for organization
+ * Update: update "browserify" to v14.4.0
+ * Fix: fix Readme typo (#473)
+
+2.6.9 / 2017-09-22
+==================
+
+ * remove ReDoS regexp in %o formatter (#504)
+
+2.6.8 / 2017-05-18
+==================
+
+ * Fix: Check for undefined on browser globals (#462, @marbemac)
+
+2.6.7 / 2017-05-16
+==================
+
+ * Fix: Update ms to 2.0.0 to fix regular expression denial of service vulnerability (#458, @hubdotcom)
+ * Fix: Inline extend function in node implementation (#452, @dougwilson)
+ * Docs: Fix typo (#455, @msasad)
+
+2.6.5 / 2017-04-27
+==================
+
+ * Fix: null reference check on window.documentElement.style.WebkitAppearance (#447, @thebigredgeek)
+ * Misc: clean up browser reference checks (#447, @thebigredgeek)
+ * Misc: add npm-debug.log to .gitignore (@thebigredgeek)
+
+
+2.6.4 / 2017-04-20
+==================
+
+ * Fix: bug that would occur if process.env.DEBUG is a non-string value. (#444, @LucianBuzzo)
+ * Chore: ignore bower.json in npm installations. (#437, @joaovieira)
+ * Misc: update "ms" to v0.7.3 (@tootallnate)
+
+2.6.3 / 2017-03-13
+==================
+
+ * Fix: Electron reference to `process.env.DEBUG` (#431, @paulcbetts)
+ * Docs: Changelog fix (@thebigredgeek)
+
+2.6.2 / 2017-03-10
+==================
+
+ * Fix: DEBUG_MAX_ARRAY_LENGTH (#420, @slavaGanzin)
+ * Docs: Add backers and sponsors from Open Collective (#422, @piamancini)
+ * Docs: Add Slackin invite badge (@tootallnate)
+
+2.6.1 / 2017-02-10
+==================
+
+ * Fix: Module's `export default` syntax fix for IE8 `Expected identifier` error
+ * Fix: Whitelist DEBUG_FD for values 1 and 2 only (#415, @pi0)
+ * Fix: IE8 "Expected identifier" error (#414, @vgoma)
+ * Fix: Namespaces would not disable once enabled (#409, @musikov)
+
+2.6.0 / 2016-12-28
+==================
+
+ * Fix: added better null pointer checks for browser useColors (@thebigredgeek)
+ * Improvement: removed explicit `window.debug` export (#404, @tootallnate)
+ * Improvement: deprecated `DEBUG_FD` environment variable (#405, @tootallnate)
+
+2.5.2 / 2016-12-25
+==================
+
+ * Fix: reference error on window within webworkers (#393, @KlausTrainer)
+ * Docs: fixed README typo (#391, @lurch)
+ * Docs: added notice about v3 api discussion (@thebigredgeek)
+
+2.5.1 / 2016-12-20
+==================
+
+ * Fix: babel-core compatibility
+
+2.5.0 / 2016-12-20
+==================
+
+ * Fix: wrong reference in bower file (@thebigredgeek)
+ * Fix: webworker compatibility (@thebigredgeek)
+ * Fix: output formatting issue (#388, @kribblo)
+ * Fix: babel-loader compatibility (#383, @escwald)
+ * Misc: removed built asset from repo and publications (@thebigredgeek)
+ * Misc: moved source files to /src (#378, @yamikuronue)
+ * Test: added karma integration and replaced babel with browserify for browser tests (#378, @yamikuronue)
+ * Test: coveralls integration (#378, @yamikuronue)
+ * Docs: simplified language in the opening paragraph (#373, @yamikuronue)
+
+2.4.5 / 2016-12-17
+==================
+
+ * Fix: `navigator` undefined in Rhino (#376, @jochenberger)
+ * Fix: custom log function (#379, @hsiliev)
+ * Improvement: bit of cleanup + linting fixes (@thebigredgeek)
+ * Improvement: rm non-maintainted `dist/` dir (#375, @freewil)
+ * Docs: simplified language in the opening paragraph. (#373, @yamikuronue)
+
+2.4.4 / 2016-12-14
+==================
+
+ * Fix: work around debug being loaded in preload scripts for electron (#368, @paulcbetts)
+
+2.4.3 / 2016-12-14
+==================
+
+ * Fix: navigation.userAgent error for react native (#364, @escwald)
+
+2.4.2 / 2016-12-14
+==================
+
+ * Fix: browser colors (#367, @tootallnate)
+ * Misc: travis ci integration (@thebigredgeek)
+ * Misc: added linting and testing boilerplate with sanity check (@thebigredgeek)
+
+2.4.1 / 2016-12-13
+==================
+
+ * Fix: typo that broke the package (#356)
+
+2.4.0 / 2016-12-13
+==================
+
+ * Fix: bower.json references unbuilt src entry point (#342, @justmatt)
+ * Fix: revert "handle regex special characters" (@tootallnate)
+ * Feature: configurable util.inspect()`options for NodeJS (#327, @tootallnate)
+ * Feature: %O`(big O) pretty-prints objects (#322, @tootallnate)
+ * Improvement: allow colors in workers (#335, @botverse)
+ * Improvement: use same color for same namespace. (#338, @lchenay)
+
+2.3.3 / 2016-11-09
+==================
+
+ * Fix: Catch `JSON.stringify()` errors (#195, Jovan Alleyne)
+ * Fix: Returning `localStorage` saved values (#331, Levi Thomason)
+ * Improvement: Don't create an empty object when no `process` (Nathan Rajlich)
+
+2.3.2 / 2016-11-09
+==================
+
+ * Fix: be super-safe in index.js as well (@TooTallNate)
+ * Fix: should check whether process exists (Tom Newby)
+
+2.3.1 / 2016-11-09
+==================
+
+ * Fix: Added electron compatibility (#324, @paulcbetts)
+ * Improvement: Added performance optimizations (@tootallnate)
+ * Readme: Corrected PowerShell environment variable example (#252, @gimre)
+ * Misc: Removed yarn lock file from source control (#321, @fengmk2)
+
+2.3.0 / 2016-11-07
+==================
+
+ * Fix: Consistent placement of ms diff at end of output (#215, @gorangajic)
+ * Fix: Escaping of regex special characters in namespace strings (#250, @zacronos)
+ * Fix: Fixed bug causing crash on react-native (#282, @vkarpov15)
+ * Feature: Enabled ES6+ compatible import via default export (#212 @bucaran)
+ * Feature: Added %O formatter to reflect Chrome's console.log capability (#279, @oncletom)
+ * Package: Update "ms" to 0.7.2 (#315, @DevSide)
+ * Package: removed superfluous version property from bower.json (#207 @kkirsche)
+ * Readme: fix USE_COLORS to DEBUG_COLORS
+ * Readme: Doc fixes for format string sugar (#269, @mlucool)
+ * Readme: Updated docs for DEBUG_FD and DEBUG_COLORS environment variables (#232, @mattlyons0)
+ * Readme: doc fixes for PowerShell (#271 #243, @exoticknight @unreadable)
+ * Readme: better docs for browser support (#224, @matthewmueller)
+ * Tooling: Added yarn integration for development (#317, @thebigredgeek)
+ * Misc: Renamed History.md to CHANGELOG.md (@thebigredgeek)
+ * Misc: Added license file (#226 #274, @CantemoInternal @sdaitzman)
+ * Misc: Updated contributors (@thebigredgeek)
+
+2.2.0 / 2015-05-09
+==================
+
+ * package: update "ms" to v0.7.1 (#202, @dougwilson)
+ * README: add logging to file example (#193, @DanielOchoa)
+ * README: fixed a typo (#191, @amir-s)
+ * browser: expose `storage` (#190, @stephenmathieson)
+ * Makefile: add a `distclean` target (#189, @stephenmathieson)
+
+2.1.3 / 2015-03-13
+==================
+
+ * Updated stdout/stderr example (#186)
+ * Updated example/stdout.js to match debug current behaviour
+ * Renamed example/stderr.js to stdout.js
+ * Update Readme.md (#184)
+ * replace high intensity foreground color for bold (#182, #183)
+
+2.1.2 / 2015-03-01
+==================
+
+ * dist: recompile
+ * update "ms" to v0.7.0
+ * package: update "browserify" to v9.0.3
+ * component: fix "ms.js" repo location
+ * changed bower package name
+ * updated documentation about using debug in a browser
+ * fix: security error on safari (#167, #168, @yields)
+
+2.1.1 / 2014-12-29
+==================
+
+ * browser: use `typeof` to check for `console` existence
+ * browser: check for `console.log` truthiness (fix IE 8/9)
+ * browser: add support for Chrome apps
+ * Readme: added Windows usage remarks
+ * Add `bower.json` to properly support bower install
+
+2.1.0 / 2014-10-15
+==================
+
+ * node: implement `DEBUG_FD` env variable support
+ * package: update "browserify" to v6.1.0
+ * package: add "license" field to package.json (#135, @panuhorsmalahti)
+
+2.0.0 / 2014-09-01
+==================
+
+ * package: update "browserify" to v5.11.0
+ * node: use stderr rather than stdout for logging (#29, @stephenmathieson)
+
+1.0.4 / 2014-07-15
+==================
+
+ * dist: recompile
+ * example: remove `console.info()` log usage
+ * example: add "Content-Type" UTF-8 header to browser example
+ * browser: place %c marker after the space character
+ * browser: reset the "content" color via `color: inherit`
+ * browser: add colors support for Firefox >= v31
+ * debug: prefer an instance `log()` function over the global one (#119)
+ * Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
+
+1.0.3 / 2014-07-09
+==================
+
+ * Add support for multiple wildcards in namespaces (#122, @seegno)
+ * browser: fix lint
+
+1.0.2 / 2014-06-10
+==================
+
+ * browser: update color palette (#113, @gscottolson)
+ * common: make console logging function configurable (#108, @timoxley)
+ * node: fix %o colors on old node <= 0.8.x
+ * Makefile: find node path using shell/which (#109, @timoxley)
+
+1.0.1 / 2014-06-06
+==================
+
+ * browser: use `removeItem()` to clear localStorage
+ * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
+ * package: add "contributors" section
+ * node: fix comment typo
+ * README: list authors
+
+1.0.0 / 2014-06-04
+==================
+
+ * make ms diff be global, not be scope
+ * debug: ignore empty strings in enable()
+ * node: make DEBUG_COLORS able to disable coloring
+ * *: export the `colors` array
+ * npmignore: don't publish the `dist` dir
+ * Makefile: refactor to use browserify
+ * package: add "browserify" as a dev dependency
+ * Readme: add Web Inspector Colors section
+ * node: reset terminal color for the debug content
+ * node: map "%o" to `util.inspect()`
+ * browser: map "%j" to `JSON.stringify()`
+ * debug: add custom "formatters"
+ * debug: use "ms" module for humanizing the diff
+ * Readme: add "bash" syntax highlighting
+ * browser: add Firebug color support
+ * browser: add colors for WebKit browsers
+ * node: apply log to `console`
+ * rewrite: abstract common logic for Node & browsers
+ * add .jshintrc file
+
+0.8.1 / 2014-04-14
+==================
+
+ * package: re-add the "component" section
+
+0.8.0 / 2014-03-30
+==================
+
+ * add `enable()` method for nodejs. Closes #27
+ * change from stderr to stdout
+ * remove unnecessary index.js file
+
+0.7.4 / 2013-11-13
+==================
+
+ * remove "browserify" key from package.json (fixes something in browserify)
+
+0.7.3 / 2013-10-30
+==================
+
+ * fix: catch localStorage security error when cookies are blocked (Chrome)
+ * add debug(err) support. Closes #46
+ * add .browser prop to package.json. Closes #42
+
+0.7.2 / 2013-02-06
+==================
+
+ * fix package.json
+ * fix: Mobile Safari (private mode) is broken with debug
+ * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
+
+0.7.1 / 2013-02-05
+==================
+
+ * add repository URL to package.json
+ * add DEBUG_COLORED to force colored output
+ * add browserify support
+ * fix component. Closes #24
+
+0.7.0 / 2012-05-04
+==================
+
+ * Added .component to package.json
+ * Added debug.component.js build
+
+0.6.0 / 2012-03-16
+==================
+
+ * Added support for "-" prefix in DEBUG [Vinay Pulim]
+ * Added `.enabled` flag to the node version [TooTallNate]
+
+0.5.0 / 2012-02-02
+==================
+
+ * Added: humanize diffs. Closes #8
+ * Added `debug.disable()` to the CS variant
+ * Removed padding. Closes #10
+ * Fixed: persist client-side variant again. Closes #9
+
+0.4.0 / 2012-02-01
+==================
+
+ * Added browser variant support for older browsers [TooTallNate]
+ * Added `debug.enable('project:*')` to browser variant [TooTallNate]
+ * Added padding to diff (moved it to the right)
+
+0.3.0 / 2012-01-26
+==================
+
+ * Added millisecond diff when isatty, otherwise UTC string
+
+0.2.0 / 2012-01-22
+==================
+
+ * Added wildcard support
+
+0.1.0 / 2011-12-02
+==================
+
+ * Added: remove colors unless stderr isatty [TooTallNate]
+
+0.0.1 / 2010-01-03
+==================
+
+ * Initial release
diff --git a/node_modules/mquery/node_modules/debug/LICENSE b/node_modules/mquery/node_modules/debug/LICENSE
new file mode 100644
index 0000000..658c933
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/LICENSE
@@ -0,0 +1,19 @@
+(The MIT License)
+
+Copyright (c) 2014 TJ Holowaychuk
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the 'Software'), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/node_modules/mquery/node_modules/debug/Makefile b/node_modules/mquery/node_modules/debug/Makefile
new file mode 100644
index 0000000..3ddd136
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/Makefile
@@ -0,0 +1,58 @@
+# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
+THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
+THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
+
+# BIN directory
+BIN := $(THIS_DIR)/node_modules/.bin
+
+# Path
+PATH := node_modules/.bin:$(PATH)
+SHELL := /bin/bash
+
+# applications
+NODE ?= $(shell which node)
+YARN ?= $(shell which yarn)
+PKG ?= $(if $(YARN),$(YARN),$(NODE) $(shell which npm))
+BROWSERIFY ?= $(NODE) $(BIN)/browserify
+
+install: node_modules
+
+browser: dist/debug.js
+
+node_modules: package.json
+ @NODE_ENV= $(PKG) install
+ @touch node_modules
+
+dist/debug.js: src/*.js node_modules
+ @mkdir -p dist
+ @$(BROWSERIFY) \
+ --standalone debug \
+ . > dist/debug.js
+
+lint:
+ @eslint *.js src/*.js
+
+test-node:
+ @istanbul cover node_modules/mocha/bin/_mocha -- test/**.js
+ @cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
+
+test-browser:
+ @$(MAKE) browser
+ @karma start --single-run
+
+test-all:
+ @concurrently \
+ "make test-node" \
+ "make test-browser"
+
+test:
+ @if [ "x$(BROWSER)" = "x" ]; then \
+ $(MAKE) test-node; \
+ else \
+ $(MAKE) test-browser; \
+ fi
+
+clean:
+ rimraf dist coverage
+
+.PHONY: browser install clean lint test test-all test-node test-browser
diff --git a/node_modules/mquery/node_modules/debug/README.md b/node_modules/mquery/node_modules/debug/README.md
new file mode 100644
index 0000000..8e754d1
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/README.md
@@ -0,0 +1,368 @@
+# debug
+[](https://travis-ci.org/visionmedia/debug) [](https://coveralls.io/github/visionmedia/debug?branch=master) [](https://visionmedia-community-slackin.now.sh/) [](#backers)
+[](#sponsors)
+
+
+
+A tiny JavaScript debugging utility modelled after Node.js core's debugging
+technique. Works in Node.js and web browsers.
+
+## Installation
+
+```bash
+$ npm install debug
+```
+
+## Usage
+
+`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
+
+Example [_app.js_](./examples/node/app.js):
+
+```js
+var debug = require('debug')('http')
+ , http = require('http')
+ , name = 'My App';
+
+// fake app
+
+debug('booting %o', name);
+
+http.createServer(function(req, res){
+ debug(req.method + ' ' + req.url);
+ res.end('hello\n');
+}).listen(3000, function(){
+ debug('listening');
+});
+
+// fake worker of some kind
+
+require('./worker');
+```
+
+Example [_worker.js_](./examples/node/worker.js):
+
+```js
+var a = require('debug')('worker:a')
+ , b = require('debug')('worker:b');
+
+function work() {
+ a('doing lots of uninteresting work');
+ setTimeout(work, Math.random() * 1000);
+}
+
+work();
+
+function workb() {
+ b('doing some work');
+ setTimeout(workb, Math.random() * 2000);
+}
+
+workb();
+```
+
+The `DEBUG` environment variable is then used to enable these based on space or
+comma-delimited names.
+
+Here are some examples:
+
+
+
+
+
+#### Windows note
+
+On Windows the environment variable is set using the `set` command.
+
+```cmd
+set DEBUG=*,-not_this
+```
+
+Note that PowerShell uses different syntax to set environment variables.
+
+```cmd
+$env:DEBUG = "*,-not_this"
+```
+
+Then, run the program to be debugged as usual.
+
+
+## Namespace Colors
+
+Every debug instance has a color generated for it based on its namespace name.
+This helps when visually parsing the debug output to identify which debug instance
+a debug line belongs to.
+
+#### Node.js
+
+In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
+the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
+otherwise debug will only use a small handful of basic colors.
+
+
+
+#### Web Browser
+
+Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
+option. These are WebKit web inspectors, Firefox ([since version
+31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
+and the Firebug plugin for Firefox (any version).
+
+
+
+
+## Millisecond diff
+
+When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
+
+
+
+When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
+
+
+
+
+## Conventions
+
+If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.
+
+## Wildcards
+
+The `*` character may be used as a wildcard. Suppose for example your library has
+debuggers named "connect:bodyParser", "connect:compress", "connect:session",
+instead of listing all three with
+`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
+`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
+
+You can also exclude specific debuggers by prefixing them with a "-" character.
+For example, `DEBUG=*,-connect:*` would include all debuggers except those
+starting with "connect:".
+
+## Environment Variables
+
+When running through Node.js, you can set a few environment variables that will
+change the behavior of the debug logging:
+
+| Name | Purpose |
+|-----------|-------------------------------------------------|
+| `DEBUG` | Enables/disables specific debugging namespaces. |
+| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
+| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
+| `DEBUG_DEPTH` | Object inspection depth. |
+| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
+
+
+__Note:__ The environment variables beginning with `DEBUG_` end up being
+converted into an Options object that gets used with `%o`/`%O` formatters.
+See the Node.js documentation for
+[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
+for the complete list.
+
+## Formatters
+
+Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
+Below are the officially supported formatters:
+
+| Formatter | Representation |
+|-----------|----------------|
+| `%O` | Pretty-print an Object on multiple lines. |
+| `%o` | Pretty-print an Object all on a single line. |
+| `%s` | String. |
+| `%d` | Number (both integer and float). |
+| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
+| `%%` | Single percent sign ('%'). This does not consume an argument. |
+
+
+### Custom formatters
+
+You can add custom formatters by extending the `debug.formatters` object.
+For example, if you wanted to add support for rendering a Buffer as hex with
+`%h`, you could do something like:
+
+```js
+const createDebug = require('debug')
+createDebug.formatters.h = (v) => {
+ return v.toString('hex')
+}
+
+// …elsewhere
+const debug = createDebug('foo')
+debug('this is hex: %h', new Buffer('hello world'))
+// foo this is hex: 68656c6c6f20776f726c6421 +0ms
+```
+
+
+## Browser Support
+
+You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
+or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
+if you don't want to build it yourself.
+
+Debug's enable state is currently persisted by `localStorage`.
+Consider the situation shown below where you have `worker:a` and `worker:b`,
+and wish to debug both. You can enable this using `localStorage.debug`:
+
+```js
+localStorage.debug = 'worker:*'
+```
+
+And then refresh the page.
+
+```js
+a = debug('worker:a');
+b = debug('worker:b');
+
+setInterval(function(){
+ a('doing some work');
+}, 1000);
+
+setInterval(function(){
+ b('doing some work');
+}, 1200);
+```
+
+
+## Output streams
+
+ By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
+
+Example [_stdout.js_](./examples/node/stdout.js):
+
+```js
+var debug = require('debug');
+var error = debug('app:error');
+
+// by default stderr is used
+error('goes to stderr!');
+
+var log = debug('app:log');
+// set this namespace to log via console.log
+log.log = console.log.bind(console); // don't forget to bind to console!
+log('goes to stdout');
+error('still goes to stderr!');
+
+// set all output to go via console.info
+// overrides all per-namespace log settings
+debug.log = console.info.bind(console);
+error('now goes to stdout via console.info');
+log('still goes to stdout, but via console.info now');
+```
+
+## Checking whether a debug target is enabled
+
+After you've created a debug instance, you can determine whether or not it is
+enabled by checking the `enabled` property:
+
+```javascript
+const debug = require('debug')('http');
+
+if (debug.enabled) {
+ // do stuff...
+}
+```
+
+You can also manually toggle this property to force the debug instance to be
+enabled or disabled.
+
+
+## Authors
+
+ - TJ Holowaychuk
+ - Nathan Rajlich
+ - Andrew Rhyne
+
+## Backers
+
+Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Sponsors
+
+Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/mquery/node_modules/debug/karma.conf.js b/node_modules/mquery/node_modules/debug/karma.conf.js
new file mode 100644
index 0000000..103a82d
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/karma.conf.js
@@ -0,0 +1,70 @@
+// Karma configuration
+// Generated on Fri Dec 16 2016 13:09:51 GMT+0000 (UTC)
+
+module.exports = function(config) {
+ config.set({
+
+ // base path that will be used to resolve all patterns (eg. files, exclude)
+ basePath: '',
+
+
+ // frameworks to use
+ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
+ frameworks: ['mocha', 'chai', 'sinon'],
+
+
+ // list of files / patterns to load in the browser
+ files: [
+ 'dist/debug.js',
+ 'test/*spec.js'
+ ],
+
+
+ // list of files to exclude
+ exclude: [
+ 'src/node.js'
+ ],
+
+
+ // preprocess matching files before serving them to the browser
+ // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
+ preprocessors: {
+ },
+
+ // test results reporter to use
+ // possible values: 'dots', 'progress'
+ // available reporters: https://npmjs.org/browse/keyword/karma-reporter
+ reporters: ['progress'],
+
+
+ // web server port
+ port: 9876,
+
+
+ // enable / disable colors in the output (reporters and logs)
+ colors: true,
+
+
+ // level of logging
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+
+ // start these browsers
+ // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
+ browsers: ['PhantomJS'],
+
+
+ // Continuous Integration mode
+ // if true, Karma captures browsers, runs the tests and exits
+ singleRun: false,
+
+ // Concurrency level
+ // how many browser should be started simultaneous
+ concurrency: Infinity
+ })
+}
diff --git a/node_modules/mquery/node_modules/debug/node.js b/node_modules/mquery/node_modules/debug/node.js
new file mode 100644
index 0000000..7fc36fe
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/node.js
@@ -0,0 +1 @@
+module.exports = require('./src/node');
diff --git a/node_modules/mquery/node_modules/debug/package.json b/node_modules/mquery/node_modules/debug/package.json
new file mode 100644
index 0000000..3a45982
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/package.json
@@ -0,0 +1,82 @@
+{
+ "_from": "debug@3.1.0",
+ "_id": "debug@3.1.0",
+ "_inBundle": false,
+ "_integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "_location": "/mquery/debug",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "debug@3.1.0",
+ "name": "debug",
+ "escapedName": "debug",
+ "rawSpec": "3.1.0",
+ "saveSpec": null,
+ "fetchSpec": "3.1.0"
+ },
+ "_requiredBy": [
+ "/mquery"
+ ],
+ "_resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "_shasum": "5bb5a0672628b64149566ba16819e61518c67261",
+ "_spec": "debug@3.1.0",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mquery",
+ "author": {
+ "name": "TJ Holowaychuk",
+ "email": "tj@vision-media.ca"
+ },
+ "browser": "./src/browser.js",
+ "bugs": {
+ "url": "https://github.com/visionmedia/debug/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Nathan Rajlich",
+ "email": "nathan@tootallnate.net",
+ "url": "http://n8.io"
+ },
+ {
+ "name": "Andrew Rhyne",
+ "email": "rhyneandrew@gmail.com"
+ }
+ ],
+ "dependencies": {
+ "ms": "2.0.0"
+ },
+ "deprecated": false,
+ "description": "small debugging utility",
+ "devDependencies": {
+ "browserify": "14.4.0",
+ "chai": "^3.5.0",
+ "concurrently": "^3.1.0",
+ "coveralls": "^2.11.15",
+ "eslint": "^3.12.1",
+ "istanbul": "^0.4.5",
+ "karma": "^1.3.0",
+ "karma-chai": "^0.1.0",
+ "karma-mocha": "^1.3.0",
+ "karma-phantomjs-launcher": "^1.0.2",
+ "karma-sinon": "^1.0.5",
+ "mocha": "^3.2.0",
+ "mocha-lcov-reporter": "^1.2.0",
+ "rimraf": "^2.5.4",
+ "sinon": "^1.17.6",
+ "sinon-chai": "^2.8.0"
+ },
+ "homepage": "https://github.com/visionmedia/debug#readme",
+ "keywords": [
+ "debug",
+ "log",
+ "debugger"
+ ],
+ "license": "MIT",
+ "main": "./src/index.js",
+ "name": "debug",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/visionmedia/debug.git"
+ },
+ "version": "3.1.0"
+}
diff --git a/node_modules/mquery/node_modules/debug/src/browser.js b/node_modules/mquery/node_modules/debug/src/browser.js
new file mode 100644
index 0000000..f5149ff
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/src/browser.js
@@ -0,0 +1,195 @@
+/**
+ * This is the web browser implementation of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = require('./debug');
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+exports.storage = 'undefined' != typeof chrome
+ && 'undefined' != typeof chrome.storage
+ ? chrome.storage.local
+ : localstorage();
+
+/**
+ * Colors.
+ */
+
+exports.colors = [
+ '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC',
+ '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF',
+ '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC',
+ '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF',
+ '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC',
+ '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033',
+ '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366',
+ '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933',
+ '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC',
+ '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF',
+ '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33'
+];
+
+/**
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+function useColors() {
+ // NB: In an Electron preload script, document will be defined but not fully
+ // initialized. Since we know we're in Chrome, we'll just detect this case
+ // explicitly
+ if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
+ return true;
+ }
+
+ // Internet Explorer and Edge do not support colors.
+ if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
+ return false;
+ }
+
+ // is webkit? http://stackoverflow.com/a/16459606/376773
+ // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
+ return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
+ // is firebug? http://stackoverflow.com/a/398120/376773
+ (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
+ // is firefox >= v31?
+ // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+ (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
+ // double check webkit in userAgent just in case we are in a worker
+ (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
+}
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+exports.formatters.j = function(v) {
+ try {
+ return JSON.stringify(v);
+ } catch (err) {
+ return '[UnexpectedJSONParseError]: ' + err.message;
+ }
+};
+
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs(args) {
+ var useColors = this.useColors;
+
+ args[0] = (useColors ? '%c' : '')
+ + this.namespace
+ + (useColors ? ' %c' : ' ')
+ + args[0]
+ + (useColors ? '%c ' : ' ')
+ + '+' + exports.humanize(this.diff);
+
+ if (!useColors) return;
+
+ var c = 'color: ' + this.color;
+ args.splice(1, 0, c, 'color: inherit')
+
+ // the final "%c" is somewhat tricky, because there could be other
+ // arguments passed either before or after the %c, so we need to
+ // figure out the correct index to insert the CSS into
+ var index = 0;
+ var lastC = 0;
+ args[0].replace(/%[a-zA-Z%]/g, function(match) {
+ if ('%%' === match) return;
+ index++;
+ if ('%c' === match) {
+ // we only are interested in the *last* %c
+ // (the user may have provided their own)
+ lastC = index;
+ }
+ });
+
+ args.splice(lastC, 0, c);
+}
+
+/**
+ * Invokes `console.log()` when available.
+ * No-op when `console.log` is not a "function".
+ *
+ * @api public
+ */
+
+function log() {
+ // this hackery is required for IE8/9, where
+ // the `console.log` function doesn't have 'apply'
+ return 'object' === typeof console
+ && console.log
+ && Function.prototype.apply.call(console.log, console, arguments);
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+ try {
+ if (null == namespaces) {
+ exports.storage.removeItem('debug');
+ } else {
+ exports.storage.debug = namespaces;
+ }
+ } catch(e) {}
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+ var r;
+ try {
+ r = exports.storage.debug;
+ } catch(e) {}
+
+ // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
+ if (!r && typeof process !== 'undefined' && 'env' in process) {
+ r = process.env.DEBUG;
+ }
+
+ return r;
+}
+
+/**
+ * Enable namespaces listed in `localStorage.debug` initially.
+ */
+
+exports.enable(load());
+
+/**
+ * Localstorage attempts to return the localstorage.
+ *
+ * This is necessary because safari throws
+ * when a user disables cookies/localstorage
+ * and you attempt to access it.
+ *
+ * @return {LocalStorage}
+ * @api private
+ */
+
+function localstorage() {
+ try {
+ return window.localStorage;
+ } catch (e) {}
+}
diff --git a/node_modules/mquery/node_modules/debug/src/debug.js b/node_modules/mquery/node_modules/debug/src/debug.js
new file mode 100644
index 0000000..77e6384
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/src/debug.js
@@ -0,0 +1,225 @@
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
+exports.coerce = coerce;
+exports.disable = disable;
+exports.enable = enable;
+exports.enabled = enabled;
+exports.humanize = require('ms');
+
+/**
+ * Active `debug` instances.
+ */
+exports.instances = [];
+
+/**
+ * The currently active debug mode names, and names to skip.
+ */
+
+exports.names = [];
+exports.skips = [];
+
+/**
+ * Map of special "%n" handling functions, for the debug "format" argument.
+ *
+ * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
+ */
+
+exports.formatters = {};
+
+/**
+ * Select a color.
+ * @param {String} namespace
+ * @return {Number}
+ * @api private
+ */
+
+function selectColor(namespace) {
+ var hash = 0, i;
+
+ for (i in namespace) {
+ hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
+ hash |= 0; // Convert to 32bit integer
+ }
+
+ return exports.colors[Math.abs(hash) % exports.colors.length];
+}
+
+/**
+ * Create a debugger with the given `namespace`.
+ *
+ * @param {String} namespace
+ * @return {Function}
+ * @api public
+ */
+
+function createDebug(namespace) {
+
+ var prevTime;
+
+ function debug() {
+ // disabled?
+ if (!debug.enabled) return;
+
+ var self = debug;
+
+ // set `diff` timestamp
+ var curr = +new Date();
+ var ms = curr - (prevTime || curr);
+ self.diff = ms;
+ self.prev = prevTime;
+ self.curr = curr;
+ prevTime = curr;
+
+ // turn the `arguments` into a proper Array
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; i++) {
+ args[i] = arguments[i];
+ }
+
+ args[0] = exports.coerce(args[0]);
+
+ if ('string' !== typeof args[0]) {
+ // anything else let's inspect with %O
+ args.unshift('%O');
+ }
+
+ // apply any `formatters` transformations
+ var index = 0;
+ args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
+ // if we encounter an escaped % then don't increase the array index
+ if (match === '%%') return match;
+ index++;
+ var formatter = exports.formatters[format];
+ if ('function' === typeof formatter) {
+ var val = args[index];
+ match = formatter.call(self, val);
+
+ // now we need to remove `args[index]` since it's inlined in the `format`
+ args.splice(index, 1);
+ index--;
+ }
+ return match;
+ });
+
+ // apply env-specific formatting (colors, etc.)
+ exports.formatArgs.call(self, args);
+
+ var logFn = debug.log || exports.log || console.log.bind(console);
+ logFn.apply(self, args);
+ }
+
+ debug.namespace = namespace;
+ debug.enabled = exports.enabled(namespace);
+ debug.useColors = exports.useColors();
+ debug.color = selectColor(namespace);
+ debug.destroy = destroy;
+
+ // env-specific initialization logic for debug instances
+ if ('function' === typeof exports.init) {
+ exports.init(debug);
+ }
+
+ exports.instances.push(debug);
+
+ return debug;
+}
+
+function destroy () {
+ var index = exports.instances.indexOf(this);
+ if (index !== -1) {
+ exports.instances.splice(index, 1);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Enables a debug mode by namespaces. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} namespaces
+ * @api public
+ */
+
+function enable(namespaces) {
+ exports.save(namespaces);
+
+ exports.names = [];
+ exports.skips = [];
+
+ var i;
+ var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
+ var len = split.length;
+
+ for (i = 0; i < len; i++) {
+ if (!split[i]) continue; // ignore empty strings
+ namespaces = split[i].replace(/\*/g, '.*?');
+ if (namespaces[0] === '-') {
+ exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
+ } else {
+ exports.names.push(new RegExp('^' + namespaces + '$'));
+ }
+ }
+
+ for (i = 0; i < exports.instances.length; i++) {
+ var instance = exports.instances[i];
+ instance.enabled = exports.enabled(instance.namespace);
+ }
+}
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+function disable() {
+ exports.enable('');
+}
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+function enabled(name) {
+ if (name[name.length - 1] === '*') {
+ return true;
+ }
+ var i, len;
+ for (i = 0, len = exports.skips.length; i < len; i++) {
+ if (exports.skips[i].test(name)) {
+ return false;
+ }
+ }
+ for (i = 0, len = exports.names.length; i < len; i++) {
+ if (exports.names[i].test(name)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Coerce `val`.
+ *
+ * @param {Mixed} val
+ * @return {Mixed}
+ * @api private
+ */
+
+function coerce(val) {
+ if (val instanceof Error) return val.stack || val.message;
+ return val;
+}
diff --git a/node_modules/mquery/node_modules/debug/src/index.js b/node_modules/mquery/node_modules/debug/src/index.js
new file mode 100644
index 0000000..cabcbcd
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/src/index.js
@@ -0,0 +1,10 @@
+/**
+ * Detect Electron renderer process, which is node, but we should
+ * treat as a browser.
+ */
+
+if (typeof process === 'undefined' || process.type === 'renderer') {
+ module.exports = require('./browser.js');
+} else {
+ module.exports = require('./node.js');
+}
diff --git a/node_modules/mquery/node_modules/debug/src/node.js b/node_modules/mquery/node_modules/debug/src/node.js
new file mode 100644
index 0000000..d666fb9
--- /dev/null
+++ b/node_modules/mquery/node_modules/debug/src/node.js
@@ -0,0 +1,186 @@
+/**
+ * Module dependencies.
+ */
+
+var tty = require('tty');
+var util = require('util');
+
+/**
+ * This is the Node.js implementation of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = require('./debug');
+exports.init = init;
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+
+/**
+ * Colors.
+ */
+
+exports.colors = [ 6, 2, 3, 4, 5, 1 ];
+
+try {
+ var supportsColor = require('supports-color');
+ if (supportsColor && supportsColor.level >= 2) {
+ exports.colors = [
+ 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68,
+ 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134,
+ 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
+ 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 214, 215, 220, 221
+ ];
+ }
+} catch (err) {
+ // swallow - we only care if `supports-color` is available; it doesn't have to be.
+}
+
+/**
+ * Build up the default `inspectOpts` object from the environment variables.
+ *
+ * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
+ */
+
+exports.inspectOpts = Object.keys(process.env).filter(function (key) {
+ return /^debug_/i.test(key);
+}).reduce(function (obj, key) {
+ // camel-case
+ var prop = key
+ .substring(6)
+ .toLowerCase()
+ .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() });
+
+ // coerce string value into JS value
+ var val = process.env[key];
+ if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
+ else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
+ else if (val === 'null') val = null;
+ else val = Number(val);
+
+ obj[prop] = val;
+ return obj;
+}, {});
+
+/**
+ * Is stdout a TTY? Colored output is enabled when `true`.
+ */
+
+function useColors() {
+ return 'colors' in exports.inspectOpts
+ ? Boolean(exports.inspectOpts.colors)
+ : tty.isatty(process.stderr.fd);
+}
+
+/**
+ * Map %o to `util.inspect()`, all on a single line.
+ */
+
+exports.formatters.o = function(v) {
+ this.inspectOpts.colors = this.useColors;
+ return util.inspect(v, this.inspectOpts)
+ .split('\n').map(function(str) {
+ return str.trim()
+ }).join(' ');
+};
+
+/**
+ * Map %o to `util.inspect()`, allowing multiple lines if needed.
+ */
+
+exports.formatters.O = function(v) {
+ this.inspectOpts.colors = this.useColors;
+ return util.inspect(v, this.inspectOpts);
+};
+
+/**
+ * Adds ANSI color escape codes if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs(args) {
+ var name = this.namespace;
+ var useColors = this.useColors;
+
+ if (useColors) {
+ var c = this.color;
+ var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c);
+ var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m';
+
+ args[0] = prefix + args[0].split('\n').join('\n' + prefix);
+ args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m');
+ } else {
+ args[0] = getDate() + name + ' ' + args[0];
+ }
+}
+
+function getDate() {
+ if (exports.inspectOpts.hideDate) {
+ return '';
+ } else {
+ return new Date().toISOString() + ' ';
+ }
+}
+
+/**
+ * Invokes `util.format()` with the specified arguments and writes to stderr.
+ */
+
+function log() {
+ return process.stderr.write(util.format.apply(util, arguments) + '\n');
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+ if (null == namespaces) {
+ // If you set a process.env field to null or undefined, it gets cast to the
+ // string 'null' or 'undefined'. Just delete instead.
+ delete process.env.DEBUG;
+ } else {
+ process.env.DEBUG = namespaces;
+ }
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+ return process.env.DEBUG;
+}
+
+/**
+ * Init logic for `debug` instances.
+ *
+ * Create a new `inspectOpts` object in case `useColors` is set
+ * differently for a particular `debug` instance.
+ */
+
+function init (debug) {
+ debug.inspectOpts = {};
+
+ var keys = Object.keys(exports.inspectOpts);
+ for (var i = 0; i < keys.length; i++) {
+ debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
+ }
+}
+
+/**
+ * Enable namespaces listed in `process.env.DEBUG` initially.
+ */
+
+exports.enable(load());
diff --git a/node_modules/mquery/package.json b/node_modules/mquery/package.json
new file mode 100644
index 0000000..76f8810
--- /dev/null
+++ b/node_modules/mquery/package.json
@@ -0,0 +1,115 @@
+{
+ "_from": "mquery@3.2.0",
+ "_id": "mquery@3.2.0",
+ "_inBundle": false,
+ "_integrity": "sha512-qPJcdK/yqcbQiKoemAt62Y0BAc0fTEKo1IThodBD+O5meQRJT/2HSe5QpBNwaa4CjskoGrYWsEyjkqgiE0qjhg==",
+ "_location": "/mquery",
+ "_phantomChildren": {
+ "ms": "2.0.0"
+ },
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "mquery@3.2.0",
+ "name": "mquery",
+ "escapedName": "mquery",
+ "rawSpec": "3.2.0",
+ "saveSpec": null,
+ "fetchSpec": "3.2.0"
+ },
+ "_requiredBy": [
+ "/mongoose"
+ ],
+ "_resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.0.tgz",
+ "_shasum": "e276472abd5109686a15eb2a8e0761db813c81cc",
+ "_spec": "mquery@3.2.0",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mongoose",
+ "author": {
+ "name": "Aaron Heckmann",
+ "email": "aaron.heckmann+github@gmail.com"
+ },
+ "bugs": {
+ "url": "https://github.com/aheckmann/mquery/issues/new"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "bluebird": "3.5.1",
+ "debug": "3.1.0",
+ "regexp-clone": "0.0.1",
+ "safe-buffer": "5.1.2",
+ "sliced": "1.0.1"
+ },
+ "deprecated": false,
+ "description": "Expressive query building for MongoDB",
+ "devDependencies": {
+ "eslint": "^4.14.0",
+ "istanbul": "^0.4.5",
+ "mocha": "4.1.0",
+ "mongodb": "3.1.1"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ },
+ "eslintConfig": {
+ "env": {
+ "node": true,
+ "mocha": true,
+ "es6": false
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaVersion": 5
+ },
+ "rules": {
+ "comma-style": "error",
+ "consistent-this": [
+ "error",
+ "_this"
+ ],
+ "indent": [
+ "error",
+ 2,
+ {
+ "SwitchCase": 1,
+ "VariableDeclarator": 2
+ }
+ ],
+ "keyword-spacing": "error",
+ "no-console": "off",
+ "no-multi-spaces": "error",
+ "func-call-spacing": "error",
+ "no-trailing-spaces": "error",
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "semi": "error",
+ "space-before-blocks": "error",
+ "space-before-function-paren": [
+ "error",
+ "never"
+ ],
+ "space-infix-ops": "error",
+ "space-unary-ops": "error"
+ }
+ },
+ "homepage": "https://github.com/aheckmann/mquery/",
+ "keywords": [
+ "mongodb",
+ "query",
+ "builder"
+ ],
+ "license": "MIT",
+ "main": "lib/mquery.js",
+ "name": "mquery",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aheckmann/mquery.git"
+ },
+ "scripts": {
+ "fix-lint": "eslint . --fix",
+ "lint": "eslint .",
+ "test": "mocha test/index.js test/*.test.js"
+ },
+ "version": "3.2.0"
+}
diff --git a/node_modules/mquery/test/collection/browser.js b/node_modules/mquery/test/collection/browser.js
new file mode 100644
index 0000000..e69de29
diff --git a/node_modules/mquery/test/collection/mongo.js b/node_modules/mquery/test/collection/mongo.js
new file mode 100644
index 0000000..e69de29
diff --git a/node_modules/mquery/test/collection/node.js b/node_modules/mquery/test/collection/node.js
new file mode 100644
index 0000000..8ac380b
--- /dev/null
+++ b/node_modules/mquery/test/collection/node.js
@@ -0,0 +1,28 @@
+
+var assert = require('assert');
+var mongo = require('mongodb');
+
+var uri = process.env.MQUERY_URI || 'mongodb://localhost/mquery';
+var client;
+var db;
+
+exports.getCollection = function(cb) {
+ mongo.MongoClient.connect(uri, function(err, _client) {
+ assert.ifError(err);
+ client = _client;
+ db = client.db();
+
+ var collection = db.collection('stuff');
+
+ // clean test db before starting
+ db.dropDatabase(function() {
+ cb(null, collection);
+ });
+ });
+};
+
+exports.dropCollection = function(cb) {
+ db.dropDatabase(function() {
+ client.close(cb);
+ });
+};
diff --git a/node_modules/mquery/test/env.js b/node_modules/mquery/test/env.js
new file mode 100644
index 0000000..38385b0
--- /dev/null
+++ b/node_modules/mquery/test/env.js
@@ -0,0 +1,21 @@
+
+var env = require('../').env;
+
+console.log('environment: %s', env.type);
+
+var col;
+switch (env.type) {
+ case 'node':
+ col = require('./collection/node');
+ break;
+ case 'mongo':
+ col = require('./collection/mongo');
+ break;
+ case 'browser':
+ col = require('./collection/browser');
+ break;
+ default:
+ throw new Error('missing collection implementation for environment: ' + env.type);
+}
+
+module.exports = exports = col;
diff --git a/node_modules/mquery/test/index.js b/node_modules/mquery/test/index.js
new file mode 100644
index 0000000..44adb17
--- /dev/null
+++ b/node_modules/mquery/test/index.js
@@ -0,0 +1,3076 @@
+var mquery = require('../');
+var assert = require('assert');
+
+/* global Map */
+
+describe('mquery', function() {
+ var col;
+
+ before(function(done) {
+ // get the env specific collection interface
+ require('./env').getCollection(function(err, collection) {
+ assert.ifError(err);
+ col = collection;
+ done();
+ });
+ });
+
+ after(function(done) {
+ require('./env').dropCollection(done);
+ });
+
+ describe('mquery', function() {
+ it('is a function', function() {
+ assert.equal('function', typeof mquery);
+ });
+ it('creates instances with the `new` keyword', function() {
+ assert.ok(mquery() instanceof mquery);
+ });
+ describe('defaults', function() {
+ it('are set', function() {
+ var m = mquery();
+ assert.strictEqual(undefined, m.op);
+ assert.deepEqual({}, m.options);
+ });
+ });
+ describe('criteria', function() {
+ it('if collection-like is used as collection', function() {
+ var m = mquery(col);
+ assert.equal(col, m._collection.collection);
+ });
+ it('non-collection-like is used as criteria', function() {
+ var m = mquery({ works: true });
+ assert.ok(!m._collection);
+ assert.deepEqual({ works: true }, m._conditions);
+ });
+ });
+ describe('options', function() {
+ it('are merged when passed', function() {
+ var m;
+ m = mquery(col, { safe: true });
+ assert.deepEqual({ safe: true }, m.options);
+ m = mquery({ name: 'mquery' }, { safe: true });
+ assert.deepEqual({ safe: true }, m.options);
+ });
+ });
+ });
+
+ describe('toConstructor', function() {
+ it('creates subclasses of mquery', function() {
+ var opts = { safe: { w: 'majority' }, readPreference: 'p' };
+ var match = { name: 'test', count: { $gt: 101 }};
+ var select = { name: 1, count: 0 };
+ var update = { $set: { x: true }};
+ var path = 'street';
+
+ var q = mquery().setOptions(opts);
+ q.where(match);
+ q.select(select);
+ q.update(update);
+ q.where(path);
+ q.find();
+
+ var M = q.toConstructor();
+ var m = M();
+
+ assert.ok(m instanceof mquery);
+ assert.deepEqual(opts, m.options);
+ assert.deepEqual(match, m._conditions);
+ assert.deepEqual(select, m._fields);
+ assert.deepEqual(update, m._update);
+ assert.equal(path, m._path);
+ assert.equal('find', m.op);
+ });
+ });
+
+ describe('setOptions', function() {
+ it('calls associated methods', function() {
+ var m = mquery();
+ assert.equal(m._collection, null);
+ m.setOptions({ collection: col });
+ assert.equal(m._collection.collection, col);
+ });
+ it('directly sets option when no method exists', function() {
+ var m = mquery();
+ assert.equal(m.options.woot, null);
+ m.setOptions({ woot: 'yay' });
+ assert.equal(m.options.woot, 'yay');
+ });
+ it('is chainable', function() {
+ var m = mquery(),
+ n;
+
+ n = m.setOptions();
+ assert.equal(m, n);
+ n = m.setOptions({ x: 1 });
+ assert.equal(m, n);
+ });
+ });
+
+ describe('collection', function() {
+ it('sets the _collection', function() {
+ var m = mquery();
+ m.collection(col);
+ assert.equal(m._collection.collection, col);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ var n = m.collection(col);
+ assert.equal(m, n);
+ });
+ });
+
+ describe('$where', function() {
+ it('sets the $where condition', function() {
+ var m = mquery();
+ function go() {}
+ m.$where(go);
+ assert.ok(go === m._conditions.$where);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ var n = m.$where('x');
+ assert.equal(m, n);
+ });
+ });
+
+ describe('where', function() {
+ it('without arguments', function() {
+ var m = mquery();
+ m.where();
+ assert.deepEqual({}, m._conditions);
+ });
+ it('with non-string/object argument', function() {
+ var m = mquery();
+
+ assert.throws(function() {
+ m.where([]);
+ }, /path must be a string or object/);
+ });
+ describe('with one argument', function() {
+ it('that is an object', function() {
+ var m = mquery();
+ m.where({ name: 'flawed' });
+ assert.strictEqual(m._conditions.name, 'flawed');
+ });
+ it('that is a query', function() {
+ var m = mquery({ name: 'first' });
+ var n = mquery({ name: 'changed' });
+ m.where(n);
+ assert.strictEqual(m._conditions.name, 'changed');
+ });
+ it('that is a string', function() {
+ var m = mquery();
+ m.where('name');
+ assert.equal('name', m._path);
+ assert.strictEqual(m._conditions.name, undefined);
+ });
+ });
+ it('with two arguments', function() {
+ var m = mquery();
+ m.where('name', 'The Great Pumpkin');
+ assert.equal('name', m._path);
+ assert.strictEqual(m._conditions.name, 'The Great Pumpkin');
+ });
+ it('is chainable', function() {
+ var m = mquery(),
+ n;
+
+ n = m.where('x', 'y');
+ assert.equal(m, n);
+ n = m.where();
+ assert.equal(m, n);
+ });
+ });
+ describe('equals', function() {
+ it('must be called after where()', function() {
+ var m = mquery();
+ assert.throws(function() {
+ m.equals();
+ }, /must be used after where/);
+ });
+ it('sets value of path set with where()', function() {
+ var m = mquery();
+ m.where('age').equals(1000);
+ assert.deepEqual({ age: 1000 }, m._conditions);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ var n = m.where('x').equals(3);
+ assert.equal(m, n);
+ });
+ });
+ describe('eq', function() {
+ it('is alias of equals', function() {
+ var m = mquery();
+ m.where('age').eq(1000);
+ assert.deepEqual({ age: 1000 }, m._conditions);
+ });
+ });
+ describe('or', function() {
+ it('pushes onto the internal $or condition', function() {
+ var m = mquery();
+ m.or({ 'Nightmare Before Christmas': true });
+ assert.deepEqual([{'Nightmare Before Christmas': true }], m._conditions.$or);
+ });
+ it('allows passing arrays', function() {
+ var m = mquery();
+ var arg = [{ 'Nightmare Before Christmas': true }, { x: 1 }];
+ m.or(arg);
+ assert.deepEqual(arg, m._conditions.$or);
+ });
+ it('allows calling multiple times', function() {
+ var m = mquery();
+ var arg = [{ looper: true }, { x: 1 }];
+ m.or(arg);
+ m.or({ y: 1 });
+ m.or([{ w: 'oo' }, { z: 'oo'} ]);
+ assert.deepEqual([{looper:true},{x:1},{y:1},{w:'oo'},{z:'oo'}], m._conditions.$or);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ m.or({ o: 'k'}).where('name', 'table');
+ assert.deepEqual({ name: 'table', $or: [{ o: 'k' }] }, m._conditions);
+ });
+ });
+
+ describe('nor', function() {
+ it('pushes onto the internal $nor condition', function() {
+ var m = mquery();
+ m.nor({ 'Nightmare Before Christmas': true });
+ assert.deepEqual([{'Nightmare Before Christmas': true }], m._conditions.$nor);
+ });
+ it('allows passing arrays', function() {
+ var m = mquery();
+ var arg = [{ 'Nightmare Before Christmas': true }, { x: 1 }];
+ m.nor(arg);
+ assert.deepEqual(arg, m._conditions.$nor);
+ });
+ it('allows calling multiple times', function() {
+ var m = mquery();
+ var arg = [{ looper: true }, { x: 1 }];
+ m.nor(arg);
+ m.nor({ y: 1 });
+ m.nor([{ w: 'oo' }, { z: 'oo'} ]);
+ assert.deepEqual([{looper:true},{x:1},{y:1},{w:'oo'},{z:'oo'}], m._conditions.$nor);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ m.nor({ o: 'k'}).where('name', 'table');
+ assert.deepEqual({ name: 'table', $nor: [{ o: 'k' }] }, m._conditions);
+ });
+ });
+
+ describe('and', function() {
+ it('pushes onto the internal $and condition', function() {
+ var m = mquery();
+ m.and({ 'Nightmare Before Christmas': true });
+ assert.deepEqual([{'Nightmare Before Christmas': true }], m._conditions.$and);
+ });
+ it('allows passing arrays', function() {
+ var m = mquery();
+ var arg = [{ 'Nightmare Before Christmas': true }, { x: 1 }];
+ m.and(arg);
+ assert.deepEqual(arg, m._conditions.$and);
+ });
+ it('allows calling multiple times', function() {
+ var m = mquery();
+ var arg = [{ looper: true }, { x: 1 }];
+ m.and(arg);
+ m.and({ y: 1 });
+ m.and([{ w: 'oo' }, { z: 'oo'} ]);
+ assert.deepEqual([{looper:true},{x:1},{y:1},{w:'oo'},{z:'oo'}], m._conditions.$and);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ m.and({ o: 'k'}).where('name', 'table');
+ assert.deepEqual({ name: 'table', $and: [{ o: 'k' }] }, m._conditions);
+ });
+ });
+
+ function generalCondition(type) {
+ return function() {
+ it('accepts 2 args', function() {
+ var m = mquery()[type]('count', 3);
+ var check = {};
+ check['$' + type] = 3;
+ assert.deepEqual(m._conditions.count, check);
+ });
+ it('uses previously set `where` path if 1 arg passed', function() {
+ var m = mquery().where('count')[type](3);
+ var check = {};
+ check['$' + type] = 3;
+ assert.deepEqual(m._conditions.count, check);
+ });
+ it('throws if 1 arg was passed but no previous `where` was used', function() {
+ assert.throws(function() {
+ mquery()[type](3);
+ }, /must be used after where/);
+ });
+ it('is chainable', function() {
+ var m = mquery().where('count')[type](3).where('x', 8);
+ var check = {x: 8, count: {}};
+ check.count['$' + type] = 3;
+ assert.deepEqual(m._conditions, check);
+ });
+ it('overwrites previous value', function() {
+ var m = mquery().where('count')[type](3)[type](8);
+ var check = {};
+ check['$' + type] = 8;
+ assert.deepEqual(m._conditions.count, check);
+ });
+ };
+ }
+
+ 'gt gte lt lte ne in nin regex size maxDistance minDistance'.split(' ').forEach(function(type) {
+ describe(type, generalCondition(type));
+ });
+
+ describe('mod', function() {
+ describe('with 1 argument', function() {
+ it('requires a previous where()', function() {
+ assert.throws(function() {
+ mquery().mod([30, 10]);
+ }, /must be used after where/);
+ });
+ it('works', function() {
+ var m = mquery().where('madmen').mod([10,20]);
+ assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
+ });
+ });
+
+ describe('with 2 arguments and second is non-Array', function() {
+ it('requires a previous where()', function() {
+ assert.throws(function() {
+ mquery().mod('x', 10);
+ }, /must be used after where/);
+ });
+ it('works', function() {
+ var m = mquery().where('madmen').mod(10, 20);
+ assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
+ });
+ });
+
+ it('with 2 arguments and second is an array', function() {
+ var m = mquery().mod('madmen', [10,20]);
+ assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
+ });
+
+ it('with 3 arguments', function() {
+ var m = mquery().mod('madmen', 10, 20);
+ assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
+ });
+
+ it('is chainable', function() {
+ var m = mquery().mod('madmen', 10, 20).where('x', 8);
+ var check = { madmen: { $mod: [10,20] }, x: 8};
+ assert.deepEqual(m._conditions, check);
+ });
+ });
+
+ describe('exists', function() {
+ it('with 0 args', function() {
+ it('throws if not used after where()', function() {
+ assert.throws(function() {
+ mquery().exists();
+ }, /must be used after where/);
+ });
+ it('works', function() {
+ var m = mquery().where('name').exists();
+ var check = { name: { $exists: true }};
+ assert.deepEqual(m._conditions, check);
+ });
+ });
+
+ describe('with 1 arg', function() {
+ describe('that is boolean', function() {
+ it('throws if not used after where()', function() {
+ assert.throws(function() {
+ mquery().exists();
+ }, /must be used after where/);
+ });
+ it('works', function() {
+ var m = mquery().exists('name', false);
+ var check = { name: { $exists: false }};
+ assert.deepEqual(m._conditions, check);
+ });
+ });
+ describe('that is not boolean', function() {
+ it('sets the value to `true`', function() {
+ var m = mquery().where('name').exists('yummy');
+ var check = { yummy: { $exists: true }};
+ assert.deepEqual(m._conditions, check);
+ });
+ });
+ });
+
+ describe('with 2 args', function() {
+ it('works', function() {
+ var m = mquery().exists('yummy', false);
+ var check = { yummy: { $exists: false }};
+ assert.deepEqual(m._conditions, check);
+ });
+ });
+
+ it('is chainable', function() {
+ var m = mquery().where('name').exists().find({ x: 1 });
+ var check = { name: { $exists: true }, x: 1};
+ assert.deepEqual(m._conditions, check);
+ });
+ });
+
+ describe('elemMatch', function() {
+ describe('with null/undefined first argument', function() {
+ assert.throws(function() {
+ mquery().elemMatch();
+ }, /Invalid argument/);
+ assert.throws(function() {
+ mquery().elemMatch(null);
+ }, /Invalid argument/);
+ assert.doesNotThrow(function() {
+ mquery().elemMatch('', {});
+ });
+ });
+
+ describe('with 1 argument', function() {
+ it('throws if not a function or object', function() {
+ assert.throws(function() {
+ mquery().elemMatch([]);
+ }, /Invalid argument/);
+ });
+
+ describe('that is an object', function() {
+ it('throws if no previous `where` was used', function() {
+ assert.throws(function() {
+ mquery().elemMatch({});
+ }, /must be used after where/);
+ });
+ it('works', function() {
+ var m = mquery().where('comment').elemMatch({ author: 'joe', votes: {$gte: 3 }});
+ assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
+ });
+ });
+ describe('that is a function', function() {
+ it('throws if no previous `where` was used', function() {
+ assert.throws(function() {
+ mquery().elemMatch(function() {});
+ }, /must be used after where/);
+ });
+ it('works', function() {
+ var m = mquery().where('comment').elemMatch(function(query) {
+ query.where({ author: 'joe', votes: {$gte: 3 }});
+ });
+ assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
+ });
+ });
+ });
+
+ describe('with 2 arguments', function() {
+ describe('and the 2nd is an object', function() {
+ it('works', function() {
+ var m = mquery().elemMatch('comment', { author: 'joe', votes: {$gte: 3 }});
+ assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
+ });
+ });
+ describe('and the 2nd is a function', function() {
+ it('works', function() {
+ var m = mquery().elemMatch('comment', function(query) {
+ query.where({ author: 'joe', votes: {$gte: 3 }});
+ });
+ assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
+ });
+ });
+ it('and the 2nd is not a function or object', function() {
+ assert.throws(function() {
+ mquery().elemMatch('comment', []);
+ }, /Invalid argument/);
+ });
+ });
+ });
+
+ describe('within', function() {
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m.where('a').within(), m);
+ });
+ describe('when called with arguments', function() {
+ it('must follow where()', function() {
+ assert.throws(function() {
+ mquery().within([]);
+ }, /must be used after where/);
+ });
+
+ describe('of length 1', function() {
+ it('throws if not a recognized shape', function() {
+ assert.throws(function() {
+ mquery().where('loc').within({});
+ }, /Invalid argument/);
+ assert.throws(function() {
+ mquery().where('loc').within(null);
+ }, /Invalid argument/);
+ });
+ it('delegates to circle when center exists', function() {
+ var m = mquery().where('loc').within({ center: [10,10], radius: 3 });
+ assert.deepEqual({ $geoWithin: {$center:[[10,10], 3]}}, m._conditions.loc);
+ });
+ it('delegates to box when exists', function() {
+ var m = mquery().where('loc').within({ box: [[10,10], [11,14]] });
+ assert.deepEqual({ $geoWithin: {$box:[[10,10], [11,14]]}}, m._conditions.loc);
+ });
+ it('delegates to polygon when exists', function() {
+ var m = mquery().where('loc').within({ polygon: [[10,10], [11,14],[10,9]] });
+ assert.deepEqual({ $geoWithin: {$polygon:[[10,10], [11,14],[10,9]]}}, m._conditions.loc);
+ });
+ it('delegates to geometry when exists', function() {
+ var m = mquery().where('loc').within({ type: 'Polygon', coordinates: [[10,10], [11,14],[10,9]] });
+ assert.deepEqual({ $geoWithin: {$geometry: {type:'Polygon', coordinates: [[10,10], [11,14],[10,9]]}}}, m._conditions.loc);
+ });
+ });
+
+ describe('of length 2', function() {
+ it('delegates to box()', function() {
+ var m = mquery().where('loc').within([1,2],[2,5]);
+ assert.deepEqual(m._conditions.loc, { $geoWithin: { $box: [[1,2],[2,5]]}});
+ });
+ });
+
+ describe('of length > 2', function() {
+ it('delegates to polygon()', function() {
+ var m = mquery().where('loc').within([1,2],[2,5],[2,4],[1,3]);
+ assert.deepEqual(m._conditions.loc, { $geoWithin: { $polygon: [[1,2],[2,5],[2,4],[1,3]]}});
+ });
+ });
+ });
+ });
+
+ describe('geoWithin', function() {
+ before(function() {
+ mquery.use$geoWithin = false;
+ });
+ after(function() {
+ mquery.use$geoWithin = true;
+ });
+ describe('when called with arguments', function() {
+ describe('of length 1', function() {
+ it('delegates to circle when center exists', function() {
+ var m = mquery().where('loc').within({ center: [10,10], radius: 3 });
+ assert.deepEqual({ $within: {$center:[[10,10], 3]}}, m._conditions.loc);
+ });
+ it('delegates to box when exists', function() {
+ var m = mquery().where('loc').within({ box: [[10,10], [11,14]] });
+ assert.deepEqual({ $within: {$box:[[10,10], [11,14]]}}, m._conditions.loc);
+ });
+ it('delegates to polygon when exists', function() {
+ var m = mquery().where('loc').within({ polygon: [[10,10], [11,14],[10,9]] });
+ assert.deepEqual({ $within: {$polygon:[[10,10], [11,14],[10,9]]}}, m._conditions.loc);
+ });
+ it('delegates to geometry when exists', function() {
+ var m = mquery().where('loc').within({ type: 'Polygon', coordinates: [[10,10], [11,14],[10,9]] });
+ assert.deepEqual({ $within: {$geometry: {type:'Polygon', coordinates: [[10,10], [11,14],[10,9]]}}}, m._conditions.loc);
+ });
+ });
+
+ describe('of length 2', function() {
+ it('delegates to box()', function() {
+ var m = mquery().where('loc').within([1,2],[2,5]);
+ assert.deepEqual(m._conditions.loc, { $within: { $box: [[1,2],[2,5]]}});
+ });
+ });
+
+ describe('of length > 2', function() {
+ it('delegates to polygon()', function() {
+ var m = mquery().where('loc').within([1,2],[2,5],[2,4],[1,3]);
+ assert.deepEqual(m._conditions.loc, { $within: { $polygon: [[1,2],[2,5],[2,4],[1,3]]}});
+ });
+ });
+ });
+ });
+
+ describe('box', function() {
+ describe('with 1 argument', function() {
+ it('throws', function() {
+ assert.throws(function() {
+ mquery().box('sometihng');
+ }, /Invalid argument/);
+ });
+ });
+ describe('with > 3 arguments', function() {
+ it('throws', function() {
+ assert.throws(function() {
+ mquery().box(1,2,3,4);
+ }, /Invalid argument/);
+ });
+ });
+
+ describe('with 2 arguments', function() {
+ it('throws if not used after where()', function() {
+ assert.throws(function() {
+ mquery().box([],[]);
+ }, /must be used after where/);
+ });
+ it('works', function() {
+ var m = mquery().where('loc').box([1,2],[3,4]);
+ assert.deepEqual(m._conditions.loc, { $geoWithin: { $box: [[1,2],[3,4]] }});
+ });
+ });
+
+ describe('with 3 arguments', function() {
+ it('works', function() {
+ var m = mquery().box('loc', [1,2],[3,4]);
+ assert.deepEqual(m._conditions.loc, { $geoWithin: { $box: [[1,2],[3,4]] }});
+ });
+ });
+ });
+
+ describe('polygon', function() {
+ describe('when first argument is not a string', function() {
+ it('throws if not used after where()', function() {
+ assert.throws(function() {
+ mquery().polygon({});
+ }, /must be used after where/);
+
+ assert.doesNotThrow(function() {
+ mquery().where('loc').polygon([1,2], [2,3], [3,6]);
+ });
+ });
+
+ it('assigns arguments to within polygon condition', function() {
+ var m = mquery().where('loc').polygon([1,2], [2,3], [3,6]);
+ assert.deepEqual(m._conditions, { loc: {$geoWithin: {$polygon: [[1,2],[2,3],[3,6]]}} });
+ });
+ });
+
+ describe('when first arg is a string', function() {
+ it('assigns remaining arguments to within polygon condition', function() {
+ var m = mquery().polygon('loc', [1,2], [2,3], [3,6]);
+ assert.deepEqual(m._conditions, { loc: {$geoWithin: {$polygon: [[1,2],[2,3],[3,6]]}} });
+ });
+ });
+ });
+
+ describe('circle', function() {
+ describe('with one arg', function() {
+ it('must follow where()', function() {
+ assert.throws(function() {
+ mquery().circle('x');
+ }, /must be used after where/);
+ assert.doesNotThrow(function() {
+ mquery().where('loc').circle({center:[0,0], radius: 3 });
+ });
+ });
+ it('works', function() {
+ var m = mquery().where('loc').circle({center:[0,0], radius: 3 });
+ assert.deepEqual(m._conditions, { loc: { $geoWithin: {$center: [[0,0],3] }}});
+ });
+ });
+ describe('with 3 args', function() {
+ it('throws', function() {
+ assert.throws(function() {
+ mquery().where('loc').circle(1,2,3);
+ }, /Invalid argument/);
+ });
+ });
+ describe('requires radius and center', function() {
+ assert.throws(function() {
+ mquery().circle('loc', { center: 1 });
+ }, /center and radius are required/);
+ assert.throws(function() {
+ mquery().circle('loc', { radius: 1 });
+ }, /center and radius are required/);
+ assert.doesNotThrow(function() {
+ mquery().circle('loc', { center: [1,2], radius: 1 });
+ });
+ });
+ });
+
+ describe('geometry', function() {
+ // within + intersects
+ var point = { type: 'Point', coordinates: [[0,0],[1,1]] };
+
+ it('must be called after within or intersects', function(done) {
+ assert.throws(function() {
+ mquery().where('a').geometry(point);
+ }, /must come after/);
+
+ assert.doesNotThrow(function() {
+ mquery().where('a').within().geometry(point);
+ });
+
+ assert.doesNotThrow(function() {
+ mquery().where('a').intersects().geometry(point);
+ });
+
+ done();
+ });
+
+ describe('when called with one argument', function() {
+ describe('after within()', function() {
+ it('and arg quacks like geoJSON', function(done) {
+ var m = mquery().where('a').within().geometry(point);
+ assert.deepEqual({ a: { $geoWithin: { $geometry: point }}}, m._conditions);
+ done();
+ });
+ });
+
+ describe('after intersects()', function() {
+ it('and arg quacks like geoJSON', function(done) {
+ var m = mquery().where('a').intersects().geometry(point);
+ assert.deepEqual({ a: { $geoIntersects: { $geometry: point }}}, m._conditions);
+ done();
+ });
+ });
+
+ it('and arg does not quack like geoJSON', function(done) {
+ assert.throws(function() {
+ mquery().where('b').within().geometry({type:1, coordinates:2});
+ }, /Invalid argument/);
+ done();
+ });
+ });
+
+ describe('when called with zero arguments', function() {
+ it('throws', function(done) {
+ assert.throws(function() {
+ mquery().where('a').within().geometry();
+ }, /Invalid argument/);
+
+ done();
+ });
+ });
+
+ describe('when called with more than one arguments', function() {
+ it('throws', function(done) {
+ assert.throws(function() {
+ mquery().where('a').within().geometry({type:'a',coordinates:[]}, 2);
+ }, /Invalid argument/);
+ done();
+ });
+ });
+ });
+
+ describe('intersects', function() {
+ it('must be used after where()', function(done) {
+ var m = mquery();
+ assert.throws(function() {
+ m.intersects();
+ }, /must be used after where/);
+ done();
+ });
+
+ it('sets geo comparison to "$intersects"', function(done) {
+ var n = mquery().where('a').intersects();
+ assert.equal('$geoIntersects', n._geoComparison);
+ done();
+ });
+
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m.where('a').intersects(), m);
+ });
+
+ it('calls geometry if argument quacks like geojson', function(done) {
+ var m = mquery();
+ var o = { type: 'LineString', coordinates: [[0,1],[3,40]] };
+ var ran = false;
+
+ m.geometry = function(arg) {
+ ran = true;
+ assert.deepEqual(o, arg);
+ };
+
+ m.where('a').intersects(o);
+ assert.ok(ran);
+
+ done();
+ });
+
+ it('throws if argument is not geometry-like', function(done) {
+ var m = mquery().where('a');
+
+ assert.throws(function() {
+ m.intersects(null);
+ }, /Invalid argument/);
+
+ assert.throws(function() {
+ m.intersects(undefined);
+ }, /Invalid argument/);
+
+ assert.throws(function() {
+ m.intersects(false);
+ }, /Invalid argument/);
+
+ assert.throws(function() {
+ m.intersects({});
+ }, /Invalid argument/);
+
+ assert.throws(function() {
+ m.intersects([]);
+ }, /Invalid argument/);
+
+ assert.throws(function() {
+ m.intersects(function() {});
+ }, /Invalid argument/);
+
+ assert.throws(function() {
+ m.intersects(NaN);
+ }, /Invalid argument/);
+
+ done();
+ });
+ });
+
+ describe('near', function() {
+ // near nearSphere
+ describe('with 0 args', function() {
+ it('is compatible with geometry()', function(done) {
+ var q = mquery().where('x').near().geometry({ type: 'Point', coordinates: [180, 11] });
+ assert.deepEqual({ $near: {$geometry: {type:'Point', coordinates: [180,11]}}}, q._conditions.x);
+ done();
+ });
+ });
+
+ describe('with 1 arg', function() {
+ it('throws if not used after where()', function() {
+ assert.throws(function() {
+ mquery().near(1);
+ }, /must be used after where/);
+ });
+ it('does not throw if used after where()', function() {
+ assert.doesNotThrow(function() {
+ mquery().where('loc').near({center:[1,1]});
+ });
+ });
+ });
+ describe('with > 2 args', function() {
+ it('throws', function() {
+ assert.throws(function() {
+ mquery().near(1,2,3);
+ }, /Invalid argument/);
+ });
+ });
+
+ it('creates $geometry args for GeoJSON', function() {
+ var m = mquery().where('loc').near({ center: { type: 'Point', coordinates: [10,10] }});
+ assert.deepEqual({ $near: {$geometry: {type:'Point', coordinates: [10,10]}}}, m._conditions.loc);
+ });
+
+ it('expects `center`', function() {
+ assert.throws(function() {
+ mquery().near('loc', { maxDistance: 3 });
+ }, /center is required/);
+ assert.doesNotThrow(function() {
+ mquery().near('loc', { center: [3,4] });
+ });
+ });
+
+ it('accepts spherical conditions', function() {
+ var m = mquery().where('loc').near({ center: [1,2], spherical: true });
+ assert.deepEqual(m._conditions, { loc: { $nearSphere: [1,2]}});
+ });
+
+ it('is non-spherical by default', function() {
+ var m = mquery().where('loc').near({ center: [1,2] });
+ assert.deepEqual(m._conditions, { loc: { $near: [1,2]}});
+ });
+
+ it('supports maxDistance', function() {
+ var m = mquery().where('loc').near({ center: [1,2], maxDistance:4 });
+ assert.deepEqual(m._conditions, { loc: { $near: [1,2], $maxDistance: 4}});
+ });
+
+ it('supports minDistance', function() {
+ var m = mquery().where('loc').near({ center: [1,2], minDistance:4 });
+ assert.deepEqual(m._conditions, { loc: { $near: [1,2], $minDistance: 4}});
+ });
+
+ it('is chainable', function() {
+ var m = mquery().where('loc').near({ center: [1,2], maxDistance:4 }).find({ x: 1 });
+ assert.deepEqual(m._conditions, { loc: { $near: [1,2], $maxDistance: 4}, x: 1});
+ });
+
+ describe('supports passing GeoJSON, gh-13', function() {
+ it('with center', function() {
+ var m = mquery().where('loc').near({
+ center: { type: 'Point', coordinates: [1,1] },
+ maxDistance: 2
+ });
+
+ var expect = {
+ loc: {
+ $near: {
+ $geometry: {
+ type: 'Point',
+ coordinates : [1,1]
+ },
+ $maxDistance : 2
+ }
+ }
+ };
+
+ assert.deepEqual(m._conditions, expect);
+ });
+ });
+ });
+
+ // fields
+
+ describe('select', function() {
+ describe('with 0 args', function() {
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m, m.select());
+ });
+ });
+
+ it('accepts an object', function() {
+ var o = { x: 1, y: 1 };
+ var m = mquery().select(o);
+ assert.deepEqual(m._fields, o);
+ });
+
+ it('accepts a string', function() {
+ var o = 'x -y';
+ var m = mquery().select(o);
+ assert.deepEqual(m._fields, { x: 1, y: 0 });
+ });
+
+ it('does accept an array', function() {
+ var o = ['x', '-y'];
+ var m = mquery().select(o);
+ assert.deepEqual(m._fields, { x: 1, y: 0 });
+ });
+
+ it('merges previous arguments', function() {
+ var o = { x: 1, y: 0, a: 1 };
+ var m = mquery().select(o);
+ m.select('z -u w').select({ x: 0 });
+ assert.deepEqual(m._fields, {
+ x: 0,
+ y: 0,
+ z: 1,
+ u: 0,
+ w: 1,
+ a: 1
+ });
+ });
+
+ it('rejects non-string, object, arrays', function() {
+ assert.throws(function() {
+ mquery().select(function() {});
+ }, /Invalid select\(\) argument/);
+ });
+
+ it('accepts arguments objects', function() {
+ var m = mquery();
+ function t() {
+ m.select(arguments);
+ assert.deepEqual(m._fields, { x: 1, y: 0 });
+ }
+ t('x', '-y');
+ });
+
+ noDistinct('select');
+ });
+
+ describe('selected', function() {
+ it('returns true when fields have been selected', function(done) {
+ var m;
+
+ m = mquery().select({ name: 1 });
+ assert.ok(m.selected());
+
+ m = mquery().select('name');
+ assert.ok(m.selected());
+
+ done();
+ });
+
+ it('returns false when no fields have been selected', function(done) {
+ var m = mquery();
+ assert.strictEqual(false, m.selected());
+ done();
+ });
+ });
+
+ describe('selectedInclusively', function() {
+ describe('returns false', function() {
+ it('when no fields have been selected', function(done) {
+ assert.strictEqual(false, mquery().selectedInclusively());
+ assert.equal(false, mquery().select({}).selectedInclusively());
+ done();
+ });
+ it('when any fields have been excluded', function(done) {
+ assert.strictEqual(false, mquery().select('-name').selectedInclusively());
+ assert.strictEqual(false, mquery().select({ name: 0 }).selectedInclusively());
+ assert.strictEqual(false, mquery().select('name bio -_id').selectedInclusively());
+ assert.strictEqual(false, mquery().select({ name: 1, _id: 0 }).selectedInclusively());
+ done();
+ });
+ it('when using $meta', function(done) {
+ assert.strictEqual(false, mquery().select({ name: { $meta: 'textScore' } }).selectedInclusively());
+ done();
+ });
+ });
+
+ describe('returns true', function() {
+ it('when fields have been included', function(done) {
+ assert.equal(true, mquery().select('name').selectedInclusively());
+ assert.equal(true, mquery().select({ name:1 }).selectedInclusively());
+ done();
+ });
+ });
+ });
+
+ describe('selectedExclusively', function() {
+ describe('returns false', function() {
+ it('when no fields have been selected', function(done) {
+ assert.equal(false, mquery().selectedExclusively());
+ assert.equal(false, mquery().select({}).selectedExclusively());
+ done();
+ });
+ it('when fields have only been included', function(done) {
+ assert.equal(false, mquery().select('name').selectedExclusively());
+ assert.equal(false, mquery().select({ name: 1 }).selectedExclusively());
+ done();
+ });
+ });
+
+ describe('returns true', function() {
+ it('when any field has been excluded', function(done) {
+ assert.equal(true, mquery().select('-name').selectedExclusively());
+ assert.equal(true, mquery().select({ name:0 }).selectedExclusively());
+ assert.equal(true, mquery().select('-_id').selectedExclusively());
+ assert.strictEqual(true, mquery().select('name bio -_id').selectedExclusively());
+ assert.strictEqual(true, mquery().select({ name: 1, _id: 0 }).selectedExclusively());
+ done();
+ });
+ });
+ });
+
+ describe('slice', function() {
+ describe('with 0 args', function() {
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m, m.slice());
+ });
+ it('is a noop', function() {
+ var m = mquery().slice();
+ assert.deepEqual(m._fields, undefined);
+ });
+ });
+
+ describe('with 1 arg', function() {
+ it('throws if not called after where()', function() {
+ assert.throws(function() {
+ mquery().slice(1);
+ }, /must be used after where/);
+ assert.doesNotThrow(function() {
+ mquery().where('a').slice(1);
+ });
+ });
+ it('that is a number', function() {
+ var query = mquery();
+ query.where('collection').slice(5);
+ assert.deepEqual(query._fields, {collection: {$slice: 5}});
+ });
+ it('that is an array', function() {
+ var query = mquery();
+ query.where('collection').slice([5,10]);
+ assert.deepEqual(query._fields, {collection: {$slice: [5,10]}});
+ });
+ it('that is an object', function() {
+ var query = mquery();
+ query.slice({ collection: [5, 10] });
+ assert.deepEqual(query._fields, {collection: {$slice: [5,10]}});
+ });
+ });
+
+ describe('with 2 args', function() {
+ describe('and first is a number', function() {
+ it('throws if not called after where', function() {
+ assert.throws(function() {
+ mquery().slice(2,3);
+ }, /must be used after where/);
+ });
+ it('does not throw if used after where', function() {
+ var query = mquery();
+ query.where('collection').slice(2,3);
+ assert.deepEqual(query._fields, {collection: {$slice: [2,3]}});
+ });
+ });
+ it('and first is not a number', function() {
+ var query = mquery().slice('collection', [-5, 2]);
+ assert.deepEqual(query._fields, {collection: {$slice: [-5,2]}});
+ });
+ });
+
+ describe('with 3 args', function() {
+ it('works', function() {
+ var query = mquery();
+ query.slice('collection', 14, 10);
+ assert.deepEqual(query._fields, {collection: {$slice: [14, 10]}});
+ });
+ });
+
+ noDistinct('slice');
+ no('count', 'slice');
+ });
+
+ // options
+
+ describe('sort', function() {
+ describe('with 0 args', function() {
+ it('chains', function() {
+ var m = mquery();
+ assert.equal(m, m.sort());
+ });
+ it('has no affect', function() {
+ var m = mquery();
+ assert.equal(m.options.sort, undefined);
+ });
+ });
+
+ it('works', function() {
+ var query = mquery();
+ query.sort('a -c b');
+ assert.deepEqual(query.options.sort, { a : 1, b: 1, c : -1});
+
+ query = mquery();
+ query.sort({'a': 1, 'c': -1, 'b': 'asc', e: 'descending', f: 'ascending'});
+ assert.deepEqual(query.options.sort, {'a': 1, 'c': -1, 'b': 1, 'e': -1, 'f': 1});
+
+ query = mquery();
+ query.sort([['a', -1], ['c', 1], ['b', 'desc'], ['e', 'ascending'], ['f', 'descending']]);
+ assert.deepEqual(query.options.sort, [['a', -1], ['c', 1], ['b', -1], ['e', 1], ['f', -1]]);
+
+ query = mquery();
+ var e = undefined;
+ try {
+ query.sort([['a', 1], { 'b': 5 }]);
+ } catch (err) {
+ e = err;
+ }
+ assert.ok(e, 'uh oh. no error was thrown');
+ assert.equal(e.message, 'Invalid sort() argument, must be array of arrays');
+
+ query = mquery();
+ e = undefined;
+
+ try {
+ query.sort('a', 1, 'c', -1, 'b', 1);
+ } catch (err) {
+ e = err;
+ }
+ assert.ok(e, 'uh oh. no error was thrown');
+ assert.equal(e.message, 'Invalid sort() argument. Must be a string, object, or array.');
+ });
+
+ it('handles $meta sort options', function() {
+ var query = mquery();
+ query.sort({ score: { $meta : 'textScore' } });
+ assert.deepEqual(query.options.sort, { score : { $meta : 'textScore' } });
+ });
+
+ it('array syntax', function() {
+ var query = mquery();
+ query.sort([['field', 1], ['test', -1]]);
+ assert.deepEqual(query.options.sort, [['field', 1], ['test', -1]]);
+ });
+
+ it('throws with mixed array/object syntax', function() {
+ var query = mquery();
+ assert.throws(function() {
+ query.sort({ field: 1 }).sort([['test', -1]]);
+ }, /Can't mix sort syntaxes/);
+ assert.throws(function() {
+ query.sort([['field', 1]]).sort({ test: 1 });
+ }, /Can't mix sort syntaxes/);
+ });
+
+ it('works with maps', function() {
+ if (typeof Map === 'undefined') {
+ return this.skip();
+ }
+ var query = mquery();
+ query.sort(new Map().set('field', 1).set('test', -1));
+ assert.deepEqual(query.options.sort, new Map().set('field', 1).set('test', -1));
+ });
+ });
+
+ function simpleOption(type, options) {
+ describe(type, function() {
+ it('sets the ' + type + ' option', function() {
+ var m = mquery()[type](2);
+ var optionName = options.name || type;
+ assert.equal(2, m.options[optionName]);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m[type](3), m);
+ });
+
+ if (!options.distinct) noDistinct(type);
+ if (!options.count) no('count', type);
+ });
+ }
+
+ var negated = {
+ limit: {distinct: false, count: true},
+ skip: {distinct: false, count: true},
+ maxScan: {distinct: false, count: false},
+ batchSize: {distinct: false, count: false},
+ maxTime: {distinct: true, count: true, name: 'maxTimeMS' },
+ comment: {distinct: false, count: false}
+ };
+ Object.keys(negated).forEach(function(key) {
+ simpleOption(key, negated[key]);
+ });
+
+ describe('snapshot', function() {
+ it('works', function() {
+ var query;
+
+ query = mquery();
+ query.snapshot();
+ assert.equal(true, query.options.snapshot);
+
+ query = mquery();
+ query.snapshot(true);
+ assert.equal(true, query.options.snapshot);
+
+ query = mquery();
+ query.snapshot(false);
+ assert.equal(false, query.options.snapshot);
+ });
+ noDistinct('snapshot');
+ no('count', 'snapshot');
+ });
+
+ describe('hint', function() {
+ it('accepts an object', function() {
+ var query2 = mquery();
+ query2.hint({'a': 1, 'b': -1});
+ assert.deepEqual(query2.options.hint, {'a': 1, 'b': -1});
+ });
+
+ it('accepts a string', function() {
+ var query2 = mquery();
+ query2.hint('a');
+ assert.deepEqual(query2.options.hint, 'a');
+ });
+
+ it('rejects everything else', function() {
+ assert.throws(function() {
+ mquery().hint(['c']);
+ }, /Invalid hint./);
+ assert.throws(function() {
+ mquery().hint(1);
+ }, /Invalid hint./);
+ });
+
+ describe('does not have side affects', function() {
+ it('on invalid arg', function() {
+ var m = mquery();
+ try {
+ m.hint(1);
+ } catch (err) {
+ // ignore
+ }
+ assert.equal(undefined, m.options.hint);
+ });
+ it('on missing arg', function() {
+ var m = mquery().hint();
+ assert.equal(undefined, m.options.hint);
+ });
+ });
+
+ noDistinct('hint');
+ });
+
+ describe('j', function() {
+ it('works', function() {
+ var m = mquery().j(true);
+ assert.equal(true, m.options.j);
+ });
+ });
+
+ describe('slaveOk', function() {
+ it('works', function() {
+ var query;
+
+ query = mquery();
+ query.slaveOk();
+ assert.equal(true, query.options.slaveOk);
+
+ query = mquery();
+ query.slaveOk(true);
+ assert.equal(true, query.options.slaveOk);
+
+ query = mquery();
+ query.slaveOk(false);
+ assert.equal(false, query.options.slaveOk);
+ });
+ });
+
+ describe('read', function() {
+ it('sets associated readPreference option', function() {
+ var m = mquery();
+ m.read('p');
+ assert.equal('primary', m.options.readPreference);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m, m.read('sp'));
+ });
+ });
+
+ describe('readConcern', function() {
+ it('sets associated readConcern option', function() {
+ var m;
+
+ m = mquery();
+ m.readConcern('s');
+ assert.deepEqual({ level: 'snapshot' }, m.options.readConcern);
+
+ m = mquery();
+ m.r('local');
+ assert.deepEqual({ level: 'local' }, m.options.readConcern);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m, m.readConcern('lz'));
+ });
+ });
+
+ describe('tailable', function() {
+ it('works', function() {
+ var query;
+
+ query = mquery();
+ query.tailable();
+ assert.equal(true, query.options.tailable);
+
+ query = mquery();
+ query.tailable(true);
+ assert.equal(true, query.options.tailable);
+
+ query = mquery();
+ query.tailable(false);
+ assert.equal(false, query.options.tailable);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m, m.tailable());
+ });
+ noDistinct('tailable');
+ no('count', 'tailable');
+ });
+
+ describe('writeConcern', function() {
+ it('sets associated writeConcern option', function() {
+ var m;
+ m = mquery();
+ m.writeConcern('majority');
+ assert.equal('majority', m.options.w);
+
+ m = mquery();
+ m.writeConcern('m'); // m is alias of majority
+ assert.equal('majority', m.options.w);
+
+ m = mquery();
+ m.writeConcern(1);
+ assert.equal(1, m.options.w);
+ });
+ it('accepts object', function() {
+ var m;
+
+ m = mquery().writeConcern({ w: 'm', j: true, wtimeout: 1000 });
+ assert.equal('m', m.options.w); // check it does not convert m to majority
+ assert.equal(true, m.options.j);
+ assert.equal(1000, m.options.wtimeout);
+
+ m = mquery().w('m').w({j: false, wtimeout: 0 });
+ assert.equal('majority', m.options.w);
+ assert.strictEqual(false, m.options.j);
+ assert.strictEqual(0, m.options.wtimeout);
+ });
+ it('is chainable', function() {
+ var m = mquery();
+ assert.equal(m, m.writeConcern('majority'));
+ });
+ });
+
+ // query utilities
+
+ describe('merge', function() {
+ describe('with falsy arg', function() {
+ it('returns itself', function() {
+ var m = mquery();
+ assert.equal(m, m.merge());
+ assert.equal(m, m.merge(null));
+ assert.equal(m, m.merge(0));
+ });
+ });
+ describe('with an argument', function() {
+ describe('that is not a query or plain object', function() {
+ it('throws', function() {
+ assert.throws(function() {
+ mquery().merge([]);
+ }, /Invalid argument/);
+ assert.throws(function() {
+ mquery().merge('merge');
+ }, /Invalid argument/);
+ assert.doesNotThrow(function() {
+ mquery().merge({});
+ }, /Invalid argument/);
+ });
+ });
+
+ describe('that is a query', function() {
+ it('merges conditions, field selection, and options', function() {
+ var m = mquery({ x: 'hi' }, { select: 'x y', another: true });
+ var n = mquery().merge(m);
+ assert.deepEqual(n._conditions, m._conditions);
+ assert.deepEqual(n._fields, m._fields);
+ assert.deepEqual(n.options, m.options);
+ });
+ it('clones update arguments', function(done) {
+ var original = { $set: { iTerm: true }};
+ var m = mquery().update(original);
+ var n = mquery().merge(m);
+ m.update({ $set: { x: 2 }});
+ assert.notDeepEqual(m._update, n._update);
+ done();
+ });
+ it('is chainable', function() {
+ var m = mquery({ x: 'hi' });
+ var n = mquery();
+ assert.equal(n, n.merge(m));
+ });
+ });
+
+ describe('that is an object', function() {
+ it('merges', function() {
+ var m = { x: 'hi' };
+ var n = mquery().merge(m);
+ assert.deepEqual(n._conditions, { x: 'hi' });
+ });
+ it('clones update arguments', function(done) {
+ var original = { $set: { iTerm: true }};
+ var m = mquery().update(original);
+ var n = mquery().merge(original);
+ m.update({ $set: { x: 2 }});
+ assert.notDeepEqual(m._update, n._update);
+ done();
+ });
+ it('is chainable', function() {
+ var m = { x: 'hi' };
+ var n = mquery();
+ assert.equal(n, n.merge(m));
+ });
+ });
+ });
+ });
+
+ // queries
+
+ describe('find', function() {
+ describe('with no callback', function() {
+ it('does not execute', function() {
+ var m = mquery();
+ assert.doesNotThrow(function() {
+ m.find();
+ });
+ assert.doesNotThrow(function() {
+ m.find({ x: 1 });
+ });
+ });
+ });
+
+ it('is chainable', function() {
+ var m = mquery().find({ x: 1 }).find().find({ y: 2 });
+ assert.deepEqual(m._conditions, {x:1,y:2});
+ });
+
+ it('merges other queries', function() {
+ var m = mquery({ name: 'mquery' });
+ m.tailable();
+ m.select('_id');
+ var a = mquery().find(m);
+ assert.deepEqual(a._conditions, m._conditions);
+ assert.deepEqual(a.options, m.options);
+ assert.deepEqual(a._fields, m._fields);
+ });
+
+ describe('executes', function() {
+ before(function(done) {
+ col.insert({ name: 'mquery' }, { safe: true }, done);
+ });
+
+ after(function(done) {
+ col.remove({ name: 'mquery' }, done);
+ });
+
+ it('when criteria is passed with a callback', function(done) {
+ mquery(col).find({ name: 'mquery' }, function(err, docs) {
+ assert.ifError(err);
+ assert.equal(1, docs.length);
+ done();
+ });
+ });
+ it('when Query is passed with a callback', function(done) {
+ var m = mquery({ name: 'mquery' });
+ mquery(col).find(m, function(err, docs) {
+ assert.ifError(err);
+ assert.equal(1, docs.length);
+ done();
+ });
+ });
+ it('when just a callback is passed', function(done) {
+ mquery({ name: 'mquery' }).collection(col).find(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(1, docs.length);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('findOne', function() {
+ describe('with no callback', function() {
+ it('does not execute', function() {
+ var m = mquery();
+ assert.doesNotThrow(function() {
+ m.findOne();
+ });
+ assert.doesNotThrow(function() {
+ m.findOne({ x: 1 });
+ });
+ });
+ });
+
+ it('is chainable', function() {
+ var m = mquery();
+ var n = m.findOne({ x: 1 }).findOne().findOne({ y: 2 });
+ assert.equal(m, n);
+ assert.deepEqual(m._conditions, {x:1,y:2});
+ assert.equal('findOne', m.op);
+ });
+
+ it('merges other queries', function() {
+ var m = mquery({ name: 'mquery' });
+ m.read('nearest');
+ m.select('_id');
+ var a = mquery().findOne(m);
+ assert.deepEqual(a._conditions, m._conditions);
+ assert.deepEqual(a.options, m.options);
+ assert.deepEqual(a._fields, m._fields);
+ });
+
+ describe('executes', function() {
+ before(function(done) {
+ col.insert({ name: 'mquery findone' }, { safe: true }, done);
+ });
+
+ after(function(done) {
+ col.remove({ name: 'mquery findone' }, done);
+ });
+
+ it('when criteria is passed with a callback', function(done) {
+ mquery(col).findOne({ name: 'mquery findone' }, function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ assert.equal('mquery findone', doc.name);
+ done();
+ });
+ });
+ it('when Query is passed with a callback', function(done) {
+ var m = mquery(col).where({ name: 'mquery findone' });
+ mquery(col).findOne(m, function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ assert.equal('mquery findone', doc.name);
+ done();
+ });
+ });
+ it('when just a callback is passed', function(done) {
+ mquery({ name: 'mquery findone' }).collection(col).findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ assert.equal('mquery findone', doc.name);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('count', function() {
+ describe('with no callback', function() {
+ it('does not execute', function() {
+ var m = mquery();
+ assert.doesNotThrow(function() {
+ m.count();
+ });
+ assert.doesNotThrow(function() {
+ m.count({ x: 1 });
+ });
+ });
+ });
+
+ it('is chainable', function() {
+ var m = mquery();
+ var n = m.count({ x: 1 }).count().count({ y: 2 });
+ assert.equal(m, n);
+ assert.deepEqual(m._conditions, {x:1,y:2});
+ assert.equal('count', m.op);
+ });
+
+ it('merges other queries', function() {
+ var m = mquery({ name: 'mquery' });
+ m.read('nearest');
+ m.select('_id');
+ var a = mquery().count(m);
+ assert.deepEqual(a._conditions, m._conditions);
+ assert.deepEqual(a.options, m.options);
+ assert.deepEqual(a._fields, m._fields);
+ });
+
+ describe('executes', function() {
+ before(function(done) {
+ col.insert({ name: 'mquery count' }, { safe: true }, done);
+ });
+
+ after(function(done) {
+ col.remove({ name: 'mquery count' }, done);
+ });
+
+ it('when criteria is passed with a callback', function(done) {
+ mquery(col).count({ name: 'mquery count' }, function(err, count) {
+ assert.ifError(err);
+ assert.ok(count);
+ assert.ok(1 === count);
+ done();
+ });
+ });
+ it('when Query is passed with a callback', function(done) {
+ var m = mquery({ name: 'mquery count' });
+ mquery(col).count(m, function(err, count) {
+ assert.ifError(err);
+ assert.ok(count);
+ assert.ok(1 === count);
+ done();
+ });
+ });
+ it('when just a callback is passed', function(done) {
+ mquery({ name: 'mquery count' }).collection(col).count(function(err, count) {
+ assert.ifError(err);
+ assert.ok(1 === count);
+ done();
+ });
+ });
+ });
+
+ describe('validates its option', function() {
+ it('sort', function(done) {
+ assert.doesNotThrow(function() {
+ mquery().sort('x').count();
+ });
+ done();
+ });
+
+ it('select', function(done) {
+ assert.throws(function() {
+ mquery().select('x').count();
+ }, /field selection and slice cannot be used with count/);
+ done();
+ });
+
+ it('slice', function(done) {
+ assert.throws(function() {
+ mquery().where('x').slice(-3).count();
+ }, /field selection and slice cannot be used with count/);
+ done();
+ });
+
+ it('limit', function(done) {
+ assert.doesNotThrow(function() {
+ mquery().limit(3).count();
+ });
+ done();
+ });
+
+ it('skip', function(done) {
+ assert.doesNotThrow(function() {
+ mquery().skip(3).count();
+ });
+ done();
+ });
+
+ it('batchSize', function(done) {
+ assert.throws(function() {
+ mquery({}, { batchSize: 3 }).count();
+ }, /batchSize cannot be used with count/);
+ done();
+ });
+
+ it('comment', function(done) {
+ assert.throws(function() {
+ mquery().comment('mquery').count();
+ }, /comment cannot be used with count/);
+ done();
+ });
+
+ it('maxScan', function(done) {
+ assert.throws(function() {
+ mquery().maxScan(300).count();
+ }, /maxScan cannot be used with count/);
+ done();
+ });
+
+ it('snapshot', function(done) {
+ assert.throws(function() {
+ mquery().snapshot().count();
+ }, /snapshot cannot be used with count/);
+ done();
+ });
+
+ it('tailable', function(done) {
+ assert.throws(function() {
+ mquery().tailable().count();
+ }, /tailable cannot be used with count/);
+ done();
+ });
+ });
+ });
+
+ describe('distinct', function() {
+ describe('with no callback', function() {
+ it('does not execute', function() {
+ var m = mquery();
+ assert.doesNotThrow(function() {
+ m.distinct();
+ });
+ assert.doesNotThrow(function() {
+ m.distinct('name');
+ });
+ assert.doesNotThrow(function() {
+ m.distinct({ name: 'mquery distinct' });
+ });
+ assert.doesNotThrow(function() {
+ m.distinct({ name: 'mquery distinct' }, 'name');
+ });
+ });
+ });
+
+ it('is chainable', function() {
+ var m = mquery({x:1}).distinct('name');
+ var n = m.distinct({y:2});
+ assert.equal(m, n);
+ assert.deepEqual(n._conditions, {x:1, y:2});
+ assert.equal('name', n._distinct);
+ assert.equal('distinct', n.op);
+ });
+
+ it('overwrites field', function() {
+ var m = mquery({ name: 'mquery' }).distinct('name');
+ m.distinct('rename');
+ assert.equal(m._distinct, 'rename');
+ m.distinct({x:1}, 'renamed');
+ assert.equal(m._distinct, 'renamed');
+ });
+
+ it('merges other queries', function() {
+ var m = mquery().distinct({ name: 'mquery' }, 'age');
+ m.read('nearest');
+ var a = mquery().distinct(m);
+ assert.deepEqual(a._conditions, m._conditions);
+ assert.deepEqual(a.options, m.options);
+ assert.deepEqual(a._fields, m._fields);
+ assert.deepEqual(a._distinct, m._distinct);
+ });
+
+ describe('executes', function() {
+ before(function(done) {
+ col.insert({ name: 'mquery distinct', age: 1 }, { safe: true }, done);
+ });
+
+ after(function(done) {
+ col.remove({ name: 'mquery distinct' }, done);
+ });
+
+ it('when distinct arg is passed with a callback', function(done) {
+ mquery(col).distinct('distinct', function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ done();
+ });
+ });
+ describe('when criteria is passed with a callback', function() {
+ it('if distinct arg was declared', function(done) {
+ mquery(col).distinct('age').distinct({ name: 'mquery distinct' }, function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ done();
+ });
+ });
+ it('but not if distinct arg was not declared', function() {
+ assert.throws(function() {
+ mquery(col).distinct({ name: 'mquery distinct' }, function() {});
+ }, /No value for `distinct`/);
+ });
+ });
+ describe('when Query is passed with a callback', function() {
+ var m = mquery({ name: 'mquery distinct' });
+ it('if distinct arg was declared', function(done) {
+ mquery(col).distinct('age').distinct(m, function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ done();
+ });
+ });
+ it('but not if distinct arg was not declared', function() {
+ assert.throws(function() {
+ mquery(col).distinct(m, function() {});
+ }, /No value for `distinct`/);
+ });
+ });
+ describe('when just a callback is passed', function() {
+ it('if distinct arg was declared', function(done) {
+ var m = mquery({ name: 'mquery distinct' });
+ m.collection(col);
+ m.distinct('age');
+ m.distinct(function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ done();
+ });
+ });
+ it('but not if no distinct arg was declared', function() {
+ var m = mquery();
+ m.collection(col);
+ assert.throws(function() {
+ m.distinct(function() {});
+ }, /No value for `distinct`/);
+ });
+ });
+ });
+
+ describe('validates its option', function() {
+ it('sort', function(done) {
+ assert.throws(function() {
+ mquery().sort('x').distinct();
+ }, /sort cannot be used with distinct/);
+ done();
+ });
+
+ it('select', function(done) {
+ assert.throws(function() {
+ mquery().select('x').distinct();
+ }, /field selection and slice cannot be used with distinct/);
+ done();
+ });
+
+ it('slice', function(done) {
+ assert.throws(function() {
+ mquery().where('x').slice(-3).distinct();
+ }, /field selection and slice cannot be used with distinct/);
+ done();
+ });
+
+ it('limit', function(done) {
+ assert.throws(function() {
+ mquery().limit(3).distinct();
+ }, /limit cannot be used with distinct/);
+ done();
+ });
+
+ it('skip', function(done) {
+ assert.throws(function() {
+ mquery().skip(3).distinct();
+ }, /skip cannot be used with distinct/);
+ done();
+ });
+
+ it('batchSize', function(done) {
+ assert.throws(function() {
+ mquery({}, { batchSize: 3 }).distinct();
+ }, /batchSize cannot be used with distinct/);
+ done();
+ });
+
+ it('comment', function(done) {
+ assert.throws(function() {
+ mquery().comment('mquery').distinct();
+ }, /comment cannot be used with distinct/);
+ done();
+ });
+
+ it('maxScan', function(done) {
+ assert.throws(function() {
+ mquery().maxScan(300).distinct();
+ }, /maxScan cannot be used with distinct/);
+ done();
+ });
+
+ it('snapshot', function(done) {
+ assert.throws(function() {
+ mquery().snapshot().distinct();
+ }, /snapshot cannot be used with distinct/);
+ done();
+ });
+
+ it('hint', function(done) {
+ assert.throws(function() {
+ mquery().hint({ x: 1 }).distinct();
+ }, /hint cannot be used with distinct/);
+ done();
+ });
+
+ it('tailable', function(done) {
+ assert.throws(function() {
+ mquery().tailable().distinct();
+ }, /tailable cannot be used with distinct/);
+ done();
+ });
+ });
+ });
+
+ describe('update', function() {
+ describe('with no callback', function() {
+ it('does not execute', function() {
+ var m = mquery();
+ assert.doesNotThrow(function() {
+ m.update({ name: 'old' }, { name: 'updated' }, { multi: true });
+ });
+ assert.doesNotThrow(function() {
+ m.update({ name: 'old' }, { name: 'updated' });
+ });
+ assert.doesNotThrow(function() {
+ m.update({ name: 'updated' });
+ });
+ assert.doesNotThrow(function() {
+ m.update();
+ });
+ });
+ });
+
+ it('is chainable', function() {
+ var m = mquery({x:1}).update({ y: 2 });
+ var n = m.where({y:2});
+ assert.equal(m, n);
+ assert.deepEqual(n._conditions, {x:1, y:2});
+ assert.deepEqual({ y: 2 }, n._update);
+ assert.equal('update', n.op);
+ });
+
+ it('merges update doc arg', function() {
+ var a = [1,2];
+ var m = mquery().where({ name: 'mquery' }).update({ x: 'stuff', a: a });
+ m.update({ z: 'stuff' });
+ assert.deepEqual(m._update, { z: 'stuff', x: 'stuff', a: a });
+ assert.deepEqual(m._conditions, { name: 'mquery' });
+ assert.ok(!m.options.overwrite);
+ m.update({}, { z: 'renamed' }, { overwrite: true });
+ assert.ok(m.options.overwrite === true);
+ assert.deepEqual(m._conditions, { name: 'mquery' });
+ assert.deepEqual(m._update, { z: 'renamed', x: 'stuff', a: a });
+ a.push(3);
+ assert.notDeepEqual(m._update, { z: 'renamed', x: 'stuff', a: a });
+ });
+
+ it('merges other options', function() {
+ var m = mquery();
+ m.setOptions({ overwrite: true });
+ m.update({ age: 77 }, { name: 'pagemill' }, { multi: true });
+ assert.deepEqual({ age: 77 }, m._conditions);
+ assert.deepEqual({ name: 'pagemill' }, m._update);
+ assert.deepEqual({ overwrite: true, multi: true }, m.options);
+ });
+
+ describe('executes', function() {
+ var id;
+ before(function(done) {
+ col.insert({ name: 'mquery update', age: 1 }, { safe: true }, function(err, res) {
+ id = res.insertedIds[0];
+ done();
+ });
+ });
+
+ after(function(done) {
+ col.remove({ _id: id }, done);
+ });
+
+ describe('when conds + doc + opts + callback passed', function() {
+ it('works', function(done) {
+ var m = mquery(col).where({ _id: id });
+ m.update({}, { name: 'Sparky' }, { safe: true }, function(err, res) {
+ assert.ifError(err);
+ assert.equal(res.result.n, 1);
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(doc.name, 'Sparky');
+ done();
+ });
+ });
+ });
+ });
+
+ describe('when conds + doc + callback passed', function() {
+ it('works', function(done) {
+ var m = mquery(col).update({ _id: id }, { name: 'fairgrounds' }, function(err, num) {
+ assert.ifError(err);
+ assert.ok(1, num);
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(doc.name, 'fairgrounds');
+ done();
+ });
+ });
+ });
+ });
+
+ describe('when doc + callback passed', function() {
+ it('works', function(done) {
+ var m = mquery(col).where({ _id: id }).update({ name: 'changed' }, function(err, num) {
+ assert.ifError(err);
+ assert.ok(1, num);
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(doc.name, 'changed');
+ done();
+ });
+ });
+ });
+ });
+
+ describe('when just callback passed', function() {
+ it('works', function(done) {
+ var m = mquery(col).where({ _id: id });
+ m.setOptions({ safe: true });
+ m.update({ name: 'Frankenweenie' });
+ m.update(function(err, res) {
+ assert.ifError(err);
+ assert.equal(res.result.n, 1);
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(doc.name, 'Frankenweenie');
+ done();
+ });
+ });
+ });
+ });
+
+ describe('without a callback', function() {
+ it('when forced by exec()', function(done) {
+ var m = mquery(col).where({ _id: id });
+ m.setOptions({ safe: true, multi: true });
+ m.update({ name: 'forced' });
+
+ var update = m._collection.update;
+ m._collection.update = function(conds, doc, opts) {
+ m._collection.update = update;
+
+ assert.ok(opts.safe);
+ assert.ok(true === opts.multi);
+ assert.equal('forced', doc.$set.name);
+ done();
+ };
+
+ m.exec();
+ });
+ });
+
+ describe('except when update doc is empty and missing overwrite flag', function() {
+ it('works', function(done) {
+ var m = mquery(col).where({ _id: id });
+ m.setOptions({ safe: true });
+ m.update({ }, function(err, num) {
+ assert.ifError(err);
+ assert.ok(0 === num);
+ setTimeout(function() {
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(3, mquery.utils.keys(doc).length);
+ assert.equal(id, doc._id.toString());
+ assert.equal('Frankenweenie', doc.name);
+ done();
+ });
+ }, 300);
+ });
+ });
+ });
+
+ describe('when update doc is set with overwrite flag', function() {
+ it('works', function(done) {
+ var m = mquery(col).where({ _id: id });
+ m.setOptions({ safe: true, overwrite: true });
+ m.update({ all: 'yep', two: 2 }, function(err, res) {
+ assert.ifError(err);
+ assert.equal(res.result.n, 1);
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(3, mquery.utils.keys(doc).length);
+ assert.equal('yep', doc.all);
+ assert.equal(2, doc.two);
+ assert.equal(id, doc._id.toString());
+ done();
+ });
+ });
+ });
+ });
+
+ describe('when update doc is empty with overwrite flag', function() {
+ it('works', function(done) {
+ var m = mquery(col).where({ _id: id });
+ m.setOptions({ safe: true, overwrite: true });
+ m.update({ }, function(err, res) {
+ assert.ifError(err);
+ assert.equal(res.result.n, 1);
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(1, mquery.utils.keys(doc).length);
+ assert.equal(id, doc._id.toString());
+ done();
+ });
+ });
+ });
+ });
+
+ describe('when boolean (true) - exec()', function() {
+ it('works', function(done) {
+ var m = mquery(col).where({ _id: id });
+ m.update({ name: 'bool' }).update(true);
+ setTimeout(function() {
+ m.findOne(function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ assert.equal('bool', doc.name);
+ done();
+ });
+ }, 300);
+ });
+ });
+ });
+ });
+
+ describe('remove', function() {
+ describe('with 0 args', function() {
+ var name = 'remove: no args test';
+ before(function(done) {
+ col.insert({ name: name }, { safe: true }, done);
+ });
+ after(function(done) {
+ col.remove({ name: name }, { safe: true }, done);
+ });
+
+ it('does not execute', function(done) {
+ var remove = col.remove;
+ col.remove = function() {
+ col.remove = remove;
+ done(new Error('remove executed!'));
+ };
+
+ mquery(col).where({ name: name }).remove();
+ setTimeout(function() {
+ col.remove = remove;
+ done();
+ }, 10);
+ });
+
+ it('chains', function() {
+ var m = mquery();
+ assert.equal(m, m.remove());
+ });
+ });
+
+ describe('with 1 argument', function() {
+ var name = 'remove: 1 arg test';
+ before(function(done) {
+ col.insert({ name: name }, { safe: true }, done);
+ });
+ after(function(done) {
+ col.remove({ name: name }, { safe: true }, done);
+ });
+
+ describe('that is a', function() {
+ it('plain object', function() {
+ var m = mquery(col).remove({ name: 'Whiskers' });
+ m.remove({ color: '#fff' });
+ assert.deepEqual({ name: 'Whiskers', color: '#fff' }, m._conditions);
+ });
+
+ it('query', function() {
+ var q = mquery({ color: '#fff' });
+ var m = mquery(col).remove({ name: 'Whiskers' });
+ m.remove(q);
+ assert.deepEqual({ name: 'Whiskers', color: '#fff' }, m._conditions);
+ });
+
+ it('function', function(done) {
+ mquery(col, { safe: true }).where({name: name}).remove(function(err) {
+ assert.ifError(err);
+ mquery(col).findOne({ name: name }, function(err, doc) {
+ assert.ifError(err);
+ assert.equal(null, doc);
+ done();
+ });
+ });
+ });
+
+ it('boolean (true) - execute', function(done) {
+ col.insert({ name: name }, { safe: true }, function(err) {
+ assert.ifError(err);
+ mquery(col).findOne({ name: name }, function(err, doc) {
+ assert.ifError(err);
+ assert.ok(doc);
+ mquery(col).remove(true);
+ setTimeout(function() {
+ mquery(col).find(function(err, docs) {
+ assert.ifError(err);
+ assert.ok(docs);
+ assert.equal(0, docs.length);
+ done();
+ });
+ }, 300);
+ });
+ });
+ });
+ });
+ });
+
+ describe('with 2 arguments', function() {
+ var name = 'remove: 2 arg test';
+ beforeEach(function(done) {
+ col.remove({}, { safe: true }, function(err) {
+ assert.ifError(err);
+ col.insert([{ name: 'shelly' }, { name: name }], { safe: true }, function(err) {
+ assert.ifError(err);
+ mquery(col).find(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(2, docs.length);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('plain object + callback', function() {
+ it('works', function(done) {
+ mquery(col).remove({ name: name }, function(err) {
+ assert.ifError(err);
+ mquery(col).find(function(err, docs) {
+ assert.ifError(err);
+ assert.ok(docs);
+ assert.equal(1, docs.length);
+ assert.equal('shelly', docs[0].name);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('mquery + callback', function() {
+ it('works', function(done) {
+ var m = mquery({ name: name });
+ mquery(col).remove(m, function(err) {
+ assert.ifError(err);
+ mquery(col).find(function(err, docs) {
+ assert.ifError(err);
+ assert.ok(docs);
+ assert.equal(1, docs.length);
+ assert.equal('shelly', docs[0].name);
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+
+ function validateFindAndModifyOptions(method) {
+ describe('validates its option', function() {
+ it('sort', function(done) {
+ assert.doesNotThrow(function() {
+ mquery().sort('x')[method]();
+ });
+ done();
+ });
+
+ it('select', function(done) {
+ assert.doesNotThrow(function() {
+ mquery().select('x')[method]();
+ });
+ done();
+ });
+
+ it('limit', function(done) {
+ assert.throws(function() {
+ mquery().limit(3)[method]();
+ }, new RegExp('limit cannot be used with ' + method));
+ done();
+ });
+
+ it('skip', function(done) {
+ assert.throws(function() {
+ mquery().skip(3)[method]();
+ }, new RegExp('skip cannot be used with ' + method));
+ done();
+ });
+
+ it('batchSize', function(done) {
+ assert.throws(function() {
+ mquery({}, { batchSize: 3 })[method]();
+ }, new RegExp('batchSize cannot be used with ' + method));
+ done();
+ });
+
+ it('maxScan', function(done) {
+ assert.throws(function() {
+ mquery().maxScan(300)[method]();
+ }, new RegExp('maxScan cannot be used with ' + method));
+ done();
+ });
+
+ it('snapshot', function(done) {
+ assert.throws(function() {
+ mquery().snapshot()[method]();
+ }, new RegExp('snapshot cannot be used with ' + method));
+ done();
+ });
+
+ it('hint', function(done) {
+ assert.throws(function() {
+ mquery().hint({ x: 1 })[method]();
+ }, new RegExp('hint cannot be used with ' + method));
+ done();
+ });
+
+ it('tailable', function(done) {
+ assert.throws(function() {
+ mquery().tailable()[method]();
+ }, new RegExp('tailable cannot be used with ' + method));
+ done();
+ });
+
+ it('comment', function(done) {
+ assert.throws(function() {
+ mquery().comment('mquery')[method]();
+ }, new RegExp('comment cannot be used with ' + method));
+ done();
+ });
+ });
+ }
+
+ describe('findOneAndUpdate', function() {
+ var name = 'findOneAndUpdate + fn';
+
+ validateFindAndModifyOptions('findOneAndUpdate');
+
+ describe('with 0 args', function() {
+ it('makes no changes', function() {
+ var m = mquery();
+ var n = m.findOneAndUpdate();
+ assert.deepEqual(m, n);
+ });
+ });
+ describe('with 1 arg', function() {
+ describe('that is an object', function() {
+ it('updates the doc', function() {
+ var m = mquery();
+ var n = m.findOneAndUpdate({ $set: { name: '1 arg' }});
+ assert.deepEqual(n._update, { $set: { name: '1 arg' }});
+ });
+ });
+ describe('that is a query', function() {
+ it('updates the doc', function() {
+ var m = mquery({ name: name }).update({ x: 1 });
+ var n = mquery().findOneAndUpdate(m);
+ assert.deepEqual(n._update, { x: 1 });
+ });
+ });
+ it('that is a function', function(done) {
+ col.insert({ name: name }, { safe: true }, function(err) {
+ assert.ifError(err);
+ var m = mquery({ name: name }).collection(col);
+ name = '1 arg';
+ var n = m.update({ $set: { name: name }});
+ n.findOneAndUpdate(function(err, res) {
+ assert.ifError(err);
+ assert.ok(res.value);
+ assert.equal(name, res.value.name);
+ done();
+ });
+ });
+ });
+ });
+ describe('with 2 args', function() {
+ it('conditions + update', function() {
+ var m = mquery(col);
+ m.findOneAndUpdate({ name: name }, { age: 100 });
+ assert.deepEqual({ name: name }, m._conditions);
+ assert.deepEqual({ age: 100 }, m._update);
+ });
+ it('query + update', function() {
+ var n = mquery({ name: name });
+ var m = mquery(col);
+ m.findOneAndUpdate(n, { age: 100 });
+ assert.deepEqual({ name: name }, m._conditions);
+ assert.deepEqual({ age: 100 }, m._update);
+ });
+ it('update + callback', function(done) {
+ var m = mquery(col).where({ name: name });
+ m.findOneAndUpdate({}, { $inc: { age: 10 }}, { new: true }, function(err, res) {
+ assert.ifError(err);
+ assert.equal(10, res.value.age);
+ done();
+ });
+ });
+ });
+ describe('with 3 args', function() {
+ it('conditions + update + options', function() {
+ var m = mquery();
+ var n = m.findOneAndUpdate({ name: name }, { works: true }, { new: false });
+ assert.deepEqual({ name: name}, n._conditions);
+ assert.deepEqual({ works: true }, n._update);
+ assert.deepEqual({ new: false }, n.options);
+ });
+ it('conditions + update + callback', function(done) {
+ var m = mquery(col);
+ m.findOneAndUpdate({ name: name }, { works: true }, { new: true }, function(err, res) {
+ assert.ifError(err);
+ assert.ok(res.value);
+ assert.equal(name, res.value.name);
+ assert.ok(true === res.value.works);
+ done();
+ });
+ });
+ });
+ describe('with 4 args', function() {
+ it('conditions + update + options + callback', function(done) {
+ var m = mquery(col);
+ m.findOneAndUpdate({ name: name }, { works: false }, { new: false }, function(err, res) {
+ assert.ifError(err);
+ assert.ok(res.value);
+ assert.equal(name, res.value.name);
+ assert.ok(true === res.value.works);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('findOneAndRemove', function() {
+ var name = 'findOneAndRemove';
+
+ validateFindAndModifyOptions('findOneAndRemove');
+
+ describe('with 0 args', function() {
+ it('makes no changes', function() {
+ var m = mquery();
+ var n = m.findOneAndRemove();
+ assert.deepEqual(m, n);
+ });
+ });
+ describe('with 1 arg', function() {
+ describe('that is an object', function() {
+ it('updates the doc', function() {
+ var m = mquery();
+ var n = m.findOneAndRemove({ name: '1 arg' });
+ assert.deepEqual(n._conditions, { name: '1 arg' });
+ });
+ });
+ describe('that is a query', function() {
+ it('updates the doc', function() {
+ var m = mquery({ name: name });
+ var n = m.findOneAndRemove(m);
+ assert.deepEqual(n._conditions, { name: name });
+ });
+ });
+ it('that is a function', function(done) {
+ col.insert({ name: name }, { safe: true }, function(err) {
+ assert.ifError(err);
+ var m = mquery({ name: name }).collection(col);
+ m.findOneAndRemove(function(err, res) {
+ assert.ifError(err);
+ assert.ok(res.value);
+ assert.equal(name, res.value.name);
+ done();
+ });
+ });
+ });
+ });
+ describe('with 2 args', function() {
+ it('conditions + options', function() {
+ var m = mquery(col);
+ m.findOneAndRemove({ name: name }, { new: false });
+ assert.deepEqual({ name: name }, m._conditions);
+ assert.deepEqual({ new: false }, m.options);
+ });
+ it('query + options', function() {
+ var n = mquery({ name: name });
+ var m = mquery(col);
+ m.findOneAndRemove(n, { sort: { x: 1 }});
+ assert.deepEqual({ name: name }, m._conditions);
+ assert.deepEqual({ sort: { 'x': 1 }}, m.options);
+ });
+ it('conditions + callback', function(done) {
+ col.insert({ name: name }, { safe: true }, function(err) {
+ assert.ifError(err);
+ var m = mquery(col);
+ m.findOneAndRemove({ name: name }, function(err, res) {
+ assert.ifError(err);
+ assert.equal(name, res.value.name);
+ done();
+ });
+ });
+ });
+ it('query + callback', function(done) {
+ col.insert({ name: name }, { safe: true }, function(err) {
+ assert.ifError(err);
+ var n = mquery({ name: name });
+ var m = mquery(col);
+ m.findOneAndRemove(n, function(err, res) {
+ assert.ifError(err);
+ assert.equal(name, res.value.name);
+ done();
+ });
+ });
+ });
+ });
+ describe('with 3 args', function() {
+ it('conditions + options + callback', function(done) {
+ name = 'findOneAndRemove + conds + options + cb';
+ col.insert([{ name: name }, { name: 'a' }], { safe: true }, function(err) {
+ assert.ifError(err);
+ var m = mquery(col);
+ m.findOneAndRemove({ name: name }, { sort: { name: 1 }}, function(err, res) {
+ assert.ifError(err);
+ assert.ok(res.value);
+ assert.equal(name, res.value.name);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ describe('exec', function() {
+ beforeEach(function(done) {
+ col.insert([{ name: 'exec', age: 1 }, { name: 'exec', age: 2 }], done);
+ });
+
+ afterEach(function(done) {
+ mquery(col).remove(done);
+ });
+
+ it('requires an op', function() {
+ assert.throws(function() {
+ mquery().exec();
+ }, /Missing query type/);
+ });
+
+ describe('find', function() {
+ it('works', function(done) {
+ var m = mquery(col).find({ name: 'exec' });
+ m.exec(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(2, docs.length);
+ done();
+ });
+ });
+
+ it('works with readPreferences', function(done) {
+ var m = mquery(col).find({ name: 'exec' });
+ try {
+ var rp = new require('mongodb').ReadPreference('primary');
+ m.read(rp);
+ } catch (e) {
+ done(e.code === 'MODULE_NOT_FOUND' ? null : e);
+ return;
+ }
+ m.exec(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(2, docs.length);
+ done();
+ });
+ });
+
+ it('works with hint', function(done) {
+ mquery(col).hint({ _id: 1 }).find({ name: 'exec' }).exec(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(2, docs.length);
+
+ mquery(col).hint('_id_').find({ age: 1 }).exec(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(1, docs.length);
+ done();
+ });
+ });
+ });
+
+ it('works with readConcern', function(done) {
+ var m = mquery(col).find({ name: 'exec' });
+ m.readConcern('l');
+ m.exec(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(2, docs.length);
+ done();
+ });
+ });
+
+ it('works with collation', function(done) {
+ var m = mquery(col).find({ name: 'EXEC' });
+ m.collation({ locale: 'en_US', strength: 1 });
+ m.exec(function(err, docs) {
+ assert.ifError(err);
+ assert.equal(2, docs.length);
+ done();
+ });
+ });
+ });
+
+ it('findOne', function(done) {
+ var m = mquery(col).findOne({ age: 2 });
+ m.exec(function(err, doc) {
+ assert.ifError(err);
+ assert.equal(2, doc.age);
+ done();
+ });
+ });
+
+ it('count', function(done) {
+ var m = mquery(col).count({ name: 'exec' });
+ m.exec(function(err, count) {
+ assert.ifError(err);
+ assert.equal(2, count);
+ done();
+ });
+ });
+
+ it('distinct', function(done) {
+ var m = mquery({ name: 'exec' });
+ m.collection(col);
+ m.distinct('age');
+ m.exec(function(err, array) {
+ assert.ifError(err);
+ assert.ok(Array.isArray(array));
+ assert.equal(2, array.length);
+ assert(~array.indexOf(1));
+ assert(~array.indexOf(2));
+ done();
+ });
+ });
+
+ describe('update', function() {
+ var num;
+
+ it('with a callback', function(done) {
+ var m = mquery(col);
+ m.where({ name: 'exec' });
+
+ m.count(function(err, _num) {
+ assert.ifError(err);
+ num = _num;
+ m.setOptions({ multi: true });
+ m.update({ name: 'exec + update' });
+ m.exec(function(err, res) {
+ assert.ifError(err);
+ assert.equal(num, res.result.n);
+ mquery(col).find({ name: 'exec + update' }, function(err, docs) {
+ assert.ifError(err);
+ assert.equal(num, docs.length);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('updateMany', function() {
+ it('works', function(done) {
+ mquery(col).updateMany({ name: 'exec' }, { name: 'test' }).
+ exec(function(error) {
+ assert.ifError(error);
+ mquery(col).count({ name: 'test' }).exec(function(error, res) {
+ assert.ifError(error);
+ assert.equal(res, 2);
+ done();
+ });
+ });
+ });
+ it('works with write concern', function(done) {
+ mquery(col).updateMany({ name: 'exec' }, { name: 'test' })
+ .w(1).j(true).wtimeout(1000)
+ .exec(function(error) {
+ assert.ifError(error);
+ mquery(col).count({ name: 'test' }).exec(function(error, res) {
+ assert.ifError(error);
+ assert.equal(res, 2);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('updateOne', function() {
+ it('works', function(done) {
+ mquery(col).updateOne({ name: 'exec' }, { name: 'test' }).
+ exec(function(error) {
+ assert.ifError(error);
+ mquery(col).count({ name: 'test' }).exec(function(error, res) {
+ assert.ifError(error);
+ assert.equal(res, 1);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('replaceOne', function() {
+ it('works', function(done) {
+ mquery(col).replaceOne({ name: 'exec' }, { name: 'test' }).
+ exec(function(error) {
+ assert.ifError(error);
+ mquery(col).findOne({ name: 'test' }).exec(function(error, res) {
+ assert.ifError(error);
+ assert.equal(res.name, 'test');
+ assert.ok(res.age == null);
+ done();
+ });
+ });
+ });
+ });
+
+ it('without a callback', function(done) {
+ var m = mquery(col);
+ m.where({ name: 'exec + update' }).setOptions({ multi: true });
+ m.update({ name: 'exec' });
+
+ // unsafe write
+ m.exec();
+
+ setTimeout(function() {
+ mquery(col).find({ name: 'exec' }, function(err, docs) {
+ assert.ifError(err);
+ assert.equal(2, docs.length);
+ done();
+ });
+ }, 200);
+ });
+ it('preserves key ordering', function(done) {
+ var m = mquery(col);
+
+ var m2 = m.update({ _id : 'something' }, { '1' : 1, '2' : 2, '3' : 3});
+ var doc = m2._updateForExec().$set;
+ var count = 0;
+ for (var i in doc) {
+ if (count == 0) {
+ assert.equal('1', i);
+ } else if (count == 1) {
+ assert.equal('2', i);
+ } else if (count == 2) {
+ assert.equal('3', i);
+ }
+ count++;
+ }
+ done();
+ });
+ });
+
+ describe('remove', function() {
+ it('with a callback', function(done) {
+ var m = mquery(col).where({ age: 2 }).remove();
+ m.exec(function(err, res) {
+ assert.ifError(err);
+ assert.equal(1, res.result.n);
+ done();
+ });
+ });
+
+ it('without a callback', function(done) {
+ var m = mquery(col).where({ age: 1 }).remove();
+ m.exec();
+
+ setTimeout(function() {
+ mquery(col).where('name', 'exec').count(function(err, num) {
+ assert.equal(1, num);
+ done();
+ });
+ }, 200);
+ });
+ });
+
+ describe('deleteOne', function() {
+ it('with a callback', function(done) {
+ var m = mquery(col).where({ age: { $gte: 0 } }).deleteOne();
+ m.exec(function(err, res) {
+ assert.ifError(err);
+ assert.equal(res.result.n, 1);
+ done();
+ });
+ });
+
+ it('with justOne set', function(done) {
+ var m = mquery(col).where({ age: { $gte: 0 } }).
+ // Should ignore `justOne`
+ setOptions({ justOne: false }).
+ deleteOne();
+ m.exec(function(err, res) {
+ assert.ifError(err);
+ assert.equal(res.result.n, 1);
+ done();
+ });
+ });
+ });
+
+ describe('deleteMany', function() {
+ it('with a callback', function(done) {
+ var m = mquery(col).where({ age: { $gte: 0 } }).deleteMany();
+ m.exec(function(err, res) {
+ assert.ifError(err);
+ assert.equal(res.result.n, 2);
+ done();
+ });
+ });
+ });
+
+ describe('findOneAndUpdate', function() {
+ it('with a callback', function(done) {
+ var m = mquery(col);
+ m.findOneAndUpdate({ name: 'exec', age: 1 }, { $set: { name: 'findOneAndUpdate' }});
+ m.exec(function(err, res) {
+ assert.ifError(err);
+ assert.equal('findOneAndUpdate', res.value.name);
+ done();
+ });
+ });
+ });
+
+ describe('findOneAndRemove', function() {
+ it('with a callback', function(done) {
+ var m = mquery(col);
+ m.findOneAndRemove({ name: 'exec', age: 2 });
+ m.exec(function(err, res) {
+ assert.ifError(err);
+ assert.equal('exec', res.value.name);
+ assert.equal(2, res.value.age);
+ mquery(col).count({ name: 'exec' }, function(err, num) {
+ assert.ifError(err);
+ assert.equal(1, num);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ describe('setTraceFunction', function() {
+ beforeEach(function(done) {
+ col.insert([{ name: 'trace', age: 93 }], done);
+ });
+
+ it('calls trace function when executing query', function(done) {
+ var m = mquery(col);
+
+ var resultTraceCalled;
+
+ m.setTraceFunction(function(method, queryInfo) {
+ try {
+ assert.equal('findOne', method);
+ assert.equal('trace', queryInfo.conditions.name);
+ } catch (e) {
+ done(e);
+ }
+
+ return function(err, result, millis) {
+ try {
+ assert.equal(93, result.age);
+ assert.ok(typeof millis === 'number');
+ } catch (e) {
+ done(e);
+ }
+ resultTraceCalled = true;
+ };
+ });
+
+ m.findOne({name: 'trace'}, function(err, doc) {
+ assert.ifError(err);
+ assert.equal(resultTraceCalled, true);
+ assert.equal(93, doc.age);
+ done();
+ });
+ });
+
+ it('inherits trace function when calling toConstructor', function(done) {
+ function traceFunction() { return function() {}; }
+
+ var tracedQuery = mquery().setTraceFunction(traceFunction).toConstructor();
+
+ var query = tracedQuery();
+ assert.equal(traceFunction, query._traceFunction);
+
+ done();
+ });
+ });
+
+ describe('thunk', function() {
+ it('returns a function', function(done) {
+ assert.equal('function', typeof mquery().thunk());
+ done();
+ });
+
+ it('passes the fn arg to `exec`', function(done) {
+ function cb() {}
+ var m = mquery();
+
+ m.exec = function testing(fn) {
+ assert.equal(this, m);
+ assert.equal(cb, fn);
+ done();
+ };
+
+ m.thunk()(cb);
+ });
+ });
+
+ describe('then', function() {
+ before(function(done) {
+ col.insert([{ name: 'then', age: 1 }, { name: 'then', age: 2 }], done);
+ });
+
+ after(function(done) {
+ mquery(col).remove({ name: 'then' }).exec(done);
+ });
+
+ it('returns a promise A+ compat object', function(done) {
+ var m = mquery(col).find();
+ assert.equal('function', typeof m.then);
+ done();
+ });
+
+ it('creates a promise that is resolved on success', function(done) {
+ var promise = mquery(col).count({ name: 'then' }).then();
+ promise.then(function(count) {
+ assert.equal(2, count);
+ done();
+ }, done);
+ });
+
+ it('supports exec() cb being called synchronously #66', function(done) {
+ var query = mquery(col).count({ name: 'then' });
+ query.exec = function(cb) {
+ cb(null, 66);
+ };
+
+ query.then(success, done);
+ function success(count) {
+ assert.equal(66, count);
+ done();
+ }
+ });
+
+ it('supports other Promise libs', function(done) {
+ var bluebird = mquery.Promise;
+
+ // hack for testing
+ mquery.Promise = function P() {
+ mquery.Promise = bluebird;
+ this.then = function(x, y) {
+ return x + y;
+ };
+ };
+
+ var val = mquery(col).count({ name: 'exec' }).then(1, 2);
+ assert.equal(val, 3);
+ done();
+ });
+ });
+
+ describe('stream', function() {
+ before(function(done) {
+ col.insert([{ name: 'stream', age: 1 }, { name: 'stream', age: 2 }], done);
+ });
+
+ after(function(done) {
+ mquery(col).remove({ name: 'stream' }).exec(done);
+ });
+
+ describe('throws', function() {
+ describe('if used with non-find operations', function() {
+ var ops = ['update', 'findOneAndUpdate', 'remove', 'count', 'distinct'];
+
+ ops.forEach(function(op) {
+ assert.throws(function() {
+ mquery(col)[op]().stream();
+ });
+ });
+ });
+ });
+
+ it('returns a stream', function(done) {
+ var stream = mquery(col).find({ name: 'stream' }).stream();
+ var count = 0;
+ var err;
+
+ stream.on('data', function(doc) {
+ assert.equal('stream', doc.name);
+ ++count;
+ });
+
+ stream.on('error', function(er) {
+ err = er;
+ });
+
+ stream.on('end', function() {
+ if (err) return done(err);
+ assert.equal(2, count);
+ done();
+ });
+ });
+ });
+
+ function noDistinct(type) {
+ it('cannot be used with distinct()', function(done) {
+ assert.throws(function() {
+ mquery().distinct('name')[type](4);
+ }, new RegExp(type + ' cannot be used with distinct'));
+ done();
+ });
+ }
+
+ function no(method, type) {
+ it('cannot be used with ' + method + '()', function(done) {
+ assert.throws(function() {
+ mquery()[method]()[type](4);
+ }, new RegExp(type + ' cannot be used with ' + method));
+ done();
+ });
+ }
+
+ // query internal
+
+ describe('_updateForExec', function() {
+ it('returns a clone of the update object with same key order #19', function(done) {
+ var update = {};
+ update.$push = { n: { $each: [{x:10}], $slice: -1, $sort: {x:1}}};
+
+ var q = mquery().update({ x: 1 }, update);
+
+ // capture original key order
+ var order = [];
+ var key;
+ for (key in q._update.$push.n) {
+ order.push(key);
+ }
+
+ // compare output
+ var doc = q._updateForExec();
+ var i = 0;
+ for (key in doc.$push.n) {
+ assert.equal(key, order[i]);
+ i++;
+ }
+
+ done();
+ });
+ });
+});
diff --git a/node_modules/mquery/test/utils.test.js b/node_modules/mquery/test/utils.test.js
new file mode 100644
index 0000000..ff95f33
--- /dev/null
+++ b/node_modules/mquery/test/utils.test.js
@@ -0,0 +1,144 @@
+'use strict';
+
+var Buffer = require('safe-buffer').Buffer;
+var utils = require('../lib/utils');
+var assert = require('assert');
+var debug = require('debug');
+
+var mongo;
+try {
+ mongo = new require('mongodb');
+} catch (e) {
+ debug('mongo', 'cannot construct mongodb instance');
+}
+
+describe('lib/utils', function() {
+ describe('clone', function() {
+ it('clones constructors named ObjectId', function(done) {
+ function ObjectId(id) {
+ this.id = id;
+ }
+
+ var o1 = new ObjectId('1234');
+ var o2 = utils.clone(o1);
+ assert.ok(o2 instanceof ObjectId);
+
+ done();
+ });
+
+ it('clones constructors named ObjectID', function(done) {
+ function ObjectID(id) {
+ this.id = id;
+ }
+
+ var o1 = new ObjectID('1234');
+ var o2 = utils.clone(o1);
+
+ assert.ok(o2 instanceof ObjectID);
+ done();
+ });
+
+ it('does not clone constructors named ObjectIdd', function(done) {
+ function ObjectIdd(id) {
+ this.id = id;
+ }
+
+ var o1 = new ObjectIdd('1234');
+ var o2 = utils.clone(o1);
+ assert.ok(!(o2 instanceof ObjectIdd));
+
+ done();
+ });
+
+ it('optionally clones ObjectId constructors using its clone method', function(done) {
+ function ObjectID(id) {
+ this.id = id;
+ this.cloned = false;
+ }
+
+ ObjectID.prototype.clone = function() {
+ var ret = new ObjectID(this.id);
+ ret.cloned = true;
+ return ret;
+ };
+
+ var id = 1234;
+ var o1 = new ObjectID(id);
+ assert.equal(id, o1.id);
+ assert.equal(false, o1.cloned);
+
+ var o2 = utils.clone(o1);
+ assert.ok(o2 instanceof ObjectID);
+ assert.equal(id, o2.id);
+ assert.ok(o2.cloned);
+ done();
+ });
+
+ it('clones mongodb.ReadPreferences', function(done) {
+ if (!mongo) return done();
+
+ var tags = [
+ {dc: 'tag1'}
+ ];
+ var prefs = [
+ new mongo.ReadPreference('primary'),
+ new mongo.ReadPreference(mongo.ReadPreference.PRIMARY_PREFERRED),
+ new mongo.ReadPreference('secondary', tags)
+ ];
+
+ var prefsCloned = utils.clone(prefs);
+
+ for (var i = 0; i < prefsCloned.length; i++) {
+ assert.notEqual(prefs[i], prefsCloned[i]);
+ if (prefs[i].tags) {
+ assert.ok(prefsCloned[i].tags);
+ assert.notEqual(prefs[i].tags, prefsCloned[i].tags);
+ assert.notEqual(prefs[i].tags[0], prefsCloned[i].tags[0]);
+ } else {
+ assert.equal(prefsCloned[i].tags, null);
+ }
+ }
+
+ done();
+ });
+
+ it('clones mongodb.Binary', function(done) {
+ if (!mongo) return done();
+ var buf = Buffer.from('hi');
+ var binary = new mongo.Binary(buf, 2);
+ var clone = utils.clone(binary);
+ assert.equal(binary.sub_type, clone.sub_type);
+ assert.equal(String(binary.buffer), String(buf));
+ assert.ok(binary !== clone);
+ done();
+ });
+
+ it('handles objects with no constructor', function(done) {
+ var name = '335';
+
+ var o = Object.create(null);
+ o.name = name;
+
+ var clone;
+ assert.doesNotThrow(function() {
+ clone = utils.clone(o);
+ });
+
+ assert.equal(name, clone.name);
+ assert.ok(o != clone);
+ done();
+ });
+
+ it('handles buffers', function(done) {
+ var buff = Buffer.alloc(10);
+ buff.fill(1);
+ var clone = utils.clone(buff);
+
+ for (var i = 0; i < buff.length; i++) {
+ assert.equal(buff[i], clone[i]);
+ }
+
+ done();
+ });
+ });
+});
diff --git a/node_modules/regexp-clone/.npmignore b/node_modules/regexp-clone/.npmignore
new file mode 100644
index 0000000..be106ca
--- /dev/null
+++ b/node_modules/regexp-clone/.npmignore
@@ -0,0 +1,2 @@
+*.sw*
+node_modules/
diff --git a/node_modules/regexp-clone/.travis.yml b/node_modules/regexp-clone/.travis.yml
new file mode 100644
index 0000000..58f2371
--- /dev/null
+++ b/node_modules/regexp-clone/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+ - 0.6
+ - 0.8
+ - 0.10
diff --git a/node_modules/regexp-clone/History.md b/node_modules/regexp-clone/History.md
new file mode 100644
index 0000000..0beedfc
--- /dev/null
+++ b/node_modules/regexp-clone/History.md
@@ -0,0 +1,5 @@
+
+0.0.1 / 2013-04-17
+==================
+
+ * initial commit
diff --git a/node_modules/regexp-clone/LICENSE b/node_modules/regexp-clone/LICENSE
new file mode 100644
index 0000000..98c7c89
--- /dev/null
+++ b/node_modules/regexp-clone/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2013 [Aaron Heckmann](aaron.heckmann+github@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/regexp-clone/Makefile b/node_modules/regexp-clone/Makefile
new file mode 100644
index 0000000..6c8fb75
--- /dev/null
+++ b/node_modules/regexp-clone/Makefile
@@ -0,0 +1,5 @@
+
+test:
+ @./node_modules/.bin/mocha $(T) --async-only $(TESTS)
+
+.PHONY: test
diff --git a/node_modules/regexp-clone/README.md b/node_modules/regexp-clone/README.md
new file mode 100644
index 0000000..2aabe5c
--- /dev/null
+++ b/node_modules/regexp-clone/README.md
@@ -0,0 +1,18 @@
+#regexp-clone
+==============
+
+Clones RegExps with flag preservation
+
+```js
+var regexpClone = require('regexp-clone');
+
+var a = /somethin/g;
+console.log(a.global); // true
+
+var b = regexpClone(a);
+console.log(b.global); // true
+```
+
+## License
+
+[MIT](https://github.com/aheckmann/regexp-clone/blob/master/LICENSE)
diff --git a/node_modules/regexp-clone/index.js b/node_modules/regexp-clone/index.js
new file mode 100644
index 0000000..e5850ca
--- /dev/null
+++ b/node_modules/regexp-clone/index.js
@@ -0,0 +1,20 @@
+
+var toString = Object.prototype.toString;
+
+function isRegExp (o) {
+ return 'object' == typeof o
+ && '[object RegExp]' == toString.call(o);
+}
+
+module.exports = exports = function (regexp) {
+ if (!isRegExp(regexp)) {
+ throw new TypeError('Not a RegExp');
+ }
+
+ var flags = [];
+ if (regexp.global) flags.push('g');
+ if (regexp.multiline) flags.push('m');
+ if (regexp.ignoreCase) flags.push('i');
+ return new RegExp(regexp.source, flags.join(''));
+}
+
diff --git a/node_modules/regexp-clone/package.json b/node_modules/regexp-clone/package.json
new file mode 100644
index 0000000..25178ba
--- /dev/null
+++ b/node_modules/regexp-clone/package.json
@@ -0,0 +1,55 @@
+{
+ "_from": "regexp-clone@0.0.1",
+ "_id": "regexp-clone@0.0.1",
+ "_inBundle": false,
+ "_integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=",
+ "_location": "/regexp-clone",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "regexp-clone@0.0.1",
+ "name": "regexp-clone",
+ "escapedName": "regexp-clone",
+ "rawSpec": "0.0.1",
+ "saveSpec": null,
+ "fetchSpec": "0.0.1"
+ },
+ "_requiredBy": [
+ "/mongoose",
+ "/mquery"
+ ],
+ "_resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz",
+ "_shasum": "a7c2e09891fdbf38fbb10d376fb73003e68ac589",
+ "_spec": "regexp-clone@0.0.1",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mongoose",
+ "author": {
+ "name": "Aaron Heckmann",
+ "email": "aaron.heckmann+github@gmail.com"
+ },
+ "bugs": {
+ "url": "https://github.com/aheckmann/regexp-clone/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Clone RegExps with options",
+ "devDependencies": {
+ "mocha": "1.8.1"
+ },
+ "homepage": "https://github.com/aheckmann/regexp-clone#readme",
+ "keywords": [
+ "RegExp",
+ "clone"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "regexp-clone",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aheckmann/regexp-clone.git"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "version": "0.0.1"
+}
diff --git a/node_modules/regexp-clone/test/index.js b/node_modules/regexp-clone/test/index.js
new file mode 100644
index 0000000..264a776
--- /dev/null
+++ b/node_modules/regexp-clone/test/index.js
@@ -0,0 +1,112 @@
+
+var assert = require('assert')
+var clone = require('../');
+
+describe('regexp-clone', function(){
+ function hasEqualSource (a, b) {
+ assert.ok(a !== b);
+ assert.equal(a.source, b.source);
+ }
+
+ function isInsensitive (a) {
+ assert.ok(a.ignoreCase);
+ }
+
+ function isGlobal (a) {
+ assert.ok(a.global);
+ }
+
+ function isMultiline (a) {
+ assert.ok(a.multiline);
+ }
+
+ function insensitiveFlag (a) {
+ var b = clone(a);
+ hasEqualSource(a, b);
+ isInsensitive(a);
+ isInsensitive(b);
+ }
+
+ function globalFlag (a) {
+ var b = clone(a);
+ hasEqualSource(a, b);
+ isGlobal(a);
+ isGlobal(b);
+ }
+
+ function multilineFlag (a) {
+ var b = clone(a);
+ hasEqualSource(a, b);
+ isMultiline(a);
+ isMultiline(b);
+ }
+
+ describe('literals', function(){
+ it('insensitive flag', function(done){
+ var a = /hello/i;
+ insensitiveFlag(a);
+ done();
+ })
+ it('global flag', function(done){
+ var a = /hello/g;
+ globalFlag(a);
+ done();
+ })
+ it('multiline flag', function(done){
+ var a = /hello/m;
+ multilineFlag(a);
+ done();
+ })
+ it('no flags', function(done){
+ var a = /hello/;
+ var b = clone(a);
+ hasEqualSource(a, b);
+ assert.ok(!a.insensitive);
+ assert.ok(!a.global);
+ assert.ok(!a.global);
+ done();
+ })
+ it('all flags', function(done){
+ var a = /hello/gim;
+ insensitiveFlag(a);
+ globalFlag(a);
+ multilineFlag(a);
+ done();
+ })
+ })
+
+ describe('instances', function(){
+ it('insensitive flag', function(done){
+ var a = new RegExp('hello', 'i');
+ insensitiveFlag(a);
+ done();
+ })
+ it('global flag', function(done){
+ var a = new RegExp('hello', 'g');
+ globalFlag(a);
+ done();
+ })
+ it('multiline flag', function(done){
+ var a = new RegExp('hello', 'm');
+ multilineFlag(a);
+ done();
+ })
+ it('no flags', function(done){
+ var a = new RegExp('hmm');
+ var b = clone(a);
+ hasEqualSource(a, b);
+ assert.ok(!a.insensitive);
+ assert.ok(!a.global);
+ assert.ok(!a.global);
+ done();
+ })
+ it('all flags', function(done){
+ var a = new RegExp('hello', 'gim');
+ insensitiveFlag(a);
+ globalFlag(a);
+ multilineFlag(a);
+ done();
+ })
+ })
+})
+
diff --git a/node_modules/require_optional/.npmignore b/node_modules/require_optional/.npmignore
new file mode 100644
index 0000000..e920c16
--- /dev/null
+++ b/node_modules/require_optional/.npmignore
@@ -0,0 +1,33 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
+node_modules
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
diff --git a/node_modules/require_optional/.travis.yml b/node_modules/require_optional/.travis.yml
new file mode 100644
index 0000000..72903c3
--- /dev/null
+++ b/node_modules/require_optional/.travis.yml
@@ -0,0 +1,9 @@
+language: node_js
+node_js:
+ - "0.10"
+ - "0.12"
+ - "4"
+ - "6"
+ - "7"
+ - "8"
+sudo: false
diff --git a/node_modules/require_optional/HISTORY.md b/node_modules/require_optional/HISTORY.md
new file mode 100644
index 0000000..7bee02f
--- /dev/null
+++ b/node_modules/require_optional/HISTORY.md
@@ -0,0 +1,7 @@
+1.0.1 03-02-2016
+================
+* Fix dependency resolution issue when a component in peerOptionalDependencies is installed at the level of the module declaring in peerOptionalDependencies.
+
+1.0.0 03-02-2016
+================
+* Initial release allowing us to optionally resolve dependencies in the package.json file under the peerOptionalDependencies tag.
\ No newline at end of file
diff --git a/node_modules/require_optional/LICENSE b/node_modules/require_optional/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/node_modules/require_optional/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/node_modules/require_optional/README.md b/node_modules/require_optional/README.md
new file mode 100644
index 0000000..c0323f0
--- /dev/null
+++ b/node_modules/require_optional/README.md
@@ -0,0 +1,2 @@
+# require_optional
+Work around the problem that we do not have a optionalPeerDependencies concept in node.js making it a hassle to optionally include native modules
diff --git a/node_modules/require_optional/index.js b/node_modules/require_optional/index.js
new file mode 100644
index 0000000..3710319
--- /dev/null
+++ b/node_modules/require_optional/index.js
@@ -0,0 +1,128 @@
+var path = require('path'),
+ fs = require('fs'),
+ f = require('util').format,
+ resolveFrom = require('resolve-from'),
+ semver = require('semver');
+
+var exists = fs.existsSync || path.existsSync;
+
+// Find the location of a package.json file near or above the given location
+var find_package_json = function(location) {
+ var found = false;
+
+ while(!found) {
+ if (exists(location + '/package.json')) {
+ found = location;
+ } else if (location !== '/') {
+ location = path.dirname(location);
+ } else {
+ return false;
+ }
+ }
+
+ return location;
+}
+
+// Find the package.json object of the module closest up the module call tree that contains name in that module's peerOptionalDependencies
+var find_package_json_with_name = function(name) {
+ // Walk up the module call tree until we find a module containing name in its peerOptionalDependencies
+ var currentModule = module;
+ var found = false;
+ while (currentModule) {
+ // Check currentModule has a package.json
+ location = currentModule.filename;
+ var location = find_package_json(location)
+ if (!location) {
+ currentModule = currentModule.parent;
+ continue;
+ }
+
+ // Read the package.json file
+ var object = JSON.parse(fs.readFileSync(f('%s/package.json', location)));
+ // Is the name defined by interal file references
+ var parts = name.split(/\//);
+
+ // Check whether this package.json contains peerOptionalDependencies containing the name we're searching for
+ if (!object.peerOptionalDependencies || (object.peerOptionalDependencies && !object.peerOptionalDependencies[parts[0]])) {
+ currentModule = currentModule.parent;
+ continue;
+ }
+ found = true;
+ break;
+ }
+
+ // Check whether name has been found in currentModule's peerOptionalDependencies
+ if (!found) {
+ throw new Error(f('no optional dependency [%s] defined in peerOptionalDependencies in any package.json', parts[0]));
+ }
+
+ return {
+ object: object,
+ parts: parts
+ }
+}
+
+var require_optional = function(name, options) {
+ options = options || {};
+ options.strict = typeof options.strict == 'boolean' ? options.strict : true;
+
+ var res = find_package_json_with_name(name)
+ var object = res.object;
+ var parts = res.parts;
+
+ // Unpack the expected version
+ var expectedVersions = object.peerOptionalDependencies[parts[0]];
+ // The resolved package
+ var moduleEntry = undefined;
+ // Module file
+ var moduleEntryFile = name;
+
+ try {
+ // Validate if it's possible to read the module
+ moduleEntry = require(moduleEntryFile);
+ } catch(err) {
+ // Attempt to resolve in top level package
+ try {
+ // Get the module entry file
+ moduleEntryFile = resolveFrom(process.cwd(), name);
+ if(moduleEntryFile == null) return undefined;
+ // Attempt to resolve the module
+ moduleEntry = require(moduleEntryFile);
+ } catch(err) {
+ if(err.code === 'MODULE_NOT_FOUND') return undefined;
+ }
+ }
+
+ // Resolve the location of the module's package.json file
+ var location = find_package_json(require.resolve(moduleEntryFile));
+ if(!location) {
+ throw new Error('package.json can not be located');
+ }
+
+ // Read the module file
+ var dependentOnModule = JSON.parse(fs.readFileSync(f('%s/package.json', location)));
+ // Get the version
+ var version = dependentOnModule.version;
+ // Validate if the found module satisfies the version id
+ if(semver.satisfies(version, expectedVersions) == false
+ && options.strict) {
+ var error = new Error(f('optional dependency [%s] found but version [%s] did not satisfy constraint [%s]', parts[0], version, expectedVersions));
+ error.code = 'OPTIONAL_MODULE_NOT_FOUND';
+ throw error;
+ }
+
+ // Satifies the module requirement
+ return moduleEntry;
+}
+
+require_optional.exists = function(name) {
+ try {
+ var m = require_optional(name);
+ if(m === undefined) return false;
+ return true;
+ } catch(err) {
+ return false;
+ }
+}
+
+module.exports = require_optional;
diff --git a/node_modules/require_optional/node_modules/resolve-from/index.js b/node_modules/require_optional/node_modules/resolve-from/index.js
new file mode 100644
index 0000000..434159f
--- /dev/null
+++ b/node_modules/require_optional/node_modules/resolve-from/index.js
@@ -0,0 +1,23 @@
+'use strict';
+var path = require('path');
+var Module = require('module');
+
+module.exports = function (fromDir, moduleId) {
+ if (typeof fromDir !== 'string' || typeof moduleId !== 'string') {
+ throw new TypeError('Expected `fromDir` and `moduleId` to be a string');
+ }
+
+ fromDir = path.resolve(fromDir);
+
+ var fromFile = path.join(fromDir, 'noop.js');
+
+ try {
+ return Module._resolveFilename(moduleId, {
+ id: fromFile,
+ filename: fromFile,
+ paths: Module._nodeModulePaths(fromDir)
+ });
+ } catch (err) {
+ return null;
+ }
+};
diff --git a/node_modules/require_optional/node_modules/resolve-from/license b/node_modules/require_optional/node_modules/resolve-from/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/node_modules/require_optional/node_modules/resolve-from/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/require_optional/node_modules/resolve-from/package.json b/node_modules/require_optional/node_modules/resolve-from/package.json
new file mode 100644
index 0000000..724a84b
--- /dev/null
+++ b/node_modules/require_optional/node_modules/resolve-from/package.json
@@ -0,0 +1,66 @@
+{
+ "_from": "resolve-from@^2.0.0",
+ "_id": "resolve-from@2.0.0",
+ "_inBundle": false,
+ "_integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=",
+ "_location": "/require_optional/resolve-from",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "resolve-from@^2.0.0",
+ "name": "resolve-from",
+ "escapedName": "resolve-from",
+ "rawSpec": "^2.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^2.0.0"
+ },
+ "_requiredBy": [
+ "/require_optional"
+ ],
+ "_resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
+ "_shasum": "9480ab20e94ffa1d9e80a804c7ea147611966b57",
+ "_spec": "resolve-from@^2.0.0",
+ "_where": "D:\\Gitterra\\om\\node_modules\\require_optional",
+ "author": {
+ "name": "Sindre Sorhus",
+ "email": "sindresorhus@gmail.com",
+ "url": "sindresorhus.com"
+ },
+ "bugs": {
+ "url": "https://github.com/sindresorhus/resolve-from/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Resolve the path of a module like require.resolve() but from a given path",
+ "devDependencies": {
+ "ava": "*",
+ "xo": "*"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/sindresorhus/resolve-from#readme",
+ "keywords": [
+ "require",
+ "resolve",
+ "path",
+ "module",
+ "from",
+ "like",
+ "path"
+ ],
+ "license": "MIT",
+ "name": "resolve-from",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sindresorhus/resolve-from.git"
+ },
+ "scripts": {
+ "test": "xo && ava"
+ },
+ "version": "2.0.0"
+}
diff --git a/node_modules/require_optional/node_modules/resolve-from/readme.md b/node_modules/require_optional/node_modules/resolve-from/readme.md
new file mode 100644
index 0000000..bb4ca91
--- /dev/null
+++ b/node_modules/require_optional/node_modules/resolve-from/readme.md
@@ -0,0 +1,58 @@
+# resolve-from [](https://travis-ci.org/sindresorhus/resolve-from)
+
+> Resolve the path of a module like [`require.resolve()`](http://nodejs.org/api/globals.html#globals_require_resolve) but from a given path
+
+Unlike `require.resolve()` it returns `null` instead of throwing when the module can't be found.
+
+
+## Install
+
+```
+$ npm install --save resolve-from
+```
+
+
+## Usage
+
+```js
+const resolveFrom = require('resolve-from');
+
+// there's a file at `./foo/bar.js`
+
+resolveFrom('foo', './bar');
+//=> '/Users/sindresorhus/dev/test/foo/bar.js'
+```
+
+
+## API
+
+### resolveFrom(fromDir, moduleId)
+
+#### fromDir
+
+Type: `string`
+
+Directory to resolve from.
+
+#### moduleId
+
+Type: `string`
+
+What you would use in `require()`.
+
+
+## Tip
+
+Create a partial using a bound function if you want to require from the same `fromDir` multiple times:
+
+```js
+const resolveFromFoo = resolveFrom.bind(null, 'foo');
+
+resolveFromFoo('./bar');
+resolveFromFoo('./baz');
+```
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/node_modules/require_optional/package.json b/node_modules/require_optional/package.json
new file mode 100644
index 0000000..642fe27
--- /dev/null
+++ b/node_modules/require_optional/package.json
@@ -0,0 +1,67 @@
+{
+ "_from": "require_optional@^1.0.1",
+ "_id": "require_optional@1.0.1",
+ "_inBundle": false,
+ "_integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
+ "_location": "/require_optional",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "require_optional@^1.0.1",
+ "name": "require_optional",
+ "escapedName": "require_optional",
+ "rawSpec": "^1.0.1",
+ "saveSpec": null,
+ "fetchSpec": "^1.0.1"
+ },
+ "_requiredBy": [
+ "/mongodb-core"
+ ],
+ "_resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
+ "_shasum": "4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e",
+ "_spec": "require_optional@^1.0.1",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mongodb-core",
+ "author": {
+ "name": "Christian Kvalheim Amor"
+ },
+ "bugs": {
+ "url": "https://github.com/christkv/require_optional/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "resolve-from": "^2.0.0",
+ "semver": "^5.1.0"
+ },
+ "deprecated": false,
+ "description": "Allows you declare optionalPeerDependencies that can be satisfied by the top level module but ignored if they are not.",
+ "devDependencies": {
+ "bson": "0.4.21",
+ "co": "4.6.0",
+ "es6-promise": "^3.0.2",
+ "mocha": "^2.4.5"
+ },
+ "homepage": "https://github.com/christkv/require_optional",
+ "keywords": [
+ "optional",
+ "require",
+ "optionalPeerDependencies"
+ ],
+ "license": "Apache-2.0",
+ "main": "index.js",
+ "name": "require_optional",
+ "peerOptionalDependencies": {
+ "co": ">=5.6.0",
+ "es6-promise": "^3.0.2",
+ "es6-promise2": "^4.0.2",
+ "bson": "0.4.21"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/christkv/require_optional.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "version": "1.0.1"
+}
diff --git a/node_modules/require_optional/test/nestedTest/index.js b/node_modules/require_optional/test/nestedTest/index.js
new file mode 100644
index 0000000..76de2ab
--- /dev/null
+++ b/node_modules/require_optional/test/nestedTest/index.js
@@ -0,0 +1,8 @@
+var require_optional = require('../../')
+
+function findPackage(packageName) {
+ var pkg = require_optional(packageName);
+ return pkg;
+}
+
+module.exports.findPackage = findPackage
diff --git a/node_modules/require_optional/test/nestedTest/package.json b/node_modules/require_optional/test/nestedTest/package.json
new file mode 100644
index 0000000..4c456a6
--- /dev/null
+++ b/node_modules/require_optional/test/nestedTest/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "nestedtest",
+ "version": "1.0.0",
+ "description": "A dummy package that facilitates testing that require_optional correctly walks up the module call stack",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "Sebastian Hallum Clarke",
+ "license": "ISC"
+}
diff --git a/node_modules/require_optional/test/require_optional_tests.js b/node_modules/require_optional/test/require_optional_tests.js
new file mode 100644
index 0000000..c9cc2a3
--- /dev/null
+++ b/node_modules/require_optional/test/require_optional_tests.js
@@ -0,0 +1,59 @@
+var assert = require('assert'),
+ require_optional = require('../'),
+ nestedTest = require('./nestedTest');
+
+describe('Require Optional', function() {
+ describe('top level require', function() {
+ it('should correctly require co library', function() {
+ var promise = require_optional('es6-promise');
+ assert.ok(promise);
+ });
+
+ it('should fail to require es6-promise library', function() {
+ try {
+ require_optional('co');
+ } catch(e) {
+ assert.equal('OPTIONAL_MODULE_NOT_FOUND', e.code);
+ return;
+ }
+
+ assert.ok(false);
+ });
+
+ it('should ignore optional library not defined', function() {
+ assert.equal(undefined, require_optional('es6-promise2'));
+ });
+ });
+
+ describe('internal module file require', function() {
+ it('should correctly require co library', function() {
+ var Long = require_optional('bson/lib/bson/long.js');
+ assert.ok(Long);
+ });
+ });
+
+ describe('top level resolve', function() {
+ it('should correctly use exists method', function() {
+ assert.equal(false, require_optional.exists('co'));
+ assert.equal(true, require_optional.exists('es6-promise'));
+ assert.equal(true, require_optional.exists('bson/lib/bson/long.js'));
+ assert.equal(false, require_optional.exists('es6-promise2'));
+ });
+ });
+
+ describe('require_optional inside dependencies', function() {
+ it('should correctly walk up module call stack searching for peerOptionalDependencies', function() {
+ assert.ok(nestedTest.findPackage('bson'))
+ });
+ it('should return null when a package is defined in top-level package.json but not installed', function() {
+ assert.equal(null, nestedTest.findPackage('es6-promise2'))
+ });
+ it('should error when searching for an optional dependency that is not defined in any ancestor package.json', function() {
+ try {
+ nestedTest.findPackage('bison')
+ } catch (err) {
+ assert.equal(err.message, 'no optional dependency [bison] defined in peerOptionalDependencies in any package.json')
+ }
+ })
+ });
+});
diff --git a/node_modules/saslprep/.editorconfig b/node_modules/saslprep/.editorconfig
new file mode 100644
index 0000000..d1d8a41
--- /dev/null
+++ b/node_modules/saslprep/.editorconfig
@@ -0,0 +1,10 @@
+# http://editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/node_modules/saslprep/.eslintrc b/node_modules/saslprep/.eslintrc
new file mode 100644
index 0000000..06c7684
--- /dev/null
+++ b/node_modules/saslprep/.eslintrc
@@ -0,0 +1,17 @@
+{
+ "plugins": [
+ "prettier"
+ ],
+ "env": {
+ "browser": true,
+ "node": true,
+ "es6": true
+ },
+ "rules": {
+ "prettier/prettier": ["error", {
+ "semi": false,
+ "singleQuote": true,
+ "trailingComma": "es5"
+ }]
+ }
+}
diff --git a/node_modules/saslprep/.gitattributes b/node_modules/saslprep/.gitattributes
new file mode 100644
index 0000000..3ba4536
--- /dev/null
+++ b/node_modules/saslprep/.gitattributes
@@ -0,0 +1 @@
+*.mem binary
diff --git a/node_modules/saslprep/.travis.yml b/node_modules/saslprep/.travis.yml
new file mode 100644
index 0000000..53b824d
--- /dev/null
+++ b/node_modules/saslprep/.travis.yml
@@ -0,0 +1,5 @@
+sudo: false
+language: node_js
+node_js:
+ - "6"
+ - "8"
diff --git a/node_modules/saslprep/LICENSE b/node_modules/saslprep/LICENSE
new file mode 100644
index 0000000..481c7a5
--- /dev/null
+++ b/node_modules/saslprep/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Dmitry Tsvettsikh
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/node_modules/saslprep/code-points.mem b/node_modules/saslprep/code-points.mem
new file mode 100644
index 0000000..4781b06
Binary files /dev/null and b/node_modules/saslprep/code-points.mem differ
diff --git a/node_modules/saslprep/generate-code-points.js b/node_modules/saslprep/generate-code-points.js
new file mode 100644
index 0000000..95f085f
--- /dev/null
+++ b/node_modules/saslprep/generate-code-points.js
@@ -0,0 +1,48 @@
+'use strict';
+
+const bitfield = require('sparse-bitfield');
+const codePoints = require('./lib/code-points');
+
+const unassigned_code_points = bitfield();
+const commonly_mapped_to_nothing = bitfield();
+const non_ascii_space_characters = bitfield();
+const prohibited_characters = bitfield();
+const bidirectional_r_al = bitfield();
+const bidirectional_l = bitfield();
+
+/**
+ * @param {bitfield} bits
+ * @param {array} src
+ */
+function traverse(bits, src) {
+ for(const code of src.keys()) {
+ bits.set(code, true);
+ }
+
+ const buffer = bits.toBuffer();
+ return Buffer.concat([ createSize(buffer), buffer ]);
+}
+
+/**
+ * @param {Buffer} buffer
+ * @returns {Buffer}
+ */
+function createSize(buffer) {
+ const buf = Buffer.alloc(4);
+ buf.writeUInt32BE(buffer.length);
+
+ return buf;
+}
+
+const memory = [];
+
+memory.push(
+ traverse(unassigned_code_points, codePoints.unassigned_code_points),
+ traverse(commonly_mapped_to_nothing, codePoints.commonly_mapped_to_nothing),
+ traverse(non_ascii_space_characters, codePoints.non_ASCII_space_characters),
+ traverse(prohibited_characters, codePoints.prohibited_characters),
+ traverse(bidirectional_r_al, codePoints.bidirectional_r_al),
+ traverse(bidirectional_l, codePoints.bidirectional_l),
+);
+
+process.stdout.write(Buffer.concat(memory));
diff --git a/node_modules/saslprep/index.js b/node_modules/saslprep/index.js
new file mode 100644
index 0000000..94fb132
--- /dev/null
+++ b/node_modules/saslprep/index.js
@@ -0,0 +1,120 @@
+'use strict'
+
+const {
+ unassigned_code_points,
+ commonly_mapped_to_nothing,
+ non_ASCII_space_characters,
+ prohibited_characters,
+ bidirectional_r_al,
+ bidirectional_l,
+} = require('./lib/memory-code-points')
+
+module.exports = saslprep
+
+// 2.1. Mapping
+
+/**
+ * non-ASCII space characters [StringPrep, C.1.2] that can be
+ * mapped to SPACE (U+0020)
+ */
+const mapping2space = non_ASCII_space_characters
+
+/**
+ * the "commonly mapped to nothing" characters [StringPrep, B.1]
+ * that can be mapped to nothing.
+ */
+const mapping2nothing = commonly_mapped_to_nothing
+
+// utils
+const getCodePoint = character => character.codePointAt(0)
+const first = x => x[0]
+const last = x => x[x.length - 1]
+
+/**
+ * SASLprep.
+ * @param {string} input
+ * @param {object} opts
+ * @param {boolean} opts.allowUnassigned
+ */
+function saslprep(input, opts = {}) {
+ if (typeof input !== 'string') {
+ throw new TypeError('Expected string.')
+ }
+
+ if (input.length === 0) {
+ return ''
+ }
+
+ // 1. Map
+ const mapped_input = input
+ .split('')
+ .map(getCodePoint)
+ // 1.1 mapping to space
+ .map(character => (mapping2space.get(character) ? 0x20 : character))
+ // 1.2 mapping to nothing
+ .filter(character => !mapping2nothing.get(character))
+
+ // 2. Normalize
+ const normalized_input = String.fromCodePoint(...mapped_input).normalize('NFKC')
+
+ const normalized_map = normalized_input.split('').map(getCodePoint)
+
+ // 3. Prohibit
+ const hasProhibited = normalized_map.some(character =>
+ prohibited_characters.get(character)
+ )
+
+ if (hasProhibited) {
+ throw new Error(
+ 'Prohibited character, see https://tools.ietf.org/html/rfc4013#section-2.3'
+ )
+ }
+
+ // Unassigned Code Points
+ if (opts.allowUnassigned !== true) {
+ const hasUnassigned = normalized_map.some(character =>
+ unassigned_code_points.get(character)
+ )
+
+ if (hasUnassigned) {
+ throw new Error(
+ 'Unassigned code point, see https://tools.ietf.org/html/rfc4013#section-2.5'
+ )
+ }
+ }
+
+ // 4. check bidi
+
+ const hasBidiRAL = normalized_map
+ .some((character) => bidirectional_r_al.get(character))
+
+ const hasBidiL = normalized_map
+ .some((character) => bidirectional_l.get(character))
+
+ // 4.1 If a string contains any RandALCat character, the string MUST NOT
+ // contain any LCat character.
+ if (hasBidiRAL && hasBidiL) {
+ throw new Error(
+ 'String must not contain RandALCat and LCat at the same time,' +
+ ' see https://tools.ietf.org/html/rfc3454#section-6'
+ )
+ }
+
+ /**
+ * 4.2 If a string contains any RandALCat character, a RandALCat
+ * character MUST be the first character of the string, and a
+ * RandALCat character MUST be the last character of the string.
+ */
+
+ const isFirstBidiRAL = bidirectional_r_al.get(getCodePoint(first(normalized_input)))
+ const isLastBidiRAL = bidirectional_r_al.get(getCodePoint(last(normalized_input)))
+
+ if (hasBidiRAL && !(isFirstBidiRAL && isLastBidiRAL)) {
+ throw new Error(
+ 'Bidirectional RandALCat character must be the first and the last' +
+ ' character of the string, see https://tools.ietf.org/html/rfc3454#section-6'
+ )
+ }
+
+ return normalized_input
+}
diff --git a/node_modules/saslprep/lib/code-points.js b/node_modules/saslprep/lib/code-points.js
new file mode 100644
index 0000000..4c5885a
--- /dev/null
+++ b/node_modules/saslprep/lib/code-points.js
@@ -0,0 +1,996 @@
+'use strict'
+
+const { range } = require('./util')
+
+/**
+ * A.1 Unassigned code points in Unicode 3.2
+ * @link https://tools.ietf.org/html/rfc3454#appendix-A.1
+ */
+const unassigned_code_points = new Set([
+ 0x0221,
+ ...range(0x0234, 0x024F),
+ ...range(0x02AE, 0x02AF),
+ ...range(0x02EF, 0x02FF),
+ ...range(0x0350, 0x035F),
+ ...range(0x0370, 0x0373),
+ ...range(0x0376, 0x0379),
+ ...range(0x037B, 0x037D),
+ ...range(0x037F, 0x0383),
+ 0x038B,
+ 0x038D,
+ 0x03A2,
+ 0x03CF,
+ ...range(0x03F7, 0x03FF),
+ 0x0487,
+ 0x04CF,
+ ...range(0x04F6, 0x04F7),
+ ...range(0x04FA, 0x04FF),
+ ...range(0x0510, 0x0530),
+ ...range(0x0557, 0x0558),
+ 0x0560,
+ 0x0588,
+ ...range(0x058B, 0x0590),
+ 0x05A2,
+ 0x05BA,
+ ...range(0x05C5, 0x05CF),
+ ...range(0x05EB, 0x05EF),
+ ...range(0x05F5, 0x060B),
+ ...range(0x060D, 0x061A),
+ ...range(0x061C, 0x061E),
+ 0x0620,
+ ...range(0x063B, 0x063F),
+ ...range(0x0656, 0x065F),
+ ...range(0x06EE, 0x06EF),
+ 0x06FF,
+ 0x070E,
+ ...range(0x072D, 0x072F),
+ ...range(0x074B, 0x077F),
+ ...range(0x07B2, 0x0900),
+ 0x0904,
+ ...range(0x093A, 0x093B),
+ ...range(0x094E, 0x094F),
+ ...range(0x0955, 0x0957),
+ ...range(0x0971, 0x0980),
+ 0x0984,
+ ...range(0x098D, 0x098E),
+ ...range(0x0991, 0x0992),
+ 0x09A9,
+ 0x09B1,
+ ...range(0x09B3, 0x09B5),
+ ...range(0x09BA, 0x09BB),
+ 0x09BD,
+ ...range(0x09C5, 0x09C6),
+ ...range(0x09C9, 0x09CA),
+ ...range(0x09CE, 0x09D6),
+ ...range(0x09D8, 0x09DB),
+ 0x09DE,
+ ...range(0x09E4, 0x09E5),
+ ...range(0x09FB, 0x0A01),
+ ...range(0x0A03, 0x0A04),
+ ...range(0x0A0B, 0x0A0E),
+ ...range(0x0A11, 0x0A12),
+ 0x0A29,
+ 0x0A31,
+ 0x0A34,
+ 0x0A37,
+ ...range(0x0A3A, 0x0A3B),
+ 0x0A3D,
+ ...range(0x0A43, 0x0A46),
+ ...range(0x0A49, 0x0A4A),
+ ...range(0x0A4E, 0x0A58),
+ 0x0A5D,
+ ...range(0x0A5F, 0x0A65),
+ ...range(0x0A75, 0x0A80),
+ 0x0A84,
+ 0x0A8C,
+ 0x0A8E,
+ 0x0A92,
+ 0x0AA9,
+ 0x0AB1,
+ 0x0AB4,
+ ...range(0x0ABA, 0x0ABB),
+ 0x0AC6,
+ 0x0ACA,
+ ...range(0x0ACE, 0x0ACF),
+ ...range(0x0AD1, 0x0ADF),
+ ...range(0x0AE1, 0x0AE5),
+ ...range(0x0AF0, 0x0B00),
+ 0x0B04,
+ ...range(0x0B0D, 0x0B0E),
+ ...range(0x0B11, 0x0B12),
+ 0x0B29,
+ 0x0B31,
+ ...range(0x0B34, 0x0B35),
+ ...range(0x0B3A, 0x0B3B),
+ ...range(0x0B44, 0x0B46),
+ ...range(0x0B49, 0x0B4A),
+ ...range(0x0B4E, 0x0B55),
+ ...range(0x0B58, 0x0B5B),
+ 0x0B5E,
+ ...range(0x0B62, 0x0B65),
+ ...range(0x0B71, 0x0B81),
+ 0x0B84,
+ ...range(0x0B8B, 0x0B8D),
+ 0x0B91,
+ ...range(0x0B96, 0x0B98),
+ 0x0B9B,
+ 0x0B9D,
+ ...range(0x0BA0, 0x0BA2),
+ ...range(0x0BA5, 0x0BA7),
+ ...range(0x0BAB, 0x0BAD),
+ 0x0BB6,
+ ...range(0x0BBA, 0x0BBD),
+ ...range(0x0BC3, 0x0BC5),
+ 0x0BC9,
+ ...range(0x0BCE, 0x0BD6),
+ ...range(0x0BD8, 0x0BE6),
+ ...range(0x0BF3, 0x0C00),
+ 0x0C04,
+ 0x0C0D,
+ 0x0C11,
+ 0x0C29,
+ 0x0C34,
+ ...range(0x0C3A, 0x0C3D),
+ 0x0C45,
+ 0x0C49,
+ ...range(0x0C4E, 0x0C54),
+ ...range(0x0C57, 0x0C5F),
+ ...range(0x0C62, 0x0C65),
+ ...range(0x0C70, 0x0C81),
+ 0x0C84,
+ 0x0C8D,
+ 0x0C91,
+ 0x0CA9,
+ 0x0CB4,
+ ...range(0x0CBA, 0x0CBD),
+ 0x0CC5,
+ 0x0CC9,
+ ...range(0x0CCE, 0x0CD4),
+ ...range(0x0CD7, 0x0CDD),
+ 0x0CDF,
+ ...range(0x0CE2, 0x0CE5),
+ ...range(0x0CF0, 0x0D01),
+ 0x0D04,
+ 0x0D0D,
+ 0x0D11,
+ 0x0D29,
+ ...range(0x0D3A, 0x0D3D),
+ ...range(0x0D44, 0x0D45),
+ 0x0D49,
+ ...range(0x0D4E, 0x0D56),
+ ...range(0x0D58, 0x0D5F),
+ ...range(0x0D62, 0x0D65),
+ ...range(0x0D70, 0x0D81),
+ 0x0D84,
+ ...range(0x0D97, 0x0D99),
+ 0x0DB2,
+ 0x0DBC,
+ ...range(0x0DBE, 0x0DBF),
+ ...range(0x0DC7, 0x0DC9),
+ ...range(0x0DCB, 0x0DCE),
+ 0x0DD5,
+ 0x0DD7,
+ ...range(0x0DE0, 0x0DF1),
+ ...range(0x0DF5, 0x0E00),
+ ...range(0x0E3B, 0x0E3E),
+ ...range(0x0E5C, 0x0E80),
+ 0x0E83,
+ ...range(0x0E85, 0x0E86),
+ 0x0E89,
+ ...range(0x0E8B, 0x0E8C),
+ ...range(0x0E8E, 0x0E93),
+ 0x0E98,
+ 0x0EA0,
+ 0x0EA4,
+ 0x0EA6,
+ ...range(0x0EA8, 0x0EA9),
+ 0x0EAC,
+ 0x0EBA,
+ ...range(0x0EBE, 0x0EBF),
+ 0x0EC5,
+ 0x0EC7,
+ ...range(0x0ECE, 0x0ECF),
+ ...range(0x0EDA, 0x0EDB),
+ ...range(0x0EDE, 0x0EFF),
+ 0x0F48,
+ ...range(0x0F6B, 0x0F70),
+ ...range(0x0F8C, 0x0F8F),
+ 0x0F98,
+ 0x0FBD,
+ ...range(0x0FCD, 0x0FCE),
+ ...range(0x0FD0, 0x0FFF),
+ 0x1022,
+ 0x1028,
+ 0x102B,
+ ...range(0x1033, 0x1035),
+ ...range(0x103A, 0x103F),
+ ...range(0x105A, 0x109F),
+ ...range(0x10C6, 0x10CF),
+ ...range(0x10F9, 0x10FA),
+ ...range(0x10FC, 0x10FF),
+ ...range(0x115A, 0x115E),
+ ...range(0x11A3, 0x11A7),
+ ...range(0x11FA, 0x11FF),
+ 0x1207,
+ 0x1247,
+ 0x1249,
+ ...range(0x124E, 0x124F),
+ 0x1257,
+ 0x1259,
+ ...range(0x125E, 0x125F),
+ 0x1287,
+ 0x1289,
+ ...range(0x128E, 0x128F),
+ 0x12AF,
+ 0x12B1,
+ ...range(0x12B6, 0x12B7),
+ 0x12BF,
+ 0x12C1,
+ ...range(0x12C6, 0x12C7),
+ 0x12CF,
+ 0x12D7,
+ 0x12EF,
+ 0x130F,
+ 0x1311,
+ ...range(0x1316, 0x1317),
+ 0x131F,
+ 0x1347,
+ ...range(0x135B, 0x1360),
+ ...range(0x137D, 0x139F),
+ ...range(0x13F5, 0x1400),
+ ...range(0x1677, 0x167F),
+ ...range(0x169D, 0x169F),
+ ...range(0x16F1, 0x16FF),
+ 0x170D,
+ ...range(0x1715, 0x171F),
+ ...range(0x1737, 0x173F),
+ ...range(0x1754, 0x175F),
+ 0x176D,
+ 0x1771,
+ ...range(0x1774, 0x177F),
+ ...range(0x17DD, 0x17DF),
+ ...range(0x17EA, 0x17FF),
+ 0x180F,
+ ...range(0x181A, 0x181F),
+ ...range(0x1878, 0x187F),
+ ...range(0x18AA, 0x1DFF),
+ ...range(0x1E9C, 0x1E9F),
+ ...range(0x1EFA, 0x1EFF),
+ ...range(0x1F16, 0x1F17),
+ ...range(0x1F1E, 0x1F1F),
+ ...range(0x1F46, 0x1F47),
+ ...range(0x1F4E, 0x1F4F),
+ 0x1F58,
+ 0x1F5A,
+ 0x1F5C,
+ 0x1F5E,
+ ...range(0x1F7E, 0x1F7F),
+ 0x1FB5,
+ 0x1FC5,
+ ...range(0x1FD4, 0x1FD5),
+ 0x1FDC,
+ ...range(0x1FF0, 0x1FF1),
+ 0x1FF5,
+ 0x1FFF,
+ ...range(0x2053, 0x2056),
+ ...range(0x2058, 0x205E),
+ ...range(0x2064, 0x2069),
+ ...range(0x2072, 0x2073),
+ ...range(0x208F, 0x209F),
+ ...range(0x20B2, 0x20CF),
+ ...range(0x20EB, 0x20FF),
+ ...range(0x213B, 0x213C),
+ ...range(0x214C, 0x2152),
+ ...range(0x2184, 0x218F),
+ ...range(0x23CF, 0x23FF),
+ ...range(0x2427, 0x243F),
+ ...range(0x244B, 0x245F),
+ 0x24FF,
+ ...range(0x2614, 0x2615),
+ 0x2618,
+ ...range(0x267E, 0x267F),
+ ...range(0x268A, 0x2700),
+ 0x2705,
+ ...range(0x270A, 0x270B),
+ 0x2728,
+ 0x274C,
+ 0x274E,
+ ...range(0x2753, 0x2755),
+ 0x2757,
+ ...range(0x275F, 0x2760),
+ ...range(0x2795, 0x2797),
+ 0x27B0,
+ ...range(0x27BF, 0x27CF),
+ ...range(0x27EC, 0x27EF),
+ ...range(0x2B00, 0x2E7F),
+ 0x2E9A,
+ ...range(0x2EF4, 0x2EFF),
+ ...range(0x2FD6, 0x2FEF),
+ ...range(0x2FFC, 0x2FFF),
+ 0x3040,
+ ...range(0x3097, 0x3098),
+ ...range(0x3100, 0x3104),
+ ...range(0x312D, 0x3130),
+ 0x318F,
+ ...range(0x31B8, 0x31EF),
+ ...range(0x321D, 0x321F),
+ ...range(0x3244, 0x3250),
+ ...range(0x327C, 0x327E),
+ ...range(0x32CC, 0x32CF),
+ 0x32FF,
+ ...range(0x3377, 0x337A),
+ ...range(0x33DE, 0x33DF),
+ 0x33FF,
+ ...range(0x4DB6, 0x4DFF),
+ ...range(0x9FA6, 0x9FFF),
+ ...range(0xA48D, 0xA48F),
+ ...range(0xA4C7, 0xABFF),
+ ...range(0xD7A4, 0xD7FF),
+ ...range(0xFA2E, 0xFA2F),
+ ...range(0xFA6B, 0xFAFF),
+ ...range(0xFB07, 0xFB12),
+ ...range(0xFB18, 0xFB1C),
+ 0xFB37,
+ 0xFB3D,
+ 0xFB3F,
+ 0xFB42,
+ 0xFB45,
+ ...range(0xFBB2, 0xFBD2),
+ ...range(0xFD40, 0xFD4F),
+ ...range(0xFD90, 0xFD91),
+ ...range(0xFDC8, 0xFDCF),
+ ...range(0xFDFD, 0xFDFF),
+ ...range(0xFE10, 0xFE1F),
+ ...range(0xFE24, 0xFE2F),
+ ...range(0xFE47, 0xFE48),
+ 0xFE53,
+ 0xFE67,
+ ...range(0xFE6C, 0xFE6F),
+ 0xFE75,
+ ...range(0xFEFD, 0xFEFE),
+ 0xFF00,
+ ...range(0xFFBF, 0xFFC1),
+ ...range(0xFFC8, 0xFFC9),
+ ...range(0xFFD0, 0xFFD1),
+ ...range(0xFFD8, 0xFFD9),
+ ...range(0xFFDD, 0xFFDF),
+ 0xFFE7,
+ ...range(0xFFEF, 0xFFF8),
+ ...range(0x10000, 0x102FF),
+ 0x1031F,
+ ...range(0x10324, 0x1032F),
+ ...range(0x1034B, 0x103FF),
+ ...range(0x10426, 0x10427),
+ ...range(0x1044E, 0x1CFFF),
+ ...range(0x1D0F6, 0x1D0FF),
+ ...range(0x1D127, 0x1D129),
+ ...range(0x1D1DE, 0x1D3FF),
+ 0x1D455,
+ 0x1D49D,
+ ...range(0x1D4A0, 0x1D4A1),
+ ...range(0x1D4A3, 0x1D4A4),
+ ...range(0x1D4A7, 0x1D4A8),
+ 0x1D4AD,
+ 0x1D4BA,
+ 0x1D4BC,
+ 0x1D4C1,
+ 0x1D4C4,
+ 0x1D506,
+ ...range(0x1D50B, 0x1D50C),
+ 0x1D515,
+ 0x1D51D,
+ 0x1D53A,
+ 0x1D53F,
+ 0x1D545,
+ ...range(0x1D547, 0x1D549),
+ 0x1D551,
+ ...range(0x1D6A4, 0x1D6A7),
+ ...range(0x1D7CA, 0x1D7CD),
+ ...range(0x1D800, 0x1FFFD),
+ ...range(0x2A6D7, 0x2F7FF),
+ ...range(0x2FA1E, 0x2FFFD),
+ ...range(0x30000, 0x3FFFD),
+ ...range(0x40000, 0x4FFFD),
+ ...range(0x50000, 0x5FFFD),
+ ...range(0x60000, 0x6FFFD),
+ ...range(0x70000, 0x7FFFD),
+ ...range(0x80000, 0x8FFFD),
+ ...range(0x90000, 0x9FFFD),
+ ...range(0xA0000, 0xAFFFD),
+ ...range(0xB0000, 0xBFFFD),
+ ...range(0xC0000, 0xCFFFD),
+ ...range(0xD0000, 0xDFFFD),
+ 0xE0000,
+ ...range(0xE0002, 0xE001F),
+ ...range(0xE0080, 0xEFFFD),
+])
+
+/**
+ * B.1 Commonly mapped to nothing
+ * @link https://tools.ietf.org/html/rfc3454#appendix-B.1
+ */
+const commonly_mapped_to_nothing = new Set([
+ 0x00AD,
+ 0x034F,
+ 0x1806,
+ 0x180B,
+ 0x180C,
+ 0x180D,
+ 0x200B,
+ 0x200C,
+ 0x200D,
+ 0x2060,
+ 0xFE00,
+ 0xFE01,
+ 0xFE02,
+ 0xFE03,
+ 0xFE04,
+ 0xFE05,
+ 0xFE06,
+ 0xFE07,
+ 0xFE08,
+ 0xFE09,
+ 0xFE0A,
+ 0xFE0B,
+ 0xFE0C,
+ 0xFE0D,
+ 0xFE0E,
+ 0xFE0F,
+ 0xFEFF,
+])
+
+/**
+ * C.1.2 Non-ASCII space characters
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.1.2
+ */
+const non_ASCII_space_characters = new Set([
+ 0x00a0 /* NO-BREAK SPACE */,
+ 0x1680 /* OGHAM SPACE MARK */,
+ 0x2000 /* EN QUAD */,
+ 0x2001 /* EM QUAD */,
+ 0x2002 /* EN SPACE */,
+ 0x2003 /* EM SPACE */,
+ 0x2004 /* THREE-PER-EM SPACE */,
+ 0x2005 /* FOUR-PER-EM SPACE */,
+ 0x2006 /* SIX-PER-EM SPACE */,
+ 0x2007 /* FIGURE SPACE */,
+ 0x2008 /* PUNCTUATION SPACE */,
+ 0x2009 /* THIN SPACE */,
+ 0x200a /* HAIR SPACE */,
+ 0x200b /* ZERO WIDTH SPACE */,
+ 0x202f /* NARROW NO-BREAK SPACE */,
+ 0x205f /* MEDIUM MATHEMATICAL SPACE */,
+ 0x3000 /* IDEOGRAPHIC SPACE */,
+])
+
+/**
+ * 2.3. Prohibited Output
+ * @type {Set}
+ */
+const prohibited_characters = new Set([
+ ...non_ASCII_space_characters,
+
+ /**
+ * C.2.1 ASCII control characters
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.1
+ */
+ ...range(0, 0x001f) /* [CONTROL CHARACTERS] */,
+ 0x007f /* DELETE */,
+
+ /**
+ * C.2.2 Non-ASCII control characters
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.2.2
+ */
+ ...range(0x0080, 0x009F), /* [CONTROL CHARACTERS] */
+ 0x06DD, /* ARABIC END OF AYAH */
+ 0x070F, /* SYRIAC ABBREVIATION MARK */
+ 0x180E, /* MONGOLIAN VOWEL SEPARATOR */
+ 0x200C, /* ZERO WIDTH NON-JOINER */
+ 0x200D, /* ZERO WIDTH JOINER */
+ 0x2028, /* LINE SEPARATOR */
+ 0x2029, /* PARAGRAPH SEPARATOR */
+ 0x2060, /* WORD JOINER */
+ 0x2061, /* FUNCTION APPLICATION */
+ 0x2062, /* INVISIBLE TIMES */
+ 0x2063, /* INVISIBLE SEPARATOR */
+ ...range(0x206A, 0x206F), /* [CONTROL CHARACTERS] */
+ 0xFEFF, /* ZERO WIDTH NO-BREAK SPACE */
+ ...range(0xFFF9, 0xFFFC), /* [CONTROL CHARACTERS] */
+ ...range(0x1D173, 0x1D17A), /* [MUSICAL CONTROL CHARACTERS] */
+
+ /**
+ * C.3 Private use
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.3
+ */
+ ...range(0xE000, 0xF8FF), /* [PRIVATE USE, PLANE 0] */
+ ...range(0xF0000, 0xFFFFD), /* [PRIVATE USE, PLANE 15] */
+ ...range(0x100000, 0x10FFFD), /* [PRIVATE USE, PLANE 16] */
+
+ /**
+ * C.4 Non-character code points
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.4
+ */
+ ...range(0xFDD0, 0xFDEF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0xFFFE, 0xFFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x1FFFE, 0x1FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x2FFFE, 0x2FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x3FFFE, 0x3FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x4FFFE, 0x4FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x5FFFE, 0x5FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x6FFFE, 0x6FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x7FFFE, 0x7FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x8FFFE, 0x8FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x9FFFE, 0x9FFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0xAFFFE, 0xAFFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0xBFFFE, 0xBFFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0xCFFFE, 0xCFFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0xDFFFE, 0xDFFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0xEFFFE, 0xEFFFF), /* [NONCHARACTER CODE POINTS] */
+ ...range(0x10FFFE, 0x10FFFF), /* [NONCHARACTER CODE POINTS] */
+
+ /**
+ * C.5 Surrogate codes
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.5
+ */
+ ...range(0xD800, 0xDFFF),
+
+ /**
+ * C.6 Inappropriate for plain text
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.6
+ */
+ 0xFFF9, /* INTERLINEAR ANNOTATION ANCHOR */
+ 0xFFFA, /* INTERLINEAR ANNOTATION SEPARATOR */
+ 0xFFFB, /* INTERLINEAR ANNOTATION TERMINATOR */
+ 0xFFFC, /* OBJECT REPLACEMENT CHARACTER */
+ 0xFFFD, /* REPLACEMENT CHARACTER */
+
+ /**
+ * C.7 Inappropriate for canonical representation
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.7
+ */
+ ...range(0x2FF0, 0x2FFB), /* [IDEOGRAPHIC DESCRIPTION CHARACTERS] */
+
+ /**
+ * C.8 Change display properties or are deprecated
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.8
+ */
+ 0x0340, /* COMBINING GRAVE TONE MARK */
+ 0x0341, /* COMBINING ACUTE TONE MARK */
+ 0x200E, /* LEFT-TO-RIGHT MARK */
+ 0x200F, /* RIGHT-TO-LEFT MARK */
+ 0x202A, /* LEFT-TO-RIGHT EMBEDDING */
+ 0x202B, /* RIGHT-TO-LEFT EMBEDDING */
+ 0x202C, /* POP DIRECTIONAL FORMATTING */
+ 0x202D, /* LEFT-TO-RIGHT OVERRIDE */
+ 0x202E, /* RIGHT-TO-LEFT OVERRIDE */
+ 0x206A, /* INHIBIT SYMMETRIC SWAPPING */
+ 0x206B, /* ACTIVATE SYMMETRIC SWAPPING */
+ 0x206C, /* INHIBIT ARABIC FORM SHAPING */
+ 0x206D, /* ACTIVATE ARABIC FORM SHAPING */
+ 0x206E, /* NATIONAL DIGIT SHAPES */
+ 0x206F, /* NOMINAL DIGIT SHAPES */
+
+ /**
+ * C.9 Tagging characters
+ * @link https://tools.ietf.org/html/rfc3454#appendix-C.9
+ */
+ 0xE0001, /* LANGUAGE TAG */
+ ...range(0xE0020, 0xE007F) /* [TAGGING CHARACTERS] */
+])
+
+/**
+ * D.1 Characters with bidirectional property "R" or "AL"
+ * @link https://tools.ietf.org/html/rfc3454#appendix-D.1
+ */
+const bidirectional_r_al = new Set([
+ 0x05BE,
+ 0x05C0,
+ 0x05C3,
+ ...range(0x05D0, 0x05EA),
+ ...range(0x05F0, 0x05F4),
+ 0x061B,
+ 0x061F,
+ ...range(0x0621, 0x063A),
+ ...range(0x0640, 0x064A),
+ ...range(0x066D, 0x066F),
+ ...range(0x0671, 0x06D5),
+ 0x06DD,
+ ...range(0x06E5, 0x06E6),
+ ...range(0x06FA, 0x06FE),
+ ...range(0x0700, 0x070D),
+ 0x0710,
+ ...range(0x0712, 0x072C),
+ ...range(0x0780, 0x07A5),
+ 0x07B1,
+ 0x200F,
+ 0xFB1D,
+ ...range(0xFB1F, 0xFB28),
+ ...range(0xFB2A, 0xFB36),
+ ...range(0xFB38, 0xFB3C),
+ 0xFB3E,
+ ...range(0xFB40, 0xFB41),
+ ...range(0xFB43, 0xFB44),
+ ...range(0xFB46, 0xFBB1),
+ ...range(0xFBD3, 0xFD3D),
+ ...range(0xFD50, 0xFD8F),
+ ...range(0xFD92, 0xFDC7),
+ ...range(0xFDF0, 0xFDFC),
+ ...range(0xFE70, 0xFE74),
+ ...range(0xFE76, 0xFEFC),
+])
+
+/**
+ * D.2 Characters with bidirectional property "L"
+ * @link https://tools.ietf.org/html/rfc3454#appendix-D.2
+ */
+const bidirectional_l = new Set([
+ ...range(0x0041, 0x005A),
+ ...range(0x0061, 0x007A),
+ 0x00AA,
+ 0x00B5,
+ 0x00BA,
+ ...range(0x00C0, 0x00D6),
+ ...range(0x00D8, 0x00F6),
+ ...range(0x00F8, 0x0220),
+ ...range(0x0222, 0x0233),
+ ...range(0x0250, 0x02AD),
+ ...range(0x02B0, 0x02B8),
+ ...range(0x02BB, 0x02C1),
+ ...range(0x02D0, 0x02D1),
+ ...range(0x02E0, 0x02E4),
+ 0x02EE,
+ 0x037A,
+ 0x0386,
+ ...range(0x0388, 0x038A),
+ 0x038C,
+ ...range(0x038E, 0x03A1),
+ ...range(0x03A3, 0x03CE),
+ ...range(0x03D0, 0x03F5),
+ ...range(0x0400, 0x0482),
+ ...range(0x048A, 0x04CE),
+ ...range(0x04D0, 0x04F5),
+ ...range(0x04F8, 0x04F9),
+ ...range(0x0500, 0x050F),
+ ...range(0x0531, 0x0556),
+ ...range(0x0559, 0x055F),
+ ...range(0x0561, 0x0587),
+ 0x0589,
+ 0x0903,
+ ...range(0x0905, 0x0939),
+ ...range(0x093D, 0x0940),
+ ...range(0x0949, 0x094C),
+ 0x0950,
+ ...range(0x0958, 0x0961),
+ ...range(0x0964, 0x0970),
+ ...range(0x0982, 0x0983),
+ ...range(0x0985, 0x098C),
+ ...range(0x098F, 0x0990),
+ ...range(0x0993, 0x09A8),
+ ...range(0x09AA, 0x09B0),
+ 0x09B2,
+ ...range(0x09B6, 0x09B9),
+ ...range(0x09BE, 0x09C0),
+ ...range(0x09C7, 0x09C8),
+ ...range(0x09CB, 0x09CC),
+ 0x09D7,
+ ...range(0x09DC, 0x09DD),
+ ...range(0x09DF, 0x09E1),
+ ...range(0x09E6, 0x09F1),
+ ...range(0x09F4, 0x09FA),
+ ...range(0x0A05, 0x0A0A),
+ ...range(0x0A0F, 0x0A10),
+ ...range(0x0A13, 0x0A28),
+ ...range(0x0A2A, 0x0A30),
+ ...range(0x0A32, 0x0A33),
+ ...range(0x0A35, 0x0A36),
+ ...range(0x0A38, 0x0A39),
+ ...range(0x0A3E, 0x0A40),
+ ...range(0x0A59, 0x0A5C),
+ 0x0A5E,
+ ...range(0x0A66, 0x0A6F),
+ ...range(0x0A72, 0x0A74),
+ 0x0A83,
+ ...range(0x0A85, 0x0A8B),
+ 0x0A8D,
+ ...range(0x0A8F, 0x0A91),
+ ...range(0x0A93, 0x0AA8),
+ ...range(0x0AAA, 0x0AB0),
+ ...range(0x0AB2, 0x0AB3),
+ ...range(0x0AB5, 0x0AB9),
+ ...range(0x0ABD, 0x0AC0),
+ 0x0AC9,
+ ...range(0x0ACB, 0x0ACC),
+ 0x0AD0,
+ 0x0AE0,
+ ...range(0x0AE6, 0x0AEF),
+ ...range(0x0B02, 0x0B03),
+ ...range(0x0B05, 0x0B0C),
+ ...range(0x0B0F, 0x0B10),
+ ...range(0x0B13, 0x0B28),
+ ...range(0x0B2A, 0x0B30),
+ ...range(0x0B32, 0x0B33),
+ ...range(0x0B36, 0x0B39),
+ ...range(0x0B3D, 0x0B3E),
+ 0x0B40,
+ ...range(0x0B47, 0x0B48),
+ ...range(0x0B4B, 0x0B4C),
+ 0x0B57,
+ ...range(0x0B5C, 0x0B5D),
+ ...range(0x0B5F, 0x0B61),
+ ...range(0x0B66, 0x0B70),
+ 0x0B83,
+ ...range(0x0B85, 0x0B8A),
+ ...range(0x0B8E, 0x0B90),
+ ...range(0x0B92, 0x0B95),
+ ...range(0x0B99, 0x0B9A),
+ 0x0B9C,
+ ...range(0x0B9E, 0x0B9F),
+ ...range(0x0BA3, 0x0BA4),
+ ...range(0x0BA8, 0x0BAA),
+ ...range(0x0BAE, 0x0BB5),
+ ...range(0x0BB7, 0x0BB9),
+ ...range(0x0BBE, 0x0BBF),
+ ...range(0x0BC1, 0x0BC2),
+ ...range(0x0BC6, 0x0BC8),
+ ...range(0x0BCA, 0x0BCC),
+ 0x0BD7,
+ ...range(0x0BE7, 0x0BF2),
+ ...range(0x0C01, 0x0C03),
+ ...range(0x0C05, 0x0C0C),
+ ...range(0x0C0E, 0x0C10),
+ ...range(0x0C12, 0x0C28),
+ ...range(0x0C2A, 0x0C33),
+ ...range(0x0C35, 0x0C39),
+ ...range(0x0C41, 0x0C44),
+ ...range(0x0C60, 0x0C61),
+ ...range(0x0C66, 0x0C6F),
+ ...range(0x0C82, 0x0C83),
+ ...range(0x0C85, 0x0C8C),
+ ...range(0x0C8E, 0x0C90),
+ ...range(0x0C92, 0x0CA8),
+ ...range(0x0CAA, 0x0CB3),
+ ...range(0x0CB5, 0x0CB9),
+ 0x0CBE,
+ ...range(0x0CC0, 0x0CC4),
+ ...range(0x0CC7, 0x0CC8),
+ ...range(0x0CCA, 0x0CCB),
+ ...range(0x0CD5, 0x0CD6),
+ 0x0CDE,
+ ...range(0x0CE0, 0x0CE1),
+ ...range(0x0CE6, 0x0CEF),
+ ...range(0x0D02, 0x0D03),
+ ...range(0x0D05, 0x0D0C),
+ ...range(0x0D0E, 0x0D10),
+ ...range(0x0D12, 0x0D28),
+ ...range(0x0D2A, 0x0D39),
+ ...range(0x0D3E, 0x0D40),
+ ...range(0x0D46, 0x0D48),
+ ...range(0x0D4A, 0x0D4C),
+ 0x0D57,
+ ...range(0x0D60, 0x0D61),
+ ...range(0x0D66, 0x0D6F),
+ ...range(0x0D82, 0x0D83),
+ ...range(0x0D85, 0x0D96),
+ ...range(0x0D9A, 0x0DB1),
+ ...range(0x0DB3, 0x0DBB),
+ 0x0DBD,
+ ...range(0x0DC0, 0x0DC6),
+ ...range(0x0DCF, 0x0DD1),
+ ...range(0x0DD8, 0x0DDF),
+ ...range(0x0DF2, 0x0DF4),
+ ...range(0x0E01, 0x0E30),
+ ...range(0x0E32, 0x0E33),
+ ...range(0x0E40, 0x0E46),
+ ...range(0x0E4F, 0x0E5B),
+ ...range(0x0E81, 0x0E82),
+ 0x0E84,
+ ...range(0x0E87, 0x0E88),
+ 0x0E8A,
+ 0x0E8D,
+ ...range(0x0E94, 0x0E97),
+ ...range(0x0E99, 0x0E9F),
+ ...range(0x0EA1, 0x0EA3),
+ 0x0EA5,
+ 0x0EA7,
+ ...range(0x0EAA, 0x0EAB),
+ ...range(0x0EAD, 0x0EB0),
+ ...range(0x0EB2, 0x0EB3),
+ 0x0EBD,
+ ...range(0x0EC0, 0x0EC4),
+ 0x0EC6,
+ ...range(0x0ED0, 0x0ED9),
+ ...range(0x0EDC, 0x0EDD),
+ ...range(0x0F00, 0x0F17),
+ ...range(0x0F1A, 0x0F34),
+ 0x0F36,
+ 0x0F38,
+ ...range(0x0F3E, 0x0F47),
+ ...range(0x0F49, 0x0F6A),
+ 0x0F7F,
+ 0x0F85,
+ ...range(0x0F88, 0x0F8B),
+ ...range(0x0FBE, 0x0FC5),
+ ...range(0x0FC7, 0x0FCC),
+ 0x0FCF,
+ ...range(0x1000, 0x1021),
+ ...range(0x1023, 0x1027),
+ ...range(0x1029, 0x102A),
+ 0x102C,
+ 0x1031,
+ 0x1038,
+ ...range(0x1040, 0x1057),
+ ...range(0x10A0, 0x10C5),
+ ...range(0x10D0, 0x10F8),
+ 0x10FB,
+ ...range(0x1100, 0x1159),
+ ...range(0x115F, 0x11A2),
+ ...range(0x11A8, 0x11F9),
+ ...range(0x1200, 0x1206),
+ ...range(0x1208, 0x1246),
+ 0x1248,
+ ...range(0x124A, 0x124D),
+ ...range(0x1250, 0x1256),
+ 0x1258,
+ ...range(0x125A, 0x125D),
+ ...range(0x1260, 0x1286),
+ 0x1288,
+ ...range(0x128A, 0x128D),
+ ...range(0x1290, 0x12AE),
+ 0x12B0,
+ ...range(0x12B2, 0x12B5),
+ ...range(0x12B8, 0x12BE),
+ 0x12C0,
+ ...range(0x12C2, 0x12C5),
+ ...range(0x12C8, 0x12CE),
+ ...range(0x12D0, 0x12D6),
+ ...range(0x12D8, 0x12EE),
+ ...range(0x12F0, 0x130E),
+ 0x1310,
+ ...range(0x1312, 0x1315),
+ ...range(0x1318, 0x131E),
+ ...range(0x1320, 0x1346),
+ ...range(0x1348, 0x135A),
+ ...range(0x1361, 0x137C),
+ ...range(0x13A0, 0x13F4),
+ ...range(0x1401, 0x1676),
+ ...range(0x1681, 0x169A),
+ ...range(0x16A0, 0x16F0),
+ ...range(0x1700, 0x170C),
+ ...range(0x170E, 0x1711),
+ ...range(0x1720, 0x1731),
+ ...range(0x1735, 0x1736),
+ ...range(0x1740, 0x1751),
+ ...range(0x1760, 0x176C),
+ ...range(0x176E, 0x1770),
+ ...range(0x1780, 0x17B6),
+ ...range(0x17BE, 0x17C5),
+ ...range(0x17C7, 0x17C8),
+ ...range(0x17D4, 0x17DA),
+ 0x17DC,
+ ...range(0x17E0, 0x17E9),
+ ...range(0x1810, 0x1819),
+ ...range(0x1820, 0x1877),
+ ...range(0x1880, 0x18A8),
+ ...range(0x1E00, 0x1E9B),
+ ...range(0x1EA0, 0x1EF9),
+ ...range(0x1F00, 0x1F15),
+ ...range(0x1F18, 0x1F1D),
+ ...range(0x1F20, 0x1F45),
+ ...range(0x1F48, 0x1F4D),
+ ...range(0x1F50, 0x1F57),
+ 0x1F59,
+ 0x1F5B,
+ 0x1F5D,
+ ...range(0x1F5F, 0x1F7D),
+ ...range(0x1F80, 0x1FB4),
+ ...range(0x1FB6, 0x1FBC),
+ 0x1FBE,
+ ...range(0x1FC2, 0x1FC4),
+ ...range(0x1FC6, 0x1FCC),
+ ...range(0x1FD0, 0x1FD3),
+ ...range(0x1FD6, 0x1FDB),
+ ...range(0x1FE0, 0x1FEC),
+ ...range(0x1FF2, 0x1FF4),
+ ...range(0x1FF6, 0x1FFC),
+ 0x200E,
+ 0x2071,
+ 0x207F,
+ 0x2102,
+ 0x2107,
+ ...range(0x210A, 0x2113),
+ 0x2115,
+ ...range(0x2119, 0x211D),
+ 0x2124,
+ 0x2126,
+ 0x2128,
+ ...range(0x212A, 0x212D),
+ ...range(0x212F, 0x2131),
+ ...range(0x2133, 0x2139),
+ ...range(0x213D, 0x213F),
+ ...range(0x2145, 0x2149),
+ ...range(0x2160, 0x2183),
+ ...range(0x2336, 0x237A),
+ 0x2395,
+ ...range(0x249C, 0x24E9),
+ ...range(0x3005, 0x3007),
+ ...range(0x3021, 0x3029),
+ ...range(0x3031, 0x3035),
+ ...range(0x3038, 0x303C),
+ ...range(0x3041, 0x3096),
+ ...range(0x309D, 0x309F),
+ ...range(0x30A1, 0x30FA),
+ ...range(0x30FC, 0x30FF),
+ ...range(0x3105, 0x312C),
+ ...range(0x3131, 0x318E),
+ ...range(0x3190, 0x31B7),
+ ...range(0x31F0, 0x321C),
+ ...range(0x3220, 0x3243),
+ ...range(0x3260, 0x327B),
+ ...range(0x327F, 0x32B0),
+ ...range(0x32C0, 0x32CB),
+ ...range(0x32D0, 0x32FE),
+ ...range(0x3300, 0x3376),
+ ...range(0x337B, 0x33DD),
+ ...range(0x33E0, 0x33FE),
+ ...range(0x3400, 0x4DB5),
+ ...range(0x4E00, 0x9FA5),
+ ...range(0xA000, 0xA48C),
+ ...range(0xAC00, 0xD7A3),
+ ...range(0xD800, 0xFA2D),
+ ...range(0xFA30, 0xFA6A),
+ ...range(0xFB00, 0xFB06),
+ ...range(0xFB13, 0xFB17),
+ ...range(0xFF21, 0xFF3A),
+ ...range(0xFF41, 0xFF5A),
+ ...range(0xFF66, 0xFFBE),
+ ...range(0xFFC2, 0xFFC7),
+ ...range(0xFFCA, 0xFFCF),
+ ...range(0xFFD2, 0xFFD7),
+ ...range(0xFFDA, 0xFFDC),
+ ...range(0x10300, 0x1031E),
+ ...range(0x10320, 0x10323),
+ ...range(0x10330, 0x1034A),
+ ...range(0x10400, 0x10425),
+ ...range(0x10428, 0x1044D),
+ ...range(0x1D000, 0x1D0F5),
+ ...range(0x1D100, 0x1D126),
+ ...range(0x1D12A, 0x1D166),
+ ...range(0x1D16A, 0x1D172),
+ ...range(0x1D183, 0x1D184),
+ ...range(0x1D18C, 0x1D1A9),
+ ...range(0x1D1AE, 0x1D1DD),
+ ...range(0x1D400, 0x1D454),
+ ...range(0x1D456, 0x1D49C),
+ ...range(0x1D49E, 0x1D49F),
+ 0x1D4A2,
+ ...range(0x1D4A5, 0x1D4A6),
+ ...range(0x1D4A9, 0x1D4AC),
+ ...range(0x1D4AE, 0x1D4B9),
+ 0x1D4BB,
+ ...range(0x1D4BD, 0x1D4C0),
+ ...range(0x1D4C2, 0x1D4C3),
+ ...range(0x1D4C5, 0x1D505),
+ ...range(0x1D507, 0x1D50A),
+ ...range(0x1D50D, 0x1D514),
+ ...range(0x1D516, 0x1D51C),
+ ...range(0x1D51E, 0x1D539),
+ ...range(0x1D53B, 0x1D53E),
+ ...range(0x1D540, 0x1D544),
+ 0x1D546,
+ ...range(0x1D54A, 0x1D550),
+ ...range(0x1D552, 0x1D6A3),
+ ...range(0x1D6A8, 0x1D7C9),
+ ...range(0x20000, 0x2A6D6),
+ ...range(0x2F800, 0x2FA1D),
+ ...range(0xF0000, 0xFFFFD),
+ ...range(0x100000, 0x10FFFD)
+])
+
+module.exports = {
+ unassigned_code_points,
+ commonly_mapped_to_nothing,
+ non_ASCII_space_characters,
+ prohibited_characters,
+ bidirectional_r_al,
+ bidirectional_l
+}
diff --git a/node_modules/saslprep/lib/memory-code-points.js b/node_modules/saslprep/lib/memory-code-points.js
new file mode 100644
index 0000000..c776d42
--- /dev/null
+++ b/node_modules/saslprep/lib/memory-code-points.js
@@ -0,0 +1,34 @@
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const bitfield = require('sparse-bitfield');
+
+const memory = fs.readFileSync(path.resolve(__dirname, '../code-points.mem'));
+let offset = 0;
+
+function read() {
+ const size = memory.readUInt32BE(offset);
+ offset += 4;
+
+ const codepoints = memory.slice(offset, offset + size);
+ offset += size;
+
+ return bitfield({ buffer: codepoints });
+}
+
+const unassigned_code_points = read();
+const commonly_mapped_to_nothing = read();
+const non_ASCII_space_characters = read();
+const prohibited_characters = read();
+const bidirectional_r_al = read();
+const bidirectional_l = read();
+
+module.exports = {
+ unassigned_code_points,
+ commonly_mapped_to_nothing,
+ non_ASCII_space_characters,
+ prohibited_characters,
+ bidirectional_r_al,
+ bidirectional_l,
+}
diff --git a/node_modules/saslprep/lib/util.js b/node_modules/saslprep/lib/util.js
new file mode 100644
index 0000000..697e0d0
--- /dev/null
+++ b/node_modules/saslprep/lib/util.js
@@ -0,0 +1,15 @@
+'use strict'
+
+function range(from, to) {
+ // TODO: make this inlined.
+ const list = new Array(to - from + 1)
+
+ for(let i = 0; i < list.length; ++i) {
+ list[i] = from + i
+ }
+ return list
+}
+
+module.exports = {
+ range
+}
diff --git a/node_modules/saslprep/package.json b/node_modules/saslprep/package.json
new file mode 100644
index 0000000..466fcee
--- /dev/null
+++ b/node_modules/saslprep/package.json
@@ -0,0 +1,66 @@
+{
+ "_from": "saslprep@^1.0.0",
+ "_id": "saslprep@1.0.2",
+ "_inBundle": false,
+ "_integrity": "sha512-4cDsYuAjXssUSjxHKRe4DTZC0agDwsCqcMqtJAQPzC74nJ7LfAJflAtC1Zed5hMzEQKj82d3tuzqdGNRsLJ4Gw==",
+ "_location": "/saslprep",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "saslprep@^1.0.0",
+ "name": "saslprep",
+ "escapedName": "saslprep",
+ "rawSpec": "^1.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^1.0.0"
+ },
+ "_requiredBy": [
+ "/mongodb-core"
+ ],
+ "_resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.2.tgz",
+ "_shasum": "da5ab936e6ea0bbae911ffec77534be370c9f52d",
+ "_spec": "saslprep@^1.0.0",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mongodb-core",
+ "author": {
+ "name": "Dmitry Tsvettsikh",
+ "email": "me@reklatsmasters.com"
+ },
+ "bugs": {
+ "url": "https://github.com/reklatsmasters/saslprep/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "sparse-bitfield": "^3.0.3"
+ },
+ "deprecated": false,
+ "description": "SASLprep: Stringprep Profile for User Names and Passwords, rfc4013.",
+ "devDependencies": {
+ "ava": "^0.25.0",
+ "eslint-plugin-prettier": "^2.1.2",
+ "prettier": "^1.4.4"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "homepage": "https://github.com/reklatsmasters/saslprep#readme",
+ "keywords": [
+ "sasl",
+ "saslprep",
+ "stringprep",
+ "rfc4013",
+ "4013"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "saslprep",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/reklatsmasters/saslprep.git"
+ },
+ "scripts": {
+ "gen-code-points": "node generate-code-points.js > code-points.mem",
+ "test": "ava"
+ },
+ "version": "1.0.2"
+}
diff --git a/node_modules/saslprep/readme.md b/node_modules/saslprep/readme.md
new file mode 100644
index 0000000..a94e760
--- /dev/null
+++ b/node_modules/saslprep/readme.md
@@ -0,0 +1,31 @@
+# saslprep
+[](https://travis-ci.org/reklatsmasters/saslprep)
+[](https://npmjs.org/package/saslprep)
+[](https://npmjs.org/package/saslprep)
+[](https://npmjs.org/package/saslprep)
+[](https://npmjs.org/package/saslprep)
+
+Stringprep Profile for User Names and Passwords, [rfc4013](https://tools.ietf.org/html/rfc4013)
+
+### Usage
+
+```js
+const saslprep = require('saslprep')
+
+saslprep('password\u00AD') // password
+saslprep('password\u0007') // Error: prohibited character
+```
+
+### API
+
+##### `saslprep(input: String, opts: Options): String`
+
+Normalize user name or password.
+
+##### `Options.allowUnassigned: bool`
+
+A special behavior for unassigned code points, see https://tools.ietf.org/html/rfc4013#section-2.5. Disabled by default.
+
+## License
+
+MIT, 2017 (c) Dmitriy Tsvettsikh
diff --git a/node_modules/saslprep/test/index.js b/node_modules/saslprep/test/index.js
new file mode 100644
index 0000000..1fb71cf
--- /dev/null
+++ b/node_modules/saslprep/test/index.js
@@ -0,0 +1,70 @@
+import test from 'ava'
+import saslprep from '../'
+
+const chr = String.fromCodePoint
+
+test('should work with liatin letters', (t) => {
+ const str = 'user'
+ t.is(saslprep(str), str)
+})
+
+test('should work be case preserved', (t) => {
+ const str = 'USER'
+ t.is(saslprep(str), str)
+})
+
+test('should remove `mapped to nothing` characters', (t) => {
+ t.is(saslprep('I\u00ADX'), 'IX')
+})
+
+test('should replace `Non-ASCII space characters` with space', (t) => {
+ t.is(saslprep('a\u00A0b'), 'a\u0020b')
+})
+
+test('should normalize as NFKC', (t) => {
+ t.is(saslprep('\u00AA'), 'a')
+ t.is(saslprep('\u2168'), 'IX')
+})
+
+test('should throws when prohibited characters', (t) => {
+ // C.2.1 ASCII control characters
+ t.throws(() => saslprep('a\u007Fb'))
+
+ // C.2.2 Non-ASCII control characters
+ t.throws(() => saslprep('a\u06DDb'))
+
+ // C.3 Private use
+ t.throws(() => saslprep('a\uE000b'))
+
+ // C.4 Non-character code points
+ t.throws(() => saslprep(`a${chr(0x1FFFE)}b`))
+
+ // C.5 Surrogate codes
+ t.throws(() => saslprep('a\uD800b'))
+
+ // C.6 Inappropriate for plain text
+ t.throws(() => saslprep('a\uFFF9b'))
+
+ // C.7 Inappropriate for canonical representation
+ t.throws(() => saslprep('a\u2FF0b'))
+
+ // C.8 Change display properties or are deprecated
+ t.throws(() => saslprep('a\u200Eb'))
+
+ // C.9 Tagging characters
+ t.throws(() => saslprep(`a${chr(0xE0001)}b`))
+})
+
+test('should not containt RandALCat and LCat bidi', (t) => {
+ t.throws(() => saslprep('a\u06DD\u00AAb'))
+})
+
+test('RandALCat should be first and last', (t) => {
+ t.notThrows(() => saslprep('\u0627\u0031\u0628'))
+ t.throws(() => saslprep('\u0627\u0031'))
+})
+
+test('should handle unassigned code points', (t) => {
+ t.throws(() => saslprep('a\u0487'))
+ t.notThrows(() => saslprep('a\u0487', {allowUnassigned: true}))
+})
diff --git a/node_modules/saslprep/test/util.js b/node_modules/saslprep/test/util.js
new file mode 100644
index 0000000..fb39a85
--- /dev/null
+++ b/node_modules/saslprep/test/util.js
@@ -0,0 +1,15 @@
+import { setFlagsFromString } from 'v8'
+import { range } from '../lib/util'
+import test from 'ava'
+
+// 984 by default.
+setFlagsFromString('--stack_size=500')
+
+test('should work', (t) => {
+ const list = range(1, 3)
+ t.deepEqual(list, [1, 2, 3])
+})
+
+test('should work for large ranges', (t) => {
+ t.notThrows(() => range(1, 1e6))
+})
diff --git a/node_modules/sliced/History.md b/node_modules/sliced/History.md
new file mode 100644
index 0000000..e45cefe
--- /dev/null
+++ b/node_modules/sliced/History.md
@@ -0,0 +1,41 @@
+
+1.0.1 / 2015-07-14
+==================
+
+ * fixed; missing file introduced in 4f5cea1
+
+1.0.0 / 2015-07-12
+==================
+
+ * Remove unnecessary files from npm package - #6 via joaquimserafim
+ * updated readme stats
+
+0.0.5 / 2013-02-05
+==================
+
+ * optimization: remove use of arguments [jkroso](https://github.com/jkroso)
+ * add scripts to component.json [jkroso](https://github.com/jkroso)
+ * tests; remove time for travis
+
+0.0.4 / 2013-01-07
+==================
+
+ * added component.json #1 [jkroso](https://github.com/jkroso)
+ * reversed array loop #1 [jkroso](https://github.com/jkroso)
+ * remove fn params
+
+0.0.3 / 2012-09-29
+==================
+
+ * faster with negative start args
+
+0.0.2 / 2012-09-29
+==================
+
+ * support full [].slice semantics
+
+0.0.1 / 2012-09-29
+===================
+
+ * initial release
+
diff --git a/node_modules/sliced/LICENSE b/node_modules/sliced/LICENSE
new file mode 100644
index 0000000..38c529d
--- /dev/null
+++ b/node_modules/sliced/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012 [Aaron Heckmann](aaron.heckmann+github@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/sliced/README.md b/node_modules/sliced/README.md
new file mode 100644
index 0000000..b6ff85e
--- /dev/null
+++ b/node_modules/sliced/README.md
@@ -0,0 +1,62 @@
+#sliced
+==========
+
+A faster alternative to `[].slice.call(arguments)`.
+
+[](http://travis-ci.org/aheckmann/sliced)
+
+Example output from [benchmark.js](https://github.com/bestiejs/benchmark.js)
+
+ Array.prototype.slice.call x 1,401,820 ops/sec ±2.16% (90 runs sampled)
+ [].slice.call x 1,313,116 ops/sec ±2.04% (96 runs sampled)
+ cached slice.call x 10,297,910 ops/sec ±1.81% (96 runs sampled)
+ sliced x 19,906,019 ops/sec ±1.23% (89 runs sampled)
+ fastest is sliced
+
+ Array.prototype.slice.call(arguments, 1) x 1,373,238 ops/sec ±1.84% (95 runs sampled)
+ [].slice.call(arguments, 1) x 1,395,336 ops/sec ±1.36% (93 runs sampled)
+ cached slice.call(arguments, 1) x 9,926,018 ops/sec ±1.67% (92 runs sampled)
+ sliced(arguments, 1) x 20,747,990 ops/sec ±1.16% (93 runs sampled)
+ fastest is sliced(arguments, 1)
+
+ Array.prototype.slice.call(arguments, -1) x 1,319,908 ops/sec ±2.12% (91 runs sampled)
+ [].slice.call(arguments, -1) x 1,336,170 ops/sec ±1.33% (97 runs sampled)
+ cached slice.call(arguments, -1) x 10,078,718 ops/sec ±1.21% (98 runs sampled)
+ sliced(arguments, -1) x 20,471,474 ops/sec ±1.81% (92 runs sampled)
+ fastest is sliced(arguments, -1)
+
+ Array.prototype.slice.call(arguments, -2, -10) x 1,369,246 ops/sec ±1.68% (97 runs sampled)
+ [].slice.call(arguments, -2, -10) x 1,387,935 ops/sec ±1.70% (95 runs sampled)
+ cached slice.call(arguments, -2, -10) x 9,593,428 ops/sec ±1.23% (97 runs sampled)
+ sliced(arguments, -2, -10) x 23,178,931 ops/sec ±1.70% (92 runs sampled)
+ fastest is sliced(arguments, -2, -10)
+
+ Array.prototype.slice.call(arguments, -2, -1) x 1,441,300 ops/sec ±1.26% (98 runs sampled)
+ [].slice.call(arguments, -2, -1) x 1,410,326 ops/sec ±1.96% (93 runs sampled)
+ cached slice.call(arguments, -2, -1) x 9,854,419 ops/sec ±1.02% (97 runs sampled)
+ sliced(arguments, -2, -1) x 22,550,801 ops/sec ±1.86% (91 runs sampled)
+ fastest is sliced(arguments, -2, -1)
+
+_Benchmark [source](https://github.com/aheckmann/sliced/blob/master/bench.js)._
+
+##Usage
+
+`sliced` accepts the same arguments as `Array#slice` so you can easily swap it out.
+
+```js
+function zing () {
+ var slow = [].slice.call(arguments, 1, 8);
+ var args = slice(arguments, 1, 8);
+
+ var slow = Array.prototype.slice.call(arguments);
+ var args = slice(arguments);
+ // etc
+}
+```
+
+## install
+
+ npm install sliced
+
+
+[LICENSE](https://github.com/aheckmann/sliced/blob/master/LICENSE)
diff --git a/node_modules/sliced/index.js b/node_modules/sliced/index.js
new file mode 100644
index 0000000..d88c85b
--- /dev/null
+++ b/node_modules/sliced/index.js
@@ -0,0 +1,33 @@
+
+/**
+ * An Array.prototype.slice.call(arguments) alternative
+ *
+ * @param {Object} args something with a length
+ * @param {Number} slice
+ * @param {Number} sliceEnd
+ * @api public
+ */
+
+module.exports = function (args, slice, sliceEnd) {
+ var ret = [];
+ var len = args.length;
+
+ if (0 === len) return ret;
+
+ var start = slice < 0
+ ? Math.max(0, slice + len)
+ : slice || 0;
+
+ if (sliceEnd !== undefined) {
+ len = sliceEnd < 0
+ ? sliceEnd + len
+ : sliceEnd
+ }
+
+ while (len-- > start) {
+ ret[len - start] = args[len];
+ }
+
+ return ret;
+}
+
diff --git a/node_modules/sliced/package.json b/node_modules/sliced/package.json
new file mode 100644
index 0000000..3a5571a
--- /dev/null
+++ b/node_modules/sliced/package.json
@@ -0,0 +1,63 @@
+{
+ "_from": "sliced@1.0.1",
+ "_id": "sliced@1.0.1",
+ "_inBundle": false,
+ "_integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=",
+ "_location": "/sliced",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "sliced@1.0.1",
+ "name": "sliced",
+ "escapedName": "sliced",
+ "rawSpec": "1.0.1",
+ "saveSpec": null,
+ "fetchSpec": "1.0.1"
+ },
+ "_requiredBy": [
+ "/mongoose",
+ "/mquery"
+ ],
+ "_resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
+ "_shasum": "0b3a662b5d04c3177b1926bea82b03f837a2ef41",
+ "_spec": "sliced@1.0.1",
+ "_where": "D:\\Gitterra\\om\\node_modules\\mongoose",
+ "author": {
+ "name": "Aaron Heckmann",
+ "email": "aaron.heckmann+github@gmail.com"
+ },
+ "bugs": {
+ "url": "https://github.com/aheckmann/sliced/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {},
+ "deprecated": false,
+ "description": "A faster Node.js alternative to Array.prototype.slice.call(arguments)",
+ "devDependencies": {
+ "benchmark": "~1.0.0",
+ "mocha": "1.5.0"
+ },
+ "files": [
+ "LICENSE",
+ "README.md",
+ "index.js"
+ ],
+ "homepage": "https://github.com/aheckmann/sliced#readme",
+ "keywords": [
+ "arguments",
+ "slice",
+ "array"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "sliced",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aheckmann/sliced.git"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "version": "1.0.1"
+}
diff --git a/node_modules/sparse-bitfield/.npmignore b/node_modules/sparse-bitfield/.npmignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/node_modules/sparse-bitfield/.npmignore
@@ -0,0 +1 @@
+node_modules
diff --git a/node_modules/sparse-bitfield/.travis.yml b/node_modules/sparse-bitfield/.travis.yml
new file mode 100644
index 0000000..c042821
--- /dev/null
+++ b/node_modules/sparse-bitfield/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+node_js:
+ - '0.10'
+ - '0.12'
+ - '4.0'
+ - '5.0'
diff --git a/node_modules/sparse-bitfield/LICENSE b/node_modules/sparse-bitfield/LICENSE
new file mode 100644
index 0000000..bae9da7
--- /dev/null
+++ b/node_modules/sparse-bitfield/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Mathias Buus
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/sparse-bitfield/README.md b/node_modules/sparse-bitfield/README.md
new file mode 100644
index 0000000..7b6b8f9
--- /dev/null
+++ b/node_modules/sparse-bitfield/README.md
@@ -0,0 +1,62 @@
+# sparse-bitfield
+
+Bitfield implementation that allocates a series of 1kb buffers to support sparse bitfields
+without allocating a massive buffer. If you want to simple implementation of a flat bitfield
+see the [bitfield](https://github.com/fb55/bitfield) module.
+
+This module is mostly useful if you need a big bitfield where you won't nessecarily set every bit.
+
+```
+npm install sparse-bitfield
+```
+
+[](http://travis-ci.org/mafintosh/sparse-bitfield)
+
+## Usage
+
+``` js
+var bitfield = require('sparse-bitfield')
+var bits = bitfield()
+
+bits.set(0, true) // set first bit
+bits.set(1, true) // set second bit
+bits.set(1000000000000, true) // set the 1.000.000.000.000th bit
+```
+
+Running the above example will allocate two 1kb buffers internally.
+Each 1kb buffer can hold information about 8192 bits so the first one will be used to store information about the first two bits and the second will be used to store the 1.000.000.000.000th bit.
+
+## API
+
+#### `var bits = bitfield([options])`
+
+Create a new bitfield. Options include
+
+``` js
+{
+ pageSize: 1024, // how big should the partial buffers be
+ buffer: anExistingBitfield,
+ trackUpdates: false // track when pages are being updated in the pager
+}
+```
+
+#### `bits.set(index, value)`
+
+Set a bit to true or false.
+
+#### `bits.get(index)`
+
+Get the value of a bit.
+
+#### `bits.pages`
+
+A [memory-pager](https://github.com/mafintosh/memory-pager) instance that is managing the underlying memory.
+If you set `trackUpdates` to true in the constructor you can use `.lastUpdate()` on this instance to get the last updated memory page.
+
+#### `var buffer = bits.toBuffer()`
+
+Get a single buffer representing the entire bitfield.
+
+## License
+
+MIT
diff --git a/node_modules/sparse-bitfield/index.js b/node_modules/sparse-bitfield/index.js
new file mode 100644
index 0000000..ff458c9
--- /dev/null
+++ b/node_modules/sparse-bitfield/index.js
@@ -0,0 +1,95 @@
+var pager = require('memory-pager')
+
+module.exports = Bitfield
+
+function Bitfield (opts) {
+ if (!(this instanceof Bitfield)) return new Bitfield(opts)
+ if (!opts) opts = {}
+ if (Buffer.isBuffer(opts)) opts = {buffer: opts}
+
+ this.pageOffset = opts.pageOffset || 0
+ this.pageSize = opts.pageSize || 1024
+ this.pages = opts.pages || pager(this.pageSize)
+
+ this.byteLength = this.pages.length * this.pageSize
+ this.length = 8 * this.byteLength
+
+ if (!powerOfTwo(this.pageSize)) throw new Error('The page size should be a power of two')
+
+ this._trackUpdates = !!opts.trackUpdates
+ this._pageMask = this.pageSize - 1
+
+ if (opts.buffer) {
+ for (var i = 0; i < opts.buffer.length; i += this.pageSize) {
+ this.pages.set(i / this.pageSize, opts.buffer.slice(i, i + this.pageSize))
+ }
+ this.byteLength = opts.buffer.length
+ this.length = 8 * this.byteLength
+ }
+}
+
+Bitfield.prototype.get = function (i) {
+ var o = i & 7
+ var j = (i - o) / 8
+
+ return !!(this.getByte(j) & (128 >> o))
+}
+
+Bitfield.prototype.getByte = function (i) {
+ var o = i & this._pageMask
+ var j = (i - o) / this.pageSize
+ var page = this.pages.get(j, true)
+
+ return page ? page.buffer[o + this.pageOffset] : 0
+}
+
+Bitfield.prototype.set = function (i, v) {
+ var o = i & 7
+ var j = (i - o) / 8
+ var b = this.getByte(j)
+
+ return this.setByte(j, v ? b | (128 >> o) : b & (255 ^ (128 >> o)))
+}
+
+Bitfield.prototype.toBuffer = function () {
+ var all = alloc(this.pages.length * this.pageSize)
+
+ for (var i = 0; i < this.pages.length; i++) {
+ var next = this.pages.get(i, true)
+ var allOffset = i * this.pageSize
+ if (next) next.buffer.copy(all, allOffset, this.pageOffset, this.pageOffset + this.pageSize)
+ }
+
+ return all
+}
+
+Bitfield.prototype.setByte = function (i, b) {
+ var o = i & this._pageMask
+ var j = (i - o) / this.pageSize
+ var page = this.pages.get(j, false)
+
+ o += this.pageOffset
+
+ if (page.buffer[o] === b) return false
+ page.buffer[o] = b
+
+ if (i >= this.byteLength) {
+ this.byteLength = i + 1
+ this.length = this.byteLength * 8
+ }
+
+ if (this._trackUpdates) this.pages.updated(page)
+
+ return true
+}
+
+function alloc (n) {
+ if (Buffer.alloc) return Buffer.alloc(n)
+ var b = new Buffer(n)
+ b.fill(0)
+ return b
+}
+
+function powerOfTwo (x) {
+ return !(x & (x - 1))
+}
diff --git a/node_modules/sparse-bitfield/package.json b/node_modules/sparse-bitfield/package.json
new file mode 100644
index 0000000..d3f5068
--- /dev/null
+++ b/node_modules/sparse-bitfield/package.json
@@ -0,0 +1,55 @@
+{
+ "_from": "sparse-bitfield@^3.0.3",
+ "_id": "sparse-bitfield@3.0.3",
+ "_inBundle": false,
+ "_integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+ "_location": "/sparse-bitfield",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "sparse-bitfield@^3.0.3",
+ "name": "sparse-bitfield",
+ "escapedName": "sparse-bitfield",
+ "rawSpec": "^3.0.3",
+ "saveSpec": null,
+ "fetchSpec": "^3.0.3"
+ },
+ "_requiredBy": [
+ "/saslprep"
+ ],
+ "_resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+ "_shasum": "ff4ae6e68656056ba4b3e792ab3334d38273ca11",
+ "_spec": "sparse-bitfield@^3.0.3",
+ "_where": "D:\\Gitterra\\om\\node_modules\\saslprep",
+ "author": {
+ "name": "Mathias Buus",
+ "url": "@mafintosh"
+ },
+ "bugs": {
+ "url": "https://github.com/mafintosh/sparse-bitfield/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "memory-pager": "^1.0.2"
+ },
+ "deprecated": false,
+ "description": "Bitfield that allocates a series of small buffers to support sparse bits without allocating a massive buffer",
+ "devDependencies": {
+ "buffer-alloc": "^1.1.0",
+ "standard": "^9.0.0",
+ "tape": "^4.6.3"
+ },
+ "homepage": "https://github.com/mafintosh/sparse-bitfield",
+ "license": "MIT",
+ "main": "index.js",
+ "name": "sparse-bitfield",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/mafintosh/sparse-bitfield.git"
+ },
+ "scripts": {
+ "test": "standard && tape test.js"
+ },
+ "version": "3.0.3"
+}
diff --git a/node_modules/sparse-bitfield/test.js b/node_modules/sparse-bitfield/test.js
new file mode 100644
index 0000000..ae42ef4
--- /dev/null
+++ b/node_modules/sparse-bitfield/test.js
@@ -0,0 +1,79 @@
+var alloc = require('buffer-alloc')
+var tape = require('tape')
+var bitfield = require('./')
+
+tape('set and get', function (t) {
+ var bits = bitfield()
+
+ t.same(bits.get(0), false, 'first bit is false')
+ bits.set(0, true)
+ t.same(bits.get(0), true, 'first bit is true')
+ t.same(bits.get(1), false, 'second bit is false')
+ bits.set(0, false)
+ t.same(bits.get(0), false, 'first bit is reset')
+ t.end()
+})
+
+tape('set large and get', function (t) {
+ var bits = bitfield()
+
+ t.same(bits.get(9999999999999), false, 'large bit is false')
+ bits.set(9999999999999, true)
+ t.same(bits.get(9999999999999), true, 'large bit is true')
+ t.same(bits.get(9999999999999 + 1), false, 'large bit + 1 is false')
+ bits.set(9999999999999, false)
+ t.same(bits.get(9999999999999), false, 'large bit is reset')
+ t.end()
+})
+
+tape('get and set buffer', function (t) {
+ var bits = bitfield({trackUpdates: true})
+
+ t.same(bits.pages.get(0, true), undefined)
+ t.same(bits.pages.get(Math.floor(9999999999999 / 8 / 1024), true), undefined)
+ bits.set(9999999999999, true)
+
+ var bits2 = bitfield()
+ var upd = bits.pages.lastUpdate()
+ bits2.pages.set(Math.floor(upd.offset / 1024), upd.buffer)
+ t.same(bits2.get(9999999999999), true, 'bit is set')
+ t.end()
+})
+
+tape('toBuffer', function (t) {
+ var bits = bitfield()
+
+ t.same(bits.toBuffer(), alloc(0))
+
+ bits.set(0, true)
+
+ t.same(bits.toBuffer(), bits.pages.get(0).buffer)
+
+ bits.set(9000, true)
+
+ t.same(bits.toBuffer(), Buffer.concat([bits.pages.get(0).buffer, bits.pages.get(1).buffer]))
+ t.end()
+})
+
+tape('pass in buffer', function (t) {
+ var bits = bitfield()
+
+ bits.set(0, true)
+ bits.set(9000, true)
+
+ var clone = bitfield(bits.toBuffer())
+
+ t.same(clone.get(0), true)
+ t.same(clone.get(9000), true)
+ t.end()
+})
+
+tape('set small buffer', function (t) {
+ var buf = alloc(1)
+ buf[0] = 255
+ var bits = bitfield(buf)
+
+ t.same(bits.get(0), true)
+ t.same(bits.pages.get(0).buffer.length, bits.pageSize)
+ t.end()
+})
diff --git a/package-lock.json b/package-lock.json
index e9d8498..c703299 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -83,6 +83,14 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
+ "async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
+ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -96,6 +104,11 @@
"safe-buffer": "5.1.2"
}
},
+ "bluebird": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
+ "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
+ },
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
@@ -122,6 +135,11 @@
"concat-map": "0.0.1"
}
},
+ "bson": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.0.tgz",
+ "integrity": "sha512-9Aeai9TacfNtWXOYarkFJRW2CWo+dRon+fuLZYJmvLV3+MiUp0bEI6IAZfXEIg7/Pl/7IWlLaDnhzTsD81etQA=="
+ },
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -511,7 +529,7 @@
},
"finalhandler": {
"version": "1.1.1",
- "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
@@ -694,6 +712,11 @@
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
},
+ "kareem": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz",
+ "integrity": "sha512-6hHxsp9e6zQU8nXsP+02HGWXwTkOEw6IROhF2ZA28cYbUk4eJ6QbtZvdqZOdD9YPKghG3apk5eOCvs+tLl3lRg=="
+ },
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -713,6 +736,12 @@
"resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
+ "memory-pager": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+ "optional": true
+ },
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -767,6 +796,50 @@
"minimist": "0.0.8"
}
},
+ "mongodb": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.10.tgz",
+ "integrity": "sha512-Uml42GeFxhTGQVml1XQ4cD0o/rp7J2ROy0fdYUcVitoE7vFqEhKH4TYVqRDpQr/bXtCJVxJdNQC1ntRxNREkPQ==",
+ "requires": {
+ "mongodb-core": "3.1.9",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "mongodb-core": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.9.tgz",
+ "integrity": "sha512-MJpciDABXMchrZphh3vMcqu8hkNf/Mi+Gk6btOimVg1XMxLXh87j6FAvRm+KmwD1A9fpu3qRQYcbQe4egj23og==",
+ "requires": {
+ "bson": "^1.1.0",
+ "require_optional": "^1.0.1",
+ "safe-buffer": "^5.1.2",
+ "saslprep": "^1.0.0"
+ }
+ },
+ "mongoose": {
+ "version": "5.4.3",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.4.3.tgz",
+ "integrity": "sha512-zhfypQm63CPYHi43O/3lTcEPVucY3h6CqONbzreMKZIEXtwgkB9HCm4wn441Z9/CpM94G6/NA3wcaRb0N2kPYQ==",
+ "requires": {
+ "async": "2.6.1",
+ "bson": "~1.1.0",
+ "kareem": "2.3.0",
+ "mongodb": "3.1.10",
+ "mongodb-core": "3.1.9",
+ "mongoose-legacy-pluralize": "1.0.2",
+ "mpath": "0.5.1",
+ "mquery": "3.2.0",
+ "ms": "2.0.0",
+ "regexp-clone": "0.0.1",
+ "safe-buffer": "5.1.2",
+ "sliced": "1.0.1"
+ }
+ },
+ "mongoose-legacy-pluralize": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
+ "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ=="
+ },
"morgan": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
@@ -779,6 +852,33 @@
"on-headers": "~1.0.1"
}
},
+ "mpath": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.5.1.tgz",
+ "integrity": "sha512-H8OVQ+QEz82sch4wbODFOz+3YQ61FYz/z3eJ5pIdbMEaUzDqA268Wd+Vt4Paw9TJfvDgVKaayC0gBzMIw2jhsg=="
+ },
+ "mquery": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.0.tgz",
+ "integrity": "sha512-qPJcdK/yqcbQiKoemAt62Y0BAc0fTEKo1IThodBD+O5meQRJT/2HSe5QpBNwaa4CjskoGrYWsEyjkqgiE0qjhg==",
+ "requires": {
+ "bluebird": "3.5.1",
+ "debug": "3.1.0",
+ "regexp-clone": "0.0.1",
+ "safe-buffer": "5.1.2",
+ "sliced": "1.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -936,6 +1036,11 @@
"unpipe": "1.0.0"
}
},
+ "regexp-clone": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz",
+ "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk="
+ },
"regexpp": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
@@ -950,6 +1055,22 @@
"resolve-from": "^1.0.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.1.0"
+ },
+ "dependencies": {
+ "resolve-from": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
+ "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
+ }
+ }
+ },
"resolve-from": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
@@ -998,6 +1119,15 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
+ "saslprep": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.2.tgz",
+ "integrity": "sha512-4cDsYuAjXssUSjxHKRe4DTZC0agDwsCqcMqtJAQPzC74nJ7LfAJflAtC1Zed5hMzEQKj82d3tuzqdGNRsLJ4Gw==",
+ "optional": true,
+ "requires": {
+ "sparse-bitfield": "^3.0.3"
+ }
+ },
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
@@ -1065,6 +1195,20 @@
"is-fullwidth-code-point": "^2.0.0"
}
},
+ "sliced": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
+ "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
+ },
+ "sparse-bitfield": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+ "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+ "optional": true,
+ "requires": {
+ "memory-pager": "^1.0.2"
+ }
+ },
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
diff --git a/package.json b/package.json
index 50cafcc..c2a9eeb 100644
--- a/package.json
+++ b/package.json
@@ -18,9 +18,12 @@
],
"author": "Barbalan, Gruenzinger, Mandutz, Pham, Roemmelt",
"dependencies": {
+ "body-parser": "^1.18.3",
"eslint": "^5.9.0",
"express": "^4.16.4",
"express-session": "^1.15.6",
+ "mongodb": "^3.1.10",
+ "mongoose": "^5.4.3",
"morgan": "^1.9.1",
"vue": "^2.5.17",
"vue-router": "^3.0.2"
diff --git a/public/createMessage.js b/public/createMessage.js
index 4dfbefc..8911371 100644
--- a/public/createMessage.js
+++ b/public/createMessage.js
@@ -32,4 +32,8 @@ const CreateMsgRouter = {
},
methods: {
},
+ mounted: function() {
+ if ($(this).bootstrapMaterialDesign)
+ $(this).bootstrapMaterialDesign();
+ },
};
diff --git a/public/home.js b/public/home.js
index 71fe34a..1bf5546 100644
--- a/public/home.js
+++ b/public/home.js
@@ -14,7 +14,7 @@ const HomeRouter = {
};
},
methods: {
- refresh_messages: function () {
+ /*refresh_messages: function () {
_messages.push({ //////// alt
id: 42,
subject: "xxx",
@@ -22,23 +22,41 @@ const HomeRouter = {
user: "nobody",
tags: "foo"
});
- },
+ },*/
+ list_messages: function () {
+ console.log("egal");
+ $.get('api/ids/').done(function (jd) {
+ // _messagelist = jd;
+ _messagelist.splice(0, _messagelist.length);
+ _messagelist.push.apply(_messagelist, jd);
+ console.log("egal2");
+ //$('#xxx').text(jd[0]);
+ for (var e in jd) {
+ if (!_messages[jd[e]]) {
+ get_insert_message(jd[e]);
+ }
+ }
+ }).fail(function (e, f, g) {
+ console.log("egal3: " + e + f + g);
+ });
+ }
},
- /*mounted: function() {
- this.refresh_messages();
- }*/
+ mounted: function() {
+ this.list_messages();
+ /*this.refresh_messages();*/
+ }
};
-function get_insert_message(id) {
- $.get(id + ".json").done(function (msg) {
+function get_insert_message(msg_id) {
+ $.get(api/msg/{msg_id}).done(function (msg) {
// _messages[id] = msg;
Vue.set(_messages, id, msg);
}).fail(function (e, f, g) {
console.log("cannot load " + id + ".json: " + e + f + g);
})
}
-$(document).ready(function () {
+/*$(document).ready(function () {
console.log("egal");
$.get('list.json').done(function (jd) {
// _messagelist = jd;
@@ -54,4 +72,4 @@ $(document).ready(function () {
}).fail(function (e, f, g) {
console.log("egal3: " + e + f + g);
});
-});
+});*/
diff --git a/public/index.html b/public/index.html
index 71280ff..21560da 100644
--- a/public/index.html
+++ b/public/index.html
@@ -79,7 +79,29 @@
bookmark
person
+
+
+
+
diff --git a/server.js b/server.js
index afedc30..143c129 100644
--- a/server.js
+++ b/server.js
@@ -20,6 +20,7 @@ const fs = require ('fs'),
//serveFavicon = require ('serve-favicon'),
bodyParser = require ('body-parser');
//MongoStore = require ('connect-mongo')(session); // uss mongodb as session storage
+const Message = require('./message.model.js');
var app = express();
@@ -73,9 +74,48 @@ app.use (bodyParser.urlencoded({extended: true}));
//var api_routes = express.Router(); // express app-object routing
//app.use ('/api', api_routes);
+app.use (function (req,res,done) {
+ console.log (req.url);
+ done();
+});
+
+//global.__basedir = __dirname;
+
// Static Files
app.use (express.static(__dirname + '/public')); // Allow server access to 'public' folder
+//app.use(express.static('resources'));
+
+// Configuring the database
+const dbConfig = require('./mongodb.config.js');
+const mongoose = require('mongoose');
+
+mongoose.Promise = global.Promise;
+
+// Connecting to the database
+mongoose.connect(dbConfig.url)
+.then(() => {
+ console.log("Successfully connected to MongoDB.");
+}).catch(err => {
+ console.log('Could not connect to MongoDB.');
+ process.exit();
+});
+
+//require('./app/routes/message.route.js')(app);
+
+app.get ("/api/test/:id", function (req, res) {
+ console.log ("id: " + req.params.id + " message " + req.query.msg); //bei POST: req.body.msg
+ Message.find ({_id: req.params.id}) .exec (function (err, results) {
+ if (err) {
+ console.log (err);
+ res .status(404) .json (err);
+ } else {
+ console.log(JSON.stringify(results));
+ res.json(results);
+ }
+ });
+});
+
// Other stuff is NOT authorized unless logged in
//app.use (authorize.genCheckAuthorized ('user'));