mongodb 6.12.0 → 6.13.0-dev.20250131.sha.e7898a4d

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 (92) hide show
  1. package/lib/beta.d.ts +176 -108
  2. package/lib/bulk/common.js +5 -7
  3. package/lib/bulk/common.js.map +1 -1
  4. package/lib/change_stream.js +16 -26
  5. package/lib/change_stream.js.map +1 -1
  6. package/lib/client-side-encryption/auto_encrypter.js +4 -2
  7. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  8. package/lib/client-side-encryption/client_encryption.js +4 -4
  9. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  10. package/lib/client-side-encryption/state_machine.js +56 -30
  11. package/lib/client-side-encryption/state_machine.js.map +1 -1
  12. package/lib/cmap/auth/mongodb_oidc.js +1 -1
  13. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  14. package/lib/cmap/command_monitoring_events.js +9 -50
  15. package/lib/cmap/command_monitoring_events.js.map +1 -1
  16. package/lib/cmap/connection.js +28 -22
  17. package/lib/cmap/connection.js.map +1 -1
  18. package/lib/cmap/connection_pool.js +88 -117
  19. package/lib/cmap/connection_pool.js.map +1 -1
  20. package/lib/cmap/wire_protocol/on_data.js +6 -1
  21. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  22. package/lib/collection.js.map +1 -1
  23. package/lib/connection_string.js +68 -86
  24. package/lib/connection_string.js.map +1 -1
  25. package/lib/cursor/abstract_cursor.js +47 -18
  26. package/lib/cursor/abstract_cursor.js.map +1 -1
  27. package/lib/cursor/aggregation_cursor.js +2 -1
  28. package/lib/cursor/aggregation_cursor.js.map +1 -1
  29. package/lib/cursor/find_cursor.js +2 -1
  30. package/lib/cursor/find_cursor.js.map +1 -1
  31. package/lib/cursor/list_collections_cursor.js +2 -1
  32. package/lib/cursor/list_collections_cursor.js.map +1 -1
  33. package/lib/db.js +2 -1
  34. package/lib/db.js.map +1 -1
  35. package/lib/encrypter.js +5 -9
  36. package/lib/encrypter.js.map +1 -1
  37. package/lib/error.js +10 -18
  38. package/lib/error.js.map +1 -1
  39. package/lib/index.js +5 -2
  40. package/lib/index.js.map +1 -1
  41. package/lib/mongo_client.js +46 -26
  42. package/lib/mongo_client.js.map +1 -1
  43. package/lib/mongo_logger.js +102 -3
  44. package/lib/mongo_logger.js.map +1 -1
  45. package/lib/operations/execute_operation.js +9 -5
  46. package/lib/operations/execute_operation.js.map +1 -1
  47. package/lib/operations/list_collections.js.map +1 -1
  48. package/lib/operations/operation.js +4 -5
  49. package/lib/operations/operation.js.map +1 -1
  50. package/lib/sdam/monitor.js +25 -31
  51. package/lib/sdam/monitor.js.map +1 -1
  52. package/lib/sdam/server.js +27 -17
  53. package/lib/sdam/server.js.map +1 -1
  54. package/lib/sdam/topology.js +20 -19
  55. package/lib/sdam/topology.js.map +1 -1
  56. package/lib/sessions.js +24 -48
  57. package/lib/sessions.js.map +1 -1
  58. package/lib/utils.js +64 -44
  59. package/lib/utils.js.map +1 -1
  60. package/mongodb.d.ts +176 -108
  61. package/package.json +2 -2
  62. package/src/bulk/common.ts +6 -9
  63. package/src/change_stream.ts +21 -33
  64. package/src/client-side-encryption/auto_encrypter.ts +12 -8
  65. package/src/client-side-encryption/client_encryption.ts +6 -4
  66. package/src/client-side-encryption/state_machine.ts +80 -36
  67. package/src/cmap/auth/mongodb_oidc.ts +1 -1
  68. package/src/cmap/command_monitoring_events.ts +10 -55
  69. package/src/cmap/connection.ts +37 -29
  70. package/src/cmap/connection_pool.ts +121 -145
  71. package/src/cmap/wire_protocol/on_data.ts +9 -2
  72. package/src/collection.ts +15 -8
  73. package/src/connection_string.ts +74 -99
  74. package/src/cursor/abstract_cursor.ts +71 -23
  75. package/src/cursor/aggregation_cursor.ts +5 -3
  76. package/src/cursor/find_cursor.ts +5 -3
  77. package/src/cursor/list_collections_cursor.ts +5 -3
  78. package/src/db.ts +11 -7
  79. package/src/encrypter.ts +6 -11
  80. package/src/error.ts +11 -23
  81. package/src/index.ts +3 -3
  82. package/src/mongo_client.ts +78 -47
  83. package/src/mongo_logger.ts +158 -11
  84. package/src/mongo_types.ts +38 -0
  85. package/src/operations/execute_operation.ts +11 -6
  86. package/src/operations/list_collections.ts +4 -1
  87. package/src/operations/operation.ts +8 -9
  88. package/src/sdam/monitor.ts +30 -38
  89. package/src/sdam/server.ts +33 -20
  90. package/src/sdam/topology.ts +29 -26
  91. package/src/sessions.ts +37 -58
  92. package/src/utils.ts +79 -43
@@ -22,7 +22,6 @@ import {
22
22
  } from '../constants';
23
23
  import {
24
24
  type AnyError,
25
- isNetworkErrorBeforeHandshake,
26
25
  isNodeShuttingDownError,
27
26
  isSDAMUnrecoverableError,
28
27
  MONGODB_ERROR_CODES,
@@ -37,12 +36,13 @@ import {
37
36
  needsRetryableWriteLabel
38
37
  } from '../error';
39
38
  import type { ServerApi } from '../mongo_client';
40
- import { TypedEventEmitter } from '../mongo_types';
39
+ import { type Abortable, TypedEventEmitter } from '../mongo_types';
41
40
  import type { GetMoreOptions } from '../operations/get_more';
42
41
  import type { ClientSession } from '../sessions';
43
42
  import { type TimeoutContext } from '../timeout';
44
43
  import { isTransactionCommand } from '../transactions';
45
44
  import {
45
+ abortable,
46
46
  type EventEmitterWithState,
47
47
  makeStateMachine,
48
48
  maxWireVersion,
@@ -108,7 +108,7 @@ export type ServerEvents = {
108
108
  /** @internal */
109
109
  export type ServerCommandOptions = Omit<CommandOptions, 'timeoutContext' | 'socketTimeoutMS'> & {
110
110
  timeoutContext: TimeoutContext;
111
- };
111
+ } & Abortable;
112
112
 
113
113
  /** @internal */
114
114
  export class Server extends TypedEventEmitter<ServerEvents> {
@@ -286,7 +286,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
286
286
  public async command(
287
287
  ns: MongoDBNamespace,
288
288
  cmd: Document,
289
- options: ServerCommandOptions,
289
+ { ...options }: ServerCommandOptions,
290
290
  responseType?: MongoDBResponseConstructor
291
291
  ): Promise<Document> {
292
292
  if (ns.db == null || typeof ns === 'string') {
@@ -297,25 +297,21 @@ export class Server extends TypedEventEmitter<ServerEvents> {
297
297
  throw new MongoServerClosedError();
298
298
  }
299
299
 
300
- // Clone the options
301
- const finalOptions = Object.assign({}, options, {
302
- wireProtocolCommand: false,
303
- directConnection: this.topology.s.options.directConnection
304
- });
300
+ options.directConnection = this.topology.s.options.directConnection;
305
301
 
306
302
  // There are cases where we need to flag the read preference not to get sent in
307
303
  // the command, such as pre-5.0 servers attempting to perform an aggregate write
308
304
  // with a non-primary read preference. In this case the effective read preference
309
305
  // (primary) is not the same as the provided and must be removed completely.
310
- if (finalOptions.omitReadPreference) {
311
- delete finalOptions.readPreference;
306
+ if (options.omitReadPreference) {
307
+ delete options.readPreference;
312
308
  }
313
309
 
314
310
  if (this.description.iscryptd) {
315
- finalOptions.omitMaxTimeMS = true;
311
+ options.omitMaxTimeMS = true;
316
312
  }
317
313
 
318
- const session = finalOptions.session;
314
+ const session = options.session;
319
315
  let conn = session?.pinnedConnection;
320
316
 
321
317
  this.incrementOperationCount();
@@ -332,26 +328,35 @@ export class Server extends TypedEventEmitter<ServerEvents> {
332
328
  }
333
329
  }
334
330
 
331
+ let reauthPromise: Promise<void> | null = null;
332
+
335
333
  try {
336
334
  try {
337
- const res = await conn.command(ns, cmd, finalOptions, responseType);
335
+ const res = await conn.command(ns, cmd, options, responseType);
338
336
  throwIfWriteConcernError(res);
339
337
  return res;
340
338
  } catch (commandError) {
341
- throw this.decorateCommandError(conn, cmd, finalOptions, commandError);
339
+ throw this.decorateCommandError(conn, cmd, options, commandError);
342
340
  }
343
341
  } catch (operationError) {
344
342
  if (
345
343
  operationError instanceof MongoError &&
346
344
  operationError.code === MONGODB_ERROR_CODES.Reauthenticate
347
345
  ) {
348
- await this.pool.reauthenticate(conn);
346
+ reauthPromise = this.pool.reauthenticate(conn).catch(error => {
347
+ reauthPromise = null;
348
+ throw error;
349
+ });
350
+
351
+ await abortable(reauthPromise, options);
352
+ reauthPromise = null; // only reachable if reauth succeeds
353
+
349
354
  try {
350
- const res = await conn.command(ns, cmd, finalOptions, responseType);
355
+ const res = await conn.command(ns, cmd, options, responseType);
351
356
  throwIfWriteConcernError(res);
352
357
  return res;
353
358
  } catch (commandError) {
354
- throw this.decorateCommandError(conn, cmd, finalOptions, commandError);
359
+ throw this.decorateCommandError(conn, cmd, options, commandError);
355
360
  }
356
361
  } else {
357
362
  throw operationError;
@@ -359,7 +364,14 @@ export class Server extends TypedEventEmitter<ServerEvents> {
359
364
  } finally {
360
365
  this.decrementOperationCount();
361
366
  if (session?.pinnedConnection !== conn) {
362
- this.pool.checkIn(conn);
367
+ if (reauthPromise != null) {
368
+ // The reauth promise only exists if it hasn't thrown.
369
+ void reauthPromise.finally(() => {
370
+ this.pool.checkIn(conn);
371
+ });
372
+ } else {
373
+ this.pool.checkIn(conn);
374
+ }
363
375
  }
364
376
  }
365
377
  }
@@ -381,7 +393,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
381
393
 
382
394
  const isNetworkNonTimeoutError =
383
395
  error instanceof MongoNetworkError && !(error instanceof MongoNetworkTimeoutError);
384
- const isNetworkTimeoutBeforeHandshakeError = isNetworkErrorBeforeHandshake(error);
396
+ const isNetworkTimeoutBeforeHandshakeError =
397
+ error instanceof MongoNetworkError && error.beforeHandshake;
385
398
  const isAuthHandshakeError = error.hasErrorLabel(MongoErrorLabel.HandshakeError);
386
399
  if (isNetworkNonTimeoutError || isNetworkTimeoutBeforeHandshakeError || isAuthHandshakeError) {
387
400
  // In load balanced mode we never mark the server as unknown and always
@@ -3,7 +3,7 @@ import type { MongoCredentials } from '../cmap/auth/mongo_credentials';
3
3
  import type { ConnectionEvents } from '../cmap/connection';
4
4
  import type { ConnectionPoolEvents } from '../cmap/connection_pool';
5
5
  import type { ClientMetadata } from '../cmap/handshake/client_metadata';
6
- import { DEFAULT_OPTIONS, FEATURE_FLAGS } from '../connection_string';
6
+ import { DEFAULT_OPTIONS } from '../connection_string';
7
7
  import {
8
8
  CLOSE,
9
9
  CONNECT,
@@ -31,15 +31,17 @@ import {
31
31
  } from '../error';
32
32
  import type { MongoClient, ServerApi } from '../mongo_client';
33
33
  import { MongoLoggableComponent, type MongoLogger, SeverityLevel } from '../mongo_logger';
34
- import { TypedEventEmitter } from '../mongo_types';
34
+ import { type Abortable, TypedEventEmitter } from '../mongo_types';
35
35
  import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
36
36
  import type { ClientSession } from '../sessions';
37
37
  import { Timeout, TimeoutContext, TimeoutError } from '../timeout';
38
38
  import type { Transaction } from '../transactions';
39
39
  import {
40
+ addAbortListener,
40
41
  type Callback,
41
42
  type EventEmitterWithState,
42
43
  HostAddress,
44
+ kDispose,
43
45
  List,
44
46
  makeStateMachine,
45
47
  now,
@@ -88,11 +90,6 @@ const stateTransition = makeStateMachine({
88
90
  [STATE_CLOSING]: [STATE_CLOSING, STATE_CLOSED]
89
91
  });
90
92
 
91
- /** @internal */
92
- const kCancelled = Symbol('cancelled');
93
- /** @internal */
94
- const kWaitQueue = Symbol('waitQueue');
95
-
96
93
  /** @internal */
97
94
  export type ServerSelectionCallback = Callback<Server>;
98
95
 
@@ -105,7 +102,7 @@ export interface ServerSelectionRequest {
105
102
  startTime: number;
106
103
  resolve: (server: Server) => void;
107
104
  reject: (error: MongoError) => void;
108
- [kCancelled]?: boolean;
105
+ cancelled: boolean;
109
106
  operationName: string;
110
107
  waitingLogged: boolean;
111
108
  previousServer?: ServerDescription;
@@ -158,7 +155,7 @@ export interface TopologyOptions extends BSONSerializeOptions, ServerOptions {
158
155
  serverMonitoringMode: ServerMonitoringMode;
159
156
  /** MongoDB server API version */
160
157
  serverApi?: ServerApi;
161
- [featureFlag: symbol]: any;
158
+ __skipPingOnConnect?: boolean;
162
159
  }
163
160
 
164
161
  /** @public */
@@ -208,7 +205,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
208
205
  /** @internal */
209
206
  s: TopologyPrivate;
210
207
  /** @internal */
211
- [kWaitQueue]: List<ServerSelectionRequest>;
208
+ waitQueue: List<ServerSelectionRequest>;
212
209
  /** @internal */
213
210
  hello?: Document;
214
211
  /** @internal */
@@ -256,8 +253,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
256
253
  // Options should only be undefined in tests, MongoClient will always have defined options
257
254
  options = options ?? {
258
255
  hosts: [HostAddress.fromString('localhost:27017')],
259
- ...Object.fromEntries(DEFAULT_OPTIONS.entries()),
260
- ...Object.fromEntries(FEATURE_FLAGS.entries())
256
+ ...Object.fromEntries(DEFAULT_OPTIONS.entries())
261
257
  };
262
258
 
263
259
  if (typeof seeds === 'string') {
@@ -293,7 +289,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
293
289
  serverDescriptions.set(hostAddress.toString(), new ServerDescription(hostAddress));
294
290
  }
295
291
 
296
- this[kWaitQueue] = new List();
292
+ this.waitQueue = new List();
297
293
  this.s = {
298
294
  // the id of this topology
299
295
  id: topologyId,
@@ -471,7 +467,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
471
467
  readPreferenceServerSelector(readPreference),
472
468
  selectServerOptions
473
469
  );
474
- const skipPingOnConnect = this.s.options[Symbol.for('@@mdb.skipPingOnConnect')] === true;
470
+ const skipPingOnConnect = this.s.options.__skipPingOnConnect === true;
475
471
  if (!skipPingOnConnect && this.s.credentials) {
476
472
  await server.command(ns('admin.$cmd'), { ping: 1 }, { timeoutContext });
477
473
  stateTransition(this, STATE_CONNECTED);
@@ -506,7 +502,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
506
502
 
507
503
  stateTransition(this, STATE_CLOSING);
508
504
 
509
- drainWaitQueue(this[kWaitQueue], new MongoTopologyClosedError());
505
+ drainWaitQueue(this.waitQueue, new MongoTopologyClosedError());
510
506
 
511
507
  if (this.s.srvPoller) {
512
508
  this.s.srvPoller.stop();
@@ -531,7 +527,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
531
527
  */
532
528
  async selectServer(
533
529
  selector: string | ReadPreference | ServerSelector,
534
- options: SelectServerOptions
530
+ options: SelectServerOptions & Abortable
535
531
  ): Promise<Server> {
536
532
  let serverSelector;
537
533
  if (typeof selector !== 'function') {
@@ -601,13 +597,19 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
601
597
  transaction,
602
598
  resolve,
603
599
  reject,
600
+ cancelled: false,
604
601
  startTime: now(),
605
602
  operationName: options.operationName,
606
603
  waitingLogged: false,
607
604
  previousServer: options.previousServer
608
605
  };
609
606
 
610
- this[kWaitQueue].push(waitQueueMember);
607
+ const abortListener = addAbortListener(options.signal, function () {
608
+ waitQueueMember.cancelled = true;
609
+ reject(this.reason);
610
+ });
611
+
612
+ this.waitQueue.push(waitQueueMember);
611
613
  processWaitQueue(this);
612
614
 
613
615
  try {
@@ -620,7 +622,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
620
622
  } catch (error) {
621
623
  if (TimeoutError.is(error)) {
622
624
  // Timeout
623
- waitQueueMember[kCancelled] = true;
625
+ waitQueueMember.cancelled = true;
624
626
  const timeoutError = new MongoServerSelectionError(
625
627
  `Server selection timed out after ${timeout?.duration} ms`,
626
628
  this.description
@@ -652,6 +654,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
652
654
  // Other server selection error
653
655
  throw error;
654
656
  } finally {
657
+ abortListener?.[kDispose]();
655
658
  if (options.timeoutContext?.clearServerSelectionTimeout) timeout?.clear();
656
659
  }
657
660
  }
@@ -721,7 +724,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
721
724
  updateServers(this, serverDescription);
722
725
 
723
726
  // attempt to resolve any outstanding server selection attempts
724
- if (this[kWaitQueue].length > 0) {
727
+ if (this.waitQueue.length > 0) {
725
728
  processWaitQueue(this);
726
729
  }
727
730
 
@@ -910,7 +913,7 @@ function drainWaitQueue(queue: List<ServerSelectionRequest>, drainError: MongoDr
910
913
  continue;
911
914
  }
912
915
 
913
- if (!waitQueueMember[kCancelled]) {
916
+ if (!waitQueueMember.cancelled) {
914
917
  if (
915
918
  waitQueueMember.mongoLogger?.willLog(
916
919
  MongoLoggableComponent.SERVER_SELECTION,
@@ -934,20 +937,20 @@ function drainWaitQueue(queue: List<ServerSelectionRequest>, drainError: MongoDr
934
937
 
935
938
  function processWaitQueue(topology: Topology) {
936
939
  if (topology.s.state === STATE_CLOSED) {
937
- drainWaitQueue(topology[kWaitQueue], new MongoTopologyClosedError());
940
+ drainWaitQueue(topology.waitQueue, new MongoTopologyClosedError());
938
941
  return;
939
942
  }
940
943
 
941
944
  const isSharded = topology.description.type === TopologyType.Sharded;
942
945
  const serverDescriptions = Array.from(topology.description.servers.values());
943
- const membersToProcess = topology[kWaitQueue].length;
946
+ const membersToProcess = topology.waitQueue.length;
944
947
  for (let i = 0; i < membersToProcess; ++i) {
945
- const waitQueueMember = topology[kWaitQueue].shift();
948
+ const waitQueueMember = topology.waitQueue.shift();
946
949
  if (!waitQueueMember) {
947
950
  continue;
948
951
  }
949
952
 
950
- if (waitQueueMember[kCancelled]) {
953
+ if (waitQueueMember.cancelled) {
951
954
  continue;
952
955
  }
953
956
 
@@ -1006,7 +1009,7 @@ function processWaitQueue(topology: Topology) {
1006
1009
  }
1007
1010
  waitQueueMember.waitingLogged = true;
1008
1011
  }
1009
- topology[kWaitQueue].push(waitQueueMember);
1012
+ topology.waitQueue.push(waitQueueMember);
1010
1013
  continue;
1011
1014
  } else if (selectedDescriptions.length === 1) {
1012
1015
  selectedServer = topology.s.servers.get(selectedDescriptions[0].address);
@@ -1069,7 +1072,7 @@ function processWaitQueue(topology: Topology) {
1069
1072
  waitQueueMember.resolve(selectedServer);
1070
1073
  }
1071
1074
 
1072
- if (topology[kWaitQueue].length > 0) {
1075
+ if (topology.waitQueue.length > 0) {
1073
1076
  // ensure all server monitors attempt monitoring soon
1074
1077
  for (const [, server] of topology.s.servers) {
1075
1078
  process.nextTick(function scheduleServerCheck() {
package/src/sessions.ts CHANGED
@@ -83,17 +83,6 @@ export type ClientSessionEvents = {
83
83
  ended(session: ClientSession): void;
84
84
  };
85
85
 
86
- /** @internal */
87
- const kServerSession = Symbol('serverSession');
88
- /** @internal */
89
- const kSnapshotTime = Symbol('snapshotTime');
90
- /** @internal */
91
- const kSnapshotEnabled = Symbol('snapshotEnabled');
92
- /** @internal */
93
- const kPinnedConnection = Symbol('pinnedConnection');
94
- /** @internal Accumulates total number of increments to add to txnNumber when applying session to command */
95
- const kTxnNumberIncrement = Symbol('txnNumberIncrement');
96
-
97
86
  /** @public */
98
87
  export interface EndSessionOptions {
99
88
  /**
@@ -132,20 +121,22 @@ export class ClientSession
132
121
  owner?: symbol | AbstractCursor;
133
122
  defaultTransactionOptions: TransactionOptions;
134
123
  transaction: Transaction;
135
- /** @internal
124
+ /**
125
+ * @internal
136
126
  * Keeps track of whether or not the current transaction has attempted to be committed. Is
137
- * initially undefined. Gets set to false when startTransaction is called. When commitTransaction is sent to server, if the commitTransaction succeeds, it is then set to undefined, otherwise, set to true */
138
- commitAttempted?: boolean;
139
- /** @internal */
140
- private [kServerSession]: ServerSession | null;
127
+ * initially undefined. Gets set to false when startTransaction is called. When commitTransaction is sent to server, if the commitTransaction succeeds, it is then set to undefined, otherwise, set to true
128
+ */
129
+ private commitAttempted?: boolean;
130
+ public readonly snapshotEnabled: boolean;
131
+
141
132
  /** @internal */
142
- [kSnapshotTime]?: Timestamp;
133
+ private _serverSession: ServerSession | null;
143
134
  /** @internal */
144
- [kSnapshotEnabled] = false;
135
+ public snapshotTime?: Timestamp;
145
136
  /** @internal */
146
- [kPinnedConnection]?: Connection;
137
+ public pinnedConnection?: Connection;
147
138
  /** @internal */
148
- [kTxnNumberIncrement]: number;
139
+ public txnNumberIncrement: number;
149
140
  /**
150
141
  * @experimental
151
142
  * Specifies the time an operation in a given `ClientSession` will run until it throws a timeout error
@@ -183,13 +174,11 @@ export class ClientSession
183
174
 
184
175
  options = options ?? {};
185
176
 
186
- if (options.snapshot === true) {
187
- this[kSnapshotEnabled] = true;
188
- if (options.causalConsistency === true) {
189
- throw new MongoInvalidArgumentError(
190
- 'Properties "causalConsistency" and "snapshot" are mutually exclusive'
191
- );
192
- }
177
+ this.snapshotEnabled = options.snapshot === true;
178
+ if (options.causalConsistency === true && this.snapshotEnabled) {
179
+ throw new MongoInvalidArgumentError(
180
+ 'Properties "causalConsistency" and "snapshot" are mutually exclusive'
181
+ );
193
182
  }
194
183
 
195
184
  this.client = client;
@@ -199,8 +188,8 @@ export class ClientSession
199
188
  this.timeoutMS = options.defaultTimeoutMS ?? client.s.options?.timeoutMS;
200
189
 
201
190
  this.explicit = !!options.explicit;
202
- this[kServerSession] = this.explicit ? this.sessionPool.acquire() : null;
203
- this[kTxnNumberIncrement] = 0;
191
+ this._serverSession = this.explicit ? this.sessionPool.acquire() : null;
192
+ this.txnNumberIncrement = 0;
204
193
 
205
194
  const defaultCausalConsistencyValue = this.explicit && options.snapshot !== true;
206
195
  this.supports = {
@@ -218,11 +207,11 @@ export class ClientSession
218
207
 
219
208
  /** The server id associated with this session */
220
209
  get id(): ServerSessionId | undefined {
221
- return this[kServerSession]?.id;
210
+ return this.serverSession?.id;
222
211
  }
223
212
 
224
213
  get serverSession(): ServerSession {
225
- let serverSession = this[kServerSession];
214
+ let serverSession = this._serverSession;
226
215
  if (serverSession == null) {
227
216
  if (this.explicit) {
228
217
  throw new MongoRuntimeError('Unexpected null serverSession for an explicit session');
@@ -231,32 +220,22 @@ export class ClientSession
231
220
  throw new MongoRuntimeError('Unexpected null serverSession for an ended implicit session');
232
221
  }
233
222
  serverSession = this.sessionPool.acquire();
234
- this[kServerSession] = serverSession;
223
+ this._serverSession = serverSession;
235
224
  }
236
225
  return serverSession;
237
226
  }
238
227
 
239
- /** Whether or not this session is configured for snapshot reads */
240
- get snapshotEnabled(): boolean {
241
- return this[kSnapshotEnabled];
242
- }
243
-
244
228
  get loadBalanced(): boolean {
245
229
  return this.client.topology?.description.type === TopologyType.LoadBalanced;
246
230
  }
247
231
 
248
- /** @internal */
249
- get pinnedConnection(): Connection | undefined {
250
- return this[kPinnedConnection];
251
- }
252
-
253
232
  /** @internal */
254
233
  pin(conn: Connection): void {
255
- if (this[kPinnedConnection]) {
234
+ if (this.pinnedConnection) {
256
235
  throw TypeError('Cannot pin multiple connections to the same session');
257
236
  }
258
237
 
259
- this[kPinnedConnection] = conn;
238
+ this.pinnedConnection = conn;
260
239
  conn.emit(
261
240
  PINNED,
262
241
  this.inTransaction() ? ConnectionPoolMetrics.TXN : ConnectionPoolMetrics.CURSOR
@@ -273,7 +252,7 @@ export class ClientSession
273
252
  }
274
253
 
275
254
  get isPinned(): boolean {
276
- return this.loadBalanced ? !!this[kPinnedConnection] : this.transaction.isPinned;
255
+ return this.loadBalanced ? !!this.pinnedConnection : this.transaction.isPinned;
277
256
  }
278
257
 
279
258
  /**
@@ -295,12 +274,12 @@ export class ClientSession
295
274
  squashError(error);
296
275
  } finally {
297
276
  if (!this.hasEnded) {
298
- const serverSession = this[kServerSession];
277
+ const serverSession = this.serverSession;
299
278
  if (serverSession != null) {
300
279
  // release the server session back to the pool
301
280
  this.sessionPool.release(serverSession);
302
281
  // Store a clone of the server session for reference (debugging)
303
- this[kServerSession] = new ServerSession(serverSession);
282
+ this._serverSession = new ServerSession(serverSession);
304
283
  }
305
284
  // mark the session as ended, and emit a signal
306
285
  this.hasEnded = true;
@@ -391,7 +370,7 @@ export class ClientSession
391
370
  * This is because the serverSession is lazily acquired after a connection is obtained
392
371
  */
393
372
  incrementTransactionNumber(): void {
394
- this[kTxnNumberIncrement] += 1;
373
+ this.txnNumberIncrement += 1;
395
374
  }
396
375
 
397
376
  /** @returns whether this session is currently in a transaction or not */
@@ -410,7 +389,7 @@ export class ClientSession
410
389
  * @param options - Options for the transaction
411
390
  */
412
391
  startTransaction(options?: TransactionOptions): void {
413
- if (this[kSnapshotEnabled]) {
392
+ if (this.snapshotEnabled) {
414
393
  throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');
415
394
  }
416
395
 
@@ -908,7 +887,7 @@ export function maybeClearPinnedConnection(
908
887
  options?: EndSessionOptions
909
888
  ): void {
910
889
  // unpin a connection if it has been pinned
911
- const conn = session[kPinnedConnection];
890
+ const conn = session.pinnedConnection;
912
891
  const error = options?.error;
913
892
 
914
893
  if (
@@ -929,7 +908,7 @@ export function maybeClearPinnedConnection(
929
908
 
930
909
  if (options?.error == null || options?.force) {
931
910
  loadBalancer.pool.checkIn(conn);
932
- session[kPinnedConnection] = undefined;
911
+ session.pinnedConnection = undefined;
933
912
  conn.emit(
934
913
  UNPINNED,
935
914
  session.transaction.state !== TxnState.NO_TRANSACTION
@@ -1123,8 +1102,8 @@ export function applySession(
1123
1102
  const isRetryableWrite = !!options.willRetryWrite;
1124
1103
 
1125
1104
  if (isRetryableWrite || inTxnOrTxnCommand) {
1126
- serverSession.txnNumber += session[kTxnNumberIncrement];
1127
- session[kTxnNumberIncrement] = 0;
1105
+ serverSession.txnNumber += session.txnNumberIncrement;
1106
+ session.txnNumberIncrement = 0;
1128
1107
  // TODO(NODE-2674): Preserve int64 sent from MongoDB
1129
1108
  command.txnNumber = Long.fromNumber(serverSession.txnNumber);
1130
1109
  }
@@ -1141,10 +1120,10 @@ export function applySession(
1141
1120
  ) {
1142
1121
  command.readConcern = command.readConcern || {};
1143
1122
  Object.assign(command.readConcern, { afterClusterTime: session.operationTime });
1144
- } else if (session[kSnapshotEnabled]) {
1123
+ } else if (session.snapshotEnabled) {
1145
1124
  command.readConcern = command.readConcern || { level: ReadConcernLevel.snapshot };
1146
- if (session[kSnapshotTime] != null) {
1147
- Object.assign(command.readConcern, { atClusterTime: session[kSnapshotTime] });
1125
+ if (session.snapshotTime != null) {
1126
+ Object.assign(command.readConcern, { atClusterTime: session.snapshotTime });
1148
1127
  }
1149
1128
  }
1150
1129
 
@@ -1187,12 +1166,12 @@ export function updateSessionFromResponse(session: ClientSession, document: Mong
1187
1166
  session.transaction._recoveryToken = document.recoveryToken;
1188
1167
  }
1189
1168
 
1190
- if (session?.[kSnapshotEnabled] && session[kSnapshotTime] == null) {
1169
+ if (session?.snapshotEnabled && session.snapshotTime == null) {
1191
1170
  // find and aggregate commands return atClusterTime on the cursor
1192
1171
  // distinct includes it in the response body
1193
1172
  const atClusterTime = document.atClusterTime;
1194
1173
  if (atClusterTime) {
1195
- session[kSnapshotTime] = atClusterTime;
1174
+ session.snapshotTime = atClusterTime;
1196
1175
  }
1197
1176
  }
1198
1177
  }