mongodb 4.0.0 → 4.1.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 (246) hide show
  1. package/README.md +62 -30
  2. package/lib/bson.js +1 -0
  3. package/lib/bson.js.map +1 -1
  4. package/lib/bulk/common.js +53 -30
  5. package/lib/bulk/common.js.map +1 -1
  6. package/lib/bulk/ordered.js +3 -2
  7. package/lib/bulk/ordered.js.map +1 -1
  8. package/lib/bulk/unordered.js +3 -2
  9. package/lib/bulk/unordered.js.map +1 -1
  10. package/lib/change_stream.js +23 -13
  11. package/lib/change_stream.js.map +1 -1
  12. package/lib/cmap/auth/auth_provider.js +2 -1
  13. package/lib/cmap/auth/auth_provider.js.map +1 -1
  14. package/lib/cmap/auth/gssapi.js +5 -4
  15. package/lib/cmap/auth/gssapi.js.map +1 -1
  16. package/lib/cmap/auth/mongo_credentials.js +9 -5
  17. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  18. package/lib/cmap/auth/mongocr.js +2 -2
  19. package/lib/cmap/auth/mongocr.js.map +1 -1
  20. package/lib/cmap/auth/mongodb_aws.js +32 -32
  21. package/lib/cmap/auth/mongodb_aws.js.map +1 -1
  22. package/lib/cmap/auth/plain.js +1 -1
  23. package/lib/cmap/auth/plain.js.map +1 -1
  24. package/lib/cmap/auth/scram.js +15 -12
  25. package/lib/cmap/auth/scram.js.map +1 -1
  26. package/lib/cmap/auth/x509.js +2 -2
  27. package/lib/cmap/auth/x509.js.map +1 -1
  28. package/lib/cmap/command_monitoring_events.js +26 -10
  29. package/lib/cmap/command_monitoring_events.js.map +1 -1
  30. package/lib/cmap/commands.js +9 -5
  31. package/lib/cmap/commands.js.map +1 -1
  32. package/lib/cmap/connect.js +23 -9
  33. package/lib/cmap/connect.js.map +1 -1
  34. package/lib/cmap/connection.js +43 -46
  35. package/lib/cmap/connection.js.map +1 -1
  36. package/lib/cmap/connection_pool.js +113 -15
  37. package/lib/cmap/connection_pool.js.map +1 -1
  38. package/lib/cmap/connection_pool_events.js +3 -1
  39. package/lib/cmap/connection_pool_events.js.map +1 -1
  40. package/lib/cmap/errors.js +3 -3
  41. package/lib/cmap/errors.js.map +1 -1
  42. package/lib/cmap/message_stream.js +1 -1
  43. package/lib/cmap/message_stream.js.map +1 -1
  44. package/lib/cmap/metrics.js +62 -0
  45. package/lib/cmap/metrics.js.map +1 -0
  46. package/lib/cmap/stream_description.js +3 -1
  47. package/lib/cmap/stream_description.js.map +1 -1
  48. package/lib/cmap/wire_protocol/compression.js +22 -9
  49. package/lib/cmap/wire_protocol/compression.js.map +1 -1
  50. package/lib/cmap/wire_protocol/shared.js +1 -1
  51. package/lib/cmap/wire_protocol/shared.js.map +1 -1
  52. package/lib/collection.js +23 -18
  53. package/lib/collection.js.map +1 -1
  54. package/lib/connection_string.js +76 -30
  55. package/lib/connection_string.js.map +1 -1
  56. package/lib/cursor/abstract_cursor.js +75 -68
  57. package/lib/cursor/abstract_cursor.js.map +1 -1
  58. package/lib/cursor/aggregation_cursor.js +47 -9
  59. package/lib/cursor/aggregation_cursor.js.map +1 -1
  60. package/lib/cursor/find_cursor.js +53 -13
  61. package/lib/cursor/find_cursor.js.map +1 -1
  62. package/lib/db.js +21 -14
  63. package/lib/db.js.map +1 -1
  64. package/lib/deps.js +16 -5
  65. package/lib/deps.js.map +1 -1
  66. package/lib/encrypter.js +5 -8
  67. package/lib/encrypter.js.map +1 -1
  68. package/lib/error.js +230 -34
  69. package/lib/error.js.map +1 -1
  70. package/lib/explain.js +2 -2
  71. package/lib/explain.js.map +1 -1
  72. package/lib/gridfs/download.js +22 -47
  73. package/lib/gridfs/download.js.map +1 -1
  74. package/lib/gridfs/index.js +4 -3
  75. package/lib/gridfs/index.js.map +1 -1
  76. package/lib/gridfs/upload.js +13 -21
  77. package/lib/gridfs/upload.js.map +1 -1
  78. package/lib/index.js +27 -2
  79. package/lib/index.js.map +1 -1
  80. package/lib/logger.js +3 -2
  81. package/lib/logger.js.map +1 -1
  82. package/lib/mongo_client.js +5 -8
  83. package/lib/mongo_client.js.map +1 -1
  84. package/lib/mongo_types.js.map +1 -1
  85. package/lib/operations/add_user.js +2 -3
  86. package/lib/operations/add_user.js.map +1 -1
  87. package/lib/operations/aggregate.js +12 -9
  88. package/lib/operations/aggregate.js.map +1 -1
  89. package/lib/operations/command.js +5 -7
  90. package/lib/operations/command.js.map +1 -1
  91. package/lib/operations/common_functions.js +1 -1
  92. package/lib/operations/common_functions.js.map +1 -1
  93. package/lib/operations/connect.js +3 -2
  94. package/lib/operations/connect.js.map +1 -1
  95. package/lib/operations/count.js +1 -1
  96. package/lib/operations/count.js.map +1 -1
  97. package/lib/operations/count_documents.js +1 -1
  98. package/lib/operations/count_documents.js.map +1 -1
  99. package/lib/operations/delete.js +5 -5
  100. package/lib/operations/delete.js.map +1 -1
  101. package/lib/operations/distinct.js +2 -2
  102. package/lib/operations/distinct.js.map +1 -1
  103. package/lib/operations/estimated_document_count.js +5 -1
  104. package/lib/operations/estimated_document_count.js.map +1 -1
  105. package/lib/operations/eval.js.map +1 -1
  106. package/lib/operations/execute_operation.js +31 -17
  107. package/lib/operations/execute_operation.js.map +1 -1
  108. package/lib/operations/find.js +13 -9
  109. package/lib/operations/find.js.map +1 -1
  110. package/lib/operations/find_and_modify.js +9 -9
  111. package/lib/operations/find_and_modify.js.map +1 -1
  112. package/lib/operations/indexes.js +8 -3
  113. package/lib/operations/indexes.js.map +1 -1
  114. package/lib/operations/insert.js +5 -3
  115. package/lib/operations/insert.js.map +1 -1
  116. package/lib/operations/is_capped.js +2 -1
  117. package/lib/operations/is_capped.js.map +1 -1
  118. package/lib/operations/list_collections.js +6 -3
  119. package/lib/operations/list_collections.js.map +1 -1
  120. package/lib/operations/map_reduce.js +1 -1
  121. package/lib/operations/map_reduce.js.map +1 -1
  122. package/lib/operations/operation.js +3 -1
  123. package/lib/operations/operation.js.map +1 -1
  124. package/lib/operations/options_operation.js +2 -1
  125. package/lib/operations/options_operation.js.map +1 -1
  126. package/lib/operations/profiling_level.js +4 -2
  127. package/lib/operations/profiling_level.js.map +1 -1
  128. package/lib/operations/set_profiling_level.js +4 -2
  129. package/lib/operations/set_profiling_level.js.map +1 -1
  130. package/lib/operations/update.js +12 -12
  131. package/lib/operations/update.js.map +1 -1
  132. package/lib/operations/validate_collection.js +6 -5
  133. package/lib/operations/validate_collection.js.map +1 -1
  134. package/lib/promise_provider.js +1 -1
  135. package/lib/promise_provider.js.map +1 -1
  136. package/lib/read_preference.js +8 -8
  137. package/lib/read_preference.js.map +1 -1
  138. package/lib/sdam/common.js +12 -10
  139. package/lib/sdam/common.js.map +1 -1
  140. package/lib/sdam/server.js +90 -25
  141. package/lib/sdam/server.js.map +1 -1
  142. package/lib/sdam/server_description.js +9 -4
  143. package/lib/sdam/server_description.js.map +1 -1
  144. package/lib/sdam/server_selection.js +10 -4
  145. package/lib/sdam/server_selection.js.map +1 -1
  146. package/lib/sdam/srv_polling.js +1 -1
  147. package/lib/sdam/srv_polling.js.map +1 -1
  148. package/lib/sdam/topology.js +42 -21
  149. package/lib/sdam/topology.js.map +1 -1
  150. package/lib/sdam/topology_description.js +7 -3
  151. package/lib/sdam/topology_description.js.map +1 -1
  152. package/lib/sessions.js +132 -31
  153. package/lib/sessions.js.map +1 -1
  154. package/lib/sort.js +3 -3
  155. package/lib/sort.js.map +1 -1
  156. package/lib/transactions.js +15 -7
  157. package/lib/transactions.js.map +1 -1
  158. package/lib/utils.js +60 -20
  159. package/lib/utils.js.map +1 -1
  160. package/mongodb.d.ts +523 -138
  161. package/mongodb.ts34.d.ts +480 -141
  162. package/package.json +44 -48
  163. package/src/bson.ts +1 -0
  164. package/src/bulk/common.ts +83 -43
  165. package/src/bulk/ordered.ts +4 -3
  166. package/src/bulk/unordered.ts +4 -3
  167. package/src/change_stream.ts +46 -29
  168. package/src/cmap/auth/auth_provider.ts +3 -2
  169. package/src/cmap/auth/gssapi.ts +15 -5
  170. package/src/cmap/auth/mongo_credentials.ts +22 -8
  171. package/src/cmap/auth/mongocr.ts +3 -3
  172. package/src/cmap/auth/mongodb_aws.ts +52 -39
  173. package/src/cmap/auth/plain.ts +2 -2
  174. package/src/cmap/auth/scram.ts +23 -13
  175. package/src/cmap/auth/x509.ts +3 -3
  176. package/src/cmap/command_monitoring_events.ts +36 -14
  177. package/src/cmap/commands.ts +12 -6
  178. package/src/cmap/connect.ts +42 -12
  179. package/src/cmap/connection.ts +54 -62
  180. package/src/cmap/connection_pool.ts +141 -20
  181. package/src/cmap/connection_pool_events.ts +8 -1
  182. package/src/cmap/errors.ts +3 -4
  183. package/src/cmap/message_stream.ts +2 -4
  184. package/src/cmap/metrics.ts +58 -0
  185. package/src/cmap/stream_description.ts +6 -1
  186. package/src/cmap/wire_protocol/compression.ts +26 -13
  187. package/src/cmap/wire_protocol/shared.ts +4 -2
  188. package/src/collection.ts +75 -70
  189. package/src/connection_string.ts +97 -34
  190. package/src/cursor/abstract_cursor.ts +141 -104
  191. package/src/cursor/aggregation_cursor.ts +34 -20
  192. package/src/cursor/find_cursor.ts +41 -21
  193. package/src/db.ts +19 -18
  194. package/src/deps.ts +110 -22
  195. package/src/encrypter.ts +6 -12
  196. package/src/error.ts +264 -48
  197. package/src/explain.ts +3 -3
  198. package/src/gridfs/download.ts +48 -53
  199. package/src/gridfs/index.ts +5 -4
  200. package/src/gridfs/upload.ts +32 -33
  201. package/src/index.ts +42 -4
  202. package/src/logger.ts +6 -3
  203. package/src/mongo_client.ts +20 -23
  204. package/src/mongo_types.ts +19 -20
  205. package/src/operations/add_user.ts +4 -5
  206. package/src/operations/aggregate.ts +18 -17
  207. package/src/operations/command.ts +7 -10
  208. package/src/operations/common_functions.ts +2 -3
  209. package/src/operations/connect.ts +4 -3
  210. package/src/operations/count.ts +2 -2
  211. package/src/operations/count_documents.ts +2 -2
  212. package/src/operations/delete.ts +8 -6
  213. package/src/operations/distinct.ts +5 -3
  214. package/src/operations/estimated_document_count.ts +5 -1
  215. package/src/operations/eval.ts +1 -1
  216. package/src/operations/execute_operation.ts +41 -20
  217. package/src/operations/find.ts +25 -16
  218. package/src/operations/find_and_modify.ts +12 -10
  219. package/src/operations/indexes.ts +39 -8
  220. package/src/operations/insert.ts +7 -4
  221. package/src/operations/is_capped.ts +3 -2
  222. package/src/operations/list_collections.ts +9 -6
  223. package/src/operations/map_reduce.ts +4 -2
  224. package/src/operations/operation.ts +7 -2
  225. package/src/operations/options_operation.ts +3 -2
  226. package/src/operations/profiling_level.ts +5 -3
  227. package/src/operations/set_profiling_level.ts +9 -3
  228. package/src/operations/update.ts +17 -13
  229. package/src/operations/validate_collection.ts +7 -6
  230. package/src/promise_provider.ts +2 -2
  231. package/src/read_preference.ts +11 -9
  232. package/src/sdam/common.ts +11 -9
  233. package/src/sdam/server.ts +168 -69
  234. package/src/sdam/server_description.ts +16 -4
  235. package/src/sdam/server_selection.ts +15 -7
  236. package/src/sdam/srv_polling.ts +2 -2
  237. package/src/sdam/topology.ts +67 -36
  238. package/src/sdam/topology_description.ts +11 -4
  239. package/src/sessions.ts +194 -37
  240. package/src/sort.ts +6 -4
  241. package/src/transactions.ts +18 -9
  242. package/src/utils.ts +73 -20
  243. package/HISTORY.md +0 -2993
  244. package/lib/operations/find_one.js +0 -34
  245. package/lib/operations/find_one.js.map +0 -1
  246. package/src/operations/find_one.ts +0 -43
@@ -1,6 +1,12 @@
1
1
  import * as crypto from 'crypto';
2
2
  import { Binary, Document } from '../../bson';
3
- import { AnyError, MongoDriverError, MongoServerError } from '../../error';
3
+ import {
4
+ AnyError,
5
+ MongoRuntimeError,
6
+ MongoServerError,
7
+ MongoInvalidArgumentError,
8
+ MongoMissingCredentialsError
9
+ } from '../../error';
4
10
  import { AuthProvider, AuthContext } from './auth_provider';
5
11
  import { Callback, ns, emitWarning } from '../../utils';
6
12
  import type { MongoCredentials } from './mongo_credentials';
@@ -22,7 +28,7 @@ class ScramSHA extends AuthProvider {
22
28
  const cryptoMethod = this.cryptoMethod;
23
29
  const credentials = authContext.credentials;
24
30
  if (!credentials) {
25
- return callback(new MongoDriverError('AuthContext must provide credentials.'));
31
+ return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
26
32
  }
27
33
  if (cryptoMethod === 'sha256' && saslprep == null) {
28
34
  emitWarning('Warning: no saslprep library specified. Passwords will not be sanitized');
@@ -103,10 +109,12 @@ function makeFirstMessage(
103
109
  function executeScram(cryptoMethod: CryptoMethod, authContext: AuthContext, callback: Callback) {
104
110
  const { connection, credentials } = authContext;
105
111
  if (!credentials) {
106
- return callback(new MongoDriverError('AuthContext must provide credentials.'));
112
+ return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
107
113
  }
108
114
  if (!authContext.nonce) {
109
- return callback(new MongoDriverError('AuthContext must contain a valid nonce property'));
115
+ return callback(
116
+ new MongoInvalidArgumentError('AuthContext must contain a valid nonce property')
117
+ );
110
118
  }
111
119
  const nonce = authContext.nonce;
112
120
  const db = credentials.source;
@@ -131,10 +139,10 @@ function continueScramConversation(
131
139
  const connection = authContext.connection;
132
140
  const credentials = authContext.credentials;
133
141
  if (!credentials) {
134
- return callback(new MongoDriverError('AuthContext must provide credentials.'));
142
+ return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
135
143
  }
136
144
  if (!authContext.nonce) {
137
- return callback(new MongoDriverError('Unable to continue SCRAM without valid nonce'));
145
+ return callback(new MongoInvalidArgumentError('Unable to continue SCRAM without valid nonce'));
138
146
  }
139
147
  const nonce = authContext.nonce;
140
148
 
@@ -161,7 +169,8 @@ function continueScramConversation(
161
169
  const iterations = parseInt(dict.i, 10);
162
170
  if (iterations && iterations < 4096) {
163
171
  callback(
164
- new MongoDriverError(`Server returned an invalid iteration count ${iterations}`),
172
+ // TODO(NODE-3483)
173
+ new MongoRuntimeError(`Server returned an invalid iteration count ${iterations}`),
165
174
  false
166
175
  );
167
176
  return;
@@ -170,7 +179,8 @@ function continueScramConversation(
170
179
  const salt = dict.s;
171
180
  const rnonce = dict.r;
172
181
  if (rnonce.startsWith('nonce')) {
173
- callback(new MongoDriverError(`Server returned an invalid nonce: ${rnonce}`), false);
182
+ // TODO(NODE-3483)
183
+ callback(new MongoRuntimeError(`Server returned an invalid nonce: ${rnonce}`), false);
174
184
  return;
175
185
  }
176
186
 
@@ -209,7 +219,7 @@ function continueScramConversation(
209
219
 
210
220
  const parsedResponse = parsePayload(r.payload.value());
211
221
  if (!compareDigest(Buffer.from(parsedResponse.v, 'base64'), serverSignature)) {
212
- callback(new MongoDriverError('Server returned an invalid signature'));
222
+ callback(new MongoRuntimeError('Server returned an invalid signature'));
213
223
  return;
214
224
  }
215
225
 
@@ -240,15 +250,15 @@ function parsePayload(payload: string) {
240
250
 
241
251
  function passwordDigest(username: string, password: string) {
242
252
  if (typeof username !== 'string') {
243
- throw new MongoDriverError('username must be a string');
253
+ throw new MongoInvalidArgumentError('Username must be a string');
244
254
  }
245
255
 
246
256
  if (typeof password !== 'string') {
247
- throw new MongoDriverError('password must be a string');
257
+ throw new MongoInvalidArgumentError('Password must be a string');
248
258
  }
249
259
 
250
260
  if (password.length === 0) {
251
- throw new MongoDriverError('password cannot be empty');
261
+ throw new MongoInvalidArgumentError('Password cannot be empty');
252
262
  }
253
263
 
254
264
  const md5 = crypto.createHash('md5');
@@ -303,7 +313,7 @@ const hiLengthMap = {
303
313
  function HI(data: string, salt: Buffer, iterations: number, cryptoMethod: CryptoMethod) {
304
314
  // omit the work if already generated
305
315
  const key = [data, salt.toString('base64'), iterations].join('_');
306
- if (_hiCache[key] !== undefined) {
316
+ if (_hiCache[key] != null) {
307
317
  return _hiCache[key];
308
318
  }
309
319
 
@@ -1,5 +1,5 @@
1
1
  import { AuthProvider, AuthContext } from './auth_provider';
2
- import { MongoDriverError } from '../../error';
2
+ import { MongoMissingCredentialsError } from '../../error';
3
3
  import type { Document } from '../../bson';
4
4
  import { Callback, ns } from '../../utils';
5
5
  import type { MongoCredentials } from './mongo_credentials';
@@ -9,7 +9,7 @@ export class X509 extends AuthProvider {
9
9
  prepare(handshakeDoc: HandshakeDocument, authContext: AuthContext, callback: Callback): void {
10
10
  const { credentials } = authContext;
11
11
  if (!credentials) {
12
- return callback(new MongoDriverError('AuthContext must provide credentials.'));
12
+ return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
13
13
  }
14
14
  Object.assign(handshakeDoc, {
15
15
  speculativeAuthenticate: x509AuthenticateCommand(credentials)
@@ -22,7 +22,7 @@ export class X509 extends AuthProvider {
22
22
  const connection = authContext.connection;
23
23
  const credentials = authContext.credentials;
24
24
  if (!credentials) {
25
- return callback(new MongoDriverError('AuthContext must provide credentials.'));
25
+ return callback(new MongoMissingCredentialsError('AuthContext must provide credentials.'));
26
26
  }
27
27
  const response = authContext.response;
28
28
 
@@ -1,8 +1,7 @@
1
1
  import { GetMore, KillCursor, Msg, WriteProtocolMessageType } from './commands';
2
2
  import { calculateDurationInMs, deepCopy } from '../utils';
3
- import type { ConnectionPool } from './connection_pool';
4
3
  import type { Connection } from './connection';
5
- import type { Document } from '../bson';
4
+ import type { Document, ObjectId } from '../bson';
6
5
 
7
6
  /**
8
7
  * An event indicating the start of a given
@@ -17,6 +16,7 @@ export class CommandStartedEvent {
17
16
  command: Document;
18
17
  address: string;
19
18
  connectionId?: string | number;
19
+ serviceId?: ObjectId;
20
20
 
21
21
  /**
22
22
  * Create a started event
@@ -25,10 +25,10 @@ export class CommandStartedEvent {
25
25
  * @param pool - the pool that originated the command
26
26
  * @param command - the command
27
27
  */
28
- constructor(pool: Connection | ConnectionPool, command: WriteProtocolMessageType) {
28
+ constructor(connection: Connection, command: WriteProtocolMessageType) {
29
29
  const cmd = extractCommand(command);
30
30
  const commandName = extractCommandName(cmd);
31
- const { address, connectionId } = extractConnectionDetails(pool);
31
+ const { address, connectionId, serviceId } = extractConnectionDetails(connection);
32
32
 
33
33
  // TODO: remove in major revision, this is not spec behavior
34
34
  if (SENSITIVE_COMMANDS.has(commandName)) {
@@ -38,11 +38,17 @@ export class CommandStartedEvent {
38
38
 
39
39
  this.address = address;
40
40
  this.connectionId = connectionId;
41
+ this.serviceId = serviceId;
41
42
  this.requestId = command.requestId;
42
43
  this.databaseName = databaseName(command);
43
44
  this.commandName = commandName;
44
45
  this.command = maybeRedact(commandName, cmd, cmd);
45
46
  }
47
+
48
+ /* @internal */
49
+ get hasServiceId(): boolean {
50
+ return !!this.serviceId;
51
+ }
46
52
  }
47
53
 
48
54
  /**
@@ -57,6 +63,7 @@ export class CommandSucceededEvent {
57
63
  duration: number;
58
64
  commandName: string;
59
65
  reply: unknown;
66
+ serviceId?: ObjectId;
60
67
 
61
68
  /**
62
69
  * Create a succeeded event
@@ -68,22 +75,28 @@ export class CommandSucceededEvent {
68
75
  * @param started - a high resolution tuple timestamp of when the command was first sent, to calculate duration
69
76
  */
70
77
  constructor(
71
- pool: Connection | ConnectionPool,
78
+ connection: Connection,
72
79
  command: WriteProtocolMessageType,
73
80
  reply: Document | undefined,
74
81
  started: number
75
82
  ) {
76
83
  const cmd = extractCommand(command);
77
84
  const commandName = extractCommandName(cmd);
78
- const { address, connectionId } = extractConnectionDetails(pool);
85
+ const { address, connectionId, serviceId } = extractConnectionDetails(connection);
79
86
 
80
87
  this.address = address;
81
88
  this.connectionId = connectionId;
89
+ this.serviceId = serviceId;
82
90
  this.requestId = command.requestId;
83
91
  this.commandName = commandName;
84
92
  this.duration = calculateDurationInMs(started);
85
93
  this.reply = maybeRedact(commandName, cmd, extractReply(command, reply));
86
94
  }
95
+
96
+ /* @internal */
97
+ get hasServiceId(): boolean {
98
+ return !!this.serviceId;
99
+ }
87
100
  }
88
101
 
89
102
  /**
@@ -98,6 +111,8 @@ export class CommandFailedEvent {
98
111
  duration: number;
99
112
  commandName: string;
100
113
  failure: Error;
114
+ serviceId?: ObjectId;
115
+
101
116
  /**
102
117
  * Create a failure event
103
118
  *
@@ -108,23 +123,29 @@ export class CommandFailedEvent {
108
123
  * @param started - a high resolution tuple timestamp of when the command was first sent, to calculate duration
109
124
  */
110
125
  constructor(
111
- pool: Connection | ConnectionPool,
126
+ connection: Connection,
112
127
  command: WriteProtocolMessageType,
113
128
  error: Error | Document,
114
129
  started: number
115
130
  ) {
116
131
  const cmd = extractCommand(command);
117
132
  const commandName = extractCommandName(cmd);
118
- const { address, connectionId } = extractConnectionDetails(pool);
133
+ const { address, connectionId, serviceId } = extractConnectionDetails(connection);
119
134
 
120
135
  this.address = address;
121
136
  this.connectionId = connectionId;
137
+ this.serviceId = serviceId;
122
138
 
123
139
  this.requestId = command.requestId;
124
140
  this.commandName = commandName;
125
141
  this.duration = calculateDurationInMs(started);
126
142
  this.failure = maybeRedact(commandName, cmd, error) as Error;
127
143
  }
144
+
145
+ /* @internal */
146
+ get hasServiceId(): boolean {
147
+ return !!this.serviceId;
148
+ }
128
149
  }
129
150
 
130
151
  /** Commands that we want to redact because of the sensitive nature of their contents */
@@ -212,7 +233,7 @@ function extractCommand(command: WriteProtocolMessageType): Document {
212
233
  // up-convert legacy find command
213
234
  result = { find: collectionName(command) };
214
235
  Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
215
- if (typeof command.query[key] !== 'undefined') {
236
+ if (command.query[key] != null) {
216
237
  result[LEGACY_FIND_QUERY_MAP[key]] = deepCopy(command.query[key]);
217
238
  }
218
239
  });
@@ -220,7 +241,7 @@ function extractCommand(command: WriteProtocolMessageType): Document {
220
241
 
221
242
  Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
222
243
  const legacyKey = key as keyof typeof LEGACY_FIND_OPTIONS_MAP;
223
- if (typeof command[legacyKey] !== 'undefined') {
244
+ if (command[legacyKey] != null) {
224
245
  result[LEGACY_FIND_OPTIONS_MAP[legacyKey]] = deepCopy(command[legacyKey]);
225
246
  }
226
247
  });
@@ -232,7 +253,7 @@ function extractCommand(command: WriteProtocolMessageType): Document {
232
253
  }
233
254
  });
234
255
 
235
- if (typeof command.pre32Limit !== 'undefined') {
256
+ if (command.pre32Limit != null) {
236
257
  result.limit = command.pre32Limit;
237
258
  }
238
259
 
@@ -253,7 +274,7 @@ function extractCommand(command: WriteProtocolMessageType): Document {
253
274
 
254
275
  for (const k in command) {
255
276
  if (k === 'query') continue;
256
- clonedCommand[k] = deepCopy(((command as unknown) as Record<string, unknown>)[k]);
277
+ clonedCommand[k] = deepCopy((command as unknown as Record<string, unknown>)[k]);
257
278
  }
258
279
  return command.query ? clonedQuery : clonedCommand;
259
280
  }
@@ -286,7 +307,7 @@ function extractReply(command: WriteProtocolMessageType, reply?: Document) {
286
307
  }
287
308
 
288
309
  // is this a legacy find command?
289
- if (command.query && typeof command.query.$query !== 'undefined') {
310
+ if (command.query && command.query.$query != null) {
290
311
  return {
291
312
  ok: 1,
292
313
  cursor: {
@@ -300,13 +321,14 @@ function extractReply(command: WriteProtocolMessageType, reply?: Document) {
300
321
  return deepCopy(reply.result ? reply.result : reply);
301
322
  }
302
323
 
303
- function extractConnectionDetails(connection: Connection | ConnectionPool) {
324
+ function extractConnectionDetails(connection: Connection) {
304
325
  let connectionId;
305
326
  if ('id' in connection) {
306
327
  connectionId = connection.id;
307
328
  }
308
329
  return {
309
330
  address: connection.address,
331
+ serviceId: connection.serviceId,
310
332
  connectionId
311
333
  };
312
334
  }
@@ -5,7 +5,7 @@ import { OP_QUERY, OP_GETMORE, OP_KILL_CURSORS, OP_MSG } from './wire_protocol/c
5
5
  import type { Long, Document, BSONSerializeOptions } from '../bson';
6
6
  import type { ClientSession } from '../sessions';
7
7
  import type { CommandOptions } from './connection';
8
- import { MongoDriverError } from '../error';
8
+ import { MongoRuntimeError, MongoInvalidArgumentError } from '../error';
9
9
 
10
10
  // Incrementing request id
11
11
  let _requestId = 0;
@@ -77,12 +77,15 @@ export class Query {
77
77
 
78
78
  constructor(ns: string, query: Document, options: OpQueryOptions) {
79
79
  // Basic options needed to be passed in
80
- if (ns == null) throw new MongoDriverError('ns must be specified for query');
81
- if (query == null) throw new MongoDriverError('query must be specified for query');
80
+ // TODO(NODE-3483): Replace with MongoCommandError
81
+ if (ns == null) throw new MongoRuntimeError('Namespace must be specified for query');
82
+ // TODO(NODE-3483): Replace with MongoCommandError
83
+ if (query == null) throw new MongoRuntimeError('A query document must be specified for query');
82
84
 
83
85
  // Validate that we are not passing 0x00 in the collection name
84
86
  if (ns.indexOf('\x00') !== -1) {
85
- throw new MongoDriverError('namespace cannot contain a null character');
87
+ // TODO(NODE-3483): Use MongoNamespace static method
88
+ throw new MongoRuntimeError('Namespace cannot contain a null character');
86
89
  }
87
90
 
88
91
  // Basic options
@@ -664,7 +667,8 @@ export class Msg {
664
667
 
665
668
  constructor(ns: string, command: Document, options: OpQueryOptions) {
666
669
  // Basic options needed to be passed in
667
- if (command == null) throw new MongoDriverError('query must be specified for query');
670
+ if (command == null)
671
+ throw new MongoInvalidArgumentError('Query document must be specified for query');
668
672
 
669
673
  // Basic options
670
674
  this.ns = ns;
@@ -852,7 +856,9 @@ export class BinMsg {
852
856
  this.index += bsonSize;
853
857
  } else if (payloadType === 1) {
854
858
  // It was decided that no driver makes use of payload type 1
855
- throw new MongoDriverError('OP_MSG Payload Type 1 detected unsupported protocol');
859
+
860
+ // TODO(NODE-3483): Replace with MongoDeprecationError
861
+ throw new MongoRuntimeError('OP_MSG Payload Type 1 detected unsupported protocol');
856
862
  }
857
863
  }
858
864
 
@@ -5,8 +5,10 @@ import {
5
5
  MongoNetworkError,
6
6
  MongoNetworkTimeoutError,
7
7
  AnyError,
8
- MongoDriverError,
9
- MongoServerError
8
+ MongoCompatibilityError,
9
+ MongoInvalidArgumentError,
10
+ MongoServerError,
11
+ MongoRuntimeError
10
12
  } from '../error';
11
13
  import { AUTH_PROVIDERS, AuthMechanism } from './auth/defaultAuthProviders';
12
14
  import { AuthContext } from './auth/auth_provider';
@@ -18,10 +20,14 @@ import {
18
20
  MIN_SUPPORTED_SERVER_VERSION
19
21
  } from './wire_protocol/constants';
20
22
  import type { Document } from '../bson';
23
+ import { Int32 } from '../bson';
21
24
 
22
25
  import type { Socket, SocketConnectOpts } from 'net';
23
26
  import type { TLSSocket, ConnectionOptions as TLSConnectionOpts } from 'tls';
24
- import { Int32 } from '../bson';
27
+
28
+ const FAKE_MONGODB_SERVICE_ID =
29
+ typeof process.env.FAKE_MONGODB_SERVICE_ID === 'string' &&
30
+ process.env.FAKE_MONGODB_SERVICE_ID.toLowerCase() === 'true';
25
31
 
26
32
  /** @public */
27
33
  export type Stream = Socket | TLSSocket;
@@ -58,13 +64,13 @@ function checkSupportedServer(ismaster: Document, options: ConnectionOptions) {
58
64
  const message = `Server at ${options.hostAddress} reports minimum wire version ${JSON.stringify(
59
65
  ismaster.minWireVersion
60
66
  )}, but this version of the Node.js Driver requires at most ${MAX_SUPPORTED_WIRE_VERSION} (MongoDB ${MAX_SUPPORTED_SERVER_VERSION})`;
61
- return new MongoDriverError(message);
67
+ return new MongoCompatibilityError(message);
62
68
  }
63
69
 
64
70
  const message = `Server at ${options.hostAddress} reports maximum wire version ${
65
71
  JSON.stringify(ismaster.maxWireVersion) ?? 0
66
72
  }, but this version of the Node.js Driver requires at least ${MIN_SUPPORTED_WIRE_VERSION} (MongoDB ${MIN_SUPPORTED_SERVER_VERSION})`;
67
- return new MongoDriverError(message);
73
+ return new MongoCompatibilityError(message);
68
74
  }
69
75
 
70
76
  function performInitialHandshake(
@@ -85,7 +91,9 @@ function performInitialHandshake(
85
91
  !(credentials.mechanism === AuthMechanism.MONGODB_DEFAULT) &&
86
92
  !AUTH_PROVIDERS.get(credentials.mechanism)
87
93
  ) {
88
- callback(new MongoDriverError(`authMechanism '${credentials.mechanism}' not supported`));
94
+ callback(
95
+ new MongoInvalidArgumentError(`AuthMechanism '${credentials.mechanism}' not supported`)
96
+ );
89
97
  return;
90
98
  }
91
99
  }
@@ -129,6 +137,21 @@ function performInitialHandshake(
129
137
  return;
130
138
  }
131
139
 
140
+ if (options.loadBalanced) {
141
+ // TODO: Durran: Remove when server support exists. (NODE-3431)
142
+ if (FAKE_MONGODB_SERVICE_ID) {
143
+ response.serviceId = response.topologyVersion.processId;
144
+ }
145
+ if (!response.serviceId) {
146
+ return callback(
147
+ new MongoCompatibilityError(
148
+ 'Driver attempted to initialize in load balancing mode, ' +
149
+ 'but the server does not support this mode.'
150
+ )
151
+ );
152
+ }
153
+ }
154
+
132
155
  // NOTE: This is metadata attached to the connection while porting away from
133
156
  // handshake being done in the `Server` class. Likely, it should be
134
157
  // relocated, or at very least restructured.
@@ -143,7 +166,9 @@ function performInitialHandshake(
143
166
  const provider = AUTH_PROVIDERS.get(resolvedCredentials.mechanism);
144
167
  if (!provider) {
145
168
  return callback(
146
- new MongoDriverError(`No AuthProvider for ${resolvedCredentials.mechanism} defined.`)
169
+ new MongoInvalidArgumentError(
170
+ `No AuthProvider for ${resolvedCredentials.mechanism} defined.`
171
+ )
147
172
  );
148
173
  }
149
174
  provider.auth(authContext, err => {
@@ -166,6 +191,7 @@ export interface HandshakeDocument extends Document {
166
191
  client: ClientMetadata;
167
192
  compression: string[];
168
193
  saslSupportedMechs?: string;
194
+ loadBalanced: boolean;
169
195
  }
170
196
 
171
197
  function prepareHandshakeDocument(authContext: AuthContext, callback: Callback<HandshakeDocument>) {
@@ -177,7 +203,8 @@ function prepareHandshakeDocument(authContext: AuthContext, callback: Callback<H
177
203
  [serverApi?.version ? 'hello' : 'ismaster']: true,
178
204
  helloOk: true,
179
205
  client: options.metadata || makeClientMetadata(options),
180
- compression: compressors
206
+ compression: compressors,
207
+ loadBalanced: options.loadBalanced
181
208
  };
182
209
 
183
210
  const credentials = authContext.credentials;
@@ -189,7 +216,9 @@ function prepareHandshakeDocument(authContext: AuthContext, callback: Callback<H
189
216
  if (!provider) {
190
217
  // This auth mechanism is always present.
191
218
  return callback(
192
- new MongoDriverError(`No AuthProvider for ${AuthMechanism.MONGODB_SCRAM_SHA256} defined.`)
219
+ new MongoInvalidArgumentError(
220
+ `No AuthProvider for ${AuthMechanism.MONGODB_SCRAM_SHA256} defined.`
221
+ )
193
222
  );
194
223
  }
195
224
  return provider.prepare(handshakeDoc, authContext, callback);
@@ -197,7 +226,7 @@ function prepareHandshakeDocument(authContext: AuthContext, callback: Callback<H
197
226
  const provider = AUTH_PROVIDERS.get(credentials.mechanism);
198
227
  if (!provider) {
199
228
  return callback(
200
- new MongoDriverError(`No AuthProvider for ${credentials.mechanism} defined.`)
229
+ new MongoInvalidArgumentError(`No AuthProvider for ${credentials.mechanism} defined.`)
201
230
  );
202
231
  }
203
232
  return provider.prepare(handshakeDoc, authContext, callback);
@@ -236,7 +265,7 @@ export const LEGAL_TCP_SOCKET_OPTIONS = [
236
265
 
237
266
  function parseConnectOptions(options: ConnectionOptions): SocketConnectOpts {
238
267
  const hostAddress = options.hostAddress;
239
- if (!hostAddress) throw new MongoDriverError('HostAddress required');
268
+ if (!hostAddress) throw new MongoInvalidArgumentError('Option "hostAddress" is required');
240
269
 
241
270
  const result: Partial<net.TcpNetConnectOpts & net.IpcNetConnectOpts> = {};
242
271
  for (const name of LEGAL_TCP_SOCKET_OPTIONS) {
@@ -255,7 +284,8 @@ function parseConnectOptions(options: ConnectionOptions): SocketConnectOpts {
255
284
  } else {
256
285
  // This should never happen since we set up HostAddresses
257
286
  // But if we don't throw here the socket could hang until timeout
258
- throw new MongoDriverError(`Unexpected HostAddress ${JSON.stringify(hostAddress)}`);
287
+ // TODO(NODE-3483)
288
+ throw new MongoRuntimeError(`Unexpected HostAddress ${JSON.stringify(hostAddress)}`);
259
289
  }
260
290
  }
261
291