utils.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  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 collection 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. */
  313. const executeOperation = (topology, operation, args, options) => {
  314. if (topology == null) {
  315. throw new TypeError('This method requires a valid topology instance');
  316. }
  317. if (!Array.isArray(args)) {
  318. throw new TypeError('This method requires an array of arguments to apply');
  319. }
  320. options = options || {};
  321. const Promise = topology.s.promiseLibrary;
  322. let callback = args[args.length - 1];
  323. // The driver sessions spec mandates that we implicitly create sessions for operations
  324. // that are not explicitly provided with a session.
  325. let session, opOptions, owner;
  326. if (!options.skipSessions && topology.hasSessionSupport()) {
  327. opOptions = args[args.length - 2];
  328. if (opOptions == null || opOptions.session == null) {
  329. owner = Symbol();
  330. session = topology.startSession({ owner });
  331. const optionsIndex = args.length - 2;
  332. args[optionsIndex] = Object.assign({}, args[optionsIndex], { session: session });
  333. } else if (opOptions.session && opOptions.session.hasEnded) {
  334. throw new MongoError('Use of expired sessions is not permitted');
  335. }
  336. }
  337. const makeExecuteCallback = (resolve, reject) =>
  338. function executeCallback(err, result) {
  339. if (session && session.owner === owner && !options.returnsCursor) {
  340. session.endSession(() => {
  341. delete opOptions.session;
  342. if (err) return reject(err);
  343. resolve(result);
  344. });
  345. } else {
  346. if (err) return reject(err);
  347. resolve(result);
  348. }
  349. };
  350. // Execute using callback
  351. if (typeof callback === 'function') {
  352. callback = args.pop();
  353. const handler = makeExecuteCallback(
  354. result => callback(null, result),
  355. err => callback(err, null)
  356. );
  357. args.push(handler);
  358. try {
  359. return operation.apply(null, args);
  360. } catch (e) {
  361. handler(e);
  362. throw e;
  363. }
  364. }
  365. // Return a Promise
  366. if (args[args.length - 1] != null) {
  367. throw new TypeError('final argument to `executeOperation` must be a callback');
  368. }
  369. return new Promise(function(resolve, reject) {
  370. const handler = makeExecuteCallback(resolve, reject);
  371. args[args.length - 1] = handler;
  372. try {
  373. return operation.apply(null, args);
  374. } catch (e) {
  375. handler(e);
  376. }
  377. });
  378. };
  379. /**
  380. * Applies retryWrites: true to a command if retryWrites is set on the command's database.
  381. *
  382. * @param {object} target The target command to which we will apply retryWrites.
  383. * @param {object} db The database from which we can inherit a retryWrites value.
  384. */
  385. function applyRetryableWrites(target, db) {
  386. if (db && db.s.options.retryWrites) {
  387. target.retryWrites = true;
  388. }
  389. return target;
  390. }
  391. /**
  392. * Applies a write concern to a command based on well defined inheritance rules, optionally
  393. * detecting support for the write concern in the first place.
  394. *
  395. * @param {Object} target the target command we will be applying the write concern to
  396. * @param {Object} sources sources where we can inherit default write concerns from
  397. * @param {Object} [options] optional settings passed into a command for write concern overrides
  398. * @returns {Object} the (now) decorated target
  399. */
  400. function applyWriteConcern(target, sources, options) {
  401. options = options || {};
  402. const db = sources.db;
  403. const coll = sources.collection;
  404. if (options.session && options.session.inTransaction()) {
  405. // writeConcern is not allowed within a multi-statement transaction
  406. if (target.writeConcern) {
  407. delete target.writeConcern;
  408. }
  409. return target;
  410. }
  411. if (options.w != null || options.j != null || options.fsync != null) {
  412. const writeConcern = {};
  413. if (options.w != null) writeConcern.w = options.w;
  414. if (options.wtimeout != null) writeConcern.wtimeout = options.wtimeout;
  415. if (options.j != null) writeConcern.j = options.j;
  416. if (options.fsync != null) writeConcern.fsync = options.fsync;
  417. return Object.assign(target, { writeConcern });
  418. }
  419. if (
  420. coll &&
  421. (coll.writeConcern.w != null || coll.writeConcern.j != null || coll.writeConcern.fsync != null)
  422. ) {
  423. return Object.assign(target, { writeConcern: Object.assign({}, coll.writeConcern) });
  424. }
  425. if (
  426. db &&
  427. (db.writeConcern.w != null || db.writeConcern.j != null || db.writeConcern.fsync != null)
  428. ) {
  429. return Object.assign(target, { writeConcern: Object.assign({}, db.writeConcern) });
  430. }
  431. return target;
  432. }
  433. /**
  434. * Resolves a read preference based on well-defined inheritance rules. This method will not only
  435. * determine the read preference (if there is one), but will also ensure the returned value is a
  436. * properly constructed instance of `ReadPreference`.
  437. *
  438. * @param {Object} options The options passed into the method, potentially containing a read preference
  439. * @param {Object} sources Sources from which we can inherit a read preference
  440. * @returns {(ReadPreference|null)} The resolved read preference
  441. */
  442. function resolveReadPreference(options, sources) {
  443. options = options || {};
  444. sources = sources || {};
  445. const db = sources.db;
  446. const coll = sources.collection;
  447. const defaultReadPreference = sources.default;
  448. const session = options.session;
  449. let readPreference;
  450. if (options.readPreference) {
  451. readPreference = options.readPreference;
  452. } else if (session && session.inTransaction() && session.transaction.options.readPreference) {
  453. // The transaction’s read preference MUST override all other user configurable read preferences.
  454. readPreference = session.transaction.options.readPreference;
  455. } else {
  456. if (coll && coll.s.readPreference) {
  457. readPreference = coll.s.readPreference;
  458. } else if (db && db.s.readPreference) {
  459. readPreference = db.s.readPreference;
  460. } else if (defaultReadPreference) {
  461. readPreference = defaultReadPreference;
  462. }
  463. }
  464. // do we even have a read preference?
  465. if (readPreference == null) {
  466. return null;
  467. }
  468. // now attempt to convert the read preference if necessary
  469. if (typeof readPreference === 'string') {
  470. readPreference = new ReadPreference(readPreference);
  471. } else if (
  472. readPreference &&
  473. !(readPreference instanceof ReadPreference) &&
  474. typeof readPreference === 'object'
  475. ) {
  476. const mode = readPreference.mode || readPreference.preference;
  477. if (mode && typeof mode === 'string') {
  478. readPreference = new ReadPreference(mode, readPreference.tags, {
  479. maxStalenessSeconds: readPreference.maxStalenessSeconds
  480. });
  481. }
  482. } else if (!(readPreference instanceof ReadPreference)) {
  483. throw new TypeError('Invalid read preference: ' + readPreference);
  484. }
  485. return readPreference;
  486. }
  487. /**
  488. * Checks if a given value is a Promise
  489. *
  490. * @param {*} maybePromise
  491. * @return true if the provided value is a Promise
  492. */
  493. function isPromiseLike(maybePromise) {
  494. return maybePromise && typeof maybePromise.then === 'function';
  495. }
  496. /**
  497. * Applies collation to a given command.
  498. *
  499. * @param {object} [command] the command on which to apply collation
  500. * @param {(Cursor|Collection)} [target] target of command
  501. * @param {object} [options] options containing collation settings
  502. */
  503. function decorateWithCollation(command, target, options) {
  504. const topology = target.s && target.s.topology;
  505. if (!topology) {
  506. throw new TypeError('parameter "target" is missing a topology');
  507. }
  508. const capabilities = topology.capabilities();
  509. if (options.collation && typeof options.collation === 'object') {
  510. if (capabilities && capabilities.commandsTakeCollation) {
  511. command.collation = options.collation;
  512. } else {
  513. throw new MongoError(`Current topology does not support collation`);
  514. }
  515. }
  516. }
  517. /**
  518. * Applies a read concern to a given command.
  519. *
  520. * @param {object} command the command on which to apply the read concern
  521. * @param {Collection} coll the parent collection of the operation calling this method
  522. */
  523. function decorateWithReadConcern(command, coll, options) {
  524. if (options && options.session && options.session.inTransaction()) {
  525. return;
  526. }
  527. let readConcern = Object.assign({}, command.readConcern || {});
  528. if (coll.s.readConcern) {
  529. Object.assign(readConcern, coll.s.readConcern);
  530. }
  531. if (Object.keys(readConcern).length > 0) {
  532. Object.assign(command, { readConcern: readConcern });
  533. }
  534. }
  535. const emitProcessWarning = msg => process.emitWarning(msg, 'DeprecationWarning');
  536. const emitConsoleWarning = msg => console.error(msg);
  537. const emitDeprecationWarning = process.emitWarning ? emitProcessWarning : emitConsoleWarning;
  538. /**
  539. * Default message handler for generating deprecation warnings.
  540. *
  541. * @param {string} name function name
  542. * @param {string} option option name
  543. * @return {string} warning message
  544. * @ignore
  545. * @api private
  546. */
  547. function defaultMsgHandler(name, option) {
  548. return `${name} option [${option}] is deprecated and will be removed in a later version.`;
  549. }
  550. /**
  551. * Deprecates a given function's options.
  552. *
  553. * @param {object} config configuration for deprecation
  554. * @param {string} config.name function name
  555. * @param {Array} config.deprecatedOptions options to deprecate
  556. * @param {number} config.optionsIndex index of options object in function arguments array
  557. * @param {function} [config.msgHandler] optional custom message handler to generate warnings
  558. * @param {function} fn the target function of deprecation
  559. * @return {function} modified function that warns once per deprecated option, and executes original function
  560. * @ignore
  561. * @api private
  562. */
  563. function deprecateOptions(config, fn) {
  564. if (process.noDeprecation === true) {
  565. return fn;
  566. }
  567. const msgHandler = config.msgHandler ? config.msgHandler : defaultMsgHandler;
  568. const optionsWarned = new Set();
  569. function deprecated() {
  570. const options = arguments[config.optionsIndex];
  571. // ensure options is a valid, non-empty object, otherwise short-circuit
  572. if (!isObject(options) || Object.keys(options).length === 0) {
  573. return fn.apply(this, arguments);
  574. }
  575. config.deprecatedOptions.forEach(deprecatedOption => {
  576. if (options.hasOwnProperty(deprecatedOption) && !optionsWarned.has(deprecatedOption)) {
  577. optionsWarned.add(deprecatedOption);
  578. const msg = msgHandler(config.name, deprecatedOption);
  579. emitDeprecationWarning(msg);
  580. if (this && this.getLogger) {
  581. const logger = this.getLogger();
  582. if (logger) {
  583. logger.warn(msg);
  584. }
  585. }
  586. }
  587. });
  588. return fn.apply(this, arguments);
  589. }
  590. // These lines copied from https://github.com/nodejs/node/blob/25e5ae41688676a5fd29b2e2e7602168eee4ceb5/lib/internal/util.js#L73-L80
  591. // The wrapper will keep the same prototype as fn to maintain prototype chain
  592. Object.setPrototypeOf(deprecated, fn);
  593. if (fn.prototype) {
  594. // Setting this (rather than using Object.setPrototype, as above) ensures
  595. // that calling the unwrapped constructor gives an instanceof the wrapped
  596. // constructor.
  597. deprecated.prototype = fn.prototype;
  598. }
  599. return deprecated;
  600. }
  601. const SUPPORTS = {};
  602. // Test asyncIterator support
  603. try {
  604. require('./async/async_iterator');
  605. SUPPORTS.ASYNC_ITERATOR = true;
  606. } catch (e) {
  607. SUPPORTS.ASYNC_ITERATOR = false;
  608. }
  609. module.exports = {
  610. filterOptions,
  611. mergeOptions,
  612. translateOptions,
  613. shallowClone,
  614. getSingleProperty,
  615. checkCollectionName,
  616. toError,
  617. formattedOrderClause,
  618. parseIndexOptions,
  619. normalizeHintField,
  620. handleCallback,
  621. decorateCommand,
  622. isObject,
  623. debugOptions,
  624. MAX_JS_INT: Number.MAX_SAFE_INTEGER + 1,
  625. mergeOptionsAndWriteConcern,
  626. translateReadPreference,
  627. executeOperation,
  628. applyRetryableWrites,
  629. applyWriteConcern,
  630. resolveReadPreference,
  631. isPromiseLike,
  632. decorateWithCollation,
  633. decorateWithReadConcern,
  634. deprecateOptions,
  635. SUPPORTS
  636. };