node_cache.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. * node-cache 4.1.1 ( 2018-01-31 )
  3. * https://github.com/mpneuried/nodecache
  4. *
  5. * Released under the MIT license
  6. * https://github.com/mpneuried/nodecache/blob/master/LICENSE
  7. *
  8. * Maintained by ( )
  9. */
  10. (function() {
  11. var EventEmitter, NodeCache, _assignIn, _isArray, _isFunction, _isNumber, _isObject, _isString, _size, _template, clone,
  12. bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
  13. extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  14. hasProp = {}.hasOwnProperty,
  15. slice = [].slice,
  16. indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
  17. _assignIn = require("lodash/assignIn");
  18. _isArray = require("lodash/isArray");
  19. _isString = require("lodash/isString");
  20. _isFunction = require("lodash/isFunction");
  21. _isNumber = require("lodash/isNumber");
  22. _isObject = require("lodash/isObject");
  23. _size = require("lodash/size");
  24. _template = require("lodash/template");
  25. clone = require("clone");
  26. EventEmitter = require('events').EventEmitter;
  27. module.exports = NodeCache = (function(superClass) {
  28. extend(NodeCache, superClass);
  29. function NodeCache(options) {
  30. this.options = options != null ? options : {};
  31. this._initErrors = bind(this._initErrors, this);
  32. this._error = bind(this._error, this);
  33. this._getValLength = bind(this._getValLength, this);
  34. this._wrap = bind(this._wrap, this);
  35. this._isInvalidKey = bind(this._isInvalidKey, this);
  36. this._check = bind(this._check, this);
  37. this._checkData = bind(this._checkData, this);
  38. this.close = bind(this.close, this);
  39. this.flushAll = bind(this.flushAll, this);
  40. this.getStats = bind(this.getStats, this);
  41. this.keys = bind(this.keys, this);
  42. this.getTtl = bind(this.getTtl, this);
  43. this.ttl = bind(this.ttl, this);
  44. this.del = bind(this.del, this);
  45. this.set = bind(this.set, this);
  46. this.mget = bind(this.mget, this);
  47. this.get = bind(this.get, this);
  48. this._initErrors();
  49. this.data = {};
  50. this.options = _assignIn({
  51. forceString: false,
  52. objectValueSize: 80,
  53. arrayValueSize: 40,
  54. stdTTL: 0,
  55. checkperiod: 600,
  56. useClones: true,
  57. errorOnMissing: false,
  58. deleteOnExpire: true
  59. }, this.options);
  60. this.stats = {
  61. hits: 0,
  62. misses: 0,
  63. keys: 0,
  64. ksize: 0,
  65. vsize: 0
  66. };
  67. this.validKeyTypes = ["string", "number"];
  68. this._checkData();
  69. return;
  70. }
  71. NodeCache.prototype.get = function(key, cb, errorOnMissing) {
  72. var _err, _ret, err;
  73. if (typeof cb === "boolean" && arguments.length === 2) {
  74. errorOnMissing = cb;
  75. cb = void 0;
  76. }
  77. if ((err = this._isInvalidKey(key)) != null) {
  78. if (cb != null) {
  79. cb(err);
  80. return;
  81. } else {
  82. throw err;
  83. }
  84. }
  85. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  86. this.stats.hits++;
  87. _ret = this._unwrap(this.data[key]);
  88. if (cb != null) {
  89. cb(null, _ret);
  90. }
  91. return _ret;
  92. } else {
  93. this.stats.misses++;
  94. if (this.options.errorOnMissing || errorOnMissing) {
  95. _err = this._error("ENOTFOUND", {
  96. key: key
  97. }, cb);
  98. if (_err != null) {
  99. throw _err;
  100. }
  101. return;
  102. } else {
  103. if (cb != null) {
  104. cb(null, void 0);
  105. }
  106. }
  107. return void 0;
  108. }
  109. };
  110. NodeCache.prototype.mget = function(keys, cb) {
  111. var _err, err, i, key, len, oRet;
  112. if (!_isArray(keys)) {
  113. _err = this._error("EKEYSTYPE");
  114. if (cb != null) {
  115. cb(_err);
  116. }
  117. return _err;
  118. }
  119. oRet = {};
  120. for (i = 0, len = keys.length; i < len; i++) {
  121. key = keys[i];
  122. if ((err = this._isInvalidKey(key)) != null) {
  123. if (cb != null) {
  124. cb(err);
  125. return;
  126. } else {
  127. throw err;
  128. }
  129. }
  130. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  131. this.stats.hits++;
  132. oRet[key] = this._unwrap(this.data[key]);
  133. } else {
  134. this.stats.misses++;
  135. }
  136. }
  137. if (cb != null) {
  138. cb(null, oRet);
  139. }
  140. return oRet;
  141. };
  142. NodeCache.prototype.set = function(key, value, ttl, cb) {
  143. var err, existent;
  144. if (this.options.forceString && !_isString(value)) {
  145. value = JSON.stringify(value);
  146. }
  147. if (arguments.length === 3 && _isFunction(ttl)) {
  148. cb = ttl;
  149. ttl = this.options.stdTTL;
  150. }
  151. if ((err = this._isInvalidKey(key)) != null) {
  152. if (cb != null) {
  153. cb(err);
  154. return;
  155. } else {
  156. throw err;
  157. }
  158. }
  159. existent = false;
  160. if (this.data[key]) {
  161. existent = true;
  162. this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false));
  163. }
  164. this.data[key] = this._wrap(value, ttl);
  165. this.stats.vsize += this._getValLength(value);
  166. if (!existent) {
  167. this.stats.ksize += this._getKeyLength(key);
  168. this.stats.keys++;
  169. }
  170. this.emit("set", key, value);
  171. if (cb != null) {
  172. cb(null, true);
  173. }
  174. return true;
  175. };
  176. NodeCache.prototype.del = function(keys, cb) {
  177. var delCount, err, i, key, len, oldVal;
  178. if (!_isArray(keys)) {
  179. keys = [keys];
  180. }
  181. delCount = 0;
  182. for (i = 0, len = keys.length; i < len; i++) {
  183. key = keys[i];
  184. if ((err = this._isInvalidKey(key)) != null) {
  185. if (cb != null) {
  186. cb(err);
  187. return;
  188. } else {
  189. throw err;
  190. }
  191. }
  192. if (this.data[key] != null) {
  193. this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false));
  194. this.stats.ksize -= this._getKeyLength(key);
  195. this.stats.keys--;
  196. delCount++;
  197. oldVal = this.data[key];
  198. delete this.data[key];
  199. this.emit("del", key, oldVal.v);
  200. } else {
  201. this.stats.misses++;
  202. }
  203. }
  204. if (cb != null) {
  205. cb(null, delCount);
  206. }
  207. return delCount;
  208. };
  209. NodeCache.prototype.ttl = function() {
  210. var arg, args, cb, err, i, key, len, ttl;
  211. key = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
  212. for (i = 0, len = args.length; i < len; i++) {
  213. arg = args[i];
  214. switch (typeof arg) {
  215. case "number":
  216. ttl = arg;
  217. break;
  218. case "function":
  219. cb = arg;
  220. }
  221. }
  222. ttl || (ttl = this.options.stdTTL);
  223. if (!key) {
  224. if (cb != null) {
  225. cb(null, false);
  226. }
  227. return false;
  228. }
  229. if ((err = this._isInvalidKey(key)) != null) {
  230. if (cb != null) {
  231. cb(err);
  232. return;
  233. } else {
  234. throw err;
  235. }
  236. }
  237. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  238. if (ttl >= 0) {
  239. this.data[key] = this._wrap(this.data[key].v, ttl, false);
  240. } else {
  241. this.del(key);
  242. }
  243. if (cb != null) {
  244. cb(null, true);
  245. }
  246. return true;
  247. } else {
  248. if (cb != null) {
  249. cb(null, false);
  250. }
  251. return false;
  252. }
  253. };
  254. NodeCache.prototype.getTtl = function(key, cb) {
  255. var _ttl, err;
  256. if (!key) {
  257. if (cb != null) {
  258. cb(null, void 0);
  259. }
  260. return void 0;
  261. }
  262. if ((err = this._isInvalidKey(key)) != null) {
  263. if (cb != null) {
  264. cb(err);
  265. return;
  266. } else {
  267. throw err;
  268. }
  269. }
  270. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  271. _ttl = this.data[key].t;
  272. if (cb != null) {
  273. cb(null, _ttl);
  274. }
  275. return _ttl;
  276. } else {
  277. if (cb != null) {
  278. cb(null, void 0);
  279. }
  280. return void 0;
  281. }
  282. };
  283. NodeCache.prototype.keys = function(cb) {
  284. var _keys;
  285. _keys = Object.keys(this.data);
  286. if (cb != null) {
  287. cb(null, _keys);
  288. }
  289. return _keys;
  290. };
  291. NodeCache.prototype.getStats = function() {
  292. return this.stats;
  293. };
  294. NodeCache.prototype.flushAll = function(_startPeriod) {
  295. if (_startPeriod == null) {
  296. _startPeriod = true;
  297. }
  298. this.data = {};
  299. this.stats = {
  300. hits: 0,
  301. misses: 0,
  302. keys: 0,
  303. ksize: 0,
  304. vsize: 0
  305. };
  306. this._killCheckPeriod();
  307. this._checkData(_startPeriod);
  308. this.emit("flush");
  309. };
  310. NodeCache.prototype.close = function() {
  311. this._killCheckPeriod();
  312. };
  313. NodeCache.prototype._checkData = function(startPeriod) {
  314. var key, ref, value;
  315. if (startPeriod == null) {
  316. startPeriod = true;
  317. }
  318. ref = this.data;
  319. for (key in ref) {
  320. value = ref[key];
  321. this._check(key, value);
  322. }
  323. if (startPeriod && this.options.checkperiod > 0) {
  324. this.checkTimeout = setTimeout(this._checkData, this.options.checkperiod * 1000, startPeriod);
  325. if (this.checkTimeout.unref != null) {
  326. this.checkTimeout.unref();
  327. }
  328. }
  329. };
  330. NodeCache.prototype._killCheckPeriod = function() {
  331. if (this.checkTimeout != null) {
  332. return clearTimeout(this.checkTimeout);
  333. }
  334. };
  335. NodeCache.prototype._check = function(key, data) {
  336. var _retval;
  337. _retval = true;
  338. if (data.t !== 0 && data.t < Date.now()) {
  339. if (this.options.deleteOnExpire) {
  340. _retval = false;
  341. this.del(key);
  342. }
  343. this.emit("expired", key, this._unwrap(data));
  344. }
  345. return _retval;
  346. };
  347. NodeCache.prototype._isInvalidKey = function(key) {
  348. var ref;
  349. if (ref = typeof key, indexOf.call(this.validKeyTypes, ref) < 0) {
  350. return this._error("EKEYTYPE", {
  351. type: typeof key
  352. });
  353. }
  354. };
  355. NodeCache.prototype._wrap = function(value, ttl, asClone) {
  356. var livetime, now, oReturn, ttlMultiplicator;
  357. if (asClone == null) {
  358. asClone = true;
  359. }
  360. if (!this.options.useClones) {
  361. asClone = false;
  362. }
  363. now = Date.now();
  364. livetime = 0;
  365. ttlMultiplicator = 1000;
  366. if (ttl === 0) {
  367. livetime = 0;
  368. } else if (ttl) {
  369. livetime = now + (ttl * ttlMultiplicator);
  370. } else {
  371. if (this.options.stdTTL === 0) {
  372. livetime = this.options.stdTTL;
  373. } else {
  374. livetime = now + (this.options.stdTTL * ttlMultiplicator);
  375. }
  376. }
  377. return oReturn = {
  378. t: livetime,
  379. v: asClone ? clone(value) : value
  380. };
  381. };
  382. NodeCache.prototype._unwrap = function(value, asClone) {
  383. if (asClone == null) {
  384. asClone = true;
  385. }
  386. if (!this.options.useClones) {
  387. asClone = false;
  388. }
  389. if (value.v != null) {
  390. if (asClone) {
  391. return clone(value.v);
  392. } else {
  393. return value.v;
  394. }
  395. }
  396. return null;
  397. };
  398. NodeCache.prototype._getKeyLength = function(key) {
  399. return key.length;
  400. };
  401. NodeCache.prototype._getValLength = function(value) {
  402. if (_isString(value)) {
  403. return value.length;
  404. } else if (this.options.forceString) {
  405. return JSON.stringify(value).length;
  406. } else if (_isArray(value)) {
  407. return this.options.arrayValueSize * value.length;
  408. } else if (_isNumber(value)) {
  409. return 8;
  410. } else if (_isObject(value)) {
  411. return this.options.objectValueSize * _size(value);
  412. } else {
  413. return 0;
  414. }
  415. };
  416. NodeCache.prototype._error = function(type, data, cb) {
  417. var error;
  418. if (data == null) {
  419. data = {};
  420. }
  421. error = new Error();
  422. error.name = type;
  423. error.errorcode = type;
  424. error.message = this.ERRORS[type] != null ? this.ERRORS[type](data) : "-";
  425. error.data = data;
  426. if (cb && _isFunction(cb)) {
  427. cb(error, null);
  428. } else {
  429. return error;
  430. }
  431. };
  432. NodeCache.prototype._initErrors = function() {
  433. var _errMsg, _errT, ref;
  434. this.ERRORS = {};
  435. ref = this._ERRORS;
  436. for (_errT in ref) {
  437. _errMsg = ref[_errT];
  438. this.ERRORS[_errT] = _template(_errMsg);
  439. }
  440. };
  441. NodeCache.prototype._ERRORS = {
  442. "ENOTFOUND": "Key `<%= key %>` not found",
  443. "EKEYTYPE": "The key argument has to be of type `string` or `number`. Found: `<%= type %>`",
  444. "EKEYSTYPE": "The keys argument has to be an array."
  445. };
  446. return NodeCache;
  447. })(EventEmitter);
  448. }).call(this);