mongodb 4.1.2 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/README.md +17 -16
  2. package/lib/admin.js +5 -5
  3. package/lib/admin.js.map +1 -1
  4. package/lib/bson.js.map +1 -1
  5. package/lib/bulk/common.js +54 -56
  6. package/lib/bulk/common.js.map +1 -1
  7. package/lib/change_stream.js +13 -14
  8. package/lib/change_stream.js.map +1 -1
  9. package/lib/cmap/auth/gssapi.js +1 -1
  10. package/lib/cmap/auth/gssapi.js.map +1 -1
  11. package/lib/cmap/auth/mongocr.js +2 -2
  12. package/lib/cmap/auth/mongocr.js.map +1 -1
  13. package/lib/cmap/auth/mongodb_aws.js +3 -3
  14. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  15. package/lib/cmap/auth/plain.js +1 -1
  16. package/lib/cmap/auth/plain.js.map +1 -1
  17. package/lib/cmap/auth/scram.js +5 -5
  18. package/lib/cmap/auth/scram.js.map +1 -1
  19. package/lib/cmap/auth/x509.js +1 -1
  20. package/lib/cmap/auth/x509.js.map +1 -1
  21. package/lib/cmap/command_monitoring_events.js +15 -15
  22. package/lib/cmap/command_monitoring_events.js.map +1 -1
  23. package/lib/cmap/commands.js +10 -7
  24. package/lib/cmap/commands.js.map +1 -1
  25. package/lib/cmap/connect.js +2 -2
  26. package/lib/cmap/connect.js.map +1 -1
  27. package/lib/cmap/connection.js +15 -15
  28. package/lib/cmap/connection.js.map +1 -1
  29. package/lib/cmap/connection_pool.js +3 -3
  30. package/lib/cmap/connection_pool.js.map +1 -1
  31. package/lib/cmap/message_stream.js +2 -2
  32. package/lib/cmap/message_stream.js.map +1 -1
  33. package/lib/cmap/stream_description.js +1 -1
  34. package/lib/cmap/stream_description.js.map +1 -1
  35. package/lib/cmap/wire_protocol/constants.js +4 -4
  36. package/lib/collection.js +42 -41
  37. package/lib/collection.js.map +1 -1
  38. package/lib/connection_string.js +73 -45
  39. package/lib/connection_string.js.map +1 -1
  40. package/lib/cursor/abstract_cursor.js +16 -13
  41. package/lib/cursor/abstract_cursor.js.map +1 -1
  42. package/lib/cursor/aggregation_cursor.js +14 -14
  43. package/lib/cursor/aggregation_cursor.js.map +1 -1
  44. package/lib/cursor/find_cursor.js +23 -23
  45. package/lib/cursor/find_cursor.js.map +1 -1
  46. package/lib/db.js +20 -20
  47. package/lib/db.js.map +1 -1
  48. package/lib/deps.js +1 -1
  49. package/lib/deps.js.map +1 -1
  50. package/lib/gridfs/index.js +3 -3
  51. package/lib/gridfs/index.js.map +1 -1
  52. package/lib/gridfs/upload.js +1 -1
  53. package/lib/gridfs/upload.js.map +1 -1
  54. package/lib/logger.js +5 -5
  55. package/lib/logger.js.map +1 -1
  56. package/lib/mongo_client.js +7 -7
  57. package/lib/mongo_client.js.map +1 -1
  58. package/lib/mongo_types.js.map +1 -1
  59. package/lib/operations/add_user.js +3 -3
  60. package/lib/operations/add_user.js.map +1 -1
  61. package/lib/operations/aggregate.js +3 -4
  62. package/lib/operations/aggregate.js.map +1 -1
  63. package/lib/operations/bulk_write.js +1 -1
  64. package/lib/operations/bulk_write.js.map +1 -1
  65. package/lib/operations/command.js +7 -3
  66. package/lib/operations/command.js.map +1 -1
  67. package/lib/operations/common_functions.js +1 -1
  68. package/lib/operations/common_functions.js.map +1 -1
  69. package/lib/operations/connect.js +1 -1
  70. package/lib/operations/connect.js.map +1 -1
  71. package/lib/operations/count.js +1 -1
  72. package/lib/operations/count.js.map +1 -1
  73. package/lib/operations/create_collection.js +1 -1
  74. package/lib/operations/create_collection.js.map +1 -1
  75. package/lib/operations/delete.js +6 -6
  76. package/lib/operations/delete.js.map +1 -1
  77. package/lib/operations/distinct.js +4 -4
  78. package/lib/operations/distinct.js.map +1 -1
  79. package/lib/operations/drop.js +2 -2
  80. package/lib/operations/drop.js.map +1 -1
  81. package/lib/operations/estimated_document_count.js +2 -2
  82. package/lib/operations/estimated_document_count.js.map +1 -1
  83. package/lib/operations/execute_operation.js +24 -7
  84. package/lib/operations/execute_operation.js.map +1 -1
  85. package/lib/operations/find.js +8 -8
  86. package/lib/operations/find.js.map +1 -1
  87. package/lib/operations/find_and_modify.js +7 -7
  88. package/lib/operations/find_and_modify.js.map +1 -1
  89. package/lib/operations/get_more.js +28 -0
  90. package/lib/operations/get_more.js.map +1 -0
  91. package/lib/operations/indexes.js +14 -14
  92. package/lib/operations/indexes.js.map +1 -1
  93. package/lib/operations/insert.js +5 -5
  94. package/lib/operations/insert.js.map +1 -1
  95. package/lib/operations/list_collections.js +12 -7
  96. package/lib/operations/list_collections.js.map +1 -1
  97. package/lib/operations/list_databases.js +1 -1
  98. package/lib/operations/list_databases.js.map +1 -1
  99. package/lib/operations/map_reduce.js +6 -6
  100. package/lib/operations/map_reduce.js.map +1 -1
  101. package/lib/operations/operation.js +4 -2
  102. package/lib/operations/operation.js.map +1 -1
  103. package/lib/operations/remove_user.js +1 -1
  104. package/lib/operations/remove_user.js.map +1 -1
  105. package/lib/operations/rename.js +2 -2
  106. package/lib/operations/rename.js.map +1 -1
  107. package/lib/operations/set_profiling_level.js +1 -1
  108. package/lib/operations/set_profiling_level.js.map +1 -1
  109. package/lib/operations/stats.js +2 -2
  110. package/lib/operations/stats.js.map +1 -1
  111. package/lib/operations/update.js +12 -12
  112. package/lib/operations/update.js.map +1 -1
  113. package/lib/sdam/monitor.js +14 -14
  114. package/lib/sdam/monitor.js.map +1 -1
  115. package/lib/sdam/server.js +19 -12
  116. package/lib/sdam/server.js.map +1 -1
  117. package/lib/sdam/server_description.js +3 -3
  118. package/lib/sdam/server_description.js.map +1 -1
  119. package/lib/sdam/server_selection.js +36 -1
  120. package/lib/sdam/server_selection.js.map +1 -1
  121. package/lib/sdam/srv_polling.js +9 -9
  122. package/lib/sdam/srv_polling.js.map +1 -1
  123. package/lib/sdam/topology.js +29 -21
  124. package/lib/sdam/topology.js.map +1 -1
  125. package/lib/sdam/topology_description.js +34 -12
  126. package/lib/sdam/topology_description.js.map +1 -1
  127. package/lib/sessions.js +19 -19
  128. package/lib/sessions.js.map +1 -1
  129. package/lib/transactions.js.map +1 -1
  130. package/lib/utils.js +53 -46
  131. package/lib/utils.js.map +1 -1
  132. package/mongodb.d.ts +62 -30
  133. package/mongodb.ts34.d.ts +63 -30
  134. package/package.json +32 -30
  135. package/src/bson.ts +1 -0
  136. package/src/bulk/common.ts +48 -35
  137. package/src/change_stream.ts +10 -8
  138. package/src/cmap/commands.ts +11 -8
  139. package/src/cmap/connection.ts +1 -0
  140. package/src/cmap/wire_protocol/constants.ts +4 -4
  141. package/src/collection.ts +24 -16
  142. package/src/connection_string.ts +75 -36
  143. package/src/cursor/abstract_cursor.ts +9 -11
  144. package/src/db.ts +5 -6
  145. package/src/mongo_client.ts +12 -0
  146. package/src/mongo_types.ts +11 -14
  147. package/src/operations/aggregate.ts +1 -2
  148. package/src/operations/command.ts +5 -0
  149. package/src/operations/create_collection.ts +1 -1
  150. package/src/operations/execute_operation.ts +22 -2
  151. package/src/operations/get_more.ts +49 -0
  152. package/src/operations/list_collections.ts +12 -4
  153. package/src/operations/list_databases.ts +6 -1
  154. package/src/operations/operation.ts +5 -1
  155. package/src/sdam/server.ts +8 -0
  156. package/src/sdam/server_selection.ts +43 -0
  157. package/src/sdam/srv_polling.ts +12 -11
  158. package/src/sdam/topology.ts +27 -10
  159. package/src/sdam/topology_description.ts +35 -11
  160. package/src/transactions.ts +2 -2
  161. package/src/utils.ts +67 -56
package/src/collection.ts CHANGED
@@ -676,12 +676,16 @@ export class Collection<TSchema extends Document = Document> {
676
676
  * @param options - Optional settings for the command
677
677
  * @param callback - An optional callback, a Promise will be returned if none is provided
678
678
  */
679
- findOne(): Promise<TSchema | null>;
680
- findOne(callback: Callback<TSchema | null>): void;
681
- findOne(filter: Filter<TSchema>): Promise<TSchema | null>;
682
- findOne(filter: Filter<TSchema>, callback: Callback<TSchema | null>): void;
683
- findOne(filter: Filter<TSchema>, options: FindOptions): Promise<TSchema | null>;
684
- findOne(filter: Filter<TSchema>, options: FindOptions, callback: Callback<TSchema | null>): void;
679
+ findOne(): Promise<WithId<TSchema> | null>;
680
+ findOne(callback: Callback<WithId<TSchema> | null>): void;
681
+ findOne(filter: Filter<TSchema>): Promise<WithId<TSchema> | null>;
682
+ findOne(filter: Filter<TSchema>, callback: Callback<WithId<TSchema> | null>): void;
683
+ findOne(filter: Filter<TSchema>, options: FindOptions): Promise<WithId<TSchema> | null>;
684
+ findOne(
685
+ filter: Filter<TSchema>,
686
+ options: FindOptions,
687
+ callback: Callback<WithId<TSchema> | null>
688
+ ): void;
685
689
 
686
690
  // allow an override of the schema.
687
691
  findOne<T = TSchema>(): Promise<T | null>;
@@ -695,10 +699,10 @@ export class Collection<TSchema extends Document = Document> {
695
699
  ): void;
696
700
 
697
701
  findOne(
698
- filter?: Filter<TSchema> | Callback<TSchema | null>,
699
- options?: FindOptions | Callback<TSchema | null>,
700
- callback?: Callback<TSchema | null>
701
- ): Promise<TSchema | null> | void {
702
+ filter?: Filter<TSchema> | Callback<WithId<TSchema> | null>,
703
+ options?: FindOptions | Callback<WithId<TSchema> | null>,
704
+ callback?: Callback<WithId<TSchema> | null>
705
+ ): Promise<WithId<TSchema> | null> | void {
702
706
  if (callback != null && typeof callback !== 'function') {
703
707
  throw new MongoInvalidArgumentError(
704
708
  'Third parameter to `findOne()` must be a callback or undefined'
@@ -706,7 +710,7 @@ export class Collection<TSchema extends Document = Document> {
706
710
  }
707
711
 
708
712
  if (typeof filter === 'function') {
709
- callback = filter as Callback<TSchema | null>;
713
+ callback = filter as Callback<WithId<TSchema> | null>;
710
714
  filter = {};
711
715
  options = {};
712
716
  }
@@ -725,10 +729,10 @@ export class Collection<TSchema extends Document = Document> {
725
729
  *
726
730
  * @param filter - The filter predicate. If unspecified, then all documents in the collection will match the predicate
727
731
  */
728
- find(): FindCursor<TSchema>;
729
- find(filter: Filter<TSchema>, options?: FindOptions): FindCursor<TSchema>;
732
+ find(): FindCursor<WithId<TSchema>>;
733
+ find(filter: Filter<TSchema>, options?: FindOptions): FindCursor<WithId<TSchema>>;
730
734
  find<T>(filter: Filter<TSchema>, options?: FindOptions): FindCursor<T>;
731
- find(filter?: Filter<TSchema>, options?: FindOptions): FindCursor<TSchema> {
735
+ find(filter?: Filter<TSchema>, options?: FindOptions): FindCursor<WithId<TSchema>> {
732
736
  if (arguments.length > 2) {
733
737
  throw new MongoInvalidArgumentError(
734
738
  'Method "collection.find()" accepts at most two arguments'
@@ -738,7 +742,7 @@ export class Collection<TSchema extends Document = Document> {
738
742
  throw new MongoInvalidArgumentError('Argument "options" must not be function');
739
743
  }
740
744
 
741
- return new FindCursor<TSchema>(
745
+ return new FindCursor<WithId<TSchema>>(
742
746
  getTopology(this),
743
747
  this.s.namespace,
744
748
  filter,
@@ -1366,7 +1370,7 @@ export class Collection<TSchema extends Document = Document> {
1366
1370
  * @param pipeline - An array of aggregation pipelines to execute
1367
1371
  * @param options - Optional settings for the command
1368
1372
  */
1369
- aggregate<T = TSchema>(
1373
+ aggregate<T = Document>(
1370
1374
  pipeline: Document[] = [],
1371
1375
  options?: AggregateOptions
1372
1376
  ): AggregationCursor<T> {
@@ -1415,6 +1419,7 @@ export class Collection<TSchema extends Document = Document> {
1415
1419
  /**
1416
1420
  * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
1417
1421
  *
1422
+ * @deprecated collection.mapReduce is deprecated. Use the aggregation pipeline instead. Visit https://docs.mongodb.com/manual/reference/map-reduce-to-aggregation-pipeline for more information on how to translate map-reduce operations to the aggregation pipeline.
1418
1423
  * @param map - The mapping function.
1419
1424
  * @param reduce - The reduce function.
1420
1425
  * @param options - Optional settings for the command
@@ -1446,6 +1451,9 @@ export class Collection<TSchema extends Document = Document> {
1446
1451
  options?: MapReduceOptions<TKey, TValue> | Callback<Document | Document[]>,
1447
1452
  callback?: Callback<Document | Document[]>
1448
1453
  ): Promise<Document | Document[]> | void {
1454
+ emitWarningOnce(
1455
+ 'collection.mapReduce is deprecated. Use the aggregation pipeline instead. Visit https://docs.mongodb.com/manual/reference/map-reduce-to-aggregation-pipeline for more information on how to translate map-reduce operations to the aggregation pipeline.'
1456
+ );
1449
1457
  if ('function' === typeof options) (callback = options), (options = {});
1450
1458
  // Out must always be defined (make sure we don't break weirdly on pre 1.8+ servers)
1451
1459
  // TODO NODE-3339: Figure out if this is still necessary given we no longer officially support pre-1.8
@@ -75,7 +75,7 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
75
75
 
76
76
  // Resolve the SRV record and use the result as the list of hosts to connect to.
77
77
  const lookupAddress = options.srvHost;
78
- dns.resolveSrv(`_mongodb._tcp.${lookupAddress}`, (err, addresses) => {
78
+ dns.resolveSrv(`_${options.srvServiceName}._tcp.${lookupAddress}`, (err, addresses) => {
79
79
  if (err) return callback(err);
80
80
 
81
81
  if (addresses.length === 0) {
@@ -92,7 +92,7 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
92
92
  HostAddress.fromString(`${r.name}:${r.port ?? 27017}`)
93
93
  );
94
94
 
95
- const lbError = validateLoadBalancedOptions(hostAddresses, options);
95
+ const lbError = validateLoadBalancedOptions(hostAddresses, options, true);
96
96
  if (lbError) {
97
97
  return callback(lbError);
98
98
  }
@@ -116,14 +116,14 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
116
116
  );
117
117
  }
118
118
 
119
+ if (VALID_TXT_RECORDS.some(option => txtRecordOptions.get(option) === '')) {
120
+ return callback(new MongoParseError('Cannot have empty URI params in DNS TXT Record'));
121
+ }
122
+
119
123
  const source = txtRecordOptions.get('authSource') ?? undefined;
120
124
  const replicaSet = txtRecordOptions.get('replicaSet') ?? undefined;
121
125
  const loadBalanced = txtRecordOptions.get('loadBalanced') ?? undefined;
122
126
 
123
- if (source === '' || replicaSet === '') {
124
- return callback(new MongoParseError('Cannot have empty URI params in DNS TXT Record'));
125
- }
126
-
127
127
  if (!options.userSpecifiedAuthSource && source) {
128
128
  options.credentials = MongoCredentials.merge(options.credentials, { source });
129
129
  }
@@ -136,7 +136,11 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
136
136
  options.loadBalanced = true;
137
137
  }
138
138
 
139
- const lbError = validateLoadBalancedOptions(hostAddresses, options);
139
+ if (options.replicaSet && options.srvMaxHosts > 0) {
140
+ return callback(new MongoParseError('Cannot combine replicaSet option with srvMaxHosts'));
141
+ }
142
+
143
+ const lbError = validateLoadBalancedOptions(hostAddresses, options, true);
140
144
  if (lbError) {
141
145
  return callback(lbError);
142
146
  }
@@ -251,13 +255,6 @@ export function parseOptions(
251
255
 
252
256
  const mongoOptions = Object.create(null);
253
257
  mongoOptions.hosts = isSRV ? [] : hosts.map(HostAddress.fromString);
254
- if (isSRV) {
255
- // SRV Record is resolved upon connecting
256
- mongoOptions.srvHost = hosts[0];
257
- if (!url.searchParams.has('tls') && !url.searchParams.has('ssl')) {
258
- options.tls = true;
259
- }
260
- }
261
258
 
262
259
  const urlOptions = new CaseInsensitiveMap();
263
260
 
@@ -289,17 +286,6 @@ export function parseOptions(
289
286
  throw new MongoAPIError('URI cannot contain options with no value');
290
287
  }
291
288
 
292
- if (key.toLowerCase() === 'serverapi') {
293
- throw new MongoParseError(
294
- 'URI cannot contain `serverApi`, it can only be passed to the client'
295
- );
296
- }
297
-
298
- if (key.toLowerCase() === 'authsource' && urlOptions.has('authSource')) {
299
- // If authSource is an explicit key in the urlOptions we need to remove the implicit dbName
300
- urlOptions.delete('authSource');
301
- }
302
-
303
289
  if (!urlOptions.has(key)) {
304
290
  urlOptions.set(key, values);
305
291
  }
@@ -309,10 +295,20 @@ export function parseOptions(
309
295
  Object.entries(options).filter(([, v]) => v != null)
310
296
  );
311
297
 
298
+ // Validate options that can only be provided by one of uri or object
299
+
300
+ if (urlOptions.has('serverApi')) {
301
+ throw new MongoParseError(
302
+ 'URI cannot contain `serverApi`, it can only be passed to the client'
303
+ );
304
+ }
305
+
312
306
  if (objectOptions.has('loadBalanced')) {
313
307
  throw new MongoParseError('loadBalanced is only a valid option in the URI');
314
308
  }
315
309
 
310
+ // All option collection
311
+
316
312
  const allOptions = new CaseInsensitiveMap();
317
313
 
318
314
  const allKeys = new Set<string>([
@@ -360,6 +356,8 @@ export function parseOptions(
360
356
  );
361
357
  }
362
358
 
359
+ // Option parsing and setting
360
+
363
361
  for (const [key, descriptor] of Object.entries(OPTIONS)) {
364
362
  const values = allOptions.get(key);
365
363
  if (!values || values.length === 0) continue;
@@ -401,25 +399,53 @@ export function parseOptions(
401
399
 
402
400
  if (options.promiseLibrary) PromiseProvider.set(options.promiseLibrary);
403
401
 
404
- if (mongoOptions.directConnection && typeof mongoOptions.srvHost === 'string') {
405
- throw new MongoAPIError('SRV URI does not support directConnection');
406
- }
407
-
408
- const lbError = validateLoadBalancedOptions(hosts, mongoOptions);
402
+ const lbError = validateLoadBalancedOptions(hosts, mongoOptions, isSRV);
409
403
  if (lbError) {
410
404
  throw lbError;
411
405
  }
406
+ if (mongoClient && mongoOptions.autoEncryption) {
407
+ Encrypter.checkForMongoCrypt();
408
+ mongoOptions.encrypter = new Encrypter(mongoClient, uri, options);
409
+ mongoOptions.autoEncrypter = mongoOptions.encrypter.autoEncrypter;
410
+ }
411
+
412
+ // Potential SRV Overrides and SRV connection string validations
412
413
 
413
- // Potential SRV Overrides
414
414
  mongoOptions.userSpecifiedAuthSource =
415
415
  objectOptions.has('authSource') || urlOptions.has('authSource');
416
416
  mongoOptions.userSpecifiedReplicaSet =
417
417
  objectOptions.has('replicaSet') || urlOptions.has('replicaSet');
418
418
 
419
- if (mongoClient && mongoOptions.autoEncryption) {
420
- Encrypter.checkForMongoCrypt();
421
- mongoOptions.encrypter = new Encrypter(mongoClient, uri, options);
422
- mongoOptions.autoEncrypter = mongoOptions.encrypter.autoEncrypter;
419
+ if (isSRV) {
420
+ // SRV Record is resolved upon connecting
421
+ mongoOptions.srvHost = hosts[0];
422
+
423
+ if (mongoOptions.directConnection) {
424
+ throw new MongoAPIError('SRV URI does not support directConnection');
425
+ }
426
+
427
+ if (mongoOptions.srvMaxHosts > 0 && typeof mongoOptions.replicaSet === 'string') {
428
+ throw new MongoParseError('Cannot use srvMaxHosts option with replicaSet');
429
+ }
430
+
431
+ // SRV turns on TLS by default, but users can override and turn it off
432
+ const noUserSpecifiedTLS = !objectOptions.has('tls') && !urlOptions.has('tls');
433
+ const noUserSpecifiedSSL = !objectOptions.has('ssl') && !urlOptions.has('ssl');
434
+ if (noUserSpecifiedTLS && noUserSpecifiedSSL) {
435
+ mongoOptions.tls = true;
436
+ }
437
+ } else {
438
+ const userSpecifiedSrvOptions =
439
+ urlOptions.has('srvMaxHosts') ||
440
+ objectOptions.has('srvMaxHosts') ||
441
+ urlOptions.has('srvServiceName') ||
442
+ objectOptions.has('srvServiceName');
443
+
444
+ if (userSpecifiedSrvOptions) {
445
+ throw new MongoParseError(
446
+ 'Cannot use srvMaxHosts or srvServiceName with a non-srv connection string'
447
+ );
448
+ }
423
449
  }
424
450
 
425
451
  return mongoOptions;
@@ -427,7 +453,8 @@ export function parseOptions(
427
453
 
428
454
  function validateLoadBalancedOptions(
429
455
  hosts: HostAddress[] | string[],
430
- mongoOptions: MongoOptions
456
+ mongoOptions: MongoOptions,
457
+ isSrv: boolean
431
458
  ): MongoParseError | undefined {
432
459
  if (mongoOptions.loadBalanced) {
433
460
  if (hosts.length > 1) {
@@ -439,6 +466,10 @@ function validateLoadBalancedOptions(
439
466
  if (mongoOptions.directConnection) {
440
467
  return new MongoParseError(LB_DIRECT_CONNECTION_ERROR);
441
468
  }
469
+
470
+ if (isSrv && mongoOptions.srvMaxHosts > 0) {
471
+ return new MongoParseError('Cannot limit srv hosts with loadBalanced enabled');
472
+ }
442
473
  }
443
474
  }
444
475
 
@@ -924,6 +955,14 @@ export const OPTIONS = {
924
955
  default: 0,
925
956
  type: 'uint'
926
957
  },
958
+ srvMaxHosts: {
959
+ type: 'uint',
960
+ default: 0
961
+ },
962
+ srvServiceName: {
963
+ type: 'string',
964
+ default: 'mongodb'
965
+ },
927
966
  ssl: {
928
967
  target: 'tls',
929
968
  type: 'boolean'
@@ -14,7 +14,8 @@ import { ReadPreference, ReadPreferenceLike } from '../read_preference';
14
14
  import type { Server } from '../sdam/server';
15
15
  import type { Topology } from '../sdam/topology';
16
16
  import { Readable, Transform } from 'stream';
17
- import type { ExecutionResult } from '../operations/execute_operation';
17
+ import { executeOperation, ExecutionResult } from '../operations/execute_operation';
18
+ import { GetMoreOperation } from '../operations/get_more';
18
19
  import { ReadConcern, ReadConcernLike } from '../read_concern';
19
20
  import { TODO_NODE_3286, TypedEventEmitter } from '../mongo_types';
20
21
 
@@ -610,16 +611,13 @@ export abstract class AbstractCursor<
610
611
  return;
611
612
  }
612
613
 
613
- server.getMore(
614
- cursorNs,
615
- cursorId,
616
- {
617
- ...this[kOptions],
618
- session: this[kSession],
619
- batchSize
620
- },
621
- callback
622
- );
614
+ const getMoreOperation = new GetMoreOperation(cursorNs, cursorId, server, {
615
+ ...this[kOptions],
616
+ session: this[kSession],
617
+ batchSize
618
+ });
619
+
620
+ executeOperation(this.topology, getMoreOperation, callback);
623
621
  }
624
622
  }
625
623
 
package/src/db.ts CHANGED
@@ -226,18 +226,17 @@ export class Db {
226
226
  * @param options - Optional settings for the command
227
227
  * @param callback - An optional callback, a Promise will be returned if none is provided
228
228
  */
229
- createCollection<TSchema extends Document = Document>(name: string): Promise<Collection<TSchema>>;
230
229
  createCollection<TSchema extends Document = Document>(
231
230
  name: string,
232
- callback: Callback<Collection<TSchema>>
233
- ): void;
231
+ options?: CreateCollectionOptions
232
+ ): Promise<Collection<TSchema>>;
234
233
  createCollection<TSchema extends Document = Document>(
235
234
  name: string,
236
- options: CreateCollectionOptions
237
- ): Promise<Collection<TSchema>>;
235
+ callback: Callback<Collection<TSchema>>
236
+ ): void;
238
237
  createCollection<TSchema extends Document = Document>(
239
238
  name: string,
240
- options: CreateCollectionOptions,
239
+ options: CreateCollectionOptions | undefined,
241
240
  callback: Callback<Collection<TSchema>>
242
241
  ): void;
243
242
  createCollection<TSchema extends Document = Document>(
@@ -132,6 +132,16 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
132
132
  compressors?: CompressorName[] | string;
133
133
  /** An integer that specifies the compression level if using zlib for network compression. */
134
134
  zlibCompressionLevel?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;
135
+ /** The maximum number of hosts to connect to when using an srv connection string, a setting of `0` means unlimited hosts */
136
+ srvMaxHosts?: number;
137
+ /**
138
+ * Modifies the srv URI to look like:
139
+ *
140
+ * `_{srvServiceName}._tcp.{hostname}.{domainname}`
141
+ *
142
+ * Querying this DNS URI is expected to respond with SRV records
143
+ */
144
+ srvServiceName?: string;
135
145
  /** The maximum number of connections in the connection pool. */
136
146
  maxPoolSize?: number;
137
147
  /** The minimum number of connections in the connection pool. */
@@ -643,6 +653,8 @@ export interface MongoOptions
643
653
  | 'retryWrites'
644
654
  | 'serverSelectionTimeoutMS'
645
655
  | 'socketTimeoutMS'
656
+ | 'srvMaxHosts'
657
+ | 'srvServiceName'
646
658
  | 'tlsAllowInvalidCertificates'
647
659
  | 'tlsAllowInvalidHostnames'
648
660
  | 'tlsInsecure'
@@ -38,9 +38,11 @@ export type WithId<TSchema> = EnhancedOmit<TSchema, '_id'> & { _id: InferIdType<
38
38
  * `TSchema['_id'] extends ObjectId` which translated to "Is the _id property ObjectId?"
39
39
  * we instead ask "Does ObjectId look like (have the same shape) as the _id?"
40
40
  */
41
- export type OptionalId<TSchema extends { _id?: any }> = ObjectId extends TSchema['_id'] // a Schema with ObjectId _id type or "any" or "indexed type" provided
42
- ? EnhancedOmit<TSchema, '_id'> & { _id?: InferIdType<TSchema> } // a Schema provided but _id type is not ObjectId
43
- : WithId<TSchema>; // TODO(NODE-3285): Improve type readability
41
+ export type OptionalId<TSchema> = TSchema extends { _id?: any }
42
+ ? ObjectId extends TSchema['_id'] // a Schema with ObjectId _id type or "any" or "indexed type" provided
43
+ ? EnhancedOmit<TSchema, '_id'> & { _id?: InferIdType<TSchema> } // a Schema provided but _id type is not ObjectId
44
+ : WithId<TSchema>
45
+ : EnhancedOmit<TSchema, '_id'> & { _id?: InferIdType<TSchema> }; // TODO(NODE-3285): Improve type readability
44
46
 
45
47
  /** TypeScript Omit (Exclude to be specific) does not work for objects with an "any" indexed type, and breaks discriminated unions @public */
46
48
  export type EnhancedOmit<TRecordOrUnion, KeyUnion> = string extends keyof TRecordOrUnion
@@ -54,9 +56,8 @@ export type WithoutId<TSchema> = Omit<TSchema, '_id'>;
54
56
 
55
57
  /** A MongoDB filter can be some portion of the schema or a set of operators @public */
56
58
  export type Filter<TSchema> = {
57
- [P in keyof TSchema]?: Condition<TSchema[P]>;
58
- } &
59
- RootFilterOperators<TSchema>;
59
+ [P in keyof WithId<TSchema>]?: Condition<WithId<TSchema>[P]>;
60
+ } & RootFilterOperators<WithId<TSchema>>;
60
61
 
61
62
  /** @public */
62
63
  export type Condition<T> = AlternativeType<T> | FilterOperators<AlternativeType<T>>;
@@ -255,8 +256,7 @@ export type SetFields<TSchema> = ({
255
256
  readonly [key in KeysOfAType<TSchema, ReadonlyArray<any> | undefined>]?:
256
257
  | OptionalId<Flatten<TSchema[key]>>
257
258
  | AddToSetOperators<Array<OptionalId<Flatten<TSchema[key]>>>>;
258
- } &
259
- NotAcceptedFields<TSchema, ReadonlyArray<any> | undefined>) & {
259
+ } & NotAcceptedFields<TSchema, ReadonlyArray<any> | undefined>) & {
260
260
  readonly [key: string]: AddToSetOperators<any> | any;
261
261
  };
262
262
 
@@ -265,8 +265,7 @@ export type PushOperator<TSchema> = ({
265
265
  readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
266
266
  | Flatten<TSchema[key]>
267
267
  | ArrayOperator<Array<Flatten<TSchema[key]>>>;
268
- } &
269
- NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
268
+ } & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
270
269
  readonly [key: string]: ArrayOperator<any> | any;
271
270
  };
272
271
 
@@ -275,16 +274,14 @@ export type PullOperator<TSchema> = ({
275
274
  readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?:
276
275
  | Partial<Flatten<TSchema[key]>>
277
276
  | FilterOperations<Flatten<TSchema[key]>>;
278
- } &
279
- NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
277
+ } & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
280
278
  readonly [key: string]: FilterOperators<any> | any;
281
279
  };
282
280
 
283
281
  /** @public */
284
282
  export type PullAllOperator<TSchema> = ({
285
283
  readonly [key in KeysOfAType<TSchema, ReadonlyArray<any>>]?: TSchema[key];
286
- } &
287
- NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
284
+ } & NotAcceptedFields<TSchema, ReadonlyArray<any>>) & {
288
285
  readonly [key: string]: ReadonlyArray<any>;
289
286
  };
290
287
 
@@ -1,5 +1,4 @@
1
1
  import { CommandOperation, CommandOperationOptions, CollationOptions } from './command';
2
- import { ReadPreference } from '../read_preference';
3
2
  import { MongoInvalidArgumentError } from '../error';
4
3
  import { maxWireVersion, MongoDBNamespace } from '../utils';
5
4
  import { Aspect, defineAspects, Hint } from './operation';
@@ -65,7 +64,7 @@ export class AggregateOperation<T = Document> extends CommandOperation<T> {
65
64
  }
66
65
 
67
66
  if (this.hasWriteStage) {
68
- this.readPreference = ReadPreference.primary;
67
+ this.trySecondaryWrite = true;
69
68
  }
70
69
 
71
70
  if (this.explain && this.writeConcern) {
@@ -10,6 +10,7 @@ import type { Server } from '../sdam/server';
10
10
  import type { BSONSerializeOptions, Document } from '../bson';
11
11
  import type { ReadConcernLike } from './../read_concern';
12
12
  import { Explain, ExplainOptions } from '../explain';
13
+ import { MIN_SECONDARY_WRITE_WIRE_VERSION } from '../sdam/server_selection';
13
14
 
14
15
  const SUPPORTS_WRITE_CONCERN_AND_COLLATION = 5;
15
16
 
@@ -126,6 +127,10 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
126
127
  Object.assign(cmd, { readConcern: this.readConcern });
127
128
  }
128
129
 
130
+ if (this.trySecondaryWrite && serverWireVersion < MIN_SECONDARY_WRITE_WIRE_VERSION) {
131
+ options.omitReadPreference = true;
132
+ }
133
+
129
134
  if (options.collation && serverWireVersion < SUPPORTS_WRITE_CONCERN_AND_COLLATION) {
130
135
  callback(
131
136
  new MongoCompatibilityError(
@@ -37,7 +37,7 @@ const ILLEGAL_COMMAND_FIELDS = new Set([
37
37
  export interface TimeSeriesCollectionOptions extends Document {
38
38
  timeField: string;
39
39
  metaField?: string;
40
- granularity?: string;
40
+ granularity?: 'seconds' | 'minutes' | 'hours' | string;
41
41
  }
42
42
 
43
43
  /** @public */
@@ -17,6 +17,11 @@ import type { Topology } from '../sdam/topology';
17
17
  import type { ClientSession } from '../sessions';
18
18
  import type { Document } from '../bson';
19
19
  import { supportsRetryableWrites } from '../utils';
20
+ import {
21
+ sameServerSelector,
22
+ secondaryWritableServerSelector,
23
+ ServerSelector
24
+ } from '../sdam/server_selection';
20
25
 
21
26
  const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
22
27
  const MMAPv1_RETRY_WRITES_ERROR_MESSAGE =
@@ -150,6 +155,21 @@ function executeWithServerSelection(
150
155
  session.unpin();
151
156
  }
152
157
 
158
+ let selector: ReadPreference | ServerSelector;
159
+
160
+ if (operation.hasAspect(Aspect.CURSOR_ITERATING)) {
161
+ // Get more operations must always select the same server, but run through
162
+ // server selection to potentially force monitor checks if the server is
163
+ // in an unknown state.
164
+ selector = sameServerSelector(operation.server?.description);
165
+ } else if (operation.trySecondaryWrite) {
166
+ // If operation should try to write to secondary use the custom server selector
167
+ // otherwise provide the read preference.
168
+ selector = secondaryWritableServerSelector(topology.commonWireVersion, readPreference);
169
+ } else {
170
+ selector = readPreference;
171
+ }
172
+
153
173
  const serverSelectionOptions = { session };
154
174
  function callbackWithRetry(err?: any, result?: any) {
155
175
  if (err == null) {
@@ -182,7 +202,7 @@ function executeWithServerSelection(
182
202
  }
183
203
 
184
204
  // select a new server, and attempt to retry the operation
185
- topology.selectServer(readPreference, serverSelectionOptions, (e?: any, server?: any) => {
205
+ topology.selectServer(selector, serverSelectionOptions, (e?: any, server?: any) => {
186
206
  if (
187
207
  e ||
188
208
  (operation.hasAspect(Aspect.READ_OPERATION) && !supportsRetryableReads(server)) ||
@@ -227,7 +247,7 @@ function executeWithServerSelection(
227
247
  }
228
248
 
229
249
  // select a server, and execute the operation against it
230
- topology.selectServer(readPreference, serverSelectionOptions, (err?: any, server?: any) => {
250
+ topology.selectServer(selector, serverSelectionOptions, (err?: any, server?: any) => {
231
251
  if (err) {
232
252
  callback(err);
233
253
  return;
@@ -0,0 +1,49 @@
1
+ import type { Document, Long } from '../bson';
2
+ import { MongoRuntimeError } from '../error';
3
+ import type { Callback, MongoDBNamespace } from '../utils';
4
+ import type { Server } from '../sdam/server';
5
+ import { Aspect, AbstractOperation, OperationOptions, defineAspects } from './operation';
6
+ import type { ClientSession } from '../sessions';
7
+
8
+ /**
9
+ * @public
10
+ */
11
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
+ export interface GetMoreOptions extends OperationOptions {
13
+ /** Set the batchSize for the getMoreCommand when iterating over the query results. */
14
+ batchSize?: number;
15
+ /** You can put a $comment field on a query to make looking in the profiler logs simpler. */
16
+ comment?: string | Document;
17
+ /** Number of milliseconds to wait before aborting the query. */
18
+ maxTimeMS?: number;
19
+ }
20
+
21
+ /** @internal */
22
+ export class GetMoreOperation extends AbstractOperation {
23
+ cursorId: Long;
24
+ options: GetMoreOptions;
25
+ server: Server;
26
+
27
+ constructor(ns: MongoDBNamespace, cursorId: Long, server: Server, options: GetMoreOptions = {}) {
28
+ super(options);
29
+ this.options = options;
30
+ this.ns = ns;
31
+ this.cursorId = cursorId;
32
+ this.server = server;
33
+ }
34
+
35
+ /**
36
+ * Although there is a server already associated with the get more operation, the signature
37
+ * for execute passes a server so we will just use that one.
38
+ */
39
+ execute(server: Server, session: ClientSession, callback: Callback<Document>): void {
40
+ if (server !== this.server) {
41
+ return callback(
42
+ new MongoRuntimeError('Getmore must run on the same server operation began on')
43
+ );
44
+ }
45
+ server.getMore(this.ns, this.cursorId, this.options, callback);
46
+ }
47
+ }
48
+
49
+ defineAspects(GetMoreOperation, [Aspect.READ_OPERATION, Aspect.CURSOR_ITERATING]);
@@ -15,6 +15,8 @@ const LIST_COLLECTIONS_WIRE_VERSION = 3;
15
15
  export interface ListCollectionsOptions extends CommandOperationOptions {
16
16
  /** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */
17
17
  nameOnly?: boolean;
18
+ /** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */
19
+ authorizedCollections?: boolean;
18
20
  /** The batchSize for the returned command cursor or if pre 2.8 the systems batch collection */
19
21
  batchSize?: number;
20
22
  }
@@ -25,6 +27,7 @@ export class ListCollectionsOperation extends CommandOperation<string[]> {
25
27
  db: Db;
26
28
  filter: Document;
27
29
  nameOnly: boolean;
30
+ authorizedCollections: boolean;
28
31
  batchSize?: number;
29
32
 
30
33
  constructor(db: Db, filter: Document, options?: ListCollectionsOptions) {
@@ -34,6 +37,7 @@ export class ListCollectionsOperation extends CommandOperation<string[]> {
34
37
  this.db = db;
35
38
  this.filter = filter;
36
39
  this.nameOnly = !!this.options.nameOnly;
40
+ this.authorizedCollections = !!this.options.authorizedCollections;
37
41
 
38
42
  if (typeof this.options.batchSize === 'number') {
39
43
  this.batchSize = this.options.batchSize;
@@ -90,14 +94,18 @@ export class ListCollectionsOperation extends CommandOperation<string[]> {
90
94
  return;
91
95
  }
92
96
 
93
- const command = {
97
+ return super.executeCommand(server, session, this.generateCommand(), callback);
98
+ }
99
+
100
+ /* This is here for the purpose of unit testing the final command that gets sent. */
101
+ generateCommand(): Document {
102
+ return {
94
103
  listCollections: 1,
95
104
  filter: this.filter,
96
105
  cursor: this.batchSize ? { batchSize: this.batchSize } : {},
97
- nameOnly: this.nameOnly
106
+ nameOnly: this.nameOnly,
107
+ authorizedCollections: this.authorizedCollections
98
108
  };
99
-
100
- return super.executeCommand(server, session, command, callback);
101
109
  }
102
110
  }
103
111
 
@@ -7,7 +7,12 @@ import type { Db } from '../db';
7
7
  import type { ClientSession } from '../sessions';
8
8
 
9
9
  /** @public */
10
- export type ListDatabasesResult = string[] | Document[];
10
+ export interface ListDatabasesResult {
11
+ databases: ({ name: string; sizeOnDisk?: number; empty?: boolean } & Document)[];
12
+ totalSize?: number;
13
+ totalSizeMb?: number;
14
+ ok: 1 | 0;
15
+ }
11
16
 
12
17
  /** @public */
13
18
  export interface ListDatabasesOptions extends CommandOperationOptions {