utils.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const ms = require('ms');
  6. const mpath = require('mpath');
  7. const sliced = require('sliced');
  8. const Buffer = require('safe-buffer').Buffer;
  9. const Decimal = require('./types/decimal128');
  10. const ObjectId = require('./types/objectid');
  11. const PopulateOptions = require('./options/PopulateOptions');
  12. const clone = require('./helpers/clone');
  13. const isObject = require('./helpers/isObject');
  14. const isBsonType = require('./helpers/isBsonType');
  15. const getFunctionName = require('./helpers/getFunctionName');
  16. const isMongooseObject = require('./helpers/isMongooseObject');
  17. const promiseOrCallback = require('./helpers/promiseOrCallback');
  18. const schemaMerge = require('./helpers/schema/merge');
  19. const specialProperties = require('./helpers/specialProperties');
  20. let Document;
  21. exports.specialProperties = specialProperties;
  22. /*!
  23. * Produces a collection name from model `name`. By default, just returns
  24. * the model name
  25. *
  26. * @param {String} name a model name
  27. * @param {Function} pluralize function that pluralizes the collection name
  28. * @return {String} a collection name
  29. * @api private
  30. */
  31. exports.toCollectionName = function(name, pluralize) {
  32. if (name === 'system.profile') {
  33. return name;
  34. }
  35. if (name === 'system.indexes') {
  36. return name;
  37. }
  38. if (typeof pluralize === 'function') {
  39. return pluralize(name);
  40. }
  41. return name;
  42. };
  43. /*!
  44. * Determines if `a` and `b` are deep equal.
  45. *
  46. * Modified from node/lib/assert.js
  47. *
  48. * @param {any} a a value to compare to `b`
  49. * @param {any} b a value to compare to `a`
  50. * @return {Boolean}
  51. * @api private
  52. */
  53. exports.deepEqual = function deepEqual(a, b) {
  54. if (a === b) {
  55. return true;
  56. }
  57. if (typeof a !== 'object' && typeof b !== 'object') {
  58. return a === b;
  59. }
  60. if (a instanceof Date && b instanceof Date) {
  61. return a.getTime() === b.getTime();
  62. }
  63. if ((isBsonType(a, 'ObjectID') && isBsonType(b, 'ObjectID')) ||
  64. (isBsonType(a, 'Decimal128') && isBsonType(b, 'Decimal128'))) {
  65. return a.toString() === b.toString();
  66. }
  67. if (a instanceof RegExp && b instanceof RegExp) {
  68. return a.source === b.source &&
  69. a.ignoreCase === b.ignoreCase &&
  70. a.multiline === b.multiline &&
  71. a.global === b.global;
  72. }
  73. if (a == null || b == null) {
  74. return false;
  75. }
  76. if (a.prototype !== b.prototype) {
  77. return false;
  78. }
  79. if (a instanceof Map && b instanceof Map) {
  80. return deepEqual(Array.from(a.keys()), Array.from(b.keys())) &&
  81. deepEqual(Array.from(a.values()), Array.from(b.values()));
  82. }
  83. // Handle MongooseNumbers
  84. if (a instanceof Number && b instanceof Number) {
  85. return a.valueOf() === b.valueOf();
  86. }
  87. if (Buffer.isBuffer(a)) {
  88. return exports.buffer.areEqual(a, b);
  89. }
  90. if (Array.isArray(a) && Array.isArray(b)) {
  91. const len = a.length;
  92. if (len !== b.length) {
  93. return false;
  94. }
  95. for (let i = 0; i < len; ++i) {
  96. if (!deepEqual(a[i], b[i])) {
  97. return false;
  98. }
  99. }
  100. return true;
  101. }
  102. if (a.$__ != null) {
  103. a = a._doc;
  104. } else if (isMongooseObject(a)) {
  105. a = a.toObject();
  106. }
  107. if (b.$__ != null) {
  108. b = b._doc;
  109. } else if (isMongooseObject(b)) {
  110. b = b.toObject();
  111. }
  112. const ka = Object.keys(a);
  113. const kb = Object.keys(b);
  114. const kaLength = ka.length;
  115. // having the same number of owned properties (keys incorporates
  116. // hasOwnProperty)
  117. if (kaLength !== kb.length) {
  118. return false;
  119. }
  120. // the same set of keys (although not necessarily the same order),
  121. ka.sort();
  122. kb.sort();
  123. // ~~~cheap key test
  124. for (let i = kaLength - 1; i >= 0; i--) {
  125. if (ka[i] !== kb[i]) {
  126. return false;
  127. }
  128. }
  129. // equivalent values for every corresponding key, and
  130. // ~~~possibly expensive deep test
  131. for (const key of ka) {
  132. if (!deepEqual(a[key], b[key])) {
  133. return false;
  134. }
  135. }
  136. return true;
  137. };
  138. /*!
  139. * Get the last element of an array
  140. */
  141. exports.last = function(arr) {
  142. if (arr.length > 0) {
  143. return arr[arr.length - 1];
  144. }
  145. return void 0;
  146. };
  147. exports.clone = clone;
  148. /*!
  149. * ignore
  150. */
  151. exports.promiseOrCallback = promiseOrCallback;
  152. /*!
  153. * ignore
  154. */
  155. exports.omit = function omit(obj, keys) {
  156. if (keys == null) {
  157. return Object.assign({}, obj);
  158. }
  159. if (!Array.isArray(keys)) {
  160. keys = [keys];
  161. }
  162. const ret = Object.assign({}, obj);
  163. for (const key of keys) {
  164. delete ret[key];
  165. }
  166. return ret;
  167. };
  168. /*!
  169. * Shallow copies defaults into options.
  170. *
  171. * @param {Object} defaults
  172. * @param {Object} options
  173. * @return {Object} the merged object
  174. * @api private
  175. */
  176. exports.options = function(defaults, options) {
  177. const keys = Object.keys(defaults);
  178. let i = keys.length;
  179. let k;
  180. options = options || {};
  181. while (i--) {
  182. k = keys[i];
  183. if (!(k in options)) {
  184. options[k] = defaults[k];
  185. }
  186. }
  187. return options;
  188. };
  189. /*!
  190. * Generates a random string
  191. *
  192. * @api private
  193. */
  194. exports.random = function() {
  195. return Math.random().toString().substr(3);
  196. };
  197. /*!
  198. * Merges `from` into `to` without overwriting existing properties.
  199. *
  200. * @param {Object} to
  201. * @param {Object} from
  202. * @api private
  203. */
  204. exports.merge = function merge(to, from, options, path) {
  205. options = options || {};
  206. const keys = Object.keys(from);
  207. let i = 0;
  208. const len = keys.length;
  209. let key;
  210. path = path || '';
  211. const omitNested = options.omitNested || {};
  212. while (i < len) {
  213. key = keys[i++];
  214. if (options.omit && options.omit[key]) {
  215. continue;
  216. }
  217. if (omitNested[path]) {
  218. continue;
  219. }
  220. if (specialProperties.has(key)) {
  221. continue;
  222. }
  223. if (to[key] == null) {
  224. to[key] = from[key];
  225. } else if (exports.isObject(from[key])) {
  226. if (!exports.isObject(to[key])) {
  227. to[key] = {};
  228. }
  229. if (from[key] != null) {
  230. // Skip merging schemas if we're creating a discriminator schema and
  231. // base schema has a given path as a single nested but discriminator schema
  232. // has the path as a document array, or vice versa (gh-9534)
  233. if (options.isDiscriminatorSchemaMerge &&
  234. (from[key].$isSingleNested && to[key].$isMongooseDocumentArray) ||
  235. (from[key].$isMongooseDocumentArray && to[key].$isSingleNested)) {
  236. continue;
  237. } else if (from[key].instanceOfSchema) {
  238. if (to[key].instanceOfSchema) {
  239. schemaMerge(to[key], from[key].clone(), options.isDiscriminatorSchemaMerge);
  240. } else {
  241. to[key] = from[key].clone();
  242. }
  243. continue;
  244. } else if (from[key] instanceof ObjectId) {
  245. to[key] = new ObjectId(from[key]);
  246. continue;
  247. }
  248. }
  249. merge(to[key], from[key], options, path ? path + '.' + key : key);
  250. } else if (options.overwrite) {
  251. to[key] = from[key];
  252. }
  253. }
  254. };
  255. /*!
  256. * Applies toObject recursively.
  257. *
  258. * @param {Document|Array|Object} obj
  259. * @return {Object}
  260. * @api private
  261. */
  262. exports.toObject = function toObject(obj) {
  263. Document || (Document = require('./document'));
  264. let ret;
  265. if (obj == null) {
  266. return obj;
  267. }
  268. if (obj instanceof Document) {
  269. return obj.toObject();
  270. }
  271. if (Array.isArray(obj)) {
  272. ret = [];
  273. for (const doc of obj) {
  274. ret.push(toObject(doc));
  275. }
  276. return ret;
  277. }
  278. if (exports.isPOJO(obj)) {
  279. ret = {};
  280. for (const k of Object.keys(obj)) {
  281. if (specialProperties.has(k)) {
  282. continue;
  283. }
  284. ret[k] = toObject(obj[k]);
  285. }
  286. return ret;
  287. }
  288. return obj;
  289. };
  290. exports.isObject = isObject;
  291. /*!
  292. * Determines if `arg` is a plain old JavaScript object (POJO). Specifically,
  293. * `arg` must be an object but not an instance of any special class, like String,
  294. * ObjectId, etc.
  295. *
  296. * `Object.getPrototypeOf()` is part of ES5: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
  297. *
  298. * @param {Object|Array|String|Function|RegExp|any} arg
  299. * @api private
  300. * @return {Boolean}
  301. */
  302. exports.isPOJO = function isPOJO(arg) {
  303. if (arg == null || typeof arg !== 'object') {
  304. return false;
  305. }
  306. const proto = Object.getPrototypeOf(arg);
  307. // Prototype may be null if you used `Object.create(null)`
  308. // Checking `proto`'s constructor is safe because `getPrototypeOf()`
  309. // explicitly crosses the boundary from object data to object metadata
  310. return !proto || proto.constructor.name === 'Object';
  311. };
  312. /*!
  313. * Determines if `obj` is a built-in object like an array, date, boolean,
  314. * etc.
  315. */
  316. exports.isNativeObject = function(arg) {
  317. return Array.isArray(arg) ||
  318. arg instanceof Date ||
  319. arg instanceof Boolean ||
  320. arg instanceof Number ||
  321. arg instanceof String;
  322. };
  323. /*!
  324. * Determines if `val` is an object that has no own keys
  325. */
  326. exports.isEmptyObject = function(val) {
  327. return val != null &&
  328. typeof val === 'object' &&
  329. Object.keys(val).length === 0;
  330. };
  331. /*!
  332. * Search if `obj` or any POJOs nested underneath `obj` has a property named
  333. * `key`
  334. */
  335. exports.hasKey = function hasKey(obj, key) {
  336. const props = Object.keys(obj);
  337. for (const prop of props) {
  338. if (prop === key) {
  339. return true;
  340. }
  341. if (exports.isPOJO(obj[prop]) && exports.hasKey(obj[prop], key)) {
  342. return true;
  343. }
  344. }
  345. return false;
  346. };
  347. /*!
  348. * A faster Array.prototype.slice.call(arguments) alternative
  349. * @api private
  350. */
  351. exports.args = sliced;
  352. /*!
  353. * process.nextTick helper.
  354. *
  355. * Wraps `callback` in a try/catch + nextTick.
  356. *
  357. * node-mongodb-native has a habit of state corruption when an error is immediately thrown from within a collection callback.
  358. *
  359. * @param {Function} callback
  360. * @api private
  361. */
  362. exports.tick = function tick(callback) {
  363. if (typeof callback !== 'function') {
  364. return;
  365. }
  366. return function() {
  367. try {
  368. callback.apply(this, arguments);
  369. } catch (err) {
  370. // only nextTick on err to get out of
  371. // the event loop and avoid state corruption.
  372. process.nextTick(function() {
  373. throw err;
  374. });
  375. }
  376. };
  377. };
  378. /*!
  379. * Returns true if `v` is an object that can be serialized as a primitive in
  380. * MongoDB
  381. */
  382. exports.isMongooseType = function(v) {
  383. return v instanceof ObjectId || v instanceof Decimal || v instanceof Buffer;
  384. };
  385. exports.isMongooseObject = isMongooseObject;
  386. /*!
  387. * Converts `expires` options of index objects to `expiresAfterSeconds` options for MongoDB.
  388. *
  389. * @param {Object} object
  390. * @api private
  391. */
  392. exports.expires = function expires(object) {
  393. if (!(object && object.constructor.name === 'Object')) {
  394. return;
  395. }
  396. if (!('expires' in object)) {
  397. return;
  398. }
  399. let when;
  400. if (typeof object.expires !== 'string') {
  401. when = object.expires;
  402. } else {
  403. when = Math.round(ms(object.expires) / 1000);
  404. }
  405. object.expireAfterSeconds = when;
  406. delete object.expires;
  407. };
  408. /*!
  409. * populate helper
  410. */
  411. exports.populate = function populate(path, select, model, match, options, subPopulate, justOne, count) {
  412. // might have passed an object specifying all arguments
  413. let obj = null;
  414. if (arguments.length === 1) {
  415. if (path instanceof PopulateOptions) {
  416. return [path];
  417. }
  418. if (Array.isArray(path)) {
  419. const singles = makeSingles(path);
  420. return singles.map(o => exports.populate(o)[0]);
  421. }
  422. if (exports.isObject(path)) {
  423. obj = Object.assign({}, path);
  424. } else {
  425. obj = { path: path };
  426. }
  427. } else if (typeof model === 'object') {
  428. obj = {
  429. path: path,
  430. select: select,
  431. match: model,
  432. options: match
  433. };
  434. } else {
  435. obj = {
  436. path: path,
  437. select: select,
  438. model: model,
  439. match: match,
  440. options: options,
  441. populate: subPopulate,
  442. justOne: justOne,
  443. count: count
  444. };
  445. }
  446. if (typeof obj.path !== 'string') {
  447. throw new TypeError('utils.populate: invalid path. Expected string. Got typeof `' + typeof path + '`');
  448. }
  449. return _populateObj(obj);
  450. // The order of select/conditions args is opposite Model.find but
  451. // necessary to keep backward compatibility (select could be
  452. // an array, string, or object literal).
  453. function makeSingles(arr) {
  454. const ret = [];
  455. arr.forEach(function(obj) {
  456. if (/[\s]/.test(obj.path)) {
  457. const paths = obj.path.split(' ');
  458. paths.forEach(function(p) {
  459. const copy = Object.assign({}, obj);
  460. copy.path = p;
  461. ret.push(copy);
  462. });
  463. } else {
  464. ret.push(obj);
  465. }
  466. });
  467. return ret;
  468. }
  469. };
  470. function _populateObj(obj) {
  471. if (Array.isArray(obj.populate)) {
  472. const ret = [];
  473. obj.populate.forEach(function(obj) {
  474. if (/[\s]/.test(obj.path)) {
  475. const copy = Object.assign({}, obj);
  476. const paths = copy.path.split(' ');
  477. paths.forEach(function(p) {
  478. copy.path = p;
  479. ret.push(exports.populate(copy)[0]);
  480. });
  481. } else {
  482. ret.push(exports.populate(obj)[0]);
  483. }
  484. });
  485. obj.populate = exports.populate(ret);
  486. } else if (obj.populate != null && typeof obj.populate === 'object') {
  487. obj.populate = exports.populate(obj.populate);
  488. }
  489. const ret = [];
  490. const paths = obj.path.split(' ');
  491. if (obj.options != null) {
  492. obj.options = exports.clone(obj.options);
  493. }
  494. for (const path of paths) {
  495. ret.push(new PopulateOptions(Object.assign({}, obj, { path: path })));
  496. }
  497. return ret;
  498. }
  499. /*!
  500. * Return the value of `obj` at the given `path`.
  501. *
  502. * @param {String} path
  503. * @param {Object} obj
  504. */
  505. exports.getValue = function(path, obj, map) {
  506. return mpath.get(path, obj, '_doc', map);
  507. };
  508. /*!
  509. * Sets the value of `obj` at the given `path`.
  510. *
  511. * @param {String} path
  512. * @param {Anything} val
  513. * @param {Object} obj
  514. */
  515. exports.setValue = function(path, val, obj, map, _copying) {
  516. mpath.set(path, val, obj, '_doc', map, _copying);
  517. };
  518. /*!
  519. * Returns an array of values from object `o`.
  520. *
  521. * @param {Object} o
  522. * @return {Array}
  523. * @private
  524. */
  525. exports.object = {};
  526. exports.object.vals = function vals(o) {
  527. const keys = Object.keys(o);
  528. let i = keys.length;
  529. const ret = [];
  530. while (i--) {
  531. ret.push(o[keys[i]]);
  532. }
  533. return ret;
  534. };
  535. /*!
  536. * @see exports.options
  537. */
  538. exports.object.shallowCopy = exports.options;
  539. /*!
  540. * Safer helper for hasOwnProperty checks
  541. *
  542. * @param {Object} obj
  543. * @param {String} prop
  544. */
  545. const hop = Object.prototype.hasOwnProperty;
  546. exports.object.hasOwnProperty = function(obj, prop) {
  547. return hop.call(obj, prop);
  548. };
  549. /*!
  550. * Determine if `val` is null or undefined
  551. *
  552. * @return {Boolean}
  553. */
  554. exports.isNullOrUndefined = function(val) {
  555. return val === null || val === undefined;
  556. };
  557. /*!
  558. * ignore
  559. */
  560. exports.array = {};
  561. /*!
  562. * Flattens an array.
  563. *
  564. * [ 1, [ 2, 3, [4] ]] -> [1,2,3,4]
  565. *
  566. * @param {Array} arr
  567. * @param {Function} [filter] If passed, will be invoked with each item in the array. If `filter` returns a falsy value, the item will not be included in the results.
  568. * @return {Array}
  569. * @private
  570. */
  571. exports.array.flatten = function flatten(arr, filter, ret) {
  572. ret || (ret = []);
  573. arr.forEach(function(item) {
  574. if (Array.isArray(item)) {
  575. flatten(item, filter, ret);
  576. } else {
  577. if (!filter || filter(item)) {
  578. ret.push(item);
  579. }
  580. }
  581. });
  582. return ret;
  583. };
  584. /*!
  585. * ignore
  586. */
  587. const _hasOwnProperty = Object.prototype.hasOwnProperty;
  588. exports.hasUserDefinedProperty = function(obj, key) {
  589. if (obj == null) {
  590. return false;
  591. }
  592. if (Array.isArray(key)) {
  593. for (const k of key) {
  594. if (exports.hasUserDefinedProperty(obj, k)) {
  595. return true;
  596. }
  597. }
  598. return false;
  599. }
  600. if (_hasOwnProperty.call(obj, key)) {
  601. return true;
  602. }
  603. if (typeof obj === 'object' && key in obj) {
  604. const v = obj[key];
  605. return v !== Object.prototype[key] && v !== Array.prototype[key];
  606. }
  607. return false;
  608. };
  609. /*!
  610. * ignore
  611. */
  612. const MAX_ARRAY_INDEX = Math.pow(2, 32) - 1;
  613. exports.isArrayIndex = function(val) {
  614. if (typeof val === 'number') {
  615. return val >= 0 && val <= MAX_ARRAY_INDEX;
  616. }
  617. if (typeof val === 'string') {
  618. if (!/^\d+$/.test(val)) {
  619. return false;
  620. }
  621. val = +val;
  622. return val >= 0 && val <= MAX_ARRAY_INDEX;
  623. }
  624. return false;
  625. };
  626. /*!
  627. * Removes duplicate values from an array
  628. *
  629. * [1, 2, 3, 3, 5] => [1, 2, 3, 5]
  630. * [ ObjectId("550988ba0c19d57f697dc45e"), ObjectId("550988ba0c19d57f697dc45e") ]
  631. * => [ObjectId("550988ba0c19d57f697dc45e")]
  632. *
  633. * @param {Array} arr
  634. * @return {Array}
  635. * @private
  636. */
  637. exports.array.unique = function(arr) {
  638. const primitives = new Set();
  639. const ids = new Set();
  640. const ret = [];
  641. for (const item of arr) {
  642. if (typeof item === 'number' || typeof item === 'string' || item == null) {
  643. if (primitives.has(item)) {
  644. continue;
  645. }
  646. ret.push(item);
  647. primitives.add(item);
  648. } else if (item instanceof ObjectId) {
  649. if (ids.has(item.toString())) {
  650. continue;
  651. }
  652. ret.push(item);
  653. ids.add(item.toString());
  654. } else {
  655. ret.push(item);
  656. }
  657. }
  658. return ret;
  659. };
  660. /*!
  661. * Determines if two buffers are equal.
  662. *
  663. * @param {Buffer} a
  664. * @param {Object} b
  665. */
  666. exports.buffer = {};
  667. exports.buffer.areEqual = function(a, b) {
  668. if (!Buffer.isBuffer(a)) {
  669. return false;
  670. }
  671. if (!Buffer.isBuffer(b)) {
  672. return false;
  673. }
  674. if (a.length !== b.length) {
  675. return false;
  676. }
  677. for (let i = 0, len = a.length; i < len; ++i) {
  678. if (a[i] !== b[i]) {
  679. return false;
  680. }
  681. }
  682. return true;
  683. };
  684. exports.getFunctionName = getFunctionName;
  685. /*!
  686. * Decorate buffers
  687. */
  688. exports.decorate = function(destination, source) {
  689. for (const key in source) {
  690. if (specialProperties.has(key)) {
  691. continue;
  692. }
  693. destination[key] = source[key];
  694. }
  695. };
  696. /**
  697. * merges to with a copy of from
  698. *
  699. * @param {Object} to
  700. * @param {Object} fromObj
  701. * @api private
  702. */
  703. exports.mergeClone = function(to, fromObj) {
  704. if (isMongooseObject(fromObj)) {
  705. fromObj = fromObj.toObject({
  706. transform: false,
  707. virtuals: false,
  708. depopulate: true,
  709. getters: false,
  710. flattenDecimals: false
  711. });
  712. }
  713. const keys = Object.keys(fromObj);
  714. const len = keys.length;
  715. let i = 0;
  716. let key;
  717. while (i < len) {
  718. key = keys[i++];
  719. if (specialProperties.has(key)) {
  720. continue;
  721. }
  722. if (typeof to[key] === 'undefined') {
  723. to[key] = exports.clone(fromObj[key], {
  724. transform: false,
  725. virtuals: false,
  726. depopulate: true,
  727. getters: false,
  728. flattenDecimals: false
  729. });
  730. } else {
  731. let val = fromObj[key];
  732. if (val != null && val.valueOf && !(val instanceof Date)) {
  733. val = val.valueOf();
  734. }
  735. if (exports.isObject(val)) {
  736. let obj = val;
  737. if (isMongooseObject(val) && !val.isMongooseBuffer) {
  738. obj = obj.toObject({
  739. transform: false,
  740. virtuals: false,
  741. depopulate: true,
  742. getters: false,
  743. flattenDecimals: false
  744. });
  745. }
  746. if (val.isMongooseBuffer) {
  747. obj = Buffer.from(obj);
  748. }
  749. exports.mergeClone(to[key], obj);
  750. } else {
  751. to[key] = exports.clone(val, {
  752. flattenDecimals: false
  753. });
  754. }
  755. }
  756. }
  757. };
  758. /**
  759. * Executes a function on each element of an array (like _.each)
  760. *
  761. * @param {Array} arr
  762. * @param {Function} fn
  763. * @api private
  764. */
  765. exports.each = function(arr, fn) {
  766. for (const item of arr) {
  767. fn(item);
  768. }
  769. };
  770. /*!
  771. * ignore
  772. */
  773. exports.getOption = function(name) {
  774. const sources = Array.prototype.slice.call(arguments, 1);
  775. for (const source of sources) {
  776. if (source[name] != null) {
  777. return source[name];
  778. }
  779. }
  780. return null;
  781. };
  782. /*!
  783. * ignore
  784. */
  785. exports.noop = function() {};
  786. exports.errorToPOJO = function errorToPOJO(error) {
  787. const isError = error instanceof Error;
  788. if (!isError) {
  789. throw new Error('`error` must be `instanceof Error`.');
  790. }
  791. const ret = {};
  792. for (const properyName of Object.getOwnPropertyNames(error)) {
  793. ret[properyName] = error[properyName];
  794. }
  795. return ret;
  796. };