mongoose 7.3.1 → 7.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/connection.js CHANGED
@@ -703,8 +703,7 @@ Connection.prototype.onOpen = function() {
703
703
  * @param {Number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation.
704
704
  * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
705
705
  * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/MongoClientOptions.html#promiseLibrary).
706
- * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
707
- * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
706
+ * @param {Number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. `socketTimeoutMS` defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
708
707
  * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
709
708
  * @param {Boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection.
710
709
  * @returns {Promise<Connection>}
@@ -84,25 +84,33 @@ function QueryCursor(query, options) {
84
84
  this.options._populateBatchSize = Math.min(this.options.batchSize, 5000);
85
85
  }
86
86
  Object.assign(this.options, query._optionsForExec());
87
- model.collection.find(query._conditions, this.options, (err, cursor) => {
88
- if (err != null) {
89
- this._markError(err);
90
- this.listeners('error').length > 0 && this.emit('error', this._error);
91
- return;
92
- }
93
-
94
- if (this._error) {
95
- cursor.close(function() {});
96
- this.listeners('error').length > 0 && this.emit('error', this._error);
97
- }
98
- this.cursor = cursor;
99
- this.emit('cursor', cursor);
100
- });
87
+ if (model.collection._shouldBufferCommands() && model.collection.buffer) {
88
+ model.collection.queue.push([
89
+ () => _getRawCursor(query, this)
90
+ ]);
91
+ } else {
92
+ _getRawCursor(query, this);
93
+ }
101
94
  });
102
95
  }
103
96
 
104
97
  util.inherits(QueryCursor, Readable);
105
98
 
99
+ /*!
100
+ * ignore
101
+ */
102
+
103
+ function _getRawCursor(query, queryCursor) {
104
+ try {
105
+ const cursor = query.model.collection.find(query._conditions, queryCursor.options);
106
+ queryCursor.cursor = cursor;
107
+ queryCursor.emit('cursor', cursor);
108
+ } catch (err) {
109
+ queryCursor._markError(err);
110
+ queryCursor.listeners('error').length > 0 && queryCursor.emit('error', queryCursor._error);
111
+ }
112
+ }
113
+
106
114
  /**
107
115
  * Necessary to satisfy the Readable API
108
116
  * @method _read
package/lib/document.js CHANGED
@@ -741,6 +741,10 @@ function init(self, obj, doc, opts, prefix) {
741
741
 
742
742
  function _init(index) {
743
743
  i = keys[index];
744
+ // avoid prototype pollution
745
+ if (i === '__proto__' || i === 'constructor') {
746
+ return;
747
+ }
744
748
  path = prefix + i;
745
749
  schemaType = docSchema.path(path);
746
750
 
@@ -2684,7 +2688,7 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2684
2688
 
2685
2689
  // Optimization: if primitive path with no validators, or array of primitives
2686
2690
  // with no validators, skip validating this path entirely.
2687
- if (!_pathType.caster && _pathType.validators.length === 0) {
2691
+ if (!_pathType.caster && _pathType.validators.length === 0 && !_pathType.$parentSchemaDocArray) {
2688
2692
  paths.delete(path);
2689
2693
  } else if (_pathType.$isMongooseArray &&
2690
2694
  !_pathType.$isMongooseDocumentArray && // Skip document arrays...
@@ -2771,7 +2775,19 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2771
2775
 
2772
2776
  for (const path of paths) {
2773
2777
  const _pathType = doc.$__schema.path(path);
2774
- if (!_pathType || !_pathType.$isSchemaMap) {
2778
+
2779
+ if (!_pathType) {
2780
+ continue;
2781
+ }
2782
+
2783
+ // If underneath a document array, may need to re-validate the parent
2784
+ // array re: gh-6818. Do this _after_ adding subpaths, because
2785
+ // we don't want to add every array subpath.
2786
+ if (_pathType.$parentSchemaDocArray && typeof _pathType.$parentSchemaDocArray.path === 'string') {
2787
+ paths.add(_pathType.$parentSchemaDocArray.path);
2788
+ }
2789
+
2790
+ if (!_pathType.$isSchemaMap) {
2775
2791
  continue;
2776
2792
  }
2777
2793
 
@@ -3318,14 +3334,7 @@ Document.prototype.$__reset = function reset() {
3318
3334
  if (this.isModified(fullPathWithIndexes) || isParentInit(fullPathWithIndexes)) {
3319
3335
  subdoc.$__reset();
3320
3336
  if (subdoc.$isDocumentArrayElement) {
3321
- if (!resetArrays.has(subdoc.parentArray())) {
3322
- const array = subdoc.parentArray();
3323
- this.$__.activePaths.clearPath(fullPathWithIndexes.replace(/\.\d+$/, '').slice(-subdoc.$basePath - 1));
3324
- array[arrayAtomicsBackupSymbol] = array[arrayAtomicsSymbol];
3325
- array[arrayAtomicsSymbol] = {};
3326
-
3327
- resetArrays.add(array);
3328
- }
3337
+ resetArrays.add(subdoc.parentArray());
3329
3338
  } else {
3330
3339
  if (subdoc.$parent() === this) {
3331
3340
  this.$__.activePaths.clearPath(subdoc.$basePath);
@@ -3338,6 +3347,12 @@ Document.prototype.$__reset = function reset() {
3338
3347
  }
3339
3348
  }
3340
3349
 
3350
+ for (const array of resetArrays) {
3351
+ this.$__.activePaths.clearPath(array.$path());
3352
+ array[arrayAtomicsBackupSymbol] = array[arrayAtomicsSymbol];
3353
+ array[arrayAtomicsSymbol] = {};
3354
+ }
3355
+
3341
3356
  function isParentInit(path) {
3342
3357
  path = path.indexOf('.') === -1 ? [path] : path.split('.');
3343
3358
  let cur = '';
@@ -64,7 +64,7 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
64
64
  schema.options.refPath == null) {
65
65
  continue;
66
66
  }
67
- const isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
67
+ const isUnderneathDocArray = schema && schema.$parentSchemaDocArray;
68
68
  if (isUnderneathDocArray && get(options, 'options.sort') != null) {
69
69
  return new MongooseError('Cannot populate with `sort` on path ' + options.path +
70
70
  ' because it is a subproperty of a document array');
@@ -540,6 +540,7 @@ function addModelNamesToMap(model, map, available, modelNames, options, data, re
540
540
  // Used internally for checking what model was used to populate this
541
541
  // path.
542
542
  options[populateModelSymbol] = Model;
543
+ currentOptions[populateModelSymbol] = Model;
543
544
  available[modelName] = {
544
545
  model: Model,
545
546
  options: currentOptions,
@@ -101,8 +101,8 @@ module.exports = function getSchemaTypes(model, schema, doc, path) {
101
101
  nestedPath.concat(parts.slice(0, p))
102
102
  );
103
103
  if (ret) {
104
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
105
- !foundschema.schema.$isSingleNested;
104
+ ret.$parentSchemaDocArray = ret.$parentSchemaDocArray ||
105
+ (foundschema.schema.$isSingleNested ? null : foundschema);
106
106
  }
107
107
  return ret;
108
108
  }
@@ -117,10 +117,10 @@ module.exports = function getSchemaTypes(model, schema, doc, path) {
117
117
  nestedPath.concat(parts.slice(0, p))
118
118
  );
119
119
  if (_ret != null) {
120
- _ret.$isUnderneathDocArray = _ret.$isUnderneathDocArray ||
121
- !foundschema.schema.$isSingleNested;
122
- if (_ret.$isUnderneathDocArray) {
123
- ret.$isUnderneathDocArray = true;
120
+ _ret.$parentSchemaDocArray = _ret.$parentSchemaDocArray ||
121
+ (foundschema.schema.$isSingleNested ? null : foundschema);
122
+ if (_ret.$parentSchemaDocArray) {
123
+ ret.$parentSchemaDocArray = _ret.$parentSchemaDocArray;
124
124
  }
125
125
  ret.push(_ret);
126
126
  }
@@ -135,8 +135,8 @@ module.exports = function getSchemaTypes(model, schema, doc, path) {
135
135
  );
136
136
 
137
137
  if (ret) {
138
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
139
- !foundschema.schema.$isSingleNested;
138
+ ret.$parentSchemaDocArray = ret.$parentSchemaDocArray ||
139
+ (foundschema.schema.$isSingleNested ? null : foundschema);
140
140
  }
141
141
  return ret;
142
142
  }
@@ -188,10 +188,6 @@ module.exports = function getSchemaTypes(model, schema, doc, path) {
188
188
  nestedPath.concat(parts.slice(0, p))
189
189
  );
190
190
 
191
- if (ret) {
192
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
193
- !model.schema.$isSingleNested;
194
- }
195
191
  return ret;
196
192
  }
197
193
  }
@@ -212,8 +208,8 @@ module.exports = function getSchemaTypes(model, schema, doc, path) {
212
208
  );
213
209
 
214
210
  if (ret != null) {
215
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
216
- !schema.$isSingleNested;
211
+ ret.$parentSchemaDocArray = ret.$parentSchemaDocArray ||
212
+ (schema.$isSingleNested ? null : schema);
217
213
  return ret;
218
214
  }
219
215
  }
package/lib/index.js CHANGED
@@ -329,8 +329,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
329
329
  * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/MongoClientOptions.html#promiseLibrary).
330
330
  * @param {Number} [options.maxPoolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
331
331
  * @param {Number} [options.minPoolSize=1] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
332
- * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
333
- * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
332
+ * @param {Number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. Defaults to 0, which means Node.js will not time out the socket due to inactivity. A socket may be inactive because of either no activity or a long-running operation. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
334
333
  * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
335
334
  * @return {Connection} the created Connection object. Connections are not thenable, so you can't do `await mongoose.createConnection()`. To await use `mongoose.createConnection(uri).asPromise()` instead.
336
335
  * @api public
@@ -385,8 +384,7 @@ Mongoose.prototype.createConnection = function(uri, options) {
385
384
  * @param {Number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation.
386
385
  * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
387
386
  * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/MongoClientOptions.html#promiseLibrary).
388
- * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
389
- * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
387
+ * @param {Number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. `socketTimeoutMS` defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
390
388
  * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
391
389
  * @param {Boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection.
392
390
  * @param {Function} [callback]
package/lib/model.js CHANGED
@@ -1046,16 +1046,18 @@ Model.prototype.$__deleteOne = function $__deleteOne(options, cb) {
1046
1046
  options.session = session;
1047
1047
  }
1048
1048
 
1049
- this[modelCollectionSymbol].deleteOne(where, options, err => {
1050
- if (!err) {
1049
+ this[modelCollectionSymbol].deleteOne(where, options).then(
1050
+ () => {
1051
1051
  this.$__.isDeleted = true;
1052
1052
  this.$emit('deleteOne', this);
1053
1053
  this.constructor.emit('deleteOne', this);
1054
1054
  return cb(null, this);
1055
+ },
1056
+ err => {
1057
+ this.$__.isDeleted = false;
1058
+ cb(err);
1055
1059
  }
1056
- this.$__.isDeleted = false;
1057
- cb(err);
1058
- });
1060
+ );
1059
1061
  };
1060
1062
 
1061
1063
  /**
@@ -3232,16 +3234,19 @@ Model.$__insertMany = function(arr, options, callback) {
3232
3234
  }
3233
3235
 
3234
3236
  // `insertedDocs` is a Mongoose-specific property
3237
+ const hasWriteErrors = error && error.writeErrors;
3235
3238
  const erroredIndexes = new Set((error && error.writeErrors || []).map(err => err.index));
3236
3239
 
3237
- for (let i = 0; i < error.writeErrors.length; ++i) {
3238
- const originalIndex = validDocIndexToOriginalIndex.get(error.writeErrors[i].index);
3239
- error.writeErrors[i] = {
3240
- ...error.writeErrors[i],
3241
- index: originalIndex
3242
- };
3243
- if (!ordered) {
3244
- results[originalIndex] = error.writeErrors[i];
3240
+ if (error.writeErrors != null) {
3241
+ for (let i = 0; i < error.writeErrors.length; ++i) {
3242
+ const originalIndex = validDocIndexToOriginalIndex.get(error.writeErrors[i].index);
3243
+ error.writeErrors[i] = {
3244
+ ...error.writeErrors[i],
3245
+ index: originalIndex
3246
+ };
3247
+ if (!ordered) {
3248
+ results[originalIndex] = error.writeErrors[i];
3249
+ }
3245
3250
  }
3246
3251
  }
3247
3252
 
@@ -3258,7 +3263,7 @@ Model.$__insertMany = function(arr, options, callback) {
3258
3263
  let firstErroredIndex = -1;
3259
3264
  error.insertedDocs = docAttributes.
3260
3265
  filter((doc, i) => {
3261
- const isErrored = erroredIndexes.has(i);
3266
+ const isErrored = !hasWriteErrors || erroredIndexes.has(i);
3262
3267
 
3263
3268
  if (ordered) {
3264
3269
  if (firstErroredIndex > -1) {
@@ -4262,7 +4267,7 @@ const excludeIdReg = /\s?-_id\s?/;
4262
4267
  const excludeIdRegGlobal = /\s?-_id\s?/g;
4263
4268
 
4264
4269
  function populate(model, docs, options, callback) {
4265
- const populateOptions = { ...options };
4270
+ const populateOptions = options;
4266
4271
  if (options.strictPopulate == null) {
4267
4272
  if (options._localModel != null && options._localModel.schema._userProvidedOptions.strictPopulate != null) {
4268
4273
  populateOptions.strictPopulate = options._localModel.schema._userProvidedOptions.strictPopulate;
package/lib/query.js CHANGED
@@ -3281,6 +3281,12 @@ Query.prototype.findOneAndUpdate = function(filter, doc, options) {
3281
3281
  */
3282
3282
 
3283
3283
  Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
3284
+ // For backwards compability with Mongoose 6 re: #13550
3285
+ if (this._mongooseOptions.overwrite) {
3286
+ this.op = 'findOneAndReplace';
3287
+ return this._findOneAndReplace();
3288
+ }
3289
+
3284
3290
  this._castConditions();
3285
3291
 
3286
3292
  _castArrayFilters(this);
@@ -4354,7 +4360,10 @@ Query.prototype.exec = async function exec(op) {
4354
4360
  }
4355
4361
 
4356
4362
  if (this.op == null) {
4357
- throw new Error('Query must have `op` before executing');
4363
+ throw new MongooseError('Query must have `op` before executing');
4364
+ }
4365
+ if (this.model == null) {
4366
+ throw new MongooseError('Query must have an associated model before executing');
4358
4367
  }
4359
4368
  this._validateOp();
4360
4369
 
@@ -4434,6 +4443,13 @@ function _executePostExecHooks(query) {
4434
4443
  */
4435
4444
 
4436
4445
  function _executePostHooks(query, res, error, op) {
4446
+ if (query._queryMiddleware == null) {
4447
+ if (error != null) {
4448
+ throw error;
4449
+ }
4450
+ return res;
4451
+ }
4452
+
4437
4453
  return new Promise((resolve, reject) => {
4438
4454
  const opts = error ? { error } : {};
4439
4455
 
@@ -4467,6 +4483,10 @@ function _executePreExecHooks(query) {
4467
4483
  */
4468
4484
 
4469
4485
  function _executePreHooks(query, op) {
4486
+ if (query._queryMiddleware == null) {
4487
+ return;
4488
+ }
4489
+
4470
4490
  return new Promise((resolve, reject) => {
4471
4491
  query._queryMiddleware.execPre(op || query.op, query, [], (error) => {
4472
4492
  if (error != null) {
package/lib/schema.js CHANGED
@@ -1131,22 +1131,22 @@ Schema.prototype.path = function(path, obj) {
1131
1131
  for (const key of Object.keys(schemaType.schema.paths)) {
1132
1132
  const _schemaType = schemaType.schema.paths[key];
1133
1133
  this.subpaths[path + '.' + key] = _schemaType;
1134
- if (typeof _schemaType === 'object' && _schemaType != null) {
1135
- _schemaType.$isUnderneathDocArray = true;
1134
+ if (typeof _schemaType === 'object' && _schemaType != null && _schemaType.$parentSchemaDocArray == null) {
1135
+ _schemaType.$parentSchemaDocArray = schemaType;
1136
1136
  }
1137
1137
  }
1138
1138
  for (const key of Object.keys(schemaType.schema.subpaths)) {
1139
1139
  const _schemaType = schemaType.schema.subpaths[key];
1140
1140
  this.subpaths[path + '.' + key] = _schemaType;
1141
- if (typeof _schemaType === 'object' && _schemaType != null) {
1142
- _schemaType.$isUnderneathDocArray = true;
1141
+ if (typeof _schemaType === 'object' && _schemaType != null && _schemaType.$parentSchemaDocArray == null) {
1142
+ _schemaType.$parentSchemaDocArray = schemaType;
1143
1143
  }
1144
1144
  }
1145
1145
  for (const key of Object.keys(schemaType.schema.singleNestedPaths)) {
1146
1146
  const _schemaType = schemaType.schema.singleNestedPaths[key];
1147
1147
  this.subpaths[path + '.' + key] = _schemaType;
1148
- if (typeof _schemaType === 'object' && _schemaType != null) {
1149
- _schemaType.$isUnderneathDocArray = true;
1148
+ if (typeof _schemaType === 'object' && _schemaType != null && _schemaType.$parentSchemaDocArray == null) {
1149
+ _schemaType.$parentSchemaDocArray = schemaType;
1150
1150
  }
1151
1151
  }
1152
1152
  }
@@ -2556,16 +2556,16 @@ Schema.prototype._getSchema = function(path) {
2556
2556
  // comments.$.comments.$.title
2557
2557
  ret = search(parts.slice(p + 1), foundschema.schema);
2558
2558
  if (ret) {
2559
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
2560
- !foundschema.schema.$isSingleNested;
2559
+ ret.$parentSchemaDocArray = ret.$parentSchemaDocArray ||
2560
+ (foundschema.schema.$isSingleNested ? null : foundschema);
2561
2561
  }
2562
2562
  return ret;
2563
2563
  }
2564
2564
  // this is the last path of the selector
2565
2565
  ret = search(parts.slice(p), foundschema.schema);
2566
2566
  if (ret) {
2567
- ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
2568
- !foundschema.schema.$isSingleNested;
2567
+ ret.$parentSchemaDocArray = ret.$parentSchemaDocArray ||
2568
+ (foundschema.schema.$isSingleNested ? null : foundschema);
2569
2569
  }
2570
2570
  return ret;
2571
2571
  }
@@ -226,6 +226,9 @@ const methods = {
226
226
  if (populated && value !== null && value !== undefined) {
227
227
  // cast to the populated Models schema
228
228
  Model = populated.options[populateModelSymbol];
229
+ if (Model == null) {
230
+ throw new MongooseError('No populated model found for path `' + this[arrayPathSymbol] + '`. This is likely a bug in Mongoose, please report an issue on github.com/Automattic/mongoose.');
231
+ }
229
232
 
230
233
  // only objects are permitted so we can safely assume that
231
234
  // non-objects are to be interpreted as _id
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "7.3.1",
4
+ "version": "7.3.3",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -28,10 +28,10 @@
28
28
  "sift": "16.0.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@babel/core": "7.22.1",
32
- "@babel/preset-env": "7.22.4",
33
- "@typescript-eslint/eslint-plugin": "5.59.8",
34
- "@typescript-eslint/parser": "5.59.8",
31
+ "@babel/core": "7.22.5",
32
+ "@babel/preset-env": "7.22.5",
33
+ "@typescript-eslint/eslint-plugin": "5.61.0",
34
+ "@typescript-eslint/parser": "5.61.0",
35
35
  "acquit": "1.3.0",
36
36
  "acquit-ignore": "0.2.1",
37
37
  "acquit-require": "0.1.1",
@@ -44,31 +44,32 @@
44
44
  "buffer": "^5.6.0",
45
45
  "cheerio": "1.0.0-rc.12",
46
46
  "crypto-browserify": "3.12.0",
47
- "dotenv": "16.1.3",
47
+ "dotenv": "16.3.1",
48
48
  "dox": "1.0.0",
49
- "eslint": "8.41.0",
49
+ "eslint": "8.44.0",
50
50
  "eslint-plugin-markdown": "^3.0.0",
51
51
  "eslint-plugin-mocha-no-only": "1.1.1",
52
52
  "express": "^4.18.1",
53
+ "fs-extra": "~11.1.1",
53
54
  "highlight.js": "11.8.0",
54
55
  "lodash.isequal": "4.5.0",
55
56
  "lodash.isequalwith": "4.4.0",
57
+ "markdownlint-cli2": "^0.8.1",
56
58
  "marked": "4.3.0",
57
- "mkdirp": "^2.1.3",
59
+ "mkdirp": "^3.0.1",
58
60
  "mocha": "10.2.0",
59
61
  "moment": "2.x",
60
- "mongodb-memory-server": "8.12.2",
62
+ "mongodb-memory-server": "8.13.0",
61
63
  "ncp": "^2.0.0",
62
64
  "nyc": "15.1.0",
63
65
  "pug": "3.0.2",
64
66
  "q": "1.5.1",
65
- "sinon": "15.1.0",
67
+ "sinon": "15.2.0",
66
68
  "stream-browserify": "3.0.0",
67
69
  "tsd": "0.28.1",
68
- "typescript": "5.1.3",
70
+ "typescript": "5.1.6",
69
71
  "uuid": "9.0.0",
70
- "webpack": "5.85.0",
71
- "fs-extra": "~11.1.1"
72
+ "webpack": "5.88.1"
72
73
  },
73
74
  "directories": {
74
75
  "lib": "./lib/mongoose"
@@ -98,9 +99,9 @@
98
99
  "docs:prepare:publish:6x": "npm run docs:checkout:6x && npm run docs:merge:6x && npm run docs:clean:stable && env DOCS_DEPLOY=true npm run docs:generate && npm run docs:move:6x:tmp && npm run docs:checkout:gh-pages && npm run docs:copy:tmp:6x",
99
100
  "docs:check-links": "blc http://127.0.0.1:8089 -ro",
100
101
  "lint": "eslint .",
101
- "lint-js": "eslint . --ext .js",
102
+ "lint-js": "eslint . --ext .js --ext .cjs",
102
103
  "lint-ts": "eslint . --ext .ts",
103
- "lint-md": "eslint . --ext .md",
104
+ "lint-md": "markdownlint-cli2 \"**/*.md\"",
104
105
  "build-browser": "(rm ./dist/* || true) && node ./scripts/build-browser.js",
105
106
  "prepublishOnly": "npm run build-browser",
106
107
  "release": "git pull && git push origin master --tags && npm publish",
package/types/index.d.ts CHANGED
@@ -141,12 +141,16 @@ declare module 'mongoose' {
141
141
  > = IfAny<
142
142
  DocType,
143
143
  any,
144
- Document<unknown, TQueryHelpers, DocType> & MergeType<
145
- Require_id<DocType>,
146
- TOverrides extends Record<string, never> ?
147
- {} :
148
- IfAny<TOverrides, {}>
149
- >
144
+ TOverrides extends Record<string, never> ?
145
+ Document<unknown, TQueryHelpers, DocType> & Require_id<DocType> :
146
+ IfAny<
147
+ TOverrides,
148
+ Document<unknown, TQueryHelpers, DocType> & Require_id<DocType>,
149
+ Document<unknown, TQueryHelpers, DocType> & MergeType<
150
+ Require_id<DocType>,
151
+ TOverrides
152
+ >
153
+ >
150
154
  >;
151
155
  export type HydratedSingleSubdocument<DocType, TOverrides = {}> = Types.Subdocument<unknown> & Require_id<DocType> & TOverrides;
152
156
  export type HydratedArraySubdocument<DocType, TOverrides = {}> = Types.ArraySubdocument<unknown> & Require_id<DocType> & TOverrides;
@@ -197,7 +201,7 @@ declare module 'mongoose' {
197
201
 
198
202
  export type DiscriminatorSchema<DocType, M, TInstanceMethods, TQueryHelpers, TVirtuals, TStaticMethods, DisSchema> =
199
203
  DisSchema extends Schema<infer DisSchemaEDocType, infer DisSchemaM, infer DisSchemaInstanceMethods, infer DisSchemaQueryhelpers, infer DisSchemaVirtuals, infer DisSchemaStatics>
200
- ? Schema<Omit<DocType, keyof DisSchemaEDocType> & DisSchemaEDocType, DiscriminatorModel<DisSchemaM, M>, DisSchemaInstanceMethods | TInstanceMethods, DisSchemaQueryhelpers | TQueryHelpers, DisSchemaVirtuals | TVirtuals, DisSchemaStatics & TStaticMethods>
204
+ ? Schema<MergeType<DocType, DisSchemaEDocType>, DiscriminatorModel<DisSchemaM, M>, DisSchemaInstanceMethods | TInstanceMethods, DisSchemaQueryhelpers | TQueryHelpers, DisSchemaVirtuals | TVirtuals, DisSchemaStatics & TStaticMethods>
201
205
  : Schema<DocType, M, TInstanceMethods, TQueryHelpers, TVirtuals, TStaticMethods>;
202
206
 
203
207
  type QueryResultType<T> = T extends Query<infer ResultType, any> ? ResultType : never;
@@ -212,7 +216,7 @@ declare module 'mongoose' {
212
216
 
213
217
  export class Schema<
214
218
  EnforcedDocType = any,
215
- M = Model<EnforcedDocType, any, any, any>,
219
+ TModelType = Model<EnforcedDocType, any, any, any>,
216
220
  TInstanceMethods = {},
217
221
  TQueryHelpers = {},
218
222
  TVirtuals = {},
@@ -225,13 +229,13 @@ declare module 'mongoose' {
225
229
  ObtainDocumentType<any, EnforcedDocType, ResolveSchemaOptions<TSchemaOptions>>,
226
230
  ResolveSchemaOptions<TSchemaOptions>
227
231
  >,
228
- THydratedDocumentType = HydratedDocument<FlatRecord<DocType>, TVirtuals & TInstanceMethods>
232
+ THydratedDocumentType = HydratedDocument<DocType, TVirtuals & TInstanceMethods>
229
233
  >
230
234
  extends events.EventEmitter {
231
235
  /**
232
236
  * Create a new schema
233
237
  */
234
- constructor(definition?: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | DocType, options?: SchemaOptions<FlatRecord<DocType>, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals, THydratedDocumentType> | ResolveSchemaOptions<TSchemaOptions>);
238
+ constructor(definition?: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | DocType, options?: SchemaOptions<DocType, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals, THydratedDocumentType> | ResolveSchemaOptions<TSchemaOptions>);
235
239
 
236
240
  /** Adds key path / schema type pairs to this schema. */
237
241
  add(obj: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | Schema, prefix?: string): this;
@@ -311,7 +315,7 @@ declare module 'mongoose' {
311
315
  pathType(path: string): string;
312
316
 
313
317
  /** Registers a plugin for this schema. */
314
- plugin<PFunc extends PluginFunction<DocType, M, any, any, any, any>, POptions extends Parameters<PFunc>[1] = Parameters<PFunc>[1]>(fn: PFunc, opts?: POptions): this;
318
+ plugin<PFunc extends PluginFunction<DocType, TModelType, any, any, any, any>, POptions extends Parameters<PFunc>[1] = Parameters<PFunc>[1]>(fn: PFunc, opts?: POptions): this;
315
319
 
316
320
  /** Defines a post hook for the model. */
317
321
 
@@ -320,7 +324,7 @@ declare module 'mongoose' {
320
324
  post<T = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;
321
325
  post<T = THydratedDocumentType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;
322
326
  post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T, Array<any>>): this;
323
- post<T = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;
327
+ post<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption<T>): this;
324
328
 
325
329
  // this = never since it never happens
326
330
  post<T = never>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: false }, fn: PostMiddlewareFunction<never, never>): this;
@@ -358,14 +362,14 @@ declare module 'mongoose' {
358
362
  // method aggregate and insertMany with PostMiddlewareFunction
359
363
  post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, fn: PostMiddlewareFunction<T, Array<AggregateExtract<T>>>): this;
360
364
  post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction<T, Array<AggregateExtract<T>>>): this;
361
- post<T = M>(method: 'insertMany' | RegExp, fn: PostMiddlewareFunction<T, T>): this;
362
- post<T = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction<T, T>): this;
365
+ post<T = TModelType>(method: 'insertMany' | RegExp, fn: PostMiddlewareFunction<T, T>): this;
366
+ post<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction<T, T>): this;
363
367
 
364
368
  // method aggregate and insertMany with ErrorHandlingMiddlewareFunction
365
369
  post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, fn: ErrorHandlingMiddlewareFunction<T, Array<any>>): this;
366
370
  post<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction<T, Array<any>>): this;
367
- post<T = M>(method: 'insertMany' | RegExp, fn: ErrorHandlingMiddlewareFunction<T>): this;
368
- post<T = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction<T>): this;
371
+ post<T = TModelType>(method: 'insertMany' | RegExp, fn: ErrorHandlingMiddlewareFunction<T>): this;
372
+ post<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction<T>): this;
369
373
 
370
374
  /** Defines a pre hook for the model. */
371
375
  // this = never since it never happens
@@ -390,8 +394,8 @@ declare module 'mongoose' {
390
394
  pre<T extends Aggregate<any>>(method: 'aggregate' | RegExp, fn: PreMiddlewareFunction<T>): this;
391
395
  pre<T extends Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPreOptions, fn: PreMiddlewareFunction<T>): this;
392
396
  /* method insertMany */
393
- pre<T = M>(method: 'insertMany' | RegExp, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;
394
- pre<T = M>(method: 'insertMany' | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;
397
+ pre<T = TModelType>(method: 'insertMany' | RegExp, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;
398
+ pre<T = TModelType>(method: 'insertMany' | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err?: CallbackError) => void, docs: any | Array<any>) => void | Promise<void>): this;
395
399
 
396
400
  /** Object of currently defined query helpers on this schema. */
397
401
  query: TQueryHelpers;
@@ -413,11 +417,12 @@ declare module 'mongoose' {
413
417
 
414
418
  /** Adds static "class" methods to Models compiled from this schema. */
415
419
  static<K extends keyof TStaticMethods>(name: K, fn: TStaticMethods[K]): this;
416
- static(obj: { [F in keyof TStaticMethods]: TStaticMethods[F] } & { [name: string]: (this: M, ...args: any[]) => any }): this;
417
- static(name: string, fn: (this: M, ...args: any[]) => any): this;
420
+ static(obj: { [F in keyof TStaticMethods]: TStaticMethods[F] } & { [name: string]: (this: TModelType, ...args: any[]) => any }): this;
421
+ static(name: string, fn: (this: TModelType, ...args: any[]) => any): this;
418
422
 
419
423
  /** Object of currently defined statics on this schema. */
420
- statics: { [F in keyof TStaticMethods]: TStaticMethods[F] } & { [name: string]: (this: M, ...args: any[]) => any };
424
+ statics: { [F in keyof TStaticMethods]: TStaticMethods[F] } &
425
+ { [name: string]: (this: TModelType, ...args: any[]) => unknown };
421
426
 
422
427
  /** Creates a virtual type with the given name. */
423
428
  virtual<T = HydratedDocument<DocType, TVirtuals & TInstanceMethods, TQueryHelpers>>(
@@ -430,6 +435,8 @@ declare module 'mongoose' {
430
435
 
431
436
  /** Returns the virtual type with the given `name`. */
432
437
  virtualpath<T = THydratedDocumentType>(name: string): VirtualType<T> | null;
438
+
439
+ static ObjectId: typeof Schema.Types.ObjectId;
433
440
  }
434
441
 
435
442
  export type NumberSchemaDefinition = typeof Number | 'number' | 'Number' | typeof Schema.Types.Number;