mongoose 8.19.4 → 9.0.0-rc0

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.
Files changed (105) hide show
  1. package/eslint.config.mjs +198 -0
  2. package/lib/aggregate.js +17 -73
  3. package/lib/cast/bigint.js +1 -1
  4. package/lib/cast/double.js +1 -1
  5. package/lib/cast/uuid.js +5 -48
  6. package/lib/cast.js +3 -3
  7. package/lib/connection.js +0 -1
  8. package/lib/cursor/aggregationCursor.js +14 -24
  9. package/lib/cursor/queryCursor.js +7 -14
  10. package/lib/document.js +125 -121
  11. package/lib/drivers/node-mongodb-native/connection.js +3 -10
  12. package/lib/error/divergentArray.js +2 -2
  13. package/lib/error/objectParameter.js +1 -2
  14. package/lib/error/validation.js +0 -8
  15. package/lib/helpers/clone.js +1 -1
  16. package/lib/helpers/common.js +1 -1
  17. package/lib/helpers/discriminator/mergeDiscriminatorSchema.js +10 -0
  18. package/lib/helpers/indexes/isIndexEqual.js +0 -1
  19. package/lib/helpers/model/applyDefaultsToPOJO.js +2 -2
  20. package/lib/helpers/model/applyHooks.js +43 -53
  21. package/lib/helpers/model/applyMethods.js +2 -2
  22. package/lib/helpers/model/applyStaticHooks.js +1 -48
  23. package/lib/helpers/model/castBulkWrite.js +1 -1
  24. package/lib/helpers/parallelLimit.js +18 -36
  25. package/lib/helpers/pluralize.js +3 -3
  26. package/lib/helpers/populate/assignRawDocsToIdStructure.js +1 -8
  27. package/lib/helpers/populate/createPopulateQueryFilter.js +1 -1
  28. package/lib/helpers/populate/getModelsMapForPopulate.js +17 -9
  29. package/lib/helpers/populate/getSchemaTypes.js +5 -5
  30. package/lib/helpers/query/cast$expr.js +8 -10
  31. package/lib/helpers/query/castFilterPath.js +1 -1
  32. package/lib/helpers/query/castUpdate.js +15 -13
  33. package/lib/helpers/query/getEmbeddedDiscriminatorPath.js +1 -1
  34. package/lib/helpers/schema/applyPlugins.js +1 -1
  35. package/lib/helpers/schema/getIndexes.js +1 -7
  36. package/lib/helpers/timestamps/setupTimestamps.js +3 -6
  37. package/lib/helpers/updateValidators.js +57 -111
  38. package/lib/model.js +419 -607
  39. package/lib/mongoose.js +41 -13
  40. package/lib/plugins/saveSubdocs.js +24 -51
  41. package/lib/plugins/sharding.js +5 -4
  42. package/lib/plugins/validateBeforeSave.js +3 -13
  43. package/lib/query.js +101 -145
  44. package/lib/queryHelpers.js +2 -2
  45. package/lib/schema/array.js +46 -84
  46. package/lib/schema/bigint.js +4 -2
  47. package/lib/schema/boolean.js +4 -2
  48. package/lib/schema/buffer.js +4 -2
  49. package/lib/schema/date.js +4 -2
  50. package/lib/schema/decimal128.js +4 -2
  51. package/lib/schema/documentArray.js +66 -91
  52. package/lib/schema/documentArrayElement.js +18 -11
  53. package/lib/schema/double.js +4 -2
  54. package/lib/schema/int32.js +4 -2
  55. package/lib/schema/map.js +87 -6
  56. package/lib/schema/mixed.js +4 -2
  57. package/lib/schema/number.js +4 -2
  58. package/lib/schema/objectId.js +4 -2
  59. package/lib/schema/string.js +5 -3
  60. package/lib/schema/subdocument.js +27 -31
  61. package/lib/schema/union.js +11 -3
  62. package/lib/schema/uuid.js +4 -23
  63. package/lib/schema.js +91 -91
  64. package/lib/schemaType.js +67 -59
  65. package/lib/types/array/index.js +2 -2
  66. package/lib/types/array/methods/index.js +4 -4
  67. package/lib/types/arraySubdocument.js +1 -1
  68. package/lib/types/buffer.js +10 -10
  69. package/lib/types/decimal128.js +1 -1
  70. package/lib/types/documentArray/index.js +1 -1
  71. package/lib/types/documentArray/methods/index.js +32 -18
  72. package/lib/types/double.js +1 -1
  73. package/lib/types/map.js +1 -2
  74. package/lib/types/objectid.js +1 -1
  75. package/lib/types/subdocument.js +15 -43
  76. package/lib/types/uuid.js +1 -1
  77. package/lib/utils.js +1 -8
  78. package/lib/validOptions.js +3 -3
  79. package/package.json +11 -24
  80. package/types/connection.d.ts +20 -11
  81. package/types/document.d.ts +96 -27
  82. package/types/index.d.ts +143 -39
  83. package/types/inferhydrateddoctype.d.ts +115 -0
  84. package/types/inferrawdoctype.d.ts +99 -75
  85. package/types/inferschematype.d.ts +17 -3
  86. package/types/middlewares.d.ts +0 -2
  87. package/types/models.d.ts +131 -199
  88. package/types/mongooseoptions.d.ts +6 -5
  89. package/types/pipelinestage.d.ts +1 -1
  90. package/types/query.d.ts +71 -139
  91. package/types/schemaoptions.d.ts +1 -1
  92. package/types/schematypes.d.ts +14 -10
  93. package/types/types.d.ts +3 -4
  94. package/types/utility.d.ts +68 -48
  95. package/types/validation.d.ts +18 -14
  96. package/browser.js +0 -8
  97. package/dist/browser.umd.js +0 -2
  98. package/lib/browser.js +0 -141
  99. package/lib/browserDocument.js +0 -101
  100. package/lib/documentProvider.js +0 -30
  101. package/lib/drivers/browser/binary.js +0 -14
  102. package/lib/drivers/browser/decimal128.js +0 -7
  103. package/lib/drivers/browser/index.js +0 -13
  104. package/lib/drivers/browser/objectid.js +0 -29
  105. package/lib/helpers/promiseOrCallback.js +0 -54
package/lib/schema.js CHANGED
@@ -23,6 +23,7 @@ const merge = require('./helpers/schema/merge');
23
23
  const mpath = require('mpath');
24
24
  const setPopulatedVirtualValue = require('./helpers/populate/setPopulatedVirtualValue');
25
25
  const setupTimestamps = require('./helpers/timestamps/setupTimestamps');
26
+ const symbols = require('./schema/symbols');
26
27
  const utils = require('./utils');
27
28
  const validateRef = require('./helpers/populate/validateRef');
28
29
 
@@ -31,7 +32,7 @@ const hasNumericSubpathRegex = /\.\d+(\.|$)/;
31
32
  let MongooseTypes;
32
33
 
33
34
  const queryHooks = require('./constants').queryMiddlewareFunctions;
34
- const documentHooks = require('./helpers/model/applyHooks').middlewareFunctions;
35
+ const documentHooks = require('./constants').documentMiddlewareFunctions;
35
36
  const hookNames = queryHooks.concat(documentHooks).
36
37
  reduce((s, hook) => s.add(hook), new Set());
37
38
 
@@ -372,6 +373,29 @@ Schema.prototype.paths;
372
373
 
373
374
  Schema.prototype.tree;
374
375
 
376
+ /**
377
+ * Creates a new schema with the given definition and options. Equivalent to `new Schema(definition, options)`.
378
+ *
379
+ * `Schema.create()` is primarily useful for automatic schema type inference in TypeScript.
380
+ *
381
+ * #### Example:
382
+ *
383
+ * const schema = Schema.create({ name: String }, { toObject: { virtuals: true } });
384
+ * // Equivalent:
385
+ * const schema2 = new Schema({ name: String }, { toObject: { virtuals: true } });
386
+ *
387
+ * @param {Object} definition
388
+ * @param {Object} [options]
389
+ * @return {Schema} the new schema
390
+ * @api public
391
+ * @memberOf Schema
392
+ * @static
393
+ */
394
+
395
+ Schema.create = function create(definition, options) {
396
+ return new Schema(definition, options);
397
+ };
398
+
375
399
  /**
376
400
  * Returns a deep copy of the schema
377
401
  *
@@ -660,6 +684,33 @@ Schema.prototype.discriminator = function(name, schema, options) {
660
684
  return this;
661
685
  };
662
686
 
687
+ /*!
688
+ * Get the document middleware for this schema, filtering out any hooks that are specific to queries.
689
+ */
690
+ Schema.prototype._getDocumentMiddleware = function _getDocumentMiddleware() {
691
+ return this.s.hooks.
692
+ filter(hook => {
693
+ if (hook.name === 'updateOne' || hook.name === 'deleteOne') {
694
+ return !!hook['document'];
695
+ }
696
+ if (hook.name === 'remove' || hook.name === 'init') {
697
+ return hook['document'] == null || !!hook['document'];
698
+ }
699
+ if (hook.query != null || hook.document != null) {
700
+ return hook.document !== false;
701
+ }
702
+ return true;
703
+ }).
704
+ filter(hook => {
705
+ // If user has overwritten the method, don't apply built-in middleware
706
+ if (this.methods[hook.name]) {
707
+ return !hook.fn[symbols.builtInMiddleware];
708
+ }
709
+
710
+ return true;
711
+ });
712
+ };
713
+
663
714
  /*!
664
715
  * Get this schema's default toObject/toJSON options, including Mongoose global
665
716
  * options.
@@ -1300,7 +1351,7 @@ Schema.prototype.path = function(path, obj) {
1300
1351
  if (schemaType.$__schemaType.$isSingleNested) {
1301
1352
  this.childSchemas.push({
1302
1353
  schema: schemaType.$__schemaType.schema,
1303
- model: schemaType.$__schemaType.caster,
1354
+ model: schemaType.$__schemaType.Constructor,
1304
1355
  path: path
1305
1356
  });
1306
1357
  }
@@ -1329,10 +1380,10 @@ Schema.prototype.path = function(path, obj) {
1329
1380
  value: this.base
1330
1381
  });
1331
1382
 
1332
- schemaType.caster.base = this.base;
1383
+ schemaType.Constructor.base = this.base;
1333
1384
  this.childSchemas.push({
1334
1385
  schema: schemaType.schema,
1335
- model: schemaType.caster,
1386
+ model: schemaType.Constructor,
1336
1387
  path: path
1337
1388
  });
1338
1389
  } else if (schemaType.$isMongooseDocumentArray) {
@@ -1343,15 +1394,15 @@ Schema.prototype.path = function(path, obj) {
1343
1394
  value: this.base
1344
1395
  });
1345
1396
 
1346
- schemaType.casterConstructor.base = this.base;
1397
+ schemaType.Constructor.base = this.base;
1347
1398
  this.childSchemas.push({
1348
1399
  schema: schemaType.schema,
1349
- model: schemaType.casterConstructor,
1400
+ model: schemaType.Constructor,
1350
1401
  path: path
1351
1402
  });
1352
1403
  }
1353
1404
 
1354
- if (schemaType.$isMongooseArray && schemaType.caster instanceof SchemaType) {
1405
+ if (schemaType.$isMongooseArray && !schemaType.$isMongooseDocumentArray) {
1355
1406
  let arrayPath = path;
1356
1407
  let _schemaType = schemaType;
1357
1408
 
@@ -1359,16 +1410,9 @@ Schema.prototype.path = function(path, obj) {
1359
1410
  while (_schemaType.$isMongooseArray) {
1360
1411
  arrayPath = arrayPath + '.$';
1361
1412
 
1362
- // Skip arrays of document arrays
1363
- if (_schemaType.$isMongooseDocumentArray) {
1364
- _schemaType.$embeddedSchemaType._arrayPath = arrayPath;
1365
- _schemaType.$embeddedSchemaType._arrayParentPath = path;
1366
- _schemaType = _schemaType.$embeddedSchemaType;
1367
- } else {
1368
- _schemaType.caster._arrayPath = arrayPath;
1369
- _schemaType.caster._arrayParentPath = path;
1370
- _schemaType = _schemaType.caster;
1371
- }
1413
+ _schemaType.embeddedSchemaType._arrayPath = arrayPath;
1414
+ _schemaType.embeddedSchemaType._arrayParentPath = path;
1415
+ _schemaType = _schemaType.embeddedSchemaType;
1372
1416
 
1373
1417
  this.subpaths[arrayPath] = _schemaType;
1374
1418
  }
@@ -1420,13 +1464,13 @@ Schema.prototype._gatherChildSchemas = function _gatherChildSchemas() {
1420
1464
  if (schematype.$isMongooseDocumentArray || schematype.$isSingleNested) {
1421
1465
  childSchemas.push({
1422
1466
  schema: schematype.schema,
1423
- model: schematype.caster,
1467
+ model: schematype.Constructor,
1424
1468
  path: path
1425
1469
  });
1426
1470
  } else if (schematype.$isSchemaMap && schematype.$__schemaType.$isSingleNested) {
1427
1471
  childSchemas.push({
1428
1472
  schema: schematype.$__schemaType.schema,
1429
- model: schematype.$__schemaType.caster,
1473
+ model: schematype.$__schemaType.Constructor,
1430
1474
  path: path
1431
1475
  });
1432
1476
  }
@@ -1572,7 +1616,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1572
1616
  let name;
1573
1617
 
1574
1618
  if (utils.isPOJO(type) || type === 'mixed') {
1575
- return new MongooseTypes.Mixed(path, obj);
1619
+ return new MongooseTypes.Mixed(path, obj, null, this);
1576
1620
  }
1577
1621
 
1578
1622
  if (Array.isArray(type) || type === Array || type === 'array' || type === MongooseTypes.Array) {
@@ -1595,7 +1639,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1595
1639
  `${path}: new Schema(...)`);
1596
1640
  }
1597
1641
  }
1598
- return new MongooseTypes.DocumentArray(path, cast, obj);
1642
+ return new MongooseTypes.DocumentArray(path, cast, obj, null, this);
1599
1643
  }
1600
1644
  if (cast &&
1601
1645
  cast[options.typeKey] &&
@@ -1612,14 +1656,14 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1612
1656
  `${path}: new Schema(...)`);
1613
1657
  }
1614
1658
  }
1615
- return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast);
1659
+ return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast, this);
1616
1660
  }
1617
1661
  if (typeof cast !== 'undefined') {
1618
1662
  if (Array.isArray(cast) || cast.type === Array || cast.type == 'Array') {
1619
1663
  if (cast && cast.type == 'Array') {
1620
1664
  cast.type = Array;
1621
1665
  }
1622
- return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj);
1666
+ return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj, null, this);
1623
1667
  }
1624
1668
  }
1625
1669
 
@@ -1660,10 +1704,10 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1660
1704
 
1661
1705
  const childSchema = new Schema(castFromTypeKey, childSchemaOptions);
1662
1706
  childSchema.$implicitlyCreated = true;
1663
- return new MongooseTypes.DocumentArray(path, childSchema, obj);
1707
+ return new MongooseTypes.DocumentArray(path, childSchema, obj, null, this);
1664
1708
  } else {
1665
1709
  // Special case: empty object becomes mixed
1666
- return new MongooseTypes.Array(path, MongooseTypes.Mixed, obj);
1710
+ return new MongooseTypes.Array(path, MongooseTypes.Mixed, obj, null, this);
1667
1711
  }
1668
1712
  }
1669
1713
 
@@ -1672,7 +1716,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1672
1716
  ? cast[options.typeKey]
1673
1717
  : cast;
1674
1718
  if (Array.isArray(type)) {
1675
- return new MongooseTypes.Array(path, this.interpretAsType(path, type, options), obj);
1719
+ return new MongooseTypes.Array(path, this.interpretAsType(path, type, options), obj, null, this);
1676
1720
  }
1677
1721
 
1678
1722
  name = typeof type === 'string'
@@ -1700,11 +1744,11 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1700
1744
  }
1701
1745
  }
1702
1746
 
1703
- return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj, options);
1747
+ return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj, options, this);
1704
1748
  }
1705
1749
 
1706
1750
  if (type && type.instanceOfSchema) {
1707
- return new MongooseTypes.Subdocument(type, path, obj);
1751
+ return new MongooseTypes.Subdocument(type, path, obj, this);
1708
1752
  }
1709
1753
 
1710
1754
  if (Buffer.isBuffer(type)) {
@@ -1743,53 +1787,11 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1743
1787
  'https://bit.ly/mongoose-schematypes for a list of valid schema types.');
1744
1788
  }
1745
1789
 
1746
- if (name === 'Union') {
1747
- obj.parentSchema = this;
1748
- }
1749
- const schemaType = new MongooseTypes[name](path, obj, options);
1750
-
1751
- if (schemaType.$isSchemaMap) {
1752
- createMapNestedSchemaType(this, schemaType, path, obj, options);
1753
- }
1790
+ const schemaType = new MongooseTypes[name](path, obj, options, this);
1754
1791
 
1755
1792
  return schemaType;
1756
1793
  };
1757
1794
 
1758
- /*!
1759
- * ignore
1760
- */
1761
-
1762
- function createMapNestedSchemaType(schema, schemaType, path, obj, options) {
1763
- const mapPath = path + '.$*';
1764
- let _mapType = { type: {} };
1765
- if (utils.hasUserDefinedProperty(obj, 'of')) {
1766
- const isInlineSchema = utils.isPOJO(obj.of) &&
1767
- Object.keys(obj.of).length > 0 &&
1768
- !utils.hasUserDefinedProperty(obj.of, schema.options.typeKey);
1769
- if (isInlineSchema) {
1770
- _mapType = { [schema.options.typeKey]: new Schema(obj.of) };
1771
- } else if (utils.isPOJO(obj.of)) {
1772
- _mapType = Object.assign({}, obj.of);
1773
- } else {
1774
- _mapType = { [schema.options.typeKey]: obj.of };
1775
- }
1776
-
1777
- if (_mapType[schema.options.typeKey] && _mapType[schema.options.typeKey].instanceOfSchema) {
1778
- const subdocumentSchema = _mapType[schema.options.typeKey];
1779
- subdocumentSchema.eachPath((subpath, type) => {
1780
- if (type.options.select === true || type.options.select === false) {
1781
- throw new MongooseError('Cannot use schema-level projections (`select: true` or `select: false`) within maps at path "' + path + '.' + subpath + '"');
1782
- }
1783
- });
1784
- }
1785
-
1786
- if (utils.hasUserDefinedProperty(obj, 'ref')) {
1787
- _mapType.ref = obj.ref;
1788
- }
1789
- }
1790
- schemaType.$__schemaType = schema.interpretAsType(mapPath, _mapType, options);
1791
- }
1792
-
1793
1795
  /**
1794
1796
  * Iterates the schemas paths similar to Array#forEach.
1795
1797
  *
@@ -1987,10 +1989,10 @@ function getPositionalPathType(self, path, cleanPath) {
1987
1989
 
1988
1990
  if (i === last && val && !/\D/.test(subpath)) {
1989
1991
  if (val.$isMongooseDocumentArray) {
1990
- val = val.$embeddedSchemaType;
1992
+ val = val.embeddedSchemaType;
1991
1993
  } else if (val instanceof MongooseTypes.Array) {
1992
1994
  // StringSchema, NumberSchema, etc
1993
- val = val.caster;
1995
+ val = val.embeddedSchemaType;
1994
1996
  } else {
1995
1997
  val = undefined;
1996
1998
  }
@@ -2001,7 +2003,7 @@ function getPositionalPathType(self, path, cleanPath) {
2001
2003
  if (!/\D/.test(subpath)) {
2002
2004
  // Nested array
2003
2005
  if (val instanceof MongooseTypes.Array && i !== last) {
2004
- val = val.caster;
2006
+ val = val.embeddedSchemaType;
2005
2007
  }
2006
2008
  continue;
2007
2009
  }
@@ -2064,23 +2066,21 @@ Schema.prototype.queue = function(name, args) {
2064
2066
  *
2065
2067
  * const toySchema = new Schema({ name: String, created: Date });
2066
2068
  *
2067
- * toySchema.pre('save', function(next) {
2069
+ * toySchema.pre('save', function() {
2068
2070
  * if (!this.created) this.created = new Date;
2069
- * next();
2070
2071
  * });
2071
2072
  *
2072
- * toySchema.pre('validate', function(next) {
2073
+ * toySchema.pre('validate', function() {
2073
2074
  * if (this.name !== 'Woody') this.name = 'Woody';
2074
- * next();
2075
2075
  * });
2076
2076
  *
2077
2077
  * // Equivalent to calling `pre()` on `find`, `findOne`, `findOneAndUpdate`.
2078
- * toySchema.pre(/^find/, function(next) {
2078
+ * toySchema.pre(/^find/, function() {
2079
2079
  * console.log(this.getFilter());
2080
2080
  * });
2081
2081
  *
2082
2082
  * // Equivalent to calling `pre()` on `updateOne`, `findOneAndUpdate`.
2083
- * toySchema.pre(['updateOne', 'findOneAndUpdate'], function(next) {
2083
+ * toySchema.pre(['updateOne', 'findOneAndUpdate'], function() {
2084
2084
  * console.log(this.getFilter());
2085
2085
  * });
2086
2086
  *
@@ -2497,8 +2497,8 @@ Object.defineProperty(Schema, 'indexTypes', {
2497
2497
  * registeredAt: { type: Date, index: true }
2498
2498
  * });
2499
2499
  *
2500
- * // [ [ { email: 1 }, { unique: true, background: true } ],
2501
- * // [ { registeredAt: 1 }, { background: true } ] ]
2500
+ * // [ [ { email: 1 }, { unique: true } ],
2501
+ * // [ { registeredAt: 1 }, {} ] ]
2502
2502
  * userSchema.indexes();
2503
2503
  *
2504
2504
  * [Plugins](https://mongoosejs.com/docs/plugins.html) can use the return value of this function to modify a schema's indexes.
@@ -2896,11 +2896,11 @@ Schema.prototype._getSchema = function(path) {
2896
2896
  if (foundschema) {
2897
2897
  resultPath.push(trypath);
2898
2898
 
2899
- if (foundschema.caster) {
2899
+ if (foundschema.embeddedSchemaType || foundschema.Constructor) {
2900
2900
  // array of Mixed?
2901
- if (foundschema.caster instanceof MongooseTypes.Mixed) {
2902
- foundschema.caster.$fullPath = resultPath.join('.');
2903
- return foundschema.caster;
2901
+ if (foundschema.embeddedSchemaType instanceof MongooseTypes.Mixed) {
2902
+ foundschema.embeddedSchemaType.$fullPath = resultPath.join('.');
2903
+ return foundschema.embeddedSchemaType;
2904
2904
  }
2905
2905
 
2906
2906
  // Now that we found the array, we need to check if there
@@ -2910,8 +2910,8 @@ Schema.prototype._getSchema = function(path) {
2910
2910
  // If there is no foundschema.schema we are dealing with
2911
2911
  // a path like array.$
2912
2912
  if (p !== parts.length) {
2913
- if (p + 1 === parts.length && foundschema.$embeddedSchemaType && (parts[p] === '$' || isArrayFilter(parts[p]))) {
2914
- return foundschema.$embeddedSchemaType;
2913
+ if (p + 1 === parts.length && foundschema.embeddedSchemaType && (parts[p] === '$' || isArrayFilter(parts[p]))) {
2914
+ return foundschema.embeddedSchemaType;
2915
2915
  }
2916
2916
 
2917
2917
  if (foundschema.schema) {
@@ -2919,7 +2919,7 @@ Schema.prototype._getSchema = function(path) {
2919
2919
  if (parts[p] === '$' || isArrayFilter(parts[p])) {
2920
2920
  if (p + 1 === parts.length) {
2921
2921
  // comments.$
2922
- return foundschema.$embeddedSchemaType;
2922
+ return foundschema.embeddedSchemaType;
2923
2923
  }
2924
2924
  // comments.$.comments.$.title
2925
2925
  ret = search(parts.slice(p + 1), foundschema.schema);
@@ -2999,9 +2999,9 @@ Schema.prototype._getPathType = function(path) {
2999
2999
  trypath = parts.slice(0, p).join('.');
3000
3000
  foundschema = schema.path(trypath);
3001
3001
  if (foundschema) {
3002
- if (foundschema.caster) {
3002
+ if (foundschema.embeddedSchemaType || foundschema.Constructor) {
3003
3003
  // array of Mixed?
3004
- if (foundschema.caster instanceof MongooseTypes.Mixed) {
3004
+ if (foundschema.embeddedSchemaType instanceof MongooseTypes.Mixed) {
3005
3005
  return { schema: foundschema, pathType: 'mixed' };
3006
3006
  }
3007
3007
 
package/lib/schemaType.js CHANGED
@@ -12,7 +12,6 @@ const clone = require('./helpers/clone');
12
12
  const handleImmutable = require('./helpers/schematype/handleImmutable');
13
13
  const isAsyncFunction = require('./helpers/isAsyncFunction');
14
14
  const isSimpleValidator = require('./helpers/isSimpleValidator');
15
- const immediate = require('./helpers/immediate');
16
15
  const schemaTypeSymbol = require('./helpers/symbols').schemaTypeSymbol;
17
16
  const utils = require('./utils');
18
17
  const validatorErrorSymbol = require('./helpers/symbols').validatorErrorSymbol;
@@ -37,10 +36,11 @@ const setOptionsForDefaults = { _skipMarkModified: true };
37
36
  * @param {String} path
38
37
  * @param {SchemaTypeOptions} [options] See [SchemaTypeOptions docs](https://mongoosejs.com/docs/api/schematypeoptions.html)
39
38
  * @param {String} [instance]
39
+ * @param {Schema} [parentSchema]
40
40
  * @api public
41
41
  */
42
42
 
43
- function SchemaType(path, options, instance) {
43
+ function SchemaType(path, options, instance, parentSchema) {
44
44
  this[schemaTypeSymbol] = true;
45
45
  this.path = path;
46
46
  this.instance = instance;
@@ -73,6 +73,7 @@ function SchemaType(path, options, instance) {
73
73
 
74
74
  const Options = this.OptionsConstructor || SchemaTypeOptions;
75
75
  this.options = new Options(options);
76
+ this.parentSchema = parentSchema;
76
77
  this._index = null;
77
78
 
78
79
  if (utils.hasUserDefinedProperty(this.options, 'immutable')) {
@@ -157,6 +158,30 @@ SchemaType.prototype.OptionsConstructor = SchemaTypeOptions;
157
158
 
158
159
  SchemaType.prototype.path;
159
160
 
161
+ /**
162
+ * Returns a plain JavaScript object representing this SchemaType.
163
+ *
164
+ * Typically used by `JSON.stringify()` or when calling `.toJSON()` on a SchemaType instance.
165
+ * Omits certain internal properties such as `parentSchema` that can cause circular references.
166
+ *
167
+ * #### Example:
168
+ *
169
+ * const schema = new Schema({ name: String });
170
+ * const schematype = schema.path('name');
171
+ * console.log(schematype.toJSON());
172
+ *
173
+ * @function toJSON
174
+ * @memberOf SchemaType
175
+ * @instance
176
+ * @api public
177
+ */
178
+
179
+ SchemaType.prototype.toJSON = function toJSON() {
180
+ const res = { ...this };
181
+ delete res.parentSchema;
182
+ return res;
183
+ };
184
+
160
185
  /**
161
186
  * The validators that Mongoose should run to validate properties at this SchemaType's path.
162
187
  *
@@ -419,13 +444,6 @@ SchemaType.prototype.default = function(val) {
419
444
  * s.path('my.date').index({ expires: 60 });
420
445
  * s.path('my.path').index({ unique: true, sparse: true });
421
446
  *
422
- * #### Note:
423
- *
424
- * _Indexes are created [in the background](https://www.mongodb.com/docs/manual/core/index-creation/#index-creation-background)
425
- * by default. If `background` is set to `false`, MongoDB will not execute any
426
- * read/write operations you send until the index build.
427
- * Specify `background: false` to override Mongoose's default._
428
- *
429
447
  * @param {Object|Boolean|String|Number} options
430
448
  * @return {SchemaType} this
431
449
  * @api public
@@ -1315,7 +1333,6 @@ SchemaType.prototype.select = function select(val) {
1315
1333
  * Performs a validation of `value` using the validators declared for this SchemaType.
1316
1334
  *
1317
1335
  * @param {Any} value
1318
- * @param {Function} callback
1319
1336
  * @param {Object} scope
1320
1337
  * @param {Object} [options]
1321
1338
  * @param {String} [options.path]
@@ -1323,28 +1340,20 @@ SchemaType.prototype.select = function select(val) {
1323
1340
  * @api public
1324
1341
  */
1325
1342
 
1326
- SchemaType.prototype.doValidate = function(value, fn, scope, options) {
1343
+ SchemaType.prototype.doValidate = async function doValidate(value, scope, options) {
1327
1344
  let err = false;
1328
1345
  const path = this.path;
1329
- if (typeof fn !== 'function') {
1330
- throw new TypeError(`Must pass callback function to doValidate(), got ${typeof fn}`);
1331
- }
1332
1346
 
1333
1347
  // Avoid non-object `validators`
1334
1348
  const validators = this.validators.
1335
1349
  filter(v => typeof v === 'object' && v !== null);
1336
1350
 
1337
- let count = validators.length;
1338
-
1339
- if (!count) {
1340
- return fn(null);
1351
+ if (!validators.length) {
1352
+ return;
1341
1353
  }
1342
1354
 
1355
+ const promises = [];
1343
1356
  for (let i = 0, len = validators.length; i < len; ++i) {
1344
- if (err) {
1345
- break;
1346
- }
1347
-
1348
1357
  const v = validators[i];
1349
1358
  const validator = v.validator;
1350
1359
  let ok;
@@ -1360,17 +1369,19 @@ SchemaType.prototype.doValidate = function(value, fn, scope, options) {
1360
1369
  }
1361
1370
  }
1362
1371
 
1363
- if (validator instanceof RegExp) {
1364
- validate(validator.test(value), validatorProperties, scope);
1372
+ if (value === undefined && validator !== this.requiredValidator) {
1365
1373
  continue;
1366
1374
  }
1367
-
1368
- if (typeof validator !== 'function') {
1375
+ if (validator instanceof RegExp) {
1376
+ ok = validator.test(value);
1377
+ if (ok === false) {
1378
+ const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
1379
+ err = new ErrorConstructor(validatorProperties, scope);
1380
+ err[validatorErrorSymbol] = true;
1381
+ throw err;
1382
+ }
1369
1383
  continue;
1370
- }
1371
-
1372
- if (value === undefined && validator !== this.requiredValidator) {
1373
- validate(true, validatorProperties, scope);
1384
+ } else if (typeof validator !== 'function') {
1374
1385
  continue;
1375
1386
  }
1376
1387
 
@@ -1389,38 +1400,35 @@ SchemaType.prototype.doValidate = function(value, fn, scope, options) {
1389
1400
  }
1390
1401
 
1391
1402
  if (ok != null && typeof ok.then === 'function') {
1392
- ok.then(
1393
- function(ok) { validate(ok, validatorProperties, scope); },
1394
- function(error) {
1395
- validatorProperties.reason = error;
1396
- validatorProperties.message = error.message;
1397
- ok = false;
1398
- validate(ok, validatorProperties, scope);
1399
- });
1400
- } else {
1401
- validate(ok, validatorProperties, scope);
1402
- }
1403
- }
1404
-
1405
- function validate(ok, validatorProperties, scope) {
1406
- if (err) {
1407
- return;
1408
- }
1409
- if (ok === undefined || ok) {
1410
- if (--count <= 0) {
1411
- immediate(function() {
1412
- fn(null);
1413
- });
1414
- }
1415
- } else {
1403
+ promises.push(
1404
+ ok.then(
1405
+ function(ok) {
1406
+ if (ok === false) {
1407
+ const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
1408
+ err = new ErrorConstructor(validatorProperties, scope);
1409
+ err[validatorErrorSymbol] = true;
1410
+ throw err;
1411
+ }
1412
+ },
1413
+ function(error) {
1414
+ validatorProperties.reason = error;
1415
+ validatorProperties.message = error.message;
1416
+ ok = false;
1417
+ const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
1418
+ err = new ErrorConstructor(validatorProperties, scope);
1419
+ err[validatorErrorSymbol] = true;
1420
+ throw err;
1421
+ })
1422
+ );
1423
+ } else if (ok !== undefined && !ok) {
1416
1424
  const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError;
1417
1425
  err = new ErrorConstructor(validatorProperties, scope);
1418
1426
  err[validatorErrorSymbol] = true;
1419
- immediate(function() {
1420
- fn(err);
1421
- });
1427
+ throw err;
1422
1428
  }
1423
1429
  }
1430
+
1431
+ await Promise.all(promises);
1424
1432
  };
1425
1433
 
1426
1434
 
@@ -1751,7 +1759,7 @@ SchemaType.prototype.checkRequired = function(val) {
1751
1759
 
1752
1760
  SchemaType.prototype.clone = function() {
1753
1761
  const options = Object.assign({}, this.options);
1754
- const schematype = new this.constructor(this.path, options, this.instance);
1762
+ const schematype = new this.constructor(this.path, options, this.instance, this.parentSchema);
1755
1763
  schematype.validators = this.validators.slice();
1756
1764
  if (this.requiredValidator !== undefined) schematype.requiredValidator = this.requiredValidator;
1757
1765
  if (this.defaultValue !== undefined) schematype.defaultValue = this.defaultValue;
@@ -1785,7 +1793,7 @@ SchemaType.prototype.clone = function() {
1785
1793
  */
1786
1794
 
1787
1795
  SchemaType.prototype.getEmbeddedSchemaType = function getEmbeddedSchemaType() {
1788
- return this.$embeddedSchemaType;
1796
+ return this.embeddedSchemaType;
1789
1797
  };
1790
1798
 
1791
1799
  /*!
@@ -88,8 +88,8 @@ function MongooseArray(values, path, doc, schematype) {
88
88
  if (schematype && schematype.virtuals && schematype.virtuals.hasOwnProperty(prop)) {
89
89
  return schematype.virtuals[prop].applyGetters(undefined, target);
90
90
  }
91
- if (typeof prop === 'string' && numberRE.test(prop) && schematype?.$embeddedSchemaType != null) {
92
- return schematype.$embeddedSchemaType.applyGetters(__array[prop], doc);
91
+ if (typeof prop === 'string' && numberRE.test(prop) && schematype?.embeddedSchemaType != null) {
92
+ return schematype.embeddedSchemaType.applyGetters(__array[prop], doc);
93
93
  }
94
94
 
95
95
  return __array[prop];
@@ -251,10 +251,10 @@ const methods = {
251
251
  if (!isDisc) {
252
252
  value = new Model(value);
253
253
  }
254
- return this[arraySchemaSymbol].caster.applySetters(value, parent, true);
254
+ return this[arraySchemaSymbol].embeddedSchemaType.applySetters(value, parent, true);
255
255
  }
256
256
 
257
- return this[arraySchemaSymbol].caster.applySetters(value, parent, false);
257
+ return this[arraySchemaSymbol].embeddedSchemaType.applySetters(value, parent, false);
258
258
  },
259
259
 
260
260
  /**
@@ -1007,7 +1007,7 @@ function _minimizePath(obj, parts, i) {
1007
1007
  function _checkManualPopulation(arr, docs) {
1008
1008
  const ref = arr == null ?
1009
1009
  null :
1010
- arr[arraySchemaSymbol] && arr[arraySchemaSymbol].caster && arr[arraySchemaSymbol].caster.options && arr[arraySchemaSymbol].caster.options.ref || null;
1010
+ arr[arraySchemaSymbol]?.embeddedSchemaType?.options?.ref || null;
1011
1011
  if (arr.length === 0 &&
1012
1012
  docs.length !== 0) {
1013
1013
  if (_isAllSubdocs(docs, ref)) {
@@ -1025,7 +1025,7 @@ function _checkManualPopulation(arr, docs) {
1025
1025
  function _depopulateIfNecessary(arr, docs) {
1026
1026
  const ref = arr == null ?
1027
1027
  null :
1028
- arr[arraySchemaSymbol] && arr[arraySchemaSymbol].caster && arr[arraySchemaSymbol].caster.options && arr[arraySchemaSymbol].caster.options.ref || null;
1028
+ arr[arraySchemaSymbol]?.embeddedSchemaType?.options?.ref || null;
1029
1029
  const parentDoc = arr[arrayParentSymbol];
1030
1030
  const path = arr[arrayPathSymbol];
1031
1031
  if (!ref || !parentDoc.populated(path)) {
@@ -41,7 +41,7 @@ function ArraySubdocument(obj, parentArr, skipId, fields, index) {
41
41
  options = { isNew: true };
42
42
  }
43
43
 
44
- Subdocument.call(this, obj, fields, this[documentArrayParent], skipId, options);
44
+ Subdocument.call(this, obj, fields, this[documentArrayParent], options);
45
45
  }
46
46
 
47
47
  /*!