mongodb 7.7.0 → 7.8.0

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 (90) hide show
  1. package/README.md +1 -1
  2. package/lib/bulk/unordered.js +1 -1
  3. package/lib/bulk/unordered.js.map +1 -1
  4. package/lib/change_stream.js +3 -11
  5. package/lib/change_stream.js.map +1 -1
  6. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  7. package/lib/cmap/wire_protocol/compression.js +2 -0
  8. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  9. package/lib/collection.js +18 -119
  10. package/lib/collection.js.map +1 -1
  11. package/lib/connection_string.js +110 -15
  12. package/lib/connection_string.js.map +1 -1
  13. package/lib/cursor/abstract_cursor.js +9 -62
  14. package/lib/cursor/abstract_cursor.js.map +1 -1
  15. package/lib/cursor/aggregation_cursor.js +0 -9
  16. package/lib/cursor/aggregation_cursor.js.map +1 -1
  17. package/lib/cursor/find_cursor.js +0 -9
  18. package/lib/cursor/find_cursor.js.map +1 -1
  19. package/lib/cursor/list_collections_cursor.js +1 -1
  20. package/lib/cursor/list_collections_cursor.js.map +1 -1
  21. package/lib/cursor/list_indexes_cursor.js +1 -1
  22. package/lib/cursor/list_indexes_cursor.js.map +1 -1
  23. package/lib/cursor/run_command_cursor.js.map +1 -1
  24. package/lib/db.js +5 -58
  25. package/lib/db.js.map +1 -1
  26. package/lib/deps.js +35 -1
  27. package/lib/deps.js.map +1 -1
  28. package/lib/explain.js +0 -8
  29. package/lib/explain.js.map +1 -1
  30. package/lib/gridfs/download.js +3 -11
  31. package/lib/gridfs/download.js.map +1 -1
  32. package/lib/gridfs/index.js +4 -4
  33. package/lib/gridfs/index.js.map +1 -1
  34. package/lib/gridfs/upload.js +3 -18
  35. package/lib/gridfs/upload.js.map +1 -1
  36. package/lib/mongo_client.js +2 -60
  37. package/lib/mongo_client.js.map +1 -1
  38. package/lib/operations/command.js +0 -2
  39. package/lib/operations/command.js.map +1 -1
  40. package/lib/operations/create_collection.js +1 -1
  41. package/lib/operations/create_collection.js.map +1 -1
  42. package/lib/operations/indexes.js +45 -16
  43. package/lib/operations/indexes.js.map +1 -1
  44. package/lib/sdam/srv_polling.js +7 -2
  45. package/lib/sdam/srv_polling.js.map +1 -1
  46. package/lib/sdam/topology.js +3 -1
  47. package/lib/sdam/topology.js.map +1 -1
  48. package/lib/sessions.js +10 -47
  49. package/lib/sessions.js.map +1 -1
  50. package/lib/sort.js +1 -1
  51. package/lib/sort.js.map +1 -1
  52. package/lib/timeout.js +2 -11
  53. package/lib/timeout.js.map +1 -1
  54. package/lib/utils.js +84 -23
  55. package/lib/utils.js.map +1 -1
  56. package/mongodb.d.ts +249 -314
  57. package/package.json +1 -1
  58. package/src/bulk/unordered.ts +1 -1
  59. package/src/change_stream.ts +4 -12
  60. package/src/client-side-encryption/client_encryption.ts +5 -32
  61. package/src/client-side-encryption/kms_options.ts +1 -0
  62. package/src/cmap/wire_protocol/compression.ts +3 -1
  63. package/src/collection.ts +83 -71
  64. package/src/connection_string.ts +135 -19
  65. package/src/cursor/abstract_cursor.ts +20 -97
  66. package/src/cursor/aggregation_cursor.ts +1 -16
  67. package/src/cursor/find_cursor.ts +2 -17
  68. package/src/cursor/list_collections_cursor.ts +1 -1
  69. package/src/cursor/list_indexes_cursor.ts +1 -1
  70. package/src/cursor/run_command_cursor.ts +4 -31
  71. package/src/db.ts +13 -59
  72. package/src/deps.ts +38 -1
  73. package/src/explain.ts +0 -8
  74. package/src/gridfs/download.ts +7 -14
  75. package/src/gridfs/index.ts +27 -8
  76. package/src/gridfs/upload.ts +6 -21
  77. package/src/index.ts +2 -0
  78. package/src/mongo_client.ts +46 -61
  79. package/src/operations/command.ts +1 -7
  80. package/src/operations/create_collection.ts +4 -2
  81. package/src/operations/drop.ts +1 -1
  82. package/src/operations/indexes.ts +213 -17
  83. package/src/operations/operation.ts +1 -1
  84. package/src/operations/run_command.ts +1 -1
  85. package/src/sdam/srv_polling.ts +12 -3
  86. package/src/sdam/topology.ts +5 -1
  87. package/src/sessions.ts +21 -63
  88. package/src/sort.ts +1 -1
  89. package/src/timeout.ts +2 -13
  90. package/src/utils.ts +96 -30
@@ -2,15 +2,14 @@ import { Writable } from 'stream';
2
2
 
3
3
  import { ByteUtils, type Document, ObjectId } from '../bson';
4
4
  import type { Collection } from '../collection';
5
- import { CursorTimeoutMode } from '../cursor/abstract_cursor';
6
5
  import {
7
6
  MongoAPIError,
8
7
  MONGODB_ERROR_CODES,
9
8
  MongoError,
10
9
  MongoOperationTimeoutError
11
10
  } from '../error';
12
- import { CSOTTimeoutContext } from '../timeout';
13
- import { type Callback, resolveTimeoutOptions, squashError } from '../utils';
11
+ import { type CSOTTimeoutContext } from '../timeout';
12
+ import { type Callback, squashError } from '../utils';
14
13
  import type { WriteConcernOptions } from '../write_concern';
15
14
  import { WriteConcern } from './../write_concern';
16
15
  import type { GridFSFile } from './download';
@@ -33,8 +32,8 @@ export interface GridFSBucketWriteStreamOptions extends WriteConcernOptions {
33
32
  /** Object to store in the file document's `metadata` field */
34
33
  metadata?: Document;
35
34
  /**
36
- * @experimental
37
35
  * Specifies the time an operation will run until it throws a timeout error
36
+ * @deprecated `timeoutMS` has no effect and will be removed in a future release.
38
37
  */
39
38
  timeoutMS?: number;
40
39
  }
@@ -132,13 +131,6 @@ export class GridFSBucketWriteStream extends Writable {
132
131
  errored: false,
133
132
  aborted: false
134
133
  };
135
-
136
- if (options.timeoutMS != null)
137
- this.timeoutContext = new CSOTTimeoutContext({
138
- timeoutMS: options.timeoutMS,
139
- serverSelectionTimeoutMS: resolveTimeoutOptions(this.bucket.s.db.client, {})
140
- .serverSelectionTimeoutMS
141
- });
142
134
  }
143
135
 
144
136
  /**
@@ -214,7 +206,7 @@ export class GridFSBucketWriteStream extends Writable {
214
206
  `Upload timed out after ${this.timeoutContext?.timeoutMS}ms`
215
207
  );
216
208
 
217
- await this.chunks.deleteMany({ files_id: this.id }, { timeoutMS: remainingTimeMS });
209
+ await this.chunks.deleteMany({ files_id: { $eq: this.id } }, { timeoutMS: remainingTimeMS });
218
210
  }
219
211
  }
220
212
 
@@ -248,7 +240,6 @@ async function checkChunksIndex(stream: GridFSBucketWriteStream): Promise<void>
248
240
  try {
249
241
  indexes = await stream.chunks
250
242
  .listIndexes({
251
- timeoutMode: remainingTimeMS != null ? CursorTimeoutMode.LIFETIME : undefined,
252
243
  timeoutMS: remainingTimeMS
253
244
  })
254
245
  .toArray();
@@ -272,12 +263,7 @@ async function checkChunksIndex(stream: GridFSBucketWriteStream): Promise<void>
272
263
  remainingTimeMS = stream.timeoutContext?.getRemainingTimeMSOrThrow(
273
264
  `Upload timed out after ${stream.timeoutContext?.timeoutMS}ms`
274
265
  );
275
- await stream.chunks.createIndex(index, {
276
- ...stream.writeConcern,
277
- background: true,
278
- unique: true,
279
- timeoutMS: remainingTimeMS
280
- });
266
+ await stream.chunks.createIndex(index, { unique: true }, { timeoutMS: remainingTimeMS });
281
267
  }
282
268
  }
283
269
 
@@ -353,7 +339,6 @@ async function checkIndexes(stream: GridFSBucketWriteStream): Promise<void> {
353
339
  `Upload timed out after ${stream.timeoutContext?.timeoutMS}ms`
354
340
  );
355
341
  const listIndexesOptions = {
356
- timeoutMode: remainingTimeMS != null ? CursorTimeoutMode.LIFETIME : undefined,
357
342
  timeoutMS: remainingTimeMS
358
343
  };
359
344
  try {
@@ -379,7 +364,7 @@ async function checkIndexes(stream: GridFSBucketWriteStream): Promise<void> {
379
364
  `Upload timed out after ${stream.timeoutContext?.timeoutMS}ms`
380
365
  );
381
366
 
382
- await stream.files.createIndex(index, { background: false, timeoutMS: remainingTimeMS });
367
+ await stream.files.createIndex(index, {}, { timeoutMS: remainingTimeMS });
383
368
  }
384
369
 
385
370
  await checkChunksIndex(stream);
package/src/index.ts CHANGED
@@ -530,11 +530,13 @@ export type {
530
530
  export type { IndexInformationOptions } from './operations/indexes';
531
531
  export type {
532
532
  CreateIndexesOptions,
533
+ CreateIndexOptions,
533
534
  DropIndexesOptions,
534
535
  IndexDescription,
535
536
  IndexDescriptionCompact,
536
537
  IndexDescriptionInfo,
537
538
  IndexDirection,
539
+ IndexOptions,
538
540
  IndexSpecification,
539
541
  ListIndexesOptions
540
542
  } from './operations/indexes';
@@ -143,8 +143,8 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
143
143
  /** Specifies the name of the replica set, if the mongod is a member of a replica set. */
144
144
  replicaSet?: string;
145
145
  /**
146
- * @experimental
147
146
  * Specifies the time an operation will run until it throws a timeout error
147
+ * @deprecated `timeoutMS` has no effect and will be removed in a future release.
148
148
  */
149
149
  timeoutMS?: number;
150
150
  /** Enables or disables TLS/SSL for the connection. */
@@ -183,6 +183,46 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
183
183
  * Querying this DNS URI is expected to respond with SRV records
184
184
  */
185
185
  srvServiceName?: string;
186
+ /**
187
+ * A host name suffix that every host returned by the SRV lookup must end with. It replaces the
188
+ * default verification, which requires returned hosts to share the parent domain of the SRV host
189
+ * name (the SRV host name without its leftmost label). Only valid with a `mongodb+srv` connection
190
+ * string, and cannot be combined with `srvHostValidator`.
191
+ *
192
+ * For example, with `mongodb+srv://cluster.mongodb.mydomain.net`, the default verification
193
+ * rejects `host1.us-east-1.mydomain.net`, while `srvAllowedHostsSuffix: '.mydomain.net'` accepts
194
+ * it. A leading `.` is optional. Internationalized domain names may be given in Unicode or in
195
+ * A-label (`xn--`) form: the value is converted to lowercase A-label form, and a value that cannot
196
+ * be converted is an error. The value must contain at least two labels, unless it is one of the
197
+ * names reserved for private or special use: `test`, `localhost`, `invalid`, `example`, `local`,
198
+ * `internal`, `corp`, `home`, or `mail`.
199
+ *
200
+ * **WARNING: Modifying the default SRV domain name validation can create vulnerabilities.**
201
+ * SRV host verification prevents a spoofed DNS response from directing the driver to arbitrary
202
+ * hosts, and this option widens the set of hosts such a response can direct it to. Configure the
203
+ * narrowest suffix that covers your deployment: for a seed of
204
+ * `cluster.test.internal.example.com`, prefer `.internal.example.com` over `.example.com`.
205
+ */
206
+ srvAllowedHostsSuffix?: string;
207
+ /**
208
+ * A synchronous function that decides whether a host returned by the SRV lookup may be used. It
209
+ * receives the host name, normalized to lowercase A-label (Punycode) form without a trailing `.`,
210
+ * and must return `true` to accept the host or `false` to reject it. Its return value replaces the
211
+ * default verification entirely: the driver applies no other checks to the host. Only valid with a
212
+ * `mongodb+srv` connection string, cannot be combined with `srvAllowedHostsSuffix`, and cannot be
213
+ * set in the connection string.
214
+ *
215
+ * The function runs synchronously during SRV resolution and SRV polling, so it must not block.
216
+ *
217
+ * If the function throws during the initial SRV lookup, `connect()` rejects with a
218
+ * `MongoAPIError` whose `cause` is the thrown error. During SRV polling, the host is rejected
219
+ * instead and no error is raised.
220
+ *
221
+ * **WARNING: Modifying the default SRV domain name validation can create vulnerabilities.**
222
+ * SRV host verification prevents a spoofed DNS response from directing the driver to arbitrary
223
+ * hosts, and a validator that accepts too broadly removes that protection.
224
+ */
225
+ srvHostValidator?: (host: string) => boolean;
186
226
  /** The maximum number of connections in the connection pool. */
187
227
  maxPoolSize?: number;
188
228
  /** The minimum number of connections in the connection pool. */
@@ -562,8 +602,9 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
562
602
  return this.s.bsonOptions;
563
603
  }
564
604
 
605
+ /** @deprecated `timeoutMS` has no effect and will be removed in a future release. */
565
606
  get timeoutMS(): number | undefined {
566
- return this.s.options.timeoutMS;
607
+ return undefined;
567
608
  }
568
609
 
569
610
  /**
@@ -874,12 +915,6 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
874
915
  * **Connection Pooling Impact:** Calling `connect` will populate the connection pool with one connection
875
916
  * to a server selected by the client's configured `readPreference` (defaults to primary).
876
917
  *
877
- * @remarks
878
- * **Timeout Behavior:** When using `timeoutMS`, the connection establishment time does not count against
879
- * the timeout for subsequent operations. This means `connect` runs without a `timeoutMS` limit, while
880
- * your database operations will still respect the configured timeout. If you need predictable operation
881
- * timing with `timeoutMS`, call `connect` explicitly before performing operations.
882
- *
883
918
  * @see https://www.mongodb.com/docs/manual/reference/connection-string/
884
919
  */
885
920
  static async connect(url: string, options?: MongoClientOptions): Promise<MongoClient> {
@@ -961,59 +996,6 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
961
996
  * watch() accepts two generic arguments for distinct use cases:
962
997
  * - The first is to provide the schema that may be defined for all the data within the current cluster
963
998
  * - The second is to override the shape of the change stream document entirely, if it is not provided the type will default to ChangeStreamDocument of the first argument
964
- *
965
- * @remarks
966
- * When `timeoutMS` is configured for a change stream, it will have different behaviour depending
967
- * on whether the change stream is in iterator mode or emitter mode. In both cases, a change
968
- * stream will time out if it does not receive a change event within `timeoutMS` of the last change
969
- * event.
970
- *
971
- * Note that if a change stream is consistently timing out when watching a collection, database or
972
- * client that is being changed, then this may be due to the server timing out before it can finish
973
- * processing the existing oplog. To address this, restart the change stream with a higher
974
- * `timeoutMS`.
975
- *
976
- * If the change stream times out the initial aggregate operation to establish the change stream on
977
- * the server, then the client will close the change stream. If the getMore calls to the server
978
- * time out, then the change stream will be left open, but will throw a MongoOperationTimeoutError
979
- * when in iterator mode and emit an error event that returns a MongoOperationTimeoutError in
980
- * emitter mode.
981
- *
982
- * To determine whether or not the change stream is still open following a timeout, check the
983
- * {@link ChangeStream.closed} getter.
984
- *
985
- * @example
986
- * In iterator mode, if a next() call throws a timeout error, it will attempt to resume the change stream.
987
- * The next call can just be retried after this succeeds.
988
- * ```ts
989
- * const changeStream = collection.watch([], { timeoutMS: 100 });
990
- * try {
991
- * await changeStream.next();
992
- * } catch (e) {
993
- * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
994
- * await changeStream.next();
995
- * }
996
- * throw e;
997
- * }
998
- * ```
999
- *
1000
- * @example
1001
- * In emitter mode, if the change stream goes `timeoutMS` without emitting a change event, it will
1002
- * emit an error event that returns a MongoOperationTimeoutError, but will not close the change
1003
- * stream unless the resume attempt fails. There is no need to re-establish change listeners as
1004
- * this will automatically continue emitting change events once the resume attempt completes.
1005
- *
1006
- * ```ts
1007
- * const changeStream = collection.watch([], { timeoutMS: 100 });
1008
- * changeStream.on('change', console.log);
1009
- * changeStream.on('error', e => {
1010
- * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
1011
- * // do nothing
1012
- * } else {
1013
- * changeStream.close();
1014
- * }
1015
- * });
1016
- * ```
1017
999
  * @param pipeline - An array of {@link https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.
1018
1000
  * @param options - Optional settings for the command
1019
1001
  * @typeParam TSchema - Type of the data being detected by the change stream
@@ -1092,6 +1074,8 @@ export interface MongoOptions
1092
1074
  appName?: string;
1093
1075
  hosts: HostAddress[];
1094
1076
  srvHost?: string;
1077
+ srvAllowedHostsSuffix?: string;
1078
+ srvHostValidator?: (host: string) => boolean;
1095
1079
  credentials?: MongoCredentials;
1096
1080
  readPreference: ReadPreference;
1097
1081
  readConcern: ReadConcern;
@@ -1173,6 +1157,7 @@ export interface MongoOptions
1173
1157
  * TODO: NODE-5671 - remove internal flag
1174
1158
  */
1175
1159
  mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable;
1160
+ /** @deprecated `timeoutMS` has no effect and will be removed in a future release. */
1176
1161
  timeoutMS?: number;
1177
1162
  /** @internal */
1178
1163
  __skipPingOnConnect?: boolean;
@@ -2,12 +2,7 @@ import { type Connection } from '..';
2
2
  import type { BSONSerializeOptions, Document } from '../bson';
3
3
  import { MIN_SUPPORTED_RAW_DATA_WIRE_VERSION } from '../cmap/wire_protocol/constants';
4
4
  import { MongoInvalidArgumentError } from '../error';
5
- import {
6
- decorateWithExplain,
7
- Explain,
8
- type ExplainOptions,
9
- validateExplainTimeoutOptions
10
- } from '../explain';
5
+ import { decorateWithExplain, Explain, type ExplainOptions } from '../explain';
11
6
  import { ReadConcern } from '../read_concern';
12
7
  import type { ReadPreference } from '../read_preference';
13
8
  import type { ServerCommandOptions } from '../sdam/server';
@@ -99,7 +94,6 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
99
94
 
100
95
  if (this.hasAspect(Aspect.EXPLAINABLE)) {
101
96
  this.explain = Explain.fromOptions(options);
102
- if (this.explain) validateExplainTimeoutOptions(this.options, this.explain);
103
97
  } else if (options?.explain != null) {
104
98
  throw new MongoInvalidArgumentError(`Option "explain" is not supported on this command`);
105
99
  }
@@ -95,7 +95,7 @@ export interface CreateCollectionOptions extends Omit<CommandOperationOptions, '
95
95
  clusteredIndex?: ClusteredCollectionOptions;
96
96
  /** The number of seconds after which a document in a timeseries or clustered collection expires. */
97
97
  expireAfterSeconds?: number;
98
- /** @experimental */
98
+ /** Queryable Encryption field schema attached at collection creation. **/
99
99
  encryptedFields?: Document;
100
100
  /**
101
101
  * If set, enables pre-update and post-update document events to be included for any
@@ -207,7 +207,9 @@ export async function createCollections<TSchema extends Document>(
207
207
  db,
208
208
  name,
209
209
  { __safeContent__: 1 },
210
- { session: options.session }
210
+ false,
211
+ { session: options.session },
212
+ undefined
211
213
  );
212
214
  await executeOperation(db.client, createIndexOp, timeoutContext);
213
215
  }
@@ -12,7 +12,7 @@ import { Aspect, defineAspects } from './operation';
12
12
 
13
13
  /** @public */
14
14
  export interface DropCollectionOptions extends Omit<CommandOperationOptions, 'rawData'> {
15
- /** @experimental */
15
+ /** Queryable Encryption field schema used during collection drop so associated state collections are dropped too. **/
16
16
  encryptedFields?: Document;
17
17
  }
18
18
 
@@ -93,7 +93,11 @@ export interface IndexInformationOptions extends ListIndexesOptions {
93
93
  full?: boolean;
94
94
  }
95
95
 
96
- /** @public */
96
+ /**
97
+ * @public
98
+ *
99
+ * Loosely aligns with `IndexModel` from the spec.
100
+ * */
97
101
  export interface IndexDescription
98
102
  extends Pick<
99
103
  CreateIndexesOptions,
@@ -160,6 +164,144 @@ export interface CreateIndexesOptions extends Omit<CommandOperationOptions, 'wri
160
164
  wildcardProjection?: Document;
161
165
  /** Specifies that the index should exist on the target collection but should not be used by the query planner when executing operations. */
162
166
  hidden?: boolean;
167
+ /** Collation */
168
+ collation?: CollationOptions;
169
+ }
170
+
171
+ // Maps to `IndexOptions` in
172
+ // https://github.com/mongodb/specifications/blob/6f64d0ee3ae49edbdb30eb995f3e29549e8cfa6a/source/index-management/index-management.md#common-api-components
173
+ /** @public */
174
+ export interface IndexOptions extends Document {
175
+ /**
176
+ * Optionally specifies the length in time, in seconds, for documents to remain in
177
+ * a collection.
178
+ */
179
+ expireAfterSeconds?: number;
180
+
181
+ /**
182
+ * Optionally specify a specific name for the index outside of the default generated
183
+ * name. If none is provided then the name is generated in the format "[field]_[direction]".
184
+ *
185
+ * Note that if an index is created for the same key pattern with different collations,
186
+ * a name must be provided by the user to avoid ambiguity.
187
+ *
188
+ * @example For an index of name: 1, age: -1, the generated name would be "name_1_age_-1".
189
+ */
190
+ name?: string;
191
+
192
+ /**
193
+ * Optionally tells the index to only reference documents with the specified field in
194
+ * the index.
195
+ */
196
+ sparse?: boolean;
197
+
198
+ /**
199
+ * Optionally allows users to configure the storage engine on a per-index basis when creating
200
+ * an index.
201
+ */
202
+ storageEngine?: Document;
203
+
204
+ /**
205
+ * Optionally forces the index to be unique.
206
+ */
207
+ unique?: boolean;
208
+
209
+ /**
210
+ * Optionally specifies the index version number, either 0 or 1.
211
+ */
212
+ version?: number;
213
+
214
+ /**
215
+ * Optionally specifies the default language for text indexes.
216
+ * Is 'english' if none is provided.
217
+ */
218
+ defaultLanguage?: string;
219
+
220
+ /**
221
+ * Optionally Specifies the field in the document to override the language.
222
+ */
223
+ languageOverride?: string;
224
+
225
+ /**
226
+ * Optionally provides the text index version number.
227
+ */
228
+ textIndexVersion?: number;
229
+
230
+ /**
231
+ * Optionally specifies fields in the index and their corresponding weight values.
232
+ */
233
+ weights?: Document;
234
+
235
+ /**
236
+ * Optionally specifies the 2dsphere index version number.
237
+ */
238
+ '2dsphereIndexVersion'?: number;
239
+
240
+ /**
241
+ * Optionally specifies the precision of the stored geo hash in the 2d index, from 1 to 32.
242
+ */
243
+ bits?: number;
244
+
245
+ /**
246
+ * Optionally sets the maximum boundary for latitude and longitude in the 2d index.
247
+ */
248
+ max?: number;
249
+
250
+ /**
251
+ * Optionally sets the minimum boundary for latitude and longitude in the index in a
252
+ * 2d index.
253
+ */
254
+ min?: number;
255
+
256
+ /**
257
+ * Optionally specifies the number of units within which to group the location values
258
+ * in a geo haystack index.
259
+ */
260
+ bucketSize?: number;
261
+
262
+ /**
263
+ * Optionally specifies a filter for use in a partial index. Only documents that match the
264
+ * filter expression are included in the index.
265
+ */
266
+ partialFilterExpression?: Document;
267
+
268
+ /**
269
+ * Optionally specifies a collation to use for the index. If not specified, no collation is
270
+ * sent and the default collation of the collection server-side is used.
271
+ */
272
+ collation?: CollationOptions;
273
+
274
+ /**
275
+ * Optionally specifies the wildcard projection of a wildcard index.
276
+ */
277
+ wildcardProjection?: Document;
278
+
279
+ /**
280
+ * Optionally specifies that the index should exist on the target collection but should not be used by the query
281
+ * planner when executing operations.
282
+ *
283
+ * This option is only supported by servers \>= 4.4.
284
+ */
285
+ hidden?: boolean;
286
+ }
287
+
288
+ /** @public */
289
+ export interface CreateIndexOptions extends Omit<CommandOperationOptions, 'collation' | 'explain'> {
290
+ /**
291
+ * Specifies how many data-bearing members of a replica set, including the primary, must
292
+ * complete the index builds successfully before the primary marks the indexes as ready.
293
+ *
294
+ * This option accepts the same values for the "w" field in a write concern plus "votingMembers",
295
+ * which indicates all voting data-bearing nodes.
296
+ *
297
+ * This option is only supported by servers \>= 4.4. Drivers MUST manually raise an error if this option
298
+ * is specified when creating an index on a pre 4.4 server. See the Q&A section for the rationale behind this.
299
+ *
300
+ * @remarks This option is sent only if the caller explicitly provides a value. The default is to not send a value.
301
+ *
302
+ * @sinceServerVersion 4.4
303
+ */
304
+ commitQuorum?: number | string;
163
305
  }
164
306
 
165
307
  function isSingleIndexTuple(t: unknown): t is [string, IndexDirection] {
@@ -197,19 +339,36 @@ function constructIndexDescriptionMap(indexSpec: IndexSpecification): Map<string
197
339
  }
198
340
 
199
341
  /**
200
- * Receives an index description and returns a modified index description which has had invalid options removed
201
- * from the description and has mapped the `version` option to the `v` option.
342
+ * Index options whose public name on {@link IndexOptions} differs from the field name the
343
+ * `createIndexes` command expects.
344
+ */
345
+ const INDEX_OPTION_RENAMES = new Map([
346
+ ['version', 'v'],
347
+ ['defaultLanguage', 'default_language'],
348
+ ['languageOverride', 'language_override']
349
+ ]);
350
+
351
+ /**
352
+ * Receives an index description and returns a modified index description which has mapped the
353
+ * options in {@link INDEX_OPTION_RENAMES} to the field names the `createIndexes` command expects.
354
+ *
355
+ * When `allowUnknownIndexOptions` is `false` (the default), options that are not in the driver's
356
+ * `VALID_INDEX_OPTIONS` allowlist are removed from the description. When `true`, all options are
357
+ * retained and passed through to the server for validation.
202
358
  */
203
359
  function resolveIndexDescription(
204
- description: IndexDescription
360
+ description: IndexDescription,
361
+ // TODO(NODE-7868): Remove allowUnknownIndexOptions with a default behavior of true in a future major version release
362
+ allowUnknownIndexOptions: boolean
205
363
  ): Omit<ResolvedIndexDescription, 'key'> {
206
- const validProvidedOptions = Object.entries(description).filter(([optionName]) =>
207
- VALID_INDEX_OPTIONS.has(optionName)
364
+ const providedOptions = Object.entries(description).filter(
365
+ ([optionName]) =>
366
+ // Ensure `key` is removed, even when `allowUnknownIndexOptions` is true
367
+ optionName !== 'key' && (allowUnknownIndexOptions || VALID_INDEX_OPTIONS.has(optionName))
208
368
  );
209
369
 
210
370
  return Object.fromEntries(
211
- // we support the `version` option, but the `createIndexes` command expects it to be the `v`
212
- validProvidedOptions.map(([name, value]) => (name === 'version' ? ['v', value] : [name, value]))
371
+ providedOptions.map(([name, value]) => [INDEX_OPTION_RENAMES.get(name) ?? name, value])
213
372
  );
214
373
  }
215
374
 
@@ -251,11 +410,13 @@ export class CreateIndexesOperation extends CommandOperation<string[]> {
251
410
  parent: OperationParent,
252
411
  collectionName: string,
253
412
  indexes: IndexDescription[],
254
- options?: CreateIndexesOptions
413
+ // TODO(NODE-7868): Remove allowUnknownIndexOptions with a default behavior of true in a future major version release
414
+ allowUnknownIndexOptions: boolean,
415
+ commandOptions: CreateIndexesOptions | CreateIndexOptions | undefined
255
416
  ) {
256
- super(parent, options);
417
+ super(parent, commandOptions);
257
418
 
258
- this.options = options ?? {};
419
+ this.options = { ...commandOptions };
259
420
  // collation is set on each index, it should not be defined at the root
260
421
  this.options.collation = undefined;
261
422
  this.collectionName = collectionName;
@@ -264,7 +425,8 @@ export class CreateIndexesOperation extends CommandOperation<string[]> {
264
425
  const key =
265
426
  userIndex.key instanceof Map ? userIndex.key : new Map(Object.entries(userIndex.key));
266
427
  const name = userIndex.name ?? Array.from(key).flat().join('_');
267
- const validIndexOptions = resolveIndexDescription(userIndex);
428
+
429
+ const validIndexOptions = resolveIndexDescription(userIndex, allowUnknownIndexOptions);
268
430
  return {
269
431
  ...validIndexOptions,
270
432
  name,
@@ -274,24 +436,58 @@ export class CreateIndexesOperation extends CommandOperation<string[]> {
274
436
  this.ns = parent.s.namespace;
275
437
  }
276
438
 
439
+ /**
440
+ * Index options are carried by the individual {@link IndexDescription} entries of `indexes`,
441
+ * so the only options bag this factory takes is the command one.
442
+ */
277
443
  static fromIndexDescriptionArray(
278
444
  parent: OperationParent,
279
445
  collectionName: string,
280
446
  indexes: IndexDescription[],
281
- options?: CreateIndexesOptions
447
+ // TODO(NODE-7868): Remove allowUnknownIndexOptions with a default behavior of true in a future major version release
448
+ allowUnknownIndexOptions: boolean,
449
+ commandOptions: CreateIndexesOptions | undefined
282
450
  ): CreateIndexesOperation {
283
- return new CreateIndexesOperation(parent, collectionName, indexes, options);
451
+ return new CreateIndexesOperation(
452
+ parent,
453
+ collectionName,
454
+ indexes,
455
+ allowUnknownIndexOptions,
456
+ commandOptions
457
+ );
284
458
  }
285
459
 
286
460
  static fromIndexSpecification(
287
461
  parent: OperationParent,
288
462
  collectionName: string,
289
463
  indexSpec: IndexSpecification,
290
- options: CreateIndexesOptions = {}
464
+ allowUnknownIndexOptions: boolean,
465
+ indexOptions: IndexOptions | undefined,
466
+ commandOptions: CreateIndexOptions | undefined
467
+ ): CreateIndexesOperation;
468
+
469
+ static fromIndexSpecification(
470
+ parent: OperationParent,
471
+ collectionName: string,
472
+ indexSpec: IndexSpecification,
473
+ allowUnknownIndexOptions: boolean,
474
+ indexOptions: CreateIndexesOptions | IndexOptions | undefined,
475
+ commandOptions: CreateIndexOptions | undefined
291
476
  ): CreateIndexesOperation {
292
477
  const key = constructIndexDescriptionMap(indexSpec);
293
- const description: IndexDescription = { ...options, key };
294
- return new CreateIndexesOperation(parent, collectionName, [description], options);
478
+ // If called with overload using `CreateIndexesOptions`, then indexOptions may contain combined index and command options
479
+ // These are filtered in CreateIndexesOperation using VALID_INDEX_OPTIONS.
480
+ // Otherwise, `IndexOptions` only contains index options (which is the ultimate goal anyway)
481
+ const description: IndexDescription = { ...indexOptions, key };
482
+ return new CreateIndexesOperation(
483
+ parent,
484
+ collectionName,
485
+ [description],
486
+ allowUnknownIndexOptions,
487
+ // TODO(NODE-7868): Remove the `?? indexOptions` fallback along with the two parameter path.
488
+ // Once `indexOptions` is index options only it must not reach the command root.
489
+ commandOptions ?? indexOptions
490
+ );
295
491
  }
296
492
 
297
493
  override get commandName() {
@@ -39,8 +39,8 @@ export interface OperationOptions extends BSONSerializeOptions {
39
39
  omitMaxTimeMS?: boolean;
40
40
 
41
41
  /**
42
- * @experimental
43
42
  * Specifies the time an operation will run until it throws a timeout error
43
+ * @deprecated `timeoutMS` has no effect and will be removed in a future release.
44
44
  */
45
45
  timeoutMS?: number;
46
46
  }
@@ -16,8 +16,8 @@ export type RunCommandOptions = {
16
16
  /** The read preference */
17
17
  readPreference?: ReadPreferenceLike;
18
18
  /**
19
- * @experimental
20
19
  * Specifies the time an operation will run until it throws a timeout error
20
+ * @deprecated `timeoutMS` has no effect and will be removed in a future release.
21
21
  */
22
22
  timeoutMS?: number;
23
23
  /** @internal */
@@ -3,7 +3,7 @@ import { clearTimeout, setTimeout } from 'timers';
3
3
 
4
4
  import { MongoRuntimeError } from '../error';
5
5
  import { TypedEventEmitter } from '../mongo_types';
6
- import { checkParentDomainMatch, HostAddress, noop, squashError } from '../utils';
6
+ import { HostAddress, noop, normalizeDnsName, squashError, verifySrvHost } from '../utils';
7
7
 
8
8
  /**
9
9
  * @internal
@@ -25,6 +25,8 @@ export interface SrvPollerOptions {
25
25
  srvServiceName: string;
26
26
  srvMaxHosts: number;
27
27
  srvHost: string;
28
+ srvAllowedHostsSuffix?: string;
29
+ srvHostValidator?: (host: string) => boolean;
28
30
  heartbeatFrequencyMS: number;
29
31
  }
30
32
 
@@ -42,6 +44,8 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
42
44
  generation: number;
43
45
  srvMaxHosts: number;
44
46
  srvServiceName: string;
47
+ srvAllowedHostsSuffix?: string;
48
+ srvHostValidator?: (host: string) => boolean;
45
49
  _timeout?: NodeJS.Timeout;
46
50
 
47
51
  /** @event */
@@ -58,6 +62,8 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
58
62
  this.srvHost = options.srvHost;
59
63
  this.srvMaxHosts = options.srvMaxHosts ?? 0;
60
64
  this.srvServiceName = options.srvServiceName ?? 'mongodb';
65
+ this.srvAllowedHostsSuffix = options.srvAllowedHostsSuffix;
66
+ this.srvHostValidator = options.srvHostValidator;
61
67
  this.rescanSrvIntervalMS = 60000;
62
68
  this.heartbeatFrequencyMS = options.heartbeatFrequencyMS ?? 10000;
63
69
 
@@ -129,9 +135,12 @@ export class SrvPoller extends TypedEventEmitter<SrvPollerEvents> {
129
135
  const finalAddresses: dns.SrvRecord[] = [];
130
136
  for (const record of srvRecords) {
131
137
  try {
132
- checkParentDomainMatch(record.name, this.srvHost);
133
- finalAddresses.push(record);
138
+ const name = normalizeDnsName(record.name);
139
+ // A validator that throws is treated as rejecting the host, so polling continues
140
+ verifySrvHost(name, this.srvHost, this);
141
+ finalAddresses.push({ ...record, name });
134
142
  } catch (error) {
143
+ // TODO(NODE-4994): log the rejected host name, as the polling spec recommends
135
144
  squashError(error);
136
145
  }
137
146
  }