mongoose 8.12.1 → 8.13.0

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.
@@ -8,6 +8,7 @@ const MongooseError = require('../error/mongooseError');
8
8
  const Readable = require('stream').Readable;
9
9
  const eachAsync = require('../helpers/cursor/eachAsync');
10
10
  const immediate = require('../helpers/immediate');
11
+ const kareem = require('kareem');
11
12
  const util = require('util');
12
13
 
13
14
  /**
@@ -62,7 +63,11 @@ util.inherits(AggregationCursor, Readable);
62
63
 
63
64
  function _init(model, c, agg) {
64
65
  if (!model.collection.buffer) {
65
- model.hooks.execPre('aggregate', agg, function() {
66
+ model.hooks.execPre('aggregate', agg, function(err) {
67
+ if (err != null) {
68
+ _handlePreHookError(c, err);
69
+ return;
70
+ }
66
71
  if (typeof agg.options?.cursor?.transform === 'function') {
67
72
  c._transforms.push(agg.options.cursor.transform);
68
73
  }
@@ -72,7 +77,12 @@ function _init(model, c, agg) {
72
77
  });
73
78
  } else {
74
79
  model.collection.emitter.once('queue', function() {
75
- model.hooks.execPre('aggregate', agg, function() {
80
+ model.hooks.execPre('aggregate', agg, function(err) {
81
+ if (err != null) {
82
+ _handlePreHookError(c, err);
83
+ return;
84
+ }
85
+
76
86
  if (typeof agg.options?.cursor?.transform === 'function') {
77
87
  c._transforms.push(agg.options.cursor.transform);
78
88
  }
@@ -84,6 +94,38 @@ function _init(model, c, agg) {
84
94
  }
85
95
  }
86
96
 
97
+ /**
98
+ * Handles error emitted from pre middleware. In particular, checks for `skipWrappedFunction`, which allows skipping
99
+ * the actual aggregation and overwriting the function's return value. Because aggregation cursors don't return a value,
100
+ * we need to make sure the user doesn't accidentally set a value in skipWrappedFunction.
101
+ *
102
+ * @param {QueryCursor} queryCursor
103
+ * @param {Error} err
104
+ * @returns
105
+ */
106
+
107
+ function _handlePreHookError(queryCursor, err) {
108
+ if (err instanceof kareem.skipWrappedFunction) {
109
+ const resultValue = err.args[0];
110
+ if (resultValue != null && (!Array.isArray(resultValue) || resultValue.length)) {
111
+ const err = new MongooseError(
112
+ 'Cannot `skipMiddlewareFunction()` with a value when using ' +
113
+ '`.aggregate().cursor()`, value must be nullish or empty array, got "' +
114
+ util.inspect(resultValue) +
115
+ '".'
116
+ );
117
+ queryCursor._markError(err);
118
+ queryCursor.listeners('error').length > 0 && queryCursor.emit('error', err);
119
+ return;
120
+ }
121
+ queryCursor.emit('cursor', null);
122
+ return;
123
+ }
124
+ queryCursor._markError(err);
125
+ queryCursor.listeners('error').length > 0 && queryCursor.emit('error', err);
126
+ }
127
+
128
+
87
129
  /**
88
130
  * Necessary to satisfy the Readable API
89
131
  * @method _read
@@ -424,6 +466,7 @@ function _next(ctx, cb) {
424
466
  err => callback(err)
425
467
  );
426
468
  } else {
469
+ ctx.once('error', cb);
427
470
  ctx.once('cursor', function() {
428
471
  _next(ctx, cb);
429
472
  });
package/lib/document.js CHANGED
@@ -747,8 +747,8 @@ function init(self, obj, doc, opts, prefix) {
747
747
  for (let index = 0; index < len; ++index) {
748
748
  i = keys[index];
749
749
  // avoid prototype pollution
750
- if (i === '__proto__' || i === 'constructor') {
751
- return;
750
+ if (specialProperties.has(i)) {
751
+ continue;
752
752
  }
753
753
  path = prefix ? prefix + i : i;
754
754
  schemaType = docSchema.path(path);
@@ -756,7 +756,7 @@ function init(self, obj, doc, opts, prefix) {
756
756
  // necessary. This is *only* to catch the case where we queried using the
757
757
  // base model and the discriminated model has a projection
758
758
  if (docSchema.$isRootDiscriminator && !self.$__isSelected(path)) {
759
- return;
759
+ continue;
760
760
  }
761
761
 
762
762
  const value = obj[i];
@@ -1720,7 +1720,7 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1720
1720
  const last = next === l;
1721
1721
  cur += (cur ? '.' + parts[i] : parts[i]);
1722
1722
  if (specialProperties.has(parts[i])) {
1723
- return;
1723
+ continue;
1724
1724
  }
1725
1725
 
1726
1726
  if (last) {
@@ -178,8 +178,8 @@ module.exports = function getSchemaTypes(model, schema, doc, path) {
178
178
  }
179
179
 
180
180
  const fullPath = nestedPath.concat([trypath]).join('.');
181
- if (topLevelDoc != null && topLevelDoc.$__ && topLevelDoc.$populated(fullPath) && p < parts.length) {
182
- const model = doc.$__.populated[fullPath].options[populateModelSymbol];
181
+ if (topLevelDoc != null && topLevelDoc.$__ && topLevelDoc.$populated(fullPath, true) && p < parts.length) {
182
+ const model = topLevelDoc.$populated(fullPath, true).options[populateModelSymbol];
183
183
  if (model != null) {
184
184
  const ret = search(
185
185
  parts.slice(p),
package/lib/model.js CHANGED
@@ -3603,7 +3603,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
3603
3603
  }
3604
3604
  await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document)));
3605
3605
 
3606
- if (bulkWriteError && bulkWriteError.writeErrors && bulkWriteError.writeErrors.length) {
3606
+ if (bulkWriteError != null) {
3607
3607
  throw bulkWriteError;
3608
3608
  }
3609
3609
 
package/lib/mongoose.js CHANGED
@@ -179,6 +179,10 @@ Mongoose.prototype.setDriver = function setDriver(driver) {
179
179
  }
180
180
  }
181
181
 
182
+ if (driver.SchemaTypes != null) {
183
+ Object.assign(mongoose.Schema.Types, driver.SchemaTypes);
184
+ }
185
+
182
186
  const Connection = driver.Connection;
183
187
  const oldDefaultConnection = _mongoose.connections[0];
184
188
  _mongoose.connections = [new Connection(_mongoose)];
@@ -150,7 +150,7 @@ SchemaDate._defaultCaster = v => {
150
150
  * // expire in 24 hours
151
151
  * new Schema({ createdAt: { type: Date, expires: 60*60*24 }});
152
152
  *
153
- * `expires` utilizes the `ms` module from [guille](https://github.com/guille/) allowing us to use a friendlier syntax:
153
+ * `expires` utilizes the `ms` module from [vercel](https://github.com/vercel/ms) allowing us to use a friendlier syntax:
154
154
  *
155
155
  * #### Example:
156
156
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.12.1",
4
+ "version": "8.13.0",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "bson": "^6.10.3",
24
24
  "kareem": "2.6.3",
25
- "mongodb": "~6.14.0",
25
+ "mongodb": "~6.15.0",
26
26
  "mpath": "0.9.0",
27
27
  "mquery": "5.0.0",
28
28
  "ms": "2.1.3",
@@ -68,7 +68,7 @@ declare module 'mongoose' {
68
68
 
69
69
  type IndexOptions = Omit<mongodb.CreateIndexesOptions, 'expires' | 'weights' | 'unique'> & {
70
70
  /**
71
- * `expires` utilizes the `ms` module from [guille](https://github.com/guille/) allowing us to use a friendlier syntax:
71
+ * `expires` utilizes the `ms` module from [vercel](https://github.com/vercel/ms) allowing us to use a friendlier syntax:
72
72
  *
73
73
  * @example
74
74
  * ```js
@@ -93,4 +93,12 @@ type AddThisParameter<T, D> = {
93
93
  : T[K];
94
94
  };
95
95
 
96
+ /**
97
+ * @summary Adds timestamp fields to a type
98
+ * @description Adds createdAt and updatedAt fields of type Date, or custom timestamp fields if specified
99
+ * @param {T} T The type to add timestamp fields to
100
+ * @param {P} P Optional SchemaTimestampsConfig or boolean to customize timestamp field names
101
+ * @returns T with timestamp fields added
102
+ */
103
+ export type WithTimestamps<T, P extends SchemaTimestampsConfig | boolean = true> = ResolveTimestamps<T, { timestamps: P }>;
96
104
  }