mongodb 6.17.0 → 6.18.0-dev.20250724.sha.acd86250

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 (80) hide show
  1. package/README.md +1 -3
  2. package/lib/beta.d.ts +69 -23
  3. package/lib/bulk/common.js.map +1 -1
  4. package/lib/change_stream.js.map +1 -1
  5. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  6. package/lib/client-side-encryption/state_machine.js +5 -5
  7. package/lib/client-side-encryption/state_machine.js.map +1 -1
  8. package/lib/cmap/command_monitoring_events.js +2 -0
  9. package/lib/cmap/command_monitoring_events.js.map +1 -1
  10. package/lib/cmap/commands.js +10 -8
  11. package/lib/cmap/commands.js.map +1 -1
  12. package/lib/cmap/connection_pool.js +6 -2
  13. package/lib/cmap/connection_pool.js.map +1 -1
  14. package/lib/cmap/handshake/client_metadata.js +18 -1
  15. package/lib/cmap/handshake/client_metadata.js.map +1 -1
  16. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  17. package/lib/cmap/wire_protocol/on_data.js +4 -0
  18. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  19. package/lib/cmap/wire_protocol/on_demand/document.js +16 -15
  20. package/lib/cmap/wire_protocol/on_demand/document.js.map +1 -1
  21. package/lib/cmap/wire_protocol/responses.js +11 -4
  22. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  23. package/lib/connection_string.js +4 -5
  24. package/lib/connection_string.js.map +1 -1
  25. package/lib/cursor/abstract_cursor.js +45 -33
  26. package/lib/cursor/abstract_cursor.js.map +1 -1
  27. package/lib/cursor/run_command_cursor.js +3 -0
  28. package/lib/cursor/run_command_cursor.js.map +1 -1
  29. package/lib/index.js.map +1 -1
  30. package/lib/mongo_client.js +12 -0
  31. package/lib/mongo_client.js.map +1 -1
  32. package/lib/mongo_types.js +4 -1
  33. package/lib/mongo_types.js.map +1 -1
  34. package/lib/operations/command.js.map +1 -1
  35. package/lib/operations/distinct.js +1 -0
  36. package/lib/operations/distinct.js.map +1 -1
  37. package/lib/operations/rename.js.map +1 -1
  38. package/lib/operations/run_command.js.map +1 -1
  39. package/lib/operations/search_indexes/create.js.map +1 -1
  40. package/lib/operations/search_indexes/drop.js.map +1 -1
  41. package/lib/operations/search_indexes/update.js.map +1 -1
  42. package/lib/sessions.js.map +1 -1
  43. package/lib/transactions.js +9 -1
  44. package/lib/transactions.js.map +1 -1
  45. package/lib/utils.js +0 -1
  46. package/lib/utils.js.map +1 -1
  47. package/lib/write_concern.js +2 -4
  48. package/lib/write_concern.js.map +1 -1
  49. package/mongodb.d.ts +69 -23
  50. package/package.json +3 -3
  51. package/src/bulk/common.ts +3 -5
  52. package/src/change_stream.ts +38 -13
  53. package/src/client-side-encryption/auto_encrypter.ts +0 -1
  54. package/src/client-side-encryption/state_machine.ts +13 -11
  55. package/src/cmap/command_monitoring_events.ts +4 -0
  56. package/src/cmap/commands.ts +31 -16
  57. package/src/cmap/connection_pool.ts +6 -2
  58. package/src/cmap/handshake/client_metadata.ts +37 -4
  59. package/src/cmap/wire_protocol/compression.ts +2 -1
  60. package/src/cmap/wire_protocol/on_data.ts +5 -0
  61. package/src/cmap/wire_protocol/on_demand/document.ts +19 -14
  62. package/src/cmap/wire_protocol/responses.ts +8 -8
  63. package/src/connection_string.ts +5 -5
  64. package/src/cursor/abstract_cursor.ts +67 -44
  65. package/src/cursor/run_command_cursor.ts +7 -1
  66. package/src/index.ts +2 -0
  67. package/src/mongo_client.ts +48 -5
  68. package/src/mongo_types.ts +4 -1
  69. package/src/operations/command.ts +4 -0
  70. package/src/operations/distinct.ts +1 -0
  71. package/src/operations/rename.ts +8 -5
  72. package/src/operations/run_command.ts +17 -4
  73. package/src/operations/search_indexes/create.ts +6 -4
  74. package/src/operations/search_indexes/drop.ts +6 -4
  75. package/src/operations/search_indexes/update.ts +8 -5
  76. package/src/sessions.ts +1 -0
  77. package/src/transactions.ts +10 -1
  78. package/src/utils.ts +8 -8
  79. package/src/write_concern.ts +2 -4
  80. package/tsconfig.json +2 -1
@@ -76,12 +76,10 @@ export class OpQueryRequest {
76
76
  partial: boolean;
77
77
  /** moreToCome is an OP_MSG only concept */
78
78
  moreToCome = false;
79
+ databaseName: string;
80
+ query: Document;
79
81
 
80
- constructor(
81
- public databaseName: string,
82
- public query: Document,
83
- options: OpQueryOptions
84
- ) {
82
+ constructor(databaseName: string, query: Document, options: OpQueryOptions) {
85
83
  // Basic options needed to be passed in
86
84
  // TODO(NODE-3483): Replace with MongoCommandError
87
85
  const ns = `${databaseName}.$cmd`;
@@ -97,7 +95,9 @@ export class OpQueryRequest {
97
95
  throw new MongoRuntimeError('Namespace cannot contain a null character');
98
96
  }
99
97
 
100
- // Basic options
98
+ // Basic optionsa
99
+ this.databaseName = databaseName;
100
+ this.query = query;
101
101
  this.ns = ns;
102
102
 
103
103
  // Additional options
@@ -496,17 +496,18 @@ export class OpMsgRequest {
496
496
  checksumPresent: boolean;
497
497
  moreToCome: boolean;
498
498
  exhaustAllowed: boolean;
499
+ databaseName: string;
500
+ command: Document;
501
+ options: OpQueryOptions;
499
502
 
500
- constructor(
501
- public databaseName: string,
502
- public command: Document,
503
- public options: OpQueryOptions
504
- ) {
503
+ constructor(databaseName: string, command: Document, options: OpQueryOptions) {
505
504
  // Basic options needed to be passed in
506
505
  if (command == null)
507
506
  throw new MongoInvalidArgumentError('Query document must be specified for query');
508
507
 
509
- // Basic options
508
+ // Basic optionsa
509
+ this.databaseName = databaseName;
510
+ this.command = command;
510
511
  this.command.$db = databaseName;
511
512
 
512
513
  // Ensure empty options
@@ -724,16 +725,30 @@ export class OpMsgResponse {
724
725
  const MESSAGE_HEADER_SIZE = 16;
725
726
  const COMPRESSION_DETAILS_SIZE = 9; // originalOpcode + uncompressedSize, compressorID
726
727
 
728
+ /**
729
+ * @internal
730
+ */
731
+ export interface OpCompressesRequestOptions {
732
+ zlibCompressionLevel: number;
733
+ agreedCompressor: CompressorName;
734
+ }
735
+
727
736
  /**
728
737
  * @internal
729
738
  *
730
739
  * An OP_COMPRESSED request wraps either an OP_QUERY or OP_MSG message.
731
740
  */
732
741
  export class OpCompressedRequest {
733
- constructor(
734
- private command: WriteProtocolMessageType,
735
- private options: { zlibCompressionLevel: number; agreedCompressor: CompressorName }
736
- ) {}
742
+ private command: WriteProtocolMessageType;
743
+ private options: OpCompressesRequestOptions;
744
+
745
+ constructor(command: WriteProtocolMessageType, options: OpCompressesRequestOptions) {
746
+ this.command = command;
747
+ this.options = {
748
+ zlibCompressionLevel: options.zlibCompressionLevel,
749
+ agreedCompressor: options.agreedCompressor
750
+ };
751
+ }
737
752
 
738
753
  // Return whether a command contains an uncompressible command term
739
754
  // Will return true if command contains no uncompressible command terms
@@ -610,13 +610,17 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
610
610
  }
611
611
 
612
612
  private createConnection(callback: Callback<Connection>) {
613
+ // Note that metadata and extendedMetadata may have changed on the client but have
614
+ // been frozen here, so we pull the extendedMetadata promise always from the client
615
+ // no mattter what options were set at the construction of the pool.
613
616
  const connectOptions: ConnectionOptions = {
614
617
  ...this.options,
615
618
  id: this.connectionCounter.next().value,
616
619
  generation: this.generation,
617
620
  cancellationToken: this.cancellationToken,
618
621
  mongoLogger: this.mongoLogger,
619
- authProviders: this.server.topology.client.s.authProviders
622
+ authProviders: this.server.topology.client.s.authProviders,
623
+ extendedMetadata: this.server.topology.client.options.extendedMetadata
620
624
  };
621
625
 
622
626
  this.pending++;
@@ -691,7 +695,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
691
695
 
692
696
  private ensureMinPoolSize() {
693
697
  const minPoolSize = this.options.minPoolSize;
694
- if (this.poolState !== PoolState.ready || minPoolSize === 0) {
698
+ if (this.poolState !== PoolState.ready) {
695
699
  return;
696
700
  }
697
701
 
@@ -2,7 +2,7 @@ import * as os from 'os';
2
2
  import * as process from 'process';
3
3
 
4
4
  import { BSON, type Document, Int32 } from '../../bson';
5
- import { MongoInvalidArgumentError } from '../../error';
5
+ import { MongoInvalidArgumentError, MongoRuntimeError } from '../../error';
6
6
  import type { MongoOptions } from '../../mongo_client';
7
7
  import { fileIsAccessible } from '../../utils';
8
8
 
@@ -11,6 +11,7 @@ const NODE_DRIVER_VERSION = require('../../../package.json').version;
11
11
 
12
12
  /**
13
13
  * @public
14
+ * @deprecated This interface will be made internal in the next major release.
14
15
  * @see https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.md#hello-command
15
16
  */
16
17
  export interface ClientMetadata {
@@ -38,7 +39,10 @@ export interface ClientMetadata {
38
39
  };
39
40
  }
40
41
 
41
- /** @public */
42
+ /**
43
+ * @public
44
+ * @deprecated This interface will be made internal in the next major release.
45
+ */
42
46
  export interface ClientMetadataOptions {
43
47
  driverInfo?: {
44
48
  name?: string;
@@ -53,7 +57,11 @@ export class LimitedSizeDocument {
53
57
  private document = new Map();
54
58
  /** BSON overhead: Int32 + Null byte */
55
59
  private documentSize = 5;
56
- constructor(private maxSize: number) {}
60
+ private maxSize: number;
61
+
62
+ constructor(maxSize: number) {
63
+ this.maxSize = maxSize;
64
+ }
57
65
 
58
66
  /** Only adds key/value if the bsonByteLength is less than MAX_SIZE */
59
67
  public ifItFitsItSits(key: string, value: Record<string, any> | string): boolean {
@@ -82,7 +90,10 @@ export class LimitedSizeDocument {
82
90
  }
83
91
  }
84
92
 
85
- type MakeClientMetadataOptions = Pick<MongoOptions, 'appName' | 'driverInfo'>;
93
+ type MakeClientMetadataOptions = Pick<
94
+ MongoOptions,
95
+ 'appName' | 'driverInfo' | 'additionalDriverInfo'
96
+ >;
86
97
  /**
87
98
  * From the specs:
88
99
  * Implementors SHOULD cumulatively update fields in the following order until the document is under the size limit:
@@ -111,6 +122,22 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe
111
122
  version: version.length > 0 ? `${NODE_DRIVER_VERSION}|${version}` : NODE_DRIVER_VERSION
112
123
  };
113
124
 
125
+ if (options.additionalDriverInfo == null) {
126
+ throw new MongoRuntimeError(
127
+ 'Client options `additionalDriverInfo` must always default to an empty array'
128
+ );
129
+ }
130
+
131
+ // This is where we handle additional driver info added after client construction.
132
+ for (const { name: n = '', version: v = '' } of options.additionalDriverInfo) {
133
+ if (n.length > 0) {
134
+ driverInfo.name = `${driverInfo.name}|${n}`;
135
+ }
136
+ if (v.length > 0) {
137
+ driverInfo.version = `${driverInfo.version}|${v}`;
138
+ }
139
+ }
140
+
114
141
  if (!metadataDocument.ifItFitsItSits('driver', driverInfo)) {
115
142
  throw new MongoInvalidArgumentError(
116
143
  'Unable to include driverInfo name and version, metadata cannot exceed 512 bytes'
@@ -122,6 +149,12 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe
122
149
  runtimeInfo = `${runtimeInfo}|${platform}`;
123
150
  }
124
151
 
152
+ for (const { platform: p = '' } of options.additionalDriverInfo) {
153
+ if (p.length > 0) {
154
+ runtimeInfo = `${runtimeInfo}|${p}`;
155
+ }
156
+ }
157
+
125
158
  if (!metadataDocument.ifItFitsItSits('platform', runtimeInfo)) {
126
159
  throw new MongoInvalidArgumentError(
127
160
  'Unable to include driverInfo platform, metadata cannot exceed 512 bytes'
@@ -7,6 +7,7 @@ import { MongoDecompressionError, MongoInvalidArgumentError } from '../../error'
7
7
  import {
8
8
  type MessageHeader,
9
9
  OpCompressedRequest,
10
+ type OpCompressesRequestOptions,
10
11
  OpMsgResponse,
11
12
  OpReply,
12
13
  type WriteProtocolMessageType
@@ -60,7 +61,7 @@ function loadSnappy() {
60
61
 
61
62
  // Facilitate compressing a message using an agreed compressor
62
63
  export async function compress(
63
- options: { zlibCompressionLevel: number; agreedCompressor: CompressorName },
64
+ options: OpCompressesRequestOptions,
64
65
  dataToBeCompressed: Buffer
65
66
  ): Promise<Buffer> {
66
67
  const zlibOptions = {} as zlib.ZlibOptions;
@@ -87,6 +87,11 @@ export function onData(
87
87
 
88
88
  [Symbol.asyncIterator]() {
89
89
  return this;
90
+ },
91
+
92
+ // Note this should currently not be used, but is required by the AsyncGenerator interface.
93
+ async [Symbol.asyncDispose]() {
94
+ await closeHandler();
90
95
  }
91
96
  };
92
97
 
@@ -14,14 +14,13 @@ import {
14
14
  toUTF8
15
15
  } from '../../../bson';
16
16
 
17
- // eslint-disable-next-line no-restricted-syntax
18
- const enum BSONElementOffset {
19
- type = 0,
20
- nameOffset = 1,
21
- nameLength = 2,
22
- offset = 3,
23
- length = 4
24
- }
17
+ const BSONElementOffset = {
18
+ type: 0,
19
+ nameOffset: 1,
20
+ nameLength: 2,
21
+ offset: 3,
22
+ length: 4
23
+ } as const;
25
24
 
26
25
  /** @internal */
27
26
  export type JSTypeOf = {
@@ -67,17 +66,23 @@ export class OnDemandDocument {
67
66
 
68
67
  /** All bson elements in this document */
69
68
  private readonly elements: ReadonlyArray<BSONElement>;
69
+ /** BSON bytes, this document begins at offset */
70
+ protected readonly bson: Uint8Array;
71
+ /** The start of the document */
72
+ private readonly offset: number;
73
+ /** If this is an embedded document, indicates if this was a BSON array */
74
+ public readonly isArray: boolean;
70
75
 
71
76
  constructor(
72
- /** BSON bytes, this document begins at offset */
73
- protected readonly bson: Uint8Array,
74
- /** The start of the document */
75
- private readonly offset = 0,
76
- /** If this is an embedded document, indicates if this was a BSON array */
77
- public readonly isArray = false,
77
+ bson: Uint8Array,
78
+ offset = 0,
79
+ isArray = false,
78
80
  /** If elements was already calculated */
79
81
  elements?: BSONElement[]
80
82
  ) {
83
+ this.bson = bson;
84
+ this.offset = offset;
85
+ this.isArray = isArray;
81
86
  this.elements = elements ?? parseToElementsToArray(this.bson, offset);
82
87
  }
83
88
 
@@ -20,14 +20,14 @@ import {
20
20
  type OnDemandDocumentDeserializeOptions
21
21
  } from './on_demand/document';
22
22
 
23
- // eslint-disable-next-line no-restricted-syntax
24
- const enum BSONElementOffset {
25
- type = 0,
26
- nameOffset = 1,
27
- nameLength = 2,
28
- offset = 3,
29
- length = 4
30
- }
23
+ const BSONElementOffset = {
24
+ type: 0,
25
+ nameOffset: 1,
26
+ nameLength: 2,
27
+ offset: 3,
28
+ length: 4
29
+ } as const;
30
+
31
31
  /**
32
32
  * Accepts a BSON payload and checks for na "ok: 0" element.
33
33
  * This utility is intended to prevent calling response class constructors
@@ -172,11 +172,6 @@ function checkTLSOptions(allOptions: CaseInsensitiveMap): void {
172
172
  };
173
173
  check('tlsInsecure', 'tlsAllowInvalidCertificates');
174
174
  check('tlsInsecure', 'tlsAllowInvalidHostnames');
175
- check('tlsInsecure', 'tlsDisableCertificateRevocationCheck');
176
- check('tlsInsecure', 'tlsDisableOCSPEndpointCheck');
177
- check('tlsAllowInvalidCertificates', 'tlsDisableCertificateRevocationCheck');
178
- check('tlsAllowInvalidCertificates', 'tlsDisableOCSPEndpointCheck');
179
- check('tlsDisableCertificateRevocationCheck', 'tlsDisableOCSPEndpointCheck');
180
175
  }
181
176
  function getBoolean(name: string, value: unknown): boolean {
182
177
  if (typeof value === 'boolean') return value;
@@ -540,6 +535,9 @@ export function parseOptions(
540
535
  }
541
536
  );
542
537
 
538
+ // Set the default for the additional driver info.
539
+ mongoOptions.additionalDriverInfo = [];
540
+
543
541
  mongoOptions.metadata = makeClientMetadata(mongoOptions);
544
542
 
545
543
  mongoOptions.extendedMetadata = addContainerMetadata(mongoOptions.metadata).then(
@@ -605,6 +603,8 @@ function setOption(
605
603
  if (values[0] == null) {
606
604
  break;
607
605
  }
606
+ // The value should always be a string here, but since the array is typed as unknown
607
+ // there still needs to be an explicit cast.
608
608
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
609
609
  mongoOptions[name] = String(values[0]);
610
610
  break;
@@ -20,7 +20,7 @@ import { ReadConcern, type ReadConcernLike } from '../read_concern';
20
20
  import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
21
21
  import { type AsyncDisposable, configureResourceManagement } from '../resource_management';
22
22
  import type { Server } from '../sdam/server';
23
- import { ClientSession, maybeClearPinnedConnection } from '../sessions';
23
+ import { type ClientSession, maybeClearPinnedConnection } from '../sessions';
24
24
  import { type CSOTTimeoutContext, type Timeout, TimeoutContext } from '../timeout';
25
25
  import {
26
26
  addAbortListener,
@@ -227,7 +227,7 @@ export abstract class AbstractCursor<
227
227
  /** @internal */
228
228
  private cursorId: Long | null;
229
229
  /** @internal */
230
- private cursorSession: ClientSession;
230
+ private cursorSession: ClientSession | null;
231
231
  /** @internal */
232
232
  private selectedServer?: Server;
233
233
  /** @internal */
@@ -352,11 +352,7 @@ export abstract class AbstractCursor<
352
352
  this.cursorOptions.maxAwaitTimeMS = options.maxAwaitTimeMS;
353
353
  }
354
354
 
355
- if (options.session instanceof ClientSession) {
356
- this.cursorSession = options.session;
357
- } else {
358
- this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
359
- }
355
+ this.cursorSession = options.session ?? null;
360
356
 
361
357
  this.deserializationOptions = {
362
358
  ...this.cursorOptions,
@@ -413,7 +409,7 @@ export abstract class AbstractCursor<
413
409
  }
414
410
 
415
411
  /** @internal */
416
- get session(): ClientSession {
412
+ get session(): ClientSession | null {
417
413
  return this.cursorSession;
418
414
  }
419
415
 
@@ -877,11 +873,12 @@ export abstract class AbstractCursor<
877
873
  this.trackCursor();
878
874
 
879
875
  // We only want to end this session if we created it, and it hasn't ended yet
880
- if (this.cursorSession.explicit === false) {
876
+ if (this.cursorSession?.explicit === false) {
881
877
  if (!this.cursorSession.hasEnded) {
882
878
  this.cursorSession.endSession().then(undefined, squashError);
883
879
  }
884
- this.cursorSession = this.cursorClient.startSession({ owner: this, explicit: false });
880
+
881
+ this.cursorSession = null;
885
882
  }
886
883
  }
887
884
 
@@ -907,6 +904,13 @@ export abstract class AbstractCursor<
907
904
  'Unexpected null selectedServer. A cursor creating command should have set this'
908
905
  );
909
906
  }
907
+
908
+ if (this.cursorSession == null) {
909
+ throw new MongoRuntimeError(
910
+ 'Unexpected null session. A cursor creating command should have set this'
911
+ );
912
+ }
913
+
910
914
  const getMoreOptions = {
911
915
  ...this.cursorOptions,
912
916
  session: this.cursorSession,
@@ -941,6 +945,7 @@ export abstract class AbstractCursor<
941
945
  );
942
946
  }
943
947
  try {
948
+ this.cursorSession ??= this.cursorClient.startSession({ owner: this, explicit: false });
944
949
  const state = await this._initialize(this.cursorSession);
945
950
  // Set omitMaxTimeMS to the value needed for subsequent getMore calls
946
951
  this.cursorOptions.omitMaxTimeMS = this.cursorOptions.timeoutMS != null;
@@ -1032,41 +1037,57 @@ export abstract class AbstractCursor<
1032
1037
  return this.timeoutContext?.refreshed();
1033
1038
  }
1034
1039
  };
1035
- try {
1036
- if (
1037
- !this.isKilled &&
1038
- this.cursorId &&
1039
- !this.cursorId.isZero() &&
1040
- this.cursorNamespace &&
1041
- this.selectedServer &&
1042
- !this.cursorSession.hasEnded
1043
- ) {
1044
- this.isKilled = true;
1045
- const cursorId = this.cursorId;
1046
- this.cursorId = Long.ZERO;
1047
-
1048
- await executeOperation(
1049
- this.cursorClient,
1050
- new KillCursorsOperation(cursorId, this.cursorNamespace, this.selectedServer, {
1051
- session: this.cursorSession
1052
- }),
1053
- timeoutContextForKillCursors()
1054
- );
1040
+
1041
+ const withEmitClose = async (fn: () => Promise<void>) => {
1042
+ try {
1043
+ await fn();
1044
+ } finally {
1045
+ this.emitClose();
1055
1046
  }
1056
- } catch (error) {
1057
- squashError(error);
1058
- } finally {
1047
+ };
1048
+
1049
+ const close = async () => {
1050
+ // if no session has been defined on the cursor, the cursor was never initialized
1051
+ // or the cursor was re-wound and never re-iterated. In either case, we
1052
+ // 1. do not need to end the session (there is no session after all)
1053
+ // 2. do not need to kill the cursor server-side
1054
+ const session = this.cursorSession;
1055
+ if (!session) return;
1056
+
1059
1057
  try {
1060
- if (this.cursorSession?.owner === this) {
1061
- await this.cursorSession.endSession({ error });
1062
- }
1063
- if (!this.cursorSession?.inTransaction()) {
1064
- maybeClearPinnedConnection(this.cursorSession, { error });
1058
+ if (
1059
+ !this.isKilled &&
1060
+ this.cursorId &&
1061
+ !this.cursorId.isZero() &&
1062
+ this.cursorNamespace &&
1063
+ this.selectedServer &&
1064
+ !session.hasEnded
1065
+ ) {
1066
+ this.isKilled = true;
1067
+ const cursorId = this.cursorId;
1068
+ this.cursorId = Long.ZERO;
1069
+
1070
+ await executeOperation(
1071
+ this.cursorClient,
1072
+ new KillCursorsOperation(cursorId, this.cursorNamespace, this.selectedServer, {
1073
+ session
1074
+ }),
1075
+ timeoutContextForKillCursors()
1076
+ );
1065
1077
  }
1078
+ } catch (error) {
1079
+ squashError(error);
1066
1080
  } finally {
1067
- this.emitClose();
1081
+ if (session.owner === this) {
1082
+ await session.endSession({ error });
1083
+ }
1084
+ if (!session.inTransaction()) {
1085
+ maybeClearPinnedConnection(session, { error });
1086
+ }
1068
1087
  }
1069
- }
1088
+ };
1089
+
1090
+ await withEmitClose(close);
1070
1091
  }
1071
1092
 
1072
1093
  /** @internal */
@@ -1213,11 +1234,13 @@ configureResourceManagement(AbstractCursor.prototype);
1213
1234
  * All timeout behavior is exactly the same as the wrapped timeout context's.
1214
1235
  */
1215
1236
  export class CursorTimeoutContext extends TimeoutContext {
1216
- constructor(
1217
- public timeoutContext: TimeoutContext,
1218
- public owner: symbol | AbstractCursor
1219
- ) {
1237
+ timeoutContext: TimeoutContext;
1238
+ owner: symbol | AbstractCursor;
1239
+
1240
+ constructor(timeoutContext: TimeoutContext, owner: symbol | AbstractCursor) {
1220
1241
  super();
1242
+ this.timeoutContext = timeoutContext;
1243
+ this.owner = owner;
1221
1244
  }
1222
1245
  override get serverSelectionTimeout(): Timeout | null {
1223
1246
  return this.timeoutContext.serverSelectionTimeout;
@@ -1,7 +1,7 @@
1
1
  import type { BSONSerializeOptions, Document } from '../bson';
2
2
  import { CursorResponse } from '../cmap/wire_protocol/responses';
3
3
  import type { Db } from '../db';
4
- import { MongoAPIError } from '../error';
4
+ import { MongoAPIError, MongoRuntimeError } from '../error';
5
5
  import { executeOperation } from '../operations/execute_operation';
6
6
  import { GetMoreOperation } from '../operations/get_more';
7
7
  import { RunCommandOperation } from '../operations/run_command';
@@ -161,6 +161,12 @@ export class RunCommandCursor extends AbstractCursor {
161
161
 
162
162
  /** @internal */
163
163
  override async getMore(_batchSize: number): Promise<CursorResponse> {
164
+ if (!this.session) {
165
+ throw new MongoRuntimeError(
166
+ 'Unexpected null session. A cursor creating command should have set this'
167
+ );
168
+ }
169
+
164
170
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
165
171
  const getMoreOperation = new GetMoreOperation(this.namespace, this.id!, this.server!, {
166
172
  ...this.cursorOptions,
package/src/index.ts CHANGED
@@ -208,6 +208,7 @@ export type {
208
208
  ChangeStreamDocumentCommon,
209
209
  ChangeStreamDocumentKey,
210
210
  ChangeStreamDocumentOperationDescription,
211
+ ChangeStreamDocumentWallTime,
211
212
  ChangeStreamDropDatabaseDocument,
212
213
  ChangeStreamDropDocument,
213
214
  ChangeStreamDropIndexDocument,
@@ -287,6 +288,7 @@ export type { TokenCache } from './cmap/auth/mongodb_oidc/token_cache';
287
288
  export type {
288
289
  MessageHeader,
289
290
  OpCompressedRequest,
291
+ OpCompressesRequestOptions,
290
292
  OpMsgOptions,
291
293
  OpMsgRequest,
292
294
  OpMsgResponse,
@@ -14,7 +14,11 @@ import { type TokenCache } from './cmap/auth/mongodb_oidc/token_cache';
14
14
  import { AuthMechanism } from './cmap/auth/providers';
15
15
  import type { LEGAL_TCP_SOCKET_OPTIONS, LEGAL_TLS_SOCKET_OPTIONS } from './cmap/connect';
16
16
  import type { Connection } from './cmap/connection';
17
- import type { ClientMetadata } from './cmap/handshake/client_metadata';
17
+ import {
18
+ addContainerMetadata,
19
+ type ClientMetadata,
20
+ makeClientMetadata
21
+ } from './cmap/handshake/client_metadata';
18
22
  import type { CompressorName } from './cmap/wire_protocol/compression';
19
23
  import { parseOptions, resolveSRVRecord } from './connection_string';
20
24
  import { MONGO_CLIENT_EVENTS } from './constants';
@@ -184,7 +188,11 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
184
188
  minPoolSize?: number;
185
189
  /** The maximum number of connections that may be in the process of being established concurrently by the connection pool. */
186
190
  maxConnecting?: number;
187
- /** The maximum number of milliseconds that a connection can remain idle in the pool before being removed and closed. */
191
+ /**
192
+ * The maximum amount of time a connection should remain idle in the connection pool before being marked idle, in milliseconds.
193
+ * If specified, this must be a number greater than or equal to 0, where 0 means there is no limit. Defaults to 0. After this
194
+ * time passes, the idle collection can be automatically cleaned up in the background.
195
+ */
188
196
  maxIdleTimeMS?: number;
189
197
  /** The maximum time in milliseconds that a thread can wait for a connection to become available. */
190
198
  waitQueueTimeoutMS?: number;
@@ -394,9 +402,31 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
394
402
  * The consolidate, parsed, transformed and merged options.
395
403
  */
396
404
  public readonly options: Readonly<
397
- Omit<MongoOptions, 'monitorCommands' | 'ca' | 'crl' | 'key' | 'cert'>
405
+ Omit<
406
+ MongoOptions,
407
+ | 'monitorCommands'
408
+ | 'ca'
409
+ | 'crl'
410
+ | 'key'
411
+ | 'cert'
412
+ | 'driverInfo'
413
+ | 'additionalDriverInfo'
414
+ | 'metadata'
415
+ | 'extendedMetadata'
416
+ >
398
417
  > &
399
- Pick<MongoOptions, 'monitorCommands' | 'ca' | 'crl' | 'key' | 'cert'>;
418
+ Pick<
419
+ MongoOptions,
420
+ | 'monitorCommands'
421
+ | 'ca'
422
+ | 'crl'
423
+ | 'key'
424
+ | 'cert'
425
+ | 'driverInfo'
426
+ | 'additionalDriverInfo'
427
+ | 'metadata'
428
+ | 'extendedMetadata'
429
+ >;
400
430
 
401
431
  constructor(url: string, options?: MongoClientOptions) {
402
432
  super();
@@ -455,6 +485,18 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
455
485
  await this.close();
456
486
  }
457
487
 
488
+ /**
489
+ * Append metadata to the client metadata after instantiation.
490
+ * @param driverInfo - Information about the application or library.
491
+ */
492
+ appendMetadata(driverInfo: DriverInfo) {
493
+ this.options.additionalDriverInfo.push(driverInfo);
494
+ this.options.metadata = makeClientMetadata(this.options);
495
+ this.options.extendedMetadata = addContainerMetadata(this.options.metadata)
496
+ .then(undefined, squashError)
497
+ .then(result => result ?? {}); // ensure Promise<Document>
498
+ }
499
+
458
500
  /** @internal */
459
501
  private checkForNonGenuineHosts() {
460
502
  const documentDBHostnames = this.options.hosts.filter((hostAddress: HostAddress) =>
@@ -1035,9 +1077,10 @@ export interface MongoOptions
1035
1077
  compressors: CompressorName[];
1036
1078
  writeConcern: WriteConcern;
1037
1079
  dbName: string;
1080
+ /** @deprecated - Will be made internal in a future major release. */
1038
1081
  metadata: ClientMetadata;
1039
- /** @internal */
1040
1082
  extendedMetadata: Promise<Document>;
1083
+ additionalDriverInfo: DriverInfo[];
1041
1084
  /** @internal */
1042
1085
  autoEncrypter?: AutoEncrypter;
1043
1086
  /** @internal */
@@ -472,7 +472,10 @@ export class TypedEventEmitter<Events extends EventsDescription> extends EventEm
472
472
  }
473
473
  }
474
474
 
475
- /** @public */
475
+ /**
476
+ * @public
477
+ * @deprecated Will be removed in favor of `AbortSignal` in the next major release.
478
+ */
476
479
  export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> {
477
480
  constructor(...args: any[]) {
478
481
  super(...args);
@@ -59,6 +59,10 @@ export interface CommandOperationOptions
59
59
  // Admin command overrides.
60
60
  dbName?: string;
61
61
  authdb?: string;
62
+ /**
63
+ * @deprecated
64
+ * This option is deprecated and will be removed in an upcoming major version.
65
+ */
62
66
  noResponse?: boolean;
63
67
  }
64
68