mongodb 6.5.0 → 6.6.0-dev.20240504.sha.2609953

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 (207) hide show
  1. package/README.md +9 -9
  2. package/lib/admin.js +9 -9
  3. package/lib/admin.js.map +1 -1
  4. package/lib/bson.js +33 -22
  5. package/lib/bson.js.map +1 -1
  6. package/lib/bulk/common.js +13 -12
  7. package/lib/bulk/common.js.map +1 -1
  8. package/lib/change_stream.js +28 -18
  9. package/lib/change_stream.js.map +1 -1
  10. package/lib/client-side-encryption/auto_encrypter.js +2 -2
  11. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  12. package/lib/client-side-encryption/client_encryption.js +6 -6
  13. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  14. package/lib/client-side-encryption/providers/aws.js +13 -10
  15. package/lib/client-side-encryption/providers/aws.js.map +1 -1
  16. package/lib/client-side-encryption/providers/azure.js +6 -3
  17. package/lib/client-side-encryption/providers/azure.js.map +1 -1
  18. package/lib/cmap/auth/aws_temporary_credentials.js +140 -0
  19. package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -0
  20. package/lib/cmap/auth/gssapi.js +7 -6
  21. package/lib/cmap/auth/gssapi.js.map +1 -1
  22. package/lib/cmap/auth/mongodb_aws.js +8 -101
  23. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  24. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js +1 -1
  25. package/lib/cmap/auth/mongodb_oidc/aws_service_workflow.js.map +1 -1
  26. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js +2 -1
  27. package/lib/cmap/auth/mongodb_oidc/callback_lock_cache.js.map +1 -1
  28. package/lib/cmap/auth/mongodb_oidc/service_workflow.js +1 -1
  29. package/lib/cmap/auth/mongodb_oidc/service_workflow.js.map +1 -1
  30. package/lib/cmap/auth/scram.js +2 -2
  31. package/lib/cmap/auth/scram.js.map +1 -1
  32. package/lib/cmap/commands.js +24 -111
  33. package/lib/cmap/commands.js.map +1 -1
  34. package/lib/cmap/connect.js +4 -4
  35. package/lib/cmap/connect.js.map +1 -1
  36. package/lib/cmap/connection.js +61 -36
  37. package/lib/cmap/connection.js.map +1 -1
  38. package/lib/cmap/connection_pool.js +22 -13
  39. package/lib/cmap/connection_pool.js.map +1 -1
  40. package/lib/cmap/handshake/client_metadata.js +2 -2
  41. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  42. package/lib/cmap/wire_protocol/compression.js +8 -8
  43. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  44. package/lib/cmap/wire_protocol/on_demand/document.js +218 -0
  45. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -0
  46. package/lib/cmap/wire_protocol/responses.js +184 -0
  47. package/lib/cmap/wire_protocol/responses.js.map +1 -0
  48. package/lib/collection.js +42 -38
  49. package/lib/collection.js.map +1 -1
  50. package/lib/connection_string.js +4 -6
  51. package/lib/connection_string.js.map +1 -1
  52. package/lib/cursor/abstract_cursor.js +76 -43
  53. package/lib/cursor/abstract_cursor.js.map +1 -1
  54. package/lib/cursor/aggregation_cursor.js +16 -33
  55. package/lib/cursor/aggregation_cursor.js.map +1 -1
  56. package/lib/cursor/find_cursor.js +36 -18
  57. package/lib/cursor/find_cursor.js.map +1 -1
  58. package/lib/cursor/run_command_cursor.js +3 -2
  59. package/lib/cursor/run_command_cursor.js.map +1 -1
  60. package/lib/db.js +15 -19
  61. package/lib/db.js.map +1 -1
  62. package/lib/deps.js +31 -26
  63. package/lib/deps.js.map +1 -1
  64. package/lib/encrypter.js +14 -5
  65. package/lib/encrypter.js.map +1 -1
  66. package/lib/error.js +4 -3
  67. package/lib/error.js.map +1 -1
  68. package/lib/gridfs/download.js +19 -14
  69. package/lib/gridfs/download.js.map +1 -1
  70. package/lib/gridfs/index.js.map +1 -1
  71. package/lib/gridfs/upload.js +6 -1
  72. package/lib/gridfs/upload.js.map +1 -1
  73. package/lib/index.js.map +1 -1
  74. package/lib/mongo_client.js +11 -7
  75. package/lib/mongo_client.js.map +1 -1
  76. package/lib/mongo_logger.js +3 -0
  77. package/lib/mongo_logger.js.map +1 -1
  78. package/lib/operations/aggregate.js +2 -1
  79. package/lib/operations/aggregate.js.map +1 -1
  80. package/lib/operations/command.js +1 -1
  81. package/lib/operations/command.js.map +1 -1
  82. package/lib/operations/create_collection.js +1 -1
  83. package/lib/operations/create_collection.js.map +1 -1
  84. package/lib/operations/delete.js +4 -3
  85. package/lib/operations/delete.js.map +1 -1
  86. package/lib/operations/drop.js +1 -1
  87. package/lib/operations/drop.js.map +1 -1
  88. package/lib/operations/execute_operation.js +23 -8
  89. package/lib/operations/execute_operation.js.map +1 -1
  90. package/lib/operations/find.js +4 -4
  91. package/lib/operations/find.js.map +1 -1
  92. package/lib/operations/get_more.js +2 -1
  93. package/lib/operations/get_more.js.map +1 -1
  94. package/lib/operations/indexes.js +29 -121
  95. package/lib/operations/indexes.js.map +1 -1
  96. package/lib/operations/insert.js +3 -3
  97. package/lib/operations/insert.js.map +1 -1
  98. package/lib/operations/kill_cursors.js +3 -1
  99. package/lib/operations/kill_cursors.js.map +1 -1
  100. package/lib/operations/list_collections.js +1 -1
  101. package/lib/operations/list_collections.js.map +1 -1
  102. package/lib/operations/list_databases.js +1 -1
  103. package/lib/operations/list_databases.js.map +1 -1
  104. package/lib/operations/operation.js.map +1 -1
  105. package/lib/operations/run_command.js +4 -2
  106. package/lib/operations/run_command.js.map +1 -1
  107. package/lib/operations/search_indexes/create.js.map +1 -1
  108. package/lib/operations/stats.js +1 -1
  109. package/lib/operations/stats.js.map +1 -1
  110. package/lib/operations/update.js +1 -1
  111. package/lib/operations/update.js.map +1 -1
  112. package/lib/sdam/common.js.map +1 -1
  113. package/lib/sdam/monitor.js +139 -42
  114. package/lib/sdam/monitor.js.map +1 -1
  115. package/lib/sdam/server.js +5 -15
  116. package/lib/sdam/server.js.map +1 -1
  117. package/lib/sdam/server_description.js +1 -0
  118. package/lib/sdam/server_description.js.map +1 -1
  119. package/lib/sdam/server_selection.js +1 -1
  120. package/lib/sdam/server_selection.js.map +1 -1
  121. package/lib/sdam/srv_polling.js +2 -1
  122. package/lib/sdam/srv_polling.js.map +1 -1
  123. package/lib/sdam/topology.js +67 -54
  124. package/lib/sdam/topology.js.map +1 -1
  125. package/lib/sdam/topology_description.js +10 -0
  126. package/lib/sdam/topology_description.js.map +1 -1
  127. package/lib/sessions.js +133 -93
  128. package/lib/sessions.js.map +1 -1
  129. package/lib/timeout.js +77 -0
  130. package/lib/timeout.js.map +1 -0
  131. package/lib/utils.js +61 -28
  132. package/lib/utils.js.map +1 -1
  133. package/mongodb.d.ts +150 -38
  134. package/package.json +17 -14
  135. package/src/admin.ts +9 -9
  136. package/src/bson.ts +14 -0
  137. package/src/bulk/common.ts +3 -2
  138. package/src/change_stream.ts +39 -30
  139. package/src/client-side-encryption/auto_encrypter.ts +2 -2
  140. package/src/client-side-encryption/client_encryption.ts +6 -6
  141. package/src/client-side-encryption/providers/aws.ts +17 -10
  142. package/src/client-side-encryption/providers/azure.ts +5 -3
  143. package/src/cmap/auth/aws_temporary_credentials.ts +169 -0
  144. package/src/cmap/auth/gssapi.ts +9 -11
  145. package/src/cmap/auth/mongodb_aws.ts +19 -126
  146. package/src/cmap/auth/mongodb_oidc/aws_service_workflow.ts +1 -1
  147. package/src/cmap/auth/mongodb_oidc/callback_lock_cache.ts +2 -1
  148. package/src/cmap/auth/mongodb_oidc/service_workflow.ts +1 -1
  149. package/src/cmap/auth/scram.ts +2 -2
  150. package/src/cmap/commands.ts +28 -132
  151. package/src/cmap/connect.ts +4 -4
  152. package/src/cmap/connection.ts +107 -43
  153. package/src/cmap/connection_pool.ts +32 -29
  154. package/src/cmap/handshake/client_metadata.ts +2 -5
  155. package/src/cmap/wire_protocol/compression.ts +11 -13
  156. package/src/cmap/wire_protocol/on_demand/document.ts +338 -0
  157. package/src/cmap/wire_protocol/responses.ts +237 -0
  158. package/src/collection.ts +87 -58
  159. package/src/connection_string.ts +9 -7
  160. package/src/cursor/abstract_cursor.ts +102 -38
  161. package/src/cursor/aggregation_cursor.ts +32 -34
  162. package/src/cursor/find_cursor.ts +33 -21
  163. package/src/cursor/list_search_indexes_cursor.ts +1 -1
  164. package/src/cursor/run_command_cursor.ts +3 -2
  165. package/src/db.ts +42 -21
  166. package/src/deps.ts +52 -40
  167. package/src/encrypter.ts +14 -5
  168. package/src/error.ts +9 -3
  169. package/src/gridfs/download.ts +19 -31
  170. package/src/gridfs/index.ts +2 -0
  171. package/src/gridfs/upload.ts +11 -8
  172. package/src/index.ts +13 -5
  173. package/src/mongo_client.ts +21 -15
  174. package/src/mongo_logger.ts +3 -0
  175. package/src/mongo_types.ts +1 -1
  176. package/src/operations/aggregate.ts +2 -1
  177. package/src/operations/command.ts +1 -1
  178. package/src/operations/create_collection.ts +7 -2
  179. package/src/operations/delete.ts +4 -3
  180. package/src/operations/drop.ts +1 -1
  181. package/src/operations/execute_operation.ts +29 -10
  182. package/src/operations/find.ts +13 -14
  183. package/src/operations/get_more.ts +9 -1
  184. package/src/operations/indexes.ts +103 -176
  185. package/src/operations/insert.ts +2 -2
  186. package/src/operations/kill_cursors.ts +3 -2
  187. package/src/operations/list_collections.ts +5 -1
  188. package/src/operations/list_databases.ts +1 -1
  189. package/src/operations/operation.ts +3 -0
  190. package/src/operations/run_command.ts +6 -4
  191. package/src/operations/search_indexes/create.ts +4 -1
  192. package/src/operations/stats.ts +1 -1
  193. package/src/operations/update.ts +7 -7
  194. package/src/sdam/common.ts +8 -2
  195. package/src/sdam/monitor.ts +178 -61
  196. package/src/sdam/server.ts +27 -20
  197. package/src/sdam/server_description.ts +8 -3
  198. package/src/sdam/server_selection.ts +2 -3
  199. package/src/sdam/srv_polling.ts +3 -2
  200. package/src/sdam/topology.ts +114 -117
  201. package/src/sdam/topology_description.ts +14 -4
  202. package/src/sessions.ts +168 -148
  203. package/src/timeout.ts +96 -0
  204. package/src/utils.ts +85 -32
  205. package/lib/operations/common_functions.js +0 -38
  206. package/lib/operations/common_functions.js.map +0 -1
  207. package/src/operations/common_functions.ts +0 -79
@@ -1,6 +1,7 @@
1
1
  import { Readable, Transform } from 'stream';
2
2
 
3
3
  import { type BSONSerializeOptions, type Document, Long, pluckBSONSerializeOptions } from '../bson';
4
+ import { CursorResponse } from '../cmap/wire_protocol/responses';
4
5
  import {
5
6
  type AnyError,
6
7
  MongoAPIError,
@@ -20,7 +21,7 @@ import { ReadConcern, type ReadConcernLike } from '../read_concern';
20
21
  import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
21
22
  import type { Server } from '../sdam/server';
22
23
  import { ClientSession, maybeClearPinnedConnection } from '../sessions';
23
- import { List, type MongoDBNamespace, ns } from '../utils';
24
+ import { List, type MongoDBNamespace, ns, squashError } from '../utils';
24
25
 
25
26
  /** @internal */
26
27
  const kId = Symbol('id');
@@ -109,6 +110,8 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
109
110
  */
110
111
  awaitData?: boolean;
111
112
  noCursorTimeout?: boolean;
113
+ /** @internal TODO(NODE-5688): make this public */
114
+ timeoutMS?: number;
112
115
  }
113
116
 
114
117
  /** @internal */
@@ -142,7 +145,13 @@ export abstract class AbstractCursor<
142
145
  /** @internal */
143
146
  [kNamespace]: MongoDBNamespace;
144
147
  /** @internal */
145
- [kDocuments]: List<TSchema>;
148
+ [kDocuments]: {
149
+ length: number;
150
+ shift(bsonOptions?: any): TSchema | null;
151
+ clear(): void;
152
+ pushMany(many: Iterable<TSchema>): void;
153
+ push(item: TSchema): void;
154
+ };
146
155
  /** @internal */
147
156
  [kClient]: MongoClient;
148
157
  /** @internal */
@@ -184,6 +193,7 @@ export abstract class AbstractCursor<
184
193
  : ReadPreference.primary,
185
194
  ...pluckBSONSerializeOptions(options)
186
195
  };
196
+ this[kOptions].timeoutMS = options.timeoutMS;
187
197
 
188
198
  const readConcern = ReadConcern.fromOptions(options);
189
199
  if (readConcern) {
@@ -283,7 +293,7 @@ export abstract class AbstractCursor<
283
293
  const documentsToRead = Math.min(number ?? this[kDocuments].length, this[kDocuments].length);
284
294
 
285
295
  for (let count = 0; count < documentsToRead; count++) {
286
- const document = this[kDocuments].shift();
296
+ const document = this[kDocuments].shift(this[kOptions]);
287
297
  if (document != null) {
288
298
  bufferedDocs.push(document);
289
299
  }
@@ -309,7 +319,11 @@ export abstract class AbstractCursor<
309
319
  const message =
310
320
  'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
311
321
 
312
- await cleanupCursor(this, { needsToEmitClosed: true }).catch(() => null);
322
+ try {
323
+ await cleanupCursor(this, { needsToEmitClosed: true });
324
+ } catch (error) {
325
+ squashError(error);
326
+ }
313
327
 
314
328
  throw new MongoAPIError(message);
315
329
  }
@@ -327,7 +341,11 @@ export abstract class AbstractCursor<
327
341
  // Only close the cursor if it has not already been closed. This finally clause handles
328
342
  // the case when a user would break out of a for await of loop early.
329
343
  if (!this.closed) {
330
- await this.close().catch(() => null);
344
+ try {
345
+ await this.close();
346
+ } catch (error) {
347
+ squashError(error);
348
+ }
331
349
  }
332
350
  }
333
351
  }
@@ -371,14 +389,7 @@ export abstract class AbstractCursor<
371
389
  return true;
372
390
  }
373
391
 
374
- const doc = await next<TSchema>(this, { blocking: true, transform: false });
375
-
376
- if (doc) {
377
- this[kDocuments].unshift(doc);
378
- return true;
379
- }
380
-
381
- return false;
392
+ return await next(this, { blocking: true, transform: false, shift: false });
382
393
  }
383
394
 
384
395
  /** Get the next available document from the cursor, returns null if no more documents are available. */
@@ -387,7 +398,7 @@ export abstract class AbstractCursor<
387
398
  throw new MongoCursorExhaustedError();
388
399
  }
389
400
 
390
- return next(this, { blocking: true, transform: true });
401
+ return await next(this, { blocking: true, transform: true, shift: true });
391
402
  }
392
403
 
393
404
  /**
@@ -398,7 +409,7 @@ export abstract class AbstractCursor<
398
409
  throw new MongoCursorExhaustedError();
399
410
  }
400
411
 
401
- return next(this, { blocking: false, transform: true });
412
+ return await next(this, { blocking: false, transform: true, shift: true });
402
413
  }
403
414
 
404
415
  /**
@@ -605,7 +616,8 @@ export abstract class AbstractCursor<
605
616
  // We only want to end this session if we created it, and it hasn't ended yet
606
617
  if (session.explicit === false) {
607
618
  if (!session.hasEnded) {
608
- session.endSession().catch(() => null);
619
+ // eslint-disable-next-line github/no-then
620
+ session.endSession().then(undefined, squashError);
609
621
  }
610
622
  this[kSession] = this.client.startSession({ owner: this, explicit: false });
611
623
  }
@@ -621,15 +633,16 @@ export abstract class AbstractCursor<
621
633
  protected abstract _initialize(session: ClientSession | undefined): Promise<ExecutionResult>;
622
634
 
623
635
  /** @internal */
624
- async getMore(batchSize: number): Promise<Document | null> {
636
+ async getMore(batchSize: number, useCursorResponse = false): Promise<Document | null> {
625
637
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
626
638
  const getMoreOperation = new GetMoreOperation(this[kNamespace], this[kId]!, this[kServer]!, {
627
639
  ...this[kOptions],
628
640
  session: this[kSession],
629
- batchSize
641
+ batchSize,
642
+ useCursorResponse
630
643
  });
631
644
 
632
- return executeOperation(this[kClient], getMoreOperation);
645
+ return await executeOperation(this[kClient], getMoreOperation);
633
646
  }
634
647
 
635
648
  /**
@@ -644,7 +657,11 @@ export abstract class AbstractCursor<
644
657
  const state = await this._initialize(this[kSession]);
645
658
  const response = state.response;
646
659
  this[kServer] = state.server;
647
- if (response.cursor) {
660
+ if (CursorResponse.is(response)) {
661
+ this[kId] = response.id;
662
+ if (response.ns) this[kNamespace] = response.ns;
663
+ this[kDocuments] = response;
664
+ } else if (response.cursor) {
648
665
  // TODO(NODE-2674): Preserve int64 sent from MongoDB
649
666
  this[kId] =
650
667
  typeof response.cursor.id === 'number'
@@ -701,13 +718,42 @@ async function next<T>(
701
718
  cursor: AbstractCursor<T>,
702
719
  {
703
720
  blocking,
704
- transform
721
+ transform,
722
+ shift
723
+ }: {
724
+ blocking: boolean;
725
+ transform: boolean;
726
+ shift: false;
727
+ }
728
+ ): Promise<boolean>;
729
+
730
+ async function next<T>(
731
+ cursor: AbstractCursor<T>,
732
+ {
733
+ blocking,
734
+ transform,
735
+ shift
736
+ }: {
737
+ blocking: boolean;
738
+ transform: boolean;
739
+ shift: true;
740
+ }
741
+ ): Promise<T | null>;
742
+
743
+ async function next<T>(
744
+ cursor: AbstractCursor<T>,
745
+ {
746
+ blocking,
747
+ transform,
748
+ shift
705
749
  }: {
706
750
  blocking: boolean;
707
751
  transform: boolean;
752
+ shift: boolean;
708
753
  }
709
- ): Promise<T | null> {
754
+ ): Promise<boolean | T | null> {
710
755
  if (cursor.closed) {
756
+ if (!shift) return false;
711
757
  return null;
712
758
  }
713
759
 
@@ -718,15 +764,19 @@ async function next<T>(
718
764
  }
719
765
 
720
766
  if (cursor[kDocuments].length !== 0) {
721
- const doc = cursor[kDocuments].shift();
767
+ if (!shift) return true;
768
+ const doc = cursor[kDocuments].shift(cursor[kOptions]);
722
769
 
723
770
  if (doc != null && transform && cursor[kTransform]) {
724
771
  try {
725
772
  return cursor[kTransform](doc);
726
773
  } catch (error) {
727
- // `cleanupCursorAsync` should never throw, but if it does we want to throw the original
728
- // error instead.
729
- await cleanupCursor(cursor, { error, needsToEmitClosed: true }).catch(() => null);
774
+ try {
775
+ await cleanupCursor(cursor, { error, needsToEmitClosed: true });
776
+ } catch (error) {
777
+ // `cleanupCursor` should never throw, squash and throw the original error
778
+ squashError(error);
779
+ }
730
780
  throw error;
731
781
  }
732
782
  }
@@ -736,9 +786,10 @@ async function next<T>(
736
786
 
737
787
  if (cursor.isDead) {
738
788
  // if the cursor is dead, we clean it up
739
- // cleanupCursorAsync should never throw, but if it does it indicates a bug in the driver
789
+ // cleanupCursor should never throw, but if it does it indicates a bug in the driver
740
790
  // and we should surface the error
741
791
  await cleanupCursor(cursor, {});
792
+ if (!shift) return false;
742
793
  return null;
743
794
  }
744
795
 
@@ -747,8 +798,10 @@ async function next<T>(
747
798
 
748
799
  try {
749
800
  const response = await cursor.getMore(batchSize);
750
-
751
- if (response) {
801
+ if (CursorResponse.is(response)) {
802
+ cursor[kId] = response.id;
803
+ cursor[kDocuments] = response;
804
+ } else if (response) {
752
805
  const cursorId =
753
806
  typeof response.cursor.id === 'number'
754
807
  ? Long.fromNumber(response.cursor.id)
@@ -760,9 +813,12 @@ async function next<T>(
760
813
  cursor[kId] = cursorId;
761
814
  }
762
815
  } catch (error) {
763
- // `cleanupCursorAsync` should never throw, but if it does we want to throw the original
764
- // error instead.
765
- await cleanupCursor(cursor, { error }).catch(() => null);
816
+ try {
817
+ await cleanupCursor(cursor, { error, needsToEmitClosed: true });
818
+ } catch (error) {
819
+ // `cleanupCursor` should never throw, squash and throw the original error
820
+ squashError(error);
821
+ }
766
822
  throw error;
767
823
  }
768
824
 
@@ -778,10 +834,12 @@ async function next<T>(
778
834
  }
779
835
 
780
836
  if (cursor[kDocuments].length === 0 && blocking === false) {
837
+ if (!shift) return false;
781
838
  return null;
782
839
  }
783
840
  } while (!cursor.isDead || cursor[kDocuments].length !== 0);
784
841
 
842
+ if (!shift) return false;
785
843
  return null;
786
844
  }
787
845
 
@@ -802,7 +860,7 @@ async function cleanupCursor(
802
860
 
803
861
  if (error) {
804
862
  if (cursor.loadBalanced && error instanceof MongoNetworkError) {
805
- return completeCleanup();
863
+ return await completeCleanup();
806
864
  }
807
865
  }
808
866
 
@@ -850,14 +908,16 @@ async function cleanupCursor(
850
908
  cursor[kKilled] = true;
851
909
 
852
910
  if (session.hasEnded) {
853
- return completeCleanup();
911
+ return await completeCleanup();
854
912
  }
855
913
 
856
914
  try {
857
915
  await executeOperation(
858
916
  cursor[kClient],
859
917
  new KillCursorsOperation(cursorId, cursorNs, server, { session })
860
- ).catch(() => null);
918
+ );
919
+ } catch (error) {
920
+ squashError(error);
861
921
  } finally {
862
922
  await completeCleanup();
863
923
  }
@@ -892,6 +952,7 @@ class ReadableCursorStream extends Readable {
892
952
  }
893
953
 
894
954
  override _destroy(error: Error | null, callback: (error?: Error | null) => void): void {
955
+ // eslint-disable-next-line github/no-then
895
956
  this._cursor.close().then(
896
957
  () => callback(error),
897
958
  closeError => callback(closeError)
@@ -899,12 +960,14 @@ class ReadableCursorStream extends Readable {
899
960
  }
900
961
 
901
962
  private _readNext() {
902
- next(this._cursor, { blocking: true, transform: true }).then(
963
+ // eslint-disable-next-line github/no-then
964
+ next(this._cursor, { blocking: true, transform: true, shift: true }).then(
903
965
  result => {
904
966
  if (result == null) {
905
967
  this.push(null);
906
968
  } else if (this.destroyed) {
907
- this._cursor.close().catch(() => null);
969
+ // eslint-disable-next-line github/no-then
970
+ this._cursor.close().then(undefined, squashError);
908
971
  } else {
909
972
  if (this.push(result)) {
910
973
  return this._readNext();
@@ -919,7 +982,8 @@ class ReadableCursorStream extends Readable {
919
982
  // a client during iteration. Alternatively, we could do the "right" thing and
920
983
  // propagate the error message by removing this special case.
921
984
  if (err.message.match(/server is closed/)) {
922
- this._cursor.close().catch(() => null);
985
+ // eslint-disable-next-line github/no-then
986
+ this._cursor.close().then(undefined, squashError);
923
987
  return this.push(null);
924
988
  }
925
989
 
@@ -76,7 +76,7 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
76
76
 
77
77
  /** Execute the explain for the cursor */
78
78
  async explain(verbosity?: ExplainVerbosityLike): Promise<Document> {
79
- return executeOperation(
79
+ return await executeOperation(
80
80
  this.client,
81
81
  new AggregateOperation(this.namespace, this[kPipeline], {
82
82
  ...this[kOptions], // NOTE: order matters here, we may need to refine this
@@ -86,33 +86,45 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
86
86
  );
87
87
  }
88
88
 
89
+ /** Add a stage to the aggregation pipeline
90
+ * @example
91
+ * ```
92
+ * const documents = await users.aggregate().addStage({ $match: { name: /Mike/ } }).toArray();
93
+ * ```
94
+ * @example
95
+ * ```
96
+ * const documents = await users.aggregate()
97
+ * .addStage<{ name: string }>({ $project: { name: true } })
98
+ * .toArray(); // type of documents is { name: string }[]
99
+ * ```
100
+ */
101
+ addStage(stage: Document): this;
102
+ addStage<T = Document>(stage: Document): AggregationCursor<T>;
103
+ addStage<T = Document>(stage: Document): AggregationCursor<T> {
104
+ assertUninitialized(this);
105
+ this[kPipeline].push(stage);
106
+ return this as unknown as AggregationCursor<T>;
107
+ }
108
+
89
109
  /** Add a group stage to the aggregation pipeline */
90
110
  group<T = TSchema>($group: Document): AggregationCursor<T>;
91
111
  group($group: Document): this {
92
- assertUninitialized(this);
93
- this[kPipeline].push({ $group });
94
- return this;
112
+ return this.addStage({ $group });
95
113
  }
96
114
 
97
115
  /** Add a limit stage to the aggregation pipeline */
98
116
  limit($limit: number): this {
99
- assertUninitialized(this);
100
- this[kPipeline].push({ $limit });
101
- return this;
117
+ return this.addStage({ $limit });
102
118
  }
103
119
 
104
120
  /** Add a match stage to the aggregation pipeline */
105
121
  match($match: Document): this {
106
- assertUninitialized(this);
107
- this[kPipeline].push({ $match });
108
- return this;
122
+ return this.addStage({ $match });
109
123
  }
110
124
 
111
125
  /** Add an out stage to the aggregation pipeline */
112
126
  out($out: { db: string; coll: string } | string): this {
113
- assertUninitialized(this);
114
- this[kPipeline].push({ $out });
115
- return this;
127
+ return this.addStage({ $out });
116
128
  }
117
129
 
118
130
  /**
@@ -157,50 +169,36 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
157
169
  * ```
158
170
  */
159
171
  project<T extends Document = Document>($project: Document): AggregationCursor<T> {
160
- assertUninitialized(this);
161
- this[kPipeline].push({ $project });
162
- return this as unknown as AggregationCursor<T>;
172
+ return this.addStage<T>({ $project });
163
173
  }
164
174
 
165
175
  /** Add a lookup stage to the aggregation pipeline */
166
176
  lookup($lookup: Document): this {
167
- assertUninitialized(this);
168
- this[kPipeline].push({ $lookup });
169
- return this;
177
+ return this.addStage({ $lookup });
170
178
  }
171
179
 
172
180
  /** Add a redact stage to the aggregation pipeline */
173
181
  redact($redact: Document): this {
174
- assertUninitialized(this);
175
- this[kPipeline].push({ $redact });
176
- return this;
182
+ return this.addStage({ $redact });
177
183
  }
178
184
 
179
185
  /** Add a skip stage to the aggregation pipeline */
180
186
  skip($skip: number): this {
181
- assertUninitialized(this);
182
- this[kPipeline].push({ $skip });
183
- return this;
187
+ return this.addStage({ $skip });
184
188
  }
185
189
 
186
190
  /** Add a sort stage to the aggregation pipeline */
187
191
  sort($sort: Sort): this {
188
- assertUninitialized(this);
189
- this[kPipeline].push({ $sort });
190
- return this;
192
+ return this.addStage({ $sort });
191
193
  }
192
194
 
193
195
  /** Add a unwind stage to the aggregation pipeline */
194
196
  unwind($unwind: Document | string): this {
195
- assertUninitialized(this);
196
- this[kPipeline].push({ $unwind });
197
- return this;
197
+ return this.addStage({ $unwind });
198
198
  }
199
199
 
200
200
  /** Add a geoNear stage to the aggregation pipeline */
201
201
  geoNear($geoNear: Document): this {
202
- assertUninitialized(this);
203
- this[kPipeline].push({ $geoNear });
204
- return this;
202
+ return this.addStage({ $geoNear });
205
203
  }
206
204
  }
@@ -1,4 +1,5 @@
1
- import { type Document, Long } from '../bson';
1
+ import { type Document } from '../bson';
2
+ import { CursorResponse } from '../cmap/wire_protocol/responses';
2
3
  import { MongoInvalidArgumentError, MongoTailableCursorError } from '../error';
3
4
  import { type ExplainVerbosityLike } from '../explain';
4
5
  import type { MongoClient } from '../mongo_client';
@@ -9,7 +10,7 @@ import { FindOperation, type FindOptions } from '../operations/find';
9
10
  import type { Hint } from '../operations/operation';
10
11
  import type { ClientSession } from '../sessions';
11
12
  import { formatSort, type Sort, type SortDirection } from '../sort';
12
- import { emitWarningOnce, mergeOptions, type MongoDBNamespace } from '../utils';
13
+ import { emitWarningOnce, mergeOptions, type MongoDBNamespace, squashError } from '../utils';
13
14
  import { AbstractCursor, assertUninitialized } from './abstract_cursor';
14
15
 
15
16
  /** @internal */
@@ -34,7 +35,7 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
34
35
  /** @internal */
35
36
  [kFilter]: Document;
36
37
  /** @internal */
37
- [kNumReturned]?: number;
38
+ [kNumReturned] = 0;
38
39
  /** @internal */
39
40
  [kBuiltOptions]: FindOptions;
40
41
 
@@ -69,7 +70,7 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
69
70
 
70
71
  /** @internal */
71
72
  async _initialize(session: ClientSession): Promise<ExecutionResult> {
72
- const findOperation = new FindOperation(undefined, this.namespace, this[kFilter], {
73
+ const findOperation = new FindOperation(this.namespace, this[kFilter], {
73
74
  ...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this
74
75
  ...this.cursorOptions,
75
76
  session
@@ -78,7 +79,12 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
78
79
  const response = await executeOperation(this.client, findOperation);
79
80
 
80
81
  // the response is not a cursor when `explain` is enabled
81
- this[kNumReturned] = response.cursor?.firstBatch?.length;
82
+ if (CursorResponse.is(response)) {
83
+ this[kNumReturned] = response.batchSize;
84
+ } else {
85
+ // Can be an explain response, hence the ?. on everything
86
+ this[kNumReturned] = this[kNumReturned] + (response?.cursor?.firstBatch?.length ?? 0);
87
+ }
82
88
 
83
89
  // TODO: NODE-2882
84
90
  return { server: findOperation.server, session, response };
@@ -94,23 +100,29 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
94
100
  limit && limit > 0 && numReturned + batchSize > limit ? limit - numReturned : batchSize;
95
101
 
96
102
  if (batchSize <= 0) {
97
- // this is an optimization for the special case of a limit for a find command to avoid an
98
- // extra getMore when the limit has been reached and the limit is a multiple of the batchSize.
99
- // This is a consequence of the new query engine in 5.0 having no knowledge of the limit as it
100
- // produces results for the find command. Once a batch is filled up, it is returned and only
101
- // on the subsequent getMore will the query framework consider the limit, determine the cursor
102
- // is exhausted and return a cursorId of zero.
103
- // instead, if we determine there are no more documents to request from the server, we preemptively
104
- // close the cursor
105
- await this.close().catch(() => null);
106
- return { cursor: { id: Long.ZERO, nextBatch: [] } };
103
+ try {
104
+ await this.close();
105
+ } catch (error) {
106
+ squashError(error);
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
+ }
116
+ return CursorResponse.emptyGetMore;
107
117
  }
108
118
  }
109
119
 
110
- const response = await super.getMore(batchSize);
120
+ const response = await super.getMore(batchSize, false);
111
121
  // TODO: wrap this in some logic to prevent it from happening if we don't need this support
112
- if (response) {
113
- this[kNumReturned] = this[kNumReturned] + response.cursor.nextBatch.length;
122
+ if (CursorResponse.is(response)) {
123
+ this[kNumReturned] = this[kNumReturned] + response.batchSize;
124
+ } else {
125
+ this[kNumReturned] = this[kNumReturned] + (response?.cursor?.nextBatch?.length ?? 0);
114
126
  }
115
127
 
116
128
  return response;
@@ -127,7 +139,7 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
127
139
  if (typeof options === 'boolean') {
128
140
  throw new MongoInvalidArgumentError('Invalid first parameter to count');
129
141
  }
130
- return executeOperation(
142
+ return await executeOperation(
131
143
  this.client,
132
144
  new CountOperation(this.namespace, this[kFilter], {
133
145
  ...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this
@@ -139,9 +151,9 @@ export class FindCursor<TSchema = any> extends AbstractCursor<TSchema> {
139
151
 
140
152
  /** Execute the explain for the cursor */
141
153
  async explain(verbosity?: ExplainVerbosityLike): Promise<Document> {
142
- return executeOperation(
154
+ return await executeOperation(
143
155
  this.client,
144
- new FindOperation(undefined, this.namespace, this[kFilter], {
156
+ new FindOperation(this.namespace, this[kFilter], {
145
157
  ...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this
146
158
  ...this.cursorOptions,
147
159
  explain: verbosity ?? true
@@ -3,7 +3,7 @@ import type { AggregateOptions } from '../operations/aggregate';
3
3
  import { AggregationCursor } from './aggregation_cursor';
4
4
 
5
5
  /** @public */
6
- export type ListSearchIndexesOptions = AggregateOptions;
6
+ export type ListSearchIndexesOptions = Omit<AggregateOptions, 'readConcern' | 'writeConcern'>;
7
7
 
8
8
  /** @public */
9
9
  export class ListSearchIndexesCursor extends AggregationCursor<{ name: string }> {
@@ -125,9 +125,10 @@ export class RunCommandCursor extends AbstractCursor {
125
125
  const getMoreOperation = new GetMoreOperation(this.namespace, this.id!, this.server!, {
126
126
  ...this.cursorOptions,
127
127
  session: this.session,
128
- ...this.getMoreOptions
128
+ ...this.getMoreOptions,
129
+ useCursorResponse: false
129
130
  });
130
131
 
131
- return executeOperation(this.client, getMoreOperation);
132
+ return await executeOperation(this.client, getMoreOperation);
132
133
  }
133
134
  }