From d3756032e605782d3143cacd99b8a9f4566322e6 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 9 Jul 2024 10:31:27 -0400 Subject: [PATCH 01/23] types: avoid automatically inferring lean result type when assigning to explicitly typed variable Fix #14697 --- test/types/document.test.ts | 5 +++-- test/types/plugin.test.ts | 2 +- test/types/queries.test.ts | 4 ++-- types/query.d.ts | 18 ++++++++++++++---- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/test/types/document.test.ts b/test/types/document.test.ts index d492cebd6e7..84451edf0f2 100644 --- a/test/types/document.test.ts +++ b/test/types/document.test.ts @@ -10,7 +10,7 @@ import { DefaultSchemaOptions } from 'mongoose'; import { DeleteResult } from 'mongodb'; -import { expectAssignable, expectError, expectType } from 'tsd'; +import { expectAssignable, expectError, expectNotAssignable, expectType } from 'tsd'; import { autoTypedModel } from './models.test'; import { autoTypedModelConnection } from './connection.test'; import { AutoTypedSchemaType } from './schema.test'; @@ -42,7 +42,8 @@ void async function main() { expectType(await doc.deleteOne()); expectType(await doc.deleteOne().findOne()); - expectType<{ _id: Types.ObjectId, name?: string } | null>(await doc.deleteOne().findOne().lean()); + expectAssignable<{ _id: Types.ObjectId, name?: string } | null>(await doc.deleteOne().findOne().lean()); + expectNotAssignable(await doc.deleteOne().findOne().lean()); }(); diff --git a/test/types/plugin.test.ts b/test/types/plugin.test.ts index 0b579ccc6b9..fc1deddf900 100644 --- a/test/types/plugin.test.ts +++ b/test/types/plugin.test.ts @@ -49,7 +49,7 @@ interface TestStaticMethods { findSomething(this: TestModel): Promise; } type TestDocument = HydratedDocument; -type TestQuery = Query & TestQueryHelpers; +type TestQuery = Query & TestQueryHelpers; interface TestQueryHelpers { whereSomething(this: TestQuery): this } diff --git a/test/types/queries.test.ts b/test/types/queries.test.ts index f10f26b3ad2..d84022526e4 100644 --- a/test/types/queries.test.ts +++ b/test/types/queries.test.ts @@ -38,11 +38,11 @@ const schema: Schema, {}, QueryHelpers> = new endDate: Date }); -schema.query._byName = function(name: string): QueryWithHelpers { +schema.query._byName = function(name: string): QueryWithHelpers { return this.find({ name }); }; -schema.query.byName = function(name: string): QueryWithHelpers { +schema.query.byName = function(name: string): QueryWithHelpers { expectError(this.notAQueryHelper()); return this._byName(name); }; diff --git a/types/query.d.ts b/types/query.d.ts index 44d9a71062f..850d06eb25a 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -224,7 +224,7 @@ declare module 'mongoose' { : MergeType : MergeType; - class Query> implements SessionOperation { + class Query> implements SessionOperation { _mongooseOptions: MongooseQueryOptions; /** @@ -548,9 +548,19 @@ declare module 'mongoose' { j(val: boolean | null): this; /** Sets the lean option. */ - lean< - LeanResultType = GetLeanResultType - >( + lean( + val?: boolean | any + ): QueryWithHelpers< + ResultType extends null + ? GetLeanResultType | null + : GetLeanResultType, + DocType, + THelpers, + RawDocType, + QueryOp, + TInstanceMethods + >; + lean( val?: boolean | any ): QueryWithHelpers< ResultType extends null From 779bfe5996562de436690de823f795d8688dd832 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 9 Jul 2024 13:27:13 -0400 Subject: [PATCH 02/23] types: make `_id` required on Document type Fix #14660 --- test/types/docArray.test.ts | 2 +- test/types/schema.test.ts | 2 +- types/document.d.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/types/docArray.test.ts b/test/types/docArray.test.ts index c296ce6fea7..78650845ba2 100644 --- a/test/types/docArray.test.ts +++ b/test/types/docArray.test.ts @@ -91,7 +91,7 @@ async function gh13424() { const TestModel = model('Test', testSchema); const doc = new TestModel(); - expectType(doc.subDocArray[0]._id); + expectType(doc.subDocArray[0]._id); } async function gh14367() { diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index 9dab60d2022..49f0f277a4c 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -1386,7 +1386,7 @@ function gh13424() { const TestModel = model('TestModel', new Schema(testSchema)); const doc = new TestModel({}); - expectType(doc.subDocArray[0]._id); + expectType(doc.subDocArray[0]._id); } function gh14147() { diff --git a/types/document.d.ts b/types/document.d.ts index c0fb5589240..93d1718fea9 100644 --- a/types/document.d.ts +++ b/types/document.d.ts @@ -22,7 +22,7 @@ declare module 'mongoose' { constructor(doc?: any); /** This documents _id. */ - _id?: T; + _id: T; /** This documents __v. */ __v?: any; From 22b41df59fdee5b6d93ac9b46fb619c77f9b5230 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 12 Jul 2024 14:19:57 -0400 Subject: [PATCH 03/23] feat(QueryCursor): add getDriverCursor() function that returns the raw driver cursor --- lib/cursor/queryCursor.js | 20 ++++++++++++++++++++ test/query.cursor.test.js | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/cursor/queryCursor.js b/lib/cursor/queryCursor.js index ff59f6aeba8..2c908de50d0 100644 --- a/lib/cursor/queryCursor.js +++ b/lib/cursor/queryCursor.js @@ -10,6 +10,7 @@ const eachAsync = require('../helpers/cursor/eachAsync'); const helpers = require('../queryHelpers'); const kareem = require('kareem'); const immediate = require('../helpers/immediate'); +const { once } = require('node:events'); const util = require('util'); /** @@ -135,6 +136,25 @@ QueryCursor.prototype._read = function() { }); }; +/** + * Returns the underlying cursor from the MongoDB Node driver that this cursor uses. + * + * @method getDriverCursor + * @memberOf QueryCursor + * @returns {Cursor} MongoDB Node driver cursor instance + * @instance + * @api public + */ + +QueryCursor.prototype.getDriverCursor = async function getDriverCursor() { + if (this.cursor) { + return this.cursor; + } + + await once(this, 'cursor'); + return this.cursor; +}; + /** * Registers a transform function which subsequently maps documents retrieved * via the streams interface or `.next()` diff --git a/test/query.cursor.test.js b/test/query.cursor.test.js index d835135a0b2..a5f7afc2027 100644 --- a/test/query.cursor.test.js +++ b/test/query.cursor.test.js @@ -900,6 +900,25 @@ describe('QueryCursor', function() { assert.ok(err); assert.ok(err.message.includes('skipMiddlewareFunction'), err.message); }); + + it('returns the underlying Node driver cursor with getDriverCursor()', async function() { + const schema = new mongoose.Schema({ name: String }); + + const Movie = db.model('Movie', schema); + + await Movie.deleteMany({}); + await Movie.create([ + { name: 'Kickboxer' }, + { name: 'Ip Man' }, + { name: 'Enter the Dragon' } + ]); + + const cursor = await Movie.find({}).cursor(); + assert.ok(!cursor.cursor); + const driverCursor = await cursor.getDriverCursor(); + assert.ok(cursor.cursor); + assert.equal(driverCursor, cursor.cursor); + }); }); async function delay(ms) { From 9a2462c81f6fe74d8e3a05a28f5e9fd7227ea48e Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 29 Jul 2024 13:54:25 -0400 Subject: [PATCH 04/23] fix: allow setting document array default to `null` Fix #6691 Re: #14717 --- lib/schema/documentArray.js | 2 +- test/document.test.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/schema/documentArray.js b/lib/schema/documentArray.js index a10d2ec76b1..aa0c0d7984a 100644 --- a/lib/schema/documentArray.js +++ b/lib/schema/documentArray.js @@ -69,7 +69,7 @@ function SchemaDocumentArray(key, schema, options, schemaOptions) { const fn = this.defaultValue; - if (!('defaultValue' in this) || fn !== void 0) { + if (!('defaultValue' in this) || fn != null) { this.default(function() { let arr = fn.call(this); if (arr != null && !Array.isArray(arr)) { diff --git a/test/document.test.js b/test/document.test.js index c11f72753ee..5957556a867 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -3203,16 +3203,22 @@ describe('document', function() { names: { type: [String], default: null + }, + tags: { + type: [{ tag: String }], + default: null } }); const Model = db.model('Test', schema); const m = new Model(); assert.strictEqual(m.names, null); + assert.strictEqual(m.tags, null); await m.save(); const doc = await Model.collection.findOne({ _id: m._id }); assert.strictEqual(doc.names, null); + assert.strictEqual(doc.tags, null); }); it('validation works when setting array index (gh-3816)', async function() { From 986f5eb3077120de530d5baea450ce95428f3266 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 30 Jul 2024 14:04:47 -0400 Subject: [PATCH 05/23] chore: release 8.5.2 --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 241620b2b87..d087c5af90b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +8.5.2 / 2024-07-30 +================== + * perf(clone): avoid further unnecessary checks if cloning a primitive value #14762 #14394 + * fix: allow setting document array default to null #14769 #14717 #6691 + * fix(model): support session: null option for save() to opt out of automatic session option with transactionAsyncLocalStorage #14744 #14736 + * fix(model+document): avoid depopulating manually populated doc as getter value #14760 #14759 + * fix: correct shardkey access in buildBulkWriteOps #14753 #14752 [adf0nt3s](https://github.com/adf0nt3s) + * fix(query): handle casting $switch in $expr #14755 #14751 + * types: allow calling SchemaType.cast() without parent and init parameters #14756 #14748 #9076 + * docs: fix a wrong example in v6 migration guide #14758 [abdelrahman-elkady](https://github.com/abdelrahman-elkady) + 8.5.1 / 2024-07-12 ================== * perf(model): performance improvements for insertMany() #14724 diff --git a/package.json b/package.json index 56b2df8ffa9..402945e1ed5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongoose", "description": "Mongoose MongoDB ODM", - "version": "8.5.1", + "version": "8.5.2", "author": "Guillermo Rauch ", "keywords": [ "mongodb", From 641250cea3f59c38f51b90355f35b96e21e4fbe8 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 1 Aug 2024 11:06:22 -0400 Subject: [PATCH 06/23] feat(model+query): support `options` parameter for distinct() Fix #8006 --- lib/model.js | 8 ++++++-- lib/query.js | 11 +++++++++-- test/docs/transactions.test.js | 20 ++++++++++++++++++++ test/query.test.js | 10 ++++++++++ test/types/queries.test.ts | 1 + types/models.d.ts | 3 ++- 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/lib/model.js b/lib/model.js index b3497669be5..7b9223d2408 100644 --- a/lib/model.js +++ b/lib/model.js @@ -2115,17 +2115,21 @@ Model.countDocuments = function countDocuments(conditions, options) { * * @param {String} field * @param {Object} [conditions] optional + * @param {Object} [options] optional * @return {Query} * @api public */ -Model.distinct = function distinct(field, conditions) { +Model.distinct = function distinct(field, conditions, options) { _checkContext(this, 'distinct'); - if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { throw new MongooseError('Model.distinct() no longer accepts a callback'); } const mq = new this.Query({}, {}, this, this.$__collection); + if (options != null) { + mq.setOptions(options); + } return mq.distinct(field, conditions); }; diff --git a/lib/query.js b/lib/query.js index ccca65f4192..20532ad1f8f 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2870,21 +2870,24 @@ Query.prototype.__distinct = async function __distinct() { * * #### Example: * + * distinct(field, conditions, options) * distinct(field, conditions) * distinct(field) * distinct() * * @param {String} [field] * @param {Object|Query} [filter] + * @param {Object} [options] * @return {Query} this * @see distinct https://www.mongodb.com/docs/manual/reference/method/db.collection.distinct/ * @api public */ -Query.prototype.distinct = function(field, conditions) { +Query.prototype.distinct = function(field, conditions, options) { if (typeof field === 'function' || typeof conditions === 'function' || - typeof arguments[2] === 'function') { + typeof options === 'function' || + typeof arguments[3] === 'function') { throw new MongooseError('Query.prototype.distinct() no longer accepts a callback'); } @@ -2903,6 +2906,10 @@ Query.prototype.distinct = function(field, conditions) { this._distinct = field; } + if (typeof options === 'object' && options != null) { + this.setOptions(options); + } + return this; }; diff --git a/test/docs/transactions.test.js b/test/docs/transactions.test.js index 2b19aef9ee3..b2e208fe17d 100644 --- a/test/docs/transactions.test.js +++ b/test/docs/transactions.test.js @@ -338,6 +338,26 @@ describe('transactions', function() { assert.deepEqual(fromDb, { name: 'Tyrion Lannister' }); }); + it('distinct (gh-8006)', async function() { + const Character = db.model('gh8006_Character', new Schema({ name: String, rank: String }, { versionKey: false })); + + + const session = await db.startSession(); + + session.startTransaction(); + await Character.create([{ name: 'Will Riker', rank: 'Commander' }, { name: 'Jean-Luc Picard', rank: 'Captain' }], { session }); + + let names = await Character.distinct('name', {}, { session }); + assert.deepStrictEqual(names.sort(), ['Jean-Luc Picard', 'Will Riker']); + + names = await Character.distinct('name', { rank: 'Captain' }, { session }); + assert.deepStrictEqual(names.sort(), ['Jean-Luc Picard']); + + // Undo both update and delete since doc should pull from `$session()` + await session.abortTransaction(); + session.endSession(); + }); + it('save() with no changes (gh-8571)', async function() { db.deleteModel(/Test/); const Test = db.model('Test', Schema({ name: String })); diff --git a/test/query.test.js b/test/query.test.js index 553ab04e152..4f75d0f2fab 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1088,6 +1088,16 @@ describe('Query', function() { assert.equal(q.op, 'distinct'); }); + + it('using options parameter for distinct', function() { + const q = new Query({}); + const options = { collation: { locale: 'en', strength: 2 } }; + + q.distinct('blah', {}, options); + + assert.equal(q.op, 'distinct'); + assert.deepEqual(q.options.collation, options.collation); + }); }); describe('findOne', function() { diff --git a/test/types/queries.test.ts b/test/types/queries.test.ts index d84022526e4..5396f384cad 100644 --- a/test/types/queries.test.ts +++ b/test/types/queries.test.ts @@ -109,6 +109,7 @@ Test.find({ name: { $gte: 'Test' } }, null, { collation: { locale: 'en-us' } }). Test.findOne().orFail(new Error('bar')).then((doc: ITest | null) => console.log('Found! ' + doc)); Test.distinct('name').exec().then((res: Array) => console.log(res[0])); +Test.distinct('name', {}, { collation: { locale: 'en', strength: 2 } }).exec().then((res: Array) => console.log(res[0])); Test.findOneAndUpdate({ name: 'test' }, { name: 'test2' }).exec().then((res: ITest | null) => console.log(res)); Test.findOneAndUpdate({ name: 'test' }, { name: 'test2' }).then((res: ITest | null) => console.log(res)); diff --git a/types/models.d.ts b/types/models.d.ts index 27c43612ac2..c042305a828 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -621,7 +621,8 @@ declare module 'mongoose' { /** Creates a `distinct` query: returns the distinct values of the given `field` that match `filter`. */ distinct( field: DocKey, - filter?: FilterQuery + filter?: FilterQuery, + options?: QueryOptions ): QueryWithHelpers< Array< DocKey extends keyof WithLevel1NestedPaths From eebf2d661669fcc8796a666fcb8ce1977347ab6a Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 1 Aug 2024 13:54:16 -0400 Subject: [PATCH 07/23] fix(document): apply virtuals to subdocuments if parent schema has `virtuals: true` for backwards compatibility Fix #14771 Re: #14394 Re: #14623 --- lib/document.js | 12 +++++++++--- test/document.test.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/document.js b/lib/document.js index f4bc5e3e53e..afc9e5ceacc 100644 --- a/lib/document.js +++ b/lib/document.js @@ -3801,7 +3801,7 @@ Document.prototype.$__handleReject = function handleReject(err) { }; /** - * Internal helper for toObject() and toJSON() that doesn't manipulate options + * Internal common logic for toObject() and toJSON() * * @return {Object} * @api private @@ -3834,14 +3834,17 @@ Document.prototype.$toObject = function(options, json) { } const depopulate = options._calledWithOptions.depopulate - ?? options._parentOptions?.depopulate ?? defaultOptions?.depopulate + ?? options.depopulate ?? false; // _isNested will only be true if this is not the top level document, we // should never depopulate the top-level document if (depopulate && options._isNested && this.$__.wasPopulated) { return clone(this.$__.wasPopulated.value || this._doc._id, options); } + if (depopulate) { + options.depopulate = true; + } // merge default options with input options. if (defaultOptions != null) { @@ -3855,7 +3858,9 @@ Document.prototype.$toObject = function(options, json) { options.json = json; options.minimize = _minimize; - options._parentOptions = options; + const parentOptions = options._parentOptions; + // Parent options should only bubble down for subdocuments, not populated docs + options._parentOptions = this.$isSubdocument ? options : null; options._skipSingleNestedGetters = false; // remember the root transform function @@ -3886,6 +3891,7 @@ Document.prototype.$toObject = function(options, json) { const virtuals = options._calledWithOptions.virtuals ?? defaultOptions.virtuals + ?? parentOptions?.virtuals ?? undefined; if (virtuals || (getters && virtuals !== false)) { diff --git a/test/document.test.js b/test/document.test.js index 5957556a867..f65479224d0 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -13750,6 +13750,38 @@ describe('document', function() { /Test error in post deleteOne hook/ ); }); + + it('applies virtuals to subschemas if top-level schema has virtuals: true (gh-14771)', function() { + const userLabSchema = new mongoose.Schema({ + capacityLevel: Number + }); + + userLabSchema.virtual('capacityLevelCeil').get(function() { + return Math.ceil(this.capacityLevel); + }); + + const labPlotSchema = new mongoose.Schema({ + plotId: Number, + lab: userLabSchema + }); + + const userSchema = new mongoose.Schema({ + username: String, + labPlots: [labPlotSchema] + }, { toObject: { virtuals: true }, toJSON: { virtuals: true } }); + + const User = db.model('User', userSchema); + + const doc = new User({ + username: 'test', + labPlots: [{ + plotId: 1, + lab: { capacityLevel: 3.14 } + }] + }); + assert.strictEqual(doc.toObject().labPlots[0].lab.capacityLevelCeil, 4); + assert.strictEqual(doc.toJSON().labPlots[0].lab.capacityLevelCeil, 4); + }); }); describe('Check if instance function that is supplied in schema option is available', function() { From c384eb0667d827f7430d0e0ba758c3c3229a9426 Mon Sep 17 00:00:00 2001 From: Danila Stryzhonak Date: Thu, 1 Aug 2024 18:05:45 +0300 Subject: [PATCH 08/23] fix: inferSchemaType, inferRawType updates to infer based on provided schema options as well --- test/types/schema.test.ts | 29 +++++++++++++++++++++++++++++ types/inferrawdoctype.d.ts | 4 ++-- types/inferschematype.d.ts | 21 ++++++++++++++++----- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index a9679c082b8..2d946adc93c 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -1575,3 +1575,32 @@ function gh14748() { const subdoc3 = schema.path>('singleNested').cast({ name: 'bar' }); expectAssignable<{ name: string }>(subdoc3); } + +function gh13215() { + const schemaDefinition = { + userName: { type: String, required: true } + } as const; + const schemaOptions = { + typeKey: 'type', + timestamps: { + createdAt: 'date', + updatedAt: false + } + } as const; + + type RawDocType = InferRawDocType< + typeof schemaDefinition, + typeof schemaOptions + >; + type User = { + userName: string; + } & { + date: Date; + }; + + expectType({} as RawDocType); + + const schema = new Schema(schemaDefinition, schemaOptions); + type SchemaType = InferSchemaType; + expectType({} as SchemaType); +} diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index 95a912ff240..e2b1d52b6b9 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -10,12 +10,12 @@ declare module 'mongoose' { export type InferRawDocType< DocDefinition, TSchemaOptions extends Record = DefaultSchemaOptions - > = { + > = ApplySchemaOptions<{ [ K in keyof (RequiredPaths & OptionalPaths) ]: ObtainRawDocumentPathType; - }; + }, TSchemaOptions>; /** * @summary Obtains schema Path type. diff --git a/types/inferschematype.d.ts b/types/inferschematype.d.ts index ff1e92b7831..2bb6abf8bbe 100644 --- a/types/inferschematype.d.ts +++ b/types/inferschematype.d.ts @@ -74,14 +74,25 @@ declare module 'mongoose' { type ApplySchemaOptions = ResolveTimestamps; - type ResolveTimestamps = O extends { timestamps: true } + type ResolveTimestamps = O extends { methods: any } | { statics: any } | { virtuals: any } | { timestamps?: false } ? T // For some reason, TypeScript sets all the document properties to unknown // if we use methods, statics, or virtuals. So avoid inferring timestamps // if any of these are set for now. See gh-12807 - ? O extends { methods: any } | { statics: any } | { virtuals: any } - ? T - : { createdAt: NativeDate; updatedAt: NativeDate; } & T - : T; + : O extends { timestamps: infer TimestampOptions } ? TimestampOptions extends true + ? { createdAt: NativeDate; updatedAt: NativeDate; } & T + : TimestampOptions extends SchemaTimestampsConfig + ? { + -readonly [K in keyof Pick< + TimestampOptions, + 'createdAt' | 'updatedAt' + > as TimestampOptions[K] extends true + ? K + : TimestampOptions[K] extends string + ? TimestampOptions[K] + : never]: NativeDate; + } & T + : T + : T; } type IsPathDefaultUndefined = PathType extends { default: undefined } ? From 069a70b2ce2071904ecb373e4cfff34d2b34c6b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:13:45 +0000 Subject: [PATCH 09/23] chore(deps): bump actions/setup-node from 4.0.2 to 4.0.3 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.2 to 4.0.3. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/60edb5dd545a775178f52524783378180af0d1f8...1e60f620b9541d16bece96c5465dc8ee9832be0b) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 2 +- .github/workflows/documentation.yml | 4 ++-- .github/workflows/test.yml | 8 ++++---- .github/workflows/tidelift-alignment.yml | 2 +- .github/workflows/tsd.yml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 10df11dc592..052b34b1075 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -26,7 +26,7 @@ jobs: with: fetch-depth: 0 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 16 diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 0fcf2297d28..0f9f032ac21 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 16 @@ -52,7 +52,7 @@ jobs: - run: git fetch --depth=1 --tags # download all tags for documentation - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 16 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2b5a189cb55..7bd4ceea192 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 18 @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: ${{ matrix.node }} @@ -96,7 +96,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 16 - name: Load MongoDB binary cache @@ -124,7 +124,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 16 - run: npm install diff --git a/.github/workflows/tidelift-alignment.yml b/.github/workflows/tidelift-alignment.yml index c151d9114ce..f79e17a20bf 100644 --- a/.github/workflows/tidelift-alignment.yml +++ b/.github/workflows/tidelift-alignment.yml @@ -17,7 +17,7 @@ jobs: - name: Checkout uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 16 - name: Alignment diff --git a/.github/workflows/tsd.yml b/.github/workflows/tsd.yml index 0960b7d1de2..e5102805477 100644 --- a/.github/workflows/tsd.yml +++ b/.github/workflows/tsd.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 18 @@ -41,7 +41,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup node - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 16 From aeada4d6d12177081d2b16d183ddb6bbc230597d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:35:43 +0000 Subject: [PATCH 10/23] chore(deps-dev): bump @babel/preset-env from 7.24.7 to 7.25.3 Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.24.7 to 7.25.3. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.25.3/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 402945e1ed5..7976df304fb 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "@babel/core": "7.24.7", - "@babel/preset-env": "7.24.7", + "@babel/preset-env": "7.25.3", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "acquit": "1.3.0", From a83da37e0a755f2223125c72029a86fd9e9a6861 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:36:35 +0000 Subject: [PATCH 11/23] chore(deps-dev): bump typescript from 5.5.3 to 5.5.4 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.5.3 to 5.5.4. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/Microsoft/TypeScript/compare/v5.5.3...v5.5.4) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 402945e1ed5..a8079702264 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "sinon": "18.0.0", "stream-browserify": "3.0.0", "tsd": "0.31.1", - "typescript": "5.5.3", + "typescript": "5.5.4", "uuid": "10.0.0", "webpack": "5.92.1" }, From 57c3396466e18422934d90f18430ea47866cff6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:37:28 +0000 Subject: [PATCH 12/23] chore(deps-dev): bump mocha from 10.6.0 to 10.7.0 Bumps [mocha](https://github.com/mochajs/mocha) from 10.6.0 to 10.7.0. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v10.6.0...v10.7.0) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 402945e1ed5..4d12c435f7b 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "markdownlint-cli2": "^0.13.0", "marked": "4.3.0", "mkdirp": "^3.0.1", - "mocha": "10.6.0", + "mocha": "10.7.0", "moment": "2.30.1", "mongodb-memory-server": "10.0.0", "ncp": "^2.0.0", From 188bbf5ccda1b20ab2608f4cac2b3e37d420430f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:37:46 +0000 Subject: [PATCH 13/23] chore(deps-dev): bump highlight.js from 11.9.0 to 11.10.0 Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 11.9.0 to 11.10.0. - [Release notes](https://github.com/highlightjs/highlight.js/releases) - [Changelog](https://github.com/highlightjs/highlight.js/blob/main/CHANGES.md) - [Commits](https://github.com/highlightjs/highlight.js/compare/11.9.0...11.10.0) --- updated-dependencies: - dependency-name: highlight.js dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 402945e1ed5..3250b4cb53c 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "eslint-plugin-mocha-no-only": "1.2.0", "express": "^4.19.2", "fs-extra": "~11.2.0", - "highlight.js": "11.9.0", + "highlight.js": "11.10.0", "lodash.isequal": "4.5.0", "lodash.isequalwith": "4.4.0", "markdownlint-cli2": "^0.13.0", From c2086753122779a9dd7bfac1d70a5bd6415b6186 Mon Sep 17 00:00:00 2001 From: Justin McBride Date: Fri, 2 Aug 2024 15:14:32 -0600 Subject: [PATCH 14/23] docs: add schema type widening advice --- docs/typescript/schemas.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/typescript/schemas.md b/docs/typescript/schemas.md index 8dfa5310556..de53923ced6 100644 --- a/docs/typescript/schemas.md +++ b/docs/typescript/schemas.md @@ -32,6 +32,8 @@ There are a few caveats for using automatic type inference: 2. You need to define your schema in the `new Schema()` call. Don't assign your schema definition to a temporary variable. Doing something like `const schemaDefinition = { name: String }; const schema = new Schema(schemaDefinition);` will not work. 3. Mongoose adds `createdAt` and `updatedAt` to your schema if you specify the `timestamps` option in your schema, *except* if you also specify `methods`, `virtuals`, or `statics`. There is a [known issue](https://github.com/Automattic/mongoose/issues/12807) with type inference with timestamps and methods/virtuals/statics options. If you use methods, virtuals, and statics, you're responsible for adding `createdAt` and `updatedAt` to your schema definition. +If you must define your schema separately, use [as const](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) (`const schemaDefinition = { ... } as const;`) to prevent *type widening*. TypeScript will automatically widen types like `required: false` to `required: boolean`, which will cause Mongoose to assume the field is required. Using `as const` forces TypeScript to retain these types. + If you need to explicitly get the raw document type (the value returned from `doc.toObject()`, `await Model.findOne().lean()`, etc.) from your schema definition, you can use Mongoose's `inferRawDocType` helper as follows: ```ts From aa39c68970c09f9759da4398d2668740368ffb95 Mon Sep 17 00:00:00 2001 From: hasezoey Date: Sat, 3 Aug 2024 14:22:30 +0200 Subject: [PATCH 15/23] style(map): document key name constraints --- lib/types/map.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/types/map.js b/lib/types/map.js index 882fc117a4d..e1874840a91 100644 --- a/lib/types/map.js +++ b/lib/types/map.js @@ -68,10 +68,17 @@ class MongooseMap extends Map { * and change tracking. Note that Mongoose maps _only_ support strings and * ObjectIds as keys. * + * Keys also cannot: + * - be named after special properties (like `prototype`, `constructor` and `__proto__`) + * - start with a dollar sign (`$`) + * - contain any dots (`.`) + * * #### Example: * * doc.myMap.set('test', 42); // works * doc.myMap.set({ obj: 42 }, 42); // Throws "Mongoose maps only support string keys" + * doc.myMap.set(10, 42); // Throws "Mongoose maps only support string keys" + * doc.myMap.set("$test", 42); // Throws "Mongoose maps do not support keys that start with "$", got "$test"" * * @api public * @method set From d8f6170ceb14ca3bea914db24c4f745fc3efb423 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:48:32 +0000 Subject: [PATCH 16/23] chore(deps-dev): bump webpack from 5.92.1 to 5.93.0 Bumps [webpack](https://github.com/webpack/webpack) from 5.92.1 to 5.93.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.92.1...v5.93.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 25c143720e4..de2a37fa884 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "tsd": "0.31.1", "typescript": "5.5.4", "uuid": "10.0.0", - "webpack": "5.92.1" + "webpack": "5.93.0" }, "directories": { "lib": "./lib/mongoose" From 28d198bc5cc79a3b219b7913bee94735b120bc87 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 7 Aug 2024 18:31:42 -0400 Subject: [PATCH 17/23] Update map.js --- lib/types/map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types/map.js b/lib/types/map.js index e1874840a91..194982f4e1a 100644 --- a/lib/types/map.js +++ b/lib/types/map.js @@ -69,7 +69,7 @@ class MongooseMap extends Map { * ObjectIds as keys. * * Keys also cannot: - * - be named after special properties (like `prototype`, `constructor` and `__proto__`) + * - be named after special properties `prototype`, `constructor`, and `__proto__` * - start with a dollar sign (`$`) * - contain any dots (`.`) * From d94d8d36e2670d139c5e7694afc0359ab7108071 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 7 Aug 2024 19:56:55 -0400 Subject: [PATCH 18/23] Update test/docs/transactions.test.js Co-authored-by: hasezoey --- test/docs/transactions.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/docs/transactions.test.js b/test/docs/transactions.test.js index b2e208fe17d..de2ecfc9952 100644 --- a/test/docs/transactions.test.js +++ b/test/docs/transactions.test.js @@ -341,7 +341,6 @@ describe('transactions', function() { it('distinct (gh-8006)', async function() { const Character = db.model('gh8006_Character', new Schema({ name: String, rank: String }, { versionKey: false })); - const session = await db.startSession(); session.startTransaction(); From f6be45a95bfb8fb4c192e519059de83f459f1a25 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 7 Aug 2024 20:05:24 -0400 Subject: [PATCH 19/23] types(query): add options param to distinct() re: #8006 --- types/query.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/query.d.ts b/types/query.d.ts index 850d06eb25a..66a0042d342 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -354,7 +354,8 @@ declare module 'mongoose' { /** Creates a `distinct` query: returns the distinct values of the given `field` that match `filter`. */ distinct( field: DocKey, - filter?: FilterQuery + filter?: FilterQuery, + options?: QueryOptions ): QueryWithHelpers< Array< DocKey extends keyof WithLevel1NestedPaths From 58f384dbf04cc9df23e19113094b836ffa6438c7 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 7 Aug 2024 20:05:53 -0400 Subject: [PATCH 20/23] fix(query): consistent handling for non-object options with countDocuments(), estimatedDocumentCount(), distinct() --- lib/query.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/query.js b/lib/query.js index 20532ad1f8f..fa5d2cd856e 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2773,7 +2773,7 @@ Query.prototype.estimatedDocumentCount = function(options) { this.op = 'estimatedDocumentCount'; this._validateOp(); - if (typeof options === 'object' && options != null) { + if (options != null) { this.setOptions(options); } @@ -2832,7 +2832,7 @@ Query.prototype.countDocuments = function(conditions, options) { this.merge(conditions); } - if (typeof options === 'object' && options != null) { + if (options != null) { this.setOptions(options); } @@ -2906,7 +2906,7 @@ Query.prototype.distinct = function(field, conditions, options) { this._distinct = field; } - if (typeof options === 'object' && options != null) { + if (options != null) { this.setOptions(options); } From 3c955b1d3518e8f9612667b01a71e06e5b758e4a Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Thu, 8 Aug 2024 13:06:26 -0400 Subject: [PATCH 21/23] allow `mongoose.connection.db` to be undefined --- test/types/connect.test.ts | 1 + test/types/connection.test.ts | 4 ++-- types/connection.d.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/types/connect.test.ts b/test/types/connect.test.ts index 1e0dc92e528..0994661b105 100644 --- a/test/types/connect.test.ts +++ b/test/types/connect.test.ts @@ -5,3 +5,4 @@ import { expectType } from 'tsd'; expectType>(connect('mongodb://127.0.0.1:27017/test')); expectType>(connect('mongodb://127.0.0.1:27017/test', {})); expectType>(connect('mongodb://127.0.0.1:27017/test', { bufferCommands: true })); +expectType>(connect('mongodb://127.0.0.1:27017/test', { bufferCommands: true })); diff --git a/test/types/connection.test.ts b/test/types/connection.test.ts index e5f5c7ac9f2..29ddf02b3ba 100644 --- a/test/types/connection.test.ts +++ b/test/types/connection.test.ts @@ -40,7 +40,7 @@ expectType>(conn.modelNames()); expectType>(createConnection('mongodb://127.0.0.1:27017/test').close()); expectType>(createConnection('mongodb://127.0.0.1:27017/test').close(true)); -expectType(conn.db); +expectType(conn.db); expectType(conn.getClient()); expectType(conn.setClient(new mongodb.MongoClient('mongodb://127.0.0.1:27017/test'))); @@ -65,7 +65,7 @@ expectError(conn.host = 'invalid'); expectError(conn.port = 'invalid'); expectType(conn.collection('test')); -expectType(conn.db.collection('test')); +expectType(conn.db?.collection('test')); expectType>(conn.startSession()); expectType>(conn.startSession({ causalConsistency: true })); diff --git a/types/connection.d.ts b/types/connection.d.ts index b34dd226eeb..08165549a86 100644 --- a/types/connection.d.ts +++ b/types/connection.d.ts @@ -72,7 +72,7 @@ declare module 'mongoose' { readonly config: any; /** The mongodb.Db instance, set when the connection is opened */ - readonly db: mongodb.Db; + readonly db: mongodb.Db | undefined; /** * Helper for `createCollection()`. Will explicitly create the given collection From 7d5c8453624bbc9f8e945cee1a0fde754c2a0439 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 8 Aug 2024 14:34:52 -0400 Subject: [PATCH 22/23] Update connect.test.ts --- test/types/connect.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/types/connect.test.ts b/test/types/connect.test.ts index 0994661b105..1e0dc92e528 100644 --- a/test/types/connect.test.ts +++ b/test/types/connect.test.ts @@ -5,4 +5,3 @@ import { expectType } from 'tsd'; expectType>(connect('mongodb://127.0.0.1:27017/test')); expectType>(connect('mongodb://127.0.0.1:27017/test', {})); expectType>(connect('mongodb://127.0.0.1:27017/test', { bufferCommands: true })); -expectType>(connect('mongodb://127.0.0.1:27017/test', { bufferCommands: true })); From 2b4b2f965e57723eb28b58d629ebc9faa8ad7c10 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 8 Aug 2024 14:44:19 -0400 Subject: [PATCH 23/23] types(cursor): indicate that cursor.next() can return null Fix #14787 --- test/types/querycursor.test.ts | 2 ++ types/cursor.d.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/types/querycursor.test.ts b/test/types/querycursor.test.ts index f63f38370e1..81978059071 100644 --- a/test/types/querycursor.test.ts +++ b/test/types/querycursor.test.ts @@ -21,6 +21,8 @@ Test.find().cursor(). }). then(() => console.log('Done!')); +Test.find().cursor().next().then((doc) => expectType(doc)); + async function gh14374() { // `Parent` represents the object as it is stored in MongoDB interface Parent { diff --git a/types/cursor.d.ts b/types/cursor.d.ts index 5c467b866d5..888dd04c79b 100644 --- a/types/cursor.d.ts +++ b/types/cursor.d.ts @@ -51,7 +51,7 @@ declare module 'mongoose' { * Get the next document from this cursor. Will return `null` when there are * no documents left. */ - next(): Promise; + next(): Promise; options: Options; }