mongodb 6.16.0 → 6.17.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 (95) hide show
  1. package/README.md +2 -0
  2. package/lib/beta.d.ts +132 -33
  3. package/lib/bulk/common.js +4 -4
  4. package/lib/bulk/common.js.map +1 -1
  5. package/lib/client-side-encryption/auto_encrypter.js +5 -2
  6. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  7. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  8. package/lib/client-side-encryption/state_machine.js +1 -1
  9. package/lib/client-side-encryption/state_machine.js.map +1 -1
  10. package/lib/cmap/auth/mongodb_oidc/automated_callback_workflow.js +6 -0
  11. package/lib/cmap/auth/mongodb_oidc/automated_callback_workflow.js.map +1 -1
  12. package/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js +21 -33
  13. package/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js.map +1 -1
  14. package/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js +14 -21
  15. package/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js.map +1 -1
  16. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js +19 -26
  17. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js.map +1 -1
  18. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js +12 -24
  19. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js.map +1 -1
  20. package/lib/cmap/auth/mongodb_oidc.js +5 -4
  21. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  22. package/lib/cmap/connect.js +4 -3
  23. package/lib/cmap/connect.js.map +1 -1
  24. package/lib/cmap/connection.js +8 -3
  25. package/lib/cmap/connection.js.map +1 -1
  26. package/lib/cmap/connection_pool.js +6 -1
  27. package/lib/cmap/connection_pool.js.map +1 -1
  28. package/lib/cmap/wire_protocol/constants.js +2 -2
  29. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  30. package/lib/collection.js.map +1 -1
  31. package/lib/connection_string.js +2 -0
  32. package/lib/connection_string.js.map +1 -1
  33. package/lib/constants.js +0 -1
  34. package/lib/constants.js.map +1 -1
  35. package/lib/encrypter.js +3 -7
  36. package/lib/encrypter.js.map +1 -1
  37. package/lib/error.js +28 -1
  38. package/lib/error.js.map +1 -1
  39. package/lib/index.js +4 -3
  40. package/lib/index.js.map +1 -1
  41. package/lib/mongo_client.js +66 -20
  42. package/lib/mongo_client.js.map +1 -1
  43. package/lib/operations/aggregate.js +0 -1
  44. package/lib/operations/aggregate.js.map +1 -1
  45. package/lib/operations/client_bulk_write/command_builder.js +20 -13
  46. package/lib/operations/client_bulk_write/command_builder.js.map +1 -1
  47. package/lib/operations/find_and_modify.js +1 -1
  48. package/lib/operations/find_and_modify.js.map +1 -1
  49. package/lib/operations/update.js +6 -2
  50. package/lib/operations/update.js.map +1 -1
  51. package/lib/sdam/server.js +4 -1
  52. package/lib/sdam/server.js.map +1 -1
  53. package/lib/sdam/topology.js +9 -4
  54. package/lib/sdam/topology.js.map +1 -1
  55. package/lib/sort.js +13 -6
  56. package/lib/sort.js.map +1 -1
  57. package/lib/utils.js +15 -1
  58. package/lib/utils.js.map +1 -1
  59. package/mongodb.d.ts +132 -33
  60. package/package.json +19 -19
  61. package/src/bulk/common.ts +11 -6
  62. package/src/client-side-encryption/auto_encrypter.ts +10 -2
  63. package/src/client-side-encryption/client_encryption.ts +5 -0
  64. package/src/client-side-encryption/state_machine.ts +1 -1
  65. package/src/cmap/auth/mongodb_oidc/automated_callback_workflow.ts +6 -0
  66. package/src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts +23 -35
  67. package/src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts +16 -23
  68. package/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts +17 -25
  69. package/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts +11 -24
  70. package/src/cmap/auth/mongodb_oidc.ts +13 -8
  71. package/src/cmap/connect.ts +4 -3
  72. package/src/cmap/connection.ts +11 -4
  73. package/src/cmap/connection_pool.ts +8 -1
  74. package/src/cmap/wire_protocol/constants.ts +2 -2
  75. package/src/cmap/wire_protocol/on_demand/document.ts +1 -1
  76. package/src/cmap/wire_protocol/responses.ts +2 -2
  77. package/src/collection.ts +2 -1
  78. package/src/connection_string.ts +2 -0
  79. package/src/constants.ts +0 -1
  80. package/src/encrypter.ts +3 -10
  81. package/src/error.ts +28 -0
  82. package/src/index.ts +1 -0
  83. package/src/mongo_client.ts +73 -21
  84. package/src/operations/aggregate.ts +0 -1
  85. package/src/operations/client_bulk_write/command_builder.ts +32 -14
  86. package/src/operations/client_bulk_write/common.ts +5 -0
  87. package/src/operations/find_and_modify.ts +1 -1
  88. package/src/operations/update.ts +12 -3
  89. package/src/sdam/server.ts +5 -1
  90. package/src/sdam/topology.ts +10 -4
  91. package/src/sort.ts +32 -23
  92. package/src/utils.ts +21 -1
  93. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js +0 -110
  94. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +0 -1
  95. package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +0 -142
@@ -1,8 +1,6 @@
1
1
  import { MongoGCPError } from '../../../error';
2
2
  import { get } from '../../../utils';
3
- import { type MongoCredentials } from '../mongo_credentials';
4
- import { type AccessToken, MachineWorkflow } from './machine_workflow';
5
- import { type TokenCache } from './token_cache';
3
+ import type { OIDCCallbackFunction, OIDCCallbackParams, OIDCResponse } from '../mongodb_oidc';
6
4
 
7
5
  /** GCP base URL. */
8
6
  const GCP_BASE_URL =
@@ -15,30 +13,25 @@ const GCP_HEADERS = Object.freeze({ 'Metadata-Flavor': 'Google' });
15
13
  const TOKEN_RESOURCE_MISSING_ERROR =
16
14
  'TOKEN_RESOURCE must be set in the auth mechanism properties when ENVIRONMENT is gcp.';
17
15
 
18
- export class GCPMachineWorkflow extends MachineWorkflow {
19
- /**
20
- * Instantiate the machine workflow.
21
- */
22
- constructor(cache: TokenCache) {
23
- super(cache);
24
- }
25
-
26
- /**
27
- * Get the token from the environment.
28
- */
29
- async getToken(credentials?: MongoCredentials): Promise<AccessToken> {
30
- const tokenAudience = credentials?.mechanismProperties.TOKEN_RESOURCE;
31
- if (!tokenAudience) {
32
- throw new MongoGCPError(TOKEN_RESOURCE_MISSING_ERROR);
33
- }
34
- return await getGcpTokenData(tokenAudience);
16
+ /**
17
+ * The callback function to be used in the automated callback workflow.
18
+ * @param params - The OIDC callback parameters.
19
+ * @returns The OIDC response.
20
+ */
21
+ export const callback: OIDCCallbackFunction = async (
22
+ params: OIDCCallbackParams
23
+ ): Promise<OIDCResponse> => {
24
+ const tokenAudience = params.tokenAudience;
25
+ if (!tokenAudience) {
26
+ throw new MongoGCPError(TOKEN_RESOURCE_MISSING_ERROR);
35
27
  }
36
- }
28
+ return await getGcpTokenData(tokenAudience);
29
+ };
37
30
 
38
31
  /**
39
32
  * Hit the GCP endpoint to get the token data.
40
33
  */
41
- async function getGcpTokenData(tokenAudience: string): Promise<AccessToken> {
34
+ async function getGcpTokenData(tokenAudience: string): Promise<OIDCResponse> {
42
35
  const url = new URL(GCP_BASE_URL);
43
36
  url.searchParams.append('audience', tokenAudience);
44
37
  const response = await get(url, {
@@ -49,5 +42,5 @@ async function getGcpTokenData(tokenAudience: string): Promise<AccessToken> {
49
42
  `Status code ${response.status} returned from the GCP endpoint. Response body: ${response.body}`
50
43
  );
51
44
  }
52
- return { access_token: response.body };
45
+ return { accessToken: response.body };
53
46
  }
@@ -1,7 +1,6 @@
1
1
  import { readFile } from 'fs/promises';
2
2
 
3
- import { type AccessToken, MachineWorkflow } from './machine_workflow';
4
- import { type TokenCache } from './token_cache';
3
+ import type { OIDCCallbackFunction, OIDCResponse } from '../mongodb_oidc';
5
4
 
6
5
  /** The fallback file name */
7
6
  const FALLBACK_FILENAME = '/var/run/secrets/kubernetes.io/serviceaccount/token';
@@ -12,27 +11,20 @@ const AZURE_FILENAME = 'AZURE_FEDERATED_TOKEN_FILE';
12
11
  /** The AWS environment variable for the file name. */
13
12
  const AWS_FILENAME = 'AWS_WEB_IDENTITY_TOKEN_FILE';
14
13
 
15
- export class K8SMachineWorkflow extends MachineWorkflow {
16
- /**
17
- * Instantiate the machine workflow.
18
- */
19
- constructor(cache: TokenCache) {
20
- super(cache);
14
+ /**
15
+ * The callback function to be used in the automated callback workflow.
16
+ * @param params - The OIDC callback parameters.
17
+ * @returns The OIDC response.
18
+ */
19
+ export const callback: OIDCCallbackFunction = async (): Promise<OIDCResponse> => {
20
+ let filename: string;
21
+ if (process.env[AZURE_FILENAME]) {
22
+ filename = process.env[AZURE_FILENAME];
23
+ } else if (process.env[AWS_FILENAME]) {
24
+ filename = process.env[AWS_FILENAME];
25
+ } else {
26
+ filename = FALLBACK_FILENAME;
21
27
  }
22
-
23
- /**
24
- * Get the token from the environment.
25
- */
26
- async getToken(): Promise<AccessToken> {
27
- let filename: string;
28
- if (process.env[AZURE_FILENAME]) {
29
- filename = process.env[AZURE_FILENAME];
30
- } else if (process.env[AWS_FILENAME]) {
31
- filename = process.env[AWS_FILENAME];
32
- } else {
33
- filename = FALLBACK_FILENAME;
34
- }
35
- const token = await readFile(filename, 'utf8');
36
- return { access_token: token };
37
- }
38
- }
28
+ const token = await readFile(filename, 'utf8');
29
+ return { accessToken: token };
30
+ };
@@ -1,34 +1,21 @@
1
1
  import * as fs from 'fs';
2
2
 
3
3
  import { MongoAWSError } from '../../../error';
4
- import { type AccessToken, MachineWorkflow } from './machine_workflow';
5
- import { type TokenCache } from './token_cache';
4
+ import type { OIDCCallbackFunction, OIDCResponse } from '../mongodb_oidc';
6
5
 
7
6
  /** Error for when the token is missing in the environment. */
8
7
  const TOKEN_MISSING_ERROR = 'OIDC_TOKEN_FILE must be set in the environment.';
9
8
 
10
9
  /**
11
- * Device workflow implementation for AWS.
12
- *
13
- * @internal
10
+ * The callback function to be used in the automated callback workflow.
11
+ * @param params - The OIDC callback parameters.
12
+ * @returns The OIDC response.
14
13
  */
15
- export class TokenMachineWorkflow extends MachineWorkflow {
16
- /**
17
- * Instantiate the machine workflow.
18
- */
19
- constructor(cache: TokenCache) {
20
- super(cache);
14
+ export const callback: OIDCCallbackFunction = async (): Promise<OIDCResponse> => {
15
+ const tokenFile = process.env.OIDC_TOKEN_FILE;
16
+ if (!tokenFile) {
17
+ throw new MongoAWSError(TOKEN_MISSING_ERROR);
21
18
  }
22
-
23
- /**
24
- * Get the token from the environment.
25
- */
26
- async getToken(): Promise<AccessToken> {
27
- const tokenFile = process.env.OIDC_TOKEN_FILE;
28
- if (!tokenFile) {
29
- throw new MongoAWSError(TOKEN_MISSING_ERROR);
30
- }
31
- const token = await fs.promises.readFile(tokenFile, 'utf8');
32
- return { access_token: token };
33
- }
34
- }
19
+ const token = await fs.promises.readFile(tokenFile, 'utf8');
20
+ return { accessToken: token };
21
+ };
@@ -4,11 +4,12 @@ import type { HandshakeDocument } from '../connect';
4
4
  import type { Connection } from '../connection';
5
5
  import { type AuthContext, AuthProvider } from './auth_provider';
6
6
  import type { MongoCredentials } from './mongo_credentials';
7
- import { AzureMachineWorkflow } from './mongodb_oidc/azure_machine_workflow';
8
- import { GCPMachineWorkflow } from './mongodb_oidc/gcp_machine_workflow';
9
- import { K8SMachineWorkflow } from './mongodb_oidc/k8s_machine_workflow';
7
+ import { AutomatedCallbackWorkflow } from './mongodb_oidc/automated_callback_workflow';
8
+ import { callback as azureCallback } from './mongodb_oidc/azure_machine_workflow';
9
+ import { callback as gcpCallback } from './mongodb_oidc/gcp_machine_workflow';
10
+ import { callback as k8sCallback } from './mongodb_oidc/k8s_machine_workflow';
10
11
  import { TokenCache } from './mongodb_oidc/token_cache';
11
- import { TokenMachineWorkflow } from './mongodb_oidc/token_machine_workflow';
12
+ import { callback as testCallback } from './mongodb_oidc/token_machine_workflow';
12
13
 
13
14
  /** Error when credentials are missing. */
14
15
  const MISSING_CREDENTIALS_ERROR = 'AuthContext must provide credentials.';
@@ -78,6 +79,8 @@ export interface OIDCCallbackParams {
78
79
  idpInfo?: IdPInfo;
79
80
  /** The refresh token, if applicable, to be used by the callback to request a new token from the issuer. */
80
81
  refreshToken?: string;
82
+ /** The token audience for GCP and Azure. */
83
+ tokenAudience?: string;
81
84
  }
82
85
 
83
86
  /**
@@ -93,6 +96,8 @@ type EnvironmentName = 'test' | 'azure' | 'gcp' | 'k8s' | undefined;
93
96
 
94
97
  /** @internal */
95
98
  export interface Workflow {
99
+ cache: TokenCache;
100
+
96
101
  /**
97
102
  * All device workflows must implement this method in order to get the access
98
103
  * token and then call authenticate with it.
@@ -116,10 +121,10 @@ export interface Workflow {
116
121
 
117
122
  /** @internal */
118
123
  export const OIDC_WORKFLOWS: Map<EnvironmentName, () => Workflow> = new Map();
119
- OIDC_WORKFLOWS.set('test', () => new TokenMachineWorkflow(new TokenCache()));
120
- OIDC_WORKFLOWS.set('azure', () => new AzureMachineWorkflow(new TokenCache()));
121
- OIDC_WORKFLOWS.set('gcp', () => new GCPMachineWorkflow(new TokenCache()));
122
- OIDC_WORKFLOWS.set('k8s', () => new K8SMachineWorkflow(new TokenCache()));
124
+ OIDC_WORKFLOWS.set('test', () => new AutomatedCallbackWorkflow(new TokenCache(), testCallback));
125
+ OIDC_WORKFLOWS.set('azure', () => new AutomatedCallbackWorkflow(new TokenCache(), azureCallback));
126
+ OIDC_WORKFLOWS.set('gcp', () => new AutomatedCallbackWorkflow(new TokenCache(), gcpCallback));
127
+ OIDC_WORKFLOWS.set('k8s', () => new AutomatedCallbackWorkflow(new TokenCache(), k8sCallback));
123
128
 
124
129
  /**
125
130
  * OIDC auth provider.
@@ -289,6 +289,7 @@ export const LEGAL_TLS_SOCKET_OPTIONS = [
289
289
  export const LEGAL_TCP_SOCKET_OPTIONS = [
290
290
  'autoSelectFamily',
291
291
  'autoSelectFamilyAttemptTimeout',
292
+ 'keepAliveInitialDelay',
292
293
  'family',
293
294
  'hints',
294
295
  'localAddress',
@@ -306,6 +307,9 @@ function parseConnectOptions(options: ConnectionOptions): SocketConnectOpts {
306
307
  (result as Document)[name] = options[name];
307
308
  }
308
309
  }
310
+ result.keepAliveInitialDelay ??= 120000;
311
+ result.keepAlive = true;
312
+ result.noDelay = options.noDelay ?? true;
309
313
 
310
314
  if (typeof hostAddress.socketPath === 'string') {
311
315
  result.path = hostAddress.socketPath;
@@ -347,7 +351,6 @@ function parseSslOptions(options: MakeConnectionOptions): TLSConnectionOpts {
347
351
 
348
352
  export async function makeSocket(options: MakeConnectionOptions): Promise<Stream> {
349
353
  const useTLS = options.tls ?? false;
350
- const noDelay = options.noDelay ?? true;
351
354
  const connectTimeoutMS = options.connectTimeoutMS ?? 30000;
352
355
  const existingSocket = options.existingSocket;
353
356
 
@@ -376,9 +379,7 @@ export async function makeSocket(options: MakeConnectionOptions): Promise<Stream
376
379
  socket = net.createConnection(parseConnectOptions(options));
377
380
  }
378
381
 
379
- socket.setKeepAlive(true, 300000);
380
382
  socket.setTimeout(connectTimeoutMS);
381
- socket.setNoDelay(noDelay);
382
383
 
383
384
  let cancellationHandler: ((err: Error) => void) | null = null;
384
385
 
@@ -247,9 +247,9 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
247
247
  this.lastUseTime = now();
248
248
 
249
249
  this.messageStream = this.socket
250
- .on('error', this.onError.bind(this))
250
+ .on('error', this.onSocketError.bind(this))
251
251
  .pipe(new SizedMessageTransform({ connection: this }))
252
- .on('error', this.onError.bind(this));
252
+ .on('error', this.onTransformError.bind(this));
253
253
  this.socket.on('close', this.onClose.bind(this));
254
254
  this.socket.on('timeout', this.onTimeout.bind(this));
255
255
 
@@ -304,6 +304,14 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
304
304
  this.lastUseTime = now();
305
305
  }
306
306
 
307
+ private onSocketError(cause: Error) {
308
+ this.onError(new MongoNetworkError(cause.message, { cause }));
309
+ }
310
+
311
+ private onTransformError(error: Error) {
312
+ this.onError(error);
313
+ }
314
+
307
315
  public onError(error: Error) {
308
316
  this.cleanup(error);
309
317
  }
@@ -769,7 +777,6 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
769
777
  } finally {
770
778
  this.dataEvents = null;
771
779
  this.messageStream.pause();
772
- this.throwIfAborted();
773
780
  }
774
781
  }
775
782
  }
@@ -857,7 +864,7 @@ export class CryptoConnection extends Connection {
857
864
  ns: MongoDBNamespace,
858
865
  cmd: Document,
859
866
  options?: CommandOptions,
860
- responseType?: T | undefined
867
+ responseType?: T
861
868
  ): Promise<Document> {
862
869
  const { autoEncrypter } = this;
863
870
  if (!autoEncrypter) {
@@ -17,6 +17,7 @@ import {
17
17
  } from '../constants';
18
18
  import {
19
19
  type AnyError,
20
+ MongoClientClosedError,
20
21
  type MongoError,
21
22
  MongoInvalidArgumentError,
22
23
  MongoMissingCredentialsError,
@@ -484,11 +485,17 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
484
485
  for (const connection of this.checkedOut) {
485
486
  if (connection.generation <= minGeneration) {
486
487
  connection.onError(new PoolClearedOnNetworkError(this));
487
- this.checkIn(connection);
488
488
  }
489
489
  }
490
490
  }
491
491
 
492
+ /** For MongoClient.close() procedures */
493
+ public closeCheckedOutConnections() {
494
+ for (const conn of this.checkedOut) {
495
+ conn.onError(new MongoClientClosedError());
496
+ }
497
+ }
498
+
492
499
  /** Close the pool */
493
500
  close(): void {
494
501
  if (this.closed) {
@@ -1,6 +1,6 @@
1
- export const MIN_SUPPORTED_SERVER_VERSION = '4.0';
1
+ export const MIN_SUPPORTED_SERVER_VERSION = '4.2';
2
2
  export const MAX_SUPPORTED_SERVER_VERSION = '8.0';
3
- export const MIN_SUPPORTED_WIRE_VERSION = 7;
3
+ export const MIN_SUPPORTED_WIRE_VERSION = 8;
4
4
  export const MAX_SUPPORTED_WIRE_VERSION = 25;
5
5
  export const MIN_SUPPORTED_QE_WIRE_VERSION = 21;
6
6
  export const MIN_SUPPORTED_QE_SERVER_VERSION = '7.0';
@@ -262,7 +262,7 @@ export class OnDemandDocument {
262
262
  public get<const T extends keyof JSTypeOf>(
263
263
  name: string | number,
264
264
  as: T,
265
- required?: boolean | undefined
265
+ required?: boolean
266
266
  ): JSTypeOf[T] | null;
267
267
 
268
268
  /** `required` will make `get` throw if name does not exist or is null/undefined */
@@ -77,7 +77,7 @@ export class MongoDBResponse extends OnDemandDocument {
77
77
  public override get<const T extends keyof JSTypeOf>(
78
78
  name: string | number,
79
79
  as: T,
80
- required?: false | undefined
80
+ required?: false
81
81
  ): JSTypeOf[T] | null;
82
82
  public override get<const T extends keyof JSTypeOf>(
83
83
  name: string | number,
@@ -87,7 +87,7 @@ export class MongoDBResponse extends OnDemandDocument {
87
87
  public override get<const T extends keyof JSTypeOf>(
88
88
  name: string | number,
89
89
  as: T,
90
- required?: boolean | undefined
90
+ required?: boolean
91
91
  ): JSTypeOf[T] | null {
92
92
  try {
93
93
  return super.get(name, as, required);
package/src/collection.ts CHANGED
@@ -87,6 +87,7 @@ import {
87
87
  } from './operations/update';
88
88
  import { ReadConcern, type ReadConcernLike } from './read_concern';
89
89
  import { ReadPreference, type ReadPreferenceLike } from './read_preference';
90
+ import { type Sort } from './sort';
90
91
  import {
91
92
  DEFAULT_PK_FACTORY,
92
93
  MongoDBCollectionNamespace,
@@ -365,7 +366,7 @@ export class Collection<TSchema extends Document = Document> {
365
366
  async updateOne(
366
367
  filter: Filter<TSchema>,
367
368
  update: UpdateFilter<TSchema> | Document[],
368
- options?: UpdateOptions
369
+ options?: UpdateOptions & { sort?: Sort }
369
370
  ): Promise<UpdateResult<TSchema>> {
370
371
  return await executeOperation(
371
372
  this.client,
@@ -605,6 +605,7 @@ function setOption(
605
605
  if (values[0] == null) {
606
606
  break;
607
607
  }
608
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
608
609
  mongoOptions[name] = String(values[0]);
609
610
  break;
610
611
  case 'record':
@@ -1273,6 +1274,7 @@ export const OPTIONS = {
1273
1274
  requestCert: { type: 'any' },
1274
1275
  rejectUnauthorized: { type: 'any' },
1275
1276
  checkServerIdentity: { type: 'any' },
1277
+ keepAliveInitialDelay: { type: 'any' },
1276
1278
  ALPNProtocols: { type: 'any' },
1277
1279
  SNICallback: { type: 'any' },
1278
1280
  session: { type: 'any' },
package/src/constants.ts CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
2
1
  export const SYSTEM_NAMESPACE_COLLECTION = 'system.namespaces';
3
2
  export const SYSTEM_INDEX_COLLECTION = 'system.indexes';
4
3
  export const SYSTEM_PROFILE_COLLECTION = 'system.profile';
package/src/encrypter.ts CHANGED
@@ -1,11 +1,8 @@
1
- import { callbackify } from 'util';
2
-
3
1
  import { AutoEncrypter, type AutoEncryptionOptions } from './client-side-encryption/auto_encrypter';
4
2
  import { MONGO_CLIENT_EVENTS } from './constants';
5
3
  import { getMongoDBClientEncryption } from './deps';
6
4
  import { MongoInvalidArgumentError, MongoMissingDependencyError } from './error';
7
5
  import { MongoClient, type MongoClientOptions } from './mongo_client';
8
- import { type Callback } from './utils';
9
6
 
10
7
  /** @internal */
11
8
  export interface EncrypterOptions {
@@ -98,20 +95,16 @@ export class Encrypter {
98
95
  }
99
96
  }
100
97
 
101
- closeCallback(client: MongoClient, force: boolean, callback: Callback<void>) {
102
- callbackify(this.close.bind(this))(client, force, callback);
103
- }
104
-
105
- async close(client: MongoClient, force: boolean): Promise<void> {
98
+ async close(client: MongoClient): Promise<void> {
106
99
  let error;
107
100
  try {
108
- await this.autoEncrypter.teardown(force);
101
+ await this.autoEncrypter.close();
109
102
  } catch (autoEncrypterError) {
110
103
  error = autoEncrypterError;
111
104
  }
112
105
  const internalClient = this.internalClient;
113
106
  if (internalClient != null && client !== internalClient) {
114
- return await internalClient.close(force);
107
+ return await internalClient.close();
115
108
  }
116
109
  if (error != null) {
117
110
  throw error;
package/src/error.ts CHANGED
@@ -1018,6 +1018,34 @@ export class MongoTopologyClosedError extends MongoAPIError {
1018
1018
  }
1019
1019
  }
1020
1020
 
1021
+ /**
1022
+ * An error generated when the MongoClient is closed and async
1023
+ * operations are interrupted.
1024
+ *
1025
+ * @public
1026
+ * @category Error
1027
+ */
1028
+ export class MongoClientClosedError extends MongoAPIError {
1029
+ /**
1030
+ * **Do not use this constructor!**
1031
+ *
1032
+ * Meant for internal use only.
1033
+ *
1034
+ * @remarks
1035
+ * This class is only meant to be constructed within the driver. This constructor is
1036
+ * not subject to semantic versioning compatibility guarantees and may change at any time.
1037
+ *
1038
+ * @public
1039
+ **/
1040
+ constructor() {
1041
+ super('Operation interrupted because client was closed');
1042
+ }
1043
+
1044
+ override get name(): string {
1045
+ return 'MongoClientClosedError';
1046
+ }
1047
+ }
1048
+
1021
1049
  /** @public */
1022
1050
  export interface MongoNetworkErrorOptions {
1023
1051
  /** Indicates the timeout happened before a connection handshake completed */
package/src/index.ts CHANGED
@@ -53,6 +53,7 @@ export {
53
53
  MongoClientBulkWriteCursorError,
54
54
  MongoClientBulkWriteError,
55
55
  MongoClientBulkWriteExecutionError,
56
+ MongoClientClosedError,
56
57
  MongoCompatibilityError,
57
58
  MongoCursorExhaustedError,
58
59
  MongoCursorInUseError,
@@ -115,7 +115,12 @@ export type SupportedTLSSocketOptions = Pick<
115
115
 
116
116
  /** @public */
117
117
  export type SupportedSocketOptions = Pick<
118
- TcpNetConnectOpts & { autoSelectFamily?: boolean; autoSelectFamilyAttemptTimeout?: number },
118
+ TcpNetConnectOpts & {
119
+ autoSelectFamily?: boolean;
120
+ autoSelectFamilyAttemptTimeout?: number;
121
+ /** Node.JS socket option to set the time the first keepalive probe is sent on an idle socket. Defaults to 120000ms */
122
+ keepAliveInitialDelay?: number;
123
+ },
119
124
  (typeof LEGAL_TCP_SOCKET_OPTIONS)[number]
120
125
  >;
121
126
 
@@ -342,22 +347,35 @@ export type MongoClientEvents = Pick<TopologyEvents, (typeof MONGO_CLIENT_EVENTS
342
347
  };
343
348
 
344
349
  /**
345
- * The **MongoClient** class is a class that allows for making Connections to MongoDB.
346
350
  * @public
347
351
  *
352
+ * The **MongoClient** class is a class that allows for making Connections to MongoDB.
353
+ *
354
+ * **NOTE:** The programmatically provided options take precedence over the URI options.
355
+ *
348
356
  * @remarks
349
- * The programmatically provided options take precedence over the URI options.
357
+ *
358
+ * A MongoClient is the entry point to connecting to a MongoDB server.
359
+ *
360
+ * It handles a multitude of features on your application's behalf:
361
+ * - **Server Host Connection Configuration**: A MongoClient is responsible for reading TLS cert, ca, and crl files if provided.
362
+ * - **SRV Record Polling**: A "`mongodb+srv`" style connection string is used to have the MongoClient resolve DNS SRV records of all server hostnames which the driver periodically monitors for changes and adjusts its current view of hosts correspondingly.
363
+ * - **Server Monitoring**: The MongoClient automatically keeps monitoring the health of server nodes in your cluster to reach out to the correct and lowest latency one available.
364
+ * - **Connection Pooling**: To avoid paying the cost of rebuilding a connection to the server on every operation the MongoClient keeps idle connections preserved for reuse.
365
+ * - **Session Pooling**: The MongoClient creates logical sessions that enable retryable writes, causal consistency, and transactions. It handles pooling these sessions for reuse in subsequent operations.
366
+ * - **Cursor Operations**: A MongoClient's cursors use the health monitoring system to send the request for more documents to the same server the query began on.
367
+ * - **Mongocryptd process**: When using auto encryption, a MongoClient will launch a `mongocryptd` instance for handling encryption if the mongocrypt shared library isn't in use.
368
+ *
369
+ * There are many more features of a MongoClient that are not listed above.
370
+ *
371
+ * In order to enable these features, a number of asynchronous Node.js resources are established by the driver: Timers, FS Requests, Sockets, etc.
372
+ * For details on cleanup, please refer to the MongoClient `close()` documentation.
350
373
  *
351
374
  * @example
352
375
  * ```ts
353
376
  * import { MongoClient } from 'mongodb';
354
- *
355
377
  * // Enable command monitoring for debugging
356
- * const client = new MongoClient('mongodb://localhost:27017', { monitorCommands: true });
357
- *
358
- * client.on('commandStarted', started => console.log(started));
359
- * client.db().collection('pets');
360
- * await client.insertOne({ name: 'spot', kind: 'dog' });
378
+ * const client = new MongoClient('mongodb://localhost:27017?appName=mflix', { monitorCommands: true });
361
379
  * ```
362
380
  */
363
381
  export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements AsyncDisposable {
@@ -636,25 +654,57 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
636
654
  }
637
655
 
638
656
  /**
639
- * Cleans up client-side resources used by the MongoCLient and . This includes:
657
+ * Cleans up resources managed by the MongoClient.
658
+ *
659
+ * The close method clears and closes all resources whose lifetimes are managed by the MongoClient.
660
+ * Please refer to the `MongoClient` class documentation for a high level overview of the client's key features and responsibilities.
661
+ *
662
+ * **However,** the close method does not handle the cleanup of resources explicitly created by the user.
663
+ * Any user-created driver resource with its own `close()` method should be explicitly closed by the user before calling MongoClient.close().
664
+ * This method is written as a "best effort" attempt to leave behind the least amount of resources server-side when possible.
640
665
  *
641
- * - Closes all open, unused connections (see note).
642
- * - Ends all in-use sessions with {@link ClientSession#endSession|ClientSession.endSession()}.
643
- * - Ends all unused sessions server-side.
644
- * - Cleans up any resources being used for auto encryption if auto encryption is enabled.
666
+ * The following list defines ideal preconditions and consequent pitfalls if they are not met.
667
+ * The MongoClient, ClientSession, Cursors and ChangeStreams all support [explicit resource management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html).
668
+ * By using explicit resource management to manage the lifetime of driver resources instead of manually managing their lifetimes, the pitfalls outlined below can be avoided.
645
669
  *
646
- * @remarks Any in-progress operations are not killed and any connections used by in progress operations
647
- * will be cleaned up lazily as operations finish.
670
+ * The close method performs the following in the order listed:
671
+ * - Client-side:
672
+ * - **Close in-use connections**: Any connections that are currently waiting on a response from the server will be closed.
673
+ * This is performed _first_ to avoid reaching the next step (server-side clean up) and having no available connections to check out.
674
+ * - _Ideal_: All operations have been awaited or cancelled, and the outcomes, regardless of success or failure, have been processed before closing the client servicing the operation.
675
+ * - _Pitfall_: When `client.close()` is called and all connections are in use, after closing them, the client must create new connections for cleanup operations, which comes at the cost of new TLS/TCP handshakes and authentication steps.
676
+ * - Server-side:
677
+ * - **Close active cursors**: All cursors that haven't been completed will have a `killCursor` operation sent to the server they were initialized on, freeing the server-side resource.
678
+ * - _Ideal_: Cursors are explicitly closed or completed before `client.close()` is called.
679
+ * - _Pitfall_: `killCursors` may have to build a new connection if the in-use closure ended all pooled connections.
680
+ * - **End active sessions**: In-use sessions created with `client.startSession()` or `client.withSession()` or implicitly by the driver will have their `.endSession()` method called.
681
+ * Contrary to the name of the method, `endSession()` returns the session to the client's pool of sessions rather than end them on the server.
682
+ * - _Ideal_: Transaction outcomes are awaited and their corresponding explicit sessions are ended before `client.close()` is called.
683
+ * - _Pitfall_: **This step aborts in-progress transactions**. It is advisable to observe the outcome of a transaction before closing your client.
684
+ * - **End all pooled sessions**: The `endSessions` command with all session IDs the client has pooled is sent to the server to inform the cluster it can clean them up.
685
+ * - _Ideal_: No user intervention is expected.
686
+ * - _Pitfall_: None.
648
687
  *
649
- * @param force - Force close, emitting no events
688
+ * The remaining shutdown is of the MongoClient resources that are intended to be entirely internal but is documented here as their existence relates to the JS event loop.
689
+ *
690
+ * - Client-side (again):
691
+ * - **Stop all server monitoring**: Connections kept live for detecting cluster changes and roundtrip time measurements are shutdown.
692
+ * - **Close all pooled connections**: Each server node in the cluster has a corresponding connection pool and all connections in the pool are closed. Any operations waiting to check out a connection will have an error thrown instead of a connection returned.
693
+ * - **Clear out server selection queue**: Any operations that are in the process of waiting for a server to be selected will have an error thrown instead of a server returned.
694
+ * - **Close encryption-related resources**: An internal MongoClient created for communicating with `mongocryptd` or other encryption purposes is closed. (Using this same method of course!)
695
+ *
696
+ * After the close method completes there should be no MongoClient related resources [ref-ed in Node.js' event loop](https://docs.libuv.org/en/v1.x/handle.html#reference-counting).
697
+ * This should allow Node.js to exit gracefully if MongoClient resources were the only active handles in the event loop.
698
+ *
699
+ * @param _force - currently an unused flag that has no effect. Defaults to `false`.
650
700
  */
651
- async close(force = false): Promise<void> {
701
+ async close(_force = false): Promise<void> {
652
702
  if (this.closeLock) {
653
703
  return await this.closeLock;
654
704
  }
655
705
 
656
706
  try {
657
- this.closeLock = this._close(force);
707
+ this.closeLock = this._close();
658
708
  await this.closeLock;
659
709
  } finally {
660
710
  // release
@@ -663,7 +713,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
663
713
  }
664
714
 
665
715
  /* @internal */
666
- private async _close(force = false): Promise<void> {
716
+ private async _close(): Promise<void> {
667
717
  // There's no way to set hasBeenClosed back to false
668
718
  Object.defineProperty(this.s, 'hasBeenClosed', {
669
719
  value: true,
@@ -672,6 +722,8 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
672
722
  writable: false
673
723
  });
674
724
 
725
+ this.topology?.closeCheckedOutConnections();
726
+
675
727
  const activeCursorCloses = Array.from(this.s.activeCursors, cursor => cursor.close());
676
728
  this.s.activeCursors.clear();
677
729
 
@@ -717,7 +769,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
717
769
 
718
770
  const { encrypter } = this.options;
719
771
  if (encrypter) {
720
- await encrypter.close(this, force);
772
+ await encrypter.close(this);
721
773
  }
722
774
  }
723
775
 
@@ -12,7 +12,6 @@ import { type CollationOptions, CommandOperation, type CommandOperationOptions }
12
12
  import { Aspect, defineAspects, type Hint } from './operation';
13
13
 
14
14
  /** @internal */
15
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
16
15
  export const DB_AGGREGATE_COLLECTION = 1 as const;
17
16
  const MIN_WIRE_VERSION_$OUT_READ_CONCERN_SUPPORT = 8;
18
17