mongodb 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/lib/admin.js +5 -5
  2. package/lib/admin.js.map +1 -1
  3. package/lib/bulk/common.js +4 -4
  4. package/lib/bulk/common.js.map +1 -1
  5. package/lib/change_stream.js +37 -28
  6. package/lib/change_stream.js.map +1 -1
  7. package/lib/cmap/auth/scram.js +12 -1
  8. package/lib/cmap/auth/scram.js.map +1 -1
  9. package/lib/cmap/connection.js +9 -1
  10. package/lib/cmap/connection.js.map +1 -1
  11. package/lib/cmap/connection_pool.js +70 -57
  12. package/lib/cmap/connection_pool.js.map +1 -1
  13. package/lib/cmap/connection_pool_events.js.map +1 -1
  14. package/lib/cmap/message_stream.js +39 -6
  15. package/lib/cmap/message_stream.js.map +1 -1
  16. package/lib/cmap/wire_protocol/compression.js +18 -2
  17. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  18. package/lib/cmap/wire_protocol/constants.js +2 -2
  19. package/lib/cmap/wire_protocol/shared.js +3 -0
  20. package/lib/cmap/wire_protocol/shared.js.map +1 -1
  21. package/lib/collection.js +30 -30
  22. package/lib/collection.js.map +1 -1
  23. package/lib/connection_string.js +10 -0
  24. package/lib/connection_string.js.map +1 -1
  25. package/lib/cursor/abstract_cursor.js +16 -11
  26. package/lib/cursor/abstract_cursor.js.map +1 -1
  27. package/lib/cursor/aggregation_cursor.js +5 -5
  28. package/lib/cursor/aggregation_cursor.js.map +1 -1
  29. package/lib/cursor/find_cursor.js +6 -6
  30. package/lib/cursor/find_cursor.js.map +1 -1
  31. package/lib/db.js +14 -14
  32. package/lib/db.js.map +1 -1
  33. package/lib/deps.js +6 -1
  34. package/lib/deps.js.map +1 -1
  35. package/lib/encrypter.js +9 -2
  36. package/lib/encrypter.js.map +1 -1
  37. package/lib/mongo_client.js +11 -0
  38. package/lib/mongo_client.js.map +1 -1
  39. package/lib/operations/connect.js +1 -0
  40. package/lib/operations/connect.js.map +1 -1
  41. package/lib/operations/create_collection.js +8 -3
  42. package/lib/operations/create_collection.js.map +1 -1
  43. package/lib/operations/drop.js +1 -20
  44. package/lib/operations/drop.js.map +1 -1
  45. package/lib/operations/estimated_document_count.js +1 -20
  46. package/lib/operations/estimated_document_count.js.map +1 -1
  47. package/lib/operations/execute_operation.js +15 -9
  48. package/lib/operations/execute_operation.js.map +1 -1
  49. package/lib/operations/indexes.js +2 -2
  50. package/lib/operations/indexes.js.map +1 -1
  51. package/lib/operations/list_collections.js +2 -2
  52. package/lib/operations/list_collections.js.map +1 -1
  53. package/lib/sdam/monitor.js +10 -3
  54. package/lib/sdam/monitor.js.map +1 -1
  55. package/lib/sdam/srv_polling.js +2 -1
  56. package/lib/sdam/srv_polling.js.map +1 -1
  57. package/lib/sdam/topology.js +3 -2
  58. package/lib/sdam/topology.js.map +1 -1
  59. package/lib/sessions.js +29 -17
  60. package/lib/sessions.js.map +1 -1
  61. package/lib/utils.js +3 -2
  62. package/lib/utils.js.map +1 -1
  63. package/mongodb.d.ts +229 -37
  64. package/package.json +10 -9
  65. package/src/admin.ts +9 -5
  66. package/src/bulk/common.ts +4 -4
  67. package/src/change_stream.ts +250 -47
  68. package/src/cmap/auth/scram.ts +11 -1
  69. package/src/cmap/connection.ts +11 -0
  70. package/src/cmap/connection_pool.ts +90 -74
  71. package/src/cmap/connection_pool_events.ts +1 -1
  72. package/src/cmap/message_stream.ts +41 -7
  73. package/src/cmap/wire_protocol/compression.ts +27 -3
  74. package/src/cmap/wire_protocol/constants.ts +2 -2
  75. package/src/cmap/wire_protocol/shared.ts +5 -1
  76. package/src/collection.ts +38 -31
  77. package/src/connection_string.ts +10 -0
  78. package/src/cursor/abstract_cursor.ts +16 -13
  79. package/src/cursor/aggregation_cursor.ts +6 -6
  80. package/src/cursor/find_cursor.ts +7 -7
  81. package/src/db.ts +18 -14
  82. package/src/deps.ts +41 -12
  83. package/src/encrypter.ts +8 -2
  84. package/src/index.ts +9 -0
  85. package/src/mongo_client.ts +18 -1
  86. package/src/operations/connect.ts +1 -0
  87. package/src/operations/create_collection.ts +13 -3
  88. package/src/operations/drop.ts +1 -23
  89. package/src/operations/estimated_document_count.ts +2 -29
  90. package/src/operations/execute_operation.ts +25 -26
  91. package/src/operations/indexes.ts +3 -9
  92. package/src/operations/list_collections.ts +3 -3
  93. package/src/sdam/monitor.ts +10 -0
  94. package/src/sdam/srv_polling.ts +1 -0
  95. package/src/sdam/topology.ts +6 -2
  96. package/src/sessions.ts +31 -20
  97. package/src/transactions.ts +1 -1
  98. package/src/utils.ts +2 -1
package/src/deps.ts CHANGED
@@ -44,6 +44,30 @@ export interface KerberosClient {
44
44
  unwrap: (challenge: string, callback?: Callback<string>) => Promise<string> | void;
45
45
  }
46
46
 
47
+ type ZStandardLib = {
48
+ /**
49
+ * Compress using zstd.
50
+ * @param buf - Buffer to be compressed.
51
+ */
52
+ compress(buf: Buffer, level?: number): Promise<Buffer>;
53
+
54
+ /**
55
+ * Decompress using zstd.
56
+ */
57
+ decompress(buf: Buffer): Promise<Buffer>;
58
+ };
59
+
60
+ export let ZStandard: ZStandardLib | { kModuleError: MongoMissingDependencyError } =
61
+ makeErrorModule(
62
+ new MongoMissingDependencyError(
63
+ 'Optional module `@mongodb-js/zstd` not found. Please install it to enable zstd compression'
64
+ )
65
+ );
66
+
67
+ try {
68
+ ZStandard = require('@mongodb-js/zstd');
69
+ } catch {} // eslint-disable-line
70
+
47
71
  type SnappyLib = {
48
72
  [PKG_VERSION]: { major: number; minor: number; patch: number };
49
73
 
@@ -300,7 +324,7 @@ export interface AutoEncryptionOptions {
300
324
  /** Command line arguments to use when auto-spawning a mongocryptd */
301
325
  mongocryptdSpawnArgs?: string[];
302
326
  /**
303
- * Full path to a CSFLE shared library to be used (instead of mongocryptd).
327
+ * Full path to a MongoDB Crypt shared library to be used (instead of mongocryptd).
304
328
  *
305
329
  * This needs to be the path to the file itself, not a directory.
306
330
  * It can be an absolute or relative path. If the path is relative and
@@ -308,32 +332,36 @@ export interface AutoEncryptionOptions {
308
332
  * containing the mongodb-client-encryption native addon file. Otherwise,
309
333
  * the path will be interpreted relative to the current working directory.
310
334
  *
311
- * Currently, loading different CSFLE shared library files from different
335
+ * Currently, loading different MongoDB Crypt shared library files from different
312
336
  * MongoClients in the same process is not supported.
313
337
  *
314
- * If this option is provided and no CSFLE shared library could be loaded
338
+ * If this option is provided and no MongoDB Crypt shared library could be loaded
315
339
  * from the specified location, creating the MongoClient will fail.
316
340
  *
317
- * If this option is not provided and `csfleRequired` is not specified,
341
+ * If this option is not provided and `cryptSharedLibRequired` is not specified,
318
342
  * the AutoEncrypter will attempt to spawn and/or use mongocryptd according
319
343
  * to the mongocryptd-specific `extraOptions` options.
320
344
  *
321
345
  * Specifying a path prevents mongocryptd from being used as a fallback.
346
+ *
347
+ * @experimental Requires the MongoDB Crypt shared library, available in MongoDB 6.0 or higher.
322
348
  */
323
- csflePath?: string;
349
+ cryptSharedLibPath?: string;
324
350
  /**
325
- * If specified, never use mongocryptd and instead fail when the CSFLE shared library
326
- * could not be loaded.
351
+ * If specified, never use mongocryptd and instead fail when the MongoDB Crypt
352
+ * shared library could not be loaded.
327
353
  *
328
- * This is always true when `csflePath` is specified.
354
+ * This is always true when `cryptSharedLibPath` is specified.
355
+ *
356
+ * @experimental Requires the MongoDB Crypt shared library, available in MongoDB 6.0 or higher.
329
357
  */
330
- csfleRequired?: boolean;
358
+ cryptSharedLibRequired?: boolean;
331
359
  /**
332
- * Search paths for a CSFLE shared library to be used (instead of mongocryptd)
360
+ * Search paths for a MongoDB Crypt shared library to be used (instead of mongocryptd)
333
361
  * Only for driver testing!
334
362
  * @internal
335
363
  */
336
- csfleSearchPaths?: string[];
364
+ cryptSharedLibSearchPaths?: string[];
337
365
  };
338
366
  proxyOptions?: ProxyOptions;
339
367
  /** The TLS options to use connecting to the KMS provider */
@@ -354,5 +382,6 @@ export interface AutoEncrypter {
354
382
  teardown(force: boolean, callback: Callback): void;
355
383
  encrypt(ns: string, cmd: Document, options: any, callback: Callback<Document>): void;
356
384
  decrypt(cmd: Document, options: any, callback: Callback<Document>): void;
357
- readonly csfleVersionInfo: { version: bigint; versionStr: string } | null;
385
+ /** @experimental */
386
+ readonly cryptSharedLibVersionInfo: { version: bigint; versionStr: string } | null;
358
387
  }
package/src/encrypter.ts CHANGED
@@ -124,9 +124,15 @@ export class Encrypter {
124
124
 
125
125
  static checkForMongoCrypt(): void {
126
126
  let mongodbClientEncryption = undefined;
127
+ // Ensure you always wrap an optional require in the try block NODE-3199
127
128
  try {
128
- // Ensure you always wrap an optional require in the try block NODE-3199
129
- mongodbClientEncryption = require('mongodb-client-encryption');
129
+ // Note (NODE-4254): This is to get around the circular dependency between
130
+ // mongodb-client-encryption and the driver in the test scenarios.
131
+ if (process.env.MONGODB_CLIENT_ENCRYPTION_OVERRIDE) {
132
+ mongodbClientEncryption = require(process.env.MONGODB_CLIENT_ENCRYPTION_OVERRIDE);
133
+ } else {
134
+ mongodbClientEncryption = require('mongodb-client-encryption');
135
+ }
130
136
  } catch (err) {
131
137
  throw new MongoMissingDependencyError(
132
138
  'Auto-encryption requested, but the module is not installed. ' +
package/src/index.ts CHANGED
@@ -171,21 +171,30 @@ export type { UnorderedBulkOperation } from './bulk/unordered';
171
171
  export type {
172
172
  ChangeStream,
173
173
  ChangeStreamAggregateRawResult,
174
+ ChangeStreamCollModDocument,
175
+ ChangeStreamCreateDocument,
176
+ ChangeStreamCreateIndexDocument,
174
177
  ChangeStreamCursor,
175
178
  ChangeStreamCursorOptions,
176
179
  ChangeStreamDeleteDocument,
177
180
  ChangeStreamDocument,
181
+ ChangeStreamDocumentCollectionUUID,
178
182
  ChangeStreamDocumentCommon,
179
183
  ChangeStreamDocumentKey,
184
+ ChangeStreamDocumentOperationDescription,
180
185
  ChangeStreamDropDatabaseDocument,
181
186
  ChangeStreamDropDocument,
187
+ ChangeStreamDropIndexDocument,
182
188
  ChangeStreamEvents,
183
189
  ChangeStreamInsertDocument,
184
190
  ChangeStreamInvalidateDocument,
185
191
  ChangeStreamNameSpace,
186
192
  ChangeStreamOptions,
193
+ ChangeStreamRefineCollectionShardKeyDocument,
187
194
  ChangeStreamRenameDocument,
188
195
  ChangeStreamReplaceDocument,
196
+ ChangeStreamReshardCollectionDocument,
197
+ ChangeStreamShardCollectionDocument,
189
198
  ChangeStreamUpdateDocument,
190
199
  OperationTime,
191
200
  PipeOptions,
@@ -141,6 +141,8 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
141
141
  maxPoolSize?: number;
142
142
  /** The minimum number of connections in the connection pool. */
143
143
  minPoolSize?: number;
144
+ /** The maximum number of connections that may be in the process of being established concurrently by the connection pool. */
145
+ maxConnecting?: number;
144
146
  /** The maximum number of milliseconds that a connection can remain idle in the pool before being removed and closed. */
145
147
  maxIdleTimeMS?: number;
146
148
  /** The maximum time in milliseconds that a thread can wait for a connection to become available. */
@@ -268,11 +270,13 @@ export interface MongoClientPrivate {
268
270
  sessions: Set<ClientSession>;
269
271
  bsonOptions: BSONSerializeOptions;
270
272
  namespace: MongoDBNamespace;
271
- readonly options?: MongoOptions;
273
+ hasBeenClosed: boolean;
274
+ readonly options: MongoOptions;
272
275
  readonly readConcern?: ReadConcern;
273
276
  readonly writeConcern?: WriteConcern;
274
277
  readonly readPreference: ReadPreference;
275
278
  readonly logger: Logger;
279
+ readonly isMongoClient: true;
276
280
  }
277
281
 
278
282
  /** @public */
@@ -351,6 +355,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
351
355
  sessions: new Set(),
352
356
  bsonOptions: resolveBSONOptions(this[kOptions]),
353
357
  namespace: ns('admin'),
358
+ hasBeenClosed: false,
354
359
 
355
360
  get options() {
356
361
  return client[kOptions];
@@ -366,6 +371,9 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
366
371
  },
367
372
  get logger() {
368
373
  return client[kOptions].logger;
374
+ },
375
+ get isMongoClient(): true {
376
+ return true;
369
377
  }
370
378
  };
371
379
  }
@@ -446,6 +454,14 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
446
454
  forceOrCallback?: boolean | Callback<void>,
447
455
  callback?: Callback<void>
448
456
  ): Promise<void> | void {
457
+ // There's no way to set hasBeenClosed back to false
458
+ Object.defineProperty(this.s, 'hasBeenClosed', {
459
+ value: true,
460
+ enumerable: true,
461
+ configurable: false,
462
+ writable: false
463
+ });
464
+
449
465
  if (typeof forceOrCallback === 'function') {
450
466
  callback = forceOrCallback;
451
467
  }
@@ -638,6 +654,7 @@ export interface MongoOptions
638
654
  | 'keepAliveInitialDelay'
639
655
  | 'localThresholdMS'
640
656
  | 'logger'
657
+ | 'maxConnecting'
641
658
  | 'maxIdleTimeMS'
642
659
  | 'maxPoolSize'
643
660
  | 'minPoolSize'
@@ -62,6 +62,7 @@ function createTopology(
62
62
  // Events can be emitted before initialization is complete so we have to
63
63
  // save the reference to the topology on the client ASAP if the event handlers need to access it
64
64
  mongoClient.topology = topology;
65
+ topology.client = mongoClient;
65
66
 
66
67
  topology.once(Topology.OPEN, () => mongoClient.emit('open', mongoClient));
67
68
 
@@ -91,6 +91,11 @@ export interface CreateCollectionOptions extends CommandOperationOptions {
91
91
  expireAfterSeconds?: number;
92
92
  /** @experimental */
93
93
  encryptedFields?: Document;
94
+ /**
95
+ * If set, enables pre-update and post-update document events to be included for any
96
+ * change streams that listen on this collection.
97
+ */
98
+ changeStreamPreAndPostImages?: { enabled: boolean };
94
99
  }
95
100
 
96
101
  /** @internal */
@@ -122,13 +127,18 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
122
127
  db.s.client.options.autoEncryption?.encryptedFieldsMap?.[`${db.databaseName}.${name}`];
123
128
 
124
129
  if (encryptedFields) {
125
- // Create auxilliary collections for FLE2 support.
130
+ // Create auxilliary collections for queryable encryption support.
126
131
  const escCollection = encryptedFields.escCollection ?? `enxcol_.${name}.esc`;
127
132
  const eccCollection = encryptedFields.eccCollection ?? `enxcol_.${name}.ecc`;
128
133
  const ecocCollection = encryptedFields.ecocCollection ?? `enxcol_.${name}.ecoc`;
129
134
 
130
135
  for (const collectionName of [escCollection, eccCollection, ecocCollection]) {
131
- const createOp = new CreateCollectionOperation(db, collectionName);
136
+ const createOp = new CreateCollectionOperation(db, collectionName, {
137
+ clusteredIndex: {
138
+ key: { _id: 1 },
139
+ unique: true
140
+ }
141
+ });
132
142
  await createOp.executeWithoutEncryptedFieldsCheck(server, session);
133
143
  }
134
144
 
@@ -140,7 +150,7 @@ export class CreateCollectionOperation extends CommandOperation<Collection> {
140
150
  const coll = await this.executeWithoutEncryptedFieldsCheck(server, session);
141
151
 
142
152
  if (encryptedFields) {
143
- // Create the required index for FLE2 support.
153
+ // Create the required index for queryable encryption support.
144
154
  const createIndexOp = new CreateIndexOperation(db, name, { __safeContent__: 1 }, {});
145
155
  await new Promise<void>((resolve, reject) => {
146
156
  createIndexOp.execute(server, session, err => (err ? reject(err) : resolve()));
@@ -51,24 +51,6 @@ export class DropCollectionOperation extends CommandOperation<boolean> {
51
51
  encryptedFields = listCollectionsResult?.[0]?.options?.encryptedFields;
52
52
  }
53
53
 
54
- let result;
55
- let errorForMainOperation;
56
- try {
57
- result = await this.executeWithoutEncryptedFieldsCheck(server, session);
58
- } catch (err) {
59
- if (
60
- !encryptedFields ||
61
- !(err instanceof MongoServerError) ||
62
- err.code !== MONGODB_ERROR_CODES.NamespaceNotFound
63
- ) {
64
- throw err;
65
- }
66
- // Save a possible NamespaceNotFound error for later
67
- // in the encryptedFields case, so that the auxilliary
68
- // collections will still be dropped.
69
- errorForMainOperation = err;
70
- }
71
-
72
54
  if (encryptedFields) {
73
55
  const escCollection = encryptedFields.escCollection || `enxcol_.${name}.esc`;
74
56
  const eccCollection = encryptedFields.eccCollection || `enxcol_.${name}.ecc`;
@@ -88,13 +70,9 @@ export class DropCollectionOperation extends CommandOperation<boolean> {
88
70
  }
89
71
  }
90
72
  }
91
-
92
- if (errorForMainOperation) {
93
- throw errorForMainOperation;
94
- }
95
73
  }
96
74
 
97
- return result;
75
+ return await this.executeWithoutEncryptedFieldsCheck(server, session);
98
76
  })().then(
99
77
  result => callback(undefined, result),
100
78
  err => callback(err)
@@ -1,9 +1,8 @@
1
1
  import type { Document } from '../bson';
2
2
  import type { Collection } from '../collection';
3
- import type { MongoServerError } from '../error';
4
3
  import type { Server } from '../sdam/server';
5
4
  import type { ClientSession } from '../sessions';
6
- import { Callback, maxWireVersion } from '../utils';
5
+ import type { Callback } from '../utils';
7
6
  import { CommandOperation, CommandOperationOptions } from './command';
8
7
  import { Aspect, defineAspects } from './operation';
9
8
 
@@ -32,32 +31,6 @@ export class EstimatedDocumentCountOperation extends CommandOperation<number> {
32
31
  server: Server,
33
32
  session: ClientSession | undefined,
34
33
  callback: Callback<number>
35
- ): void {
36
- if (maxWireVersion(server) < 12) {
37
- return this.executeLegacy(server, session, callback);
38
- }
39
- const pipeline = [{ $collStats: { count: {} } }, { $group: { _id: 1, n: { $sum: '$count' } } }];
40
-
41
- const cmd: Document = { aggregate: this.collectionName, pipeline, cursor: {} };
42
-
43
- if (typeof this.options.maxTimeMS === 'number') {
44
- cmd.maxTimeMS = this.options.maxTimeMS;
45
- }
46
-
47
- super.executeCommand(server, session, cmd, (err, response) => {
48
- if (err && (err as MongoServerError).code !== 26) {
49
- callback(err);
50
- return;
51
- }
52
-
53
- callback(undefined, response?.cursor?.firstBatch[0]?.n || 0);
54
- });
55
- }
56
-
57
- executeLegacy(
58
- server: Server,
59
- session: ClientSession | undefined,
60
- callback: Callback<number>
61
34
  ): void {
62
35
  const cmd: Document = { count: this.collectionName };
63
36
 
@@ -71,7 +44,7 @@ export class EstimatedDocumentCountOperation extends CommandOperation<number> {
71
44
  return;
72
45
  }
73
46
 
74
- callback(undefined, response.n || 0);
47
+ callback(undefined, response?.n || 0);
75
48
  });
76
49
  }
77
50
  }
@@ -7,11 +7,13 @@ import {
7
7
  MongoError,
8
8
  MongoExpiredSessionError,
9
9
  MongoNetworkError,
10
+ MongoNotConnectedError,
10
11
  MongoRuntimeError,
11
12
  MongoServerError,
12
13
  MongoTransactionError,
13
14
  MongoUnexpectedServerResponseError
14
15
  } from '../error';
16
+ import type { MongoClient } from '../mongo_client';
15
17
  import { ReadPreference } from '../read_preference';
16
18
  import type { Server } from '../sdam/server';
17
19
  import {
@@ -21,13 +23,7 @@ import {
21
23
  } from '../sdam/server_selection';
22
24
  import type { Topology } from '../sdam/topology';
23
25
  import type { ClientSession } from '../sessions';
24
- import {
25
- Callback,
26
- getTopology,
27
- maybePromise,
28
- supportsRetryableWrites,
29
- TopologyProvider
30
- } from '../utils';
26
+ import { Callback, maybePromise, supportsRetryableWrites } from '../utils';
31
27
  import { AbstractOperation, Aspect } from './operation';
32
28
 
33
29
  const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
@@ -66,45 +62,48 @@ export interface ExecutionResult {
66
62
  export function executeOperation<
67
63
  T extends AbstractOperation<TResult>,
68
64
  TResult = ResultTypeFromOperation<T>
69
- >(topologyProvider: TopologyProvider, operation: T): Promise<TResult>;
65
+ >(client: MongoClient, operation: T): Promise<TResult>;
70
66
  export function executeOperation<
71
67
  T extends AbstractOperation<TResult>,
72
68
  TResult = ResultTypeFromOperation<T>
73
- >(topologyProvider: TopologyProvider, operation: T, callback: Callback<TResult>): void;
69
+ >(client: MongoClient, operation: T, callback: Callback<TResult>): void;
74
70
  export function executeOperation<
75
71
  T extends AbstractOperation<TResult>,
76
72
  TResult = ResultTypeFromOperation<T>
77
- >(
78
- topologyProvider: TopologyProvider,
79
- operation: T,
80
- callback?: Callback<TResult>
81
- ): Promise<TResult> | void;
73
+ >(client: MongoClient, operation: T, callback?: Callback<TResult>): Promise<TResult> | void;
82
74
  export function executeOperation<
83
75
  T extends AbstractOperation<TResult>,
84
76
  TResult = ResultTypeFromOperation<T>
85
- >(
86
- topologyProvider: TopologyProvider,
87
- operation: T,
88
- callback?: Callback<TResult>
89
- ): Promise<TResult> | void {
77
+ >(client: MongoClient, operation: T, callback?: Callback<TResult>): Promise<TResult> | void {
90
78
  if (!(operation instanceof AbstractOperation)) {
91
79
  // TODO(NODE-3483): Extend MongoRuntimeError
92
80
  throw new MongoRuntimeError('This method requires a valid operation instance');
93
81
  }
94
82
 
95
83
  return maybePromise(callback, callback => {
96
- let topology: Topology;
97
- try {
98
- // TODO(NODE-4151): Use skipPingOnConnect and call connect here to make client.connect optional
99
- topology = getTopology(topologyProvider);
100
- } catch (error) {
101
- return callback(error);
84
+ const topology = client.topology;
85
+
86
+ if (topology == null) {
87
+ if (client.s.hasBeenClosed) {
88
+ return callback(
89
+ new MongoNotConnectedError('Client must be connected before running operations')
90
+ );
91
+ }
92
+ client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
93
+ return client.connect(error => {
94
+ delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
95
+ if (error) {
96
+ return callback(error);
97
+ }
98
+ return executeOperation<T, TResult>(client, operation, callback);
99
+ });
102
100
  }
101
+
103
102
  if (topology.shouldCheckForSessionSupport()) {
104
103
  return topology.selectServer(ReadPreference.primaryPreferred, {}, err => {
105
104
  if (err) return callback(err);
106
105
 
107
- executeOperation<T, TResult>(topologyProvider, operation, callback);
106
+ executeOperation<T, TResult>(client, operation, callback);
108
107
  });
109
108
  }
110
109
 
@@ -7,13 +7,7 @@ import type { OneOrMore } from '../mongo_types';
7
7
  import { ReadPreference } from '../read_preference';
8
8
  import type { Server } from '../sdam/server';
9
9
  import type { ClientSession } from '../sessions';
10
- import {
11
- Callback,
12
- getTopology,
13
- maxWireVersion,
14
- MongoDBNamespace,
15
- parseIndexOptions
16
- } from '../utils';
10
+ import { Callback, maxWireVersion, MongoDBNamespace, parseIndexOptions } from '../utils';
17
11
  import {
18
12
  CollationOptions,
19
13
  CommandOperation,
@@ -424,7 +418,7 @@ export class ListIndexesCursor extends AbstractCursor {
424
418
  options?: ListIndexesOptions;
425
419
 
426
420
  constructor(collection: Collection, options?: ListIndexesOptions) {
427
- super(getTopology(collection), collection.s.namespace, options);
421
+ super(collection.s.db.s.client, collection.s.namespace, options);
428
422
  this.parent = collection;
429
423
  this.options = options;
430
424
  }
@@ -444,7 +438,7 @@ export class ListIndexesCursor extends AbstractCursor {
444
438
  session
445
439
  });
446
440
 
447
- executeOperation(this.parent, operation, (err, response) => {
441
+ executeOperation(this.parent.s.db.s.client, operation, (err, response) => {
448
442
  if (err || response == null) return callback(err);
449
443
 
450
444
  // TODO: NODE-2882
@@ -3,7 +3,7 @@ import { AbstractCursor } from '../cursor/abstract_cursor';
3
3
  import type { Db } from '../db';
4
4
  import type { Server } from '../sdam/server';
5
5
  import type { ClientSession } from '../sessions';
6
- import { Callback, getTopology, maxWireVersion } from '../utils';
6
+ import { Callback, maxWireVersion } from '../utils';
7
7
  import { CommandOperation, CommandOperationOptions } from './command';
8
8
  import { executeOperation, ExecutionResult } from './execute_operation';
9
9
  import { Aspect, defineAspects } from './operation';
@@ -97,7 +97,7 @@ export class ListCollectionsCursor<
97
97
  options?: ListCollectionsOptions;
98
98
 
99
99
  constructor(db: Db, filter: Document, options?: ListCollectionsOptions) {
100
- super(getTopology(db), db.s.namespace, options);
100
+ super(db.s.client, db.s.namespace, options);
101
101
  this.parent = db;
102
102
  this.filter = filter;
103
103
  this.options = options;
@@ -118,7 +118,7 @@ export class ListCollectionsCursor<
118
118
  session
119
119
  });
120
120
 
121
- executeOperation(this.parent, operation, (err, response) => {
121
+ executeOperation(this.parent.s.client, operation, (err, response) => {
122
122
  if (err || response == null) return callback(err);
123
123
 
124
124
  // TODO: NODE-2882
@@ -1,3 +1,5 @@
1
+ import { setTimeout } from 'timers';
2
+
1
3
  import { Document, Long } from '../bson';
2
4
  import { connect } from '../cmap/connect';
3
5
  import { Connection, ConnectionOptions } from '../cmap/connection';
@@ -88,6 +90,10 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
88
90
  [kMonitorId]?: InterruptibleAsyncInterval;
89
91
  [kRTTPinger]?: RTTPinger;
90
92
 
93
+ get connection(): Connection | undefined {
94
+ return this[kConnection];
95
+ }
96
+
91
97
  constructor(server: Server, options: MonitorOptions) {
92
98
  super();
93
99
 
@@ -310,6 +316,10 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
310
316
  }
311
317
 
312
318
  if (conn) {
319
+ // Tell the connection that we are using the streaming protocol so that the
320
+ // connection's message stream will only read the last hello on the buffer.
321
+ conn.isMonitoringConnection = true;
322
+
313
323
  if (isInCloseState(monitor)) {
314
324
  conn.destroy({ force: true });
315
325
  return;
@@ -1,4 +1,5 @@
1
1
  import * as dns from 'dns';
2
+ import { setTimeout } from 'timers';
2
3
 
3
4
  import { MongoRuntimeError } from '../error';
4
5
  import { Logger, LoggerOptions } from '../logger';
@@ -1,4 +1,6 @@
1
1
  import Denque = require('denque');
2
+ import { setTimeout } from 'timers';
3
+
2
4
  import type { BSONSerializeOptions, Document } from '../bson';
3
5
  import { deserialize, serialize } from '../bson';
4
6
  import type { MongoCredentials } from '../cmap/auth/mongo_credentials';
@@ -27,7 +29,7 @@ import {
27
29
  MongoServerSelectionError,
28
30
  MongoTopologyClosedError
29
31
  } from '../error';
30
- import type { MongoOptions, ServerApi } from '../mongo_client';
32
+ import type { MongoClient, MongoOptions, ServerApi } from '../mongo_client';
31
33
  import { TypedEventEmitter } from '../mongo_types';
32
34
  import { ReadPreference, ReadPreferenceLike } from '../read_preference';
33
35
  import {
@@ -203,6 +205,8 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
203
205
  /** @internal */
204
206
  _type?: string;
205
207
 
208
+ client!: MongoClient;
209
+
206
210
  /** @event */
207
211
  static readonly SERVER_OPENING = SERVER_OPENING;
208
212
  /** @event */
@@ -626,7 +630,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
626
630
 
627
631
  /** Start a logical session */
628
632
  startSession(options: ClientSessionOptions, clientOptions?: MongoOptions): ClientSession {
629
- const session = new ClientSession(this, this.s.sessionPool, options, clientOptions);
633
+ const session = new ClientSession(this.client, this.s.sessionPool, options, clientOptions);
630
634
  session.once('ended', () => {
631
635
  this.s.sessions.delete(session);
632
636
  });