mongoose 8.6.3 → 8.7.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.
package/lib/cast.js CHANGED
@@ -65,10 +65,11 @@ module.exports = function cast(schema, obj, options, context) {
65
65
  if (!Array.isArray(val)) {
66
66
  throw new CastError('Array', val, path);
67
67
  }
68
- for (let k = 0; k < val.length; ++k) {
68
+ for (let k = val.length - 1; k >= 0; k--) {
69
69
  if (val[k] == null || typeof val[k] !== 'object') {
70
70
  throw new CastError('Object', val[k], path + '.' + k);
71
71
  }
72
+ const beforeCastKeysLength = Object.keys(val[k]).length;
72
73
  const discriminatorValue = val[k][schema.options.discriminatorKey];
73
74
  if (discriminatorValue == null) {
74
75
  val[k] = cast(schema, val[k], options, context);
@@ -76,6 +77,15 @@ module.exports = function cast(schema, obj, options, context) {
76
77
  const discriminatorSchema = getSchemaDiscriminatorByValue(context.schema, discriminatorValue);
77
78
  val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context);
78
79
  }
80
+
81
+ if (Object.keys(val[k]).length === 0 && beforeCastKeysLength !== 0) {
82
+ val.splice(k, 1);
83
+ }
84
+ }
85
+
86
+ // delete empty: {$or: []} -> {}
87
+ if (val.length === 0) {
88
+ delete obj[path];
79
89
  }
80
90
  } else if (path === '$where') {
81
91
  type = typeof val;
package/lib/connection.js CHANGED
@@ -106,6 +106,15 @@ Object.setPrototypeOf(Connection.prototype, EventEmitter.prototype);
106
106
 
107
107
  Object.defineProperty(Connection.prototype, 'readyState', {
108
108
  get: function() {
109
+ // If connection thinks it is connected, but we haven't received a heartbeat in 2 heartbeat intervals,
110
+ // that likely means the connection is stale (potentially due to frozen AWS Lambda container)
111
+ if (
112
+ this._readyState === STATES.connected &&
113
+ this._lastHeartbeatAt != null &&
114
+ typeof this.client?.topology?.s?.description?.heartbeatFrequencyMS === 'number' &&
115
+ Date.now() - this._lastHeartbeatAt >= this.client.topology.s.description.heartbeatFrequencyMS * 2) {
116
+ return STATES.disconnected;
117
+ }
109
118
  return this._readyState;
110
119
  },
111
120
  set: function(val) {
package/lib/constants.js CHANGED
@@ -34,3 +34,40 @@ const queryMiddlewareFunctions = queryOperations.concat([
34
34
  ]);
35
35
 
36
36
  exports.queryMiddlewareFunctions = queryMiddlewareFunctions;
37
+
38
+ /*!
39
+ * ignore
40
+ */
41
+
42
+ const aggregateMiddlewareFunctions = [
43
+ 'aggregate'
44
+ ];
45
+
46
+ exports.aggregateMiddlewareFunctions = aggregateMiddlewareFunctions;
47
+
48
+ /*!
49
+ * ignore
50
+ */
51
+
52
+ const modelMiddlewareFunctions = [
53
+ 'bulkWrite',
54
+ 'createCollection',
55
+ 'insertMany'
56
+ ];
57
+
58
+ exports.modelMiddlewareFunctions = modelMiddlewareFunctions;
59
+
60
+ /*!
61
+ * ignore
62
+ */
63
+
64
+ const documentMiddlewareFunctions = [
65
+ 'validate',
66
+ 'save',
67
+ 'remove',
68
+ 'updateOne',
69
+ 'deleteOne',
70
+ 'init'
71
+ ];
72
+
73
+ exports.documentMiddlewareFunctions = documentMiddlewareFunctions;
package/lib/document.js CHANGED
@@ -2689,7 +2689,7 @@ function _evaluateRequiredFunctions(doc) {
2689
2689
  * ignore
2690
2690
  */
2691
2691
 
2692
- function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2692
+ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate) {
2693
2693
  const doValidateOptions = {};
2694
2694
 
2695
2695
  _evaluateRequiredFunctions(doc);
@@ -2709,35 +2709,40 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2709
2709
  Object.keys(doc.$__.activePaths.getStatePaths('default')).forEach(addToPaths);
2710
2710
  function addToPaths(p) { paths.add(p); }
2711
2711
 
2712
- const subdocs = doc.$getAllSubdocs();
2713
- const modifiedPaths = doc.modifiedPaths();
2714
- for (const subdoc of subdocs) {
2715
- if (subdoc.$basePath) {
2716
- const fullPathToSubdoc = subdoc.$isSingleNested ? subdoc.$__pathRelativeToParent() : subdoc.$__fullPathWithIndexes();
2717
-
2718
- // Remove child paths for now, because we'll be validating the whole
2719
- // subdoc.
2720
- // The following is a faster take on looping through every path in `paths`
2721
- // and checking if the path starts with `fullPathToSubdoc` re: gh-13191
2722
- for (const modifiedPath of subdoc.modifiedPaths()) {
2723
- paths.delete(fullPathToSubdoc + '.' + modifiedPath);
2724
- }
2712
+ if (!isNestedValidate) {
2713
+ // If we're validating a subdocument, all this logic will run anyway on the top-level document, so skip for subdocuments
2714
+ const subdocs = doc.$getAllSubdocs();
2715
+ const modifiedPaths = doc.modifiedPaths();
2716
+ for (const subdoc of subdocs) {
2717
+ if (subdoc.$basePath) {
2718
+ const fullPathToSubdoc = subdoc.$isSingleNested ? subdoc.$__pathRelativeToParent() : subdoc.$__fullPathWithIndexes();
2719
+
2720
+ // Remove child paths for now, because we'll be validating the whole
2721
+ // subdoc.
2722
+ // The following is a faster take on looping through every path in `paths`
2723
+ // and checking if the path starts with `fullPathToSubdoc` re: gh-13191
2724
+ for (const modifiedPath of subdoc.modifiedPaths()) {
2725
+ paths.delete(fullPathToSubdoc + '.' + modifiedPath);
2726
+ }
2725
2727
 
2726
- if (doc.$isModified(fullPathToSubdoc, null, modifiedPaths) &&
2727
- !doc.isDirectModified(fullPathToSubdoc) &&
2728
- !doc.$isDefault(fullPathToSubdoc)) {
2729
- paths.add(fullPathToSubdoc);
2728
+ if (doc.$isModified(fullPathToSubdoc, null, modifiedPaths) &&
2729
+ // Avoid using isDirectModified() here because that does additional checks on whether the parent path
2730
+ // is direct modified, which can cause performance issues re: gh-14897
2731
+ !doc.$__.activePaths.getStatePaths('modify').hasOwnProperty(fullPathToSubdoc) &&
2732
+ !doc.$isDefault(fullPathToSubdoc)) {
2733
+ paths.add(fullPathToSubdoc);
2730
2734
 
2731
- if (doc.$__.pathsToScopes == null) {
2732
- doc.$__.pathsToScopes = {};
2733
- }
2734
- doc.$__.pathsToScopes[fullPathToSubdoc] = subdoc.$isDocumentArrayElement ?
2735
- subdoc.__parentArray :
2736
- subdoc.$parent();
2735
+ if (doc.$__.pathsToScopes == null) {
2736
+ doc.$__.pathsToScopes = {};
2737
+ }
2738
+ doc.$__.pathsToScopes[fullPathToSubdoc] = subdoc.$isDocumentArrayElement ?
2739
+ subdoc.__parentArray :
2740
+ subdoc.$parent();
2737
2741
 
2738
- doValidateOptions[fullPathToSubdoc] = { skipSchemaValidators: true };
2739
- if (subdoc.$isDocumentArrayElement && subdoc.__index != null) {
2740
- doValidateOptions[fullPathToSubdoc].index = subdoc.__index;
2742
+ doValidateOptions[fullPathToSubdoc] = { skipSchemaValidators: true };
2743
+ if (subdoc.$isDocumentArrayElement && subdoc.__index != null) {
2744
+ doValidateOptions[fullPathToSubdoc].index = subdoc.__index;
2745
+ }
2741
2746
  }
2742
2747
  }
2743
2748
  }
@@ -2972,7 +2977,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2972
2977
  paths = [...paths];
2973
2978
  doValidateOptionsByPath = {};
2974
2979
  } else {
2975
- const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip);
2980
+ const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options && options._nestedValidate);
2976
2981
  paths = shouldValidateModifiedOnly ?
2977
2982
  pathDetails[0].filter((path) => this.$isModified(path)) :
2978
2983
  pathDetails[0];
@@ -3059,7 +3064,8 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
3059
3064
  const doValidateOptions = {
3060
3065
  ...doValidateOptionsByPath[path],
3061
3066
  path: path,
3062
- validateAllPaths
3067
+ validateAllPaths,
3068
+ _nestedValidate: true
3063
3069
  };
3064
3070
 
3065
3071
  schemaType.doValidate(val, function(err) {
@@ -3478,44 +3484,9 @@ Document.prototype.$__reset = function reset() {
3478
3484
  // Skip for subdocuments
3479
3485
  const subdocs = !this.$isSubdocument ? this.$getAllSubdocs() : null;
3480
3486
  if (subdocs && subdocs.length > 0) {
3481
- const resetArrays = new Set();
3482
3487
  for (const subdoc of subdocs) {
3483
- const fullPathWithIndexes = subdoc.$__fullPathWithIndexes();
3484
3488
  subdoc.$__reset();
3485
- if (this.isModified(fullPathWithIndexes) || isParentInit(fullPathWithIndexes)) {
3486
- if (subdoc.$isDocumentArrayElement) {
3487
- resetArrays.add(subdoc.parentArray());
3488
- } else {
3489
- const parent = subdoc.$parent();
3490
- if (parent === this) {
3491
- this.$__.activePaths.clearPath(subdoc.$basePath);
3492
- } else if (parent != null && parent.$isSubdocument) {
3493
- // If map path underneath subdocument, may end up with a case where
3494
- // map path is modified but parent still needs to be reset. See gh-10295
3495
- parent.$__reset();
3496
- }
3497
- }
3498
- }
3499
- }
3500
-
3501
- for (const array of resetArrays) {
3502
- this.$__.activePaths.clearPath(array.$path());
3503
- array[arrayAtomicsBackupSymbol] = array[arrayAtomicsSymbol];
3504
- array[arrayAtomicsSymbol] = {};
3505
- }
3506
- }
3507
-
3508
- function isParentInit(path) {
3509
- path = path.indexOf('.') === -1 ? [path] : path.split('.');
3510
- let cur = '';
3511
- for (let i = 0; i < path.length; ++i) {
3512
- cur += (cur.length ? '.' : '') + path[i];
3513
- if (_this.$__.activePaths[cur] === 'init') {
3514
- return true;
3515
- }
3516
3489
  }
3517
-
3518
- return false;
3519
3490
  }
3520
3491
 
3521
3492
  // clear atomics
@@ -23,6 +23,11 @@ const utils = require('../../utils');
23
23
  function NativeConnection() {
24
24
  MongooseConnection.apply(this, arguments);
25
25
  this._listening = false;
26
+ // Tracks the last time (as unix timestamp) the connection received a
27
+ // serverHeartbeatSucceeded or serverHeartbeatFailed event from the underlying MongoClient.
28
+ // If we haven't received one in a while (like due to a frozen AWS Lambda container) then
29
+ // `readyState` is likely stale.
30
+ this._lastHeartbeatAt = null;
26
31
  }
27
32
 
28
33
  /**
@@ -106,6 +111,7 @@ NativeConnection.prototype.useDb = function(name, options) {
106
111
  _opts.noListener = options.noListener;
107
112
  }
108
113
  newConn.db = _this.client.db(name, _opts);
114
+ newConn._lastHeartbeatAt = _this._lastHeartbeatAt;
109
115
  newConn.onOpen();
110
116
  }
111
117
 
@@ -409,6 +415,9 @@ function _setClient(conn, client, options, dbName) {
409
415
  }
410
416
  });
411
417
  }
418
+ client.on('serverHeartbeatSucceeded', () => {
419
+ conn._lastHeartbeatAt = Date.now();
420
+ });
412
421
 
413
422
  if (options.monitorCommands) {
414
423
  client.on('commandStarted', (data) => conn.emit('commandStarted', data));
@@ -417,6 +426,9 @@ function _setClient(conn, client, options, dbName) {
417
426
  }
418
427
 
419
428
  conn.onOpen();
429
+ if (client.topology?.s?.state === 'connected') {
430
+ conn._lastHeartbeatAt = Date.now();
431
+ }
420
432
 
421
433
  for (const i in conn.collections) {
422
434
  if (utils.object.hasOwnProperty(conn.collections, i)) {
@@ -6,11 +6,12 @@
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
+ /**
10
+ * MissingSchema Error constructor.
11
+ */
9
12
 
10
13
  class MissingSchemaError extends MongooseError {
11
- /**
12
- * MissingSchema Error constructor.
13
- */
14
+
14
15
  constructor() {
15
16
  super('Schema hasn\'t been registered for document.\n'
16
17
  + 'Use mongoose.Document(name, schema)');
@@ -0,0 +1,44 @@
1
+ /*!
2
+ * Module dependencies.
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const MongooseError = require('./mongooseError');
8
+
9
+
10
+ /**
11
+ * If the underwriting `bulkWrite()` for `bulkSave()` succeeded, but wasn't able to update or
12
+ * insert all documents, we throw this error.
13
+ *
14
+ * @api private
15
+ */
16
+
17
+ class MongooseBulkSaveIncompleteError extends MongooseError {
18
+ constructor(modelName, documents, bulkWriteResult) {
19
+ const matchedCount = bulkWriteResult?.matchedCount ?? 0;
20
+ const insertedCount = bulkWriteResult?.insertedCount ?? 0;
21
+ let preview = documents.map(doc => doc._id).join(', ');
22
+ if (preview.length > 100) {
23
+ preview = preview.slice(0, 100) + '...';
24
+ }
25
+
26
+ const numDocumentsNotUpdated = documents.length - matchedCount - insertedCount;
27
+ super(`${modelName}.bulkSave() was not able to update ${numDocumentsNotUpdated} of the given documents due to incorrect version or optimistic concurrency, document ids: ${preview}`);
28
+
29
+ this.modelName = modelName;
30
+ this.documents = documents;
31
+ this.bulkWriteResult = bulkWriteResult;
32
+ this.numDocumentsNotUpdated = numDocumentsNotUpdated;
33
+ }
34
+ }
35
+
36
+ Object.defineProperty(MongooseBulkSaveIncompleteError.prototype, 'name', {
37
+ value: 'MongooseBulkSaveIncompleteError'
38
+ });
39
+
40
+ /*!
41
+ * exports
42
+ */
43
+
44
+ module.exports = MongooseBulkSaveIncompleteError;
@@ -7,12 +7,14 @@
7
7
 
8
8
  const MongooseError = require('./mongooseError');
9
9
 
10
+ /**
11
+ * DivergentArrayError constructor.
12
+ * @param {Array<String>} paths
13
+ * @api private
14
+ */
15
+
10
16
  class DivergentArrayError extends MongooseError {
11
- /**
12
- * DivergentArrayError constructor.
13
- * @param {Array<String>} paths
14
- * @api private
15
- */
17
+
16
18
  constructor(paths) {
17
19
  const msg = 'For your own good, using `document.save()` to update an array '
18
20
  + 'which was selected using an $elemMatch projection OR '
@@ -7,12 +7,14 @@
7
7
 
8
8
  const MongooseError = require('./mongooseError');
9
9
 
10
+ /**
11
+ * InvalidSchemaOption Error constructor.
12
+ * @param {String} name
13
+ * @api private
14
+ */
15
+
10
16
  class InvalidSchemaOptionError extends MongooseError {
11
- /**
12
- * InvalidSchemaOption Error constructor.
13
- * @param {String} name
14
- * @api private
15
- */
17
+
16
18
  constructor(name, option) {
17
19
  const msg = `Cannot create use schema for property "${name}" because the schema has the ${option} option enabled.`;
18
20
  super(msg);
@@ -7,12 +7,14 @@
7
7
 
8
8
  const MongooseError = require('./mongooseError');
9
9
 
10
+ /**
11
+ * MissingSchema Error constructor.
12
+ * @param {String} name
13
+ * @api private
14
+ */
15
+
10
16
  class MissingSchemaError extends MongooseError {
11
- /**
12
- * MissingSchema Error constructor.
13
- * @param {String} name
14
- * @api private
15
- */
17
+
16
18
  constructor(name) {
17
19
  const msg = 'Schema hasn\'t been registered for model "' + name + '".\n'
18
20
  + 'Use mongoose.model(name, schema)';
@@ -7,11 +7,13 @@
7
7
  const MongooseError = require('./mongooseError');
8
8
  const util = require('util');
9
9
 
10
+ /**
11
+ * OverwriteModel Error constructor.
12
+ * @api private
13
+ */
14
+
10
15
  class DocumentNotFoundError extends MongooseError {
11
- /**
12
- * OverwriteModel Error constructor.
13
- * @api private
14
- */
16
+
15
17
  constructor(filter, model, numAffected, result) {
16
18
  let msg;
17
19
  const messages = MongooseError.messages;
@@ -6,15 +6,16 @@
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
+ /**
10
+ * Strict mode error constructor
11
+ *
12
+ * @param {string} type
13
+ * @param {string} value
14
+ * @api private
15
+ */
9
16
 
10
17
  class ObjectExpectedError extends MongooseError {
11
- /**
12
- * Strict mode error constructor
13
- *
14
- * @param {string} type
15
- * @param {string} value
16
- * @api private
17
- */
18
+
18
19
  constructor(path, val) {
19
20
  const typeDescription = Array.isArray(val) ? 'array' : 'primitive value';
20
21
  super('Tried to set nested object field `' + path +
@@ -6,16 +6,18 @@
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
+ /**
10
+ * Constructor for errors that happen when a parameter that's expected to be
11
+ * an object isn't an object
12
+ *
13
+ * @param {Any} value
14
+ * @param {String} paramName
15
+ * @param {String} fnName
16
+ * @api private
17
+ */
18
+
9
19
  class ObjectParameterError extends MongooseError {
10
- /**
11
- * Constructor for errors that happen when a parameter that's expected to be
12
- * an object isn't an object
13
- *
14
- * @param {Any} value
15
- * @param {String} paramName
16
- * @param {String} fnName
17
- * @api private
18
- */
20
+
19
21
  constructor(value, paramName, fnName) {
20
22
  super('Parameter "' + paramName + '" to ' + fnName +
21
23
  '() must be an object, got "' + value.toString() + '" (type ' + typeof value + ')');
@@ -7,13 +7,14 @@
7
7
 
8
8
  const MongooseError = require('./mongooseError');
9
9
 
10
+ /**
11
+ * OverwriteModel Error constructor.
12
+ * @param {String} name
13
+ * @api private
14
+ */
10
15
 
11
16
  class OverwriteModelError extends MongooseError {
12
- /**
13
- * OverwriteModel Error constructor.
14
- * @param {String} name
15
- * @api private
16
- */
17
+
17
18
  constructor(name) {
18
19
  super('Cannot overwrite `' + name + '` model once compiled.');
19
20
  }
@@ -6,13 +6,16 @@
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
+
10
+ /**
11
+ * ParallelSave Error constructor.
12
+ *
13
+ * @param {Document} doc
14
+ * @api private
15
+ */
16
+
9
17
  class ParallelSaveError extends MongooseError {
10
- /**
11
- * ParallelSave Error constructor.
12
- *
13
- * @param {Document} doc
14
- * @api private
15
- */
18
+
16
19
  constructor(doc) {
17
20
  const msg = 'Can\'t save() the same doc multiple times in parallel. Document: ';
18
21
  super(msg + doc._doc._id);
@@ -7,13 +7,15 @@
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
9
 
10
+ /**
11
+ * ParallelValidate Error constructor.
12
+ *
13
+ * @param {Document} doc
14
+ * @api private
15
+ */
16
+
10
17
  class ParallelValidateError extends MongooseError {
11
- /**
12
- * ParallelValidate Error constructor.
13
- *
14
- * @param {Document} doc
15
- * @api private
16
- */
18
+
17
19
  constructor(doc) {
18
20
  const msg = 'Can\'t validate() the same doc multiple times in parallel. Document: ';
19
21
  super(msg + doc._doc._id);
@@ -8,13 +8,15 @@ const MongooseError = require('./mongooseError');
8
8
  const util = require('util');
9
9
  const combinePathErrors = require('../helpers/error/combinePathErrors');
10
10
 
11
+ /**
12
+ * Mongoose.set Error
13
+ *
14
+ * @api private
15
+ * @inherits MongooseError
16
+ */
17
+
11
18
  class SetOptionError extends MongooseError {
12
- /**
13
- * Mongoose.set Error
14
- *
15
- * @api private
16
- * @inherits MongooseError
17
- */
19
+
18
20
  constructor() {
19
21
  super('');
20
22
 
@@ -6,17 +6,19 @@
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
+ /**
10
+ * Strict mode error constructor
11
+ *
12
+ * @param {String} path
13
+ * @param {String} [msg]
14
+ * @param {Boolean} [immutable]
15
+ * @inherits MongooseError
16
+ * @api private
17
+ */
18
+
9
19
 
10
20
  class StrictModeError extends MongooseError {
11
- /**
12
- * Strict mode error constructor
13
- *
14
- * @param {String} path
15
- * @param {String} [msg]
16
- * @param {Boolean} [immutable]
17
- * @inherits MongooseError
18
- * @api private
19
- */
21
+
20
22
  constructor(path, msg, immutable) {
21
23
  msg = msg || 'Field `' + path + '` is not in schema and strict ' +
22
24
  'mode is set to throw.';
@@ -6,15 +6,17 @@
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
+ /**
10
+ * Strict mode error constructor
11
+ *
12
+ * @param {String} path
13
+ * @param {String} [msg]
14
+ * @inherits MongooseError
15
+ * @api private
16
+ */
17
+
9
18
  class StrictPopulateError extends MongooseError {
10
- /**
11
- * Strict mode error constructor
12
- *
13
- * @param {String} path
14
- * @param {String} [msg]
15
- * @inherits MongooseError
16
- * @api private
17
- */
19
+
18
20
  constructor(path, msg) {
19
21
  msg = msg || 'Cannot populate path `' + path + '` because it is not in your schema. ' + 'Set the `strictPopulate` option to false to override.';
20
22
  super(msg);
@@ -9,14 +9,16 @@ const getConstructorName = require('../helpers/getConstructorName');
9
9
  const util = require('util');
10
10
  const combinePathErrors = require('../helpers/error/combinePathErrors');
11
11
 
12
+ /**
13
+ * Document Validation Error
14
+ *
15
+ * @api private
16
+ * @param {Document} [instance]
17
+ * @inherits MongooseError
18
+ */
19
+
12
20
  class ValidationError extends MongooseError {
13
- /**
14
- * Document Validation Error
15
- *
16
- * @api private
17
- * @param {Document} [instance]
18
- * @inherits MongooseError
19
- */
21
+
20
22
  constructor(instance) {
21
23
  let _message;
22
24
  if (getConstructorName(instance) === 'model') {
@@ -6,15 +6,16 @@
6
6
 
7
7
  const MongooseError = require('./mongooseError');
8
8
 
9
+ /**
10
+ * Schema validator error
11
+ *
12
+ * @param {Object} properties
13
+ * @param {Document} doc
14
+ * @api private
15
+ */
9
16
 
10
17
  class ValidatorError extends MongooseError {
11
- /**
12
- * Schema validator error
13
- *
14
- * @param {Object} properties
15
- * @param {Document} doc
16
- * @api private
17
- */
18
+
18
19
  constructor(properties, doc) {
19
20
  let msg = properties.message;
20
21
  if (!msg) {