Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.js 22KB

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