mongoose 8.24.0 → 8.24.1

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
@@ -1922,7 +1922,9 @@ Document.prototype.get = function(path, type, options) {
1922
1922
 
1923
1923
  // Fast path if we know we're just accessing top-level path on the document:
1924
1924
  // just get the schema path, avoid `$__path()` because that does string manipulation
1925
- let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path);
1925
+ let schema = noDottedPath ?
1926
+ Object.hasOwn(this.$__schema.paths, path) ? this.$__schema.paths[path] : undefined :
1927
+ this.$__path(path);
1926
1928
  if (schema == null) {
1927
1929
  schema = this.$__schema.virtualpath(path);
1928
1930
 
@@ -1932,7 +1934,7 @@ Document.prototype.get = function(path, type, options) {
1932
1934
  }
1933
1935
 
1934
1936
  if (noDottedPath) {
1935
- let obj = this._doc[path];
1937
+ let obj = Object.hasOwn(this._doc, path) ? this._doc[path] : undefined;
1936
1938
  if (adhoc) {
1937
1939
  obj = adhoc.cast(obj);
1938
1940
  }
@@ -1959,6 +1961,10 @@ Document.prototype.get = function(path, type, options) {
1959
1961
  }
1960
1962
 
1961
1963
  for (let i = 0, l = pieces.length; i < l; i++) {
1964
+ if (specialProperties.has(pieces[i])) {
1965
+ return undefined;
1966
+ }
1967
+
1962
1968
  if (obj && obj._doc) {
1963
1969
  obj = obj._doc;
1964
1970
  }
@@ -1980,7 +1986,7 @@ Document.prototype.get = function(path, type, options) {
1980
1986
 
1981
1987
  if (schema != null && options.getters !== false) {
1982
1988
  obj = schema.applyGetters(obj, this);
1983
- } else if (this.$__schema.nested[path] && options.virtuals) {
1989
+ } else if (Object.hasOwn(this.$__schema.nested, path) && options.virtuals) {
1984
1990
  // Might need to apply virtuals if this is a nested path
1985
1991
  return applyVirtuals(this, clone(obj) || {}, { path: path });
1986
1992
  }
@@ -202,10 +202,15 @@ function cloneArray(arr, options) {
202
202
 
203
203
  let ret = null;
204
204
  if (options?.retainDocuments) {
205
+ // Use the cloned parent doc (options.parentDoc) when available so the new
206
+ // array is wired up to the clone, not the source. Falling back to the
207
+ // source's parent preserves the previous behavior for callers that don't
208
+ // pass parentDoc.
209
+ const newParent = options.parentDoc ?? (arr.isMongooseArray ? arr.$parent() : undefined);
205
210
  if (arr.isMongooseDocumentArray) {
206
- ret = new (arr.$schemaType().schema.base.Types.DocumentArray)([], arr.$path(), arr.$parent(), arr.$schemaType());
211
+ ret = new (arr.$schemaType().schema.base.Types.DocumentArray)([], arr.$path(), newParent, arr.$schemaType());
207
212
  } else if (arr.isMongooseArray) {
208
- ret = new (arr.$parent().schema.base.Types.Array)([], arr.$path(), arr.$parent(), arr.$schemaType());
213
+ ret = new (arr.$parent().schema.base.Types.Array)([], arr.$path(), newParent, arr.$schemaType());
209
214
  } else {
210
215
  ret = new Array(len);
211
216
  }
@@ -218,9 +223,21 @@ function cloneArray(arr, options) {
218
223
  // Create new options object to avoid mutating the shared options.
219
224
  // Subdocs need parentArray to point to their own cloned array.
220
225
  options = { ...options, parentArray: ret };
221
- }
222
- for (i = 0; i < len; ++i) {
223
- ret[i] = clone(arr[i], options, true);
226
+ // Skip change-tracking while populating the freshly cloned array: every
227
+ // index assignment would otherwise call markModified on the cloned parent
228
+ // doc and (before this guard) on the source parent doc.
229
+ for (i = 0; i < len; ++i) {
230
+ const doc = clone(arr[i], options, true);
231
+ // Document arrays can contain null entries, so only restamp actual subdocs.
232
+ if (typeof doc?.$setIndex === 'function') {
233
+ doc.$setIndex(i);
234
+ }
235
+ ret.set(i, doc, true);
236
+ }
237
+ } else {
238
+ for (i = 0; i < len; ++i) {
239
+ ret[i] = clone(arr[i], options, true);
240
+ }
224
241
  }
225
242
 
226
243
  return ret;
package/lib/schema.js CHANGED
@@ -1213,7 +1213,7 @@ reserved.collection = 1;
1213
1213
 
1214
1214
  Schema.prototype.path = function(path, obj) {
1215
1215
  if (obj === undefined) {
1216
- if (this.paths[path] != null) {
1216
+ if (Object.hasOwn(this.paths, path)) {
1217
1217
  return this.paths[path];
1218
1218
  }
1219
1219
  // Convert to '.$' to check subpaths re: gh-6405
@@ -1241,9 +1241,15 @@ Schema.prototype.path = function(path, obj) {
1241
1241
  : undefined;
1242
1242
  }
1243
1243
 
1244
+ const subpaths = path.indexOf('.') === -1 ? [path] : path.split('.');
1245
+ const last = subpaths.pop();
1246
+ if (utils.specialProperties.has(last)) {
1247
+ throw new Error('Cannot set special property `' + last + '` on a schema');
1248
+ }
1249
+
1244
1250
  // some path names conflict with document methods
1245
- const firstPieceOfPath = path.split('.')[0];
1246
- if (reserved[firstPieceOfPath] && !this.options.suppressReservedKeysWarning) {
1251
+ const firstPieceOfPath = subpaths.length === 0 ? last : subpaths[0];
1252
+ if (Object.hasOwn(reserved, firstPieceOfPath) && !this.options.suppressReservedKeysWarning) {
1247
1253
  const errorMessage = `\`${firstPieceOfPath}\` is a reserved schema pathname and may break some functionality. ` +
1248
1254
  'You are allowed to use it, but use at your own risk. ' +
1249
1255
  'To disable this warning pass `suppressReservedKeysWarning` as a schema option.';
@@ -1256,8 +1262,6 @@ Schema.prototype.path = function(path, obj) {
1256
1262
  }
1257
1263
 
1258
1264
  // update the tree
1259
- const subpaths = path.split(/\./);
1260
- const last = subpaths.pop();
1261
1265
  let branch = this.tree;
1262
1266
  let fullPath = '';
1263
1267
 
@@ -1488,7 +1492,7 @@ function getMapPath(schema, path) {
1488
1492
  } else if (val.schema && path.startsWith(cleanPath + '.')) {
1489
1493
  let remnant = path.slice(cleanPath.length + 1);
1490
1494
  remnant = remnant.slice(remnant.indexOf('.') + 1);
1491
- return val.schema.paths[remnant];
1495
+ return val.schema.path(remnant);
1492
1496
  } else if (val.$isSchemaMap && path.startsWith(cleanPath + '.')) {
1493
1497
  let remnant = path.slice(cleanPath.length + 1);
1494
1498
  remnant = remnant.slice(remnant.indexOf('.') + 1);
@@ -2986,7 +2990,7 @@ Schema.prototype._getPathType = function(path) {
2986
2990
  };
2987
2991
  }
2988
2992
  return { schema: foundschema, pathType: 'real' };
2989
- } else if (p === parts.length && schema.nested[trypath]) {
2993
+ } else if (p === parts.length && Object.hasOwn(schema.nested, trypath)) {
2990
2994
  return { schema: schema, pathType: 'nested' };
2991
2995
  }
2992
2996
  }
@@ -12,6 +12,7 @@ const arrayPathSymbol = require('../../../helpers/symbols').arrayPathSymbol;
12
12
  const arraySchemaSymbol = require('../../../helpers/symbols').arraySchemaSymbol;
13
13
  const documentArrayParent = require('../../../helpers/symbols').documentArrayParent;
14
14
 
15
+ const _baseReverse = Array.prototype.reverse;
15
16
  const _baseToString = Array.prototype.toString;
16
17
 
17
18
  const methods = {
@@ -209,7 +210,7 @@ const methods = {
209
210
  },
210
211
 
211
212
  /**
212
- * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
213
+ * Wraps [`Array#push`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
213
214
  *
214
215
  * @param {...Object} [args]
215
216
  * @api public
@@ -218,8 +219,37 @@ const methods = {
218
219
  */
219
220
 
220
221
  push() {
222
+ const shouldReindex = _shouldReindexAfterPush(arguments);
221
223
  const ret = ArrayMethods.push.apply(this, arguments);
222
224
 
225
+ if (shouldReindex) {
226
+ _updateSubdocIndexes(this);
227
+ }
228
+ _updateParentPopulated(this);
229
+
230
+ return ret;
231
+ },
232
+
233
+ /**
234
+ * Wraps [`Array#pop`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking.
235
+ * @api private
236
+ */
237
+
238
+ pop() {
239
+ const ret = ArrayMethods.pop.apply(this, arguments);
240
+
241
+ _updateParentPopulated(this);
242
+
243
+ return ret;
244
+ },
245
+
246
+ /*!
247
+ * ignore
248
+ */
249
+
250
+ $pop() {
251
+ const ret = ArrayMethods.$pop.apply(this, arguments);
252
+
223
253
  _updateParentPopulated(this);
224
254
 
225
255
  return ret;
@@ -237,32 +267,96 @@ const methods = {
237
267
  pull() {
238
268
  const ret = ArrayMethods.pull.apply(this, arguments);
239
269
 
270
+ _updateSubdocIndexes(this);
240
271
  _updateParentPopulated(this);
241
272
 
242
273
  return ret;
243
274
  },
244
275
 
245
276
  /**
246
- * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
277
+ * Wraps [`Array#shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) with proper change tracking.
247
278
  * @api private
248
279
  */
249
280
 
250
281
  shift() {
251
282
  const ret = ArrayMethods.shift.apply(this, arguments);
252
283
 
284
+ _updateSubdocIndexes(this);
285
+ _updateParentPopulated(this);
286
+
287
+ return ret;
288
+ },
289
+
290
+ /*!
291
+ * ignore
292
+ */
293
+
294
+ $shift() {
295
+ const ret = ArrayMethods.$shift.apply(this, arguments);
296
+
297
+ _updateSubdocIndexes(this);
253
298
  _updateParentPopulated(this);
254
299
 
255
300
  return ret;
256
301
  },
257
302
 
258
303
  /**
259
- * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
304
+ * Wraps [`Array#splice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
260
305
  * @api private
261
306
  */
262
307
 
263
308
  splice() {
264
309
  const ret = ArrayMethods.splice.apply(this, arguments);
265
310
 
311
+ _updateSubdocIndexes(this);
312
+ _updateParentPopulated(this);
313
+
314
+ return ret;
315
+ },
316
+
317
+ /**
318
+ * Wraps [`Array#reverse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse)
319
+ * with proper change tracking.
320
+ * @api private
321
+ */
322
+
323
+ reverse() {
324
+ _baseReverse.call(this.__array);
325
+ this._registerAtomic('$set', this);
326
+
327
+ _updateSubdocIndexes(this);
328
+ _updateParentPopulated(this);
329
+
330
+ return this;
331
+ },
332
+
333
+ /**
334
+ * Wraps [`Array#sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
335
+ * with proper change tracking.
336
+ *
337
+ * @param {Function} [compareFunction]
338
+ * @api private
339
+ */
340
+
341
+ sort() {
342
+ const ret = ArrayMethods.sort.apply(this, arguments);
343
+
344
+ _updateSubdocIndexes(this);
345
+ _updateParentPopulated(this);
346
+
347
+ return ret;
348
+ },
349
+
350
+ /**
351
+ * Wraps [`Array#unshift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift)
352
+ * with proper change tracking.
353
+ * @api private
354
+ */
355
+
356
+ unshift() {
357
+ const ret = ArrayMethods.unshift.apply(this, arguments);
358
+
359
+ _updateSubdocIndexes(this);
266
360
  _updateParentPopulated(this);
267
361
 
268
362
  return ret;
@@ -387,6 +481,26 @@ const methods = {
387
481
 
388
482
  module.exports = methods;
389
483
 
484
+ /*!
485
+ * ignore
486
+ */
487
+
488
+ function _updateSubdocIndexes(arr) {
489
+ const rawArray = utils.isMongooseArray(arr) ? arr.__array : arr;
490
+
491
+ for (let i = 0; i < rawArray.length; ++i) {
492
+ if (typeof rawArray[i]?.$setIndex === 'function') {
493
+ rawArray[i].$setIndex(i);
494
+ }
495
+ }
496
+ }
497
+
498
+ function _shouldReindexAfterPush(args) {
499
+ return args[0] != null &&
500
+ utils.hasUserDefinedProperty(args[0], '$each') &&
501
+ args[0].$position != null;
502
+ }
503
+
390
504
  /**
391
505
  * If this is a document array, each element may contain single
392
506
  * populated paths, so we need to modify the top-level document's
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.24.0",
4
+ "version": "8.24.1",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",