mongoose 8.10.0 → 8.10.2

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
@@ -23,6 +23,7 @@ const CreateCollectionsError = require('./error/createCollectionsError');
23
23
  const castBulkWrite = require('./helpers/model/castBulkWrite');
24
24
  const { modelSymbol } = require('./helpers/symbols');
25
25
  const isPromise = require('./helpers/isPromise');
26
+ const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');
26
27
 
27
28
  const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol;
28
29
  const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments;
@@ -559,7 +560,9 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
559
560
  'bulkWrite'
560
561
  );
561
562
  }
562
- return getDefaultBulkwriteResult();
563
+ const BulkWriteResult = this.base.driver.get().BulkWriteResult;
564
+ const res = new BulkWriteResult(getDefaultBulkwriteResult(), false);
565
+ return decorateBulkWriteResult(res, validationErrors, results);
563
566
  }
564
567
 
565
568
  let error;
@@ -567,16 +570,17 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
567
570
  then(res => ([res, null])).
568
571
  catch(err => ([null, err]));
569
572
 
573
+ for (let i = 0; i < validOpIndexes.length; ++i) {
574
+ results[validOpIndexes[i]] = null;
575
+ }
570
576
  if (error) {
571
577
  if (validationErrors.length > 0) {
578
+ decorateBulkWriteResult(error, validationErrors, results);
572
579
  error.mongoose = error.mongoose || {};
573
580
  error.mongoose.validationErrors = validationErrors;
574
581
  }
575
582
  }
576
583
 
577
- for (let i = 0; i < validOpIndexes.length; ++i) {
578
- results[validOpIndexes[i]] = null;
579
- }
580
584
  if (validationErrors.length > 0) {
581
585
  if (options.throwOnValidationError) {
582
586
  throw new MongooseBulkWriteError(
@@ -586,9 +590,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
586
590
  'bulkWrite'
587
591
  );
588
592
  } else {
589
- res.mongoose = res.mongoose || {};
590
- res.mongoose.validationErrors = validationErrors;
591
- res.mongoose.results = results;
593
+ decorateBulkWriteResult(res, validationErrors, results);
592
594
  }
593
595
  }
594
596
  }
@@ -818,32 +820,41 @@ Connection.prototype.dropCollection = async function dropCollection(collection)
818
820
  /**
819
821
  * Waits for connection to be established, so the connection has a `client`
820
822
  *
823
+ * @param {Boolean} [noTimeout=false] if set, don't put a timeout on the operation. Used internally so `mongoose.model()` doesn't leave open handles.
821
824
  * @return Promise
822
825
  * @api private
823
826
  */
824
827
 
825
- Connection.prototype._waitForConnect = async function _waitForConnect() {
828
+ Connection.prototype._waitForConnect = async function _waitForConnect(noTimeout) {
826
829
  if ((this.readyState === STATES.connecting || this.readyState === STATES.disconnected) && this._shouldBufferCommands()) {
827
830
  const bufferTimeoutMS = this._getBufferTimeoutMS();
828
831
  let timeout = null;
829
832
  let timedOut = false;
830
833
  // The element that this function pushes onto `_queue`, stored to make it easy to remove later
831
834
  const queueElement = {};
832
- await Promise.race([
833
- new Promise(resolve => {
834
- queueElement.fn = resolve;
835
- this._queue.push(queueElement);
836
- }),
837
- new Promise(resolve => {
838
- timeout = setTimeout(
839
- () => {
840
- timedOut = true;
841
- resolve();
842
- },
843
- bufferTimeoutMS
844
- );
845
- })
846
- ]);
835
+
836
+ // Mongoose executes all elements in `_queue` when initial connection succeeds in `onOpen()`.
837
+ const waitForConnectPromise = new Promise(resolve => {
838
+ queueElement.fn = resolve;
839
+ this._queue.push(queueElement);
840
+ });
841
+
842
+ if (noTimeout) {
843
+ await waitForConnectPromise;
844
+ } else {
845
+ await Promise.race([
846
+ waitForConnectPromise,
847
+ new Promise(resolve => {
848
+ timeout = setTimeout(
849
+ () => {
850
+ timedOut = true;
851
+ resolve();
852
+ },
853
+ bufferTimeoutMS
854
+ );
855
+ })
856
+ ]);
857
+ }
847
858
 
848
859
  if (timedOut) {
849
860
  const index = this._queue.indexOf(queueElement);
package/lib/document.js CHANGED
@@ -741,15 +741,10 @@ function init(self, obj, doc, opts, prefix) {
741
741
  let schemaType;
742
742
  let path;
743
743
  let i;
744
- let index = 0;
745
744
  const strict = self.$__.strictMode;
746
745
  const docSchema = self.$__schema;
747
746
 
748
- while (index < len) {
749
- _init(index++);
750
- }
751
-
752
- function _init(index) {
747
+ for (let index = 0; index < len; ++index) {
753
748
  i = keys[index];
754
749
  // avoid prototype pollution
755
750
  if (i === '__proto__' || i === 'constructor') {
@@ -3558,8 +3553,10 @@ Document.prototype.$__undoReset = function $__undoReset() {
3558
3553
  }
3559
3554
  }
3560
3555
 
3561
- for (const subdoc of this.$getAllSubdocs()) {
3562
- subdoc.$__undoReset();
3556
+ if (!this.$isSubdocument) {
3557
+ for (const subdoc of this.$getAllSubdocs()) {
3558
+ subdoc.$__undoReset();
3559
+ }
3563
3560
  }
3564
3561
  };
3565
3562
 
@@ -10,3 +10,4 @@ exports.Collection = function() {
10
10
  exports.Connection = function() {
11
11
  throw new Error('Cannot create a connection from browser library');
12
12
  };
13
+ exports.BulkWriteResult = function() {};
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const BulkWriteResult = require('mongodb/lib/bulk/common').BulkWriteResult;
4
+
5
+ module.exports = BulkWriteResult;
@@ -13,6 +13,8 @@ const internalToObjectOptions = require('../../options').internalToObjectOptions
13
13
  const stream = require('stream');
14
14
  const util = require('util');
15
15
 
16
+ const formatToObjectOptions = Object.freeze({ ...internalToObjectOptions, copyTrustedSymbol: false });
17
+
16
18
  /**
17
19
  * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
18
20
  *
@@ -384,7 +386,9 @@ function format(obj, sub, color, shell) {
384
386
  }
385
387
 
386
388
  const clone = require('../../helpers/clone');
387
- let x = clone(obj, internalToObjectOptions);
389
+ // `sub` indicates `format()` was called recursively, so skip cloning because we already
390
+ // did a deep clone on the top-level object.
391
+ let x = sub ? obj : clone(obj, formatToObjectOptions);
388
392
  const constructorName = getConstructorName(x);
389
393
 
390
394
  if (constructorName === 'Binary') {
@@ -4,5 +4,6 @@
4
4
 
5
5
  'use strict';
6
6
 
7
+ exports.BulkWriteResult = require('./bulkWriteResult');
7
8
  exports.Collection = require('./collection');
8
9
  exports.Connection = require('./connection');
@@ -147,7 +147,7 @@ function cloneObject(obj, options, isArrayChild) {
147
147
  } else if (seen) {
148
148
  seen.set(obj, ret);
149
149
  }
150
- if (trustedSymbol in obj) {
150
+ if (trustedSymbol in obj && options?.copyTrustedSymbol !== false) {
151
151
  ret[trustedSymbol] = obj[trustedSymbol];
152
152
  }
153
153
 
@@ -1,26 +1,17 @@
1
1
  'use strict';
2
+
2
3
  function getDefaultBulkwriteResult() {
3
4
  return {
4
- result: {
5
- ok: 1,
6
- writeErrors: [],
7
- writeConcernErrors: [],
8
- insertedIds: [],
9
- nInserted: 0,
10
- nUpserted: 0,
11
- nMatched: 0,
12
- nModified: 0,
13
- nRemoved: 0,
14
- upserted: []
15
- },
16
- insertedCount: 0,
17
- matchedCount: 0,
18
- modifiedCount: 0,
19
- deletedCount: 0,
20
- upsertedCount: 0,
21
- upsertedIds: {},
22
- insertedIds: {},
23
- n: 0
5
+ ok: 1,
6
+ nInserted: 0,
7
+ nUpserted: 0,
8
+ nMatched: 0,
9
+ nModified: 0,
10
+ nRemoved: 0,
11
+ upserted: [],
12
+ writeErrors: [],
13
+ insertedIds: [],
14
+ writeConcernErrors: []
24
15
  };
25
16
  }
26
17
 
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ module.exports = function decorateBulkWriteResult(resultOrError, validationErrors, results) {
4
+ resultOrError.mongoose = resultOrError.mongoose || {};
5
+ resultOrError.mongoose.validationErrors = validationErrors;
6
+ resultOrError.mongoose.results = results;
7
+ return resultOrError;
8
+ };
@@ -82,6 +82,16 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
82
82
  schema = schema.discriminators[discriminatorValue] ||
83
83
  (byValue && byValue.schema) ||
84
84
  schema;
85
+ } else if (schema != null &&
86
+ options.overwriteDiscriminatorKey &&
87
+ obj.$set != null &&
88
+ utils.hasUserDefinedProperty(obj.$set, schema.options.discriminatorKey) &&
89
+ schema.discriminators != null) {
90
+ const discriminatorValue = obj.$set[schema.options.discriminatorKey];
91
+ const byValue = getDiscriminatorByValue(context.model.discriminators, discriminatorValue);
92
+ schema = schema.discriminators[discriminatorValue] ||
93
+ (byValue && byValue.schema) ||
94
+ schema;
85
95
  }
86
96
 
87
97
  if (options.upsert) {
package/lib/model.js CHANGED
@@ -10,7 +10,6 @@ const Document = require('./document');
10
10
  const DocumentNotFoundError = require('./error/notFound');
11
11
  const EventEmitter = require('events').EventEmitter;
12
12
  const Kareem = require('kareem');
13
- const { MongoBulkWriteError } = require('mongodb');
14
13
  const MongooseBulkWriteError = require('./error/bulkWriteError');
15
14
  const MongooseError = require('./error/index');
16
15
  const ObjectParameterError = require('./error/objectParameter');
@@ -69,6 +68,7 @@ const utils = require('./utils');
69
68
  const minimize = require('./helpers/minimize');
70
69
  const MongooseBulkSaveIncompleteError = require('./error/bulkSaveIncompleteError');
71
70
  const ObjectExpectedError = require('./error/objectExpected');
71
+ const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');
72
72
 
73
73
  const modelCollectionSymbol = Symbol('mongoose#Model#collection');
74
74
  const modelDbSymbol = Symbol('mongoose#Model#db');
@@ -1103,16 +1103,28 @@ Model.init = function init() {
1103
1103
  return results;
1104
1104
  };
1105
1105
  const _createCollection = async() => {
1106
- await conn._waitForConnect();
1107
- const autoCreate = utils.getOption(
1106
+ let autoCreate = utils.getOption(
1108
1107
  'autoCreate',
1109
1108
  this.schema.options,
1110
- conn.config,
1111
- conn.base.options
1109
+ conn.config
1110
+ // No base.options here because we don't want to take the base value if the connection hasn't
1111
+ // set it yet
1112
1112
  );
1113
+ if (autoCreate == null) {
1114
+ // `autoCreate` may later be set when the connection is opened, so wait for connect before checking
1115
+ await conn._waitForConnect(true);
1116
+ autoCreate = utils.getOption(
1117
+ 'autoCreate',
1118
+ this.schema.options,
1119
+ conn.config,
1120
+ conn.base.options
1121
+ );
1122
+ }
1123
+
1113
1124
  if (!autoCreate) {
1114
1125
  return;
1115
1126
  }
1127
+
1116
1128
  return await this.createCollection();
1117
1129
  };
1118
1130
 
@@ -3387,7 +3399,11 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3387
3399
  const ordered = options.ordered == null ? true : options.ordered;
3388
3400
 
3389
3401
  if (ops.length === 0) {
3390
- return getDefaultBulkwriteResult();
3402
+ const BulkWriteResult = this.base.driver.get().BulkWriteResult;
3403
+ const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false);
3404
+ bulkWriteResult.n = 0;
3405
+ decorateBulkWriteResult(bulkWriteResult, [], []);
3406
+ return bulkWriteResult;
3391
3407
  }
3392
3408
 
3393
3409
  const validations = ops.map(op => castBulkWrite(this, op, options));
@@ -3458,7 +3474,11 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3458
3474
  'bulkWrite'
3459
3475
  );
3460
3476
  }
3461
- return getDefaultBulkwriteResult();
3477
+ const BulkWriteResult = this.base.driver.get().BulkWriteResult;
3478
+ const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false);
3479
+ bulkWriteResult.result = getDefaultBulkwriteResult();
3480
+ decorateBulkWriteResult(bulkWriteResult, validationErrors, results);
3481
+ return bulkWriteResult;
3462
3482
  }
3463
3483
 
3464
3484
  let error;
@@ -3466,10 +3486,12 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3466
3486
  then(res => ([res, null])).
3467
3487
  catch(error => ([null, error]));
3468
3488
 
3489
+ for (let i = 0; i < validOpIndexes.length; ++i) {
3490
+ results[validOpIndexes[i]] = null;
3491
+ }
3469
3492
  if (error) {
3470
3493
  if (validationErrors.length > 0) {
3471
- error.mongoose = error.mongoose || {};
3472
- error.mongoose.validationErrors = validationErrors;
3494
+ decorateBulkWriteResult(error, validationErrors, results);
3473
3495
  }
3474
3496
 
3475
3497
  await new Promise((resolve, reject) => {
@@ -3483,9 +3505,6 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3483
3505
  });
3484
3506
  }
3485
3507
 
3486
- for (let i = 0; i < validOpIndexes.length; ++i) {
3487
- results[validOpIndexes[i]] = null;
3488
- }
3489
3508
  if (validationErrors.length > 0) {
3490
3509
  if (options.throwOnValidationError) {
3491
3510
  throw new MongooseBulkWriteError(
@@ -3495,9 +3514,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3495
3514
  'bulkWrite'
3496
3515
  );
3497
3516
  } else {
3498
- res.mongoose = res.mongoose || {};
3499
- res.mongoose.validationErrors = validationErrors;
3500
- res.mongoose.results = results;
3517
+ decorateBulkWriteResult(res, validationErrors, results);
3501
3518
  }
3502
3519
  }
3503
3520
  }
@@ -3563,7 +3580,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
3563
3580
  (err) => ({ bulkWriteResult: null, bulkWriteError: err })
3564
3581
  );
3565
3582
  // If not a MongoBulkWriteError, treat this as all documents failed to save.
3566
- if (bulkWriteError != null && !(bulkWriteError instanceof MongoBulkWriteError)) {
3583
+ if (bulkWriteError != null && bulkWriteError.name !== 'MongoBulkWriteError') {
3567
3584
  throw bulkWriteError;
3568
3585
  }
3569
3586
 
package/lib/query.js CHANGED
@@ -65,6 +65,25 @@ const queryOptionMethods = new Set([
65
65
  'wtimeout'
66
66
  ]);
67
67
 
68
+ // Map from operation name to the name of the function that executes the actual operation against MongoDB.
69
+ // Called a thunk for legacy reasons, "thunk" means function that takes exactly 1 param, a callback.
70
+ // Currently `_countDocuments()`, etc. are async functions that take no params.
71
+ const opToThunk = new Map([
72
+ ['countDocuments', '_countDocuments'],
73
+ ['distinct', '__distinct'],
74
+ ['estimatedDocumentCount', '_estimatedDocumentCount'],
75
+ ['find', '_find'],
76
+ ['findOne', '_findOne'],
77
+ ['findOneAndReplace', '_findOneAndReplace'],
78
+ ['findOneAndUpdate', '_findOneAndUpdate'],
79
+ ['replaceOne', '_replaceOne'],
80
+ ['updateMany', '_updateMany'],
81
+ ['updateOne', '_updateOne'],
82
+ ['deleteMany', '_deleteMany'],
83
+ ['deleteOne', '_deleteOne'],
84
+ ['findOneAndDelete', '_findOneAndDelete']
85
+ ]);
86
+
68
87
  /**
69
88
  * Query constructor used for building queries. You do not need
70
89
  * to instantiate a `Query` directly. Instead use Model functions like
@@ -2337,18 +2356,17 @@ Query.prototype._find = async function _find() {
2337
2356
  }
2338
2357
 
2339
2358
  const mongooseOptions = this._mongooseOptions;
2340
- const _this = this;
2341
- const userProvidedFields = _this._userProvidedFields || {};
2359
+ const userProvidedFields = this._userProvidedFields || {};
2342
2360
 
2343
2361
  applyGlobalMaxTimeMS(this.options, this.model.db.options, this.model.base.options);
2344
2362
  applyGlobalDiskUse(this.options, this.model.db.options, this.model.base.options);
2345
2363
 
2346
2364
  // Separate options to pass down to `completeMany()` in case we need to
2347
2365
  // set a session on the document
2348
- const completeManyOptions = Object.assign({}, {
2366
+ const completeManyOptions = {
2349
2367
  session: this && this.options && this.options.session || null,
2350
2368
  lean: mongooseOptions.lean || null
2351
- });
2369
+ };
2352
2370
 
2353
2371
  const options = this._optionsForExec();
2354
2372
 
@@ -2366,7 +2384,7 @@ Query.prototype._find = async function _find() {
2366
2384
  }
2367
2385
 
2368
2386
  if (!mongooseOptions.populate) {
2369
- const versionKey = _this.schema.options.versionKey;
2387
+ const versionKey = this.schema.options.versionKey;
2370
2388
  if (mongooseOptions.lean && mongooseOptions.lean.versionKey === false && versionKey) {
2371
2389
  docs.forEach((doc) => {
2372
2390
  if (versionKey in doc) {
@@ -2375,17 +2393,17 @@ Query.prototype._find = async function _find() {
2375
2393
  });
2376
2394
  }
2377
2395
  return mongooseOptions.lean ?
2378
- _completeManyLean(_this.model.schema, docs, null, completeManyOptions) :
2379
- _this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
2396
+ _completeManyLean(this.model.schema, docs, null, completeManyOptions) :
2397
+ this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
2380
2398
  }
2381
2399
 
2382
- const pop = helpers.preparePopulationOptionsMQ(_this, mongooseOptions);
2400
+ const pop = helpers.preparePopulationOptionsMQ(this, mongooseOptions);
2383
2401
 
2384
2402
  if (mongooseOptions.lean) {
2385
- return _this.model.populate(docs, pop);
2403
+ return this.model.populate(docs, pop);
2386
2404
  }
2387
2405
 
2388
- docs = await _this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
2406
+ docs = await this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
2389
2407
  await this.model.populate(docs, pop);
2390
2408
 
2391
2409
  return docs;
@@ -4397,22 +4415,14 @@ Query.prototype.exec = async function exec(op) {
4397
4415
  if (this.model == null) {
4398
4416
  throw new MongooseError('Query must have an associated model before executing');
4399
4417
  }
4400
- this._validateOp();
4401
-
4402
- if (!this.op) {
4403
- return;
4404
- }
4405
4418
 
4406
- if (this.options && this.options.sort) {
4407
- const keys = Object.keys(this.options.sort);
4408
- if (keys.includes('')) {
4409
- throw new Error('Invalid field "" passed to sort()');
4410
- }
4419
+ const thunk = opToThunk.get(this.op);
4420
+ if (!thunk) {
4421
+ throw new MongooseError('Query has invalid `op`: "' + this.op + '"');
4411
4422
  }
4412
4423
 
4413
- let thunk = '_' + this.op;
4414
- if (this.op === 'distinct') {
4415
- thunk = '__distinct';
4424
+ if (this.options && this.options.sort && typeof this.options.sort === 'object' && this.options.sort.hasOwnProperty('')) {
4425
+ throw new Error('Invalid field "" passed to sort()');
4416
4426
  }
4417
4427
 
4418
4428
  if (this._executionStack != null) {
package/lib/schema/map.js CHANGED
@@ -91,14 +91,7 @@ class SchemaMap extends SchemaType {
91
91
 
92
92
  const isRequired = this.options.required && typeof this.options.required !== 'function';
93
93
  const result = createJSONSchemaTypeDefinition('object', 'object', useBsonType, isRequired);
94
-
95
- if (embeddedSchemaType.schema) {
96
- result.additionalProperties = useBsonType
97
- ? { ...embeddedSchemaType.toJSONSchema(options) }
98
- : { ...embeddedSchemaType.toJSONSchema(options) };
99
- } else {
100
- result.additionalProperties = embeddedSchemaType.toJSONSchema(options);
101
- }
94
+ result.additionalProperties = embeddedSchemaType.toJSONSchema(options);
102
95
 
103
96
  return result;
104
97
  }
package/lib/schema.js CHANGED
@@ -1118,6 +1118,9 @@ Schema.prototype.path = function(path, obj) {
1118
1118
  this.paths[path] = this.interpretAsType(path, obj, this.options);
1119
1119
  const schemaType = this.paths[path];
1120
1120
 
1121
+ // If overwriting an existing path, make sure to clear the childSchemas
1122
+ this.childSchemas = this.childSchemas.filter(childSchema => childSchema.path !== path);
1123
+
1121
1124
  if (schemaType.$isSchemaMap) {
1122
1125
  // Maps can have arbitrary keys, so `$*` is internal shorthand for "any key"
1123
1126
  // The '$' is to imply this path should never be stored in MongoDB so we
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.10.0",
4
+ "version": "8.10.2",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",