mongoose 8.22.1 → 8.23.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/document.js CHANGED
@@ -4149,6 +4149,7 @@ Document.prototype.$__toObjectShallow = function $__toObjectShallow(schemaFields
4149
4149
  * @param {Boolean} [options.versionKey=true] if false, exclude the version key (`__v` by default) from the output
4150
4150
  * @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
4151
4151
  * @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
4152
+ * @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.
4152
4153
  * @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.
4153
4154
  * @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.
4154
4155
  * @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.
@@ -4421,6 +4422,7 @@ function omitDeselectedFields(self, json) {
4421
4422
  * @param {Object} options
4422
4423
  * @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.
4423
4424
  * @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
4425
+ * @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.
4424
4426
  * @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.
4425
4427
  * @return {Object}
4426
4428
  * @see Document#toObject https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()
@@ -13,6 +13,9 @@ const symbols = require('./symbols');
13
13
  const trustedSymbol = require('./query/trusted').trustedSymbol;
14
14
  const BSON = require('bson');
15
15
 
16
+ const Binary = BSON.Binary;
17
+ const UUID = BSON.UUID;
18
+
16
19
  /**
17
20
  * Object clone with Mongoose natives support.
18
21
  *
@@ -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);
@@ -25,7 +25,7 @@ module.exports = function(filter, schema, castedDoc, options) {
25
25
  }
26
26
 
27
27
  const keys = Object.keys(castedDoc || {});
28
- const updatedKeys = {};
28
+ const updatedKeys = Object.create(null);
29
29
  const updatedValues = {};
30
30
  const numKeys = keys.length;
31
31
 
@@ -58,6 +58,20 @@ module.exports = function(filter, schema, castedDoc, options) {
58
58
  }
59
59
  }
60
60
  updatedKeys[path] = true;
61
+ if (path.indexOf('.') === -1) {
62
+ continue;
63
+ }
64
+ // Also mark all parent prefixes so child-path lookups are O(1).
65
+ // e.g. 'extraProps.location' also marks 'extraProps'
66
+ const pieces = schema.paths[path] ?
67
+ // If the SchemaType already split for us, use that to avoid the extra overhead
68
+ schema.paths[path].splitPath() :
69
+ path.split('.');
70
+ let cur = pieces[0];
71
+ for (let j = 1; j < pieces.length; ++j) {
72
+ updatedKeys[cur] = true;
73
+ cur += '.' + pieces[j];
74
+ }
61
75
  }
62
76
 
63
77
  if (options && options.overwrite && !hasDollarUpdate) {
package/lib/model.js CHANGED
@@ -3490,7 +3490,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3490
3490
  sort((v1, v2) => v1.index - v2.index).
3491
3491
  map(v => v.error);
3492
3492
 
3493
- const validOps = validOpIndexes.sort().map(index => ops[index]);
3493
+ const validOps = validOpIndexes.sort((a, b) => a - b).map(index => ops[index]);
3494
3494
 
3495
3495
  if (validOps.length === 0) {
3496
3496
  if (options.throwOnValidationError && validationErrors.length) {
package/lib/options.js CHANGED
@@ -13,5 +13,6 @@ exports.internalToObjectOptions = {
13
13
  flattenDecimals: false,
14
14
  useProjection: false,
15
15
  versionKey: true,
16
- flattenObjectIds: false
16
+ flattenObjectIds: false,
17
+ flattenUUIDs: false
17
18
  };
@@ -474,12 +474,14 @@ SchemaString.prototype.maxlength = function(value, message) {
474
474
 
475
475
  if (value !== null && value !== undefined) {
476
476
  let msg = message || MongooseError.messages.String.maxlength;
477
- msg = msg.replace(/{MAXLENGTH}/, value);
477
+ if (typeof msg !== 'function') {
478
+ msg = msg.replace(/{MAXLENGTH}/, value);
479
+ }
478
480
  this.validators.push({
479
481
  validator: this.maxlengthValidator = function(v) {
480
482
  return v === null || v.length <= value;
481
483
  },
482
- message: msg,
484
+ message: formatMaxLengthValidatorMessage(msg),
483
485
  type: 'maxlength',
484
486
  maxlength: value
485
487
  });
@@ -731,3 +733,18 @@ SchemaString.prototype.autoEncryptionType = function autoEncryptionType() {
731
733
  */
732
734
 
733
735
  module.exports = SchemaString;
736
+
737
+ function formatMaxLengthValidatorMessage(msg) {
738
+ return function(props, doc) {
739
+ if (typeof msg === 'function') {
740
+ return MongooseError.ValidatorError.prototype.formatMessage(msg, props, doc);
741
+ }
742
+
743
+ if (typeof msg === 'string' && typeof props.value === 'string' && props.value.length > 30) {
744
+ props = Object.assign({}, props, {
745
+ value: props.value.slice(0, 30) + '...'
746
+ });
747
+ }
748
+ return MongooseError.ValidatorError.prototype.formatMessage(msg, props, doc);
749
+ };
750
+ }
package/lib/schemaType.js CHANGED
@@ -1381,9 +1381,6 @@ SchemaType.prototype.doValidate = function(value, fn, scope, options) {
1381
1381
  validatorProperties.value = value;
1382
1382
  if (typeof value === 'string') {
1383
1383
  validatorProperties.length = value.length;
1384
- if (validatorProperties.value.length > 30) {
1385
- validatorProperties.value = validatorProperties.value.slice(0, 30) + '...';
1386
- }
1387
1384
  }
1388
1385
 
1389
1386
  if (validator instanceof RegExp) {
@@ -1508,9 +1505,6 @@ SchemaType.prototype.doValidateSync = function(value, scope, options) {
1508
1505
  validatorProperties.value = value;
1509
1506
  if (typeof value === 'string') {
1510
1507
  validatorProperties.length = value.length;
1511
- if (validatorProperties.value.length > 30) {
1512
- validatorProperties.value = validatorProperties.value.slice(0, 30) + '...';
1513
- }
1514
1508
  }
1515
1509
  let ok = false;
1516
1510
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.22.1",
4
+ "version": "8.23.1",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -258,33 +258,13 @@ declare module 'mongoose' {
258
258
  toBSON(): Require_id<DocType>;
259
259
 
260
260
  /** The return value of this method is used in calls to JSON.stringify(doc). */
261
- toJSON(options: ToObjectOptions & { versionKey: false, virtuals: true, flattenObjectIds: true }): Omit<ObjectIdToString<FlattenMaps<Require_id<DocType & TVirtuals>>>, '__v'>;
262
- toJSON(options: ToObjectOptions & { virtuals: true, flattenObjectIds: true }): ObjectIdToString<FlattenMaps<Default__v<Require_id<DocType & TVirtuals>, ResolveSchemaOptions<TSchemaOptions>>>>;
263
- toJSON(options: ToObjectOptions & { versionKey: false, virtuals: true }): Omit<Require_id<DocType & TVirtuals>, '__v'>;
264
- toJSON(options: ToObjectOptions & { versionKey: false, flattenObjectIds: true }): ObjectIdToString<FlattenMaps<Omit<Require_id<DocType>, '__v'>>>;
265
- toJSON(options: ToObjectOptions & { virtuals: true }): Default__v<Require_id<DocType & TVirtuals>, ResolveSchemaOptions<TSchemaOptions>>;
266
- toJSON(options: ToObjectOptions & { versionKey: false }): Omit<Require_id<DocType & TVirtuals>, '__v'>;
267
- toJSON(options?: ToObjectOptions & { flattenMaps?: true, flattenObjectIds?: false }): FlattenMaps<Default__v<Require_id<DocType>, ResolveSchemaOptions<TSchemaOptions>>>;
268
- toJSON(options: ToObjectOptions & { flattenObjectIds: false }): FlattenMaps<Default__v<Require_id<DocType>, ResolveSchemaOptions<TSchemaOptions>>>;
269
- toJSON(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<FlattenMaps<Default__v<Require_id<DocType>, ResolveSchemaOptions<TSchemaOptions>>>>;
270
- toJSON(options: ToObjectOptions & { flattenMaps: false }): Default__v<Require_id<DocType>, ResolveSchemaOptions<TSchemaOptions>>;
271
- toJSON(options: ToObjectOptions & { flattenMaps: false, flattenObjectIds: true }): ObjectIdToString<Default__v<Require_id<DocType>, ResolveSchemaOptions<TSchemaOptions>>>;
272
-
273
- toJSON<T = Default__v<Require_id<DocType>, TSchemaOptions>>(options?: ToObjectOptions & { flattenMaps?: true, flattenObjectIds?: false }): FlattenMaps<T>;
274
- toJSON<T = Default__v<Require_id<DocType>, TSchemaOptions>>(options: ToObjectOptions & { flattenObjectIds: false }): FlattenMaps<T>;
275
- toJSON<T = Default__v<Require_id<DocType>, TSchemaOptions>>(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<FlattenMaps<T>>;
276
- toJSON<T = Default__v<Require_id<DocType>, TSchemaOptions>>(options: ToObjectOptions & { flattenMaps: false }): T;
277
- toJSON<T = Default__v<Require_id<DocType>, TSchemaOptions>>(options: ToObjectOptions & { flattenMaps: false, flattenObjectIds: true }): ObjectIdToString<T>;
261
+ toJSON<O extends ToObjectOptions>(options: O): ToObjectReturnType<DocType, TVirtuals, O, TSchemaOptions>;
262
+ toJSON(): Default__v<Require_id<FlattenMaps<DocType>>, TSchemaOptions>;
263
+ toJSON<T>(options?: ToObjectOptions): Default__v<Require_id<T>, ResolveSchemaOptions<TSchemaOptions>>;
278
264
 
279
265
  /** Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). */
280
- toObject(options: ToObjectOptions & { versionKey: false, virtuals: true, flattenObjectIds: true }): Omit<ObjectIdToString<Require_id<DocType & TVirtuals>>, '__v'>;
281
- toObject(options: ToObjectOptions & { virtuals: true, flattenObjectIds: true }): ObjectIdToString<Default__v<Require_id<DocType & TVirtuals>, ResolveSchemaOptions<TSchemaOptions>>>;
282
- toObject(options: ToObjectOptions & { versionKey: false, flattenObjectIds: true }): Omit<ObjectIdToString<Require_id<DocType & TVirtuals>>, '__v'>;
283
- toObject(options: ToObjectOptions & { versionKey: false, virtuals: true }): Omit<Require_id<DocType & TVirtuals>, '__v'>;
284
- toObject(options: ToObjectOptions & { virtuals: true }): Default__v<Require_id<DocType & TVirtuals>, ResolveSchemaOptions<TSchemaOptions>>;
285
- toObject(options: ToObjectOptions & { versionKey: false }): Omit<Require_id<DocType & TVirtuals>, '__v'>;
286
- toObject(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString<Default__v<Require_id<DocType & TVirtuals>, ResolveSchemaOptions<TSchemaOptions>>>;
287
- toObject(options?: ToObjectOptions): Default__v<Require_id<DocType>, ResolveSchemaOptions<TSchemaOptions>>;
266
+ toObject<O extends ToObjectOptions>(options: O): ToObjectReturnType<DocType, TVirtuals, O, TSchemaOptions>;
267
+ toObject(): Default__v<Require_id<DocType>, TSchemaOptions>;
288
268
  toObject<T>(options?: ToObjectOptions): Default__v<Require_id<T>, ResolveSchemaOptions<TSchemaOptions>>;
289
269
 
290
270
  /** Clears the modified state on the specified path. */
package/types/index.d.ts CHANGED
@@ -230,6 +230,8 @@ declare module 'mongoose' {
230
230
  flattenMaps?: boolean;
231
231
  /** if true, convert any ObjectIds in the result to 24 character hex strings. */
232
232
  flattenObjectIds?: boolean;
233
+ /** if true, convert any UUIDs in the result to 36 character hex strings. */
234
+ flattenUUIDs?: boolean;
233
235
  /** apply all getters (path and virtual getters) */
234
236
  getters?: boolean;
235
237
  /** remove empty objects (defaults to true) */
@@ -854,6 +856,35 @@ declare module 'mongoose' {
854
856
  : BufferToBinary<T[K]>;
855
857
  } : T;
856
858
 
859
+ /**
860
+ * Converts any UUID properties into strings for JSON serialization
861
+ */
862
+ export type UUIDToString<T> = T extends Types.UUID
863
+ ? string
864
+ : T extends mongodb.UUID
865
+ ? string
866
+ : T extends Document
867
+ ? T
868
+ : T extends TreatAsPrimitives
869
+ ? T
870
+ : T extends Record<string, any> ? {
871
+ [K in keyof T]: T[K] extends Types.UUID
872
+ ? string
873
+ : T[K] extends mongodb.UUID
874
+ ? string
875
+ : T[K] extends Types.DocumentArray<infer ItemType>
876
+ ? Types.DocumentArray<UUIDToString<ItemType>>
877
+ : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
878
+ ? HydratedSingleSubdocument<UUIDToString<SubdocType>>
879
+ : UUIDToString<T[K]>;
880
+ } : T;
881
+
882
+ /**
883
+ * Alias for UUIDToString for backwards compatibility.
884
+ * @deprecated Use UUIDToString instead.
885
+ */
886
+ export type UUIDToJSON<T> = UUIDToString<T>;
887
+
857
888
  /**
858
889
  * Converts any ObjectId properties into strings for JSON serialization
859
890
  */
@@ -913,12 +944,85 @@ declare module 'mongoose' {
913
944
  FlattenMaps<
914
945
  BufferToJSON<
915
946
  ObjectIdToString<
916
- DateToString<T>
947
+ UUIDToString<
948
+ DateToString<T>
949
+ >
917
950
  >
918
951
  >
919
952
  >
920
953
  >;
921
954
 
955
+ /**
956
+ * Helper types for computing toObject/toJSON return types based on options.
957
+ * These compose transforms conditionally to avoid combinatorial explosion of overloads.
958
+ */
959
+ type ApplyVirtuals<T, TVirtuals, O> =
960
+ O extends { virtuals: true } ? T & TVirtuals : T;
961
+
962
+ type GetVersionKeyName<TSchemaOptions> =
963
+ TSchemaOptions extends { versionKey: infer VK }
964
+ ? VK extends string ? VK : '__v'
965
+ : '__v';
966
+
967
+ type ApplyVersionKey<T, O, TSchemaOptions = {}> =
968
+ O extends { versionKey: false } ? Omit<T, GetVersionKeyName<TSchemaOptions>> : T;
969
+
970
+ /**
971
+ * Single-pass type that applies all flatten transforms (flattenMaps, flattenObjectIds, flattenUUIDs)
972
+ * in one recursive traversal. This avoids TypeScript's "union type too complex" error that occurs
973
+ * when nesting separate recursive transform types.
974
+ *
975
+ * By handling all transforms in one pass, we correctly handle ObjectIds/UUIDs nested inside Maps
976
+ * since we recurse into Map values during the same traversal that converts ObjectIds/UUIDs.
977
+ */
978
+ type ApplyFlattenTransforms<T, O> =
979
+ // Handle ObjectId first (before TreatAsPrimitives since ObjectId is in TreatAsPrimitives)
980
+ T extends mongodb.ObjectId
981
+ ? O extends { flattenObjectIds: true } ? string : T
982
+ // Handle UUID (both Types.UUID and mongodb.UUID)
983
+ : T extends Types.UUID
984
+ ? O extends { flattenUUIDs: true } ? string : T
985
+ : T extends mongodb.UUID
986
+ ? O extends { flattenUUIDs: true } ? string : T
987
+ // Handle Map - flatten to Record and recurse into values
988
+ : T extends Map<any, infer V>
989
+ ? O extends { flattenMaps: true }
990
+ ? Record<string, ApplyFlattenTransforms<V, O>>
991
+ : T
992
+ // Don't recurse into Documents
993
+ : T extends Document
994
+ ? T
995
+ // Don't modify primitives
996
+ : T extends TreatAsPrimitives
997
+ ? T
998
+ // Handle DocumentArray - recurse into items
999
+ : T extends Types.DocumentArray<infer ItemType>
1000
+ ? Types.DocumentArray<ApplyFlattenTransforms<ItemType, O>>
1001
+ // Handle Subdocument - recurse into subdoc type
1002
+ : T extends Types.Subdocument<unknown, unknown, infer SubdocType>
1003
+ ? HydratedSingleSubdocument<ApplyFlattenTransforms<SubdocType, O>>
1004
+ // Handle regular objects - recurse into properties
1005
+ : T extends Record<string, any>
1006
+ ? { [K in keyof T]: ApplyFlattenTransforms<T[K], O> }
1007
+ : T;
1008
+
1009
+ /**
1010
+ * Computes the return type of toObject/toJSON based on the provided options.
1011
+ * Uses a single-pass transform for flatten operations to correctly handle all combinations.
1012
+ */
1013
+ export type ToObjectReturnType<DocType, TVirtuals, O extends ToObjectOptions, TSchemaOptions = {}> =
1014
+ ApplyVersionKey<
1015
+ Default__v<
1016
+ ApplyFlattenTransforms<
1017
+ ApplyVirtuals<Require_id<DocType>, TVirtuals, O>,
1018
+ O
1019
+ >,
1020
+ TSchemaOptions
1021
+ >,
1022
+ O,
1023
+ TSchemaOptions
1024
+ >;
1025
+
922
1026
  /**
923
1027
  * Separate type is needed for properties of union type (for example, Types.DocumentArray | undefined) to apply conditional check to each member of it
924
1028
  * https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
@@ -103,7 +103,12 @@ declare module 'mongoose' {
103
103
  }[alias]
104
104
  : unknown;
105
105
 
106
- type ResolveSchemaOptions<T> = MergeType<DefaultSchemaOptions, T>;
106
+ type TypesAreEqual<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false;
107
+
108
+ type ResolveSchemaOptions<T> =
109
+ keyof T extends never ? DefaultSchemaOptions :
110
+ TypesAreEqual<T, DefaultSchemaOptions> extends true ? DefaultSchemaOptions :
111
+ MergeType<DefaultSchemaOptions, T>;
107
112
 
108
113
  type ApplySchemaOptions<T, O = DefaultSchemaOptions> = ResolveTimestamps<T, O>;
109
114