@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/README.md CHANGED
@@ -13,16 +13,10 @@ npm install @depup/mongoose
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [mongoose](https://www.npmjs.com/package/mongoose) @ 9.3.3 |
17
- | Processed | 2026-03-25 |
16
+ | Original | [mongoose](https://www.npmjs.com/package/mongoose) @ 9.8.0 |
17
+ | Processed | 2026-07-21 |
18
18
  | Smoke test | passed |
19
- | Deps updated | 1 |
20
-
21
- ## Dependency Changes
22
-
23
- | Dependency | From | To |
24
- |------------|------|-----|
25
- | mongodb | ~7.1 | ^7.1.1 |
19
+ | Deps updated | 0 |
26
20
 
27
21
  ---
28
22
 
package/changes.json CHANGED
@@ -1,10 +1,5 @@
1
1
  {
2
- "bumped": {
3
- "mongodb": {
4
- "from": "~7.1",
5
- "to": "^7.1.1"
6
- }
7
- },
8
- "timestamp": "2026-03-25T20:16:19.527Z",
9
- "totalUpdated": 1
2
+ "bumped": {},
3
+ "timestamp": "2026-07-21T16:17:39.127Z",
4
+ "totalUpdated": 0
10
5
  }
package/eslint.config.mjs CHANGED
@@ -15,7 +15,10 @@ export default defineConfig([
15
15
  '!**/.*',
16
16
  '**/node_modules',
17
17
  '**/.git',
18
- '**/data'
18
+ '**/data',
19
+ '**/docs/3.*/**',
20
+ '**/docs/5.*/**',
21
+ '**/docs/vendor/*'
19
22
  ]),
20
23
  js.configs.recommended,
21
24
  // general options
package/lib/aggregate.js CHANGED
@@ -15,6 +15,8 @@ const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
15
15
  const stringifyFunctionOperators = require('./helpers/aggregate/stringifyFunctionOperators');
16
16
  const utils = require('./utils');
17
17
  const { modelSymbol } = require('./helpers/symbols');
18
+ const { createTracedChannel } = require('./tracing');
19
+ const { trace: traceAggregate } = createTracedChannel('mongoose:aggregate');
18
20
  const read = Query.prototype.read;
19
21
  const readConcern = Query.prototype.readConcern;
20
22
 
@@ -613,6 +615,9 @@ Aggregate.prototype.graphLookup = function(options) {
613
615
  *
614
616
  * aggregate.sample(3); // Add a pipeline that picks 3 random documents
615
617
  *
618
+ * // `$match` before `$sample` samples from the filtered subset
619
+ * aggregate.match({ difficulty: 'easy' }).sample(3);
620
+ *
616
621
  * @see $sample https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/#pipe._S_sample
617
622
  * @param {number} size number of random documents to pick
618
623
  * @return {Aggregate}
@@ -1032,7 +1037,7 @@ Aggregate.prototype.pipeline = function() {
1032
1037
  * const base = MyModel.aggregate().match({ test: 1 });
1033
1038
  * base.pipelineForUnionWith(); // [{ $match: { test: 1 } }]
1034
1039
  *
1035
- * @return {Array} The current pipeline with `$unionWith` stage restrictions
1040
+ * @return {PipelineStage[]} The current pipeline with `$unionWith` stage restrictions
1036
1041
  * @api public
1037
1042
  */
1038
1043
 
@@ -1071,8 +1076,20 @@ Aggregate.prototype.exec = async function exec() {
1071
1076
 
1072
1077
  this._optionsForExec();
1073
1078
 
1074
- const cursor = await this._connection.client.db().aggregate(this._pipeline, this.options);
1075
- return await cursor.toArray();
1079
+ const _this = this;
1080
+ return traceAggregate(async function maybeTracedConnectionAggregate() {
1081
+ const cursor = await _this._connection.client.db().aggregate(_this._pipeline, _this.options);
1082
+ return await cursor.toArray();
1083
+ }, () => ({
1084
+ operation: 'aggregate',
1085
+ database: _this._connection.name,
1086
+ serverAddress: _this._connection.host,
1087
+ serverPort: _this._connection.port,
1088
+ args: {
1089
+ pipeline: _this._pipeline,
1090
+ options: _this.options
1091
+ }
1092
+ }));
1076
1093
  }
1077
1094
 
1078
1095
  const model = this._model;
@@ -1090,32 +1107,45 @@ Aggregate.prototype.exec = async function exec() {
1090
1107
  prepareDiscriminatorPipeline(this._pipeline, this._model.schema);
1091
1108
  stringifyFunctionOperators(this._pipeline);
1092
1109
 
1093
- const preFilter = buildMiddlewareFilter(this.options, 'pre');
1094
- const postFilter = buildMiddlewareFilter(this.options, 'post');
1110
+ const _this = this;
1111
+ return traceAggregate(async function maybeTracedAggregateExec() {
1112
+ const preFilter = buildMiddlewareFilter(_this.options, 'pre');
1113
+ const postFilter = buildMiddlewareFilter(_this.options, 'post');
1095
1114
 
1096
- try {
1097
- await model.hooks.execPre('aggregate', this, [], { filter: preFilter });
1098
- } catch (error) {
1099
- return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
1100
- }
1115
+ try {
1116
+ await model.hooks.execPre('aggregate', _this, [], { filter: preFilter });
1117
+ } catch (error) {
1118
+ return await model.hooks.execPost('aggregate', _this, [null], { error, filter: postFilter });
1119
+ }
1101
1120
 
1102
- if (!this._pipeline.length) {
1103
- throw new MongooseError('Aggregate has empty pipeline');
1104
- }
1121
+ if (!_this._pipeline.length) {
1122
+ throw new MongooseError('Aggregate has empty pipeline');
1123
+ }
1105
1124
 
1106
- const options = clone(this.options || {});
1107
- delete options.middleware;
1125
+ const options = clone(_this.options || {});
1126
+ delete options.middleware;
1108
1127
 
1109
- let result;
1110
- try {
1111
- const cursor = await collection.aggregate(this._pipeline, options);
1112
- result = await cursor.toArray();
1113
- } catch (error) {
1114
- return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
1115
- }
1128
+ let result;
1129
+ try {
1130
+ const cursor = await collection.aggregate(_this._pipeline, options);
1131
+ result = await cursor.toArray();
1132
+ } catch (error) {
1133
+ return await model.hooks.execPost('aggregate', _this, [null], { error, filter: postFilter });
1134
+ }
1116
1135
 
1117
- await model.hooks.execPost('aggregate', this, [result], { error: null, filter: postFilter });
1118
- return result;
1136
+ await model.hooks.execPost('aggregate', _this, [result], { error: null, filter: postFilter });
1137
+ return result;
1138
+ }, () => ({
1139
+ operation: 'aggregate',
1140
+ collection: collection.name,
1141
+ database: model.db?.name,
1142
+ serverAddress: model.db?.host,
1143
+ serverPort: model.db?.port,
1144
+ args: {
1145
+ pipeline: _this._pipeline,
1146
+ options: _this.options
1147
+ }
1148
+ }));
1119
1149
  };
1120
1150
 
1121
1151
  /**
package/lib/cast.js CHANGED
@@ -413,7 +413,7 @@ function _cast(val, numbertype, context) {
413
413
  _cast(item, numbertype, context);
414
414
  val[nkey] = item;
415
415
  } else {
416
- val[nkey] = numbertype.castForQuery({ val: item, context: context });
416
+ val[nkey] = numbertype.castForQuery(null, item, context);
417
417
  }
418
418
  }
419
419
  }
package/lib/connection.js CHANGED
@@ -1005,10 +1005,7 @@ Connection.prototype.error = function error(err, callback) {
1005
1005
  Connection.prototype.onOpen = function() {
1006
1006
  this.readyState = STATES.connected;
1007
1007
 
1008
- for (const d of this._queue) {
1009
- d.fn.apply(d.ctx, d.args);
1010
- }
1011
- this._queue = [];
1008
+ this._flushQueue();
1012
1009
 
1013
1010
  // avoid having the collection subscribe to our event emitter
1014
1011
  // to prevent 0.3 warning
@@ -1021,6 +1018,21 @@ Connection.prototype.onOpen = function() {
1021
1018
  this.emit('open');
1022
1019
  };
1023
1020
 
1021
+ /**
1022
+ * Flush all buffered operations in `_queue`. Called by `onOpen()` and
1023
+ * by the heartbeat handler when a previously-stale connection recovers.
1024
+ *
1025
+ * @api private
1026
+ */
1027
+
1028
+ Connection.prototype._flushQueue = function _flushQueue() {
1029
+ const queue = this._queue;
1030
+ this._queue = [];
1031
+ for (const d of queue) {
1032
+ d.fn.apply(d.ctx, d.args);
1033
+ }
1034
+ };
1035
+
1024
1036
  /**
1025
1037
  * Opens the connection with a URI using `MongoClient.connect()`.
1026
1038
  *
@@ -1033,11 +1045,11 @@ Connection.prototype.onOpen = function() {
1033
1045
  * @param {string} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
1034
1046
  * @param {number} [options.maxPoolSize=100] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
1035
1047
  * @param {number} [options.minPoolSize=0] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
1036
- * @param {number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds).
1037
- * @param {number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation.
1048
+ * @param {number} [options.serverSelectionTimeoutMS] The MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds).
1049
+ * @param {number} [options.heartbeatFrequencyMS] The MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation.
1038
1050
  * @param {boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
1039
1051
  * @param {number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. `socketTimeoutMS` defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
1040
- * @param {number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
1052
+ * @param {number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
1041
1053
  * @param {boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection.
1042
1054
  * @returns {Promise<Connection>}
1043
1055
  * @api public
@@ -1342,7 +1354,7 @@ Connection.prototype.onClose = function onClose(force) {
1342
1354
 
1343
1355
  /**
1344
1356
  * Retrieves a raw collection instance, creating it if not cached.
1345
- * This method returns a thin wrapper around a [MongoDB Node.js driver collection]([MongoDB Node.js driver collection](https://mongodb.github.io/node-mongodb-native/Next/classes/Collection.html)).
1357
+ * This method returns a thin wrapper around a [MongoDB Node.js driver collection](https://mongodb.github.io/node-mongodb-native/Next/classes/Collection.html).
1346
1358
  * Using a Collection bypasses Mongoose middleware, validation, and casting,
1347
1359
  * letting you use [MongoDB Node.js driver](https://mongodb.github.io/node-mongodb-native/) functionality directly.
1348
1360
  *
@@ -10,6 +10,7 @@ const eachAsync = require('../helpers/cursor/eachAsync');
10
10
  const immediate = require('../helpers/immediate');
11
11
  const kareem = require('kareem');
12
12
  const util = require('util');
13
+ const { cursorNextChannel } = require('../tracing');
13
14
 
14
15
  /**
15
16
  * An AggregationCursor is a concurrency primitive for processing aggregation
@@ -62,13 +63,7 @@ util.inherits(AggregationCursor, Readable);
62
63
  */
63
64
 
64
65
  function _init(model, c, agg) {
65
- if (!model.collection.buffer) {
66
- model.hooks.execPre('aggregate', agg).then(() => onPreComplete(null), err => onPreComplete(err));
67
- } else {
68
- model.collection.emitter.once('queue', function() {
69
- model.hooks.execPre('aggregate', agg).then(() => onPreComplete(null), err => onPreComplete(err));
70
- });
71
- }
66
+ model.hooks.execPre('aggregate', agg).then(() => onPreComplete(null), err => onPreComplete(err));
72
67
 
73
68
  function onPreComplete(err) {
74
69
  if (err != null) {
@@ -79,8 +74,28 @@ function _init(model, c, agg) {
79
74
  c._transforms.push(agg.options.cursor.transform);
80
75
  }
81
76
 
82
- c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
83
- c.emit('cursor', c.cursor);
77
+ if (model.collection._shouldBufferCommands() && model.collection.buffer) {
78
+ model.collection.queue.push([
79
+ () => _getRawCursor(model, c, agg)
80
+ ]);
81
+ } else {
82
+ _getRawCursor(model, c, agg);
83
+ }
84
+ }
85
+ }
86
+
87
+ /*!
88
+ * ignore
89
+ */
90
+
91
+ function _getRawCursor(model, aggregationCursor, agg) {
92
+ try {
93
+ const cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
94
+ aggregationCursor.cursor = cursor;
95
+ aggregationCursor.emit('cursor', cursor);
96
+ } catch (err) {
97
+ aggregationCursor._markError(err);
98
+ aggregationCursor.listeners('error').length > 0 && aggregationCursor.emit('error', aggregationCursor._error);
84
99
  }
85
100
  }
86
101
 
@@ -277,14 +292,30 @@ AggregationCursor.prototype.next = async function next() {
277
292
  if (typeof arguments[0] === 'function') {
278
293
  throw new MongooseError('AggregationCursor.prototype.next() no longer accepts a callback');
279
294
  }
280
- return new Promise((resolve, reject) => {
281
- _next(this, (err, res) => {
282
- if (err != null) {
283
- return reject(err);
284
- }
285
- resolve(res);
295
+ const _this = this;
296
+ const model = this.agg._model;
297
+ return cursorNextChannel.trace(function maybeTracedAggCursorNext() {
298
+ return new Promise((resolve, reject) => {
299
+ _next(_this, (err, res) => {
300
+ if (err != null) {
301
+ return reject(err);
302
+ }
303
+ resolve(res);
304
+ });
286
305
  });
287
- });
306
+ }, () => ({
307
+ operation: 'aggregate',
308
+ collection: model?.collection?.name,
309
+ database: model?.db?.name,
310
+ serverAddress: model?.db?.host,
311
+ serverPort: model?.db?.port,
312
+ batchSize: _this.agg.options?.cursor?.batchSize,
313
+ tailable: false,
314
+ args: {
315
+ pipeline: _this.agg._pipeline,
316
+ options: _this.agg.options
317
+ }
318
+ }));
288
319
  };
289
320
 
290
321
  /**
@@ -12,6 +12,7 @@ const kareem = require('kareem');
12
12
  const immediate = require('../helpers/immediate');
13
13
  const { once } = require('events');
14
14
  const util = require('util');
15
+ const { cursorNextChannel } = require('../tracing');
15
16
 
16
17
  /**
17
18
  * A QueryCursor is a concurrency primitive for processing query results
@@ -310,14 +311,29 @@ QueryCursor.prototype.next = async function next() {
310
311
  if (this._closed) {
311
312
  throw new MongooseError('Cannot call `next()` on a closed cursor');
312
313
  }
313
- return new Promise((resolve, reject) => {
314
- _next(this, function(error, doc) {
315
- if (error) {
316
- return reject(error);
317
- }
318
- resolve(doc);
314
+ const _this = this;
315
+ return cursorNextChannel.trace(function maybeTracedQueryCursorNext() {
316
+ return new Promise((resolve, reject) => {
317
+ _next(_this, function(error, doc) {
318
+ if (error) {
319
+ return reject(error);
320
+ }
321
+ resolve(doc);
322
+ });
319
323
  });
320
- });
324
+ }, () => ({
325
+ operation: _this.query.op || 'find',
326
+ collection: _this.query.mongooseCollection.name,
327
+ database: _this.model.db?.name,
328
+ serverAddress: _this.model.db?.host,
329
+ serverPort: _this.model.db?.port,
330
+ batchSize: _this.options.batchSize || _this.query.options?.batchSize,
331
+ tailable: _this.options.tailable || _this.query.options?.tailable || false,
332
+ args: {
333
+ filter: _this.query.getFilter(),
334
+ options: _this.query._mongooseOptions
335
+ }
336
+ }));
321
337
  };
322
338
 
323
339
  /**