mongoose 9.1.6 → 9.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/aggregate.js CHANGED
@@ -11,6 +11,7 @@ const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/ap
11
11
  const clone = require('./helpers/clone');
12
12
  const getConstructorName = require('./helpers/getConstructorName');
13
13
  const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
14
+ const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
14
15
  const stringifyFunctionOperators = require('./helpers/aggregate/stringifyFunctionOperators');
15
16
  const utils = require('./utils');
16
17
  const { modelSymbol } = require('./helpers/symbols');
@@ -718,7 +719,7 @@ Aggregate.prototype.read = function(pref, tags) {
718
719
  *
719
720
  * await Model.aggregate(pipeline).readConcern('majority');
720
721
  *
721
- * @param {String} level one of the listed read concern level or their aliases
722
+ * @param {'local'|'available'|'majority'|'snapshot'|'linearizable'|'l'|'a'|'m'|'s'|'lz'} level one of the listed read concern level or their aliases
722
723
  * @see mongodb https://www.mongodb.com/docs/manual/reference/read-concern/
723
724
  * @return {Aggregate} this
724
725
  * @api public
@@ -784,7 +785,7 @@ Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) {
784
785
  *
785
786
  * Model.aggregate(..).explain()
786
787
  *
787
- * @param {String} [verbosity]
788
+ * @param {'queryPlanner'|'executionStats'|'allPlansExecution'} [verbosity]
788
789
  * @return {Promise}
789
790
  */
790
791
 
@@ -800,13 +801,20 @@ Aggregate.prototype.explain = async function explain(verbosity) {
800
801
 
801
802
  prepareDiscriminatorPipeline(this._pipeline, this._model.schema);
802
803
 
804
+ const preFilter = buildMiddlewareFilter(this.options, 'pre');
805
+ const postFilter = buildMiddlewareFilter(this.options, 'post');
806
+
807
+ // Remove middleware option before passing to MongoDB
808
+ const options = this.options != null ? { ...this.options } : {};
809
+ delete options.middleware;
810
+
803
811
  try {
804
- await model.hooks.execPre('aggregate', this);
812
+ await model.hooks.execPre('aggregate', this, [], { filter: preFilter });
805
813
  } catch (error) {
806
- return await model.hooks.execPost('aggregate', this, [null], { error });
814
+ return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
807
815
  }
808
816
 
809
- const cursor = model.collection.aggregate(this._pipeline, this.options);
817
+ const cursor = await model.collection.aggregate(this._pipeline, options);
810
818
 
811
819
  if (verbosity == null) {
812
820
  verbosity = true;
@@ -816,10 +824,10 @@ Aggregate.prototype.explain = async function explain(verbosity) {
816
824
  try {
817
825
  result = await cursor.explain(verbosity);
818
826
  } catch (error) {
819
- return await model.hooks.execPost('aggregate', this, [null], { error });
827
+ return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
820
828
  }
821
829
 
822
- await model.hooks.execPost('aggregate', this, [result], { error: null });
830
+ await model.hooks.execPost('aggregate', this, [result], { error: null, filter: postFilter });
823
831
 
824
832
  return result;
825
833
  };
@@ -893,6 +901,9 @@ Aggregate.prototype.session = function(session) {
893
901
  * @param {Boolean} [options.allowDiskUse] boolean if true, the MongoDB server will use the hard drive to store data during this aggregation
894
902
  * @param {Object} [options.collation] object see [`Aggregate.prototype.collation()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.collation())
895
903
  * @param {ClientSession} [options.session] ClientSession see [`Aggregate.prototype.session()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.session())
904
+ * @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
905
+ * @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
906
+ * @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
896
907
  * @see mongodb https://www.mongodb.com/docs/manual/reference/command/aggregate/
897
908
  * @return {Aggregate} this
898
909
  * @api public
@@ -1056,10 +1067,13 @@ Aggregate.prototype.exec = async function exec() {
1056
1067
  prepareDiscriminatorPipeline(this._pipeline, this._model.schema);
1057
1068
  stringifyFunctionOperators(this._pipeline);
1058
1069
 
1070
+ const preFilter = buildMiddlewareFilter(this.options, 'pre');
1071
+ const postFilter = buildMiddlewareFilter(this.options, 'post');
1072
+
1059
1073
  try {
1060
- await model.hooks.execPre('aggregate', this);
1074
+ await model.hooks.execPre('aggregate', this, [], { filter: preFilter });
1061
1075
  } catch (error) {
1062
- return await model.hooks.execPost('aggregate', this, [null], { error });
1076
+ return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
1063
1077
  }
1064
1078
 
1065
1079
  if (!this._pipeline.length) {
@@ -1067,17 +1081,17 @@ Aggregate.prototype.exec = async function exec() {
1067
1081
  }
1068
1082
 
1069
1083
  const options = clone(this.options || {});
1084
+ delete options.middleware;
1070
1085
 
1071
1086
  let result;
1072
1087
  try {
1073
1088
  const cursor = await collection.aggregate(this._pipeline, options);
1074
1089
  result = await cursor.toArray();
1075
1090
  } catch (error) {
1076
- return await model.hooks.execPost('aggregate', this, [null], { error });
1091
+ return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter });
1077
1092
  }
1078
1093
 
1079
- await model.hooks.execPost('aggregate', this, [result], { error: null });
1080
-
1094
+ await model.hooks.execPost('aggregate', this, [result], { error: null, filter: postFilter });
1081
1095
  return result;
1082
1096
  };
1083
1097
 
@@ -389,7 +389,7 @@ function _transformForAsyncIterator(doc) {
389
389
  * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/4.9/classes/AggregationCursor.html#addCursorFlag).
390
390
  * Useful for setting the `noCursorTimeout` and `tailable` flags.
391
391
  *
392
- * @param {String} flag
392
+ * @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag
393
393
  * @param {Boolean} value
394
394
  * @return {AggregationCursor} this
395
395
  * @api public
@@ -371,7 +371,7 @@ QueryCursor.prototype.options;
371
371
  * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/4.9/classes/FindCursor.html#addCursorFlag).
372
372
  * Useful for setting the `noCursorTimeout` and `tailable` flags.
373
373
  *
374
- * @param {String} flag
374
+ * @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag
375
375
  * @param {Boolean} value
376
376
  * @return {AggregationCursor} this
377
377
  * @api public
package/lib/document.js CHANGED
@@ -41,6 +41,7 @@ const minimize = require('./helpers/minimize');
41
41
  const mpath = require('mpath');
42
42
  const parentPaths = require('./helpers/path/parentPaths');
43
43
  const queryhelpers = require('./queryHelpers');
44
+ const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
44
45
  const utils = require('./utils');
45
46
  const isPromise = require('./helpers/isPromise');
46
47
 
@@ -860,8 +861,11 @@ function init(self, obj, doc, opts, prefix) {
860
861
  * @param {Object} update
861
862
  * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
862
863
  * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and the [Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
863
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
864
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
864
865
  * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
866
+ * @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
867
+ * @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
868
+ * @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
865
869
  * @return {Query}
866
870
  * @api public
867
871
  * @memberOf Document
@@ -872,7 +876,7 @@ Document.prototype.updateOne = function updateOne(update, options) {
872
876
  const query = this.constructor.updateOne();
873
877
  const self = this;
874
878
  query.pre(async function queryPreUpdateOne() {
875
- const res = await self._execDocumentPreHooks('updateOne', self, update, options);
879
+ const res = await self._execDocumentPreHooks('updateOne', options, [self, update, options]);
876
880
  // `self` is passed to pre hooks as argument for backwards compatibility, but that
877
881
  // isn't the actual arguments passed to the wrapped function.
878
882
  if (res[0] !== self || res[1] !== update || res[2] !== options) {
@@ -892,7 +896,7 @@ Document.prototype.updateOne = function updateOne(update, options) {
892
896
  return res;
893
897
  });
894
898
  query.post(function queryPostUpdateOne() {
895
- return self._execDocumentPostHooks('updateOne');
899
+ return self._execDocumentPostHooks('updateOne', options);
896
900
  });
897
901
 
898
902
  return query;
@@ -2646,6 +2650,9 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) {
2646
2650
  * @param {Object} [options] internal options
2647
2651
  * @param {Boolean} [options.validateModifiedOnly=false] if `true` mongoose validates only modified paths.
2648
2652
  * @param {Array|string} [options.pathsToSkip] list of paths to skip. If set, Mongoose will validate every modified path that is not in this list.
2653
+ * @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
2654
+ * @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
2655
+ * @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
2649
2656
  * @return {Promise} Returns a Promise.
2650
2657
  * @api public
2651
2658
  */
@@ -2961,16 +2968,18 @@ function _pushNestedArrayPaths(val, paths, path) {
2961
2968
  * ignore
2962
2969
  */
2963
2970
 
2964
- Document.prototype._execDocumentPreHooks = async function _execDocumentPreHooks(opName, ...args) {
2965
- return this.$__middleware.execPre(opName, this, [...args]);
2971
+ Document.prototype._execDocumentPreHooks = async function _execDocumentPreHooks(opName, options, argsForHooks) {
2972
+ const filter = buildMiddlewareFilter(options, 'pre');
2973
+ return this.$__middleware.execPre(opName, this, argsForHooks || [], { filter });
2966
2974
  };
2967
2975
 
2968
2976
  /*!
2969
2977
  * ignore
2970
2978
  */
2971
2979
 
2972
- Document.prototype._execDocumentPostHooks = async function _execDocumentPostHooks(opName, error) {
2973
- return this.$__middleware.execPost(opName, this, [this], { error });
2980
+ Document.prototype._execDocumentPostHooks = async function _execDocumentPostHooks(opName, options, error) {
2981
+ const filter = buildMiddlewareFilter(options, 'post');
2982
+ return this.$__middleware.execPost(opName, this, [this], { error, filter });
2974
2983
  };
2975
2984
 
2976
2985
  /*!
@@ -2979,9 +2988,9 @@ Document.prototype._execDocumentPostHooks = async function _execDocumentPostHook
2979
2988
 
2980
2989
  Document.prototype.$__validate = async function $__validate(pathsToValidate, options) {
2981
2990
  try {
2982
- [options] = await this._execDocumentPreHooks('validate', options);
2991
+ [options] = await this._execDocumentPreHooks('validate', options, [options]);
2983
2992
  } catch (error) {
2984
- await this._execDocumentPostHooks('validate', error);
2993
+ await this._execDocumentPostHooks('validate', options, error);
2985
2994
  return;
2986
2995
  }
2987
2996
 
@@ -3085,7 +3094,7 @@ Document.prototype.$__validate = async function $__validate(pathsToValidate, opt
3085
3094
 
3086
3095
  if (paths.length === 0) {
3087
3096
  const error = _complete();
3088
- await this._execDocumentPostHooks('validate', error);
3097
+ await this._execDocumentPostHooks('validate', options, error);
3089
3098
  return;
3090
3099
  }
3091
3100
 
@@ -3108,7 +3117,7 @@ Document.prototype.$__validate = async function $__validate(pathsToValidate, opt
3108
3117
  }
3109
3118
  await Promise.all(promises);
3110
3119
  const error = _complete();
3111
- await this._execDocumentPostHooks('validate', error);
3120
+ await this._execDocumentPostHooks('validate', options, error);
3112
3121
 
3113
3122
  async function validatePath(path) {
3114
3123
  if (path == null || validated[path]) {
@@ -3233,6 +3242,9 @@ function _handlePathsToSkip(paths, pathsToSkip) {
3233
3242
  * @param {Object} [options] options for validation
3234
3243
  * @param {Boolean} [options.validateModifiedOnly=false] If `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths.
3235
3244
  * @param {Array|string} [options.pathsToSkip] list of paths to skip. If set, Mongoose will validate every modified path that is not in this list.
3245
+ * @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
3246
+ * @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
3247
+ * @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
3236
3248
  * @return {ValidationError|undefined} ValidationError if there are errors during validation, or undefined if there is no error.
3237
3249
  * @api public
3238
3250
  */
@@ -4162,6 +4174,7 @@ Document.prototype.$__toObjectShallow = function $__toObjectShallow(schemaFields
4162
4174
  * @param {Boolean} [options.versionKey=true] if false, exclude the version key (`__v` by default) from the output
4163
4175
  * @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
4164
4176
  * @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
4177
+ * @param {Boolean} [options.flattenUUIDs=false] if true, convert any UUIDs in the result to 36-character UUID strings in 8-4-4-4-12 format.
4165
4178
  * @param {Boolean} [options.useProjection=false] - If true, omits fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema.
4166
4179
  * @param {Boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toObject()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema.
4167
4180
  * @return {Object} document as a plain old JavaScript object (POJO). This object may contain ObjectIds, Maps, Dates, mongodb.Binary, Buffers, and other non-POJO values.
@@ -4434,6 +4447,7 @@ function omitDeselectedFields(self, json) {
4434
4447
  * @param {Object} options
4435
4448
  * @param {Boolean} [options.flattenMaps=true] if true, convert Maps to [POJOs](https://masteringjs.io/tutorials/fundamentals/pojo). Useful if you want to `JSON.stringify()` the result.
4436
4449
  * @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
4450
+ * @param {Boolean} [options.flattenUUIDs=false] if true, convert any UUIDs in the result to 36-character UUID strings in 8-4-4-4-12 format.
4437
4451
  * @param {Boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toJSON()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema.
4438
4452
  * @return {Object}
4439
4453
  * @see Document#toObject https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ const symbols = require('../schema/symbols');
4
+
5
+ /**
6
+ * Filter predicate that returns true for built-in middleware (marked with `builtInMiddleware` symbol).
7
+ */
8
+ const isBuiltInMiddleware = hook => hook.fn[symbols.builtInMiddleware];
9
+
10
+ /**
11
+ * Builds a filter for kareem's execPre/execPost based on middleware options.
12
+ *
13
+ * @param {Object} options - Options object that may contain `middleware` setting
14
+ * @param {String} phase - Either 'pre' or 'post'
15
+ * @returns {Function|null} - null runs all middleware, isBuiltInMiddleware skips user middleware
16
+ */
17
+ function buildMiddlewareFilter(options, phase) {
18
+ const shouldRun = options?.middleware?.[phase] ?? options?.middleware ?? true;
19
+ return shouldRun ? null : isBuiltInMiddleware;
20
+ }
21
+
22
+ module.exports = {
23
+ buildMiddlewareFilter
24
+ };
@@ -12,6 +12,9 @@ const isPOJO = require('./isPOJO');
12
12
  const symbols = require('./symbols');
13
13
  const trustedSymbol = require('./query/trusted').trustedSymbol;
14
14
  const BSON = require('mongodb/lib/bson');
15
+ const UUID = BSON.UUID;
16
+
17
+ const Binary = BSON.Binary;
15
18
 
16
19
  /**
17
20
  * Object clone with Mongoose natives support.
@@ -45,6 +48,14 @@ function clone(obj, options, isArrayChild) {
45
48
 
46
49
  if (isMongooseObject(obj)) {
47
50
  if (options) {
51
+ if (options.flattenUUIDs) {
52
+ if (obj instanceof Binary && obj._subtype === Binary.SUBTYPE_UUID) {
53
+ return obj.toString();
54
+ }
55
+ if (obj?.isMongooseBuffer && obj._subtype === Binary.SUBTYPE_UUID) {
56
+ return obj.toUUID().toString();
57
+ }
58
+ }
48
59
  if (options.retainDocuments && obj.$__ != null) {
49
60
  const clonedDoc = obj.$clone();
50
61
  if (obj.__index != null) {
@@ -111,6 +122,13 @@ function clone(obj, options, isArrayChild) {
111
122
  return Decimal.fromString(obj.toString());
112
123
  }
113
124
 
125
+ if (obj instanceof UUID) {
126
+ if (options?.flattenUUIDs) {
127
+ return obj.toJSON();
128
+ }
129
+ return new UUID(obj.buffer);
130
+ }
131
+
114
132
  // object created with Object.create(null)
115
133
  if (!objConstructor && isObject(obj)) {
116
134
  return cloneObject(obj, options, isArrayChild);
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { buildMiddlewareFilter } = require('../buildMiddlewareFilter');
4
+
3
5
  /*!
4
6
  * ignore
5
7
  */
@@ -84,7 +86,16 @@ function applyHooks(model, schema, options) {
84
86
  model._middleware = middleware;
85
87
 
86
88
  objToDecorate.$__init = middleware.
87
- createWrapperSync('init', objToDecorate.$__init, null, kareemOptions);
89
+ createWrapperSync('init', objToDecorate.$__init, null, {
90
+ ...kareemOptions,
91
+ getOptions: (args) => {
92
+ const opts = args[1];
93
+ return {
94
+ pre: { filter: buildMiddlewareFilter(opts, 'pre') },
95
+ post: { filter: buildMiddlewareFilter(opts, 'post') }
96
+ };
97
+ }
98
+ });
88
99
 
89
100
  // Support hooks for custom methods
90
101
  const customMethods = Object.keys(schema.methods);
@@ -41,7 +41,7 @@ const mongodbUpdateOperators = new Set([
41
41
  * @param {Schema} schema
42
42
  * @param {Object} obj
43
43
  * @param {Object} [options]
44
- * @param {Boolean|String} [options.strict] defaults to true
44
+ * @param {Boolean|'throw'} [options.strict] defaults to true
45
45
  * @param {Query} context passed to setters
46
46
  * @return {Boolean} true iff the update is non-empty
47
47
  * @api private
@@ -205,7 +205,7 @@ function castPipelineOperator(op, val) {
205
205
  * @param {Object} obj part of a query
206
206
  * @param {String} op the atomic operator ($pull, $set, etc)
207
207
  * @param {Object} [options]
208
- * @param {Boolean|String} [options.strict]
208
+ * @param {Boolean|'throw'} [options.strict]
209
209
  * @param {Query} context
210
210
  * @param {Object} filter
211
211
  * @param {String} pref path prefix (internal only)
@@ -2,7 +2,6 @@
2
2
 
3
3
  const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
4
4
  const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
5
- const get = require('../get');
6
5
  const handleTimestampOption = require('../schema/handleTimestampOption');
7
6
  const setDocumentTimestamps = require('./setDocumentTimestamps');
8
7
  const symbols = require('../../schema/symbols');
@@ -42,14 +41,7 @@ module.exports = function setupTimestamps(schema, timestamps) {
42
41
 
43
42
  schema.add(schemaAdditions);
44
43
 
45
- schema.pre('save', function timestampsPreSave() {
46
- const timestampOption = get(this, '$__.saveOptions.timestamps');
47
- if (timestampOption === false) {
48
- return;
49
- }
50
-
51
- setDocumentTimestamps(this, timestampOption, currentTime, createdAt, updatedAt);
52
- });
44
+ schema.pre('save', timestampsPreSave);
53
45
 
54
46
  schema.methods.initializeTimestamps = function(timestampsOptions) {
55
47
  if (timestampsOptions === false) {
@@ -85,8 +77,6 @@ module.exports = function setupTimestamps(schema, timestamps) {
85
77
  return this;
86
78
  };
87
79
 
88
- _setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
89
-
90
80
  const opts = { query: true, model: false };
91
81
  schema.pre('findOneAndReplace', opts, _setTimestampsOnUpdate);
92
82
  schema.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate);
@@ -94,23 +84,49 @@ module.exports = function setupTimestamps(schema, timestamps) {
94
84
  schema.pre('update', opts, _setTimestampsOnUpdate);
95
85
  schema.pre('updateOne', opts, _setTimestampsOnUpdate);
96
86
  schema.pre('updateMany', opts, _setTimestampsOnUpdate);
87
+ };
97
88
 
98
- function _setTimestampsOnUpdate() {
99
- const now = currentTime != null ?
100
- currentTime() :
101
- this.model.base.now();
102
- // Replacing with null update should still trigger timestamps
103
- if (replaceOps.has(this.op) && this.getUpdate() == null) {
104
- this.setUpdate({});
105
- }
106
- applyTimestampsToUpdate(
107
- now,
108
- createdAt,
109
- updatedAt,
110
- this.getUpdate(),
111
- this._mongooseOptions,
112
- replaceOps.has(this.op)
113
- );
114
- applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
89
+ function timestampsPreSave() {
90
+ const timestampOption = this.$__?.saveOptions?.timestamps;
91
+ if (timestampOption === false) {
92
+ return;
115
93
  }
116
- };
94
+
95
+ const schema = this.$__schema;
96
+ const { createdAt, updatedAt } = schema.$timestamps;
97
+ const timestamps = schema.options.timestamps;
98
+ const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ?
99
+ timestamps.currentTime :
100
+ null;
101
+
102
+ setDocumentTimestamps(this, timestampOption, currentTime, createdAt, updatedAt);
103
+ }
104
+
105
+ function _setTimestampsOnUpdate() {
106
+ const schema = this.model.schema;
107
+ const { createdAt, updatedAt } = schema.$timestamps;
108
+ const timestamps = schema.options.timestamps;
109
+ const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ?
110
+ timestamps.currentTime :
111
+ null;
112
+
113
+ const now = currentTime != null ?
114
+ currentTime() :
115
+ this.model.base.now();
116
+ // Replacing with null update should still trigger timestamps
117
+ if (replaceOps.has(this.op) && this.getUpdate() == null) {
118
+ this.setUpdate({});
119
+ }
120
+ applyTimestampsToUpdate(
121
+ now,
122
+ createdAt,
123
+ updatedAt,
124
+ this.getUpdate(),
125
+ this._mongooseOptions,
126
+ replaceOps.has(this.op)
127
+ );
128
+ applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
129
+ }
130
+
131
+ timestampsPreSave[symbols.builtInMiddleware] = true;
132
+ _setTimestampsOnUpdate[symbols.builtInMiddleware] = true;