mongodb 6.20.0 → 7.0.0-dev.20251107.sha.5db818c2

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 (151) hide show
  1. package/README.md +17 -18
  2. package/lib/bulk/common.js +7 -9
  3. package/lib/bulk/common.js.map +1 -1
  4. package/lib/change_stream.js +84 -38
  5. package/lib/change_stream.js.map +1 -1
  6. package/lib/client-side-encryption/auto_encrypter.js +2 -4
  7. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  8. package/lib/client-side-encryption/client_encryption.js +2 -3
  9. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  10. package/lib/client-side-encryption/errors.js +3 -1
  11. package/lib/client-side-encryption/errors.js.map +1 -1
  12. package/lib/client-side-encryption/mongocryptd_manager.js +1 -1
  13. package/lib/client-side-encryption/mongocryptd_manager.js.map +1 -1
  14. package/lib/cmap/auth/aws_temporary_credentials.js +10 -58
  15. package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -1
  16. package/lib/cmap/auth/mongo_credentials.js +0 -15
  17. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  18. package/lib/cmap/auth/mongodb_aws.js +2 -7
  19. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  20. package/lib/cmap/auth/providers.js +0 -1
  21. package/lib/cmap/auth/providers.js.map +1 -1
  22. package/lib/cmap/connect.js +1 -1
  23. package/lib/cmap/connect.js.map +1 -1
  24. package/lib/cmap/connection.js +28 -27
  25. package/lib/cmap/connection.js.map +1 -1
  26. package/lib/cmap/connection_pool.js +59 -59
  27. package/lib/cmap/connection_pool.js.map +1 -1
  28. package/lib/cmap/errors.js +1 -1
  29. package/lib/cmap/errors.js.map +1 -1
  30. package/lib/cmap/handshake/client_metadata.js +7 -5
  31. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  32. package/lib/cmap/metrics.js +3 -3
  33. package/lib/cmap/metrics.js.map +1 -1
  34. package/lib/cmap/wire_protocol/constants.js +3 -1
  35. package/lib/cmap/wire_protocol/constants.js.map +1 -1
  36. package/lib/cmap/wire_protocol/on_data.js +0 -1
  37. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  38. package/lib/cmap/wire_protocol/responses.js +2 -2
  39. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  40. package/lib/collection.js +1 -1
  41. package/lib/collection.js.map +1 -1
  42. package/lib/connection_string.js +8 -10
  43. package/lib/connection_string.js.map +1 -1
  44. package/lib/cursor/abstract_cursor.js +17 -34
  45. package/lib/cursor/abstract_cursor.js.map +1 -1
  46. package/lib/cursor/change_stream_cursor.js +2 -2
  47. package/lib/cursor/change_stream_cursor.js.map +1 -1
  48. package/lib/cursor/find_cursor.js +37 -26
  49. package/lib/cursor/find_cursor.js.map +1 -1
  50. package/lib/cursor/run_command_cursor.js +1 -1
  51. package/lib/cursor/run_command_cursor.js.map +1 -1
  52. package/lib/db.js +6 -6
  53. package/lib/db.js.map +1 -1
  54. package/lib/error.js +2 -2
  55. package/lib/error.js.map +1 -1
  56. package/lib/gridfs/download.js +5 -5
  57. package/lib/gridfs/download.js.map +1 -1
  58. package/lib/gridfs/index.js +9 -9
  59. package/lib/gridfs/index.js.map +1 -1
  60. package/lib/gridfs/upload.js +2 -8
  61. package/lib/gridfs/upload.js.map +1 -1
  62. package/lib/index.js +2 -4
  63. package/lib/index.js.map +1 -1
  64. package/lib/mongo_client.js +58 -67
  65. package/lib/mongo_client.js.map +1 -1
  66. package/lib/mongo_client_auth_providers.js +0 -6
  67. package/lib/mongo_client_auth_providers.js.map +1 -1
  68. package/lib/mongo_logger.js.map +1 -1
  69. package/lib/mongo_types.js +1 -2
  70. package/lib/mongo_types.js.map +1 -1
  71. package/lib/operations/aggregate.js +0 -3
  72. package/lib/operations/aggregate.js.map +1 -1
  73. package/lib/operations/command.js.map +1 -1
  74. package/lib/operations/create_collection.js +0 -1
  75. package/lib/operations/create_collection.js.map +1 -1
  76. package/lib/operations/drop.js +8 -9
  77. package/lib/operations/drop.js.map +1 -1
  78. package/lib/operations/end_sessions.js +34 -0
  79. package/lib/operations/end_sessions.js.map +1 -0
  80. package/lib/operations/execute_operation.js +3 -1
  81. package/lib/operations/execute_operation.js.map +1 -1
  82. package/lib/operations/find.js.map +1 -1
  83. package/lib/read_preference.js +10 -14
  84. package/lib/read_preference.js.map +1 -1
  85. package/lib/sdam/server.js +14 -14
  86. package/lib/sdam/server.js.map +1 -1
  87. package/lib/sdam/srv_polling.js +2 -2
  88. package/lib/sdam/srv_polling.js.map +1 -1
  89. package/lib/sdam/topology.js +24 -68
  90. package/lib/sdam/topology.js.map +1 -1
  91. package/lib/sessions.js +5 -4
  92. package/lib/sessions.js.map +1 -1
  93. package/lib/transactions.js +2 -13
  94. package/lib/transactions.js.map +1 -1
  95. package/lib/utils.js +0 -14
  96. package/lib/utils.js.map +1 -1
  97. package/mongodb.d.ts +72 -239
  98. package/package.json +17 -20
  99. package/src/bulk/common.ts +9 -11
  100. package/src/change_stream.ts +85 -37
  101. package/src/client-side-encryption/auto_encrypter.ts +6 -12
  102. package/src/client-side-encryption/client_encryption.ts +5 -6
  103. package/src/client-side-encryption/errors.ts +3 -0
  104. package/src/cmap/auth/aws_temporary_credentials.ts +12 -70
  105. package/src/cmap/auth/mongo_credentials.ts +1 -20
  106. package/src/cmap/auth/mongodb_aws.ts +8 -17
  107. package/src/cmap/auth/providers.ts +0 -1
  108. package/src/cmap/connect.ts +1 -1
  109. package/src/cmap/connection.ts +14 -16
  110. package/src/cmap/connection_pool.ts +4 -13
  111. package/src/cmap/errors.ts +1 -1
  112. package/src/cmap/handshake/client_metadata.ts +18 -26
  113. package/src/cmap/wire_protocol/constants.ts +2 -0
  114. package/src/cmap/wire_protocol/on_data.ts +1 -2
  115. package/src/collection.ts +1 -1
  116. package/src/connection_string.ts +13 -20
  117. package/src/cursor/abstract_cursor.ts +12 -49
  118. package/src/cursor/change_stream_cursor.ts +2 -2
  119. package/src/cursor/find_cursor.ts +40 -27
  120. package/src/cursor/run_command_cursor.ts +1 -1
  121. package/src/error.ts +2 -2
  122. package/src/gridfs/download.ts +0 -4
  123. package/src/gridfs/upload.ts +0 -22
  124. package/src/index.ts +2 -8
  125. package/src/mongo_client.ts +68 -100
  126. package/src/mongo_client_auth_providers.ts +0 -8
  127. package/src/mongo_logger.ts +1 -1
  128. package/src/mongo_types.ts +1 -2
  129. package/src/operations/aggregate.ts +0 -6
  130. package/src/operations/command.ts +0 -12
  131. package/src/operations/create_collection.ts +0 -3
  132. package/src/operations/drop.ts +9 -11
  133. package/src/operations/end_sessions.ts +44 -0
  134. package/src/operations/execute_operation.ts +6 -2
  135. package/src/operations/find.ts +2 -11
  136. package/src/read_preference.ts +0 -9
  137. package/src/sdam/topology.ts +2 -60
  138. package/src/sessions.ts +2 -8
  139. package/src/transactions.ts +2 -17
  140. package/src/utils.ts +0 -18
  141. package/tsconfig.json +5 -7
  142. package/lib/beta.d.ts +0 -9224
  143. package/lib/beta.js +0 -21
  144. package/lib/beta.js.map +0 -1
  145. package/lib/client-side-encryption/crypto_callbacks.js +0 -81
  146. package/lib/client-side-encryption/crypto_callbacks.js.map +0 -1
  147. package/lib/resource_management.js +0 -58
  148. package/lib/resource_management.js.map +0 -1
  149. package/src/beta.ts +0 -22
  150. package/src/client-side-encryption/crypto_callbacks.ts +0 -87
  151. package/src/resource_management.ts +0 -74
@@ -11,9 +11,7 @@ import { type AuthContext, AuthProvider } from './auth_provider';
11
11
  import {
12
12
  type AWSCredentialProvider,
13
13
  AWSSDKCredentialProvider,
14
- type AWSTempCredentials,
15
- AWSTemporaryCredentialProvider,
16
- LegacyAWSTemporaryCredentialProvider
14
+ type AWSTempCredentials
17
15
  } from './aws_temporary_credentials';
18
16
  import { MongoCredentials } from './mongo_credentials';
19
17
  import { AuthMechanism } from './providers';
@@ -34,16 +32,11 @@ interface AWSSaslContinuePayload {
34
32
  }
35
33
 
36
34
  export class MongoDBAWS extends AuthProvider {
37
- private credentialFetcher: AWSTemporaryCredentialProvider;
38
- private credentialProvider?: AWSCredentialProvider;
35
+ private credentialFetcher: AWSSDKCredentialProvider;
39
36
 
40
37
  constructor(credentialProvider?: AWSCredentialProvider) {
41
38
  super();
42
-
43
- this.credentialProvider = credentialProvider;
44
- this.credentialFetcher = AWSTemporaryCredentialProvider.isAWSSDKInstalled
45
- ? new AWSSDKCredentialProvider(credentialProvider)
46
- : new LegacyAWSTemporaryCredentialProvider();
39
+ this.credentialFetcher = new AWSSDKCredentialProvider(credentialProvider);
47
40
  }
48
41
 
49
42
  override async auth(authContext: AuthContext): Promise<void> {
@@ -63,12 +56,10 @@ export class MongoDBAWS extends AuthProvider {
63
56
  );
64
57
  }
65
58
 
66
- if (!authContext.credentials.username) {
67
- authContext.credentials = await makeTempCredentials(
68
- authContext.credentials,
69
- this.credentialFetcher
70
- );
71
- }
59
+ authContext.credentials = await makeTempCredentials(
60
+ authContext.credentials,
61
+ this.credentialFetcher
62
+ );
72
63
 
73
64
  const { credentials } = authContext;
74
65
 
@@ -162,7 +153,7 @@ export class MongoDBAWS extends AuthProvider {
162
153
 
163
154
  async function makeTempCredentials(
164
155
  credentials: MongoCredentials,
165
- awsCredentialFetcher: AWSTemporaryCredentialProvider
156
+ awsCredentialFetcher: AWSSDKCredentialProvider
166
157
  ): Promise<MongoCredentials> {
167
158
  function makeMongoCredentialsFromAWSTemp(creds: AWSTempCredentials) {
168
159
  // The AWS session token (creds.Token) may or may not be set.
@@ -1,7 +1,6 @@
1
1
  /** @public */
2
2
  export const AuthMechanism = Object.freeze({
3
3
  MONGODB_AWS: 'MONGODB-AWS',
4
- MONGODB_CR: 'MONGODB-CR',
5
4
  MONGODB_DEFAULT: 'DEFAULT',
6
5
  MONGODB_GSSAPI: 'GSSAPI',
7
6
  MONGODB_PLAIN: 'PLAIN',
@@ -222,7 +222,7 @@ export async function prepareHandshakeDocument(
222
222
  const options = authContext.options;
223
223
  const compressors = options.compressors ? options.compressors : [];
224
224
  const { serverApi } = authContext.connection;
225
- const clientMetadata: Document = await options.extendedMetadata;
225
+ const clientMetadata: Document = await options.metadata;
226
226
 
227
227
  const handshakeDoc: HandshakeDocument = {
228
228
  [serverApi?.version || options.loadBalanced === true ? 'hello' : LEGACY_HELLO_COMMAND]: 1,
@@ -22,7 +22,6 @@ import {
22
22
  import {
23
23
  MongoCompatibilityError,
24
24
  MONGODB_ERROR_CODES,
25
- MongoMissingDependencyError,
26
25
  MongoNetworkError,
27
26
  MongoNetworkTimeoutError,
28
27
  MongoOperationTimeoutError,
@@ -91,7 +90,6 @@ export interface CommandOptions extends BSONSerializeOptions {
91
90
  /** Session to use for the operation */
92
91
  session?: ClientSession;
93
92
  documentsReturnedIn?: string;
94
- noResponse?: boolean;
95
93
  omitMaxTimeMS?: boolean;
96
94
 
97
95
  // TODO(NODE-2802): Currently the CommandOptions take a property willRetryWrite which is a hint
@@ -139,10 +137,10 @@ export interface ConnectionOptions
139
137
  tls: boolean;
140
138
  noDelay?: boolean;
141
139
  socketTimeoutMS?: number;
140
+ /** @internal */
142
141
  cancellationToken?: CancellationToken;
143
- metadata: ClientMetadata;
144
142
  /** @internal */
145
- extendedMetadata: Promise<Document>;
143
+ metadata: Promise<ClientMetadata>;
146
144
  /** @internal */
147
145
  mongoLogger?: MongoLogger | undefined;
148
146
  }
@@ -468,7 +466,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
468
466
  signal: options.signal
469
467
  });
470
468
 
471
- if (options.noResponse || message.moreToCome) {
469
+ if (message.moreToCome) {
472
470
  yield MongoDBResponse.empty;
473
471
  return;
474
472
  }
@@ -568,11 +566,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
568
566
  new CommandSucceededEvent(
569
567
  this,
570
568
  message,
571
- options.noResponse
572
- ? undefined
573
- : message.moreToCome
574
- ? { ok: 1 }
575
- : (object ??= document.toObject(bsonOptions)),
569
+ message.moreToCome ? { ok: 1 } : (object ??= document.toObject(bsonOptions)),
576
570
  started,
577
571
  this.description.serverConnectionId
578
572
  )
@@ -715,7 +709,15 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
715
709
  }
716
710
  }
717
711
 
718
- if (this.socket.write(buffer)) return;
712
+ try {
713
+ if (this.socket.write(buffer)) return;
714
+ } catch (writeError) {
715
+ const networkError = new MongoNetworkError('unexpected error writing to socket', {
716
+ cause: writeError
717
+ });
718
+ this.onError(networkError);
719
+ throw networkError;
720
+ }
719
721
 
720
722
  const drainEvent = once<void>(this.socket, 'drain', options);
721
723
  const timeout = options?.timeoutContext?.timeoutForSocketWrite;
@@ -868,11 +870,7 @@ export class CryptoConnection extends Connection {
868
870
  ): Promise<Document> {
869
871
  const { autoEncrypter } = this;
870
872
  if (!autoEncrypter) {
871
- // TODO(NODE-6065): throw a MongoRuntimeError in Node V7
872
- // @ts-expect-error No cause provided because there is no underlying error.
873
- throw new MongoMissingDependencyError('No AutoEncrypter available for encryption', {
874
- dependencyName: 'n/a'
875
- });
873
+ throw new MongoRuntimeError('No AutoEncrypter available for encryption');
876
874
  }
877
875
 
878
876
  const serverWireVersion = maxWireVersion(this);
@@ -97,15 +97,6 @@ export const PoolState = Object.freeze({
97
97
 
98
98
  type PoolState = (typeof PoolState)[keyof typeof PoolState];
99
99
 
100
- /**
101
- * @public
102
- * @deprecated This interface is deprecated and will be removed in a future release as it is not used
103
- * in the driver
104
- */
105
- export interface CloseOptions {
106
- force?: boolean;
107
- }
108
-
109
100
  /** @public */
110
101
  export type ConnectionPoolEvents = {
111
102
  connectionPoolCreated(event: ConnectionPoolCreatedEvent): void;
@@ -610,9 +601,9 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
610
601
  }
611
602
 
612
603
  private createConnection(callback: Callback<Connection>) {
613
- // Note that metadata and extendedMetadata may have changed on the client but have
614
- // been frozen here, so we pull the extendedMetadata promise always from the client
615
- // no mattter what options were set at the construction of the pool.
604
+ // Note that metadata may have changed on the client but have
605
+ // been frozen here, so we pull the metadata promise always from the client
606
+ // no matter what options were set at the construction of the pool.
616
607
  const connectOptions: ConnectionOptions = {
617
608
  ...this.options,
618
609
  id: this.connectionCounter.next().value,
@@ -620,7 +611,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
620
611
  cancellationToken: this.cancellationToken,
621
612
  mongoLogger: this.mongoLogger,
622
613
  authProviders: this.server.topology.client.s.authProviders,
623
- extendedMetadata: this.server.topology.client.options.extendedMetadata
614
+ metadata: this.server.topology.client.options.metadata
624
615
  };
625
616
 
626
617
  this.pending++;
@@ -56,7 +56,7 @@ export class PoolClearedError extends MongoNetworkError {
56
56
  super(errorMessage, pool.serverError ? { cause: pool.serverError } : undefined);
57
57
  this.address = pool.address;
58
58
 
59
- this.addErrorLabel(MongoErrorLabel.PoolRequstedRetry);
59
+ this.addErrorLabel(MongoErrorLabel.PoolRequestedRetry);
60
60
  }
61
61
 
62
62
  override get name(): string {
@@ -27,8 +27,7 @@ export function isDriverInfoEqual(info1: DriverInfo, info2: DriverInfo): boolean
27
27
  }
28
28
 
29
29
  /**
30
- * @public
31
- * @deprecated This interface will be made internal in the next major release.
30
+ * @internal
32
31
  * @see https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.md#hello-command
33
32
  */
34
33
  export interface ClientMetadata {
@@ -48,27 +47,17 @@ export interface ClientMetadata {
48
47
  };
49
48
  /** FaaS environment information */
50
49
  env?: {
51
- name: 'aws.lambda' | 'gcp.func' | 'azure.func' | 'vercel';
50
+ name?: 'aws.lambda' | 'gcp.func' | 'azure.func' | 'vercel';
52
51
  timeout_sec?: Int32;
53
52
  memory_mb?: Int32;
54
53
  region?: string;
55
- url?: string;
54
+ container?: {
55
+ runtime?: string;
56
+ orchestrator?: string;
57
+ };
56
58
  };
57
59
  }
58
60
 
59
- /**
60
- * @public
61
- * @deprecated This interface will be made internal in the next major release.
62
- */
63
- export interface ClientMetadataOptions {
64
- driverInfo?: {
65
- name?: string;
66
- version?: string;
67
- platform?: string;
68
- };
69
- appName?: string;
70
- }
71
-
72
61
  /** @internal */
73
62
  export class LimitedSizeDocument {
74
63
  private document = new Map();
@@ -116,10 +105,10 @@ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName'>;
116
105
  * 3. Omit the `env` document entirely.
117
106
  * 4. Truncate `platform`. -- special we do not truncate this field
118
107
  */
119
- export function makeClientMetadata(
108
+ export async function makeClientMetadata(
120
109
  driverInfoList: DriverInfo[],
121
110
  { appName = '' }: MakeClientMetadataOptions
122
- ): ClientMetadata {
111
+ ): Promise<ClientMetadata> {
123
112
  const metadataDocument = new LimitedSizeDocument(512);
124
113
 
125
114
  // Add app name first, it must be sent
@@ -191,19 +180,21 @@ export function makeClientMetadata(
191
180
  }
192
181
  }
193
182
  }
194
- return metadataDocument.toObject() as ClientMetadata;
183
+ return await addContainerMetadata(metadataDocument.toObject() as ClientMetadata);
195
184
  }
196
185
 
197
186
  let dockerPromise: Promise<boolean>;
187
+ type ContainerMetadata = NonNullable<NonNullable<ClientMetadata['env']>['container']>;
198
188
  /** @internal */
199
- async function getContainerMetadata() {
200
- const containerMetadata: Record<string, any> = {};
189
+ async function getContainerMetadata(): Promise<ContainerMetadata> {
201
190
  dockerPromise ??= fileIsAccessible('/.dockerenv');
202
191
  const isDocker = await dockerPromise;
203
192
 
204
193
  const { KUBERNETES_SERVICE_HOST = '' } = process.env;
205
194
  const isKubernetes = KUBERNETES_SERVICE_HOST.length > 0 ? true : false;
206
195
 
196
+ const containerMetadata: ContainerMetadata = {};
197
+
207
198
  if (isDocker) containerMetadata.runtime = 'docker';
208
199
  if (isKubernetes) containerMetadata.orchestrator = 'kubernetes';
209
200
 
@@ -215,15 +206,16 @@ async function getContainerMetadata() {
215
206
  * Re-add each metadata value.
216
207
  * Attempt to add new env container metadata, but keep old data if it does not fit.
217
208
  */
218
- export async function addContainerMetadata(
219
- originalMetadata: ClientMetadata
220
- ): Promise<ClientMetadata> {
209
+ async function addContainerMetadata(originalMetadata: ClientMetadata): Promise<ClientMetadata> {
221
210
  const containerMetadata = await getContainerMetadata();
222
211
  if (Object.keys(containerMetadata).length === 0) return originalMetadata;
223
212
 
224
213
  const extendedMetadata = new LimitedSizeDocument(512);
225
214
 
226
- const extendedEnvMetadata = { ...originalMetadata?.env, container: containerMetadata };
215
+ const extendedEnvMetadata: NonNullable<ClientMetadata['env']> = {
216
+ ...originalMetadata?.env,
217
+ container: containerMetadata
218
+ };
227
219
 
228
220
  for (const [key, val] of Object.entries(originalMetadata)) {
229
221
  if (key !== 'env') {
@@ -1,5 +1,7 @@
1
1
  export const MIN_SUPPORTED_SERVER_VERSION = '4.2';
2
2
  export const MAX_SUPPORTED_SERVER_VERSION = '8.2';
3
+ export const MIN_SUPPORTED_SNAPSHOT_READS_WIRE_VERSION = 13;
4
+ export const MIN_SUPPORTED_SNAPSHOT_READS_SERVER_VERSION = '5.0';
3
5
  export const MIN_SUPPORTED_WIRE_VERSION = 8;
4
6
  export const MAX_SUPPORTED_WIRE_VERSION = 27;
5
7
  export const MIN_SUPPORTED_QE_WIRE_VERSION = 21;
@@ -49,7 +49,7 @@ export function onData(
49
49
  /** Set to true only after event listeners have been removed. */
50
50
  let finished = false;
51
51
 
52
- const iterator: AsyncGenerator<Buffer> = {
52
+ const iterator: AsyncGenerator<Buffer> & AsyncDisposable = {
53
53
  next() {
54
54
  // First, we consume all unread events
55
55
  const value = unconsumedEvents.shift();
@@ -89,7 +89,6 @@ export function onData(
89
89
  return this;
90
90
  },
91
91
 
92
- // Note this should currently not be used, but is required by the AsyncGenerator interface.
93
92
  async [Symbol.asyncDispose]() {
94
93
  await closeHandler();
95
94
  }
package/src/collection.ts CHANGED
@@ -546,7 +546,7 @@ export class Collection<TSchema extends Document = Document> {
546
546
  // Explicitly set the limit to 1 and singleBatch to true for all commands, per the spec.
547
547
  // noCursorTimeout must be unset as well as batchSize.
548
548
  // See: https://github.com/mongodb/specifications/blob/master/source/crud/crud.md#findone-api-details
549
- const { batchSize: _batchSize, noCursorTimeout: _noCursorTimeout, ...opts } = options;
549
+ const { ...opts } = options;
550
550
  opts.singleBatch = true;
551
551
  const cursor = this.find(filter, opts).limit(1);
552
552
  const result = await cursor.next();
@@ -7,12 +7,7 @@ import { MongoCredentials } from './cmap/auth/mongo_credentials';
7
7
  import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './cmap/auth/providers';
8
8
  import { Compressor, type CompressorName } from './cmap/wire_protocol/compression';
9
9
  import { Encrypter } from './encrypter';
10
- import {
11
- MongoAPIError,
12
- MongoInvalidArgumentError,
13
- MongoMissingCredentialsError,
14
- MongoParseError
15
- } from './error';
10
+ import { MongoAPIError, MongoInvalidArgumentError, MongoParseError } from './error';
16
11
  import {
17
12
  MongoClient,
18
13
  type MongoClientOptions,
@@ -417,10 +412,18 @@ export function parseOptions(
417
412
  });
418
413
  }
419
414
 
420
- if (isAws && mongoOptions.credentials.username && !mongoOptions.credentials.password) {
421
- throw new MongoMissingCredentialsError(
422
- `When using ${mongoOptions.credentials.mechanism} password must be set when a username is specified`
423
- );
415
+ if (isAws) {
416
+ const { username, password } = mongoOptions.credentials;
417
+ if (username || password) {
418
+ throw new MongoAPIError(
419
+ 'username and password cannot be provided when using MONGODB-AWS. Credentials must be provided in a manner that can be read by the AWS SDK.'
420
+ );
421
+ }
422
+ if (mongoOptions.credentials.mechanismProperties.AWS_SESSION_TOKEN) {
423
+ throw new MongoAPIError(
424
+ 'AWS_SESSION_TOKEN cannot be provided when using MONGODB-AWS. Credentials must be provided in a manner that can be read by the AWS SDK.'
425
+ );
426
+ }
424
427
  }
425
428
 
426
429
  mongoOptions.credentials.validate();
@@ -1283,16 +1286,6 @@ export const OPTIONS = {
1283
1286
  secureProtocol: { type: 'any' },
1284
1287
  index: { type: 'any' },
1285
1288
  // Legacy options from v3 era
1286
- useNewUrlParser: {
1287
- type: 'boolean',
1288
- deprecated:
1289
- 'useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
1290
- } as OptionDescriptor,
1291
- useUnifiedTopology: {
1292
- type: 'boolean',
1293
- deprecated:
1294
- 'useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version'
1295
- } as OptionDescriptor,
1296
1289
  __skipPingOnConnect: { type: 'boolean' }
1297
1290
  } as Record<keyof MongoClientOptions, OptionDescriptor>;
1298
1291
 
@@ -1,4 +1,4 @@
1
- import { Readable, Transform } from 'stream';
1
+ import { Readable } from 'stream';
2
2
 
3
3
  import { type BSONSerializeOptions, type Document, Long, pluckBSONSerializeOptions } from '../bson';
4
4
  import { type OnDemandDocumentDeserializeOptions } from '../cmap/wire_protocol/on_demand/document';
@@ -18,7 +18,6 @@ import { GetMoreOperation } from '../operations/get_more';
18
18
  import { KillCursorsOperation } from '../operations/kill_cursors';
19
19
  import { ReadConcern, type ReadConcernLike } from '../read_concern';
20
20
  import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
21
- import { type AsyncDisposable, configureResourceManagement } from '../resource_management';
22
21
  import type { Server } from '../sdam/server';
23
22
  import { type ClientSession, maybeClearPinnedConnection } from '../sessions';
24
23
  import { type CSOTTimeoutContext, type Timeout, TimeoutContext } from '../timeout';
@@ -59,12 +58,6 @@ export const CURSOR_FLAGS = [
59
58
  'partial'
60
59
  ] as const;
61
60
 
62
- /** @public */
63
- export interface CursorStreamOptions {
64
- /** A transformation method applied to each document emitted by the stream */
65
- transform?(this: void, doc: Document): Document;
66
- }
67
-
68
61
  /** @public */
69
62
  export type CursorFlag = (typeof CURSOR_FLAGS)[number];
70
63
 
@@ -437,13 +430,10 @@ export abstract class AbstractCursor<
437
430
  }
438
431
 
439
432
  /**
440
- * @beta
441
433
  * @experimental
442
434
  * An alias for {@link AbstractCursor.close|AbstractCursor.close()}.
443
435
  */
444
- declare [Symbol.asyncDispose]: () => Promise<void>;
445
- /** @internal */
446
- async asyncDispose() {
436
+ async [Symbol.asyncDispose]() {
447
437
  await this.close();
448
438
  }
449
439
 
@@ -523,7 +513,7 @@ export abstract class AbstractCursor<
523
513
  }
524
514
  }
525
515
 
526
- stream(options?: CursorStreamOptions): Readable & AsyncIterable<TSchema> {
516
+ stream(): Readable & AsyncIterable<TSchema> {
527
517
  const readable = new ReadableCursorStream(this);
528
518
  const abortListener = addAbortListener(this.signal, function () {
529
519
  readable.destroy(this.reason);
@@ -532,31 +522,6 @@ export abstract class AbstractCursor<
532
522
  abortListener?.[kDispose]();
533
523
  });
534
524
 
535
- if (options?.transform) {
536
- const transform = options.transform;
537
-
538
- const transformedStream = readable.pipe(
539
- new Transform({
540
- objectMode: true,
541
- highWaterMark: 1,
542
- transform(chunk, _, callback) {
543
- try {
544
- const transformed = transform(chunk);
545
- callback(undefined, transformed);
546
- } catch (err) {
547
- callback(err);
548
- }
549
- }
550
- })
551
- );
552
-
553
- // Bubble errors to transformed stream, because otherwise no way
554
- // to handle this error.
555
- readable.on('error', err => transformedStream.emit('error', err));
556
-
557
- return transformedStream;
558
- }
559
-
560
525
  return readable;
561
526
  }
562
527
 
@@ -701,7 +666,11 @@ export abstract class AbstractCursor<
701
666
  array.push(await this.transformDocument(doc));
702
667
  }
703
668
  } else {
704
- array.push(...docs);
669
+ // Note: previous versions of this logic used `array.push(...)`, which adds each item
670
+ // to the callstack. For large arrays, this can exceed the maximum call size.
671
+ for (const doc of docs) {
672
+ array.push(doc);
673
+ }
705
674
  }
706
675
  }
707
676
  return array;
@@ -893,7 +862,7 @@ export abstract class AbstractCursor<
893
862
  ): Promise<InitialCursorResponse>;
894
863
 
895
864
  /** @internal */
896
- async getMore(batchSize: number): Promise<CursorResponse> {
865
+ async getMore(): Promise<CursorResponse> {
897
866
  if (this.cursorId == null) {
898
867
  throw new MongoRuntimeError(
899
868
  'Unexpected null cursor id. A cursor creating command should have set this'
@@ -910,11 +879,10 @@ export abstract class AbstractCursor<
910
879
  'Unexpected null session. A cursor creating command should have set this'
911
880
  );
912
881
  }
913
-
914
882
  const getMoreOptions = {
915
883
  ...this.cursorOptions,
916
884
  session: this.cursorSession,
917
- batchSize
885
+ batchSize: this.cursorOptions.batchSize
918
886
  };
919
887
 
920
888
  const getMoreOperation = new GetMoreOperation(
@@ -987,14 +955,11 @@ export abstract class AbstractCursor<
987
955
  await this.cursorInit();
988
956
  // If the cursor died or returned documents, return
989
957
  if ((this.documents?.length ?? 0) !== 0 || this.isDead) return;
990
- // Otherwise, run a getMore
991
958
  }
992
959
 
993
- // otherwise need to call getMore
994
- const batchSize = this.cursorOptions.batchSize || 1000;
995
-
960
+ // Otherwise, run a getMore
996
961
  try {
997
- const response = await this.getMore(batchSize);
962
+ const response = await this.getMore();
998
963
  this.cursorId = response.id;
999
964
  this.documents = response;
1000
965
  } catch (error) {
@@ -1223,8 +1188,6 @@ class ReadableCursorStream extends Readable {
1223
1188
  }
1224
1189
  }
1225
1190
 
1226
- configureResourceManagement(AbstractCursor.prototype);
1227
-
1228
1191
  /**
1229
1192
  * @internal
1230
1193
  * The cursor timeout context is a wrapper around a timeout context
@@ -158,8 +158,8 @@ export class ChangeStreamCursor<
158
158
  return { server, session, response };
159
159
  }
160
160
 
161
- override async getMore(batchSize: number): Promise<CursorResponse> {
162
- const response = await super.getMore(batchSize);
161
+ override async getMore(): Promise<CursorResponse> {
162
+ const response = await super.getMore();
163
163
 
164
164
  this.maxWireVersion = maxWireVersion(this.server);
165
165
  this._processBatch(response);
@@ -16,7 +16,7 @@ import { FindOperation, type FindOptions } from '../operations/find';
16
16
  import type { Hint } from '../operations/operation';
17
17
  import type { ClientSession } from '../sessions';
18
18
  import { formatSort, type Sort, type SortDirection } from '../sort';
19
- import { emitWarningOnce, mergeOptions, type MongoDBNamespace, squashError } from '../utils';
19
+ import { emitWarningOnce, mergeOptions, type MongoDBNamespace, noop, squashError } from '../utils';
20
20
  import { type InitialCursorResponse } from './abstract_cursor';
21
21
  import { ExplainableCursor } from './explainable_cursor';
22
22
 
@@ -98,37 +98,50 @@ export class FindCursor<TSchema = any> extends ExplainableCursor<TSchema> {
98
98
  }
99
99
 
100
100
  /** @internal */
101
- override async getMore(batchSize: number): Promise<CursorResponse> {
101
+ override async getMore(): Promise<CursorResponse> {
102
102
  const numReturned = this.numReturned;
103
- if (numReturned) {
104
- // TODO(DRIVERS-1448): Remove logic to enforce `limit` in the driver
105
- const limit = this.findOptions.limit;
106
- batchSize =
107
- limit && limit > 0 && numReturned + batchSize > limit ? limit - numReturned : batchSize;
108
-
109
- if (batchSize <= 0) {
110
- try {
111
- await this.close();
112
- } catch (error) {
113
- squashError(error);
114
- // this is an optimization for the special case of a limit for a find command to avoid an
115
- // extra getMore when the limit has been reached and the limit is a multiple of the batchSize.
116
- // This is a consequence of the new query engine in 5.0 having no knowledge of the limit as it
117
- // produces results for the find command. Once a batch is filled up, it is returned and only
118
- // on the subsequent getMore will the query framework consider the limit, determine the cursor
119
- // is exhausted and return a cursorId of zero.
120
- // instead, if we determine there are no more documents to request from the server, we preemptively
121
- // close the cursor
122
- }
123
- return CursorResponse.emptyGetMore;
103
+ const limit = this.findOptions.limit ?? Infinity;
104
+ const remaining = limit - numReturned;
105
+
106
+ if (numReturned === limit && !this.id?.isZero()) {
107
+ // this is an optimization for the special case of a limit for a find command to avoid an
108
+ // extra getMore when the limit has been reached and the limit is a multiple of the batchSize.
109
+ // This is a consequence of the new query engine in 5.0 having no knowledge of the limit as it
110
+ // produces results for the find command. Once a batch is filled up, it is returned and only
111
+ // on the subsequent getMore will the query framework consider the limit, determine the cursor
112
+ // is exhausted and return a cursorId of zero.
113
+ // instead, if we determine there are no more documents to request from the server, we preemptively
114
+ // close the cursor
115
+ try {
116
+ await this.close();
117
+ } catch (error) {
118
+ squashError(error);
124
119
  }
120
+ return CursorResponse.emptyGetMore;
121
+ }
122
+
123
+ // TODO(DRIVERS-1448): Remove logic to enforce `limit` in the driver
124
+ let cleanup: () => void = noop;
125
+ const { batchSize } = this.cursorOptions;
126
+ if (batchSize != null && batchSize > remaining) {
127
+ this.cursorOptions.batchSize = remaining;
128
+
129
+ // After executing the final getMore, re-assign the batchSize back to its original value so that
130
+ // if the cursor is rewound and executed, the batchSize is still correct.
131
+ cleanup = () => {
132
+ this.cursorOptions.batchSize = batchSize;
133
+ };
125
134
  }
126
135
 
127
- const response = await super.getMore(batchSize);
128
- // TODO: wrap this in some logic to prevent it from happening if we don't need this support
129
- this.numReturned = this.numReturned + response.batchSize;
136
+ try {
137
+ const response = await super.getMore();
130
138
 
131
- return response;
139
+ this.numReturned = this.numReturned + response.batchSize;
140
+
141
+ return response;
142
+ } finally {
143
+ cleanup?.();
144
+ }
132
145
  }
133
146
 
134
147
  /**
@@ -159,7 +159,7 @@ export class RunCommandCursor extends AbstractCursor {
159
159
  }
160
160
 
161
161
  /** @internal */
162
- override async getMore(_batchSize: number): Promise<CursorResponse> {
162
+ override async getMore(): Promise<CursorResponse> {
163
163
  if (!this.session) {
164
164
  throw new MongoRuntimeError(
165
165
  'Unexpected null session. A cursor creating command should have set this'
package/src/error.ts CHANGED
@@ -97,7 +97,7 @@ export const MongoErrorLabel = Object.freeze({
97
97
  ResumableChangeStreamError: 'ResumableChangeStreamError',
98
98
  HandshakeError: 'HandshakeError',
99
99
  ResetPool: 'ResetPool',
100
- PoolRequstedRetry: 'PoolRequstedRetry',
100
+ PoolRequestedRetry: 'PoolRequestedRetry',
101
101
  InterruptInUseConnections: 'InterruptInUseConnections',
102
102
  NoWritesPerformed: 'NoWritesPerformed'
103
103
  } as const);
@@ -1444,7 +1444,7 @@ export function needsRetryableWriteLabel(
1444
1444
  export function isRetryableWriteError(error: MongoError): boolean {
1445
1445
  return (
1446
1446
  error.hasErrorLabel(MongoErrorLabel.RetryableWriteError) ||
1447
- error.hasErrorLabel(MongoErrorLabel.PoolRequstedRetry)
1447
+ error.hasErrorLabel(MongoErrorLabel.PoolRequestedRetry)
1448
1448
  );
1449
1449
  }
1450
1450