mongoose 8.13.1 → 8.13.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/document.js CHANGED
@@ -2721,12 +2721,33 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
2721
2721
  function addToPaths(p) { paths.add(p); }
2722
2722
 
2723
2723
  if (!isNestedValidate) {
2724
- // If we're validating a subdocument, all this logic will run anyway on the top-level document, so skip for subdocuments
2725
- const subdocs = doc.$getAllSubdocs({ useCache: true });
2724
+ // If we're validating a subdocument, all this logic will run anyway on the top-level document, so skip for subdocuments.
2725
+ // But only run for top-level subdocuments, because we're looking for subdocuments that are not modified at top-level but
2726
+ // have a modified path. If that is the case, we will run validation on the top-level subdocument, and via that run validation
2727
+ // on any subdocuments down to the modified path.
2728
+ const topLevelSubdocs = [];
2729
+ for (const path of Object.keys(doc.$__schema.paths)) {
2730
+ const schemaType = doc.$__schema.path(path);
2731
+ if (schemaType.$isSingleNested) {
2732
+ const subdoc = doc.$get(path);
2733
+ if (subdoc) {
2734
+ topLevelSubdocs.push(subdoc);
2735
+ }
2736
+ } else if (schemaType.$isMongooseDocumentArray) {
2737
+ const arr = doc.$get(path);
2738
+ if (arr && arr.length) {
2739
+ for (const subdoc of arr) {
2740
+ if (subdoc) {
2741
+ topLevelSubdocs.push(subdoc);
2742
+ }
2743
+ }
2744
+ }
2745
+ }
2746
+ }
2726
2747
  const modifiedPaths = doc.modifiedPaths();
2727
- for (const subdoc of subdocs) {
2748
+ for (const subdoc of topLevelSubdocs) {
2728
2749
  if (subdoc.$basePath) {
2729
- const fullPathToSubdoc = subdoc.$isSingleNested ? subdoc.$__pathRelativeToParent() : subdoc.$__fullPathWithIndexes();
2750
+ const fullPathToSubdoc = subdoc.$__pathRelativeToParent();
2730
2751
 
2731
2752
  // Remove child paths for now, because we'll be validating the whole
2732
2753
  // subdoc.
@@ -2736,11 +2757,12 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
2736
2757
  paths.delete(fullPathToSubdoc + '.' + modifiedPath);
2737
2758
  }
2738
2759
 
2760
+ const subdocParent = subdoc.$parent();
2739
2761
  if (doc.$isModified(fullPathToSubdoc, null, modifiedPaths) &&
2740
2762
  // Avoid using isDirectModified() here because that does additional checks on whether the parent path
2741
2763
  // is direct modified, which can cause performance issues re: gh-14897
2742
- !doc.$__.activePaths.getStatePaths('modify').hasOwnProperty(fullPathToSubdoc) &&
2743
- !doc.$isDefault(fullPathToSubdoc)) {
2764
+ !subdocParent.$__.activePaths.getStatePaths('modify').hasOwnProperty(fullPathToSubdoc) &&
2765
+ !subdocParent.$isDefault(fullPathToSubdoc)) {
2744
2766
  paths.add(fullPathToSubdoc);
2745
2767
 
2746
2768
  if (doc.$__.pathsToScopes == null) {
@@ -65,32 +65,32 @@ MongooseError.messages = require('./messages');
65
65
  MongooseError.Messages = MongooseError.messages;
66
66
 
67
67
  /**
68
- * An instance of this error class will be returned when `save()` fails
69
- * because the underlying
70
- * document was not found. The constructor takes one parameter, the
71
- * conditions that mongoose passed to `updateOne()` when trying to update
72
- * the document.
68
+ * An instance of this error class will be thrown when mongoose failed to
69
+ * cast a value.
73
70
  *
74
71
  * @api public
75
72
  * @memberOf Error
76
73
  * @static
77
74
  */
78
75
 
79
- MongooseError.DocumentNotFoundError = require('./notFound');
76
+ MongooseError.CastError = require('./cast');
80
77
 
81
78
  /**
82
- * An instance of this error class will be returned when mongoose failed to
83
- * cast a value.
79
+ * An instance of this error class will be thrown when `save()` fails
80
+ * because the underlying
81
+ * document was not found. The constructor takes one parameter, the
82
+ * conditions that mongoose passed to `updateOne()` when trying to update
83
+ * the document.
84
84
  *
85
85
  * @api public
86
86
  * @memberOf Error
87
87
  * @static
88
88
  */
89
89
 
90
- MongooseError.CastError = require('./cast');
90
+ MongooseError.DocumentNotFoundError = require('./notFound');
91
91
 
92
92
  /**
93
- * An instance of this error class will be returned when [validation](https://mongoosejs.com/docs/validation.html) failed.
93
+ * An instance of this error class will be thrown when [validation](https://mongoosejs.com/docs/validation.html) failed.
94
94
  * The `errors` property contains an object whose keys are the paths that failed and whose values are
95
95
  * instances of CastError or ValidationError.
96
96
  *
@@ -137,7 +137,7 @@ MongooseError.ValidationError = require('./validation');
137
137
  MongooseError.ValidatorError = require('./validator');
138
138
 
139
139
  /**
140
- * An instance of this error class will be returned when you call `save()` after
140
+ * An instance of this error class will be thrown when you call `save()` after
141
141
  * the document in the database was changed in a potentially unsafe way. See
142
142
  * the [`versionKey` option](https://mongoosejs.com/docs/guide.html#versionKey) for more information.
143
143
  *
@@ -149,7 +149,7 @@ MongooseError.ValidatorError = require('./validator');
149
149
  MongooseError.VersionError = require('./version');
150
150
 
151
151
  /**
152
- * An instance of this error class will be returned when you call `save()` multiple
152
+ * An instance of this error class will be thrown when you call `save()` multiple
153
153
  * times on the same document in parallel. See the [FAQ](https://mongoosejs.com/docs/faq.html) for more
154
154
  * information.
155
155
  *
@@ -181,6 +181,16 @@ MongooseError.OverwriteModelError = require('./overwriteModel');
181
181
 
182
182
  MongooseError.MissingSchemaError = require('./missingSchema');
183
183
 
184
+ /**
185
+ * Thrown when some documents failed to save when calling `bulkSave()`
186
+ *
187
+ * @api public
188
+ * @memberOf Error
189
+ * @static
190
+ */
191
+
192
+ MongooseError.MongooseBulkSaveIncompleteError = require('./bulkSaveIncompleteError');
193
+
184
194
  /**
185
195
  * Thrown when the MongoDB Node driver can't connect to a valid server
186
196
  * to send an operation to.
@@ -193,7 +203,7 @@ MongooseError.MissingSchemaError = require('./missingSchema');
193
203
  MongooseError.MongooseServerSelectionError = require('./serverSelection');
194
204
 
195
205
  /**
196
- * An instance of this error will be returned if you used an array projection
206
+ * An instance of this error will be thrown if you used an array projection
197
207
  * and then modified the array in an unsafe way.
198
208
  *
199
209
  * @api public
package/lib/query.js CHANGED
@@ -3096,13 +3096,12 @@ function _handleSortValue(val, key) {
3096
3096
  *
3097
3097
  * await Character.deleteOne({ name: 'Eddard Stark' });
3098
3098
  *
3099
- * This function calls the MongoDB driver's [`Collection#deleteOne()` function](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#deleteOne).
3099
+ * This function calls the MongoDB driver's [`Collection#deleteOne()` function](https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteOne).
3100
3100
  * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
3101
- * object that contains 3 properties:
3101
+ * object that contains 2 properties:
3102
3102
  *
3103
- * - `ok`: `1` if no errors occurred
3103
+ * - `acknowledged`: boolean
3104
3104
  * - `deletedCount`: the number of documents deleted
3105
- * - `n`: the number of documents deleted. Equal to `deletedCount`.
3106
3105
  *
3107
3106
  * #### Example:
3108
3107
  *
@@ -3113,8 +3112,8 @@ function _handleSortValue(val, key) {
3113
3112
  * @param {Object|Query} [filter] mongodb selector
3114
3113
  * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
3115
3114
  * @return {Query} this
3116
- * @see DeleteResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/DeleteResult.html
3117
- * @see deleteOne https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#deleteOne
3115
+ * @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
3116
+ * @see deleteOne https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteOne
3118
3117
  * @api public
3119
3118
  */
3120
3119
 
@@ -3169,13 +3168,12 @@ Query.prototype._deleteOne = async function _deleteOne() {
3169
3168
  *
3170
3169
  * await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } });
3171
3170
  *
3172
- * This function calls the MongoDB driver's [`Collection#deleteMany()` function](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#deleteMany).
3171
+ * This function calls the MongoDB driver's [`Collection#deleteMany()` function](https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteMany).
3173
3172
  * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
3174
- * object that contains 3 properties:
3173
+ * object that contains 2 properties:
3175
3174
  *
3176
- * - `ok`: `1` if no errors occurred
3175
+ * - `acknowledged`: boolean
3177
3176
  * - `deletedCount`: the number of documents deleted
3178
- * - `n`: the number of documents deleted. Equal to `deletedCount`.
3179
3177
  *
3180
3178
  * #### Example:
3181
3179
  *
@@ -3186,8 +3184,8 @@ Query.prototype._deleteOne = async function _deleteOne() {
3186
3184
  * @param {Object|Query} [filter] mongodb selector
3187
3185
  * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
3188
3186
  * @return {Query} this
3189
- * @see DeleteResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/DeleteResult.html
3190
- * @see deleteMany https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#deleteMany
3187
+ * @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
3188
+ * @see deleteMany https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteMany
3191
3189
  * @api public
3192
3190
  */
3193
3191
 
package/lib/utils.js CHANGED
@@ -234,6 +234,38 @@ exports.omit = function omit(obj, keys) {
234
234
  return ret;
235
235
  };
236
236
 
237
+ /**
238
+ * Simplified version of `clone()` that only clones POJOs and arrays. Skips documents, dates, objectids, etc.
239
+ * @param {*} val
240
+ * @returns
241
+ */
242
+
243
+ exports.clonePOJOsAndArrays = function clonePOJOsAndArrays(val) {
244
+ if (val == null) {
245
+ return val;
246
+ }
247
+ // Skip documents because we assume they'll be cloned later. See gh-15312 for how documents are handled with `merge()`.
248
+ if (val.$__ != null) {
249
+ return val;
250
+ }
251
+ if (isPOJO(val)) {
252
+ val = { ...val };
253
+ for (const key of Object.keys(val)) {
254
+ val[key] = exports.clonePOJOsAndArrays(val[key]);
255
+ }
256
+ return val;
257
+ }
258
+ if (Array.isArray(val)) {
259
+ val = [...val];
260
+ for (let i = 0; i < val.length; ++i) {
261
+ val[i] = exports.clonePOJOsAndArrays(val[i]);
262
+ }
263
+ return val;
264
+ }
265
+
266
+ return val;
267
+ };
268
+
237
269
  /**
238
270
  * Merges `from` into `to` without overwriting existing properties.
239
271
  *
@@ -271,13 +303,7 @@ exports.merge = function merge(to, from, options, path) {
271
303
  continue;
272
304
  }
273
305
  if (to[key] == null) {
274
- if (isPOJO(from[key])) {
275
- to[key] = { ...from[key] };
276
- } else if (Array.isArray(from[key])) {
277
- to[key] = [...from[key]];
278
- } else {
279
- to[key] = from[key];
280
- }
306
+ to[key] = exports.clonePOJOsAndArrays(from[key]);
281
307
  } else if (exports.isObject(from[key])) {
282
308
  if (!exports.isObject(to[key])) {
283
309
  to[key] = {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.13.1",
4
+ "version": "8.13.3",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -29,7 +29,7 @@
29
29
  "sift": "17.1.3"
30
30
  },
31
31
  "devDependencies": {
32
- "@babel/core": "7.26.9",
32
+ "@babel/core": "7.26.10",
33
33
  "@babel/preset-env": "7.26.9",
34
34
  "@typescript-eslint/eslint-plugin": "^8.19.1",
35
35
  "@typescript-eslint/parser": "^8.19.1",
@@ -62,7 +62,7 @@
62
62
  "nyc": "15.1.0",
63
63
  "pug": "3.0.3",
64
64
  "q": "1.5.1",
65
- "sinon": "19.0.2",
65
+ "sinon": "20.0.0",
66
66
  "stream-browserify": "3.0.0",
67
67
  "tsd": "0.31.2",
68
68
  "typescript": "5.7.3",
@@ -124,7 +124,7 @@ declare module 'mongoose' {
124
124
  pipeline(): PipelineStage[];
125
125
 
126
126
  /** Appends a new $project operator to this aggregate pipeline. */
127
- project(arg: PipelineStage.Project['$project']): this;
127
+ project(arg: PipelineStage.Project['$project'] | string): this;
128
128
 
129
129
  /** Sets the readPreference option for the aggregation query. */
130
130
  read(pref: mongodb.ReadPreferenceLike): this;
@@ -58,12 +58,34 @@ declare module 'mongoose' {
58
58
  sanitizeFilter?: boolean;
59
59
  }
60
60
 
61
+ export type AnyConnectionBulkWriteModel<TSchema extends AnyObject> = Omit<mongodb.ClientInsertOneModel<TSchema>, 'namespace'>
62
+ | Omit<mongodb.ClientReplaceOneModel<TSchema>, 'namespace'>
63
+ | Omit<mongodb.ClientUpdateOneModel<TSchema>, 'namespace'>
64
+ | Omit<mongodb.ClientUpdateManyModel<TSchema>, 'namespace'>
65
+ | Omit<mongodb.ClientDeleteOneModel<TSchema>, 'namespace'>
66
+ | Omit<mongodb.ClientDeleteManyModel<TSchema>, 'namespace'>;
67
+
68
+ export type ConnectionBulkWriteModel<SchemaMap extends Record<string, AnyObject> = Record<string, AnyObject>> = {
69
+ [ModelName in keyof SchemaMap]: AnyConnectionBulkWriteModel<SchemaMap[ModelName]> & {
70
+ model: ModelName;
71
+ };
72
+ }[keyof SchemaMap];
73
+
61
74
  class Connection extends events.EventEmitter implements SessionStarter {
62
75
  aggregate<ResultType = unknown>(pipeline?: PipelineStage[] | null, options?: AggregateOptions): Aggregate<Array<ResultType>>;
63
76
 
64
77
  /** Returns a promise that resolves when this connection successfully connects to MongoDB */
65
78
  asPromise(): Promise<this>;
66
79
 
80
+ bulkWrite<TSchemaMap extends Record<string, AnyObject>>(
81
+ ops: Array<ConnectionBulkWriteModel<TSchemaMap>>,
82
+ options: mongodb.ClientBulkWriteOptions & { ordered: false }
83
+ ): Promise<mongodb.ClientBulkWriteResult & { mongoose?: { validationErrors: Error[], results: Array<Error | mongodb.WriteError | null> } }>;
84
+ bulkWrite<TSchemaMap extends Record<string, AnyObject>>(
85
+ ops: Array<ConnectionBulkWriteModel<TSchemaMap>>,
86
+ options?: mongodb.ClientBulkWriteOptions
87
+ ): Promise<mongodb.ClientBulkWriteResult>;
88
+
67
89
  /** Closes the connection */
68
90
  close(force?: boolean): Promise<void>;
69
91
 
package/types/error.d.ts CHANGED
@@ -129,5 +129,12 @@ declare module 'mongoose' {
129
129
  name: 'StrictPopulateError';
130
130
  path: string;
131
131
  }
132
+
133
+ export class MongooseBulkSaveIncompleteError extends MongooseError {
134
+ name: 'MongooseBulkSaveIncompleteError';
135
+ modelName: string;
136
+ bulkWriteResult: mongodb.BulkWriteResult;
137
+ numDocumentsNotUpdated: number;
138
+ }
132
139
  }
133
140
  }