utils.js 21 KB

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