mongodb 4.1.3 → 4.2.2

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 (168) hide show
  1. package/README.md +15 -14
  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 +4 -4
  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/error.js +47 -24
  51. package/lib/error.js.map +1 -1
  52. package/lib/gridfs/index.js +3 -3
  53. package/lib/gridfs/index.js.map +1 -1
  54. package/lib/gridfs/upload.js +1 -1
  55. package/lib/gridfs/upload.js.map +1 -1
  56. package/lib/logger.js +5 -5
  57. package/lib/logger.js.map +1 -1
  58. package/lib/mongo_client.js +7 -7
  59. package/lib/mongo_client.js.map +1 -1
  60. package/lib/mongo_types.js.map +1 -1
  61. package/lib/operations/add_user.js +3 -3
  62. package/lib/operations/add_user.js.map +1 -1
  63. package/lib/operations/aggregate.js +3 -4
  64. package/lib/operations/aggregate.js.map +1 -1
  65. package/lib/operations/bulk_write.js +1 -1
  66. package/lib/operations/bulk_write.js.map +1 -1
  67. package/lib/operations/command.js +7 -3
  68. package/lib/operations/command.js.map +1 -1
  69. package/lib/operations/common_functions.js +1 -1
  70. package/lib/operations/common_functions.js.map +1 -1
  71. package/lib/operations/connect.js +1 -1
  72. package/lib/operations/connect.js.map +1 -1
  73. package/lib/operations/count.js +1 -1
  74. package/lib/operations/count.js.map +1 -1
  75. package/lib/operations/create_collection.js +1 -1
  76. package/lib/operations/create_collection.js.map +1 -1
  77. package/lib/operations/delete.js +6 -6
  78. package/lib/operations/delete.js.map +1 -1
  79. package/lib/operations/distinct.js +4 -4
  80. package/lib/operations/distinct.js.map +1 -1
  81. package/lib/operations/drop.js +2 -2
  82. package/lib/operations/drop.js.map +1 -1
  83. package/lib/operations/estimated_document_count.js +2 -2
  84. package/lib/operations/estimated_document_count.js.map +1 -1
  85. package/lib/operations/execute_operation.js +24 -7
  86. package/lib/operations/execute_operation.js.map +1 -1
  87. package/lib/operations/find.js +8 -8
  88. package/lib/operations/find.js.map +1 -1
  89. package/lib/operations/find_and_modify.js +7 -7
  90. package/lib/operations/find_and_modify.js.map +1 -1
  91. package/lib/operations/get_more.js +28 -0
  92. package/lib/operations/get_more.js.map +1 -0
  93. package/lib/operations/indexes.js +14 -14
  94. package/lib/operations/indexes.js.map +1 -1
  95. package/lib/operations/insert.js +5 -5
  96. package/lib/operations/insert.js.map +1 -1
  97. package/lib/operations/list_collections.js +12 -7
  98. package/lib/operations/list_collections.js.map +1 -1
  99. package/lib/operations/list_databases.js +1 -1
  100. package/lib/operations/list_databases.js.map +1 -1
  101. package/lib/operations/map_reduce.js +6 -6
  102. package/lib/operations/map_reduce.js.map +1 -1
  103. package/lib/operations/operation.js +4 -2
  104. package/lib/operations/operation.js.map +1 -1
  105. package/lib/operations/remove_user.js +1 -1
  106. package/lib/operations/remove_user.js.map +1 -1
  107. package/lib/operations/rename.js +2 -2
  108. package/lib/operations/rename.js.map +1 -1
  109. package/lib/operations/set_profiling_level.js +1 -1
  110. package/lib/operations/set_profiling_level.js.map +1 -1
  111. package/lib/operations/stats.js +2 -2
  112. package/lib/operations/stats.js.map +1 -1
  113. package/lib/operations/update.js +12 -12
  114. package/lib/operations/update.js.map +1 -1
  115. package/lib/read_preference.js +4 -1
  116. package/lib/read_preference.js.map +1 -1
  117. package/lib/sdam/monitor.js +14 -14
  118. package/lib/sdam/monitor.js.map +1 -1
  119. package/lib/sdam/server.js +19 -12
  120. package/lib/sdam/server.js.map +1 -1
  121. package/lib/sdam/server_description.js +3 -3
  122. package/lib/sdam/server_description.js.map +1 -1
  123. package/lib/sdam/server_selection.js +36 -1
  124. package/lib/sdam/server_selection.js.map +1 -1
  125. package/lib/sdam/srv_polling.js +9 -9
  126. package/lib/sdam/srv_polling.js.map +1 -1
  127. package/lib/sdam/topology.js +29 -21
  128. package/lib/sdam/topology.js.map +1 -1
  129. package/lib/sdam/topology_description.js +34 -12
  130. package/lib/sdam/topology_description.js.map +1 -1
  131. package/lib/sessions.js +19 -19
  132. package/lib/sessions.js.map +1 -1
  133. package/lib/transactions.js.map +1 -1
  134. package/lib/utils.js +53 -46
  135. package/lib/utils.js.map +1 -1
  136. package/mongodb.d.ts +60 -37
  137. package/mongodb.ts34.d.ts +61 -37
  138. package/package.json +26 -30
  139. package/src/bson.ts +1 -0
  140. package/src/bulk/common.ts +50 -37
  141. package/src/change_stream.ts +10 -8
  142. package/src/cmap/commands.ts +11 -8
  143. package/src/cmap/connection.ts +1 -0
  144. package/src/cmap/stream_description.ts +3 -3
  145. package/src/cmap/wire_protocol/constants.ts +4 -4
  146. package/src/collection.ts +40 -25
  147. package/src/connection_string.ts +75 -36
  148. package/src/cursor/abstract_cursor.ts +9 -11
  149. package/src/db.ts +5 -6
  150. package/src/error.ts +51 -23
  151. package/src/mongo_client.ts +12 -0
  152. package/src/mongo_types.ts +11 -14
  153. package/src/operations/aggregate.ts +1 -2
  154. package/src/operations/command.ts +5 -0
  155. package/src/operations/create_collection.ts +1 -1
  156. package/src/operations/execute_operation.ts +22 -2
  157. package/src/operations/get_more.ts +49 -0
  158. package/src/operations/list_collections.ts +12 -4
  159. package/src/operations/operation.ts +5 -1
  160. package/src/read_preference.ts +4 -1
  161. package/src/sdam/server.ts +8 -0
  162. package/src/sdam/server_selection.ts +43 -0
  163. package/src/sdam/srv_polling.ts +12 -11
  164. package/src/sdam/topology.ts +27 -10
  165. package/src/sdam/topology_description.ts +35 -11
  166. package/src/sessions.ts +2 -1
  167. package/src/transactions.ts +2 -2
  168. package/src/utils.ts +67 -56
@@ -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>(
package/src/error.ts CHANGED
@@ -8,6 +8,27 @@ export type AnyError = MongoError | Error;
8
8
  /** @internal */
9
9
  const kErrorLabels = Symbol('errorLabels');
10
10
 
11
+ /**
12
+ * @internal
13
+ * The legacy error message from the server that indicates the node is not a writable primary
14
+ * https://github.com/mongodb/specifications/blob/b07c26dc40d04ac20349f989db531c9845fdd755/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-writable-primary-and-node-is-recovering
15
+ */
16
+ export const LEGACY_NOT_WRITABLE_PRIMARY_ERROR_MESSAGE = 'not master';
17
+
18
+ /**
19
+ * @internal
20
+ * The legacy error message from the server that indicates the node is not a primary or secondary
21
+ * https://github.com/mongodb/specifications/blob/b07c26dc40d04ac20349f989db531c9845fdd755/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-writable-primary-and-node-is-recovering
22
+ */
23
+ export const LEGACY_NOT_PRIMARY_OR_SECONDARY_ERROR_MESSAGE = 'not master or secondary';
24
+
25
+ /**
26
+ * @internal
27
+ * The error message from the server that indicates the node is recovering
28
+ * https://github.com/mongodb/specifications/blob/b07c26dc40d04ac20349f989db531c9845fdd755/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-writable-primary-and-node-is-recovering
29
+ */
30
+ export const NODE_IS_RECOVERING_ERROR_MESSAGE = 'node is recovering';
31
+
11
32
  /** @internal MongoDB Error Codes */
12
33
  export const MONGODB_ERROR_CODES = Object.freeze({
13
34
  HostUnreachable: 6,
@@ -17,11 +38,11 @@ export const MONGODB_ERROR_CODES = Object.freeze({
17
38
  PrimarySteppedDown: 189,
18
39
  ExceededTimeLimit: 262,
19
40
  SocketException: 9001,
20
- NotMaster: 10107,
41
+ NotWritablePrimary: 10107,
21
42
  InterruptedAtShutdown: 11600,
22
43
  InterruptedDueToReplStateChange: 11602,
23
- NotMasterNoSlaveOk: 13435,
24
- NotMasterOrSecondary: 13436,
44
+ NotPrimaryNoSecondaryOk: 13435,
45
+ NotPrimaryOrSecondary: 13436,
25
46
  StaleShardVersion: 63,
26
47
  StaleEpoch: 150,
27
48
  StaleConfig: 13388,
@@ -46,11 +67,11 @@ export const GET_MORE_RESUMABLE_CODES = new Set<number>([
46
67
  MONGODB_ERROR_CODES.PrimarySteppedDown,
47
68
  MONGODB_ERROR_CODES.ExceededTimeLimit,
48
69
  MONGODB_ERROR_CODES.SocketException,
49
- MONGODB_ERROR_CODES.NotMaster,
70
+ MONGODB_ERROR_CODES.NotWritablePrimary,
50
71
  MONGODB_ERROR_CODES.InterruptedAtShutdown,
51
72
  MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
52
- MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
53
- MONGODB_ERROR_CODES.NotMasterOrSecondary,
73
+ MONGODB_ERROR_CODES.NotPrimaryNoSecondaryOk,
74
+ MONGODB_ERROR_CODES.NotPrimaryOrSecondary,
54
75
  MONGODB_ERROR_CODES.StaleShardVersion,
55
76
  MONGODB_ERROR_CODES.StaleEpoch,
56
77
  MONGODB_ERROR_CODES.StaleConfig,
@@ -675,19 +696,19 @@ const RETRYABLE_ERROR_CODES = new Set<number>([
675
696
  MONGODB_ERROR_CODES.ShutdownInProgress,
676
697
  MONGODB_ERROR_CODES.PrimarySteppedDown,
677
698
  MONGODB_ERROR_CODES.SocketException,
678
- MONGODB_ERROR_CODES.NotMaster,
699
+ MONGODB_ERROR_CODES.NotWritablePrimary,
679
700
  MONGODB_ERROR_CODES.InterruptedAtShutdown,
680
701
  MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
681
- MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
682
- MONGODB_ERROR_CODES.NotMasterOrSecondary
702
+ MONGODB_ERROR_CODES.NotPrimaryNoSecondaryOk,
703
+ MONGODB_ERROR_CODES.NotPrimaryOrSecondary
683
704
  ]);
684
705
 
685
706
  const RETRYABLE_WRITE_ERROR_CODES = new Set<number>([
686
707
  MONGODB_ERROR_CODES.InterruptedAtShutdown,
687
708
  MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
688
- MONGODB_ERROR_CODES.NotMaster,
689
- MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
690
- MONGODB_ERROR_CODES.NotMasterOrSecondary,
709
+ MONGODB_ERROR_CODES.NotWritablePrimary,
710
+ MONGODB_ERROR_CODES.NotPrimaryNoSecondaryOk,
711
+ MONGODB_ERROR_CODES.NotPrimaryOrSecondary,
691
712
  MONGODB_ERROR_CODES.PrimarySteppedDown,
692
713
  MONGODB_ERROR_CODES.ShutdownInProgress,
693
714
  MONGODB_ERROR_CODES.HostNotFound,
@@ -697,6 +718,10 @@ const RETRYABLE_WRITE_ERROR_CODES = new Set<number>([
697
718
  MONGODB_ERROR_CODES.ExceededTimeLimit
698
719
  ]);
699
720
 
721
+ export function isRetryableEndTransactionError(error: MongoError): boolean {
722
+ return error.hasErrorLabel('RetryableWriteError');
723
+ }
724
+
700
725
  export function isRetryableWriteError(error: MongoError): boolean {
701
726
  if (error instanceof MongoWriteConcernError) {
702
727
  return RETRYABLE_WRITE_ERROR_CODES.has(error.result?.code ?? error.code ?? 0);
@@ -710,8 +735,8 @@ export function isRetryableError(error: MongoError): boolean {
710
735
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
711
736
  (typeof error.code === 'number' && RETRYABLE_ERROR_CODES.has(error.code!)) ||
712
737
  error instanceof MongoNetworkError ||
713
- !!error.message.match(/not master/) ||
714
- !!error.message.match(/node is recovering/)
738
+ !!error.message.match(new RegExp(LEGACY_NOT_WRITABLE_PRIMARY_ERROR_MESSAGE)) ||
739
+ !!error.message.match(new RegExp(NODE_IS_RECOVERING_ERROR_MESSAGE))
715
740
  );
716
741
  }
717
742
 
@@ -720,12 +745,12 @@ const SDAM_RECOVERING_CODES = new Set<number>([
720
745
  MONGODB_ERROR_CODES.PrimarySteppedDown,
721
746
  MONGODB_ERROR_CODES.InterruptedAtShutdown,
722
747
  MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
723
- MONGODB_ERROR_CODES.NotMasterOrSecondary
748
+ MONGODB_ERROR_CODES.NotPrimaryOrSecondary
724
749
  ]);
725
750
 
726
- const SDAM_NOTMASTER_CODES = new Set<number>([
727
- MONGODB_ERROR_CODES.NotMaster,
728
- MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
751
+ const SDAM_NOTPRIMARY_CODES = new Set<number>([
752
+ MONGODB_ERROR_CODES.NotWritablePrimary,
753
+ MONGODB_ERROR_CODES.NotPrimaryNoSecondaryOk,
729
754
  MONGODB_ERROR_CODES.LegacyNotPrimary
730
755
  ]);
731
756
 
@@ -740,20 +765,23 @@ function isRecoveringError(err: MongoError) {
740
765
  return SDAM_RECOVERING_CODES.has(err.code);
741
766
  }
742
767
 
743
- return /not master or secondary/.test(err.message) || /node is recovering/.test(err.message);
768
+ return (
769
+ new RegExp(LEGACY_NOT_PRIMARY_OR_SECONDARY_ERROR_MESSAGE).test(err.message) ||
770
+ new RegExp(NODE_IS_RECOVERING_ERROR_MESSAGE).test(err.message)
771
+ );
744
772
  }
745
773
 
746
- function isNotMasterError(err: MongoError) {
774
+ function isNotWritablePrimaryError(err: MongoError) {
747
775
  if (typeof err.code === 'number') {
748
776
  // If any error code exists, we ignore the error.message
749
- return SDAM_NOTMASTER_CODES.has(err.code);
777
+ return SDAM_NOTPRIMARY_CODES.has(err.code);
750
778
  }
751
779
 
752
780
  if (isRecoveringError(err)) {
753
781
  return false;
754
782
  }
755
783
 
756
- return /not master/.test(err.message);
784
+ return new RegExp(LEGACY_NOT_WRITABLE_PRIMARY_ERROR_MESSAGE).test(err.message);
757
785
  }
758
786
 
759
787
  export function isNodeShuttingDownError(err: MongoError): boolean {
@@ -774,7 +802,7 @@ export function isSDAMUnrecoverableError(error: MongoError): boolean {
774
802
  return true;
775
803
  }
776
804
 
777
- return isRecoveringError(error) || isNotMasterError(error);
805
+ return isRecoveringError(error) || isNotWritablePrimaryError(error);
778
806
  }
779
807
 
780
808
  export function isNetworkTimeoutError(err: MongoError): err is MongoNetworkError {
@@ -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]);