mongoose 9.3.3 → 9.4.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.
@@ -8,7 +8,6 @@ const MongooseError = require('../error/index');
8
8
  const SchemaNumberOptions = require('../options/schemaNumberOptions');
9
9
  const SchemaType = require('../schemaType');
10
10
  const castNumber = require('../cast/number');
11
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
12
11
  const handleBitwiseOperator = require('./operators/bitwise');
13
12
  const utils = require('../utils');
14
13
 
@@ -473,7 +472,7 @@ SchemaNumber.prototype.castForQuery = function($conditional, val, context) {
473
472
  if ($conditional != null) {
474
473
  handler = this.$conditionalHandlers[$conditional];
475
474
  if (!handler) {
476
- throw new CastError('number', val, this.path, null, this);
475
+ throw new MongooseError('Can\'t use ' + $conditional + ' with Number.');
477
476
  }
478
477
  return handler.call(this, val, context);
479
478
  }
@@ -499,8 +498,7 @@ SchemaNumber.prototype.castForQuery = function($conditional, val, context) {
499
498
  */
500
499
 
501
500
  SchemaNumber.prototype.toJSONSchema = function toJSONSchema(options) {
502
- const isRequired = (this.options.required && typeof this.options.required !== 'function') || this.path === '_id';
503
- return createJSONSchemaTypeDefinition('number', 'number', options?.useBsonType, isRequired);
501
+ return this._createJSONSchemaTypeDefinition('number', 'number', options);
504
502
  };
505
503
 
506
504
  /*!
@@ -7,7 +7,6 @@
7
7
  const SchemaObjectIdOptions = require('../options/schemaObjectIdOptions');
8
8
  const SchemaType = require('../schemaType');
9
9
  const castObjectId = require('../cast/objectid');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
  const getConstructorName = require('../helpers/getConstructorName');
12
11
  const oid = require('../types/objectid');
13
12
  const isBsonType = require('../helpers/isBsonType');
@@ -318,8 +317,7 @@ function resetId(v) {
318
317
  */
319
318
 
320
319
  SchemaObjectId.prototype.toJSONSchema = function toJSONSchema(options) {
321
- const isRequired = (this.options.required && typeof this.options.required !== 'function') || this.path === '_id';
322
- return createJSONSchemaTypeDefinition('string', 'objectId', options?.useBsonType, isRequired);
320
+ return this._createJSONSchemaTypeDefinition('string', 'objectId', options);
323
321
  };
324
322
 
325
323
  SchemaObjectId.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -8,7 +8,6 @@ const SchemaType = require('../schemaType');
8
8
  const MongooseError = require('../error/index');
9
9
  const SchemaStringOptions = require('../options/schemaStringOptions');
10
10
  const castString = require('../cast/string');
11
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
12
11
  const utils = require('../utils');
13
12
  const isBsonType = require('../helpers/isBsonType');
14
13
 
@@ -718,8 +717,7 @@ SchemaString.prototype.castForQuery = function($conditional, val, context) {
718
717
  */
719
718
 
720
719
  SchemaString.prototype.toJSONSchema = function toJSONSchema(options) {
721
- const isRequired = this.options.required && typeof this.options.required !== 'function';
722
- return createJSONSchemaTypeDefinition('string', 'string', options?.useBsonType, isRequired);
720
+ return this._createJSONSchemaTypeDefinition('string', 'string', options);
723
721
  };
724
722
 
725
723
  SchemaString.prototype.autoEncryptionType = function autoEncryptionType() {
@@ -12,7 +12,6 @@ const SchemaType = require('../schemaType');
12
12
  const applyDefaults = require('../helpers/document/applyDefaults');
13
13
  const $exists = require('./operators/exists');
14
14
  const castToNumber = require('./operators/helpers').castToNumber;
15
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
16
15
  const discriminator = require('../helpers/model/discriminator');
17
16
  const geospatial = require('./operators/geospatial');
18
17
  const getConstructor = require('../helpers/discriminator/getConstructor');
@@ -428,9 +427,8 @@ SchemaSubdocument.prototype.clone = function() {
428
427
  */
429
428
 
430
429
  SchemaSubdocument.prototype.toJSONSchema = function toJSONSchema(options) {
431
- const isRequired = this.options.required && typeof this.options.required !== 'function';
432
430
  return {
433
431
  ...this.schema.toJSONSchema(options),
434
- ...createJSONSchemaTypeDefinition('object', 'object', options?.useBsonType, isRequired)
432
+ ...this._createJSONSchemaTypeDefinition('object', 'object', options)
435
433
  };
436
434
  };
@@ -28,6 +28,7 @@ class Union extends SchemaType {
28
28
  throw new Error('Union schema type requires an array of types');
29
29
  }
30
30
  this.schemaTypes = options.of.map(obj => parentSchema.interpretAsType(key, obj, schemaOptions));
31
+ this.$isSchemaUnion = true;
31
32
  }
32
33
 
33
34
  cast(val, doc, init, prev, options) {
@@ -90,12 +91,61 @@ class Union extends SchemaType {
90
91
  throw lastError;
91
92
  }
92
93
 
94
+ async doValidate(value, scope, options) {
95
+ if (options && options.skipSchemaValidators) {
96
+ if (value != null && typeof value.validate === 'function') {
97
+ return value.validate();
98
+ }
99
+ return;
100
+ }
101
+
102
+ await super.doValidate(value, scope, options);
103
+ if (value != null && typeof value.validate === 'function') {
104
+ await value.validate();
105
+ }
106
+ }
107
+
108
+ doValidateSync(value, scope, options) {
109
+ if (!options || !options.skipSchemaValidators) {
110
+ const schemaTypeError = super.doValidateSync(value, scope, options);
111
+ if (schemaTypeError) {
112
+ return schemaTypeError;
113
+ }
114
+ }
115
+ if (value != null && typeof value.validateSync === 'function') {
116
+ return value.validateSync();
117
+ }
118
+ }
119
+
93
120
  clone() {
94
121
  const schematype = super.clone();
95
122
 
96
123
  schematype.schemaTypes = this.schemaTypes.map(schemaType => schemaType.clone());
97
124
  return schematype;
98
125
  }
126
+
127
+ /**
128
+ * Returns this schema type's representation in a JSON schema.
129
+ *
130
+ * @param {object} [options]
131
+ * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`.
132
+ * @returns {object} JSON schema properties
133
+ */
134
+ toJSONSchema(options) {
135
+ const isRequired = this.options.required && typeof this.options.required !== 'function';
136
+ const childOptions = { ...options, _overrideRequired: true };
137
+ const jsonSchemas = this.schemaTypes.map(schemaType => schemaType.toJSONSchema(childOptions));
138
+ if (isRequired) {
139
+ return { anyOf: jsonSchemas };
140
+ }
141
+
142
+ return {
143
+ anyOf: [
144
+ options?.useBsonType ? { bsonType: 'null' } : { type: 'null' },
145
+ ...jsonSchemas
146
+ ]
147
+ };
148
+ }
99
149
  }
100
150
 
101
151
  /**
@@ -7,7 +7,6 @@
7
7
  const SchemaType = require('../schemaType');
8
8
  const CastError = SchemaType.CastError;
9
9
  const castUUID = require('../cast/uuid');
10
- const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition');
11
10
  const utils = require('../utils');
12
11
  const handleBitwiseOperator = require('./operators/bitwise');
13
12
 
@@ -290,8 +289,7 @@ SchemaUUID.prototype.castForQuery = function($conditional, val, context) {
290
289
  */
291
290
 
292
291
  SchemaUUID.prototype.toJSONSchema = function toJSONSchema(options) {
293
- const isRequired = this.options.required && typeof this.options.required !== 'function';
294
- return createJSONSchemaTypeDefinition('string', 'binData', options?.useBsonType, isRequired);
292
+ return this._createJSONSchemaTypeDefinition('string', 'binData', options);
295
293
  };
296
294
 
297
295
  SchemaUUID.prototype.autoEncryptionType = function autoEncryptionType() {
package/lib/schema.js CHANGED
@@ -59,7 +59,7 @@ const numberRE = /^\d+$/;
59
59
  * - [autoIndex](https://mongoosejs.com/docs/guide.html#autoIndex): bool - defaults to null (which means use the connection's autoIndex option)
60
60
  * - [autoCreate](https://mongoosejs.com/docs/guide.html#autoCreate): bool - defaults to null (which means use the connection's autoCreate option)
61
61
  * - [bufferCommands](https://mongoosejs.com/docs/guide.html#bufferCommands): bool - defaults to true
62
- * - [bufferTimeoutMS](https://mongoosejs.com/docs/guide.html#bufferTimeoutMS): number - defaults to 10000 (10 seconds). If `bufferCommands` is enabled, the amount of time Mongoose will wait for connectivity to be restablished before erroring out.
62
+ * - [bufferTimeoutMS](https://mongoosejs.com/docs/guide.html#bufferTimeoutMS): number - defaults to 10000 (10 seconds). If `bufferCommands` is enabled, the amount of time Mongoose will wait for connectivity to be established before erroring out.
63
63
  * - [capped](https://mongoosejs.com/docs/guide.html#capped): bool | number | object - defaults to false
64
64
  * - [collection](https://mongoosejs.com/docs/guide.html#collection): string - no default
65
65
  * - [discriminatorKey](https://mongoosejs.com/docs/guide.html#discriminatorKey): string - defaults to `__t`
@@ -750,7 +750,7 @@ Schema.prototype.encryptionType = function encryptionType(encryptionType) {
750
750
  return this.options.encryptionType;
751
751
  }
752
752
  if (!(typeof encryptionType === 'string' || encryptionType === null)) {
753
- throw new MongooseError('invalid `encryptionType`: ${encryptionType}');
753
+ throw new MongooseError(`invalid \`encryptionType\`: ${encryptionType}`);
754
754
  }
755
755
  this.options.encryptionType = encryptionType;
756
756
  };
package/lib/schemaType.js CHANGED
@@ -181,6 +181,39 @@ 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
+
204
+ if (useBsonType) {
205
+ if (isRequired) {
206
+ return { bsonType };
207
+ }
208
+ return { bsonType: [bsonType, 'null'] };
209
+ }
210
+
211
+ if (isRequired) {
212
+ return { type };
213
+ }
214
+ return { type: [type, 'null'] };
215
+ };
216
+
184
217
  /**
185
218
  * The validators that Mongoose should run to validate properties at this SchemaType's path.
186
219
  *
@@ -380,10 +413,10 @@ SchemaType.get = function(getter) {
380
413
  * #### Example:
381
414
  *
382
415
  * // values are cast:
383
- * const schema = new Schema({ aNumber: { type: Number, default: 4.815162342 }})
416
+ * const schema = new Schema({ num: { type: Number, default: 4.815162342 }})
384
417
  * const M = db.model('M', schema)
385
418
  * const m = new M;
386
- * console.log(m.aNumber) // 4.815162342
419
+ * console.log(m.num) // 4.815162342
387
420
  *
388
421
  * // default unique objects for Mixed types:
389
422
  * const schema = new Schema({ mixed: Schema.Types.Mixed });
@@ -786,7 +819,7 @@ SchemaType.prototype.set = function(fn) {
786
819
  * // defining within the schema
787
820
  * const s = new Schema({ born: { type: Date, get: dob })
788
821
  *
789
- * // or by retreiving its SchemaType
822
+ * // or by retrieving its SchemaType
790
823
  * const s = new Schema({ born: Date })
791
824
  * s.path('born').get(dob)
792
825
  *
@@ -936,7 +969,7 @@ SchemaType.prototype.validateAll = function(validators) {
936
969
  * }
937
970
  * });
938
971
  *
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.
972
+ * 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
973
  *
941
974
  * Validation occurs `pre('save')` or whenever you manually execute [document#validate](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()).
942
975
  *
@@ -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
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) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "9.3.3",
4
+ "version": "9.4.0",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -37,11 +37,11 @@
37
37
  "acquit-require": "0.1.1",
38
38
  "ajv": "8.18.0",
39
39
  "c8": "11.0.0",
40
- "cheerio": "^1.2",
40
+ "cheerio": "1.2.0",
41
41
  "dox": "1.0.0",
42
42
  "eslint": "10.0.2",
43
43
  "eslint-plugin-mocha-no-only": "1.2.0",
44
- "express": "^4.19.2",
44
+ "express": "4.22.1",
45
45
  "fs-extra": "~11.3.0",
46
46
  "globals": "^17.4.0",
47
47
  "glob": "^13.0.6",
@@ -60,7 +60,7 @@
60
60
  "ncp": "^2.0.0",
61
61
  "pug": "3.0.3",
62
62
  "sinon": "21.0.1",
63
- "tstyche": "^6.2.0",
63
+ "tstyche": "^7.0.0-rc.0",
64
64
  "typescript": "5.9.3",
65
65
  "typescript-eslint": "^8.31.1",
66
66
  "uuid": "11.1.0"
@@ -80,7 +80,7 @@
80
80
  "docs:generate:sponsorData": "node ./scripts/loadSponsorData.js",
81
81
  "docs:test": "npm run docs:generate",
82
82
  "docs:view": "node ./scripts/static.js",
83
- "docs:prepare:publish:stable": "git checkout gh-pages && git merge master && npm run docs:generate",
83
+ "docs:prepare:publish:stable": "git checkout gh-pages && git merge master && env GENERATE_SEARCH=true npm run docs:generate",
84
84
  "docs:prepare:publish:5x": "git checkout 5.x && git merge 5.x && npm run docs:clean:stable && npm run docs:generate && npm run docs:copy:tmp && git checkout gh-pages && npm run docs:copy:tmp:5x",
85
85
  "docs:prepare:publish:6x": "git checkout 6.x && git merge 6.x && npm run docs:clean:stable && env DOCS_DEPLOY=true npm run docs:generate && mv ./docs/6.x ./tmp && git checkout gh-pages && npm run docs:copy:tmp:6x",
86
86
  "docs:prepare:publish:7x": "env DOCS_DEPLOY=true npm run docs:generate && git checkout gh-pages && rm -rf ./docs/7.x && mv ./tmp ./docs/7.x",
@@ -90,7 +90,7 @@
90
90
  "lint": "eslint .",
91
91
  "lint-js": "eslint . --ext .js --ext .cjs",
92
92
  "lint-ts": "eslint . --ext .ts",
93
- "lint-md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#benchmarks\"",
93
+ "lint-md": "markdownlint-cli2 \"docs/**/*.md\" \"docs/*.md\" \"*.md\"",
94
94
  "release": "git pull && git push origin master --tags && npm publish",
95
95
  "release-5x": "git pull origin 5.x && git push origin 5.x && git push origin 5.x --tags && npm publish --tag 5x",
96
96
  "release-6x": "git pull origin 6.x && git push origin 6.x && git push origin 6.x --tags && npm publish --tag 6x",
@@ -32,7 +32,7 @@ declare module 'mongoose' {
32
32
  _id: T;
33
33
 
34
34
  /** Assert that a given path or paths is populated. Throws an error if not populated. */
35
- $assertPopulated<Paths = {}>(path: string | string[], values?: Partial<Paths>): Omit<this, keyof Paths> & Paths;
35
+ $assertPopulated<Paths = {}>(path: string | string[], values?: Partial<Paths>): PopulateDocumentResult<this, Paths, PopulatedPathsDocumentType<DocType, Paths>, DocType>;
36
36
 
37
37
  /** Clear the document's modified paths. */
38
38
  $clearModifiedPaths(): this;
@@ -171,6 +171,13 @@ declare module 'mongoose' {
171
171
  * Returns the changes that happened to the document
172
172
  * in the format that will be sent to MongoDB.
173
173
  */
174
+ $getChanges(): UpdateQuery<this>;
175
+
176
+ /**
177
+ * Returns the changes that happened to the document
178
+ * in the format that will be sent to MongoDB.
179
+ * @deprecated Use `$getChanges()` instead.
180
+ */
174
181
  getChanges(): UpdateQuery<this>;
175
182
 
176
183
  /** Signal that we desire an increment of this documents version. */
@@ -238,8 +245,8 @@ declare module 'mongoose' {
238
245
  $parent(): Document | undefined;
239
246
 
240
247
  /** Populates document references. */
241
- populate<Paths = {}>(path: string | PopulateOptions | (string | PopulateOptions)[]): Promise<MergeType<this, Paths>>;
242
- populate<Paths = {}>(path: string, select?: string | AnyObject, model?: Model<any>, match?: AnyObject, options?: PopulateOptions): Promise<MergeType<this, Paths>>;
248
+ populate<Paths = {}>(path: string | PopulateOptions | (string | PopulateOptions)[]): Promise<PopulateDocumentResult<this, Paths, PopulatedPathsDocumentType<DocType, Paths>, DocType>>;
249
+ populate<Paths = {}>(path: string, select?: string | AnyObject, model?: Model<any>, match?: AnyObject, options?: PopulateOptions): Promise<PopulateDocumentResult<this, Paths, PopulatedPathsDocumentType<DocType, Paths>, DocType>>;
243
250
 
244
251
  /** Gets _id(s) used during population of the given `path`. If the path was not populated, returns `undefined`. */
245
252
  populated(path: string): any;
@@ -262,11 +269,41 @@ declare module 'mongoose' {
262
269
  toBSON(): Require_id<DocType>;
263
270
 
264
271
  /** The return value of this method is used in calls to JSON.stringify(doc). */
272
+ toJSON<PopulatedRawDocType, DepopulatedRawDocType>(
273
+ this: PopulatedDocumentMarker<PopulatedRawDocType, DepopulatedRawDocType>,
274
+ options: { depopulate: true }
275
+ ): Default__v<Require_id<DepopulatedRawDocType>, TSchemaOptions>;
276
+ toJSON<PopulatedRawDocType, DepopulatedRawDocType, O extends ToObjectOptions & { depopulate: true }>(
277
+ this: PopulatedDocumentMarker<PopulatedRawDocType, DepopulatedRawDocType>,
278
+ options: O
279
+ ): ToObjectReturnType<DepopulatedRawDocType, TVirtuals, O, TSchemaOptions>;
280
+ toJSON<PopulatedRawDocType, O extends ToObjectOptions>(
281
+ this: PopulatedDocumentMarker<PopulatedRawDocType, any>,
282
+ options: O
283
+ ): ToObjectReturnType<PopulatedRawDocType, TVirtuals, O, TSchemaOptions>;
284
+ toJSON<PopulatedRawDocType>(
285
+ this: PopulatedDocumentMarker<PopulatedRawDocType, any>
286
+ ): Default__v<Require_id<PopulatedRawDocType>, TSchemaOptions>;
265
287
  toJSON<O extends ToObjectOptions>(options: O): ToObjectReturnType<DocType, TVirtuals, O, TSchemaOptions>;
266
288
  toJSON(options?: ToObjectOptions): Default__v<Require_id<DocType>, TSchemaOptions>;
267
289
  toJSON<T>(options?: ToObjectOptions): Default__v<Require_id<T>, ResolveSchemaOptions<TSchemaOptions>>;
268
290
 
269
291
  /** Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). */
292
+ toObject<PopulatedRawDocType, DepopulatedRawDocType>(
293
+ this: PopulatedDocumentMarker<PopulatedRawDocType, DepopulatedRawDocType>,
294
+ options: { depopulate: true }
295
+ ): Default__v<Require_id<DepopulatedRawDocType>, TSchemaOptions>;
296
+ toObject<PopulatedRawDocType, DepopulatedRawDocType, O extends ToObjectOptions & { depopulate: true }>(
297
+ this: PopulatedDocumentMarker<PopulatedRawDocType, DepopulatedRawDocType>,
298
+ options: O
299
+ ): ToObjectReturnType<DepopulatedRawDocType, TVirtuals, O, TSchemaOptions>;
300
+ toObject<PopulatedRawDocType, O extends ToObjectOptions>(
301
+ this: PopulatedDocumentMarker<PopulatedRawDocType, any>,
302
+ options: O
303
+ ): ToObjectReturnType<PopulatedRawDocType, TVirtuals, O, TSchemaOptions>;
304
+ toObject<PopulatedRawDocType>(
305
+ this: PopulatedDocumentMarker<PopulatedRawDocType, any>
306
+ ): Default__v<Require_id<PopulatedRawDocType>, TSchemaOptions>;
270
307
  toObject<O extends ToObjectOptions>(options: O): ToObjectReturnType<DocType, TVirtuals, O, TSchemaOptions>;
271
308
  toObject(options?: ToObjectOptions): Default__v<Require_id<DocType>, TSchemaOptions>;
272
309
  toObject<T>(options?: ToObjectOptions): Default__v<Require_id<T>, ResolveSchemaOptions<TSchemaOptions>>;
package/types/index.d.ts CHANGED
@@ -177,13 +177,25 @@ declare module 'mongoose' {
177
177
  HydratedDocPathsType,
178
178
  any,
179
179
  TOverrides extends Record<string, never> ?
180
- Document<unknown, TQueryHelpers, RawDocType, TVirtuals, TSchemaOptions> & Default__v<Require_id<HydratedDocPathsType>, TSchemaOptions> & AddDefaultId<HydratedDocPathsType, {}, TSchemaOptions> :
180
+ Document<unknown, TQueryHelpers, RawDocType, TVirtuals, TSchemaOptions> &
181
+ Default__v<Require_id<HydratedDocPathsType>, TSchemaOptions> &
182
+ IfEquals<
183
+ TVirtuals,
184
+ {},
185
+ AddDefaultId<HydratedDocPathsType, {}, TSchemaOptions>,
186
+ TVirtuals
187
+ > :
181
188
  IfAny<
182
189
  TOverrides,
183
190
  Document<unknown, TQueryHelpers, RawDocType, TVirtuals, TSchemaOptions> & Default__v<Require_id<HydratedDocPathsType>, TSchemaOptions>,
184
191
  Document<unknown, TQueryHelpers, RawDocType, TVirtuals, TSchemaOptions> & MergeType<
185
192
  Default__v<Require_id<HydratedDocPathsType>, TSchemaOptions>,
186
- TOverrides
193
+ IfEquals<
194
+ TOverrides,
195
+ {},
196
+ TOverrides,
197
+ TOverrides & AddDefaultId<HydratedDocPathsType, TVirtuals, TSchemaOptions>
198
+ >
187
199
  >
188
200
  >
189
201
  >;
@@ -1094,6 +1106,9 @@ declare module 'mongoose' {
1094
1106
  // Handle DocumentArray - recurse into items
1095
1107
  : T extends Types.DocumentArray<infer ItemType>
1096
1108
  ? Types.DocumentArray<ApplyFlattenTransforms<ItemType, O>>
1109
+ // Handle plain arrays - recurse into items
1110
+ : T extends Array<infer ItemType>
1111
+ ? ApplyFlattenTransforms<ItemType, O>[]
1097
1112
  // Handle Subdocument - recurse into subdoc type
1098
1113
  : T extends Types.Subdocument<unknown, unknown, infer SubdocType>
1099
1114
  ? HydratedSingleSubdocument<ApplyFlattenTransforms<SubdocType, O>>
package/types/models.d.ts CHANGED
@@ -628,10 +628,10 @@ declare module 'mongoose' {
628
628
  populate<Paths>(
629
629
  docs: Array<any>,
630
630
  options: PopulateOptions | Array<PopulateOptions> | string
631
- ): Promise<Array<MergeType<THydratedDocumentType, Paths>>>;
631
+ ): Promise<Array<PopulateDocumentResult<THydratedDocumentType, Paths, PopulatedPathsDocumentType<TRawDocType, Paths>, TRawDocType>>>;
632
632
  populate<Paths>(
633
633
  doc: any, options: PopulateOptions | Array<PopulateOptions> | string
634
- ): Promise<MergeType<THydratedDocumentType, Paths>>;
634
+ ): Promise<PopulateDocumentResult<THydratedDocumentType, Paths, PopulatedPathsDocumentType<TRawDocType, Paths>, TRawDocType>>;
635
635
 
636
636
  /**
637
637
  * Update an existing [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/).
@@ -8,6 +8,50 @@ declare module 'mongoose' {
8
8
  RawId extends RefType = (PopulatedType extends { _id?: RefType; } ? NonNullable<PopulatedType['_id']> : Types.ObjectId) | undefined
9
9
  > = PopulatedType | RawId;
10
10
 
11
+ const mongoosePopulatedDocumentMarker: unique symbol;
12
+
13
+ type ExtractDocumentObjectType<T> = T extends infer ObjectType & Document ? FlatRecord<ObjectType> : T;
14
+
15
+ type PopulatePathToRawDocType<T> =
16
+ T extends Types.DocumentArray<any, infer ItemType>
17
+ ? PopulatePathToRawDocType<ItemType>[]
18
+ : T extends Array<infer ItemType>
19
+ ? PopulatePathToRawDocType<ItemType>[]
20
+ : T extends Document
21
+ ? SubdocsToPOJOs<ExtractDocumentObjectType<T>>
22
+ : T extends Record<string, any>
23
+ ? { [K in keyof T]: PopulatePathToRawDocType<T[K]> }
24
+ : T;
25
+
26
+ type PopulatedPathsDocumentType<RawDocType, Paths> = UnpackedIntersection<RawDocType, PopulatePathToRawDocType<Paths>>;
27
+
28
+ type PopulatedDocumentMarker<
29
+ PopulatedRawDocType,
30
+ DepopulatedRawDocType,
31
+ > = {
32
+ [mongoosePopulatedDocumentMarker]?: {
33
+ populated: PopulatedRawDocType,
34
+ depopulated: DepopulatedRawDocType
35
+ }
36
+ };
37
+
38
+ type ResolvePopulatedRawDocType<
39
+ ThisType,
40
+ FallbackRawDocType,
41
+ O = never
42
+ > = ThisType extends PopulatedDocumentMarker<infer PopulatedRawDocType, infer DepopulatedRawDocType>
43
+ ? O extends { depopulate: true }
44
+ ? DepopulatedRawDocType
45
+ : PopulatedRawDocType
46
+ : FallbackRawDocType;
47
+
48
+ type PopulateDocumentResult<
49
+ Doc,
50
+ Paths,
51
+ PopulatedRawDocType,
52
+ DepopulatedRawDocType = PopulatedRawDocType
53
+ > = MergeType<Doc, Paths> & PopulatedDocumentMarker<PopulatedRawDocType, DepopulatedRawDocType>;
54
+
11
55
  interface PopulateOptions {
12
56
  /** space delimited path(s) to populate */
13
57
  path: string;
package/types/query.d.ts CHANGED
@@ -18,17 +18,34 @@ declare module 'mongoose' {
18
18
  ? DateQueryTypeCasting
19
19
  : T;
20
20
 
21
- export type ApplyBasicQueryCasting<T> = QueryTypeCasting<T> | QueryTypeCasting<T[]> | (T extends (infer U)[] ? QueryTypeCasting<U> : T) | null;
21
+ export type ApplyBasicQueryCasting<T> = QueryTypeCasting<T> | QueryTypeCasting<T>[] | (T extends (infer U)[] ? QueryTypeCasting<U> : T) | null;
22
+
23
+ type RemoveIndexSignature<T> = {
24
+ [K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: T[K];
25
+ };
26
+
27
+ type StrictFilterOperators<TValue> = RemoveIndexSignature<mongodb.FilterOperators<TValue>>;
28
+
29
+ type StrictCondition<T> = mongodb.AlternativeType<T> | StrictFilterOperators<mongodb.AlternativeType<T>>;
30
+
31
+ type _StrictFilter<TSchema> = {
32
+ [P in keyof TSchema]?: StrictCondition<ApplyBasicQueryCasting<TSchema[P]>>;
33
+ } & StrictRootFilterOperators<TSchema>;
34
+
35
+ type StrictRootFilterOperators<TSchema> = Omit<mongodb.RootFilterOperators<TSchema>, '$and' | '$or' | '$nor'> & {
36
+ $and?: _StrictFilter<TSchema>[];
37
+ $or?: _StrictFilter<TSchema>[];
38
+ $nor?: _StrictFilter<TSchema>[];
39
+ };
22
40
 
23
41
  type _QueryFilter<T> = (
24
- { [P in keyof T]?: mongodb.Condition<ApplyBasicQueryCasting<T[P]>>; } &
25
- mongodb.RootFilterOperators<{ [P in keyof mongodb.WithId<T>]?: ApplyBasicQueryCasting<mongodb.WithId<T>[P]>; }>
42
+ { [P in keyof T]?: StrictCondition<ApplyBasicQueryCasting<T[P]>>; } &
43
+ StrictRootFilterOperators<{ [P in keyof mongodb.WithId<T>]?: ApplyBasicQueryCasting<mongodb.WithId<T>[P]>; }>
26
44
  );
27
45
  type _QueryFilterLooseId<T> = (
28
- { [P in keyof T]?: mongodb.Condition<ApplyBasicQueryCasting<T[P]>>; } &
29
- mongodb.RootFilterOperators<
30
- { [P in keyof T]?: ApplyBasicQueryCasting<T[P]>; } &
31
- { _id?: any; }
46
+ { [P in keyof T]?: StrictCondition<ApplyBasicQueryCasting<T[P]>>; } &
47
+ StrictRootFilterOperators<
48
+ { [P in keyof T]?: ApplyBasicQueryCasting<T[P]>; }
32
49
  >
33
50
  );
34
51
  type QueryFilter<T> = IsItRecordAndNotAny<T> extends true ? _QueryFilter<WithLevel1NestedPaths<T>> : _QueryFilterLooseId<Record<string, any>>;
@@ -201,10 +218,10 @@ declare module 'mongoose' {
201
218
  ? ResultType
202
219
  : ResultType extends (infer U)[]
203
220
  ? U extends Document
204
- ? HydratedDocument<MergeType<RawDocType, Paths>, TDocOverrides, TQueryHelpers>[]
221
+ ? PopulateDocumentResult<U, Paths, MergeType<RawDocType, Paths>, RawDocType>[]
205
222
  : (MergeType<U, Paths>)[]
206
223
  : ResultType extends Document
207
- ? HydratedDocument<MergeType<RawDocType, Paths>, TDocOverrides, TQueryHelpers>
224
+ ? PopulateDocumentResult<ResultType, Paths, MergeType<RawDocType, Paths>, RawDocType>
208
225
  : MergeType<ResultType, Paths>
209
226
  : MergeType<ResultType, Paths>;
210
227
 
@@ -98,10 +98,10 @@ declare module 'mongoose' {
98
98
  type UnpackedIntersection<T, U> = T extends null
99
99
  ? null
100
100
  : T extends (infer A)[]
101
- ? (Omit<A, keyof U> & U)[]
101
+ ? (A extends any ? (Omit<A, keyof U> & U) : never)[]
102
102
  : keyof U extends never
103
103
  ? T
104
- : Omit<T, keyof U> & U;
104
+ : T extends any ? (Omit<T, keyof U> & U) : never;
105
105
 
106
106
  type MergeType<A, B> = A extends unknown ? Omit<A, keyof B> & B : never;
107
107
 
@@ -1,24 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Handles creating `{ type: 'object' }` vs `{ bsonType: 'object' }` vs `{ bsonType: ['object', 'null'] }`
5
- *
6
- * @param {string} type
7
- * @param {string} bsonType
8
- * @param {boolean} useBsonType
9
- * @param {boolean} isRequired
10
- */
11
-
12
- module.exports = function createJSONSchemaTypeArray(type, bsonType, useBsonType, isRequired) {
13
- if (useBsonType) {
14
- if (isRequired) {
15
- return { bsonType };
16
- }
17
- return { bsonType: [bsonType, 'null'] };
18
- } else {
19
- if (isRequired) {
20
- return { type };
21
- }
22
- return { type: [type, 'null'] };
23
- }
24
- };