mongodb 7.5.0 → 7.6.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 (107) hide show
  1. package/README.md +9 -1
  2. package/lib/bulk/common.js +27 -5
  3. package/lib/bulk/common.js.map +1 -1
  4. package/lib/bulk/ordered.js +25 -7
  5. package/lib/bulk/ordered.js.map +1 -1
  6. package/lib/bulk/unordered.js +25 -7
  7. package/lib/bulk/unordered.js.map +1 -1
  8. package/lib/client-side-encryption/auto_encrypter.js +8 -2
  9. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  10. package/lib/client-side-encryption/client_encryption.js +12 -4
  11. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  12. package/lib/client-side-encryption/kms_options.js +3 -0
  13. package/lib/client-side-encryption/kms_options.js.map +1 -0
  14. package/lib/client-side-encryption/state_machine.js +54 -7
  15. package/lib/client-side-encryption/state_machine.js.map +1 -1
  16. package/lib/cmap/auth/gssapi.js +2 -1
  17. package/lib/cmap/auth/gssapi.js.map +1 -1
  18. package/lib/cmap/auth/mongo_credentials.js +0 -1
  19. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  20. package/lib/cmap/auth/mongodb_aws.js +0 -3
  21. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  22. package/lib/cmap/command_monitoring_events.js +6 -7
  23. package/lib/cmap/command_monitoring_events.js.map +1 -1
  24. package/lib/cmap/commands.js +13 -0
  25. package/lib/cmap/commands.js.map +1 -1
  26. package/lib/cmap/connect.js +2 -2
  27. package/lib/cmap/connect.js.map +1 -1
  28. package/lib/cmap/connection.js +3 -1
  29. package/lib/cmap/connection.js.map +1 -1
  30. package/lib/cmap/handshake/client_metadata.js +2 -1
  31. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  32. package/lib/cmap/wire_protocol/constants.js +2 -2
  33. package/lib/connection_string.js +4 -0
  34. package/lib/connection_string.js.map +1 -1
  35. package/lib/cursor/abstract_cursor.js.map +1 -1
  36. package/lib/error.js +7 -11
  37. package/lib/error.js.map +1 -1
  38. package/lib/gridfs/download.js +1 -1
  39. package/lib/gridfs/download.js.map +1 -1
  40. package/lib/index.js.map +1 -1
  41. package/lib/operations/command.js.map +1 -1
  42. package/lib/operations/delete.js +7 -10
  43. package/lib/operations/delete.js.map +1 -1
  44. package/lib/operations/execute_operation.js +29 -2
  45. package/lib/operations/execute_operation.js.map +1 -1
  46. package/lib/operations/find_and_modify.js +1 -5
  47. package/lib/operations/find_and_modify.js.map +1 -1
  48. package/lib/operations/get_more.js +2 -3
  49. package/lib/operations/get_more.js.map +1 -1
  50. package/lib/operations/indexes.js +3 -9
  51. package/lib/operations/indexes.js.map +1 -1
  52. package/lib/operations/insert.js +6 -2
  53. package/lib/operations/insert.js.map +1 -1
  54. package/lib/operations/list_collections.js +2 -3
  55. package/lib/operations/list_collections.js.map +1 -1
  56. package/lib/operations/list_databases.js +2 -2
  57. package/lib/operations/list_databases.js.map +1 -1
  58. package/lib/operations/update.js +6 -2
  59. package/lib/operations/update.js.map +1 -1
  60. package/lib/runtime_adapters.js +15 -16
  61. package/lib/runtime_adapters.js.map +1 -1
  62. package/lib/sdam/server.js +1 -1
  63. package/lib/sdam/server.js.map +1 -1
  64. package/lib/sessions.js +2 -2
  65. package/lib/sessions.js.map +1 -1
  66. package/lib/utils.js +1 -3
  67. package/lib/utils.js.map +1 -1
  68. package/mongodb.d.ts +77 -20
  69. package/package.json +3 -2
  70. package/src/bulk/common.ts +37 -5
  71. package/src/bulk/ordered.ts +23 -7
  72. package/src/bulk/unordered.ts +23 -8
  73. package/src/client-side-encryption/auto_encrypter.ts +19 -3
  74. package/src/client-side-encryption/client_encryption.ts +29 -9
  75. package/src/client-side-encryption/kms_options.ts +93 -0
  76. package/src/client-side-encryption/state_machine.ts +68 -47
  77. package/src/cmap/auth/gssapi.ts +3 -4
  78. package/src/cmap/auth/mongo_credentials.ts +0 -1
  79. package/src/cmap/auth/mongodb_aws.ts +2 -12
  80. package/src/cmap/command_monitoring_events.ts +9 -10
  81. package/src/cmap/commands.ts +17 -0
  82. package/src/cmap/connect.ts +3 -3
  83. package/src/cmap/connection.ts +4 -2
  84. package/src/cmap/handshake/client_metadata.ts +2 -1
  85. package/src/cmap/wire_protocol/constants.ts +2 -2
  86. package/src/connection_string.ts +4 -0
  87. package/src/cursor/abstract_cursor.ts +0 -5
  88. package/src/error.ts +7 -19
  89. package/src/gridfs/download.ts +1 -1
  90. package/src/index.ts +7 -6
  91. package/src/mongo_client.ts +1 -1
  92. package/src/operations/command.ts +0 -5
  93. package/src/operations/delete.ts +17 -15
  94. package/src/operations/execute_operation.ts +29 -2
  95. package/src/operations/find_and_modify.ts +3 -15
  96. package/src/operations/get_more.ts +3 -5
  97. package/src/operations/indexes.ts +6 -16
  98. package/src/operations/insert.ts +13 -2
  99. package/src/operations/list_collections.ts +5 -6
  100. package/src/operations/list_databases.ts +3 -3
  101. package/src/operations/update.ts +9 -2
  102. package/src/operations/validate_collection.ts +1 -1
  103. package/src/runtime_adapters.ts +19 -18
  104. package/src/sdam/server.ts +1 -1
  105. package/src/sessions.ts +2 -5
  106. package/src/utils.ts +1 -3
  107. package/tsconfig.json +9 -2
@@ -146,7 +146,7 @@ export interface ConnectionOptions
146
146
  /** @internal */
147
147
  mongoLogger?: MongoLogger | undefined;
148
148
  /** @internal */
149
- runtime: Runtime;
149
+ runtime: Promise<Runtime>;
150
150
  }
151
151
 
152
152
  /** @public */
@@ -286,7 +286,9 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
286
286
  private get supportsOpMsg(): boolean {
287
287
  return (
288
288
  this.description != null &&
289
- // TODO(NODE-6672,NODE-6287): This guard is primarily for maxWireVersion = 0
289
+ // OP_MSG was introduced in server 3.6 (wire version 6). This also guards
290
+ // against the pre-handshake case where maxWireVersion() returns 0.
291
+ // TODO(NODE-6672,NODE-6287): remove this helper once OP_QUERY support is dropped
290
292
  maxWireVersion(this) >= 6 &&
291
293
  !this.description.__nodejs_mock_server__
292
294
  );
@@ -107,8 +107,9 @@ type MakeClientMetadataOptions = Pick<MongoOptions, 'appName' | 'runtime'>;
107
107
  */
108
108
  export async function makeClientMetadata(
109
109
  driverInfoList: DriverInfo[],
110
- { appName = '', runtime: { os } }: MakeClientMetadataOptions
110
+ { appName = '', runtime }: MakeClientMetadataOptions
111
111
  ): Promise<ClientMetadata> {
112
+ const { os } = await runtime;
112
113
  const metadataDocument = new LimitedSizeDocument(512);
113
114
 
114
115
  // Add app name first, it must be sent
@@ -1,8 +1,8 @@
1
- export const MIN_SUPPORTED_SERVER_VERSION = '4.2';
1
+ export const MIN_SUPPORTED_SERVER_VERSION = '4.4';
2
2
  export const MAX_SUPPORTED_SERVER_VERSION = '9.0';
3
3
  export const MIN_SUPPORTED_SNAPSHOT_READS_WIRE_VERSION = 13;
4
4
  export const MIN_SUPPORTED_SNAPSHOT_READS_SERVER_VERSION = '5.0';
5
- export const MIN_SUPPORTED_WIRE_VERSION = 8;
5
+ export const MIN_SUPPORTED_WIRE_VERSION = 9;
6
6
  export const MAX_SUPPORTED_WIRE_VERSION = 29;
7
7
  export const MIN_SUPPORTED_QE_WIRE_VERSION = 21;
8
8
  export const MIN_SUPPORTED_QE_SERVER_VERSION = '7.0';
@@ -544,6 +544,10 @@ export function parseOptions(
544
544
  );
545
545
 
546
546
  mongoOptions.runtime = resolveRuntimeAdapters(options);
547
+ // Mark the promise as handled so a rejection before any consumer awaits it (possible only in
548
+ // runtimes where the dynamic import of the default os adapter fails) cannot crash the process
549
+ // as an unhandled rejection; consumers awaiting `runtime` still observe the rejection.
550
+ mongoOptions.runtime.then(undefined, squashError);
547
551
 
548
552
  return mongoOptions;
549
553
  }
@@ -126,11 +126,6 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
126
126
  maxAwaitTimeMS?: number;
127
127
  /**
128
128
  * Comment to apply to the operation.
129
- *
130
- * In server versions pre-4.4, 'comment' must be string. A server
131
- * error will be thrown if any other type is provided.
132
- *
133
- * In server versions 4.4 and above, 'comment' can be any valid BSON type.
134
129
  */
135
130
  comment?: unknown;
136
131
  /**
package/src/error.ts CHANGED
@@ -3,7 +3,6 @@ import {
3
3
  type ClientBulkWriteError,
4
4
  type ClientBulkWriteResult
5
5
  } from './operations/client_bulk_write/common';
6
- import type { ServerType } from './sdam/common';
7
6
  import type { TopologyVersion } from './sdam/server_description';
8
7
  import type { TopologyDescription } from './sdam/topology_description';
9
8
 
@@ -1394,23 +1393,13 @@ const RETRYABLE_READ_ERROR_CODES = new Set<number>([
1394
1393
  // see: https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.md#terms
1395
1394
  const RETRYABLE_WRITE_ERROR_CODES = RETRYABLE_READ_ERROR_CODES;
1396
1395
 
1397
- export function needsRetryableWriteLabel(
1398
- error: Error,
1399
- maxWireVersion: number,
1400
- serverType: ServerType
1401
- ): boolean {
1402
- // pre-4.4 server, then the driver adds an error label for every valid case
1403
- // execute operation will only inspect the label, code/message logic is handled here
1396
+ export function needsRetryableWriteLabel(error: Error): boolean {
1404
1397
  if (error instanceof MongoNetworkError) {
1405
1398
  return true;
1406
1399
  }
1407
-
1408
1400
  if (error instanceof MongoError) {
1409
- if (
1410
- (maxWireVersion >= 9 || isRetryableWriteError(error)) &&
1411
- !error.hasErrorLabel(MongoErrorLabel.HandshakeError)
1412
- ) {
1413
- // If we already have the error label no need to add it again. 4.4+ servers add the label.
1401
+ if (!error.hasErrorLabel(MongoErrorLabel.HandshakeError)) {
1402
+ // If we already have the error label no need to add it again. servers add the label.
1414
1403
  // In the case where we have a handshake error, need to fall down to the logic checking
1415
1404
  // the codes.
1416
1405
  return false;
@@ -1418,10 +1407,6 @@ export function needsRetryableWriteLabel(
1418
1407
  }
1419
1408
 
1420
1409
  if (error instanceof MongoWriteConcernError) {
1421
- if (serverType === 'Mongos' && maxWireVersion < 9) {
1422
- // use original top-level code from server response
1423
- return RETRYABLE_WRITE_ERROR_CODES.has(error.result.code ?? 0);
1424
- }
1425
1410
  const code = error.result.writeConcernError.code ?? Number(error.code);
1426
1411
  return RETRYABLE_WRITE_ERROR_CODES.has(Number.isNaN(code) ? 0 : code);
1427
1412
  }
@@ -1551,8 +1536,11 @@ export function isResumableError(error?: Error, wireVersion?: number): boolean {
1551
1536
  return true;
1552
1537
  }
1553
1538
 
1539
+ // wireVersion of 0 is possible when the server version is unknown (e.g heartbeat failure), so despite
1540
+ // all supported servers being wire version 9+, the >= 9 bound must stay to route "unknown" to the
1541
+ // code-list fallback below to maintain parity.
1554
1542
  if (wireVersion != null && wireVersion >= 9) {
1555
- // DRIVERS-1308: For 4.4 drivers running against 4.4 servers, drivers will add a special case to treat the CursorNotFound error code as resumable
1543
+ // DRIVERS-1308: Drivers will add a special case to treat the CursorNotFound error code as resumable
1556
1544
  if (error.code === MONGODB_ERROR_CODES.CursorNotFound) {
1557
1545
  return true;
1558
1546
  }
@@ -351,7 +351,7 @@ function init(stream: GridFSBucketReadStream): void {
351
351
 
352
352
  const filter: Document = { files_id: doc._id };
353
353
 
354
- // Currently (MongoDB 3.4.4) skip function does not support the index,
354
+ // Currently the skip function does not support the index,
355
355
  // it needs to retrieve all the documents first and then skip them. (CS-25811)
356
356
  // As work around we use $gte on the "n" field.
357
357
  if (stream.s.options && stream.s.options.start != null) {
package/src/index.ts CHANGED
@@ -252,6 +252,12 @@ export {
252
252
  MongoCryptInvalidArgumentError,
253
253
  MongoCryptKMSRequestNetworkTimeoutError
254
254
  } from './client-side-encryption/errors';
255
+ export type {
256
+ ClientEncryptionSocketOptions,
257
+ ClientEncryptionTlsOptions,
258
+ CSFLEKMSTlsOptions,
259
+ KMSConnectCallback
260
+ } from './client-side-encryption/kms_options';
255
261
  export type { MongocryptdManager } from './client-side-encryption/mongocryptd_manager';
256
262
  export type {
257
263
  AWSKMSProviderConfiguration,
@@ -263,12 +269,7 @@ export type {
263
269
  KMSProviders,
264
270
  LocalKMSProviderConfiguration
265
271
  } from './client-side-encryption/providers/index';
266
- export type {
267
- ClientEncryptionSocketOptions,
268
- ClientEncryptionTlsOptions,
269
- CSFLEKMSTlsOptions,
270
- StateMachineExecutable
271
- } from './client-side-encryption/state_machine';
272
+ export type { StateMachineExecutable } from './client-side-encryption/state_machine';
272
273
  export type { AuthContext, AuthProvider } from './cmap/auth/auth_provider';
273
274
  export type {
274
275
  AuthMechanismProperties,
@@ -1178,5 +1178,5 @@ export interface MongoOptions
1178
1178
  __skipPingOnConnect?: boolean;
1179
1179
 
1180
1180
  /** @internal */
1181
- runtime: Runtime;
1181
+ runtime: Promise<Runtime>;
1182
1182
  }
@@ -46,11 +46,6 @@ export interface CommandOperationOptions
46
46
  maxTimeMS?: number;
47
47
  /**
48
48
  * Comment to apply to the operation.
49
- *
50
- * In server versions pre-4.4, 'comment' must be string. A server
51
- * error will be thrown if any other type is provided.
52
- *
53
- * In server versions 4.4 and above, 'comment' can be any valid BSON type.
54
49
  */
55
50
  comment?: unknown;
56
51
  // Admin command overrides.
@@ -1,9 +1,10 @@
1
1
  import type { Document } from '../bson';
2
+ import { makeDocumentSequence } from '../cmap/commands';
2
3
  import { type Connection } from '../cmap/connection';
3
4
  import { MongoDBResponse } from '../cmap/wire_protocol/responses';
4
- import { MongoCompatibilityError, MongoServerError } from '../error';
5
+ import { MongoServerError } from '../error';
5
6
  import type { ClientSession } from '../sessions';
6
- import { maxWireVersion, type MongoDBCollectionNamespace, type MongoDBNamespace } from '../utils';
7
+ import { type MongoDBCollectionNamespace, type MongoDBNamespace } from '../utils';
7
8
  import { type WriteConcernOptions } from '../write_concern';
8
9
  import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
9
10
  import { Aspect, defineAspects, type Hint } from './operation';
@@ -45,12 +46,20 @@ export class DeleteOperation extends CommandOperation<Document> {
45
46
  override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
46
47
  override options: DeleteOptions;
47
48
  statements: DeleteStatement[];
48
-
49
- constructor(ns: MongoDBNamespace, statements: DeleteStatement[], options: DeleteOptions) {
49
+ /** @internal */
50
+ serializedOperations?: Uint8Array[];
51
+
52
+ constructor(
53
+ ns: MongoDBNamespace,
54
+ statements: DeleteStatement[],
55
+ options: DeleteOptions,
56
+ serializedOperations?: Uint8Array[]
57
+ ) {
50
58
  super(undefined, options);
51
59
  this.options = options;
52
60
  this.ns = ns;
53
61
  this.statements = statements;
62
+ this.serializedOperations = serializedOperations;
54
63
  }
55
64
 
56
65
  override get commandName() {
@@ -65,13 +74,15 @@ export class DeleteOperation extends CommandOperation<Document> {
65
74
  return this.statements.every(op => (op.limit != null ? op.limit > 0 : true));
66
75
  }
67
76
 
68
- override buildCommandDocument(connection: Connection, _session?: ClientSession): Document {
77
+ override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
69
78
  const options = this.options;
70
79
 
71
80
  const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
72
81
  const command: Document = {
73
82
  delete: this.ns.collection,
74
- deletes: this.statements,
83
+ deletes: this.serializedOperations
84
+ ? makeDocumentSequence('deletes', this.statements, this.serializedOperations)
85
+ : this.statements,
75
86
  ordered
76
87
  };
77
88
 
@@ -85,15 +96,6 @@ export class DeleteOperation extends CommandOperation<Document> {
85
96
  command.comment = options.comment;
86
97
  }
87
98
 
88
- const unacknowledgedWrite = this.writeConcern && this.writeConcern.w === 0;
89
- if (unacknowledgedWrite && maxWireVersion(connection) < 9) {
90
- if (this.statements.find((o: Document) => o.hint)) {
91
- throw new MongoCompatibilityError(
92
- `hint for the delete command is only supported on MongoDB 4.4+`
93
- );
94
- }
95
- }
96
-
97
99
  return command;
98
100
  }
99
101
  }
@@ -170,10 +170,33 @@ type RetryOptions = {
170
170
  timeoutContext: TimeoutContext;
171
171
  };
172
172
  /** @internal The base backoff duration in milliseconds */
173
- const BASE_BACKOFF_MS = 100;
173
+ export const BASE_BACKOFF_MS = 100;
174
174
  /** @internal The maximum backoff duration in milliseconds */
175
175
  const MAX_BACKOFF_MS = 10_000;
176
176
 
177
+ /**
178
+ * @internal
179
+ * The server-supplied base backoff duration in milliseconds, if the error carries a usable one.
180
+ *
181
+ * The server may attach `baseBackoffMS` to an overload error to indicate how long clients should
182
+ * back off in place of the driver's default. Only a positive value is honoured: the server disables
183
+ * the behaviour by reporting `0`, in which case we fall back to `BASE_BACKOFF_MS`.
184
+ */
185
+ export function calculateBaseBackoffMS(error: MongoError): number {
186
+ if (!(error instanceof MongoServerError)) return BASE_BACKOFF_MS;
187
+
188
+ // The server sends baseBackoffMS as an int64, so its runtime type depends on the client's BSON
189
+ // options: a number by default, a Long under `promoteLongs: false`, a bigint under
190
+ // `useBigInt64: true`. `Number()` handles all three, so coerce rather than check for `number`.
191
+ // `errorResponse` is indexed as `any`; narrow to `unknown` so the checks below are real.
192
+ const raw: unknown = error.errorResponse.baseBackoffMS;
193
+ const isNumeric = typeof raw === 'number' || typeof raw === 'bigint' || typeof raw === 'object';
194
+ const baseBackoffMS = isNumeric ? Number(raw) : NaN;
195
+ const useServerValue = Number.isFinite(baseBackoffMS) && baseBackoffMS > 0;
196
+ const result = useServerValue ? baseBackoffMS : BASE_BACKOFF_MS;
197
+ return result;
198
+ }
199
+
177
200
  /**
178
201
  * Executes an operation and retries as appropriate
179
202
  * @internal
@@ -327,7 +350,11 @@ async function executeOperationWithRetries<
327
350
  }
328
351
 
329
352
  if (operationError.hasErrorLabel(MongoErrorLabel.SystemOverloadedError)) {
330
- const backoffMS = Math.random() * Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** attempt);
353
+ const baseBackoffMS = calculateBaseBackoffMS(operationError);
354
+ // The spec numbers the first retry as attempt 1, while `attempt` here is the zero-based
355
+ // index of the attempt that just failed -- hence the `+ 1`.
356
+ const backoffMS =
357
+ Math.random() * Math.min(MAX_BACKOFF_MS, baseBackoffMS * 2 ** (attempt + 1));
331
358
 
332
359
  // if the backoff would exhaust the CSOT timeout, short-circuit.
333
360
  if (timeoutContext.csotEnabled() && backoffMS > timeoutContext.remainingTimeMS) {
@@ -2,11 +2,11 @@ import { type Connection } from '..';
2
2
  import type { Document } from '../bson';
3
3
  import { MongoDBResponse } from '../cmap/wire_protocol/responses';
4
4
  import type { Collection } from '../collection';
5
- import { MongoCompatibilityError, MongoInvalidArgumentError } from '../error';
5
+ import { MongoInvalidArgumentError } from '../error';
6
6
  import { ReadPreference } from '../read_preference';
7
7
  import type { ClientSession } from '../sessions';
8
8
  import { formatSort, type Sort, type SortForCmd } from '../sort';
9
- import { decorateWithCollation, hasAtomicOperators, maxWireVersion } from '../utils';
9
+ import { decorateWithCollation, hasAtomicOperators } from '../utils';
10
10
  import { type WriteConcern, type WriteConcernSettings } from '../write_concern';
11
11
  import { CommandOperation, type CommandOperationOptions } from './command';
12
12
  import { Aspect, defineAspects } from './operation';
@@ -97,11 +97,6 @@ interface FindAndModifyCmdBase {
97
97
  writeConcern?: WriteConcern | WriteConcernSettings;
98
98
  /**
99
99
  * Comment to apply to the operation.
100
- *
101
- * In server versions pre-4.4, 'comment' must be string. A server
102
- * error will be thrown if any other type is provided.
103
- *
104
- * In server versions 4.4 and above, 'comment' can be any valid BSON type.
105
100
  */
106
101
  comment?: unknown;
107
102
  }
@@ -146,7 +141,7 @@ export class FindAndModifyOperation extends CommandOperation<Document> {
146
141
  }
147
142
 
148
143
  override buildCommandDocument(
149
- connection: Connection,
144
+ _connection: Connection,
150
145
  _session?: ClientSession
151
146
  ): Document & FindAndModifyCmdBase {
152
147
  const options = this.options;
@@ -191,13 +186,6 @@ export class FindAndModifyOperation extends CommandOperation<Document> {
191
186
  decorateWithCollation(command, options);
192
187
 
193
188
  if (options.hint) {
194
- const unacknowledgedWrite = this.writeConcern?.w === 0;
195
- if (unacknowledgedWrite && maxWireVersion(connection) < 9) {
196
- throw new MongoCompatibilityError(
197
- 'hint for the findAndModify command is only supported on MongoDB 4.4+'
198
- );
199
- }
200
-
201
189
  command.hint = options.hint;
202
190
  }
203
191
 
@@ -4,7 +4,7 @@ import { CursorResponse } from '../cmap/wire_protocol/responses';
4
4
  import { MongoRuntimeError } from '../error';
5
5
  import type { Server, ServerCommandOptions } from '../sdam/server';
6
6
  import { type TimeoutContext } from '../timeout';
7
- import { maxWireVersion, type MongoDBNamespace } from '../utils';
7
+ import { type MongoDBNamespace } from '../utils';
8
8
  import { AbstractOperation, Aspect, defineAspects, type OperationOptions } from './operation';
9
9
 
10
10
  /** @internal */
@@ -13,8 +13,6 @@ export interface GetMoreOptions extends OperationOptions {
13
13
  batchSize?: number;
14
14
  /**
15
15
  * Comment to apply to the operation.
16
- *
17
- * getMore only supports 'comment' in server versions 4.4 and above.
18
16
  */
19
17
  comment?: unknown;
20
18
  /** Number of milliseconds to wait before aborting the query. */
@@ -55,7 +53,7 @@ export class GetMoreOperation extends AbstractOperation<CursorResponse> {
55
53
  return 'getMore' as const;
56
54
  }
57
55
 
58
- override buildCommand(connection: Connection): Document {
56
+ override buildCommand(_connection: Connection): Document {
59
57
  if (this.cursorId == null || this.cursorId.isZero()) {
60
58
  throw new MongoRuntimeError('Unable to iterate cursor with no id');
61
59
  }
@@ -82,7 +80,7 @@ export class GetMoreOperation extends AbstractOperation<CursorResponse> {
82
80
 
83
81
  // we check for undefined specifically here to allow falsy values
84
82
  // eslint-disable-next-line no-restricted-syntax
85
- if (this.options.comment !== undefined && maxWireVersion(connection) >= 9) {
83
+ if (this.options.comment !== undefined) {
86
84
  getMoreCmd.comment = this.options.comment;
87
85
  }
88
86
 
@@ -3,9 +3,8 @@ import { type Connection } from '../cmap/connection';
3
3
  import { CursorResponse, MongoDBResponse } from '../cmap/wire_protocol/responses';
4
4
  import type { Collection } from '../collection';
5
5
  import { type AbstractCursorOptions } from '../cursor/abstract_cursor';
6
- import { MongoCompatibilityError } from '../error';
7
6
  import { type OneOrMore } from '../mongo_types';
8
- import { isObject, maxWireVersion, type MongoDBNamespace } from '../utils';
7
+ import { isObject, type MongoDBNamespace } from '../utils';
9
8
  import {
10
9
  type CollationOptions,
11
10
  CommandOperation,
@@ -138,7 +137,7 @@ export interface CreateIndexesOptions extends Omit<CommandOperationOptions, 'wri
138
137
  expireAfterSeconds?: number;
139
138
  /** Allows users to configure the storage engine on a per-index basis when creating an index. (MongoDB 3.0 or higher) */
140
139
  storageEngine?: Document;
141
- /** (MongoDB 4.4. or higher) Specifies how many data-bearing members of a replica set, including the primary, must complete the index builds successfully before the primary marks the indexes as ready. This option accepts the same values for the "w" field in a write concern plus "votingMembers", which indicates all voting data-bearing nodes. */
140
+ /** Specifies how many data-bearing members of a replica set, including the primary, must complete the index builds successfully before the primary marks the indexes as ready. This option accepts the same values for the "w" field in a write concern plus "votingMembers", which indicates all voting data-bearing nodes. */
142
141
  commitQuorum?: number | string;
143
142
  /** Specifies the index version number, either 0 or 1. */
144
143
  version?: number;
@@ -159,7 +158,7 @@ export interface CreateIndexesOptions extends Omit<CommandOperationOptions, 'wri
159
158
  bucketSize?: number;
160
159
  // wildcard indexes
161
160
  wildcardProjection?: Document;
162
- /** Specifies that the index should exist on the target collection but should not be used by the query planner when executing operations. (MongoDB 4.4 or higher) */
161
+ /** Specifies that the index should exist on the target collection but should not be used by the query planner when executing operations. */
163
162
  hidden?: boolean;
164
163
  }
165
164
 
@@ -299,20 +298,13 @@ export class CreateIndexesOperation extends CommandOperation<string[]> {
299
298
  return 'createIndexes';
300
299
  }
301
300
 
302
- override buildCommandDocument(connection: Connection): Document {
301
+ override buildCommandDocument(_connection: Connection): Document {
303
302
  const options = this.options;
304
303
  const indexes = this.indexes;
305
304
 
306
- const serverWireVersion = maxWireVersion(connection);
307
-
308
305
  const cmd: Document = { createIndexes: this.collectionName, indexes };
309
306
 
310
307
  if (options.commitQuorum != null) {
311
- if (serverWireVersion < 9) {
312
- throw new MongoCompatibilityError(
313
- 'Option `commitQuorum` for `createIndexes` not supported on servers < 4.4'
314
- );
315
- }
316
308
  cmd.commitQuorum = options.commitQuorum;
317
309
  }
318
310
  return cmd;
@@ -385,16 +377,14 @@ export class ListIndexesOperation extends CommandOperation<CursorResponse> {
385
377
  return 'listIndexes' as const;
386
378
  }
387
379
 
388
- override buildCommandDocument(connection: Connection): Document {
389
- const serverWireVersion = maxWireVersion(connection);
390
-
380
+ override buildCommandDocument(_connection: Connection): Document {
391
381
  const cursor = this.options.batchSize ? { batchSize: this.options.batchSize } : {};
392
382
 
393
383
  const command: Document = { listIndexes: this.collectionNamespace.collection, cursor };
394
384
 
395
385
  // we check for undefined specifically here to allow falsy values
396
386
  // eslint-disable-next-line no-restricted-syntax
397
- if (serverWireVersion >= 9 && this.options.comment !== undefined) {
387
+ if (this.options.comment !== undefined) {
398
388
  command.comment = this.options.comment;
399
389
  }
400
390
 
@@ -1,6 +1,7 @@
1
1
  import { type Connection } from '..';
2
2
  import type { Document } from '../bson';
3
3
  import type { BulkWriteOptions } from '../bulk/common';
4
+ import { makeDocumentSequence } from '../cmap/commands';
4
5
  import { MongoDBResponse } from '../cmap/wire_protocol/responses';
5
6
  import type { Collection } from '../collection';
6
7
  import { MongoServerError } from '../error';
@@ -15,12 +16,20 @@ export class InsertOperation extends CommandOperation<Document> {
15
16
  override options: BulkWriteOptions;
16
17
 
17
18
  documents: Document[];
19
+ /** @internal Per-operation pre-serialized BSON, reused to build a DocumentSequence. */
20
+ serializedOperations?: Uint8Array[];
18
21
 
19
- constructor(ns: MongoDBNamespace, documents: Document[], options: BulkWriteOptions) {
22
+ constructor(
23
+ ns: MongoDBNamespace,
24
+ documents: Document[],
25
+ options: BulkWriteOptions,
26
+ serializedOperations?: Uint8Array[]
27
+ ) {
20
28
  super(undefined, options);
21
29
  this.options = { ...options, checkKeys: options.checkKeys ?? false };
22
30
  this.ns = ns;
23
31
  this.documents = documents;
32
+ this.serializedOperations = serializedOperations;
24
33
  }
25
34
 
26
35
  override get commandName() {
@@ -32,7 +41,9 @@ export class InsertOperation extends CommandOperation<Document> {
32
41
  const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
33
42
  const command: Document = {
34
43
  insert: this.ns.collection,
35
- documents: this.documents,
44
+ documents: this.serializedOperations
45
+ ? makeDocumentSequence('documents', this.documents, this.serializedOperations)
46
+ : this.documents,
36
47
  ordered
37
48
  };
38
49
 
@@ -4,7 +4,6 @@ import { CursorResponse, ExplainedCursorResponse } from '../cmap/wire_protocol/r
4
4
  import { type CursorTimeoutContext, type CursorTimeoutMode } from '../cursor/abstract_cursor';
5
5
  import type { Db } from '../db';
6
6
  import { type Abortable } from '../mongo_types';
7
- import { maxWireVersion } from '../utils';
8
7
  import { CommandOperation, type CommandOperationOptions } from './command';
9
8
  import { Aspect, defineAspects } from './operation';
10
9
 
@@ -12,11 +11,11 @@ import { Aspect, defineAspects } from './operation';
12
11
  export interface ListCollectionsOptions
13
12
  extends Omit<CommandOperationOptions, 'writeConcern'>,
14
13
  Abortable {
15
- /** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */
14
+ /** If true, will only return the collection name in the response, and will omit additional info */
16
15
  nameOnly?: boolean;
17
- /** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */
16
+ /** If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */
18
17
  authorizedCollections?: boolean;
19
- /** The batchSize for the returned command cursor or if pre 2.8 the systems batch collection */
18
+ /** The batchSize for the returned command cursor */
20
19
  batchSize?: number;
21
20
  /** @internal */
22
21
  timeoutMode?: CursorTimeoutMode;
@@ -63,7 +62,7 @@ export class ListCollectionsOperation extends CommandOperation<CursorResponse> {
63
62
  return 'listCollections' as const;
64
63
  }
65
64
 
66
- override buildCommandDocument(connection: Connection): Document {
65
+ override buildCommandDocument(_connection: Connection): Document {
67
66
  const command: Document = {
68
67
  listCollections: 1,
69
68
  filter: this.filter,
@@ -74,7 +73,7 @@ export class ListCollectionsOperation extends CommandOperation<CursorResponse> {
74
73
 
75
74
  // we check for undefined specifically here to allow falsy values
76
75
  // eslint-disable-next-line no-restricted-syntax
77
- if (maxWireVersion(connection) >= 9 && this.options.comment !== undefined) {
76
+ if (this.options.comment !== undefined) {
78
77
  command.comment = this.options.comment;
79
78
  }
80
79
 
@@ -3,7 +3,7 @@ import type { Document } from '../bson';
3
3
  import { MongoDBResponse } from '../cmap/wire_protocol/responses';
4
4
  import type { Db } from '../db';
5
5
  import type { ClientSession } from '../sessions';
6
- import { maxWireVersion, MongoDBNamespace } from '../utils';
6
+ import { MongoDBNamespace } from '../utils';
7
7
  import { CommandOperation, type CommandOperationOptions } from './command';
8
8
  import { Aspect, defineAspects } from './operation';
9
9
 
@@ -40,7 +40,7 @@ export class ListDatabasesOperation extends CommandOperation<ListDatabasesResult
40
40
  return 'listDatabases' as const;
41
41
  }
42
42
 
43
- override buildCommandDocument(connection: Connection, _session?: ClientSession): Document {
43
+ override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
44
44
  const cmd: Document = { listDatabases: 1 };
45
45
 
46
46
  if (typeof this.options.nameOnly === 'boolean') {
@@ -57,7 +57,7 @@ export class ListDatabasesOperation extends CommandOperation<ListDatabasesResult
57
57
 
58
58
  // we check for undefined specifically here to allow falsy values
59
59
  // eslint-disable-next-line no-restricted-syntax
60
- if (maxWireVersion(connection) >= 9 && this.options.comment !== undefined) {
60
+ if (this.options.comment !== undefined) {
61
61
  cmd.comment = this.options.comment;
62
62
  }
63
63
 
@@ -1,4 +1,5 @@
1
1
  import type { Document } from '../bson';
2
+ import { makeDocumentSequence } from '../cmap/commands';
2
3
  import { type Connection } from '../cmap/connection';
3
4
  import { MongoDBResponse } from '../cmap/wire_protocol/responses';
4
5
  import { MongoInvalidArgumentError, MongoServerError } from '../error';
@@ -74,17 +75,21 @@ export class UpdateOperation extends CommandOperation<Document> {
74
75
  override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
75
76
  override options: UpdateOptions & { ordered?: boolean };
76
77
  statements: UpdateStatement[];
78
+ /** @internal */
79
+ serializedOperations?: Uint8Array[];
77
80
 
78
81
  constructor(
79
82
  ns: MongoDBNamespace,
80
83
  statements: UpdateStatement[],
81
- options: UpdateOptions & { ordered?: boolean }
84
+ options: UpdateOptions & { ordered?: boolean },
85
+ serializedOperations?: Uint8Array[]
82
86
  ) {
83
87
  super(undefined, options);
84
88
  this.options = options;
85
89
  this.ns = ns;
86
90
 
87
91
  this.statements = statements;
92
+ this.serializedOperations = serializedOperations;
88
93
  }
89
94
 
90
95
  override get commandName() {
@@ -103,7 +108,9 @@ export class UpdateOperation extends CommandOperation<Document> {
103
108
  const options = this.options;
104
109
  const command: Document = {
105
110
  update: this.ns.collection,
106
- updates: this.statements,
111
+ updates: this.serializedOperations
112
+ ? makeDocumentSequence('updates', this.statements, this.serializedOperations)
113
+ : this.statements,
107
114
  ordered: options.ordered ?? true
108
115
  };
109
116
 
@@ -8,7 +8,7 @@ import { CommandOperation, type CommandOperationOptions } from './command';
8
8
 
9
9
  /** @public */
10
10
  export interface ValidateCollectionOptions extends Omit<CommandOperationOptions, 'rawData'> {
11
- /** Validates a collection in the background, without interrupting read or write traffic (only in MongoDB 4.4+) */
11
+ /** Validates a collection in the background, without interrupting read or write traffic */
12
12
  background?: boolean;
13
13
  }
14
14