mongodb 6.18.0 → 6.19.0-dev.20250827.sha.3c5bb1d5

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 (158) hide show
  1. package/lib/admin.js +2 -1
  2. package/lib/admin.js.map +1 -1
  3. package/lib/beta.d.ts +59 -7
  4. package/lib/bulk/common.js +19 -32
  5. package/lib/bulk/common.js.map +1 -1
  6. package/lib/client-side-encryption/client_encryption.js +4 -1
  7. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  8. package/lib/client-side-encryption/state_machine.js +4 -0
  9. package/lib/client-side-encryption/state_machine.js.map +1 -1
  10. package/lib/cmap/connection.js.map +1 -1
  11. package/lib/collection.js +62 -19
  12. package/lib/collection.js.map +1 -1
  13. package/lib/cursor/aggregation_cursor.js +2 -1
  14. package/lib/cursor/aggregation_cursor.js.map +1 -1
  15. package/lib/cursor/explainable_cursor.js +36 -0
  16. package/lib/cursor/explainable_cursor.js.map +1 -0
  17. package/lib/cursor/find_cursor.js +2 -1
  18. package/lib/cursor/find_cursor.js.map +1 -1
  19. package/lib/cursor/run_command_cursor.js +2 -4
  20. package/lib/cursor/run_command_cursor.js.map +1 -1
  21. package/lib/db.js +12 -5
  22. package/lib/db.js.map +1 -1
  23. package/lib/explain.js +1 -33
  24. package/lib/explain.js.map +1 -1
  25. package/lib/index.js +6 -6
  26. package/lib/index.js.map +1 -1
  27. package/lib/mongo_client.js +25 -2
  28. package/lib/mongo_client.js.map +1 -1
  29. package/lib/operations/aggregate.js +9 -7
  30. package/lib/operations/aggregate.js.map +1 -1
  31. package/lib/operations/client_bulk_write/client_bulk_write.js +8 -41
  32. package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -1
  33. package/lib/operations/command.js +16 -19
  34. package/lib/operations/command.js.map +1 -1
  35. package/lib/operations/count.js +7 -3
  36. package/lib/operations/count.js.map +1 -1
  37. package/lib/operations/create_collection.js +56 -49
  38. package/lib/operations/create_collection.js.map +1 -1
  39. package/lib/operations/delete.js +15 -12
  40. package/lib/operations/delete.js.map +1 -1
  41. package/lib/operations/distinct.js +18 -26
  42. package/lib/operations/distinct.js.map +1 -1
  43. package/lib/operations/drop.js +57 -39
  44. package/lib/operations/drop.js.map +1 -1
  45. package/lib/operations/estimated_document_count.js +7 -3
  46. package/lib/operations/estimated_document_count.js.map +1 -1
  47. package/lib/operations/execute_operation.js +14 -3
  48. package/lib/operations/execute_operation.js.map +1 -1
  49. package/lib/operations/find.js +21 -30
  50. package/lib/operations/find.js.map +1 -1
  51. package/lib/operations/find_and_modify.js +52 -41
  52. package/lib/operations/find_and_modify.js.map +1 -1
  53. package/lib/operations/get_more.js +10 -11
  54. package/lib/operations/get_more.js.map +1 -1
  55. package/lib/operations/indexes.js +21 -12
  56. package/lib/operations/indexes.js.map +1 -1
  57. package/lib/operations/insert.js +8 -47
  58. package/lib/operations/insert.js.map +1 -1
  59. package/lib/operations/kill_cursors.js +13 -15
  60. package/lib/operations/kill_cursors.js.map +1 -1
  61. package/lib/operations/list_collections.js +7 -6
  62. package/lib/operations/list_collections.js.map +1 -1
  63. package/lib/operations/list_databases.js +5 -3
  64. package/lib/operations/list_databases.js.map +1 -1
  65. package/lib/operations/operation.js +30 -1
  66. package/lib/operations/operation.js.map +1 -1
  67. package/lib/operations/profiling_level.js +14 -4
  68. package/lib/operations/profiling_level.js.map +1 -1
  69. package/lib/operations/remove_user.js +6 -2
  70. package/lib/operations/remove_user.js.map +1 -1
  71. package/lib/operations/rename.js +10 -8
  72. package/lib/operations/rename.js.map +1 -1
  73. package/lib/operations/run_command.js +28 -32
  74. package/lib/operations/run_command.js.map +1 -1
  75. package/lib/operations/search_indexes/create.js +11 -8
  76. package/lib/operations/search_indexes/create.js.map +1 -1
  77. package/lib/operations/search_indexes/drop.js +16 -9
  78. package/lib/operations/search_indexes/drop.js.map +1 -1
  79. package/lib/operations/search_indexes/update.js +11 -4
  80. package/lib/operations/search_indexes/update.js.map +1 -1
  81. package/lib/operations/set_profiling_level.js +8 -4
  82. package/lib/operations/set_profiling_level.js.map +1 -1
  83. package/lib/operations/stats.js +4 -2
  84. package/lib/operations/stats.js.map +1 -1
  85. package/lib/operations/update.js +22 -19
  86. package/lib/operations/update.js.map +1 -1
  87. package/lib/operations/validate_collection.js +17 -18
  88. package/lib/operations/validate_collection.js.map +1 -1
  89. package/lib/sdam/server.js +46 -35
  90. package/lib/sdam/server.js.map +1 -1
  91. package/lib/sdam/topology.js +4 -3
  92. package/lib/sdam/topology.js.map +1 -1
  93. package/lib/sessions.js +3 -3
  94. package/lib/sessions.js.map +1 -1
  95. package/lib/utils.js +8 -13
  96. package/lib/utils.js.map +1 -1
  97. package/mongodb.d.ts +59 -7
  98. package/package.json +19 -19
  99. package/src/admin.ts +3 -2
  100. package/src/bulk/common.ts +21 -41
  101. package/src/client-side-encryption/client_encryption.ts +52 -3
  102. package/src/client-side-encryption/state_machine.ts +5 -1
  103. package/src/cmap/connection.ts +0 -1
  104. package/src/collection.ts +94 -65
  105. package/src/cursor/aggregation_cursor.ts +1 -1
  106. package/src/cursor/explainable_cursor.ts +51 -0
  107. package/src/cursor/find_cursor.ts +1 -1
  108. package/src/cursor/run_command_cursor.ts +4 -5
  109. package/src/db.ts +16 -19
  110. package/src/explain.ts +0 -49
  111. package/src/index.ts +4 -4
  112. package/src/mongo_client.ts +24 -9
  113. package/src/operations/aggregate.ts +15 -20
  114. package/src/operations/client_bulk_write/client_bulk_write.ts +21 -65
  115. package/src/operations/command.ts +22 -45
  116. package/src/operations/count.ts +9 -9
  117. package/src/operations/create_collection.ts +83 -75
  118. package/src/operations/delete.ts +27 -35
  119. package/src/operations/distinct.ts +23 -40
  120. package/src/operations/drop.ts +76 -60
  121. package/src/operations/estimated_document_count.ts +8 -9
  122. package/src/operations/execute_operation.ts +21 -11
  123. package/src/operations/find.ts +40 -57
  124. package/src/operations/find_and_modify.ts +78 -54
  125. package/src/operations/get_more.ts +17 -19
  126. package/src/operations/indexes.ts +26 -30
  127. package/src/operations/insert.ts +11 -77
  128. package/src/operations/kill_cursors.ts +21 -22
  129. package/src/operations/list_collections.ts +13 -21
  130. package/src/operations/list_databases.ts +6 -15
  131. package/src/operations/operation.ts +47 -10
  132. package/src/operations/profiling_level.ts +17 -11
  133. package/src/operations/remove_user.ts +9 -9
  134. package/src/operations/rename.ts +11 -14
  135. package/src/operations/run_command.ts +40 -68
  136. package/src/operations/search_indexes/create.ts +15 -15
  137. package/src/operations/search_indexes/drop.ts +23 -14
  138. package/src/operations/search_indexes/update.ts +14 -9
  139. package/src/operations/set_profiling_level.ts +13 -11
  140. package/src/operations/stats.ts +5 -10
  141. package/src/operations/update.ts +49 -52
  142. package/src/operations/validate_collection.ts +19 -27
  143. package/src/sdam/server.ts +56 -59
  144. package/src/sdam/topology.ts +5 -4
  145. package/src/sessions.ts +5 -4
  146. package/src/utils.ts +10 -25
  147. package/lib/operations/bulk_write.js +0 -39
  148. package/lib/operations/bulk_write.js.map +0 -1
  149. package/lib/operations/collections.js +0 -33
  150. package/lib/operations/collections.js.map +0 -1
  151. package/lib/operations/is_capped.js +0 -28
  152. package/lib/operations/is_capped.js.map +0 -1
  153. package/lib/operations/options_operation.js +0 -28
  154. package/lib/operations/options_operation.js.map +0 -1
  155. package/src/operations/bulk_write.ts +0 -64
  156. package/src/operations/collections.ts +0 -47
  157. package/src/operations/is_capped.ts +0 -35
  158. package/src/operations/options_operation.ts +0 -35
@@ -1,12 +1,15 @@
1
1
  import type { Document } from '../../bson';
2
+ import { type Connection } from '../../cmap/connection';
3
+ import { MongoDBResponse } from '../../cmap/wire_protocol/responses';
2
4
  import type { Collection } from '../../collection';
3
- import type { Server } from '../../sdam/server';
5
+ import type { ServerCommandOptions } from '../../sdam/server';
4
6
  import type { ClientSession } from '../../sessions';
5
7
  import { type TimeoutContext } from '../../timeout';
6
8
  import { AbstractOperation } from '../operation';
7
9
 
8
10
  /** @internal */
9
11
  export class UpdateSearchIndexOperation extends AbstractOperation<void> {
12
+ override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
10
13
  private readonly collection: Collection;
11
14
  private readonly name: string;
12
15
  private readonly definition: Document;
@@ -16,25 +19,27 @@ export class UpdateSearchIndexOperation extends AbstractOperation<void> {
16
19
  this.collection = collection;
17
20
  this.name = name;
18
21
  this.definition = definition;
22
+ this.ns = collection.fullNamespace;
19
23
  }
20
24
 
21
25
  override get commandName() {
22
26
  return 'updateSearchIndex' as const;
23
27
  }
24
28
 
25
- override async execute(
26
- server: Server,
27
- session: ClientSession | undefined,
28
- timeoutContext: TimeoutContext
29
- ): Promise<void> {
29
+ override buildCommand(_connection: Connection, _session?: ClientSession): Document {
30
30
  const namespace = this.collection.fullNamespace;
31
- const command = {
31
+ return {
32
32
  updateSearchIndex: namespace.collection,
33
33
  name: this.name,
34
34
  definition: this.definition
35
35
  };
36
+ }
37
+
38
+ override handleOk(_response: MongoDBResponse): void {
39
+ // no response.
40
+ }
36
41
 
37
- await server.command(namespace, command, { session, timeoutContext });
38
- return;
42
+ override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {
43
+ return { session: this.session, timeoutContext };
39
44
  }
40
45
  }
@@ -1,8 +1,8 @@
1
+ import { type Document } from '../bson';
2
+ import { type Connection } from '../cmap/connection';
3
+ import { MongoDBResponse } from '../cmap/wire_protocol/responses';
1
4
  import type { Db } from '../db';
2
5
  import { MongoInvalidArgumentError } from '../error';
3
- import type { Server } from '../sdam/server';
4
- import type { ClientSession } from '../sessions';
5
- import { type TimeoutContext } from '../timeout';
6
6
  import { enumToString } from '../utils';
7
7
  import { CommandOperation, type CommandOperationOptions } from './command';
8
8
 
@@ -23,6 +23,7 @@ export type SetProfilingLevelOptions = CommandOperationOptions;
23
23
 
24
24
  /** @internal */
25
25
  export class SetProfilingLevelOperation extends CommandOperation<ProfilingLevel> {
26
+ override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
26
27
  override options: SetProfilingLevelOptions;
27
28
  level: ProfilingLevel;
28
29
  profile: 0 | 1 | 2;
@@ -52,21 +53,22 @@ export class SetProfilingLevelOperation extends CommandOperation<ProfilingLevel>
52
53
  return 'profile' as const;
53
54
  }
54
55
 
55
- override async execute(
56
- server: Server,
57
- session: ClientSession | undefined,
58
- timeoutContext: TimeoutContext
59
- ): Promise<ProfilingLevel> {
56
+ override buildCommandDocument(_connection: Connection): Document {
60
57
  const level = this.level;
61
58
 
62
59
  if (!levelValues.has(level)) {
60
+ // TODO(NODE-3483): Determine error to put here
63
61
  throw new MongoInvalidArgumentError(
64
62
  `Profiling level must be one of "${enumToString(ProfilingLevel)}"`
65
63
  );
66
64
  }
67
65
 
68
- // TODO(NODE-3483): Determine error to put here
69
- await super.executeCommand(server, session, { profile: this.profile }, timeoutContext);
70
- return level;
66
+ return { profile: this.profile };
67
+ }
68
+
69
+ override handleOk(
70
+ _response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
71
+ ): ProfilingLevel {
72
+ return this.level;
71
73
  }
72
74
  }
@@ -1,8 +1,7 @@
1
1
  import type { Document } from '../bson';
2
+ import { type Connection } from '../cmap/connection';
3
+ import { MongoDBResponse } from '../cmap/wire_protocol/responses';
2
4
  import type { Db } from '../db';
3
- import type { Server } from '../sdam/server';
4
- import type { ClientSession } from '../sessions';
5
- import { type TimeoutContext } from '../timeout';
6
5
  import { CommandOperation, type CommandOperationOptions } from './command';
7
6
  import { Aspect, defineAspects } from './operation';
8
7
 
@@ -14,6 +13,7 @@ export interface DbStatsOptions extends CommandOperationOptions {
14
13
 
15
14
  /** @internal */
16
15
  export class DbStatsOperation extends CommandOperation<Document> {
16
+ override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
17
17
  override options: DbStatsOptions;
18
18
 
19
19
  constructor(db: Db, options: DbStatsOptions) {
@@ -25,17 +25,12 @@ export class DbStatsOperation extends CommandOperation<Document> {
25
25
  return 'dbStats' as const;
26
26
  }
27
27
 
28
- override async execute(
29
- server: Server,
30
- session: ClientSession | undefined,
31
- timeoutContext: TimeoutContext
32
- ): Promise<Document> {
28
+ override buildCommandDocument(_connection: Connection): Document {
33
29
  const command: Document = { dbStats: true };
34
30
  if (this.options.scale != null) {
35
31
  command.scale = this.options.scale;
36
32
  }
37
-
38
- return await super.executeCommand(server, session, command, timeoutContext);
33
+ return command;
39
34
  }
40
35
  }
41
36
 
@@ -1,12 +1,15 @@
1
1
  import type { Document } from '../bson';
2
- import type { Collection } from '../collection';
2
+ import { type Connection } from '../cmap/connection';
3
+ import { MongoDBResponse } from '../cmap/wire_protocol/responses';
3
4
  import { MongoCompatibilityError, MongoInvalidArgumentError, MongoServerError } from '../error';
4
- import type { InferIdType, TODO_NODE_3286 } from '../mongo_types';
5
- import type { Server } from '../sdam/server';
5
+ import type { InferIdType } from '../mongo_types';
6
6
  import type { ClientSession } from '../sessions';
7
7
  import { formatSort, type Sort, type SortForCmd } from '../sort';
8
- import { type TimeoutContext } from '../timeout';
9
- import { hasAtomicOperators, type MongoDBNamespace } from '../utils';
8
+ import {
9
+ hasAtomicOperators,
10
+ type MongoDBCollectionNamespace,
11
+ type MongoDBNamespace
12
+ } from '../utils';
10
13
  import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
11
14
  import { Aspect, defineAspects, type Hint } from './operation';
12
15
 
@@ -68,6 +71,7 @@ export interface UpdateStatement {
68
71
  * UpdateOperation is used in bulk write, while UpdateOneOperation and UpdateManyOperation are only used in the collections API
69
72
  */
70
73
  export class UpdateOperation extends CommandOperation<Document> {
74
+ override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
71
75
  override options: UpdateOptions & { ordered?: boolean };
72
76
  statements: UpdateStatement[];
73
77
 
@@ -95,17 +99,12 @@ export class UpdateOperation extends CommandOperation<Document> {
95
99
  return this.statements.every(op => op.multi == null || op.multi === false);
96
100
  }
97
101
 
98
- override async execute(
99
- server: Server,
100
- session: ClientSession | undefined,
101
- timeoutContext: TimeoutContext
102
- ): Promise<Document> {
103
- const options = this.options ?? {};
104
- const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
102
+ override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
103
+ const options = this.options;
105
104
  const command: Document = {
106
105
  update: this.ns.collection,
107
106
  updates: this.statements,
108
- ordered
107
+ ordered: options.ordered ?? true
109
108
  };
110
109
 
111
110
  if (typeof options.bypassDocumentValidation === 'boolean') {
@@ -122,7 +121,7 @@ export class UpdateOperation extends CommandOperation<Document> {
122
121
  command.comment = options.comment;
123
122
  }
124
123
 
125
- const unacknowledgedWrite = this.writeConcern && this.writeConcern.w === 0;
124
+ const unacknowledgedWrite = this.writeConcern?.w === 0;
126
125
  if (unacknowledgedWrite) {
127
126
  if (this.statements.find((o: Document) => o.hint)) {
128
127
  // TODO(NODE-3541): fix error for hint with unacknowledged writes
@@ -130,32 +129,33 @@ export class UpdateOperation extends CommandOperation<Document> {
130
129
  }
131
130
  }
132
131
 
133
- const res = await super.executeCommand(server, session, command, timeoutContext);
134
- return res;
132
+ return command;
135
133
  }
136
134
  }
137
135
 
138
136
  /** @internal */
139
137
  export class UpdateOneOperation extends UpdateOperation {
140
- constructor(collection: Collection, filter: Document, update: Document, options: UpdateOptions) {
141
- super(
142
- collection.s.namespace,
143
- [makeUpdateStatement(filter, update, { ...options, multi: false })],
144
- options
145
- );
138
+ constructor(
139
+ ns: MongoDBCollectionNamespace,
140
+ filter: Document,
141
+ update: Document,
142
+ options: UpdateOptions
143
+ ) {
144
+ super(ns, [makeUpdateStatement(filter, update, { ...options, multi: false })], options);
146
145
 
147
146
  if (!hasAtomicOperators(update, options)) {
148
147
  throw new MongoInvalidArgumentError('Update document requires atomic operators');
149
148
  }
150
149
  }
151
150
 
152
- override async execute(
153
- server: Server,
154
- session: ClientSession | undefined,
155
- timeoutContext: TimeoutContext
156
- ): Promise<UpdateResult> {
157
- const res: TODO_NODE_3286 = await super.execute(server, session, timeoutContext);
151
+ override handleOk(
152
+ response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
153
+ ): UpdateResult {
154
+ const res = super.handleOk(response);
155
+
156
+ // @ts-expect-error Explain typing is broken
158
157
  if (this.explain != null) return res;
158
+
159
159
  if (res.code) throw new MongoServerError(res);
160
160
  if (res.writeErrors) throw new MongoServerError(res.writeErrors[0]);
161
161
 
@@ -172,24 +172,25 @@ export class UpdateOneOperation extends UpdateOperation {
172
172
 
173
173
  /** @internal */
174
174
  export class UpdateManyOperation extends UpdateOperation {
175
- constructor(collection: Collection, filter: Document, update: Document, options: UpdateOptions) {
176
- super(
177
- collection.s.namespace,
178
- [makeUpdateStatement(filter, update, { ...options, multi: true })],
179
- options
180
- );
175
+ constructor(
176
+ ns: MongoDBCollectionNamespace,
177
+ filter: Document,
178
+ update: Document,
179
+ options: UpdateOptions
180
+ ) {
181
+ super(ns, [makeUpdateStatement(filter, update, { ...options, multi: true })], options);
181
182
 
182
183
  if (!hasAtomicOperators(update, options)) {
183
184
  throw new MongoInvalidArgumentError('Update document requires atomic operators');
184
185
  }
185
186
  }
186
187
 
187
- override async execute(
188
- server: Server,
189
- session: ClientSession | undefined,
190
- timeoutContext: TimeoutContext
191
- ): Promise<UpdateResult> {
192
- const res: TODO_NODE_3286 = await super.execute(server, session, timeoutContext);
188
+ override handleOk(
189
+ response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
190
+ ): UpdateResult {
191
+ const res = super.handleOk(response);
192
+
193
+ // @ts-expect-error Explain typing is broken
193
194
  if (this.explain != null) return res;
194
195
  if (res.code) throw new MongoServerError(res);
195
196
  if (res.writeErrors) throw new MongoServerError(res.writeErrors[0]);
@@ -224,28 +225,24 @@ export interface ReplaceOptions extends CommandOperationOptions {
224
225
  /** @internal */
225
226
  export class ReplaceOneOperation extends UpdateOperation {
226
227
  constructor(
227
- collection: Collection,
228
+ ns: MongoDBCollectionNamespace,
228
229
  filter: Document,
229
230
  replacement: Document,
230
231
  options: ReplaceOptions
231
232
  ) {
232
- super(
233
- collection.s.namespace,
234
- [makeUpdateStatement(filter, replacement, { ...options, multi: false })],
235
- options
236
- );
233
+ super(ns, [makeUpdateStatement(filter, replacement, { ...options, multi: false })], options);
237
234
 
238
235
  if (hasAtomicOperators(replacement)) {
239
236
  throw new MongoInvalidArgumentError('Replacement document must not contain atomic operators');
240
237
  }
241
238
  }
242
239
 
243
- override async execute(
244
- server: Server,
245
- session: ClientSession | undefined,
246
- timeoutContext: TimeoutContext
247
- ): Promise<UpdateResult> {
248
- const res: TODO_NODE_3286 = await super.execute(server, session, timeoutContext);
240
+ override handleOk(
241
+ response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
242
+ ): UpdateResult {
243
+ const res = super.handleOk(response);
244
+
245
+ // @ts-expect-error Explain typing is broken
249
246
  if (this.explain != null) return res;
250
247
  if (res.code) throw new MongoServerError(res);
251
248
  if (res.writeErrors) throw new MongoServerError(res.writeErrors[0]);
@@ -1,9 +1,9 @@
1
+ import { type Connection } from '..';
1
2
  import type { Admin } from '../admin';
2
- import type { Document } from '../bson';
3
+ import { type Document } from '../bson';
4
+ import { MongoDBResponse } from '../cmap/wire_protocol/responses';
3
5
  import { MongoUnexpectedServerResponseError } from '../error';
4
- import type { Server } from '../sdam/server';
5
6
  import type { ClientSession } from '../sessions';
6
- import { type TimeoutContext } from '../timeout';
7
7
  import { CommandOperation, type CommandOperationOptions } from './command';
8
8
 
9
9
  /** @public */
@@ -14,23 +14,13 @@ export interface ValidateCollectionOptions extends CommandOperationOptions {
14
14
 
15
15
  /** @internal */
16
16
  export class ValidateCollectionOperation extends CommandOperation<Document> {
17
+ override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
17
18
  override options: ValidateCollectionOptions;
18
19
  collectionName: string;
19
- command: Document;
20
20
 
21
21
  constructor(admin: Admin, collectionName: string, options: ValidateCollectionOptions) {
22
- // Decorate command with extra options
23
- const command: Document = { validate: collectionName };
24
- const keys = Object.keys(options);
25
- for (let i = 0; i < keys.length; i++) {
26
- if (Object.prototype.hasOwnProperty.call(options, keys[i]) && keys[i] !== 'session') {
27
- command[keys[i]] = (options as Document)[keys[i]];
28
- }
29
- }
30
-
31
22
  super(admin.s.db, options);
32
23
  this.options = options;
33
- this.command = command;
34
24
  this.collectionName = collectionName;
35
25
  }
36
26
 
@@ -38,21 +28,23 @@ export class ValidateCollectionOperation extends CommandOperation<Document> {
38
28
  return 'validate' as const;
39
29
  }
40
30
 
41
- override async execute(
42
- server: Server,
43
- session: ClientSession | undefined,
44
- timeoutContext: TimeoutContext
45
- ): Promise<Document> {
46
- const collectionName = this.collectionName;
31
+ override buildCommandDocument(_connection: Connection, _session?: ClientSession): Document {
32
+ // Decorate command with extra options
33
+ return {
34
+ validate: this.collectionName,
35
+ ...Object.fromEntries(Object.entries(this.options).filter(entry => entry[0] !== 'session'))
36
+ };
37
+ }
47
38
 
48
- const doc = await super.executeCommand(server, session, this.command, timeoutContext);
49
- if (doc.result != null && typeof doc.result !== 'string')
39
+ override handleOk(response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>): Document {
40
+ const result = super.handleOk(response);
41
+ if (result.result != null && typeof result.result !== 'string')
50
42
  throw new MongoUnexpectedServerResponseError('Error with validation data');
51
- if (doc.result != null && doc.result.match(/exception|corrupt/) != null)
52
- throw new MongoUnexpectedServerResponseError(`Invalid collection ${collectionName}`);
53
- if (doc.valid != null && !doc.valid)
54
- throw new MongoUnexpectedServerResponseError(`Invalid collection ${collectionName}`);
43
+ if (result.result != null && result.result.match(/exception|corrupt/) != null)
44
+ throw new MongoUnexpectedServerResponseError(`Invalid collection ${this.collectionName}`);
45
+ if (result.valid != null && !result.valid)
46
+ throw new MongoUnexpectedServerResponseError(`Invalid collection ${this.collectionName}`);
55
47
 
56
- return doc;
48
+ return response;
57
49
  }
58
50
  }
@@ -7,7 +7,6 @@ import {
7
7
  type ConnectionPoolOptions
8
8
  } from '../cmap/connection_pool';
9
9
  import { PoolClearedError } from '../cmap/errors';
10
- import { type MongoDBResponseConstructor } from '../cmap/wire_protocol/responses';
11
10
  import {
12
11
  APM_EVENTS,
13
12
  CLOSED,
@@ -27,7 +26,6 @@ import {
27
26
  MONGODB_ERROR_CODES,
28
27
  MongoError,
29
28
  MongoErrorLabel,
30
- MongoInvalidArgumentError,
31
29
  MongoNetworkError,
32
30
  MongoNetworkTimeoutError,
33
31
  MongoRuntimeError,
@@ -37,7 +35,9 @@ import {
37
35
  } from '../error';
38
36
  import type { ServerApi } from '../mongo_client';
39
37
  import { type Abortable, TypedEventEmitter } from '../mongo_types';
38
+ import { AggregateOperation } from '../operations/aggregate';
40
39
  import type { GetMoreOptions } from '../operations/get_more';
40
+ import { type AbstractOperation } from '../operations/operation';
41
41
  import type { ClientSession } from '../sessions';
42
42
  import { type TimeoutContext } from '../timeout';
43
43
  import { isTransactionCommand } from '../transactions';
@@ -46,7 +46,6 @@ import {
46
46
  type EventEmitterWithState,
47
47
  makeStateMachine,
48
48
  maxWireVersion,
49
- type MongoDBNamespace,
50
49
  noop,
51
50
  squashError,
52
51
  supportsRetryableWrites
@@ -67,6 +66,7 @@ import type {
67
66
  } from './events';
68
67
  import { Monitor, type MonitorOptions } from './monitor';
69
68
  import { compareTopologyVersion, ServerDescription } from './server_description';
69
+ import { MIN_SECONDARY_WRITE_WIRE_VERSION } from './server_selection';
70
70
  import type { Topology } from './topology';
71
71
 
72
72
  const stateTransition = makeStateMachine({
@@ -110,6 +110,7 @@ export type ServerEvents = {
110
110
  /** @internal */
111
111
  export type ServerCommandOptions = Omit<CommandOptions, 'timeoutContext' | 'socketTimeoutMS'> & {
112
112
  timeoutContext: TimeoutContext;
113
+ returnFieldSelector?: Document | null;
113
114
  } & Abortable;
114
115
 
115
116
  /** @internal */
@@ -277,57 +278,21 @@ export class Server extends TypedEventEmitter<ServerEvents> {
277
278
  }
278
279
  }
279
280
 
280
- public async command<T extends MongoDBResponseConstructor>(
281
- ns: MongoDBNamespace,
282
- command: Document,
283
- options: ServerCommandOptions,
284
- responseType: T | undefined
285
- ): Promise<typeof responseType extends undefined ? Document : InstanceType<T>>;
286
-
287
- public async command(
288
- ns: MongoDBNamespace,
289
- command: Document,
290
- options: ServerCommandOptions
291
- ): Promise<Document>;
292
-
293
- public async command(
294
- ns: MongoDBNamespace,
295
- cmd: Document,
296
- { ...options }: ServerCommandOptions,
297
- responseType?: MongoDBResponseConstructor
298
- ): Promise<Document> {
299
- if (ns.db == null || typeof ns === 'string') {
300
- throw new MongoInvalidArgumentError('Namespace must not be a string');
301
- }
302
-
281
+ public async command<TResult>(
282
+ operation: AbstractOperation<TResult>,
283
+ timeoutContext: TimeoutContext
284
+ ): Promise<InstanceType<typeof operation.SERVER_COMMAND_RESPONSE_TYPE>> {
303
285
  if (this.s.state === STATE_CLOSING || this.s.state === STATE_CLOSED) {
304
286
  throw new MongoServerClosedError();
305
287
  }
288
+ const session = operation.session;
306
289
 
307
- options.directConnection = this.topology.s.options.directConnection;
308
-
309
- // There are cases where we need to flag the read preference not to get sent in
310
- // the command, such as pre-5.0 servers attempting to perform an aggregate write
311
- // with a non-primary read preference. In this case the effective read preference
312
- // (primary) is not the same as the provided and must be removed completely.
313
- if (options.omitReadPreference) {
314
- delete options.readPreference;
315
- }
316
-
317
- if (this.description.iscryptd) {
318
- options.omitMaxTimeMS = true;
319
- }
320
-
321
- const session = options.session;
322
290
  let conn = session?.pinnedConnection;
323
291
 
324
292
  this.incrementOperationCount();
325
293
  if (conn == null) {
326
294
  try {
327
- conn = await this.pool.checkOut(options);
328
- if (this.loadBalanced && isPinnableCommand(cmd, session)) {
329
- session?.pin(conn);
330
- }
295
+ conn = await this.pool.checkOut({ timeoutContext, signal: operation.options.signal });
331
296
  } catch (checkoutError) {
332
297
  this.decrementOperationCount();
333
298
  if (!(checkoutError instanceof PoolClearedError)) this.handleError(checkoutError);
@@ -336,10 +301,53 @@ export class Server extends TypedEventEmitter<ServerEvents> {
336
301
  }
337
302
 
338
303
  let reauthPromise: Promise<void> | null = null;
304
+ const cleanup = () => {
305
+ this.decrementOperationCount();
306
+ if (session?.pinnedConnection !== conn) {
307
+ if (reauthPromise != null) {
308
+ // The reauth promise only exists if it hasn't thrown.
309
+ const checkBackIn = () => {
310
+ this.pool.checkIn(conn);
311
+ };
312
+ void reauthPromise.then(checkBackIn, checkBackIn);
313
+ } else {
314
+ this.pool.checkIn(conn);
315
+ }
316
+ }
317
+ };
318
+
319
+ let cmd;
320
+ try {
321
+ cmd = operation.buildCommand(conn, session);
322
+ } catch (e) {
323
+ cleanup();
324
+ throw e;
325
+ }
326
+
327
+ const options = operation.buildOptions(timeoutContext);
328
+ const ns = operation.ns;
329
+
330
+ if (this.loadBalanced && isPinnableCommand(cmd, session) && !session?.pinnedConnection) {
331
+ session?.pin(conn);
332
+ }
333
+
334
+ options.directConnection = this.topology.s.options.directConnection;
335
+
336
+ const omitReadPreference =
337
+ operation instanceof AggregateOperation &&
338
+ operation.hasWriteStage &&
339
+ maxWireVersion(conn) < MIN_SECONDARY_WRITE_WIRE_VERSION;
340
+ if (omitReadPreference) {
341
+ delete options.readPreference;
342
+ }
343
+
344
+ if (this.description.iscryptd) {
345
+ options.omitMaxTimeMS = true;
346
+ }
339
347
 
340
348
  try {
341
349
  try {
342
- const res = await conn.command(ns, cmd, options, responseType);
350
+ const res = await conn.command(ns, cmd, options, operation.SERVER_COMMAND_RESPONSE_TYPE);
343
351
  throwIfWriteConcernError(res);
344
352
  return res;
345
353
  } catch (commandError) {
@@ -360,7 +368,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
360
368
  reauthPromise = null; // only reachable if reauth succeeds
361
369
 
362
370
  try {
363
- const res = await conn.command(ns, cmd, options, responseType);
371
+ const res = await conn.command(ns, cmd, options, operation.SERVER_COMMAND_RESPONSE_TYPE);
364
372
  throwIfWriteConcernError(res);
365
373
  return res;
366
374
  } catch (commandError) {
@@ -370,18 +378,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
370
378
  throw operationError;
371
379
  }
372
380
  } finally {
373
- this.decrementOperationCount();
374
- if (session?.pinnedConnection !== conn) {
375
- if (reauthPromise != null) {
376
- // The reauth promise only exists if it hasn't thrown.
377
- const checkBackIn = () => {
378
- this.pool.checkIn(conn);
379
- };
380
- void reauthPromise.then(checkBackIn, checkBackIn);
381
- } else {
382
- this.pool.checkIn(conn);
383
- }
384
- }
381
+ cleanup();
385
382
  }
386
383
  }
387
384
 
@@ -46,7 +46,6 @@ import {
46
46
  makeStateMachine,
47
47
  noop,
48
48
  now,
49
- ns,
50
49
  promiseWithResolvers,
51
50
  shuffle
52
51
  } from '../utils';
@@ -459,7 +458,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
459
458
  waitQueueTimeoutMS: this.client.s.options.waitQueueTimeoutMS
460
459
  });
461
460
  const selectServerOptions = {
462
- operationName: 'ping',
461
+ operationName: 'handshake',
463
462
  ...options,
464
463
  timeoutContext
465
464
  };
@@ -469,9 +468,11 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
469
468
  readPreferenceServerSelector(readPreference),
470
469
  selectServerOptions
471
470
  );
471
+
472
472
  const skipPingOnConnect = this.s.options.__skipPingOnConnect === true;
473
473
  if (!skipPingOnConnect && this.s.credentials) {
474
- await server.command(ns('admin.$cmd'), { ping: 1 }, { timeoutContext });
474
+ const connection = await server.pool.checkOut({ timeoutContext: timeoutContext });
475
+ server.pool.checkIn(connection);
475
476
  stateTransition(this, STATE_CONNECTED);
476
477
  this.emit(Topology.OPEN, this);
477
478
  this.emit(Topology.CONNECT, this);
@@ -749,7 +750,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
749
750
  }
750
751
 
751
752
  auth(credentials?: MongoCredentials, callback?: Callback): void {
752
- if (typeof credentials === 'function') (callback = credentials), (credentials = undefined);
753
+ if (typeof credentials === 'function') ((callback = credentials), (credentials = undefined));
753
754
  if (typeof callback === 'function') callback(undefined, true);
754
755
  }
755
756
 
package/src/sessions.ts CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  import type { MongoClient, MongoOptions } from './mongo_client';
25
25
  import { TypedEventEmitter } from './mongo_types';
26
26
  import { executeOperation } from './operations/execute_operation';
27
- import { RunAdminCommandOperation } from './operations/run_command';
27
+ import { RunCommandOperation } from './operations/run_command';
28
28
  import { ReadConcernLevel } from './read_concern';
29
29
  import { ReadPreference } from './read_preference';
30
30
  import { type AsyncDisposable, configureResourceManagement } from './resource_management';
@@ -43,6 +43,7 @@ import {
43
43
  isPromiseLike,
44
44
  List,
45
45
  maxWireVersion,
46
+ MongoDBNamespace,
46
47
  noop,
47
48
  now,
48
49
  squashError,
@@ -505,7 +506,7 @@ export class ClientSession
505
506
  command.recoveryToken = this.transaction.recoveryToken;
506
507
  }
507
508
 
508
- const operation = new RunAdminCommandOperation(command, {
509
+ const operation = new RunCommandOperation(new MongoDBNamespace('admin'), command, {
509
510
  session: this,
510
511
  readPreference: ReadPreference.primary,
511
512
  bypassPinningCheck: true
@@ -536,7 +537,7 @@ export class ClientSession
536
537
  try {
537
538
  await executeOperation(
538
539
  this.client,
539
- new RunAdminCommandOperation(command, {
540
+ new RunCommandOperation(new MongoDBNamespace('admin'), command, {
540
541
  session: this,
541
542
  readPreference: ReadPreference.primary,
542
543
  bypassPinningCheck: true
@@ -637,7 +638,7 @@ export class ClientSession
637
638
  command.recoveryToken = this.transaction.recoveryToken;
638
639
  }
639
640
 
640
- const operation = new RunAdminCommandOperation(command, {
641
+ const operation = new RunCommandOperation(new MongoDBNamespace('admin'), command, {
641
642
  session: this,
642
643
  readPreference: ReadPreference.primary,
643
644
  bypassPinningCheck: true