utils.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. 'use strict';
  2. const MongoError = require('mongodb-core').MongoError;
  3. const ReadPreference = require('mongodb-core').ReadPreference;
  4. var shallowClone = function(obj) {
  5. var copy = {};
  6. for (var name in obj) copy[name] = obj[name];
  7. return copy;
  8. };
  9. // Figure out the read preference
  10. var translateReadPreference = function(options) {
  11. var r = null;
  12. if (options.readPreference) {
  13. r = options.readPreference;
  14. } else {
  15. return options;
  16. }
  17. if (typeof r === 'string') {
  18. options.readPreference = new ReadPreference(r);
  19. } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
  20. const mode = r.mode || r.preference;
  21. if (mode && typeof mode === 'string') {
  22. options.readPreference = new ReadPreference(mode, r.tags, {
  23. maxStalenessSeconds: r.maxStalenessSeconds
  24. });
  25. }
  26. } else if (!(r instanceof ReadPreference)) {
  27. throw new TypeError('Invalid read preference: ' + r);
  28. }
  29. return options;
  30. };
  31. // Set simple property
  32. var getSingleProperty = function(obj, name, value) {
  33. Object.defineProperty(obj, name, {
  34. enumerable: true,
  35. get: function() {
  36. return value;
  37. }
  38. });
  39. };
  40. var formatSortValue = (exports.formatSortValue = function(sortDirection) {
  41. var value = ('' + sortDirection).toLowerCase();
  42. switch (value) {
  43. case 'ascending':
  44. case 'asc':
  45. case '1':
  46. return 1;
  47. case 'descending':
  48. case 'desc':
  49. case '-1':
  50. return -1;
  51. default:
  52. throw new Error(
  53. 'Illegal sort clause, must be of the form ' +
  54. "[['field1', '(ascending|descending)'], " +
  55. "['field2', '(ascending|descending)']]"
  56. );
  57. }
  58. });
  59. var formattedOrderClause = (exports.formattedOrderClause = function(sortValue) {
  60. var orderBy = {};
  61. if (sortValue == null) return null;
  62. if (Array.isArray(sortValue)) {
  63. if (sortValue.length === 0) {
  64. return null;
  65. }
  66. for (var i = 0; i < sortValue.length; i++) {
  67. if (sortValue[i].constructor === String) {
  68. orderBy[sortValue[i]] = 1;
  69. } else {
  70. orderBy[sortValue[i][0]] = formatSortValue(sortValue[i][1]);
  71. }
  72. }
  73. } else if (sortValue != null && typeof sortValue === 'object') {
  74. orderBy = sortValue;
  75. } else if (typeof sortValue === 'string') {
  76. orderBy[sortValue] = 1;
  77. } else {
  78. throw new Error(
  79. 'Illegal sort clause, must be of the form ' +
  80. "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
  81. );
  82. }
  83. return orderBy;
  84. });
  85. var checkCollectionName = function checkCollectionName(collectionName) {
  86. if ('string' !== typeof collectionName) {
  87. throw new MongoError('collection name must be a String');
  88. }
  89. if (!collectionName || collectionName.indexOf('..') !== -1) {
  90. throw new MongoError('collection names cannot be empty');
  91. }
  92. if (
  93. collectionName.indexOf('$') !== -1 &&
  94. collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null
  95. ) {
  96. throw new MongoError("collection names must not contain '$'");
  97. }
  98. if (collectionName.match(/^\.|\.$/) != null) {
  99. throw new MongoError("collection names must not start or end with '.'");
  100. }
  101. // Validate that we are not passing 0x00 in the colletion name
  102. if (collectionName.indexOf('\x00') !== -1) {
  103. throw new MongoError('collection names cannot contain a null character');
  104. }
  105. };
  106. var handleCallback = function(callback, err, value1, value2) {
  107. try {
  108. if (callback == null) return;
  109. if (callback) {
  110. return value2 ? callback(err, value1, value2) : callback(err, value1);
  111. }
  112. } catch (err) {
  113. process.nextTick(function() {
  114. throw err;
  115. });
  116. return false;
  117. }
  118. return true;
  119. };
  120. /**
  121. * Wrap a Mongo error document in an Error instance
  122. * @ignore
  123. * @api private
  124. */
  125. var toError = function(error) {
  126. if (error instanceof Error) return error;
  127. var msg = error.err || error.errmsg || error.errMessage || error;
  128. var e = MongoError.create({ message: msg, driver: true });
  129. // Get all object keys
  130. var keys = typeof error === 'object' ? Object.keys(error) : [];
  131. for (var i = 0; i < keys.length; i++) {
  132. try {
  133. e[keys[i]] = error[keys[i]];
  134. } catch (err) {
  135. // continue
  136. }
  137. }
  138. return e;
  139. };
  140. /**
  141. * @ignore
  142. */
  143. var normalizeHintField = function normalizeHintField(hint) {
  144. var finalHint = null;
  145. if (typeof hint === 'string') {
  146. finalHint = hint;
  147. } else if (Array.isArray(hint)) {
  148. finalHint = {};
  149. hint.forEach(function(param) {
  150. finalHint[param] = 1;
  151. });
  152. } else if (hint != null && typeof hint === 'object') {
  153. finalHint = {};
  154. for (var name in hint) {
  155. finalHint[name] = hint[name];
  156. }
  157. }
  158. return finalHint;
  159. };
  160. /**
  161. * Create index name based on field spec
  162. *
  163. * @ignore
  164. * @api private
  165. */
  166. var parseIndexOptions = function(fieldOrSpec) {
  167. var fieldHash = {};
  168. var indexes = [];
  169. var keys;
  170. // Get all the fields accordingly
  171. if ('string' === typeof fieldOrSpec) {
  172. // 'type'
  173. indexes.push(fieldOrSpec + '_' + 1);
  174. fieldHash[fieldOrSpec] = 1;
  175. } else if (Array.isArray(fieldOrSpec)) {
  176. fieldOrSpec.forEach(function(f) {
  177. if ('string' === typeof f) {
  178. // [{location:'2d'}, 'type']
  179. indexes.push(f + '_' + 1);
  180. fieldHash[f] = 1;
  181. } else if (Array.isArray(f)) {
  182. // [['location', '2d'],['type', 1]]
  183. indexes.push(f[0] + '_' + (f[1] || 1));
  184. fieldHash[f[0]] = f[1] || 1;
  185. } else if (isObject(f)) {
  186. // [{location:'2d'}, {type:1}]
  187. keys = Object.keys(f);
  188. keys.forEach(function(k) {
  189. indexes.push(k + '_' + f[k]);
  190. fieldHash[k] = f[k];
  191. });
  192. } else {
  193. // undefined (ignore)
  194. }
  195. });
  196. } else if (isObject(fieldOrSpec)) {
  197. // {location:'2d', type:1}
  198. keys = Object.keys(fieldOrSpec);
  199. keys.forEach(function(key) {
  200. indexes.push(key + '_' + fieldOrSpec[key]);
  201. fieldHash[key] = fieldOrSpec[key];
  202. });
  203. }
  204. return {
  205. name: indexes.join('_'),
  206. keys: keys,
  207. fieldHash: fieldHash
  208. };
  209. };
  210. var isObject = (exports.isObject = function(arg) {
  211. return '[object Object]' === Object.prototype.toString.call(arg);
  212. });
  213. var debugOptions = function(debugFields, options) {
  214. var finaloptions = {};
  215. debugFields.forEach(function(n) {
  216. finaloptions[n] = options[n];
  217. });
  218. return finaloptions;
  219. };
  220. var decorateCommand = function(command, options, exclude) {
  221. for (var name in options) {
  222. if (exclude.indexOf(name) === -1) command[name] = options[name];
  223. }
  224. return command;
  225. };
  226. var mergeOptions = function(target, source) {
  227. for (var name in source) {
  228. target[name] = source[name];
  229. }
  230. return target;
  231. };
  232. // Merge options with translation
  233. var translateOptions = function(target, source) {
  234. var translations = {
  235. // SSL translation options
  236. sslCA: 'ca',
  237. sslCRL: 'crl',
  238. sslValidate: 'rejectUnauthorized',
  239. sslKey: 'key',
  240. sslCert: 'cert',
  241. sslPass: 'passphrase',
  242. // SocketTimeout translation options
  243. socketTimeoutMS: 'socketTimeout',
  244. connectTimeoutMS: 'connectionTimeout',
  245. // Replicaset options
  246. replicaSet: 'setName',
  247. rs_name: 'setName',
  248. secondaryAcceptableLatencyMS: 'acceptableLatency',
  249. connectWithNoPrimary: 'secondaryOnlyConnectionAllowed',
  250. // Mongos options
  251. acceptableLatencyMS: 'localThresholdMS'
  252. };
  253. for (var name in source) {
  254. if (translations[name]) {
  255. target[translations[name]] = source[name];
  256. } else {
  257. target[name] = source[name];
  258. }
  259. }
  260. return target;
  261. };
  262. var filterOptions = function(options, names) {
  263. var filterOptions = {};
  264. for (var name in options) {
  265. if (names.indexOf(name) !== -1) filterOptions[name] = options[name];
  266. }
  267. // Filtered options
  268. return filterOptions;
  269. };
  270. // Write concern keys
  271. var writeConcernKeys = ['w', 'j', 'wtimeout', 'fsync'];
  272. // Merge the write concern options
  273. var mergeOptionsAndWriteConcern = function(targetOptions, sourceOptions, keys, mergeWriteConcern) {
  274. // Mix in any allowed options
  275. for (var i = 0; i < keys.length; i++) {
  276. if (!targetOptions[keys[i]] && sourceOptions[keys[i]] !== undefined) {
  277. targetOptions[keys[i]] = sourceOptions[keys[i]];
  278. }
  279. }
  280. // No merging of write concern
  281. if (!mergeWriteConcern) return targetOptions;
  282. // Found no write Concern options
  283. var found = false;
  284. for (i = 0; i < writeConcernKeys.length; i++) {
  285. if (targetOptions[writeConcernKeys[i]]) {
  286. found = true;
  287. break;
  288. }
  289. }
  290. if (!found) {
  291. for (i = 0; i < writeConcernKeys.length; i++) {
  292. if (sourceOptions[writeConcernKeys[i]]) {
  293. targetOptions[writeConcernKeys[i]] = sourceOptions[writeConcernKeys[i]];
  294. }
  295. }
  296. }
  297. return targetOptions;
  298. };
  299. /**
  300. * Executes the given operation with provided arguments.
  301. *
  302. * This method reduces large amounts of duplication in the entire codebase by providing
  303. * a single point for determining whether callbacks or promises should be used. Additionally
  304. * it allows for a single point of entry to provide features such as implicit sessions, which
  305. * are required by the Driver Sessions specification in the event that a ClientSession is
  306. * not provided
  307. *
  308. * @param {object} topology The topology to execute this operation on
  309. * @param {function} operation The operation to execute
  310. * @param {array} args Arguments to apply the provided operation
  311. * @param {object} [options] Options that modify the behavior of the method
  312. * @param {function]} [options.resultMutator] Allows for the result of the operation to be changed for custom return types
  313. */
  314. const executeOperation = (topology, operation, args, options) => {
  315. if (topology == null) {
  316. throw new TypeError('This method requires a valid topology instance');
  317. }
  318. if (!Array.isArray(args)) {
  319. throw new TypeError('This method requires an array of arguments to apply');
  320. }
  321. options = options || {};
  322. const Promise = topology.s.promiseLibrary;
  323. let resultMutator = options.resultMutator;
  324. let callback = args[args.length - 1];
  325. // The driver sessions spec mandates that we implicitly create sessions for operations
  326. // that are not explicitly provided with a session.
  327. let session, opOptions, owner;
  328. if (!options.skipSessions && topology.hasSessionSupport()) {
  329. opOptions = args[args.length - 2];
  330. if (opOptions == null || opOptions.session == null) {
  331. owner = Symbol();
  332. session = topology.startSession({ owner });
  333. const optionsIndex = args.length - 2;
  334. args[optionsIndex] = Object.assign({}, args[optionsIndex], { session: session });
  335. } else if (opOptions.session && opOptions.session.hasEnded) {
  336. throw new MongoError('Use of expired sessions is not permitted');
  337. }
  338. }
  339. const makeExecuteCallback = (resolve, reject) =>
  340. function executeCallback(err, result) {
  341. if (session && session.owner === owner && !options.returnsCursor) {
  342. session.endSession(() => {
  343. delete opOptions.session;
  344. if (err) return reject(err);
  345. if (resultMutator) return resolve(resultMutator(result));
  346. resolve(result);
  347. });
  348. } else {
  349. if (err) return reject(err);
  350. if (resultMutator) return resolve(resultMutator(result));
  351. resolve(result);
  352. }
  353. };
  354. // Execute using callback
  355. if (typeof callback === 'function') {
  356. callback = args.pop();
  357. const handler = makeExecuteCallback(
  358. result => callback(null, result),
  359. err => callback(err, null)
  360. );
  361. args.push(handler);
  362. try {
  363. return operation.apply(null, args);
  364. } catch (e) {
  365. handler(e);
  366. throw e;
  367. }
  368. }
  369. // Return a Promise
  370. if (args[args.length - 1] != null) {
  371. throw new TypeError('final argument to `executeOperation` must be a callback');
  372. }
  373. return new Promise(function(resolve, reject) {
  374. const handler = makeExecuteCallback(resolve, reject);
  375. args[args.length - 1] = handler;
  376. try {
  377. return operation.apply(null, args);
  378. } catch (e) {
  379. handler(e);
  380. }
  381. });
  382. };
  383. /**
  384. * Applies retryWrites: true to a command if retryWrites is set on the command's database.
  385. *
  386. * @param {object} target The target command to which we will apply retryWrites.
  387. * @param {object} db The database from which we can inherit a retryWrites value.
  388. */
  389. function applyRetryableWrites(target, db) {
  390. if (db && db.s.options.retryWrites) {
  391. target.retryWrites = true;
  392. }
  393. return target;
  394. }
  395. /**
  396. * Applies a write concern to a command based on well defined inheritance rules, optionally
  397. * detecting support for the write concern in the first place.
  398. *
  399. * @param {Object} target the target command we will be applying the write concern to
  400. * @param {Object} sources sources where we can inherit default write concerns from
  401. * @param {Object} [options] optional settings passed into a command for write concern overrides
  402. * @returns {Object} the (now) decorated target
  403. */
  404. function applyWriteConcern(target, sources, options) {
  405. options = options || {};
  406. const db = sources.db;
  407. const coll = sources.collection;
  408. if (options.session && options.session.inTransaction()) {
  409. // writeConcern is not allowed within a multi-statement transaction
  410. if (target.writeConcern) {
  411. delete target.writeConcern;
  412. }
  413. return target;
  414. }
  415. if (options.w != null || options.j != null || options.fsync != null) {
  416. const writeConcern = {};
  417. if (options.w != null) writeConcern.w = options.w;
  418. if (options.wtimeout != null) writeConcern.wtimeout = options.wtimeout;
  419. if (options.j != null) writeConcern.j = options.j;
  420. if (options.fsync != null) writeConcern.fsync = options.fsync;
  421. return Object.assign(target, { writeConcern });
  422. }
  423. if (
  424. coll &&
  425. (coll.writeConcern.w != null || coll.writeConcern.j != null || coll.writeConcern.fsync != null)
  426. ) {
  427. return Object.assign(target, { writeConcern: Object.assign({}, coll.writeConcern) });
  428. }
  429. if (
  430. db &&
  431. (db.writeConcern.w != null || db.writeConcern.j != null || db.writeConcern.fsync != null)
  432. ) {
  433. return Object.assign(target, { writeConcern: Object.assign({}, db.writeConcern) });
  434. }
  435. return target;
  436. }
  437. /**
  438. * Resolves a read preference based on well-defined inheritance rules. This method will not only
  439. * determine the read preference (if there is one), but will also ensure the returned value is a
  440. * properly constructed instance of `ReadPreference`.
  441. *
  442. * @param {Object} options The options passed into the method, potentially containing a read preference
  443. * @param {Object} sources Sources from which we can inherit a read preference
  444. * @returns {(ReadPreference|null)} The resolved read preference
  445. */
  446. function resolveReadPreference(options, sources) {
  447. options = options || {};
  448. sources = sources || {};
  449. const db = sources.db;
  450. const coll = sources.collection;
  451. const defaultReadPreference = sources.default;
  452. const session = options.session;
  453. let readPreference;
  454. if (options.readPreference) {
  455. readPreference = options.readPreference;
  456. } else if (session && session.inTransaction() && session.transaction.options.readPreference) {
  457. // The transaction’s read preference MUST override all other user configurable read preferences.
  458. readPreference = session.transaction.options.readPreference;
  459. } else {
  460. if (coll && coll.s.readPreference) {
  461. readPreference = coll.s.readPreference;
  462. } else if (db && db.s.readPreference) {
  463. readPreference = db.s.readPreference;
  464. } else if (defaultReadPreference) {
  465. readPreference = defaultReadPreference;
  466. }
  467. }
  468. // do we even have a read preference?
  469. if (readPreference == null) {
  470. return null;
  471. }
  472. // now attempt to convert the read preference if necessary
  473. if (typeof readPreference === 'string') {
  474. readPreference = new ReadPreference(readPreference);
  475. } else if (
  476. readPreference &&
  477. !(readPreference instanceof ReadPreference) &&
  478. typeof readPreference === 'object'
  479. ) {
  480. const mode = readPreference.mode || readPreference.preference;
  481. if (mode && typeof mode === 'string') {
  482. readPreference = new ReadPreference(mode, readPreference.tags, {
  483. maxStalenessSeconds: readPreference.maxStalenessSeconds
  484. });
  485. }
  486. } else if (!(readPreference instanceof ReadPreference)) {
  487. throw new TypeError('Invalid read preference: ' + readPreference);
  488. }
  489. return readPreference;
  490. }
  491. /**
  492. * Checks if a given value is a Promise
  493. *
  494. * @param {*} maybePromise
  495. * @return true if the provided value is a Promise
  496. */
  497. function isPromiseLike(maybePromise) {
  498. return maybePromise && typeof maybePromise.then === 'function';
  499. }
  500. /**
  501. * Applies collation to a given command.
  502. *
  503. * @param {object} [command] the command on which to apply collation
  504. * @param {(Cursor|Collection)} [target] target of command
  505. * @param {object} [options] options containing collation settings
  506. */
  507. function decorateWithCollation(command, target, options) {
  508. const topology = target.s && target.s.topology;
  509. if (!topology) {
  510. throw new TypeError('parameter "target" is missing a topology');
  511. }
  512. const capabilities = target.s.topology.capabilities();
  513. if (options.collation && typeof options.collation === 'object') {
  514. if (capabilities && capabilities.commandsTakeCollation) {
  515. command.collation = options.collation;
  516. } else {
  517. throw new MongoError(`server ${topology.s.coreTopology.name} does not support collation`);
  518. }
  519. }
  520. }
  521. /**
  522. * Applies a read concern to a given command.
  523. *
  524. * @param {object} command the command on which to apply the read concern
  525. * @param {Collection} coll the parent collection of the operation calling this method
  526. */
  527. function decorateWithReadConcern(command, coll, options) {
  528. if (options && options.session && options.session.inTransaction()) {
  529. return;
  530. }
  531. let readConcern = Object.assign({}, command.readConcern || {});
  532. if (coll.s.readConcern) {
  533. Object.assign(readConcern, coll.s.readConcern);
  534. }
  535. if (Object.keys(readConcern).length > 0) {
  536. Object.assign(command, { readConcern: readConcern });
  537. }
  538. }
  539. const emitProcessWarning = msg => process.emitWarning(msg, 'DeprecationWarning');
  540. const emitConsoleWarning = msg => console.error(msg);
  541. const emitDeprecationWarning = process.emitWarning ? emitProcessWarning : emitConsoleWarning;
  542. /**
  543. * Default message handler for generating deprecation warnings.
  544. *
  545. * @param {string} name function name
  546. * @param {string} option option name
  547. * @return {string} warning message
  548. * @ignore
  549. * @api private
  550. */
  551. function defaultMsgHandler(name, option) {
  552. return `${name} option [${option}] is deprecated and will be removed in a later version.`;
  553. }
  554. /**
  555. * Deprecates a given function's options.
  556. *
  557. * @param {object} config configuration for deprecation
  558. * @param {string} config.name function name
  559. * @param {Array} config.deprecatedOptions options to deprecate
  560. * @param {number} config.optionsIndex index of options object in function arguments array
  561. * @param {function} [config.msgHandler] optional custom message handler to generate warnings
  562. * @param {function} fn the target function of deprecation
  563. * @return {function} modified function that warns once per deprecated option, and executes original function
  564. * @ignore
  565. * @api private
  566. */
  567. function deprecateOptions(config, fn) {
  568. if (process.noDeprecation === true) {
  569. return fn;
  570. }
  571. const msgHandler = config.msgHandler ? config.msgHandler : defaultMsgHandler;
  572. const optionsWarned = new Set();
  573. function deprecated() {
  574. const options = arguments[config.optionsIndex];
  575. // ensure options is a valid, non-empty object, otherwise short-circuit
  576. if (!isObject(options) || Object.keys(options).length === 0) {
  577. return fn.apply(this, arguments);
  578. }
  579. config.deprecatedOptions.forEach(deprecatedOption => {
  580. if (options.hasOwnProperty(deprecatedOption) && !optionsWarned.has(deprecatedOption)) {
  581. optionsWarned.add(deprecatedOption);
  582. const msg = msgHandler(config.name, deprecatedOption);
  583. emitDeprecationWarning(msg);
  584. if (this && this.getLogger) {
  585. const logger = this.getLogger();
  586. if (logger) {
  587. logger.warn(msg);
  588. }
  589. }
  590. }
  591. });
  592. return fn.apply(this, arguments);
  593. }
  594. // These lines copied from https://github.com/nodejs/node/blob/25e5ae41688676a5fd29b2e2e7602168eee4ceb5/lib/internal/util.js#L73-L80
  595. // The wrapper will keep the same prototype as fn to maintain prototype chain
  596. Object.setPrototypeOf(deprecated, fn);
  597. if (fn.prototype) {
  598. // Setting this (rather than using Object.setPrototype, as above) ensures
  599. // that calling the unwrapped constructor gives an instanceof the wrapped
  600. // constructor.
  601. deprecated.prototype = fn.prototype;
  602. }
  603. return deprecated;
  604. }
  605. module.exports = {
  606. filterOptions,
  607. mergeOptions,
  608. translateOptions,
  609. shallowClone,
  610. getSingleProperty,
  611. checkCollectionName,
  612. toError,
  613. formattedOrderClause,
  614. parseIndexOptions,
  615. normalizeHintField,
  616. handleCallback,
  617. decorateCommand,
  618. isObject,
  619. debugOptions,
  620. MAX_JS_INT: Number.MAX_SAFE_INTEGER + 1,
  621. mergeOptionsAndWriteConcern,
  622. translateReadPreference,
  623. executeOperation,
  624. applyRetryableWrites,
  625. applyWriteConcern,
  626. resolveReadPreference,
  627. isPromiseLike,
  628. decorateWithCollation,
  629. decorateWithReadConcern,
  630. deprecateOptions
  631. };