mongodb 6.9.0 → 6.10.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 (127) hide show
  1. package/lib/beta.d.ts +407 -12
  2. package/lib/bson.js +1 -0
  3. package/lib/bson.js.map +1 -1
  4. package/lib/bulk/common.js +60 -71
  5. package/lib/bulk/common.js.map +1 -1
  6. package/lib/bulk/unordered.js +3 -3
  7. package/lib/bulk/unordered.js.map +1 -1
  8. package/lib/change_stream.js +3 -2
  9. package/lib/change_stream.js.map +1 -1
  10. package/lib/cmap/auth/mongo_credentials.js +2 -7
  11. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  12. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
  13. package/lib/cmap/auth/mongodb_oidc/command_builders.js +1 -1
  14. package/lib/cmap/auth/mongodb_oidc/command_builders.js.map +1 -1
  15. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js +1 -1
  16. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js.map +1 -1
  17. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +1 -1
  18. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  19. package/lib/cmap/command_monitoring_events.js +10 -1
  20. package/lib/cmap/command_monitoring_events.js.map +1 -1
  21. package/lib/cmap/commands.js +50 -18
  22. package/lib/cmap/commands.js.map +1 -1
  23. package/lib/cmap/connection.js +9 -2
  24. package/lib/cmap/connection.js.map +1 -1
  25. package/lib/cmap/wire_protocol/constants.js +2 -2
  26. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
  27. package/lib/cmap/wire_protocol/responses.js +26 -1
  28. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  29. package/lib/connection_string.js +1 -7
  30. package/lib/connection_string.js.map +1 -1
  31. package/lib/cursor/aggregation_cursor.js.map +1 -1
  32. package/lib/cursor/client_bulk_write_cursor.js +52 -0
  33. package/lib/cursor/client_bulk_write_cursor.js.map +1 -0
  34. package/lib/cursor/find_cursor.js.map +1 -1
  35. package/lib/db.js +1 -1
  36. package/lib/error.js +76 -3
  37. package/lib/error.js.map +1 -1
  38. package/lib/explain.js +6 -6
  39. package/lib/explain.js.map +1 -1
  40. package/lib/index.js +6 -3
  41. package/lib/index.js.map +1 -1
  42. package/lib/mongo_client.js +14 -0
  43. package/lib/mongo_client.js.map +1 -1
  44. package/lib/mongo_client_auth_providers.js +6 -2
  45. package/lib/mongo_client_auth_providers.js.map +1 -1
  46. package/lib/mongo_types.js.map +1 -1
  47. package/lib/operations/aggregate.js.map +1 -1
  48. package/lib/operations/client_bulk_write/client_bulk_write.js +83 -0
  49. package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -0
  50. package/lib/operations/client_bulk_write/command_builder.js +154 -19
  51. package/lib/operations/client_bulk_write/command_builder.js.map +1 -1
  52. package/lib/operations/client_bulk_write/executor.js +109 -0
  53. package/lib/operations/client_bulk_write/executor.js.map +1 -0
  54. package/lib/operations/client_bulk_write/results_merger.js +204 -0
  55. package/lib/operations/client_bulk_write/results_merger.js.map +1 -0
  56. package/lib/operations/execute_operation.js +7 -0
  57. package/lib/operations/execute_operation.js.map +1 -1
  58. package/lib/operations/find.js.map +1 -1
  59. package/lib/operations/operation.js +5 -1
  60. package/lib/operations/operation.js.map +1 -1
  61. package/lib/operations/search_indexes/create.js.map +1 -1
  62. package/lib/operations/search_indexes/drop.js.map +1 -1
  63. package/lib/operations/search_indexes/update.js.map +1 -1
  64. package/lib/sdam/server.js +2 -1
  65. package/lib/sdam/server.js.map +1 -1
  66. package/lib/sdam/server_description.js +3 -0
  67. package/lib/sdam/server_description.js.map +1 -1
  68. package/lib/sdam/srv_polling.js +5 -1
  69. package/lib/sdam/srv_polling.js.map +1 -1
  70. package/lib/sdam/topology_description.js.map +1 -1
  71. package/lib/sessions.js +9 -2
  72. package/lib/sessions.js.map +1 -1
  73. package/lib/utils.js +25 -9
  74. package/lib/utils.js.map +1 -1
  75. package/lib/write_concern.js.map +1 -1
  76. package/mongodb.d.ts +407 -12
  77. package/package.json +1 -1
  78. package/src/beta.ts +1 -1
  79. package/src/bson.ts +3 -0
  80. package/src/bulk/common.ts +80 -120
  81. package/src/bulk/unordered.ts +3 -4
  82. package/src/change_stream.ts +5 -2
  83. package/src/cmap/auth/mongo_credentials.ts +2 -8
  84. package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +1 -1
  85. package/src/cmap/auth/mongodb_oidc/command_builders.ts +1 -2
  86. package/src/cmap/auth/mongodb_oidc/human_callback_workflow.ts +1 -2
  87. package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +1 -1
  88. package/src/cmap/auth/mongodb_oidc.ts +1 -2
  89. package/src/cmap/command_monitoring_events.ts +16 -2
  90. package/src/cmap/commands.ts +71 -25
  91. package/src/cmap/connection.ts +17 -4
  92. package/src/cmap/wire_protocol/constants.ts +2 -2
  93. package/src/cmap/wire_protocol/on_demand/document.ts +1 -2
  94. package/src/cmap/wire_protocol/responses.ts +31 -2
  95. package/src/connection_string.ts +2 -9
  96. package/src/cursor/aggregation_cursor.ts +2 -2
  97. package/src/cursor/client_bulk_write_cursor.ts +79 -0
  98. package/src/cursor/find_cursor.ts +2 -2
  99. package/src/db.ts +1 -1
  100. package/src/error.ts +98 -2
  101. package/src/explain.ts +47 -11
  102. package/src/index.ts +26 -1
  103. package/src/mongo_client.ts +29 -1
  104. package/src/mongo_client_auth_providers.ts +8 -2
  105. package/src/mongo_types.ts +2 -1
  106. package/src/operations/aggregate.ts +9 -1
  107. package/src/operations/client_bulk_write/client_bulk_write.ts +107 -0
  108. package/src/operations/client_bulk_write/command_builder.ts +216 -30
  109. package/src/operations/client_bulk_write/common.ts +148 -23
  110. package/src/operations/client_bulk_write/executor.ts +137 -0
  111. package/src/operations/client_bulk_write/results_merger.ts +260 -0
  112. package/src/operations/execute_operation.ts +8 -0
  113. package/src/operations/find.ts +8 -1
  114. package/src/operations/operation.ts +6 -1
  115. package/src/operations/search_indexes/create.ts +1 -2
  116. package/src/operations/search_indexes/drop.ts +1 -2
  117. package/src/operations/search_indexes/update.ts +1 -2
  118. package/src/sdam/server.ts +2 -1
  119. package/src/sdam/server_description.ts +9 -0
  120. package/src/sdam/srv_polling.ts +5 -2
  121. package/src/sdam/topology_description.ts +0 -1
  122. package/src/sessions.ts +16 -2
  123. package/src/utils.ts +40 -10
  124. package/src/write_concern.ts +4 -1
  125. package/lib/cmap/auth/mongocr.js +0 -35
  126. package/lib/cmap/auth/mongocr.js.map +0 -1
  127. package/src/cmap/auth/mongocr.ts +0 -38
@@ -1,5 +1,3 @@
1
- import { promisify } from 'util';
2
-
3
1
  import { type BSONSerializeOptions, type Document, EJSON, resolveBSONOptions } from '../bson';
4
2
  import type { Collection } from '../collection';
5
3
  import {
@@ -7,6 +5,7 @@ import {
7
5
  MongoBatchReExecutionError,
8
6
  MONGODB_ERROR_CODES,
9
7
  MongoInvalidArgumentError,
8
+ MongoRuntimeError,
10
9
  MongoServerError,
11
10
  MongoWriteConcernError
12
11
  } from '../error';
@@ -22,7 +21,6 @@ import type { Topology } from '../sdam/topology';
22
21
  import type { ClientSession } from '../sessions';
23
22
  import {
24
23
  applyRetryableWrites,
25
- type Callback,
26
24
  getTopology,
27
25
  hasAtomicOperators,
28
26
  maybeAddIdToDocuments,
@@ -500,86 +498,46 @@ export function mergeBatchResults(
500
498
  }
501
499
  }
502
500
 
503
- function executeCommands(
501
+ async function executeCommands(
504
502
  bulkOperation: BulkOperationBase,
505
- options: BulkWriteOptions,
506
- callback: Callback<BulkWriteResult>
507
- ) {
503
+ options: BulkWriteOptions
504
+ ): Promise<BulkWriteResult> {
508
505
  if (bulkOperation.s.batches.length === 0) {
509
- return callback(
510
- undefined,
511
- new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered)
512
- );
506
+ return new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
513
507
  }
514
508
 
515
- const batch = bulkOperation.s.batches.shift() as Batch;
509
+ for (const batch of bulkOperation.s.batches) {
510
+ const finalOptions = resolveOptions(bulkOperation, {
511
+ ...options,
512
+ ordered: bulkOperation.isOrdered
513
+ });
516
514
 
517
- function resultHandler(err?: AnyError, result?: Document) {
518
- // Error is a driver related error not a bulk op error, return early
519
- if (err && 'message' in err && !(err instanceof MongoWriteConcernError)) {
520
- return callback(
521
- new MongoBulkWriteError(
522
- err,
523
- new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered)
524
- )
525
- );
515
+ if (finalOptions.bypassDocumentValidation !== true) {
516
+ delete finalOptions.bypassDocumentValidation;
526
517
  }
527
518
 
528
- if (err instanceof MongoWriteConcernError) {
529
- return handleMongoWriteConcernError(
530
- batch,
531
- bulkOperation.s.bulkResult,
532
- bulkOperation.isOrdered,
533
- err,
534
- callback
535
- );
519
+ // Is the bypassDocumentValidation options specific
520
+ if (bulkOperation.s.bypassDocumentValidation === true) {
521
+ finalOptions.bypassDocumentValidation = true;
536
522
  }
537
523
 
538
- // Merge the results together
539
- mergeBatchResults(batch, bulkOperation.s.bulkResult, err, result);
540
- const writeResult = new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
541
- if (bulkOperation.handleWriteError(callback, writeResult)) return;
542
-
543
- // Execute the next command in line
544
- executeCommands(bulkOperation, options, callback);
545
- }
546
-
547
- const finalOptions = resolveOptions(bulkOperation, {
548
- ...options,
549
- ordered: bulkOperation.isOrdered
550
- });
551
-
552
- if (finalOptions.bypassDocumentValidation !== true) {
553
- delete finalOptions.bypassDocumentValidation;
554
- }
555
-
556
- // Set an operationIf if provided
557
- if (bulkOperation.operationId) {
558
- resultHandler.operationId = bulkOperation.operationId;
559
- }
560
-
561
- // Is the bypassDocumentValidation options specific
562
- if (bulkOperation.s.bypassDocumentValidation === true) {
563
- finalOptions.bypassDocumentValidation = true;
564
- }
565
-
566
- // Is the checkKeys option disabled
567
- if (bulkOperation.s.checkKeys === false) {
568
- finalOptions.checkKeys = false;
569
- }
570
-
571
- if (finalOptions.retryWrites) {
572
- if (isUpdateBatch(batch)) {
573
- finalOptions.retryWrites = finalOptions.retryWrites && !batch.operations.some(op => op.multi);
524
+ // Is the checkKeys option disabled
525
+ if (bulkOperation.s.checkKeys === false) {
526
+ finalOptions.checkKeys = false;
574
527
  }
575
528
 
576
- if (isDeleteBatch(batch)) {
577
- finalOptions.retryWrites =
578
- finalOptions.retryWrites && !batch.operations.some(op => op.limit === 0);
529
+ if (finalOptions.retryWrites) {
530
+ if (isUpdateBatch(batch)) {
531
+ finalOptions.retryWrites =
532
+ finalOptions.retryWrites && !batch.operations.some(op => op.multi);
533
+ }
534
+
535
+ if (isDeleteBatch(batch)) {
536
+ finalOptions.retryWrites =
537
+ finalOptions.retryWrites && !batch.operations.some(op => op.limit === 0);
538
+ }
579
539
  }
580
- }
581
540
 
582
- try {
583
541
  const operation = isInsertBatch(batch)
584
542
  ? new InsertOperation(bulkOperation.s.namespace, batch.operations, finalOptions)
585
543
  : isUpdateBatch(batch)
@@ -588,39 +546,50 @@ function executeCommands(
588
546
  ? new DeleteOperation(bulkOperation.s.namespace, batch.operations, finalOptions)
589
547
  : null;
590
548
 
591
- if (operation != null) {
592
- executeOperation(bulkOperation.s.collection.client, operation).then(
593
- result => resultHandler(undefined, result),
594
- error => resultHandler(error)
595
- );
549
+ if (operation == null) throw new MongoRuntimeError(`Unknown batchType: ${batch.batchType}`);
550
+
551
+ let thrownError = null;
552
+ let result;
553
+ try {
554
+ result = await executeOperation(bulkOperation.s.collection.client, operation);
555
+ } catch (error) {
556
+ thrownError = error;
557
+ }
558
+
559
+ if (thrownError != null) {
560
+ if (thrownError instanceof MongoWriteConcernError) {
561
+ mergeBatchResults(batch, bulkOperation.s.bulkResult, thrownError, result);
562
+ const writeResult = new BulkWriteResult(
563
+ bulkOperation.s.bulkResult,
564
+ bulkOperation.isOrdered
565
+ );
566
+
567
+ throw new MongoBulkWriteError(
568
+ {
569
+ message: thrownError.result.writeConcernError.errmsg,
570
+ code: thrownError.result.writeConcernError.code
571
+ },
572
+ writeResult
573
+ );
574
+ } else {
575
+ // Error is a driver related error not a bulk op error, return early
576
+ throw new MongoBulkWriteError(
577
+ thrownError,
578
+ new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered)
579
+ );
580
+ }
596
581
  }
597
- } catch (err) {
598
- // Force top level error
599
- err.ok = 0;
600
- // Merge top level error and return
601
- mergeBatchResults(batch, bulkOperation.s.bulkResult, err, undefined);
602
- callback();
582
+
583
+ mergeBatchResults(batch, bulkOperation.s.bulkResult, thrownError, result);
584
+ const writeResult = new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
585
+ bulkOperation.handleWriteError(writeResult);
603
586
  }
604
- }
605
587
 
606
- function handleMongoWriteConcernError(
607
- batch: Batch,
608
- bulkResult: BulkResult,
609
- isOrdered: boolean,
610
- err: MongoWriteConcernError,
611
- callback: Callback<BulkWriteResult>
612
- ) {
613
- mergeBatchResults(batch, bulkResult, undefined, err.result);
614
-
615
- callback(
616
- new MongoBulkWriteError(
617
- {
618
- message: err.result.writeConcernError.errmsg,
619
- code: err.result.writeConcernError.code
620
- },
621
- new BulkWriteResult(bulkResult, isOrdered)
622
- )
623
- );
588
+ bulkOperation.s.batches.length = 0;
589
+
590
+ const writeResult = new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
591
+ bulkOperation.handleWriteError(writeResult);
592
+ return writeResult;
624
593
  }
625
594
 
626
595
  /**
@@ -875,8 +844,6 @@ export interface BulkWriteOptions extends CommandOperationOptions {
875
844
  let?: Document;
876
845
  }
877
846
 
878
- const executeCommandsAsync = promisify(executeCommands);
879
-
880
847
  /**
881
848
  * TODO(NODE-4063)
882
849
  * BulkWrites merge complexity is implemented in executeCommands
@@ -895,7 +862,7 @@ export class BulkWriteShimOperation extends AbstractOperation {
895
862
  return 'bulkWrite' as const;
896
863
  }
897
864
 
898
- execute(_server: Server, session: ClientSession | undefined): Promise<any> {
865
+ async execute(_server: Server, session: ClientSession | undefined): Promise<any> {
899
866
  if (this.options.session == null) {
900
867
  // An implicit session could have been created by 'executeOperation'
901
868
  // So if we stick it on finalOptions here, each bulk operation
@@ -903,7 +870,7 @@ export class BulkWriteShimOperation extends AbstractOperation {
903
870
  // an explicit session would be
904
871
  this.options.session = session;
905
872
  }
906
- return executeCommandsAsync(this.bulkOperation, this.options);
873
+ return await executeCommands(this.bulkOperation, this.options);
907
874
  }
908
875
  }
909
876
 
@@ -1239,33 +1206,26 @@ export abstract class BulkOperationBase {
1239
1206
  * Handles the write error before executing commands
1240
1207
  * @internal
1241
1208
  */
1242
- handleWriteError(callback: Callback<BulkWriteResult>, writeResult: BulkWriteResult): boolean {
1209
+ handleWriteError(writeResult: BulkWriteResult): void {
1243
1210
  if (this.s.bulkResult.writeErrors.length > 0) {
1244
1211
  const msg = this.s.bulkResult.writeErrors[0].errmsg
1245
1212
  ? this.s.bulkResult.writeErrors[0].errmsg
1246
1213
  : 'write operation failed';
1247
1214
 
1248
- callback(
1249
- new MongoBulkWriteError(
1250
- {
1251
- message: msg,
1252
- code: this.s.bulkResult.writeErrors[0].code,
1253
- writeErrors: this.s.bulkResult.writeErrors
1254
- },
1255
- writeResult
1256
- )
1215
+ throw new MongoBulkWriteError(
1216
+ {
1217
+ message: msg,
1218
+ code: this.s.bulkResult.writeErrors[0].code,
1219
+ writeErrors: this.s.bulkResult.writeErrors
1220
+ },
1221
+ writeResult
1257
1222
  );
1258
-
1259
- return true;
1260
1223
  }
1261
1224
 
1262
1225
  const writeConcernError = writeResult.getWriteConcernError();
1263
1226
  if (writeConcernError) {
1264
- callback(new MongoBulkWriteError(writeConcernError, writeResult));
1265
- return true;
1227
+ throw new MongoBulkWriteError(writeConcernError, writeResult);
1266
1228
  }
1267
-
1268
- return false;
1269
1229
  }
1270
1230
 
1271
1231
  abstract addToOperationsList(
@@ -4,7 +4,6 @@ import type { Collection } from '../collection';
4
4
  import { MongoInvalidArgumentError } from '../error';
5
5
  import type { DeleteStatement } from '../operations/delete';
6
6
  import type { UpdateStatement } from '../operations/update';
7
- import { type Callback } from '../utils';
8
7
  import {
9
8
  Batch,
10
9
  BatchType,
@@ -20,12 +19,12 @@ export class UnorderedBulkOperation extends BulkOperationBase {
20
19
  super(collection, options, false);
21
20
  }
22
21
 
23
- override handleWriteError(callback: Callback, writeResult: BulkWriteResult): boolean {
22
+ override handleWriteError(writeResult: BulkWriteResult): void {
24
23
  if (this.s.batches.length) {
25
- return false;
24
+ return;
26
25
  }
27
26
 
28
- return super.handleWriteError(callback, writeResult);
27
+ return super.handleWriteError(writeResult);
29
28
  }
30
29
 
31
30
  addToOperationsList(
@@ -946,7 +946,7 @@ export class ChangeStream<
946
946
  // If the change stream has been closed explicitly, do not process error.
947
947
  if (this[kClosed]) return;
948
948
 
949
- if (isResumableError(changeStreamError, this.cursor.maxWireVersion)) {
949
+ if (this.cursor.id != null && isResumableError(changeStreamError, this.cursor.maxWireVersion)) {
950
950
  this._endStream();
951
951
 
952
952
  this.cursor.close().then(undefined, squashError);
@@ -975,7 +975,10 @@ export class ChangeStream<
975
975
  throw new MongoAPIError(CHANGESTREAM_CLOSED_ERROR);
976
976
  }
977
977
 
978
- if (!isResumableError(changeStreamError, this.cursor.maxWireVersion)) {
978
+ if (
979
+ this.cursor.id == null ||
980
+ !isResumableError(changeStreamError, this.cursor.maxWireVersion)
981
+ ) {
979
982
  try {
980
983
  await this.close();
981
984
  } catch (error) {
@@ -20,15 +20,10 @@ function getDefaultAuthMechanism(hello: Document | null): AuthMechanism {
20
20
  ? AuthMechanism.MONGODB_SCRAM_SHA256
21
21
  : AuthMechanism.MONGODB_SCRAM_SHA1;
22
22
  }
23
-
24
- // Fallback to legacy selection method. If wire version >= 3, use scram-sha-1
25
- if (hello.maxWireVersion >= 3) {
26
- return AuthMechanism.MONGODB_SCRAM_SHA1;
27
- }
28
23
  }
29
24
 
30
- // Default for wireprotocol < 3
31
- return AuthMechanism.MONGODB_CR;
25
+ // Default auth mechanism for 4.0 and higher.
26
+ return AuthMechanism.MONGODB_SCRAM_SHA256;
32
27
  }
33
28
 
34
29
  const ALLOWED_ENVIRONMENT_NAMES: AuthMechanismProperties['ENVIRONMENT'][] = [
@@ -173,7 +168,6 @@ export class MongoCredentials {
173
168
  validate(): void {
174
169
  if (
175
170
  (this.mechanism === AuthMechanism.MONGODB_GSSAPI ||
176
- this.mechanism === AuthMechanism.MONGODB_CR ||
177
171
  this.mechanism === AuthMechanism.MONGODB_PLAIN ||
178
172
  this.mechanism === AuthMechanism.MONGODB_SCRAM_SHA1 ||
179
173
  this.mechanism === AuthMechanism.MONGODB_SCRAM_SHA256) &&
@@ -1,6 +1,6 @@
1
- import { type Document } from 'bson';
2
1
  import { setTimeout } from 'timers/promises';
3
2
 
3
+ import { type Document } from '../../../bson';
4
4
  import { MongoMissingCredentialsError } from '../../../error';
5
5
  import { ns } from '../../../utils';
6
6
  import type { Connection } from '../../connection';
@@ -1,5 +1,4 @@
1
- import { Binary, BSON, type Document } from 'bson';
2
-
1
+ import { Binary, BSON, type Document } from '../../../bson';
3
2
  import { type MongoCredentials } from '../mongo_credentials';
4
3
  import { AuthMechanism } from '../providers';
5
4
 
@@ -1,5 +1,4 @@
1
- import { BSON } from 'bson';
2
-
1
+ import { BSON } from '../../../bson';
3
2
  import { MONGODB_ERROR_CODES, MongoError, MongoOIDCError } from '../../../error';
4
3
  import { Timeout, TimeoutError } from '../../../timeout';
5
4
  import { type Connection } from '../../connection';
@@ -1,6 +1,6 @@
1
- import { type Document } from 'bson';
2
1
  import { setTimeout } from 'timers/promises';
3
2
 
3
+ import { type Document } from '../../../bson';
4
4
  import { ns } from '../../../utils';
5
5
  import type { Connection } from '../../connection';
6
6
  import type { MongoCredentials } from '../mongo_credentials';
@@ -1,5 +1,4 @@
1
- import type { Document } from 'bson';
2
-
1
+ import type { Document } from '../../bson';
3
2
  import { MongoInvalidArgumentError, MongoMissingCredentialsError } from '../../error';
4
3
  import type { HandshakeDocument } from '../connect';
5
4
  import type { Connection } from '../connection';
@@ -7,7 +7,12 @@ import {
7
7
  LEGACY_HELLO_COMMAND_CAMEL_CASE
8
8
  } from '../constants';
9
9
  import { calculateDurationInMs, deepCopy } from '../utils';
10
- import { OpMsgRequest, type OpQueryRequest, type WriteProtocolMessageType } from './commands';
10
+ import {
11
+ DocumentSequence,
12
+ OpMsgRequest,
13
+ type OpQueryRequest,
14
+ type WriteProtocolMessageType
15
+ } from './commands';
11
16
  import type { Connection } from './connection';
12
17
 
13
18
  /**
@@ -249,7 +254,16 @@ const OP_QUERY_KEYS = [
249
254
  /** Extract the actual command from the query, possibly up-converting if it's a legacy format */
250
255
  function extractCommand(command: WriteProtocolMessageType): Document {
251
256
  if (command instanceof OpMsgRequest) {
252
- return deepCopy(command.command);
257
+ const cmd = deepCopy(command.command);
258
+ // For OP_MSG with payload type 1 we need to pull the documents
259
+ // array out of the document sequence for monitoring.
260
+ if (cmd.ops instanceof DocumentSequence) {
261
+ cmd.ops = cmd.ops.documents;
262
+ }
263
+ if (cmd.nsInfo instanceof DocumentSequence) {
264
+ cmd.nsInfo = cmd.nsInfo.documents;
265
+ }
266
+ return cmd;
253
267
  }
254
268
 
255
269
  if (command.query?.$query) {
@@ -74,6 +74,8 @@ export class OpQueryRequest {
74
74
  awaitData: boolean;
75
75
  exhaust: boolean;
76
76
  partial: boolean;
77
+ /** moreToCome is an OP_MSG only concept */
78
+ moreToCome = false;
77
79
 
78
80
  constructor(
79
81
  public databaseName: string,
@@ -407,22 +409,80 @@ const OPTS_EXHAUST_ALLOWED = 1 << 16;
407
409
 
408
410
  /** @internal */
409
411
  export interface OpMsgOptions {
410
- requestId: number;
411
- serializeFunctions: boolean;
412
- ignoreUndefined: boolean;
413
- checkKeys: boolean;
414
- maxBsonSize: number;
415
- moreToCome: boolean;
416
- exhaustAllowed: boolean;
412
+ socketTimeoutMS?: number;
413
+ session?: ClientSession;
414
+ numberToSkip?: number;
415
+ numberToReturn?: number;
416
+ returnFieldSelector?: Document;
417
+ pre32Limit?: number;
418
+ serializeFunctions?: boolean;
419
+ ignoreUndefined?: boolean;
420
+ maxBsonSize?: number;
421
+ checkKeys?: boolean;
422
+ secondaryOk?: boolean;
423
+
424
+ requestId?: number;
425
+ moreToCome?: boolean;
426
+ exhaustAllowed?: boolean;
417
427
  readPreference: ReadPreference;
418
428
  }
419
429
 
420
430
  /** @internal */
421
431
  export class DocumentSequence {
432
+ field: string;
422
433
  documents: Document[];
434
+ serializedDocumentsLength: number;
435
+ private chunks: Uint8Array[];
436
+ private header: Buffer;
437
+
438
+ /**
439
+ * Create a new document sequence for the provided field.
440
+ * @param field - The field it will replace.
441
+ */
442
+ constructor(field: string, documents?: Document[]) {
443
+ this.field = field;
444
+ this.documents = [];
445
+ this.chunks = [];
446
+ this.serializedDocumentsLength = 0;
447
+ // Document sequences starts with type 1 at the first byte.
448
+ // Field strings must always be UTF-8.
449
+ const buffer = Buffer.allocUnsafe(1 + 4 + this.field.length + 1);
450
+ buffer[0] = 1;
451
+ // Third part is the field name at offset 5 with trailing null byte.
452
+ encodeUTF8Into(buffer, `${this.field}\0`, 5);
453
+ this.chunks.push(buffer);
454
+ this.header = buffer;
455
+ if (documents) {
456
+ for (const doc of documents) {
457
+ this.push(doc, BSON.serialize(doc));
458
+ }
459
+ }
460
+ }
423
461
 
424
- constructor(documents: Document[]) {
425
- this.documents = documents;
462
+ /**
463
+ * Push a document to the document sequence. Will serialize the document
464
+ * as well and return the current serialized length of all documents.
465
+ * @param document - The document to add.
466
+ * @param buffer - The serialized document in raw BSON.
467
+ * @returns The new total document sequence length.
468
+ */
469
+ push(document: Document, buffer: Uint8Array): number {
470
+ this.serializedDocumentsLength += buffer.length;
471
+ // Push the document.
472
+ this.documents.push(document);
473
+ // Push the document raw bson.
474
+ this.chunks.push(buffer);
475
+ // Write the new length.
476
+ this.header?.writeInt32LE(4 + this.field.length + 1 + this.serializedDocumentsLength, 1);
477
+ return this.serializedDocumentsLength + this.header.length;
478
+ }
479
+
480
+ /**
481
+ * Get the fully serialized bytes for the document sequence section.
482
+ * @returns The section bytes.
483
+ */
484
+ toBin(): Uint8Array {
485
+ return Buffer.concat(this.chunks);
426
486
  }
427
487
  }
428
488
 
@@ -465,7 +525,7 @@ export class OpMsgRequest {
465
525
 
466
526
  // flags
467
527
  this.checksumPresent = false;
468
- this.moreToCome = options.moreToCome || false;
528
+ this.moreToCome = options.moreToCome ?? command.writeConcern?.w === 0;
469
529
  this.exhaustAllowed =
470
530
  typeof options.exhaustAllowed === 'boolean' ? options.exhaustAllowed : false;
471
531
  }
@@ -533,21 +593,7 @@ export class OpMsgRequest {
533
593
  const chunks = [];
534
594
  for (const [key, value] of Object.entries(document)) {
535
595
  if (value instanceof DocumentSequence) {
536
- // Document sequences starts with type 1 at the first byte.
537
- const buffer = Buffer.allocUnsafe(1 + 4 + key.length);
538
- buffer[0] = 1;
539
- // Third part is the field name at offset 5.
540
- encodeUTF8Into(buffer, key, 5);
541
- chunks.push(buffer);
542
- // Fourth part are the documents' bytes.
543
- let docsLength = 0;
544
- for (const doc of value.documents) {
545
- const docBson = this.serializeBson(doc);
546
- docsLength += docBson.length;
547
- chunks.push(docBson);
548
- }
549
- // Second part of the sequence is the length at offset 1;
550
- buffer.writeInt32LE(key.length + docsLength, 1);
596
+ chunks.push(value.toBin());
551
597
  // Why are we removing the field from the command? This is because it needs to be
552
598
  // removed in the OP_MSG request first section, and DocumentSequence is not a
553
599
  // BSON type and is specific to the MongoDB wire protocol so there's nothing
@@ -1,8 +1,13 @@
1
- import { type DeserializeOptions } from 'bson';
2
1
  import { type Readable, Transform, type TransformCallback } from 'stream';
3
2
  import { clearTimeout, setTimeout } from 'timers';
4
3
 
5
- import { type BSONSerializeOptions, deserialize, type Document, type ObjectId } from '../bson';
4
+ import {
5
+ type BSONSerializeOptions,
6
+ deserialize,
7
+ type DeserializeOptions,
8
+ type Document,
9
+ type ObjectId
10
+ } from '../bson';
6
11
  import { type AutoEncrypter } from '../client-side-encryption/auto_encrypter';
7
12
  import {
8
13
  CLOSE,
@@ -237,6 +242,8 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
237
242
  .on('error', this.onError.bind(this));
238
243
  this.socket.on('close', this.onClose.bind(this));
239
244
  this.socket.on('timeout', this.onTimeout.bind(this));
245
+
246
+ this.messageStream.pause();
240
247
  }
241
248
 
242
249
  public get hello() {
@@ -439,7 +446,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
439
446
  zlibCompressionLevel: this.description.zlibCompressionLevel
440
447
  });
441
448
 
442
- if (options.noResponse) {
449
+ if (options.noResponse || message.moreToCome) {
443
450
  yield MongoDBResponse.empty;
444
451
  return;
445
452
  }
@@ -527,7 +534,11 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
527
534
  new CommandSucceededEvent(
528
535
  this,
529
536
  message,
530
- options.noResponse ? undefined : (object ??= document.toObject(bsonOptions)),
537
+ options.noResponse
538
+ ? undefined
539
+ : message.moreToCome
540
+ ? { ok: 1 }
541
+ : (object ??= document.toObject(bsonOptions)),
531
542
  started,
532
543
  this.description.serverConnectionId
533
544
  )
@@ -647,6 +658,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
647
658
  private async *readMany(): AsyncGenerator<OpMsgResponse | OpReply> {
648
659
  try {
649
660
  this.dataEvents = onData(this.messageStream);
661
+ this.messageStream.resume();
650
662
  for await (const message of this.dataEvents) {
651
663
  const response = await decompressResponse(message);
652
664
  yield response;
@@ -657,6 +669,7 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
657
669
  }
658
670
  } finally {
659
671
  this.dataEvents = null;
672
+ this.messageStream.pause();
660
673
  this.throwIfAborted();
661
674
  }
662
675
  }
@@ -1,6 +1,6 @@
1
- export const MIN_SUPPORTED_SERVER_VERSION = '3.6';
1
+ export const MIN_SUPPORTED_SERVER_VERSION = '4.0';
2
2
  export const MAX_SUPPORTED_SERVER_VERSION = '8.0';
3
- export const MIN_SUPPORTED_WIRE_VERSION = 6;
3
+ export const MIN_SUPPORTED_WIRE_VERSION = 7;
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';
@@ -1,11 +1,10 @@
1
- import { type DeserializeOptions } from 'bson';
2
-
3
1
  import {
4
2
  Binary,
5
3
  type BSONElement,
6
4
  BSONError,
7
5
  BSONType,
8
6
  deserialize,
7
+ type DeserializeOptions,
9
8
  getBigInt64LE,
10
9
  getFloat64LE,
11
10
  getInt32LE,