utils.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. var Buffer = require('safe-buffer').Buffer;
  6. var RegExpClone = require('regexp-clone');
  7. var specialProperties = ['__proto__', 'constructor', 'prototype'];
  8. /**
  9. * Clones objects
  10. *
  11. * @param {Object} obj the object to clone
  12. * @param {Object} options
  13. * @return {Object} the cloned object
  14. * @api private
  15. */
  16. var clone = exports.clone = function clone(obj, options) {
  17. if (obj === undefined || obj === null)
  18. return obj;
  19. if (Array.isArray(obj))
  20. return exports.cloneArray(obj, options);
  21. if (obj.constructor) {
  22. if (/ObjectI[dD]$/.test(obj.constructor.name)) {
  23. return 'function' == typeof obj.clone
  24. ? obj.clone()
  25. : new obj.constructor(obj.id);
  26. }
  27. if (obj.constructor.name === 'ReadPreference') {
  28. return new obj.constructor(obj.mode, clone(obj.tags, options));
  29. }
  30. if ('Binary' == obj._bsontype && obj.buffer && obj.value) {
  31. return 'function' == typeof obj.clone
  32. ? obj.clone()
  33. : new obj.constructor(obj.value(true), obj.sub_type);
  34. }
  35. if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
  36. return new obj.constructor(+obj);
  37. if ('RegExp' === obj.constructor.name)
  38. return RegExpClone(obj);
  39. if ('Buffer' === obj.constructor.name)
  40. return exports.cloneBuffer(obj);
  41. }
  42. if (isObject(obj))
  43. return exports.cloneObject(obj, options);
  44. if (obj.valueOf)
  45. return obj.valueOf();
  46. };
  47. /*!
  48. * ignore
  49. */
  50. exports.cloneObject = function cloneObject(obj, options) {
  51. var minimize = options && options.minimize;
  52. var ret = {};
  53. var hasKeys;
  54. var val;
  55. for (const k of Object.keys(obj)) {
  56. // Not technically prototype pollution because this wouldn't merge properties
  57. // onto `Object.prototype`, but avoid properties like __proto__ as a precaution.
  58. if (specialProperties.indexOf(k) !== -1) {
  59. continue;
  60. }
  61. val = clone(obj[k], options);
  62. if (!minimize || ('undefined' !== typeof val)) {
  63. hasKeys || (hasKeys = true);
  64. ret[k] = val;
  65. }
  66. }
  67. return minimize
  68. ? hasKeys && ret
  69. : ret;
  70. };
  71. exports.cloneArray = function cloneArray(arr, options) {
  72. var ret = [];
  73. for (var i = 0, l = arr.length; i < l; i++)
  74. ret.push(clone(arr[i], options));
  75. return ret;
  76. };
  77. /**
  78. * process.nextTick helper.
  79. *
  80. * Wraps the given `callback` in a try/catch. If an error is
  81. * caught it will be thrown on nextTick.
  82. *
  83. * node-mongodb-native had a habit of state corruption when
  84. * an error was immediately thrown from within a collection
  85. * method (find, update, etc) callback.
  86. *
  87. * @param {Function} [callback]
  88. * @api private
  89. */
  90. exports.tick = function tick(callback) {
  91. if ('function' !== typeof callback) return;
  92. return function() {
  93. // callbacks should always be fired on the next
  94. // turn of the event loop. A side benefit is
  95. // errors thrown from executing the callback
  96. // will not cause drivers state to be corrupted
  97. // which has historically been a problem.
  98. var args = arguments;
  99. soon(function() {
  100. callback.apply(this, args);
  101. });
  102. };
  103. };
  104. /**
  105. * Merges `from` into `to` without overwriting existing properties.
  106. *
  107. * @param {Object} to
  108. * @param {Object} from
  109. * @api private
  110. */
  111. exports.merge = function merge(to, from) {
  112. var keys = Object.keys(from),
  113. i = keys.length,
  114. key;
  115. while (i--) {
  116. key = keys[i];
  117. if (specialProperties.indexOf(key) !== -1) {
  118. continue;
  119. }
  120. if ('undefined' === typeof to[key]) {
  121. to[key] = from[key];
  122. } else {
  123. if (exports.isObject(from[key])) {
  124. merge(to[key], from[key]);
  125. } else {
  126. to[key] = from[key];
  127. }
  128. }
  129. }
  130. };
  131. /**
  132. * Same as merge but clones the assigned values.
  133. *
  134. * @param {Object} to
  135. * @param {Object} from
  136. * @api private
  137. */
  138. exports.mergeClone = function mergeClone(to, from) {
  139. var keys = Object.keys(from),
  140. i = keys.length,
  141. key;
  142. while (i--) {
  143. key = keys[i];
  144. if (specialProperties.indexOf(key) !== -1) {
  145. continue;
  146. }
  147. if ('undefined' === typeof to[key]) {
  148. to[key] = clone(from[key]);
  149. } else {
  150. if (exports.isObject(from[key])) {
  151. mergeClone(to[key], from[key]);
  152. } else {
  153. to[key] = clone(from[key]);
  154. }
  155. }
  156. }
  157. };
  158. /**
  159. * Read pref helper (mongo 2.2 drivers support this)
  160. *
  161. * Allows using aliases instead of full preference names:
  162. *
  163. * p primary
  164. * pp primaryPreferred
  165. * s secondary
  166. * sp secondaryPreferred
  167. * n nearest
  168. *
  169. * @param {String} pref
  170. */
  171. exports.readPref = function readPref(pref) {
  172. switch (pref) {
  173. case 'p':
  174. pref = 'primary';
  175. break;
  176. case 'pp':
  177. pref = 'primaryPreferred';
  178. break;
  179. case 's':
  180. pref = 'secondary';
  181. break;
  182. case 'sp':
  183. pref = 'secondaryPreferred';
  184. break;
  185. case 'n':
  186. pref = 'nearest';
  187. break;
  188. }
  189. return pref;
  190. };
  191. /**
  192. * Read Concern helper (mongo 3.2 drivers support this)
  193. *
  194. * Allows using string to specify read concern level:
  195. *
  196. * local 3.2+
  197. * available 3.6+
  198. * majority 3.2+
  199. * linearizable 3.4+
  200. * snapshot 4.0+
  201. *
  202. * @param {String|Object} concern
  203. */
  204. exports.readConcern = function readConcern(concern) {
  205. if ('string' === typeof concern) {
  206. switch (concern) {
  207. case 'l':
  208. concern = 'local';
  209. break;
  210. case 'a':
  211. concern = 'available';
  212. break;
  213. case 'm':
  214. concern = 'majority';
  215. break;
  216. case 'lz':
  217. concern = 'linearizable';
  218. break;
  219. case 's':
  220. concern = 'snapshot';
  221. break;
  222. }
  223. concern = { level: concern };
  224. }
  225. return concern;
  226. };
  227. /**
  228. * Object.prototype.toString.call helper
  229. */
  230. var _toString = Object.prototype.toString;
  231. exports.toString = function(arg) {
  232. return _toString.call(arg);
  233. };
  234. /**
  235. * Determines if `arg` is an object.
  236. *
  237. * @param {Object|Array|String|Function|RegExp|any} arg
  238. * @return {Boolean}
  239. */
  240. var isObject = exports.isObject = function(arg) {
  241. return '[object Object]' == exports.toString(arg);
  242. };
  243. /**
  244. * Determines if `arg` is an array.
  245. *
  246. * @param {Object}
  247. * @return {Boolean}
  248. * @see nodejs utils
  249. */
  250. exports.isArray = function(arg) {
  251. return Array.isArray(arg) ||
  252. 'object' == typeof arg && '[object Array]' == exports.toString(arg);
  253. };
  254. /**
  255. * Object.keys helper
  256. */
  257. exports.keys = Object.keys;
  258. /**
  259. * Basic Object.create polyfill.
  260. * Only one argument is supported.
  261. *
  262. * Based on https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
  263. */
  264. exports.create = 'function' == typeof Object.create
  265. ? Object.create
  266. : create;
  267. function create(proto) {
  268. if (arguments.length > 1) {
  269. throw new Error('Adding properties is not supported');
  270. }
  271. function F() {}
  272. F.prototype = proto;
  273. return new F;
  274. }
  275. /**
  276. * inheritance
  277. */
  278. exports.inherits = function(ctor, superCtor) {
  279. ctor.prototype = exports.create(superCtor.prototype);
  280. ctor.prototype.constructor = ctor;
  281. };
  282. /**
  283. * nextTick helper
  284. * compat with node 0.10 which behaves differently than previous versions
  285. */
  286. var soon = exports.soon = 'function' == typeof setImmediate
  287. ? setImmediate
  288. : process.nextTick;
  289. /**
  290. * Clones the contents of a buffer.
  291. *
  292. * @param {Buffer} buff
  293. * @return {Buffer}
  294. */
  295. exports.cloneBuffer = function(buff) {
  296. var dupe = Buffer.alloc(buff.length);
  297. buff.copy(dupe, 0, 0, buff.length);
  298. return dupe;
  299. };
  300. /**
  301. * Check if this object is an arguments object
  302. *
  303. * @param {Any} v
  304. * @return {Boolean}
  305. */
  306. exports.isArgumentsObject = function(v) {
  307. return Object.prototype.toString.call(v) === '[object Arguments]';
  308. };