@depup/mongoose 9.3.3-depup.0 → 9.8.0-depup.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.
Files changed (73) hide show
  1. package/README.md +3 -9
  2. package/changes.json +3 -8
  3. package/eslint.config.mjs +4 -1
  4. package/lib/aggregate.js +54 -24
  5. package/lib/cast.js +1 -1
  6. package/lib/connection.js +20 -8
  7. package/lib/cursor/aggregationCursor.js +47 -16
  8. package/lib/cursor/queryCursor.js +23 -7
  9. package/lib/document.js +240 -105
  10. package/lib/drivers/node-mongodb-native/collection.js +47 -14
  11. package/lib/drivers/node-mongodb-native/connection.js +13 -0
  12. package/lib/error/cast.js +18 -9
  13. package/lib/error/divergentArray.js +1 -1
  14. package/lib/error/index.js +1 -1
  15. package/lib/error/messages.js +1 -0
  16. package/lib/helpers/clone.js +22 -5
  17. package/lib/helpers/document/applyDefaults.js +5 -0
  18. package/lib/helpers/populate/createPopulateQueryFilter.js +9 -1
  19. package/lib/helpers/populate/getModelsMapForPopulate.js +8 -6
  20. package/lib/helpers/populate/splitPopulateQuery.js +81 -0
  21. package/lib/helpers/query/castUpdate.js +4 -0
  22. package/lib/helpers/schema/getKeysInSchemaOrder.js +13 -16
  23. package/lib/helpers/update/applyTimestampsToUpdate.js +4 -2
  24. package/lib/model.js +159 -41
  25. package/lib/mongoose.js +3 -3
  26. package/lib/options/schemaTypeOptions.js +14 -0
  27. package/lib/plugins/saveSubdocs.js +16 -13
  28. package/lib/query.js +208 -102
  29. package/lib/queryHelpers.js +13 -12
  30. package/lib/schema/array.js +4 -6
  31. package/lib/schema/bigint.js +1 -3
  32. package/lib/schema/boolean.js +1 -3
  33. package/lib/schema/buffer.js +1 -3
  34. package/lib/schema/date.js +8 -8
  35. package/lib/schema/decimal128.js +1 -3
  36. package/lib/schema/documentArray.js +3 -5
  37. package/lib/schema/double.js +1 -3
  38. package/lib/schema/int32.js +1 -3
  39. package/lib/schema/map.js +1 -4
  40. package/lib/schema/number.js +2 -4
  41. package/lib/schema/objectId.js +7 -3
  42. package/lib/schema/string.js +20 -5
  43. package/lib/schema/subdocument.js +2 -4
  44. package/lib/schema/union.js +50 -0
  45. package/lib/schema/uuid.js +1 -3
  46. package/lib/schema.js +40 -14
  47. package/lib/schemaType.js +93 -12
  48. package/lib/standardSchema/convertErrorToIssues.js +20 -0
  49. package/lib/stateMachine.js +11 -6
  50. package/lib/tracing.js +38 -0
  51. package/lib/types/array/methods/index.js +4 -4
  52. package/lib/types/documentArray/methods/index.js +117 -3
  53. package/lib/types/subdocument.js +4 -2
  54. package/lib/utils.js +12 -2
  55. package/lib/validOptions.js +1 -0
  56. package/package.json +30 -33
  57. package/{tstyche.config.json → tstyche.json} +2 -1
  58. package/types/augmentations.d.ts +2 -2
  59. package/types/document.d.ts +61 -7
  60. package/types/expressions.d.ts +16 -0
  61. package/types/index.d.ts +25 -7
  62. package/types/inferhydrateddoctype.d.ts +1 -1
  63. package/types/inferrawdoctype.d.ts +6 -3
  64. package/types/inferschematype.d.ts +10 -2
  65. package/types/models.d.ts +30 -13
  66. package/types/mongooseoptions.d.ts +9 -1
  67. package/types/populate.d.ts +52 -0
  68. package/types/query.d.ts +39 -12
  69. package/types/schemaoptions.d.ts +5 -0
  70. package/types/schematypes.d.ts +10 -0
  71. package/types/tracing.d.ts +10 -0
  72. package/types/utility.d.ts +11 -2
  73. package/lib/helpers/createJSONSchemaTypeDefinition.js +0 -24
package/lib/schemaType.js CHANGED
@@ -181,6 +181,40 @@ SchemaType.prototype.toJSON = function toJSON() {
181
181
  return res;
182
182
  };
183
183
 
184
+ /**
185
+ * Helper for creating `{ type: 'object' }` vs `{ bsonType: 'object' }` vs nullable variants.
186
+ *
187
+ * @param {string} type
188
+ * @param {string} bsonType
189
+ * @param {object} [options]
190
+ * @param {boolean} [options.useBsonType=false]
191
+ * @param {boolean} [options._defaultRequired=false]
192
+ * @param {boolean} [options._overrideRequired]
193
+ * @returns {object}
194
+ * @api private
195
+ */
196
+
197
+ SchemaType.prototype._createJSONSchemaTypeDefinition = function _createJSONSchemaTypeDefinition(type, bsonType, options) {
198
+ const useBsonType = options?.useBsonType;
199
+ const isRequired = options?._overrideRequired ??
200
+ (this.options.required == null ?
201
+ (options?._defaultRequired === true || this.path === '_id') :
202
+ this.options.required && typeof this.options.required !== 'function');
203
+ const allowNull = this.options.allowNull !== false;
204
+
205
+ if (useBsonType) {
206
+ if (isRequired || !allowNull) {
207
+ return { bsonType };
208
+ }
209
+ return { bsonType: [bsonType, 'null'] };
210
+ }
211
+
212
+ if (isRequired || !allowNull) {
213
+ return { type };
214
+ }
215
+ return { type: [type, 'null'] };
216
+ };
217
+
184
218
  /**
185
219
  * The validators that Mongoose should run to validate properties at this SchemaType's path.
186
220
  *
@@ -380,10 +414,10 @@ SchemaType.get = function(getter) {
380
414
  * #### Example:
381
415
  *
382
416
  * // values are cast:
383
- * const schema = new Schema({ aNumber: { type: Number, default: 4.815162342 }})
417
+ * const schema = new Schema({ num: { type: Number, default: 4.815162342 }})
384
418
  * const M = db.model('M', schema)
385
419
  * const m = new M;
386
- * console.log(m.aNumber) // 4.815162342
420
+ * console.log(m.num) // 4.815162342
387
421
  *
388
422
  * // default unique objects for Mixed types:
389
423
  * const schema = new Schema({ mixed: Schema.Types.Mixed });
@@ -786,7 +820,7 @@ SchemaType.prototype.set = function(fn) {
786
820
  * // defining within the schema
787
821
  * const s = new Schema({ born: { type: Date, get: dob })
788
822
  *
789
- * // or by retreiving its SchemaType
823
+ * // or by retrieving its SchemaType
790
824
  * const s = new Schema({ born: Date })
791
825
  * s.path('born').get(dob)
792
826
  *
@@ -936,7 +970,7 @@ SchemaType.prototype.validateAll = function(validators) {
936
970
  * }
937
971
  * });
938
972
  *
939
- * You might use asynchronous validators to retreive other documents from the database to validate against or to meet other I/O bound validation needs.
973
+ * You might use asynchronous validators to retrieve other documents from the database to validate against or to meet other I/O bound validation needs.
940
974
  *
941
975
  * Validation occurs `pre('save')` or whenever you manually execute [document#validate](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()).
942
976
  *
@@ -1109,6 +1143,12 @@ SchemaType.prototype.required = function(required, message) {
1109
1143
 
1110
1144
  const _this = this;
1111
1145
  this.isRequired = true;
1146
+ this.originalRequiredValue = required;
1147
+
1148
+ if (typeof this.originalRequiredValue !== 'function' &&
1149
+ (utils.hasUserDefinedProperty(this.options, 'allowNull') || this.allowNullValidator != null)) {
1150
+ throw new MongooseError('Path "' + this.path + '" may not have `allowNull` specified when `required` is true');
1151
+ }
1112
1152
 
1113
1153
  this.requiredValidator = function(v) {
1114
1154
  const cachedRequired = this?.$__?.cachedRequired;
@@ -1133,8 +1173,6 @@ SchemaType.prototype.required = function(required, message) {
1133
1173
 
1134
1174
  return _this.checkRequired(v, this);
1135
1175
  };
1136
- this.originalRequiredValue = required;
1137
-
1138
1176
  if (typeof required === 'string') {
1139
1177
  message = required;
1140
1178
  required = undefined;
@@ -1150,6 +1188,54 @@ SchemaType.prototype.required = function(required, message) {
1150
1188
  return this;
1151
1189
  };
1152
1190
 
1191
+ /**
1192
+ * Adds a validator that disallows `null` for this path without making the path
1193
+ * required. `undefined` values still pass validation.
1194
+ *
1195
+ * #### Example:
1196
+ *
1197
+ * const schema = new Schema({
1198
+ * name: { type: String, allowNull: false }
1199
+ * });
1200
+ *
1201
+ * new Model({}).validateSync(); // OK
1202
+ * new Model({ name: undefined }).validateSync(); // OK, Mongoose strips out `name` when its value is `undefined`
1203
+ * new Model({ name: null }).validateSync(); // ValidationError
1204
+ *
1205
+ * @param {boolean} allowNull
1206
+ * @return {SchemaType} this
1207
+ * @api public
1208
+ */
1209
+
1210
+ SchemaType.prototype.allowNull = function(allowNull) {
1211
+ if (arguments.length > 0 && this.isRequired && typeof this.originalRequiredValue !== 'function') {
1212
+ throw new MongooseError('Path "' + this.path + '" may not have `allowNull` specified when `required` is true');
1213
+ }
1214
+
1215
+ this.validators = this.validators.filter(function(v) {
1216
+ return v.validator !== this.allowNullValidator;
1217
+ }, this);
1218
+
1219
+ if (allowNull !== false) {
1220
+ delete this.options.allowNull;
1221
+ delete this.allowNullValidator;
1222
+ return this;
1223
+ }
1224
+
1225
+ this.options.allowNull = false;
1226
+ this.allowNullValidator = function(v) {
1227
+ return v !== null;
1228
+ };
1229
+
1230
+ this.validators.push({
1231
+ validator: this.allowNullValidator,
1232
+ message: MongooseError.messages.general.allowNull,
1233
+ type: 'allowNull'
1234
+ });
1235
+
1236
+ return this;
1237
+ };
1238
+
1153
1239
  /**
1154
1240
  * Set the model that this path refers to. This is the option that [populate](https://mongoosejs.com/docs/populate.html)
1155
1241
  * looks at to determine the foreign collection it should query.
@@ -1371,9 +1457,6 @@ SchemaType.prototype.doValidate = async function doValidate(value, scope, option
1371
1457
  validatorProperties.value = value;
1372
1458
  if (typeof value === 'string') {
1373
1459
  validatorProperties.length = value.length;
1374
- if (validatorProperties.value.length > 30) {
1375
- validatorProperties.value = validatorProperties.value.slice(0, 30) + '...';
1376
- }
1377
1460
  }
1378
1461
 
1379
1462
  if (value === undefined && validator !== this.requiredValidator) {
@@ -1497,9 +1580,6 @@ SchemaType.prototype.doValidateSync = function(value, scope, options) {
1497
1580
  validatorProperties.value = value;
1498
1581
  if (typeof value === 'string') {
1499
1582
  validatorProperties.length = value.length;
1500
- if (validatorProperties.value.length > 30) {
1501
- validatorProperties.value = validatorProperties.value.slice(0, 30) + '...';
1502
- }
1503
1583
  }
1504
1584
  let ok = false;
1505
1585
 
@@ -1769,6 +1849,7 @@ SchemaType.prototype.clone = function() {
1769
1849
  const schematype = new this.constructor(this.path, options, this.instance, this.parentSchema);
1770
1850
  schematype.validators = this.validators.slice();
1771
1851
  if (this.requiredValidator !== undefined) schematype.requiredValidator = this.requiredValidator;
1852
+ if (this.allowNullValidator !== undefined) schematype.allowNullValidator = this.allowNullValidator;
1772
1853
  if (this.defaultValue !== undefined) schematype.defaultValue = this.defaultValue;
1773
1854
  if (this.$immutable !== undefined && this.options.immutable === undefined) {
1774
1855
  schematype.$immutable = this.$immutable;
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const ValidationError = require('../error/validation');
4
+
5
+ module.exports = function convertErrorToIssues(error) {
6
+ if (error instanceof ValidationError) {
7
+ return Object.keys(error.errors).map(path => {
8
+ const err = error.errors[path];
9
+ return {
10
+ message: err.message,
11
+ path: path.split('.').map(part => {
12
+ const num = +part;
13
+ return Number.isInteger(num) && String(num) === part ? num : part;
14
+ })
15
+ };
16
+ });
17
+ }
18
+
19
+ return [{ message: error.message }];
20
+ };
@@ -69,8 +69,10 @@ StateMachine.prototype._changeState = function _changeState(path, nextState) {
69
69
  if (prevState === nextState) {
70
70
  return;
71
71
  }
72
- const prevBucket = this.states[prevState];
73
- if (prevBucket) delete prevBucket[path];
72
+ if (prevState !== undefined) {
73
+ const prevBucket = this.states[prevState];
74
+ if (prevBucket) delete prevBucket[path];
75
+ }
74
76
 
75
77
  this.paths[path] = nextState;
76
78
  this.states[nextState] = this.states[nextState] || {};
@@ -86,13 +88,16 @@ StateMachine.prototype.clear = function clear(state) {
86
88
  return;
87
89
  }
88
90
  const keys = Object.keys(this.states[state]);
91
+ if (keys.length === 0) {
92
+ return;
93
+ }
94
+ // Replace the bucket rather than deleting each key: repeated `delete` puts
95
+ // the object in dictionary mode, which is significantly slower.
96
+ this.states[state] = {};
89
97
  let i = keys.length;
90
- let path;
91
98
 
92
99
  while (i--) {
93
- path = keys[i];
94
- delete this.states[state][path];
95
- delete this.paths[path];
100
+ delete this.paths[keys[i]];
96
101
  }
97
102
  };
98
103
 
package/lib/tracing.js ADDED
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ let dc;
4
+ try {
5
+ dc = (typeof process !== 'undefined' && 'getBuiltinModule' in process)
6
+ ? process.getBuiltinModule('node:diagnostics_channel')
7
+ : require('node:diagnostics_channel');
8
+ } catch {
9
+ // diagnostics_channel not available
10
+ }
11
+
12
+ const hasTracingChannel = !!dc && typeof dc.tracingChannel === 'function';
13
+
14
+ function shouldTrace(channel) {
15
+ // Node 18 and Bun don't expose hasSubscribers on TracingChannel
16
+ // so undefined means it may or may not have subscribers, so we'll trace anyway
17
+ return !!channel && channel.hasSubscribers !== false;
18
+ }
19
+
20
+ function createTracedChannel(name) {
21
+ const ch = hasTracingChannel ? dc.tracingChannel(name) : undefined;
22
+
23
+ function trace(fn, contextFactory) {
24
+ if (!shouldTrace(ch)) {
25
+ return fn();
26
+ }
27
+
28
+ const traced = ch.tracePromise(fn, contextFactory());
29
+ return traced;
30
+ }
31
+
32
+ return { channel: ch, trace };
33
+ }
34
+
35
+ module.exports = {
36
+ createTracedChannel,
37
+ cursorNextChannel: createTracedChannel('mongoose:cursor:next')
38
+ };
@@ -572,7 +572,7 @@ const methods = {
572
572
  *
573
573
  * #### Note:
574
574
  *
575
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
575
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwriting any changes that happen between when you retrieved the object and when you save it._
576
576
  *
577
577
  * @param {...any} [args]
578
578
  * @api public
@@ -830,7 +830,7 @@ const methods = {
830
830
  *
831
831
  * #### Note:
832
832
  *
833
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
833
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwriting any changes that happen between when you retrieved the object and when you save it._
834
834
  *
835
835
  * @api public
836
836
  * @method shift
@@ -850,7 +850,7 @@ const methods = {
850
850
  *
851
851
  * #### Note:
852
852
  *
853
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
853
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwriting any changes that happen between when you retrieved the object and when you save it._
854
854
  *
855
855
  * @api public
856
856
  * @method sort
@@ -870,7 +870,7 @@ const methods = {
870
870
  *
871
871
  * #### Note:
872
872
  *
873
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
873
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwriting any changes that happen between when you retrieved the object and when you save it._
874
874
  *
875
875
  * @api public
876
876
  * @method splice
@@ -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 = {
@@ -211,7 +212,7 @@ const methods = {
211
212
  },
212
213
 
213
214
  /**
214
- * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
215
+ * Wraps [`Array#push`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
215
216
  *
216
217
  * @param {...object} [args]
217
218
  * @api public
@@ -220,8 +221,37 @@ const methods = {
220
221
  */
221
222
 
222
223
  push() {
224
+ const shouldReindex = _shouldReindexAfterPush(arguments);
223
225
  const ret = ArrayMethods.push.apply(this, arguments);
224
226
 
227
+ if (shouldReindex) {
228
+ _updateSubdocIndexes(this);
229
+ }
230
+ _updateParentPopulated(this);
231
+
232
+ return ret;
233
+ },
234
+
235
+ /**
236
+ * Wraps [`Array#pop`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking.
237
+ * @api private
238
+ */
239
+
240
+ pop() {
241
+ const ret = ArrayMethods.pop.apply(this, arguments);
242
+
243
+ _updateParentPopulated(this);
244
+
245
+ return ret;
246
+ },
247
+
248
+ /*!
249
+ * ignore
250
+ */
251
+
252
+ $pop() {
253
+ const ret = ArrayMethods.$pop.apply(this, arguments);
254
+
225
255
  _updateParentPopulated(this);
226
256
 
227
257
  return ret;
@@ -239,32 +269,96 @@ const methods = {
239
269
  pull() {
240
270
  const ret = ArrayMethods.pull.apply(this, arguments);
241
271
 
272
+ _updateSubdocIndexes(this);
242
273
  _updateParentPopulated(this);
243
274
 
244
275
  return ret;
245
276
  },
246
277
 
247
278
  /**
248
- * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
279
+ * Wraps [`Array#shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) with proper change tracking.
249
280
  * @api private
250
281
  */
251
282
 
252
283
  shift() {
253
284
  const ret = ArrayMethods.shift.apply(this, arguments);
254
285
 
286
+ _updateSubdocIndexes(this);
287
+ _updateParentPopulated(this);
288
+
289
+ return ret;
290
+ },
291
+
292
+ /*!
293
+ * ignore
294
+ */
295
+
296
+ $shift() {
297
+ const ret = ArrayMethods.$shift.apply(this, arguments);
298
+
299
+ _updateSubdocIndexes(this);
255
300
  _updateParentPopulated(this);
256
301
 
257
302
  return ret;
258
303
  },
259
304
 
260
305
  /**
261
- * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
306
+ * Wraps [`Array#splice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
262
307
  * @api private
263
308
  */
264
309
 
265
310
  splice() {
266
311
  const ret = ArrayMethods.splice.apply(this, arguments);
267
312
 
313
+ _updateSubdocIndexes(this);
314
+ _updateParentPopulated(this);
315
+
316
+ return ret;
317
+ },
318
+
319
+ /**
320
+ * Wraps [`Array#reverse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse)
321
+ * with proper change tracking.
322
+ * @api private
323
+ */
324
+
325
+ reverse() {
326
+ _baseReverse.call(this.__array);
327
+ this._registerAtomic('$set', this);
328
+
329
+ _updateSubdocIndexes(this);
330
+ _updateParentPopulated(this);
331
+
332
+ return this;
333
+ },
334
+
335
+ /**
336
+ * Wraps [`Array#sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
337
+ * with proper change tracking.
338
+ *
339
+ * @param {Function} [compareFunction]
340
+ * @api private
341
+ */
342
+
343
+ sort() {
344
+ const ret = ArrayMethods.sort.apply(this, arguments);
345
+
346
+ _updateSubdocIndexes(this);
347
+ _updateParentPopulated(this);
348
+
349
+ return ret;
350
+ },
351
+
352
+ /**
353
+ * Wraps [`Array#unshift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift)
354
+ * with proper change tracking.
355
+ * @api private
356
+ */
357
+
358
+ unshift() {
359
+ const ret = ArrayMethods.unshift.apply(this, arguments);
360
+
361
+ _updateSubdocIndexes(this);
268
362
  _updateParentPopulated(this);
269
363
 
270
364
  return ret;
@@ -389,6 +483,26 @@ const methods = {
389
483
 
390
484
  module.exports = methods;
391
485
 
486
+ /*!
487
+ * ignore
488
+ */
489
+
490
+ function _updateSubdocIndexes(arr) {
491
+ const rawArray = utils.isMongooseArray(arr) ? arr.__array : arr;
492
+
493
+ for (let i = 0; i < rawArray.length; ++i) {
494
+ if (typeof rawArray[i]?.$setIndex === 'function') {
495
+ rawArray[i].$setIndex(i);
496
+ }
497
+ }
498
+ }
499
+
500
+ function _shouldReindexAfterPush(args) {
501
+ return args[0] != null &&
502
+ utils.hasUserDefinedProperty(args[0], '$each') &&
503
+ args[0].$position != null;
504
+ }
505
+
392
506
  /**
393
507
  * If this is a document array, each element may contain single
394
508
  * populated paths, so we need to modify the top-level document's
@@ -403,7 +403,9 @@ if (util.inspect.custom) {
403
403
 
404
404
  /**
405
405
  * Override `$toObject()` to handle minimizing the whole path. Should not minimize if schematype-level minimize
406
- * is set to false re: gh-11247, gh-14058, gh-14151
406
+ * is set to false re: gh-11247, gh-14058, gh-14151. Should not minimize document array elements: an array
407
+ * element cannot be removed without shifting the array, so minimizing it to `undefined` makes the BSON
408
+ * serializer store `null` re: gh-7322.
407
409
  */
408
410
 
409
411
  Subdocument.prototype.$toObject = function $toObject(options, json) {
@@ -413,7 +415,7 @@ Subdocument.prototype.$toObject = function $toObject(options, json) {
413
415
  // If minimize is set, then we can minimize out the whole object.
414
416
  if (utils.hasOwnKeys(ret) === false && options?._calledWithOptions != null) {
415
417
  const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize;
416
- if (minimize && !this.constructor.$__required) {
418
+ if (minimize && !this.constructor.$__required && !this.$isDocumentArrayElement) {
417
419
  return undefined;
418
420
  }
419
421
  }
package/lib/utils.js CHANGED
@@ -119,6 +119,16 @@ exports.deepEqual = function deepEqual(a, b) {
119
119
  deepEqual(Array.from(a.values()), Array.from(b.values()));
120
120
  }
121
121
 
122
+ if (a instanceof Set || b instanceof Set) {
123
+ if (!(a instanceof Set) || !(b instanceof Set)) {
124
+ return false;
125
+ }
126
+ if (a.size !== b.size) {
127
+ return false;
128
+ }
129
+ return deepEqual(Array.from(a.values()), Array.from(b.values()));
130
+ }
131
+
122
132
  // Handle MongooseNumbers
123
133
  if (a instanceof Number && b instanceof Number) {
124
134
  return a.valueOf() === b.valueOf();
@@ -313,8 +323,8 @@ exports.merge = function merge(to, from, options, path) {
313
323
  // base schema has a given path as a single nested but discriminator schema
314
324
  // has the path as a document array, or vice versa (gh-9534)
315
325
  if (options.isDiscriminatorSchemaMerge &&
316
- (from[key].$isSingleNested && to[key].$isMongooseDocumentArray) ||
317
- (from[key].$isMongooseDocumentArray && to[key].$isSingleNested)) {
326
+ ((from[key].$isSingleNested && to[key].$isMongooseDocumentArray) ||
327
+ (from[key].$isMongooseDocumentArray && to[key].$isSingleNested))) {
318
328
  continue;
319
329
  } else if (from[key].instanceOfSchema) {
320
330
  if (to[key].instanceOfSchema) {
@@ -32,6 +32,7 @@ const VALID_OPTIONS = Object.freeze([
32
32
  'strict',
33
33
  'strictPopulate',
34
34
  'strictQuery',
35
+ 'strictRead',
35
36
  'timestamps.createdAt.immutable',
36
37
  'toJSON',
37
38
  'toObject',