mongodb 3.3.0-beta1 → 3.3.0-beta2

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.
@@ -1,731 +0,0 @@
1
- 'use strict';
2
-
3
- const deprecate = require('util').deprecate;
4
- const Logger = require('../core').Logger;
5
- const MongoError = require('../core').MongoError;
6
- const Mongos = require('../topologies/mongos');
7
- const parse = require('../core').parseConnectionString;
8
- const ReadPreference = require('../core').ReadPreference;
9
- const ReplSet = require('../topologies/replset');
10
- const Server = require('../topologies/server');
11
- const ServerSessionPool = require('../core').Sessions.ServerSessionPool;
12
- const NativeTopology = require('../topologies/native_topology');
13
- const MongoCredentials = require('../core').MongoCredentials;
14
- const ReadConcern = require('../read_concern');
15
- const os = require('os');
16
-
17
- let client;
18
- function loadClient() {
19
- if (!client) {
20
- client = require('../mongo_client');
21
- }
22
- return client;
23
- }
24
-
25
- const monitoringEvents = [
26
- 'timeout',
27
- 'close',
28
- 'serverOpening',
29
- 'serverDescriptionChanged',
30
- 'serverHeartbeatStarted',
31
- 'serverHeartbeatSucceeded',
32
- 'serverHeartbeatFailed',
33
- 'serverClosed',
34
- 'topologyOpening',
35
- 'topologyClosed',
36
- 'topologyDescriptionChanged',
37
- 'commandStarted',
38
- 'commandSucceeded',
39
- 'commandFailed',
40
- 'joined',
41
- 'left',
42
- 'ping',
43
- 'ha',
44
- 'all',
45
- 'fullsetup',
46
- 'open'
47
- ];
48
- const ignoreOptionNames = ['native_parser'];
49
- const legacyOptionNames = ['server', 'replset', 'replSet', 'mongos', 'db'];
50
- const legacyParse = deprecate(
51
- require('../url_parser'),
52
- 'current URL string parser is deprecated, and will be removed in a future version. ' +
53
- 'To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.'
54
- );
55
- const validOptionNames = [
56
- 'poolSize',
57
- 'ssl',
58
- 'sslValidate',
59
- 'sslCA',
60
- 'sslCert',
61
- 'sslKey',
62
- 'sslPass',
63
- 'sslCRL',
64
- 'autoReconnect',
65
- 'noDelay',
66
- 'keepAlive',
67
- 'keepAliveInitialDelay',
68
- 'connectTimeoutMS',
69
- 'family',
70
- 'socketTimeoutMS',
71
- 'reconnectTries',
72
- 'reconnectInterval',
73
- 'ha',
74
- 'haInterval',
75
- 'replicaSet',
76
- 'secondaryAcceptableLatencyMS',
77
- 'acceptableLatencyMS',
78
- 'connectWithNoPrimary',
79
- 'authSource',
80
- 'w',
81
- 'wtimeout',
82
- 'j',
83
- 'forceServerObjectId',
84
- 'serializeFunctions',
85
- 'ignoreUndefined',
86
- 'raw',
87
- 'bufferMaxEntries',
88
- 'readPreference',
89
- 'pkFactory',
90
- 'promiseLibrary',
91
- 'readConcern',
92
- 'maxStalenessSeconds',
93
- 'loggerLevel',
94
- 'logger',
95
- 'promoteValues',
96
- 'promoteBuffers',
97
- 'promoteLongs',
98
- 'domainsEnabled',
99
- 'checkServerIdentity',
100
- 'validateOptions',
101
- 'appname',
102
- 'auth',
103
- 'user',
104
- 'password',
105
- 'authMechanism',
106
- 'compression',
107
- 'fsync',
108
- 'readPreferenceTags',
109
- 'numberOfRetries',
110
- 'auto_reconnect',
111
- 'minSize',
112
- 'monitorCommands',
113
- 'retryWrites',
114
- 'useNewUrlParser',
115
- 'useUnifiedTopology',
116
- 'serverSelectionTimeoutMS',
117
- 'useRecoveryToken',
118
- 'autoEncryption'
119
- ];
120
-
121
- function addListeners(mongoClient, topology) {
122
- topology.on('authenticated', createListener(mongoClient, 'authenticated'));
123
- topology.on('error', createListener(mongoClient, 'error'));
124
- topology.on('timeout', createListener(mongoClient, 'timeout'));
125
- topology.on('close', createListener(mongoClient, 'close'));
126
- topology.on('parseError', createListener(mongoClient, 'parseError'));
127
- topology.once('open', createListener(mongoClient, 'open'));
128
- topology.once('fullsetup', createListener(mongoClient, 'fullsetup'));
129
- topology.once('all', createListener(mongoClient, 'all'));
130
- topology.on('reconnect', createListener(mongoClient, 'reconnect'));
131
- }
132
-
133
- function assignTopology(client, topology) {
134
- client.topology = topology;
135
- topology.s.sessionPool =
136
- topology instanceof NativeTopology
137
- ? new ServerSessionPool(topology)
138
- : new ServerSessionPool(topology.s.coreTopology);
139
- }
140
-
141
- // Clear out all events
142
- function clearAllEvents(topology) {
143
- monitoringEvents.forEach(event => topology.removeAllListeners(event));
144
- }
145
-
146
- // Collect all events in order from SDAM
147
- function collectEvents(mongoClient, topology) {
148
- let MongoClient = loadClient();
149
- const collectedEvents = [];
150
-
151
- if (mongoClient instanceof MongoClient) {
152
- monitoringEvents.forEach(event => {
153
- topology.on(event, (object1, object2) => {
154
- if (event === 'open') {
155
- collectedEvents.push({ event: event, object1: mongoClient });
156
- } else {
157
- collectedEvents.push({ event: event, object1: object1, object2: object2 });
158
- }
159
- });
160
- });
161
- }
162
-
163
- return collectedEvents;
164
- }
165
-
166
- /**
167
- * Connect to MongoDB using a url as documented at
168
- *
169
- * docs.mongodb.org/manual/reference/connection-string/
170
- *
171
- * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
172
- *
173
- * @method
174
- * @param {MongoClient} mongoClient The MongoClient instance with which to connect.
175
- * @param {string} url The connection URI string
176
- * @param {object} [options] Optional settings. See MongoClient.prototype.connect for a list of options.
177
- * @param {MongoClient~connectCallback} [callback] The command result callback
178
- */
179
- function connect(mongoClient, url, options, callback) {
180
- options = Object.assign({}, options);
181
-
182
- // If callback is null throw an exception
183
- if (callback == null) {
184
- throw new Error('no callback function provided');
185
- }
186
-
187
- let didRequestAuthentication = false;
188
- const logger = Logger('MongoClient', options);
189
-
190
- // Did we pass in a Server/ReplSet/Mongos
191
- if (url instanceof Server || url instanceof ReplSet || url instanceof Mongos) {
192
- return connectWithUrl(mongoClient, url, options, connectCallback);
193
- }
194
-
195
- const parseFn = options.useNewUrlParser ? parse : legacyParse;
196
- const transform = options.useNewUrlParser ? transformUrlOptions : legacyTransformUrlOptions;
197
-
198
- parseFn(url, options, (err, _object) => {
199
- // Do not attempt to connect if parsing error
200
- if (err) return callback(err);
201
-
202
- // Flatten
203
- const object = transform(_object);
204
-
205
- // Parse the string
206
- const _finalOptions = createUnifiedOptions(object, options);
207
-
208
- // Check if we have connection and socket timeout set
209
- if (_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
210
- if (_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
211
- if (_finalOptions.retryWrites == null) _finalOptions.retryWrites = true;
212
-
213
- if (_finalOptions.db_options && _finalOptions.db_options.auth) {
214
- delete _finalOptions.db_options.auth;
215
- }
216
-
217
- // Store the merged options object
218
- mongoClient.s.options = _finalOptions;
219
-
220
- // Failure modes
221
- if (object.servers.length === 0) {
222
- return callback(new Error('connection string must contain at least one seed host'));
223
- }
224
-
225
- if (_finalOptions.auth && !_finalOptions.credentials) {
226
- try {
227
- didRequestAuthentication = true;
228
- _finalOptions.credentials = generateCredentials(
229
- mongoClient,
230
- _finalOptions.auth.user,
231
- _finalOptions.auth.password,
232
- _finalOptions
233
- );
234
- } catch (err) {
235
- return callback(err);
236
- }
237
- }
238
-
239
- if (_finalOptions.useUnifiedTopology) {
240
- return createTopology(mongoClient, 'unified', _finalOptions, connectCallback);
241
- }
242
-
243
- // Do we have a replicaset then skip discovery and go straight to connectivity
244
- if (_finalOptions.replicaSet || _finalOptions.rs_name) {
245
- return createTopology(mongoClient, 'replicaset', _finalOptions, connectCallback);
246
- } else if (object.servers.length > 1) {
247
- return createTopology(mongoClient, 'mongos', _finalOptions, connectCallback);
248
- } else {
249
- return createServer(mongoClient, _finalOptions, connectCallback);
250
- }
251
- });
252
-
253
- function connectCallback(err, topology) {
254
- const warningMessage = `seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name`;
255
- if (err && err.message === 'no mongos proxies found in seed list') {
256
- if (logger.isWarn()) {
257
- logger.warn(warningMessage);
258
- }
259
-
260
- // Return a more specific error message for MongoClient.connect
261
- return callback(new MongoError(warningMessage));
262
- }
263
-
264
- if (didRequestAuthentication) {
265
- mongoClient.emit('authenticated', null, true);
266
- }
267
-
268
- // Return the error and db instance
269
- callback(err, topology);
270
- }
271
- }
272
-
273
- /**
274
- * Connect to MongoDB using a url as documented at
275
- *
276
- * docs.mongodb.org/manual/reference/connection-string/
277
- *
278
- * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
279
- *
280
- * @method
281
- * @param {MongoClient} mongoClient The MongoClient instance with which to connect.
282
- * @param {MongoClient~connectCallback} [callback] The command result callback
283
- */
284
- function connectOp(mongoClient, err, callback) {
285
- // Did we have a validation error
286
- if (err) return callback(err);
287
- // Fallback to callback based connect
288
- connect(mongoClient, mongoClient.s.url, mongoClient.s.options, err => {
289
- if (err) return callback(err);
290
- callback(null, mongoClient);
291
- });
292
- }
293
-
294
- function connectWithUrl(mongoClient, url, options, connectCallback) {
295
- // Set the topology
296
- assignTopology(mongoClient, url);
297
-
298
- // Add listeners
299
- addListeners(mongoClient, url);
300
-
301
- // Propagate the events to the client
302
- relayEvents(mongoClient, url);
303
-
304
- let finalOptions = Object.assign({}, options);
305
-
306
- // If we have a readPreference passed in by the db options, convert it from a string
307
- if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
308
- finalOptions.readPreference = new ReadPreference(
309
- options.readPreference || options.read_preference
310
- );
311
- }
312
-
313
- const isDoingAuth = finalOptions.user || finalOptions.password || finalOptions.authMechanism;
314
- if (isDoingAuth && !finalOptions.credentials) {
315
- try {
316
- finalOptions.credentials = generateCredentials(
317
- mongoClient,
318
- finalOptions.user,
319
- finalOptions.password,
320
- finalOptions
321
- );
322
- } catch (err) {
323
- return connectCallback(err, url);
324
- }
325
- }
326
-
327
- return url.connect(finalOptions, connectCallback);
328
- }
329
-
330
- function createListener(mongoClient, event) {
331
- const eventSet = new Set(['all', 'fullsetup', 'open', 'reconnect']);
332
- return (v1, v2) => {
333
- if (eventSet.has(event)) {
334
- return mongoClient.emit(event, mongoClient);
335
- }
336
-
337
- mongoClient.emit(event, v1, v2);
338
- };
339
- }
340
-
341
- function createServer(mongoClient, options, callback) {
342
- // Pass in the promise library
343
- options.promiseLibrary = mongoClient.s.promiseLibrary;
344
-
345
- // Set default options
346
- const servers = translateOptions(options);
347
-
348
- const server = servers[0];
349
-
350
- // Propagate the events to the client
351
- const collectedEvents = collectEvents(mongoClient, server);
352
-
353
- // Connect to topology
354
- server.connect(options, (err, topology) => {
355
- if (err) {
356
- server.close(true);
357
- return callback(err);
358
- }
359
- // Clear out all the collected event listeners
360
- clearAllEvents(server);
361
-
362
- // Relay all the events
363
- relayEvents(mongoClient, server);
364
- // Add listeners
365
- addListeners(mongoClient, server);
366
- // Check if we are really speaking to a mongos
367
- const ismaster = topology.lastIsMaster();
368
-
369
- // Set the topology
370
- assignTopology(mongoClient, topology);
371
-
372
- // Do we actually have a mongos
373
- if (ismaster && ismaster.msg === 'isdbgrid') {
374
- // Destroy the current connection
375
- topology.close();
376
- // Create mongos connection instead
377
- return createTopology(mongoClient, 'mongos', options, callback);
378
- }
379
-
380
- // Fire all the events
381
- replayEvents(mongoClient, collectedEvents);
382
- // Otherwise callback
383
- callback(err, topology);
384
- });
385
- }
386
-
387
- function createTopology(mongoClient, topologyType, options, callback) {
388
- // Pass in the promise library
389
- options.promiseLibrary = mongoClient.s.promiseLibrary;
390
-
391
- const translationOptions = {};
392
- if (topologyType === 'unified') translationOptions.createServers = false;
393
-
394
- // Set default options
395
- const servers = translateOptions(options, translationOptions);
396
-
397
- // Create the topology
398
- let topology;
399
- if (topologyType === 'mongos') {
400
- topology = new Mongos(servers, options);
401
- } else if (topologyType === 'replicaset') {
402
- topology = new ReplSet(servers, options);
403
- } else if (topologyType === 'unified') {
404
- topology = new NativeTopology(options.servers, options);
405
- }
406
-
407
- // Add listeners
408
- addListeners(mongoClient, topology);
409
-
410
- // Propagate the events to the client
411
- relayEvents(mongoClient, topology);
412
-
413
- // Open the connection
414
- topology.connect(options, (err, newTopology) => {
415
- if (err) {
416
- topology.close(true);
417
- return callback(err);
418
- }
419
-
420
- assignTopology(mongoClient, newTopology);
421
- if (options.autoEncryption == null) {
422
- callback(null, newTopology);
423
- return;
424
- }
425
-
426
- // setup for client side encryption
427
- let AutoEncrypter;
428
- try {
429
- AutoEncrypter = require('mongodb-client-encryption').AutoEncrypter;
430
- } catch (err) {
431
- callback(
432
- new MongoError(
433
- 'Auto-encryption requested, but the module is not installed. Please add `mongodb-client-encryption` as a dependency of your project'
434
- )
435
- );
436
-
437
- return;
438
- }
439
-
440
- const MongoClient = loadClient();
441
- const connectionString =
442
- os.platform() === 'win32'
443
- ? 'mongodb://localhost:27020/?serverSelectionTimeoutMS=1000'
444
- : 'mongodb://%2Ftmp%2Fmongocryptd.sock/?serverSelectionTimeoutMS=1000';
445
-
446
- const mongocryptdClient = new MongoClient(connectionString, { useUnifiedTopology: true });
447
- mongocryptdClient.connect(err => {
448
- if (err) return callback(err, null);
449
-
450
- const mongoCryptOptions = Object.assign({}, options.autoEncryption, {
451
- mongocryptdClient
452
- });
453
-
454
- topology.s.options.autoEncrypter = new AutoEncrypter(mongoClient, mongoCryptOptions);
455
- callback(null, newTopology);
456
- });
457
- });
458
- }
459
-
460
- function createUnifiedOptions(finalOptions, options) {
461
- const childOptions = [
462
- 'mongos',
463
- 'server',
464
- 'db',
465
- 'replset',
466
- 'db_options',
467
- 'server_options',
468
- 'rs_options',
469
- 'mongos_options'
470
- ];
471
- const noMerge = ['readconcern', 'compression'];
472
-
473
- for (const name in options) {
474
- if (noMerge.indexOf(name.toLowerCase()) !== -1) {
475
- finalOptions[name] = options[name];
476
- } else if (childOptions.indexOf(name.toLowerCase()) !== -1) {
477
- finalOptions = mergeOptions(finalOptions, options[name], false);
478
- } else {
479
- if (
480
- options[name] &&
481
- typeof options[name] === 'object' &&
482
- !Buffer.isBuffer(options[name]) &&
483
- !Array.isArray(options[name])
484
- ) {
485
- finalOptions = mergeOptions(finalOptions, options[name], true);
486
- } else {
487
- finalOptions[name] = options[name];
488
- }
489
- }
490
- }
491
-
492
- return finalOptions;
493
- }
494
-
495
- function legacyTransformUrlOptions(object) {
496
- return mergeOptions(createUnifiedOptions({}, object), object, false);
497
- }
498
-
499
- function mergeOptions(target, source, flatten) {
500
- for (const name in source) {
501
- if (source[name] && typeof source[name] === 'object' && flatten) {
502
- target = mergeOptions(target, source[name], flatten);
503
- } else {
504
- target[name] = source[name];
505
- }
506
- }
507
-
508
- return target;
509
- }
510
-
511
- function relayEvents(mongoClient, topology) {
512
- const serverOrCommandEvents = [
513
- 'serverOpening',
514
- 'serverDescriptionChanged',
515
- 'serverHeartbeatStarted',
516
- 'serverHeartbeatSucceeded',
517
- 'serverHeartbeatFailed',
518
- 'serverClosed',
519
- 'topologyOpening',
520
- 'topologyClosed',
521
- 'topologyDescriptionChanged',
522
- 'commandStarted',
523
- 'commandSucceeded',
524
- 'commandFailed',
525
- 'joined',
526
- 'left',
527
- 'ping',
528
- 'ha'
529
- ];
530
-
531
- serverOrCommandEvents.forEach(event => {
532
- topology.on(event, (object1, object2) => {
533
- mongoClient.emit(event, object1, object2);
534
- });
535
- });
536
- }
537
-
538
- //
539
- // Replay any events due to single server connection switching to Mongos
540
- //
541
- function replayEvents(mongoClient, events) {
542
- for (let i = 0; i < events.length; i++) {
543
- mongoClient.emit(events[i].event, events[i].object1, events[i].object2);
544
- }
545
- }
546
-
547
- const LEGACY_OPTIONS_MAP = validOptionNames.reduce((obj, name) => {
548
- obj[name.toLowerCase()] = name;
549
- return obj;
550
- }, {});
551
-
552
- function transformUrlOptions(_object) {
553
- let object = Object.assign({ servers: _object.hosts }, _object.options);
554
- for (let name in object) {
555
- const camelCaseName = LEGACY_OPTIONS_MAP[name];
556
- if (camelCaseName) {
557
- object[camelCaseName] = object[name];
558
- }
559
- }
560
-
561
- const hasUsername = _object.auth && _object.auth.username;
562
- const hasAuthMechanism = _object.options && _object.options.authMechanism;
563
- if (hasUsername || hasAuthMechanism) {
564
- object.auth = Object.assign({}, _object.auth);
565
- if (object.auth.db) {
566
- object.authSource = object.authSource || object.auth.db;
567
- }
568
-
569
- if (object.auth.username) {
570
- object.auth.user = object.auth.username;
571
- }
572
- }
573
-
574
- if (_object.defaultDatabase) {
575
- object.dbName = _object.defaultDatabase;
576
- }
577
-
578
- if (object.maxpoolsize) {
579
- object.poolSize = object.maxpoolsize;
580
- }
581
-
582
- if (object.readconcernlevel) {
583
- object.readConcern = new ReadConcern(object.readconcernlevel);
584
- }
585
-
586
- if (object.wtimeoutms) {
587
- object.wtimeout = object.wtimeoutms;
588
- }
589
-
590
- if (_object.srvHost) {
591
- object.srvHost = _object.srvHost;
592
- }
593
-
594
- return object;
595
- }
596
-
597
- function translateOptions(options, translationOptions) {
598
- translationOptions = Object.assign({}, { createServers: true }, translationOptions);
599
-
600
- // If we have a readPreference passed in by the db options
601
- if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
602
- options.readPreference = new ReadPreference(options.readPreference || options.read_preference);
603
- }
604
-
605
- // Do we have readPreference tags, add them
606
- if (options.readPreference && (options.readPreferenceTags || options.read_preference_tags)) {
607
- options.readPreference.tags = options.readPreferenceTags || options.read_preference_tags;
608
- }
609
-
610
- // Do we have maxStalenessSeconds
611
- if (options.maxStalenessSeconds) {
612
- options.readPreference.maxStalenessSeconds = options.maxStalenessSeconds;
613
- }
614
-
615
- // Set the socket and connection timeouts
616
- if (options.socketTimeoutMS == null) options.socketTimeoutMS = 360000;
617
- if (options.connectTimeoutMS == null) options.connectTimeoutMS = 30000;
618
-
619
- if (!translationOptions.createServers) {
620
- return;
621
- }
622
-
623
- // Create server instances
624
- return options.servers.map(serverObj => {
625
- return serverObj.domain_socket
626
- ? new Server(serverObj.domain_socket, 27017, options)
627
- : new Server(serverObj.host, serverObj.port, options);
628
- });
629
- }
630
-
631
- // Validate options object
632
- function validOptions(options) {
633
- const _validOptions = validOptionNames.concat(legacyOptionNames);
634
-
635
- for (const name in options) {
636
- if (ignoreOptionNames.indexOf(name) !== -1) {
637
- continue;
638
- }
639
-
640
- if (_validOptions.indexOf(name) === -1) {
641
- if (options.validateOptions) {
642
- return new MongoError(`option ${name} is not supported`);
643
- } else {
644
- console.warn(`the options [${name}] is not supported`);
645
- }
646
- }
647
-
648
- if (legacyOptionNames.indexOf(name) !== -1) {
649
- console.warn(
650
- `the server/replset/mongos/db options are deprecated, ` +
651
- `all their options are supported at the top level of the options object [${validOptionNames}]`
652
- );
653
- }
654
- }
655
- }
656
-
657
- const VALID_AUTH_MECHANISMS = new Set([
658
- 'DEFAULT',
659
- 'MONGODB-CR',
660
- 'PLAIN',
661
- 'MONGODB-X509',
662
- 'SCRAM-SHA-1',
663
- 'SCRAM-SHA-256',
664
- 'GSSAPI'
665
- ]);
666
-
667
- const AUTH_MECHANISM_INTERNAL_MAP = {
668
- DEFAULT: 'default',
669
- 'MONGODB-CR': 'mongocr',
670
- PLAIN: 'plain',
671
- 'MONGODB-X509': 'x509',
672
- 'SCRAM-SHA-1': 'scram-sha-1',
673
- 'SCRAM-SHA-256': 'scram-sha-256'
674
- };
675
-
676
- function generateCredentials(client, username, password, options) {
677
- options = Object.assign({}, options);
678
-
679
- // the default db to authenticate against is 'self'
680
- // if authenticate is called from a retry context, it may be another one, like admin
681
- const source = options.authSource || options.authdb || options.dbName;
682
-
683
- // authMechanism
684
- const authMechanismRaw = options.authMechanism || 'DEFAULT';
685
- const authMechanism = authMechanismRaw.toUpperCase();
686
-
687
- if (!VALID_AUTH_MECHANISMS.has(authMechanism)) {
688
- throw MongoError.create({
689
- message: `authentication mechanism ${authMechanismRaw} not supported', options.authMechanism`,
690
- driver: true
691
- });
692
- }
693
-
694
- if (authMechanism === 'GSSAPI') {
695
- return new MongoCredentials({
696
- mechanism: process.platform === 'win32' ? 'sspi' : 'gssapi',
697
- mechanismProperties: options,
698
- source,
699
- username,
700
- password
701
- });
702
- }
703
-
704
- return new MongoCredentials({
705
- mechanism: AUTH_MECHANISM_INTERNAL_MAP[authMechanism],
706
- source,
707
- username,
708
- password
709
- });
710
- }
711
-
712
- function closeOperation(client, force, callback) {
713
- const completeClose = err => {
714
- client.emit('close', client);
715
- for (const name in client.s.dbCache) {
716
- client.s.dbCache[name].emit('close', client);
717
- }
718
-
719
- client.removeAllListeners('close');
720
- callback(err, null);
721
- };
722
-
723
- if (client.topology == null) {
724
- completeClose();
725
- return;
726
- }
727
-
728
- client.topology.close(force, completeClose);
729
- }
730
-
731
- module.exports = { connectOp, validOptions, closeOperation };