mongodb 6.8.0-dev.20240905.sha.65e0e15c → 6.8.1

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 (246) hide show
  1. package/README.md +1 -14
  2. package/lib/bson.js +13 -4
  3. package/lib/bson.js.map +1 -1
  4. package/lib/bulk/common.js +21 -16
  5. package/lib/bulk/common.js.map +1 -1
  6. package/lib/bulk/ordered.js.map +1 -1
  7. package/lib/bulk/unordered.js.map +1 -1
  8. package/lib/change_stream.js +8 -10
  9. package/lib/change_stream.js.map +1 -1
  10. package/lib/client-side-encryption/auto_encrypter.js +3 -14
  11. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  12. package/lib/client-side-encryption/client_encryption.js +7 -25
  13. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  14. package/lib/client-side-encryption/crypto_callbacks.js +6 -6
  15. package/lib/client-side-encryption/crypto_callbacks.js.map +1 -1
  16. package/lib/client-side-encryption/mongocryptd_manager.js +5 -9
  17. package/lib/client-side-encryption/mongocryptd_manager.js.map +1 -1
  18. package/lib/client-side-encryption/providers/aws.js +2 -1
  19. package/lib/client-side-encryption/providers/aws.js.map +1 -1
  20. package/lib/client-side-encryption/providers/azure.js +5 -5
  21. package/lib/client-side-encryption/providers/azure.js.map +1 -1
  22. package/lib/client-side-encryption/providers/gcp.js +2 -1
  23. package/lib/client-side-encryption/providers/gcp.js.map +1 -1
  24. package/lib/client-side-encryption/providers/index.js +3 -2
  25. package/lib/client-side-encryption/providers/index.js.map +1 -1
  26. package/lib/client-side-encryption/state_machine.js +4 -9
  27. package/lib/client-side-encryption/state_machine.js.map +1 -1
  28. package/lib/cmap/auth/auth_provider.js.map +1 -1
  29. package/lib/cmap/auth/aws_temporary_credentials.js.map +1 -1
  30. package/lib/cmap/auth/gssapi.js +4 -4
  31. package/lib/cmap/auth/gssapi.js.map +1 -1
  32. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  33. package/lib/cmap/auth/mongocr.js.map +1 -1
  34. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  35. package/lib/cmap/auth/mongodb_oidc/automated_callback_workflow.js.map +1 -1
  36. package/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js.map +1 -1
  37. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js +2 -0
  38. package/lib/cmap/auth/mongodb_oidc/callback_workflow.js.map +1 -1
  39. package/lib/cmap/auth/mongodb_oidc/command_builders.js +3 -2
  40. package/lib/cmap/auth/mongodb_oidc/command_builders.js.map +1 -1
  41. package/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js.map +1 -1
  42. package/lib/cmap/auth/mongodb_oidc/human_callback_workflow.js.map +1 -1
  43. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js +2 -0
  44. package/lib/cmap/auth/mongodb_oidc/machine_workflow.js.map +1 -1
  45. package/lib/cmap/auth/mongodb_oidc/token_cache.js.map +1 -1
  46. package/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js.map +1 -1
  47. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  48. package/lib/cmap/auth/plain.js.map +1 -1
  49. package/lib/cmap/auth/scram.js.map +1 -1
  50. package/lib/cmap/auth/x509.js.map +1 -1
  51. package/lib/cmap/command_monitoring_events.js.map +1 -1
  52. package/lib/cmap/commands.js +5 -62
  53. package/lib/cmap/commands.js.map +1 -1
  54. package/lib/cmap/connect.js +7 -9
  55. package/lib/cmap/connect.js.map +1 -1
  56. package/lib/cmap/connection.js +5 -3
  57. package/lib/cmap/connection.js.map +1 -1
  58. package/lib/cmap/connection_pool.js +9 -11
  59. package/lib/cmap/connection_pool.js.map +1 -1
  60. package/lib/cmap/connection_pool_events.js +3 -7
  61. package/lib/cmap/connection_pool_events.js.map +1 -1
  62. package/lib/cmap/handshake/client_metadata.js +5 -5
  63. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  64. package/lib/cmap/metrics.js +1 -1
  65. package/lib/cmap/metrics.js.map +1 -1
  66. package/lib/cmap/stream_description.js.map +1 -1
  67. package/lib/cmap/wire_protocol/compression.js +5 -5
  68. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  69. package/lib/cmap/wire_protocol/constants.js +2 -2
  70. package/lib/cmap/wire_protocol/on_data.js +2 -1
  71. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  72. package/lib/cmap/wire_protocol/on_demand/document.js +3 -12
  73. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
  74. package/lib/cmap/wire_protocol/responses.js +15 -6
  75. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  76. package/lib/cmap/wire_protocol/shared.js +3 -2
  77. package/lib/cmap/wire_protocol/shared.js.map +1 -1
  78. package/lib/collection.js.map +1 -1
  79. package/lib/connection_string.js +5 -10
  80. package/lib/connection_string.js.map +1 -1
  81. package/lib/constants.js +0 -1
  82. package/lib/constants.js.map +1 -1
  83. package/lib/cursor/abstract_cursor.js +16 -25
  84. package/lib/cursor/abstract_cursor.js.map +1 -1
  85. package/lib/cursor/aggregation_cursor.js +2 -2
  86. package/lib/cursor/aggregation_cursor.js.map +1 -1
  87. package/lib/cursor/change_stream_cursor.js.map +1 -1
  88. package/lib/cursor/find_cursor.js +4 -4
  89. package/lib/cursor/find_cursor.js.map +1 -1
  90. package/lib/db.js +1 -1
  91. package/lib/db.js.map +1 -1
  92. package/lib/deps.js +8 -16
  93. package/lib/deps.js.map +1 -1
  94. package/lib/encrypter.js.map +1 -1
  95. package/lib/error.js +14 -19
  96. package/lib/error.js.map +1 -1
  97. package/lib/explain.js.map +1 -1
  98. package/lib/gridfs/download.js +4 -1
  99. package/lib/gridfs/download.js.map +1 -1
  100. package/lib/gridfs/index.js +1 -1
  101. package/lib/gridfs/index.js.map +1 -1
  102. package/lib/gridfs/upload.js +4 -0
  103. package/lib/gridfs/upload.js.map +1 -1
  104. package/lib/index.js +2 -4
  105. package/lib/index.js.map +1 -1
  106. package/lib/mongo_client.js +1 -15
  107. package/lib/mongo_client.js.map +1 -1
  108. package/lib/mongo_client_auth_providers.js.map +1 -1
  109. package/lib/mongo_logger.js +8 -8
  110. package/lib/mongo_logger.js.map +1 -1
  111. package/lib/mongo_types.js +0 -1
  112. package/lib/mongo_types.js.map +1 -1
  113. package/lib/operations/aggregate.js +0 -1
  114. package/lib/operations/aggregate.js.map +1 -1
  115. package/lib/operations/bulk_write.js.map +1 -1
  116. package/lib/operations/collections.js.map +1 -1
  117. package/lib/operations/command.js +1 -1
  118. package/lib/operations/command.js.map +1 -1
  119. package/lib/operations/count.js.map +1 -1
  120. package/lib/operations/create_collection.js.map +1 -1
  121. package/lib/operations/delete.js +2 -2
  122. package/lib/operations/delete.js.map +1 -1
  123. package/lib/operations/distinct.js.map +1 -1
  124. package/lib/operations/drop.js.map +1 -1
  125. package/lib/operations/estimated_document_count.js.map +1 -1
  126. package/lib/operations/execute_operation.js +109 -111
  127. package/lib/operations/execute_operation.js.map +1 -1
  128. package/lib/operations/find.js.map +1 -1
  129. package/lib/operations/find_and_modify.js +8 -2
  130. package/lib/operations/find_and_modify.js.map +1 -1
  131. package/lib/operations/get_more.js.map +1 -1
  132. package/lib/operations/indexes.js.map +1 -1
  133. package/lib/operations/insert.js.map +1 -1
  134. package/lib/operations/is_capped.js.map +1 -1
  135. package/lib/operations/kill_cursors.js.map +1 -1
  136. package/lib/operations/list_collections.js.map +1 -1
  137. package/lib/operations/list_databases.js.map +1 -1
  138. package/lib/operations/operation.js +5 -5
  139. package/lib/operations/operation.js.map +1 -1
  140. package/lib/operations/options_operation.js.map +1 -1
  141. package/lib/operations/profiling_level.js.map +1 -1
  142. package/lib/operations/search_indexes/drop.js.map +1 -1
  143. package/lib/operations/set_profiling_level.js.map +1 -1
  144. package/lib/operations/stats.js.map +1 -1
  145. package/lib/operations/update.js +2 -2
  146. package/lib/operations/update.js.map +1 -1
  147. package/lib/operations/validate_collection.js.map +1 -1
  148. package/lib/read_concern.js.map +1 -1
  149. package/lib/read_preference.js +1 -1
  150. package/lib/read_preference.js.map +1 -1
  151. package/lib/sdam/common.js +3 -3
  152. package/lib/sdam/common.js.map +1 -1
  153. package/lib/sdam/monitor.js +5 -1
  154. package/lib/sdam/monitor.js.map +1 -1
  155. package/lib/sdam/server.js +2 -2
  156. package/lib/sdam/server.js.map +1 -1
  157. package/lib/sdam/server_description.js +3 -3
  158. package/lib/sdam/server_description.js.map +1 -1
  159. package/lib/sdam/server_selection.js +5 -5
  160. package/lib/sdam/server_selection.js.map +1 -1
  161. package/lib/sdam/srv_polling.js +3 -2
  162. package/lib/sdam/srv_polling.js.map +1 -1
  163. package/lib/sdam/topology.js +1 -1
  164. package/lib/sdam/topology.js.map +1 -1
  165. package/lib/sdam/topology_description.js.map +1 -1
  166. package/lib/sessions.js +218 -221
  167. package/lib/sessions.js.map +1 -1
  168. package/lib/sort.js +3 -2
  169. package/lib/sort.js.map +1 -1
  170. package/lib/timeout.js +1 -0
  171. package/lib/timeout.js.map +1 -1
  172. package/lib/transactions.js +2 -2
  173. package/lib/transactions.js.map +1 -1
  174. package/lib/utils.js +51 -49
  175. package/lib/utils.js.map +1 -1
  176. package/lib/write_concern.js +2 -2
  177. package/lib/write_concern.js.map +1 -1
  178. package/mongodb.d.ts +143 -146
  179. package/package.json +28 -27
  180. package/src/bson.ts +13 -1
  181. package/src/bulk/common.ts +18 -18
  182. package/src/change_stream.ts +15 -33
  183. package/src/client-side-encryption/auto_encrypter.ts +82 -18
  184. package/src/client-side-encryption/client_encryption.ts +54 -51
  185. package/src/client-side-encryption/mongocryptd_manager.ts +6 -10
  186. package/src/client-side-encryption/state_machine.ts +6 -28
  187. package/src/cmap/auth/gssapi.ts +1 -1
  188. package/src/cmap/auth/mongodb_aws.ts +2 -2
  189. package/src/cmap/auth/mongodb_oidc/callback_workflow.ts +2 -2
  190. package/src/cmap/auth/mongodb_oidc/machine_workflow.ts +2 -2
  191. package/src/cmap/commands.ts +5 -70
  192. package/src/cmap/connect.ts +1 -3
  193. package/src/cmap/connection.ts +4 -3
  194. package/src/cmap/connection_pool.ts +9 -17
  195. package/src/cmap/connection_pool_events.ts +2 -34
  196. package/src/cmap/handshake/client_metadata.ts +1 -1
  197. package/src/cmap/wire_protocol/constants.ts +2 -2
  198. package/src/cmap/wire_protocol/on_demand/document.ts +14 -18
  199. package/src/cmap/wire_protocol/responses.ts +23 -5
  200. package/src/cmap/wire_protocol/shared.ts +2 -1
  201. package/src/collection.ts +15 -16
  202. package/src/connection_string.ts +2 -8
  203. package/src/constants.ts +0 -1
  204. package/src/cursor/abstract_cursor.ts +28 -42
  205. package/src/cursor/aggregation_cursor.ts +5 -7
  206. package/src/cursor/find_cursor.ts +1 -1
  207. package/src/deps.ts +1 -8
  208. package/src/error.ts +14 -33
  209. package/src/gridfs/download.ts +4 -28
  210. package/src/gridfs/upload.ts +6 -1
  211. package/src/index.ts +6 -6
  212. package/src/mongo_client.ts +3 -25
  213. package/src/mongo_logger.ts +3 -5
  214. package/src/mongo_types.ts +68 -69
  215. package/src/operations/aggregate.ts +1 -2
  216. package/src/operations/bulk_write.ts +2 -2
  217. package/src/operations/command.ts +1 -1
  218. package/src/operations/execute_operation.ts +131 -137
  219. package/src/operations/find_and_modify.ts +7 -2
  220. package/src/operations/insert.ts +4 -3
  221. package/src/operations/operation.ts +10 -7
  222. package/src/operations/search_indexes/drop.ts +1 -4
  223. package/src/sdam/monitor.ts +5 -3
  224. package/src/sdam/server.ts +1 -1
  225. package/src/sdam/server_description.ts +6 -5
  226. package/src/sdam/srv_polling.ts +2 -1
  227. package/src/sessions.ts +277 -291
  228. package/src/sort.ts +1 -1
  229. package/src/timeout.ts +1 -0
  230. package/src/transactions.ts +2 -1
  231. package/src/utils.ts +4 -9
  232. package/src/write_concern.ts +2 -2
  233. package/tsconfig.json +1 -2
  234. package/lib/beta.d.ts +0 -7900
  235. package/lib/beta.js +0 -21
  236. package/lib/beta.js.map +0 -1
  237. package/lib/operations/client_bulk_write/command_builder.js +0 -198
  238. package/lib/operations/client_bulk_write/command_builder.js.map +0 -1
  239. package/lib/operations/client_bulk_write/common.js +0 -3
  240. package/lib/operations/client_bulk_write/common.js.map +0 -1
  241. package/lib/resource_management.js +0 -58
  242. package/lib/resource_management.js.map +0 -1
  243. package/src/beta.ts +0 -22
  244. package/src/operations/client_bulk_write/command_builder.ts +0 -283
  245. package/src/operations/client_bulk_write/common.ts +0 -146
  246. package/src/resource_management.ts +0 -74
package/src/sessions.ts CHANGED
@@ -27,7 +27,6 @@ import { executeOperation } from './operations/execute_operation';
27
27
  import { RunAdminCommandOperation } from './operations/run_command';
28
28
  import { ReadConcernLevel } from './read_concern';
29
29
  import { ReadPreference } from './read_preference';
30
- import { type AsyncDisposable, configureResourceManagement } from './resource_management';
31
30
  import { _advanceClusterTime, type ClusterTime, TopologyType } from './sdam/common';
32
31
  import {
33
32
  isTransactionCommand,
@@ -46,7 +45,7 @@ import {
46
45
  squashError,
47
46
  uuidV4
48
47
  } from './utils';
49
- import { WriteConcern, type WriteConcernOptions, type WriteConcernSettings } from './write_concern';
48
+ import { WriteConcern } from './write_concern';
50
49
 
51
50
  const minWireVersionForShardedTransactions = 8;
52
51
 
@@ -106,10 +105,7 @@ export interface EndSessionOptions {
106
105
  * NOTE: not meant to be instantiated directly.
107
106
  * @public
108
107
  */
109
- export class ClientSession
110
- extends TypedEventEmitter<ClientSessionEvents>
111
- implements AsyncDisposable
112
- {
108
+ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
113
109
  /** @internal */
114
110
  client: MongoClient;
115
111
  /** @internal */
@@ -259,10 +255,7 @@ export class ClientSession
259
255
  }
260
256
 
261
257
  /**
262
- * Frees any client-side resources held by the current session. If a session is in a transaction,
263
- * the transaction is aborted.
264
- *
265
- * Does not end the session on the server.
258
+ * Ends this session on the server
266
259
  *
267
260
  * @param options - Optional settings. Currently reserved for future use
268
261
  */
@@ -293,16 +286,6 @@ export class ClientSession
293
286
  maybeClearPinnedConnection(this, { force: true, ...options });
294
287
  }
295
288
  }
296
- /**
297
- * @beta
298
- * @experimental
299
- * An alias for {@link ClientSession.endSession|ClientSession.endSession()}.
300
- */
301
- declare [Symbol.asyncDispose]: () => Promise<void>;
302
- /** @internal */
303
- async asyncDispose() {
304
- await this.endSession({ force: true });
305
- }
306
289
 
307
290
  /**
308
291
  * Advances the operationTime for a ClientSession.
@@ -443,167 +426,14 @@ export class ClientSession
443
426
  * Commits the currently active transaction in this session.
444
427
  */
445
428
  async commitTransaction(): Promise<void> {
446
- if (this.transaction.state === TxnState.NO_TRANSACTION) {
447
- throw new MongoTransactionError('No transaction started');
448
- }
449
-
450
- if (
451
- this.transaction.state === TxnState.STARTING_TRANSACTION ||
452
- this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY
453
- ) {
454
- // the transaction was never started, we can safely exit here
455
- this.transaction.transition(TxnState.TRANSACTION_COMMITTED_EMPTY);
456
- return;
457
- }
458
-
459
- if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {
460
- throw new MongoTransactionError(
461
- 'Cannot call commitTransaction after calling abortTransaction'
462
- );
463
- }
464
-
465
- const command: {
466
- commitTransaction: 1;
467
- writeConcern?: WriteConcernSettings;
468
- recoveryToken?: Document;
469
- maxTimeMS?: number;
470
- } = { commitTransaction: 1 };
471
-
472
- const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
473
- if (wc != null) {
474
- WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
475
- }
476
-
477
- if (this.transaction.state === TxnState.TRANSACTION_COMMITTED) {
478
- WriteConcern.apply(command, { wtimeoutMS: 10000, ...wc, w: 'majority' });
479
- }
480
-
481
- if (typeof this.transaction.options.maxTimeMS === 'number') {
482
- command.maxTimeMS = this.transaction.options.maxTimeMS;
483
- }
484
-
485
- if (this.transaction.recoveryToken) {
486
- command.recoveryToken = this.transaction.recoveryToken;
487
- }
488
-
489
- const operation = new RunAdminCommandOperation(command, {
490
- session: this,
491
- readPreference: ReadPreference.primary,
492
- bypassPinningCheck: true
493
- });
494
-
495
- try {
496
- await executeOperation(this.client, operation);
497
- return;
498
- } catch (firstCommitError) {
499
- if (firstCommitError instanceof MongoError && isRetryableWriteError(firstCommitError)) {
500
- // SPEC-1185: apply majority write concern when retrying commitTransaction
501
- WriteConcern.apply(command, { wtimeoutMS: 10000, ...wc, w: 'majority' });
502
- // per txns spec, must unpin session in this case
503
- this.unpin({ force: true });
504
-
505
- try {
506
- await executeOperation(this.client, operation);
507
- return;
508
- } catch (retryCommitError) {
509
- // If the retry failed, we process that error instead of the original
510
- if (shouldAddUnknownTransactionCommitResultLabel(retryCommitError)) {
511
- retryCommitError.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
512
- }
513
-
514
- if (shouldUnpinAfterCommitError(retryCommitError)) {
515
- this.unpin({ error: retryCommitError });
516
- }
517
-
518
- throw retryCommitError;
519
- }
520
- }
521
-
522
- if (shouldAddUnknownTransactionCommitResultLabel(firstCommitError)) {
523
- firstCommitError.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
524
- }
525
-
526
- if (shouldUnpinAfterCommitError(firstCommitError)) {
527
- this.unpin({ error: firstCommitError });
528
- }
529
-
530
- throw firstCommitError;
531
- } finally {
532
- this.transaction.transition(TxnState.TRANSACTION_COMMITTED);
533
- }
429
+ return await endTransaction(this, 'commitTransaction');
534
430
  }
535
431
 
536
432
  /**
537
433
  * Aborts the currently active transaction in this session.
538
434
  */
539
435
  async abortTransaction(): Promise<void> {
540
- if (this.transaction.state === TxnState.NO_TRANSACTION) {
541
- throw new MongoTransactionError('No transaction started');
542
- }
543
-
544
- if (this.transaction.state === TxnState.STARTING_TRANSACTION) {
545
- // the transaction was never started, we can safely exit here
546
- this.transaction.transition(TxnState.TRANSACTION_ABORTED);
547
- return;
548
- }
549
-
550
- if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {
551
- throw new MongoTransactionError('Cannot call abortTransaction twice');
552
- }
553
-
554
- if (
555
- this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
556
- this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY
557
- ) {
558
- throw new MongoTransactionError(
559
- 'Cannot call abortTransaction after calling commitTransaction'
560
- );
561
- }
562
-
563
- const command: {
564
- abortTransaction: 1;
565
- writeConcern?: WriteConcernOptions;
566
- recoveryToken?: Document;
567
- } = { abortTransaction: 1 };
568
-
569
- const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
570
- if (wc != null) {
571
- WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
572
- }
573
-
574
- if (this.transaction.recoveryToken) {
575
- command.recoveryToken = this.transaction.recoveryToken;
576
- }
577
-
578
- const operation = new RunAdminCommandOperation(command, {
579
- session: this,
580
- readPreference: ReadPreference.primary,
581
- bypassPinningCheck: true
582
- });
583
-
584
- try {
585
- await executeOperation(this.client, operation);
586
- this.unpin();
587
- return;
588
- } catch (firstAbortError) {
589
- this.unpin();
590
-
591
- if (firstAbortError instanceof MongoError && isRetryableWriteError(firstAbortError)) {
592
- try {
593
- await executeOperation(this.client, operation);
594
- return;
595
- } catch {
596
- // we do not retry the retry
597
- }
598
- }
599
-
600
- // The spec indicates that if the operation times out or fails with a non-retryable error, we should ignore all errors on `abortTransaction`
601
- } finally {
602
- this.transaction.transition(TxnState.TRANSACTION_ABORTED);
603
- if (this.loadBalanced) {
604
- maybeClearPinnedConnection(this, { force: false });
605
- }
606
- }
436
+ return await endTransaction(this, 'abortTransaction');
607
437
  }
608
438
 
609
439
  /**
@@ -649,132 +479,23 @@ export class ClientSession
649
479
  fn: WithTransactionCallback<T>,
650
480
  options?: TransactionOptions
651
481
  ): Promise<T> {
652
- const MAX_TIMEOUT = 120000;
653
482
  const startTime = now();
654
-
655
- let committed = false;
656
- let result: any;
657
-
658
- while (!committed) {
659
- this.startTransaction(options); // may throw on error
660
-
661
- try {
662
- const promise = fn(this);
663
- if (!isPromiseLike(promise)) {
664
- throw new MongoInvalidArgumentError(
665
- 'Function provided to `withTransaction` must return a Promise'
666
- );
667
- }
668
-
669
- result = await promise;
670
-
671
- if (
672
- this.transaction.state === TxnState.NO_TRANSACTION ||
673
- this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
674
- this.transaction.state === TxnState.TRANSACTION_ABORTED
675
- ) {
676
- // Assume callback intentionally ended the transaction
677
- return result;
678
- }
679
- } catch (fnError) {
680
- if (!(fnError instanceof MongoError) || fnError instanceof MongoInvalidArgumentError) {
681
- await this.abortTransaction();
682
- throw fnError;
683
- }
684
-
685
- if (
686
- this.transaction.state === TxnState.STARTING_TRANSACTION ||
687
- this.transaction.state === TxnState.TRANSACTION_IN_PROGRESS
688
- ) {
689
- await this.abortTransaction();
690
- }
691
-
692
- if (
693
- fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
694
- now() - startTime < MAX_TIMEOUT
695
- ) {
696
- continue;
697
- }
698
-
699
- throw fnError;
700
- }
701
-
702
- while (!committed) {
703
- try {
704
- /*
705
- * We will rely on ClientSession.commitTransaction() to
706
- * apply a majority write concern if commitTransaction is
707
- * being retried (see: DRIVERS-601)
708
- */
709
- await this.commitTransaction();
710
- committed = true;
711
- } catch (commitError) {
712
- /*
713
- * Note: a maxTimeMS error will have the MaxTimeMSExpired
714
- * code (50) and can be reported as a top-level error or
715
- * inside writeConcernError, ex.
716
- * { ok:0, code: 50, codeName: 'MaxTimeMSExpired' }
717
- * { ok:1, writeConcernError: { code: 50, codeName: 'MaxTimeMSExpired' } }
718
- */
719
- if (
720
- !isMaxTimeMSExpiredError(commitError) &&
721
- commitError.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult) &&
722
- now() - startTime < MAX_TIMEOUT
723
- ) {
724
- continue;
725
- }
726
-
727
- if (
728
- commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
729
- now() - startTime < MAX_TIMEOUT
730
- ) {
731
- break;
732
- }
733
-
734
- throw commitError;
735
- }
736
- }
737
- }
738
-
739
- return result;
483
+ return await attemptTransaction(this, startTime, fn, options);
740
484
  }
741
485
  }
742
486
 
743
- configureResourceManagement(ClientSession.prototype);
744
-
487
+ const MAX_WITH_TRANSACTION_TIMEOUT = 120000;
745
488
  const NON_DETERMINISTIC_WRITE_CONCERN_ERRORS = new Set([
746
489
  'CannotSatisfyWriteConcern',
747
490
  'UnknownReplWriteConcern',
748
491
  'UnsatisfiableWriteConcern'
749
492
  ]);
750
493
 
751
- function shouldUnpinAfterCommitError(commitError: Error) {
752
- if (commitError instanceof MongoError) {
753
- if (
754
- isRetryableWriteError(commitError) ||
755
- commitError instanceof MongoWriteConcernError ||
756
- isMaxTimeMSExpiredError(commitError)
757
- ) {
758
- if (isUnknownTransactionCommitResult(commitError)) {
759
- // per txns spec, must unpin session in this case
760
- return true;
761
- }
762
- } else if (commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
763
- return true;
764
- }
765
- }
766
- return false;
767
- }
768
-
769
- function shouldAddUnknownTransactionCommitResultLabel(commitError: MongoError) {
770
- let ok = isRetryableWriteError(commitError);
771
- ok ||= commitError instanceof MongoWriteConcernError;
772
- ok ||= isMaxTimeMSExpiredError(commitError);
773
- ok &&= isUnknownTransactionCommitResult(commitError);
774
- return ok;
494
+ function hasNotTimedOut(startTime: number, max: number) {
495
+ return calculateDurationInMs(startTime) < max;
775
496
  }
776
497
 
777
- function isUnknownTransactionCommitResult(err: MongoError): err is MongoError {
498
+ function isUnknownTransactionCommitResult(err: MongoError) {
778
499
  const isNonDeterministicWriteConcernError =
779
500
  err instanceof MongoServerError &&
780
501
  err.codeName &&
@@ -829,17 +550,282 @@ export function maybeClearPinnedConnection(
829
550
  }
830
551
  }
831
552
 
832
- function isMaxTimeMSExpiredError(err: MongoError): boolean {
553
+ function isMaxTimeMSExpiredError(err: MongoError) {
833
554
  if (err == null || !(err instanceof MongoServerError)) {
834
555
  return false;
835
556
  }
836
557
 
837
558
  return (
838
559
  err.code === MONGODB_ERROR_CODES.MaxTimeMSExpired ||
839
- err.writeConcernError?.code === MONGODB_ERROR_CODES.MaxTimeMSExpired
560
+ (err.writeConcernError && err.writeConcernError.code === MONGODB_ERROR_CODES.MaxTimeMSExpired)
840
561
  );
841
562
  }
842
563
 
564
+ async function attemptTransactionCommit<T>(
565
+ session: ClientSession,
566
+ startTime: number,
567
+ fn: WithTransactionCallback<T>,
568
+ result: T,
569
+ options: TransactionOptions
570
+ ): Promise<T> {
571
+ try {
572
+ await session.commitTransaction();
573
+ return result;
574
+ } catch (commitErr) {
575
+ if (
576
+ commitErr instanceof MongoError &&
577
+ hasNotTimedOut(startTime, MAX_WITH_TRANSACTION_TIMEOUT) &&
578
+ !isMaxTimeMSExpiredError(commitErr)
579
+ ) {
580
+ if (commitErr.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult)) {
581
+ return await attemptTransactionCommit(session, startTime, fn, result, options);
582
+ }
583
+
584
+ if (commitErr.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
585
+ return await attemptTransaction(session, startTime, fn, options);
586
+ }
587
+ }
588
+
589
+ throw commitErr;
590
+ }
591
+ }
592
+
593
+ const USER_EXPLICIT_TXN_END_STATES = new Set<TxnState>([
594
+ TxnState.NO_TRANSACTION,
595
+ TxnState.TRANSACTION_COMMITTED,
596
+ TxnState.TRANSACTION_ABORTED
597
+ ]);
598
+
599
+ function userExplicitlyEndedTransaction(session: ClientSession) {
600
+ return USER_EXPLICIT_TXN_END_STATES.has(session.transaction.state);
601
+ }
602
+
603
+ async function attemptTransaction<T>(
604
+ session: ClientSession,
605
+ startTime: number,
606
+ fn: WithTransactionCallback<T>,
607
+ options: TransactionOptions = {}
608
+ ): Promise<T> {
609
+ session.startTransaction(options);
610
+
611
+ let promise;
612
+ try {
613
+ promise = fn(session);
614
+ } catch (err) {
615
+ promise = Promise.reject(err);
616
+ }
617
+
618
+ if (!isPromiseLike(promise)) {
619
+ try {
620
+ await session.abortTransaction();
621
+ } catch (error) {
622
+ squashError(error);
623
+ }
624
+ throw new MongoInvalidArgumentError(
625
+ 'Function provided to `withTransaction` must return a Promise'
626
+ );
627
+ }
628
+
629
+ try {
630
+ const result = await promise;
631
+ if (userExplicitlyEndedTransaction(session)) {
632
+ return result;
633
+ }
634
+ return await attemptTransactionCommit(session, startTime, fn, result, options);
635
+ } catch (err) {
636
+ if (session.inTransaction()) {
637
+ await session.abortTransaction();
638
+ }
639
+
640
+ if (
641
+ err instanceof MongoError &&
642
+ err.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
643
+ hasNotTimedOut(startTime, MAX_WITH_TRANSACTION_TIMEOUT)
644
+ ) {
645
+ return await attemptTransaction(session, startTime, fn, options);
646
+ }
647
+
648
+ if (isMaxTimeMSExpiredError(err)) {
649
+ err.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
650
+ }
651
+
652
+ throw err;
653
+ }
654
+ }
655
+
656
+ async function endTransaction(
657
+ session: ClientSession,
658
+ commandName: 'abortTransaction' | 'commitTransaction'
659
+ ): Promise<void> {
660
+ // handle any initial problematic cases
661
+ const txnState = session.transaction.state;
662
+
663
+ if (txnState === TxnState.NO_TRANSACTION) {
664
+ throw new MongoTransactionError('No transaction started');
665
+ }
666
+
667
+ if (commandName === 'commitTransaction') {
668
+ if (
669
+ txnState === TxnState.STARTING_TRANSACTION ||
670
+ txnState === TxnState.TRANSACTION_COMMITTED_EMPTY
671
+ ) {
672
+ // the transaction was never started, we can safely exit here
673
+ session.transaction.transition(TxnState.TRANSACTION_COMMITTED_EMPTY);
674
+ return;
675
+ }
676
+
677
+ if (txnState === TxnState.TRANSACTION_ABORTED) {
678
+ throw new MongoTransactionError(
679
+ 'Cannot call commitTransaction after calling abortTransaction'
680
+ );
681
+ }
682
+ } else {
683
+ if (txnState === TxnState.STARTING_TRANSACTION) {
684
+ // the transaction was never started, we can safely exit here
685
+ session.transaction.transition(TxnState.TRANSACTION_ABORTED);
686
+ return;
687
+ }
688
+
689
+ if (txnState === TxnState.TRANSACTION_ABORTED) {
690
+ throw new MongoTransactionError('Cannot call abortTransaction twice');
691
+ }
692
+
693
+ if (
694
+ txnState === TxnState.TRANSACTION_COMMITTED ||
695
+ txnState === TxnState.TRANSACTION_COMMITTED_EMPTY
696
+ ) {
697
+ throw new MongoTransactionError(
698
+ 'Cannot call abortTransaction after calling commitTransaction'
699
+ );
700
+ }
701
+ }
702
+
703
+ // construct and send the command
704
+ const command: Document = { [commandName]: 1 };
705
+
706
+ // apply a writeConcern if specified
707
+ let writeConcern;
708
+ if (session.transaction.options.writeConcern) {
709
+ writeConcern = Object.assign({}, session.transaction.options.writeConcern);
710
+ } else if (session.clientOptions && session.clientOptions.writeConcern) {
711
+ writeConcern = { w: session.clientOptions.writeConcern.w };
712
+ }
713
+
714
+ if (txnState === TxnState.TRANSACTION_COMMITTED) {
715
+ writeConcern = Object.assign({ wtimeoutMS: 10000 }, writeConcern, { w: 'majority' });
716
+ }
717
+
718
+ if (writeConcern) {
719
+ WriteConcern.apply(command, writeConcern);
720
+ }
721
+
722
+ if (commandName === 'commitTransaction' && session.transaction.options.maxTimeMS) {
723
+ Object.assign(command, { maxTimeMS: session.transaction.options.maxTimeMS });
724
+ }
725
+
726
+ if (session.transaction.recoveryToken) {
727
+ command.recoveryToken = session.transaction.recoveryToken;
728
+ }
729
+
730
+ try {
731
+ // send the command
732
+ await executeOperation(
733
+ session.client,
734
+ new RunAdminCommandOperation(command, {
735
+ session,
736
+ readPreference: ReadPreference.primary,
737
+ bypassPinningCheck: true
738
+ })
739
+ );
740
+ if (command.abortTransaction) {
741
+ // always unpin on abort regardless of command outcome
742
+ session.unpin();
743
+ }
744
+ if (commandName !== 'commitTransaction') {
745
+ session.transaction.transition(TxnState.TRANSACTION_ABORTED);
746
+ if (session.loadBalanced) {
747
+ maybeClearPinnedConnection(session, { force: false });
748
+ }
749
+ } else {
750
+ session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
751
+ }
752
+ } catch (firstAttemptErr) {
753
+ if (command.abortTransaction) {
754
+ // always unpin on abort regardless of command outcome
755
+ session.unpin();
756
+ }
757
+ if (firstAttemptErr instanceof MongoError && isRetryableWriteError(firstAttemptErr)) {
758
+ // SPEC-1185: apply majority write concern when retrying commitTransaction
759
+ if (command.commitTransaction) {
760
+ // per txns spec, must unpin session in this case
761
+ session.unpin({ force: true });
762
+
763
+ command.writeConcern = Object.assign({ wtimeout: 10000 }, command.writeConcern, {
764
+ w: 'majority'
765
+ });
766
+ }
767
+
768
+ try {
769
+ await executeOperation(
770
+ session.client,
771
+ new RunAdminCommandOperation(command, {
772
+ session,
773
+ readPreference: ReadPreference.primary,
774
+ bypassPinningCheck: true
775
+ })
776
+ );
777
+ if (commandName !== 'commitTransaction') {
778
+ session.transaction.transition(TxnState.TRANSACTION_ABORTED);
779
+ if (session.loadBalanced) {
780
+ maybeClearPinnedConnection(session, { force: false });
781
+ }
782
+ } else {
783
+ session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
784
+ }
785
+ } catch (secondAttemptErr) {
786
+ handleEndTransactionError(session, commandName, secondAttemptErr);
787
+ }
788
+ } else {
789
+ handleEndTransactionError(session, commandName, firstAttemptErr);
790
+ }
791
+ }
792
+ }
793
+
794
+ function handleEndTransactionError(
795
+ session: ClientSession,
796
+ commandName: 'abortTransaction' | 'commitTransaction',
797
+ error: Error
798
+ ) {
799
+ if (commandName !== 'commitTransaction') {
800
+ session.transaction.transition(TxnState.TRANSACTION_ABORTED);
801
+ if (session.loadBalanced) {
802
+ maybeClearPinnedConnection(session, { force: false });
803
+ }
804
+ // The spec indicates that if the operation times out or fails with a non-retryable error, we should ignore all errors on `abortTransaction`
805
+ return;
806
+ }
807
+
808
+ session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
809
+ if (error instanceof MongoError) {
810
+ if (
811
+ isRetryableWriteError(error) ||
812
+ error instanceof MongoWriteConcernError ||
813
+ isMaxTimeMSExpiredError(error)
814
+ ) {
815
+ if (isUnknownTransactionCommitResult(error)) {
816
+ error.addErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult);
817
+
818
+ // per txns spec, must unpin session in this case
819
+ session.unpin({ error });
820
+ }
821
+ } else if (error.hasErrorLabel(MongoErrorLabel.TransientTransactionError)) {
822
+ session.unpin({ error });
823
+ }
824
+ }
825
+
826
+ throw error;
827
+ }
828
+
843
829
  /** @public */
844
830
  export type ServerSessionId = { id: Binary };
845
831
 
package/src/sort.ts CHANGED
@@ -60,7 +60,7 @@ function isPair(t: Sort): t is [string, SortDirection] {
60
60
  try {
61
61
  prepareDirection(t[1]);
62
62
  return true;
63
- } catch {
63
+ } catch (e) {
64
64
  return false;
65
65
  }
66
66
  }
package/src/timeout.ts CHANGED
@@ -89,6 +89,7 @@ export class Timeout extends Promise<never> {
89
89
  Symbol.toStringTag in timeout &&
90
90
  timeout[Symbol.toStringTag] === 'MongoDBTimeout' &&
91
91
  'then' in timeout &&
92
+ // eslint-disable-next-line github/no-then
92
93
  typeof timeout.then === 'function'
93
94
  );
94
95
  }
@@ -2,7 +2,8 @@ import type { Document } from './bson';
2
2
  import { MongoRuntimeError, MongoTransactionError } from './error';
3
3
  import type { CommandOperationOptions } from './operations/command';
4
4
  import { ReadConcern, type ReadConcernLike } from './read_concern';
5
- import { ReadPreference, type ReadPreferenceLike } from './read_preference';
5
+ import type { ReadPreferenceLike } from './read_preference';
6
+ import { ReadPreference } from './read_preference';
6
7
  import type { Server } from './sdam/server';
7
8
  import { WriteConcern } from './write_concern';
8
9