node_cache.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. /*
  2. * node-cache 4.2.1 ( 2019-07-24 )
  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. // lodash requires
  12. var EventEmitter, NodeCache, _assignIn, _isArray, _isFunction, _isNumber, _isObject, _isString, _size, _template, clone,
  13. boundMethodCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } },
  14. indexOf = [].indexOf;
  15. _assignIn = require("lodash/assignIn");
  16. _isArray = require("lodash/isArray");
  17. _isString = require("lodash/isString");
  18. _isFunction = require("lodash/isFunction");
  19. _isNumber = require("lodash/isNumber");
  20. _isObject = require("lodash/isObject");
  21. _size = require("lodash/size");
  22. _template = require("lodash/template");
  23. clone = require("clone");
  24. EventEmitter = require('events').EventEmitter;
  25. // generate superclass
  26. module.exports = NodeCache = (function() {
  27. class NodeCache extends EventEmitter {
  28. constructor(options = {}) {
  29. super();
  30. // ## get
  31. // get a cached key and change the stats
  32. // **Parameters:**
  33. // * `key` ( String | Number ): cache key
  34. // * `[cb]` ( Function ): Callback function
  35. // * `[errorOnMissing=false]` ( Boolean ) return a error to the `cb` or throw it if no `cb` is used. Otherwise the get will return `undefined` on a miss.
  36. // **Example:**
  37. // myCache.get "myKey", ( err, val )->
  38. // console.log( err, val )
  39. // return
  40. this.get = this.get.bind(this);
  41. // ## mget
  42. // get multiple cached keys at once and change the stats
  43. // **Parameters:**
  44. // * `keys` ( String|Number[] ): an array of keys
  45. // * `[cb]` ( Function ): Callback function
  46. // **Example:**
  47. // myCache.mget [ "foo", "bar" ], ( err, val )->
  48. // console.log( err, val )
  49. // return
  50. this.mget = this.mget.bind(this);
  51. // ## set
  52. // set a cached key and change the stats
  53. // **Parameters:**
  54. // * `key` ( String | Number ): cache key
  55. // * `value` ( Any ): A element to cache. If the option `option.forceString` is `true` the module trys to translate it to a serialized JSON
  56. // * `[ ttl ]` ( Number | String ): ( optional ) The time to live in seconds.
  57. // * `[cb]` ( Function ): Callback function
  58. // **Example:**
  59. // myCache.set "myKey", "my_String Value", ( err, success )->
  60. // console.log( err, success )
  61. // myCache.set "myKey", "my_String Value", "10", ( err, success )->
  62. // console.log( err, success )
  63. this.set = this.set.bind(this);
  64. // ## del
  65. // remove keys
  66. // **Parameters:**
  67. // * `keys` ( String | Number | String|Number[] ): cache key to delete or a array of cache keys
  68. // * `[cb]` ( Function ): Callback function
  69. // **Return**
  70. // ( Number ): Number of deleted keys
  71. // **Example:**
  72. // myCache.del( "myKey" )
  73. // myCache.del( "myKey", ( err, delCount )->
  74. // console.log( err, delCount )
  75. // return
  76. this.del = this.del.bind(this);
  77. // ## ttl
  78. // reset or redefine the ttl of a key. `ttl` = 0 means infinite lifetime.
  79. // If `ttl` is not passed the default ttl is used.
  80. // If `ttl` < 0 the key will be deleted.
  81. // **Parameters:**
  82. // * `key` ( String | Number ): cache key to reset the ttl value
  83. // * `ttl` ( Number ): ( optional -> options.stdTTL || 0 ) The time to live in seconds
  84. // * `[cb]` ( Function ): Callback function
  85. // **Return**
  86. // ( Boolen ): key found and ttl set
  87. // **Example:**
  88. // myCache.ttl( "myKey" ) // will set ttl to default ttl
  89. // myCache.ttl( "myKey", 1000, ( err, keyFound )->
  90. // console.log( err, success )
  91. this.ttl = this.ttl.bind(this);
  92. // ## getTtl
  93. // receive the ttl of a key.
  94. // **Parameters:**
  95. // * `key` ( String | Number ): cache key to check the ttl value
  96. // * `[cb]` ( Function ): Callback function
  97. // **Return**
  98. // ( Number|undefined ): The timestamp in ms when the key will expire, 0 if it will never expire or undefined if it not exists
  99. // **Example:**
  100. // ts = myCache.getTtl( "myKey" )
  101. // myCache.getTtl( "myKey",( err, ttl )->
  102. // console.log( err, ttl )
  103. // return
  104. this.getTtl = this.getTtl.bind(this);
  105. // ## keys
  106. // list all keys within this cache
  107. // **Parameters:**
  108. // * `[cb]` ( Function ): Callback function
  109. // **Return**
  110. // ( Array ): An array of all keys
  111. // **Example:**
  112. // _keys = myCache.keys()
  113. // # [ "foo", "bar", "fizz", "buzz", "anotherKeys" ]
  114. this.keys = this.keys.bind(this);
  115. // ## getStats
  116. // get the stats
  117. // **Parameters:**
  118. // -
  119. // **Return**
  120. // ( Object ): Stats data
  121. // **Example:**
  122. // myCache.getStats()
  123. // # {
  124. // # hits: 0,
  125. // # misses: 0,
  126. // # keys: 0,
  127. // # ksize: 0,
  128. // # vsize: 0
  129. // # }
  130. this.getStats = this.getStats.bind(this);
  131. // ## flushAll
  132. // flush the whole data and reset the stats
  133. // **Example:**
  134. // myCache.flushAll()
  135. // myCache.getStats()
  136. // # {
  137. // # hits: 0,
  138. // # misses: 0,
  139. // # keys: 0,
  140. // # ksize: 0,
  141. // # vsize: 0
  142. // # }
  143. this.flushAll = this.flushAll.bind(this);
  144. // ## close
  145. // This will clear the interval timeout which is set on checkperiod option.
  146. // **Example:**
  147. // myCache.close()
  148. this.close = this.close.bind(this);
  149. // ## _checkData
  150. // internal housekeeping method.
  151. // Check all the cached data and delete the invalid values
  152. this._checkData = this._checkData.bind(this);
  153. // ## _check
  154. // internal method the check the value. If it's not valid any more delete it
  155. this._check = this._check.bind(this);
  156. // ## _isInvalidKey
  157. // internal method to check if the type of a key is either `number` or `string`
  158. this._isInvalidKey = this._isInvalidKey.bind(this);
  159. // ## _wrap
  160. // internal method to wrap a value in an object with some metadata
  161. this._wrap = this._wrap.bind(this);
  162. // ## _getValLength
  163. // internal method to calculate the value length
  164. this._getValLength = this._getValLength.bind(this);
  165. // ## _error
  166. // internal method to handle an error message
  167. this._error = this._error.bind(this);
  168. // ## _initErrors
  169. // internal method to generate error message templates
  170. this._initErrors = this._initErrors.bind(this);
  171. this.options = options;
  172. this._initErrors();
  173. // container for cached data
  174. this.data = {};
  175. // module options
  176. this.options = _assignIn({
  177. // convert all elements to string
  178. forceString: false,
  179. // used standard size for calculating value size
  180. objectValueSize: 80,
  181. promiseValueSize: 80,
  182. arrayValueSize: 40,
  183. // standard time to live in seconds. 0 = infinity;
  184. stdTTL: 0,
  185. // time in seconds to check all data and delete expired keys
  186. checkperiod: 600,
  187. // en/disable cloning of variables. If `true` you'll get a copy of the cached variable. If `false` you'll save and get just the reference
  188. useClones: true,
  189. // en/disable throwing errors when trying to `.get` missing or expired values.
  190. errorOnMissing: false,
  191. // whether values should be deleted automatically at expiration
  192. deleteOnExpire: true
  193. }, this.options);
  194. // statistics container
  195. this.stats = {
  196. hits: 0,
  197. misses: 0,
  198. keys: 0,
  199. ksize: 0,
  200. vsize: 0
  201. };
  202. // pre allocate valid keytypes array
  203. this.validKeyTypes = ["string", "number"];
  204. // initalize checking period
  205. this._checkData();
  206. return;
  207. }
  208. get(key, cb, errorOnMissing) {
  209. var _err, _ret, err;
  210. boundMethodCheck(this, NodeCache);
  211. // handle passing in errorOnMissing without cb
  212. if (typeof cb === "boolean" && arguments.length === 2) {
  213. errorOnMissing = cb;
  214. cb = void 0;
  215. }
  216. // handle invalid key types
  217. if ((err = this._isInvalidKey(key)) != null) {
  218. if (cb != null) {
  219. cb(err);
  220. return;
  221. } else {
  222. throw err;
  223. }
  224. }
  225. // get data and incremet stats
  226. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  227. this.stats.hits++;
  228. _ret = this._unwrap(this.data[key]);
  229. if (cb != null) {
  230. // return data
  231. cb(null, _ret);
  232. }
  233. return _ret;
  234. } else {
  235. // if not found return a error
  236. this.stats.misses++;
  237. if (this.options.errorOnMissing || errorOnMissing) {
  238. _err = this._error("ENOTFOUND", {
  239. key: key
  240. }, cb);
  241. if (_err != null) {
  242. throw _err;
  243. }
  244. return;
  245. } else {
  246. if (cb != null) {
  247. cb(null, void 0);
  248. }
  249. }
  250. return void 0;
  251. }
  252. }
  253. mget(keys, cb) {
  254. var _err, err, i, key, len, oRet;
  255. boundMethodCheck(this, NodeCache);
  256. // convert a string to an array of one key
  257. if (!_isArray(keys)) {
  258. _err = this._error("EKEYSTYPE");
  259. if (cb != null) {
  260. cb(_err);
  261. }
  262. return _err;
  263. }
  264. // define return
  265. oRet = {};
  266. for (i = 0, len = keys.length; i < len; i++) {
  267. key = keys[i];
  268. // handle invalid key types
  269. if ((err = this._isInvalidKey(key)) != null) {
  270. if (cb != null) {
  271. cb(err);
  272. return;
  273. } else {
  274. throw err;
  275. }
  276. }
  277. // get data and increment stats
  278. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  279. this.stats.hits++;
  280. oRet[key] = this._unwrap(this.data[key]);
  281. } else {
  282. // if not found return a error
  283. this.stats.misses++;
  284. }
  285. }
  286. if (cb != null) {
  287. // return all found keys
  288. cb(null, oRet);
  289. }
  290. return oRet;
  291. }
  292. set(key, value, ttl, cb) {
  293. var err, existent;
  294. boundMethodCheck(this, NodeCache);
  295. // force the data to string
  296. if (this.options.forceString && !_isString(value)) {
  297. value = JSON.stringify(value);
  298. }
  299. // remap the arguments if `ttl` is not passed
  300. if (arguments.length === 3 && _isFunction(ttl)) {
  301. cb = ttl;
  302. ttl = this.options.stdTTL;
  303. }
  304. // handle invalid key types
  305. if ((err = this._isInvalidKey(key)) != null) {
  306. if (cb != null) {
  307. cb(err);
  308. return;
  309. } else {
  310. throw err;
  311. }
  312. }
  313. // internal helper variables
  314. existent = false;
  315. // remove existing data from stats
  316. if (this.data[key]) {
  317. existent = true;
  318. this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false));
  319. }
  320. // set the value
  321. this.data[key] = this._wrap(value, ttl);
  322. this.stats.vsize += this._getValLength(value);
  323. // only add the keys and key-size if the key is new
  324. if (!existent) {
  325. this.stats.ksize += this._getKeyLength(key);
  326. this.stats.keys++;
  327. }
  328. this.emit("set", key, value);
  329. if (cb != null) {
  330. // return true
  331. cb(null, true);
  332. }
  333. return true;
  334. }
  335. del(keys, cb) {
  336. var delCount, err, i, key, len, oldVal;
  337. boundMethodCheck(this, NodeCache);
  338. // convert keys to an array of itself
  339. if (!_isArray(keys)) {
  340. keys = [keys];
  341. }
  342. delCount = 0;
  343. for (i = 0, len = keys.length; i < len; i++) {
  344. key = keys[i];
  345. // handle invalid key types
  346. if ((err = this._isInvalidKey(key)) != null) {
  347. if (cb != null) {
  348. cb(err);
  349. return;
  350. } else {
  351. throw err;
  352. }
  353. }
  354. // only delete if existent
  355. if (this.data[key] != null) {
  356. // calc the stats
  357. this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false));
  358. this.stats.ksize -= this._getKeyLength(key);
  359. this.stats.keys--;
  360. delCount++;
  361. // delete the value
  362. oldVal = this.data[key];
  363. delete this.data[key];
  364. // return true
  365. this.emit("del", key, oldVal.v);
  366. } else {
  367. // if the key has not been found return an error
  368. this.stats.misses++;
  369. }
  370. }
  371. if (cb != null) {
  372. cb(null, delCount);
  373. }
  374. return delCount;
  375. }
  376. ttl() {
  377. var arg, args, cb, err, i, key, len, ttl;
  378. boundMethodCheck(this, NodeCache);
  379. // change args if only key and callback are passed
  380. [key, ...args] = arguments;
  381. for (i = 0, len = args.length; i < len; i++) {
  382. arg = args[i];
  383. switch (typeof arg) {
  384. case "number":
  385. ttl = arg;
  386. break;
  387. case "function":
  388. cb = arg;
  389. }
  390. }
  391. ttl || (ttl = this.options.stdTTL);
  392. if (!key) {
  393. if (cb != null) {
  394. cb(null, false);
  395. }
  396. return false;
  397. }
  398. // handle invalid key types
  399. if ((err = this._isInvalidKey(key)) != null) {
  400. if (cb != null) {
  401. cb(err);
  402. return;
  403. } else {
  404. throw err;
  405. }
  406. }
  407. // check for existant data and update the ttl value
  408. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  409. // if ttl < 0 delete the key. otherwise reset the value
  410. if (ttl >= 0) {
  411. this.data[key] = this._wrap(this.data[key].v, ttl, false);
  412. } else {
  413. this.del(key);
  414. }
  415. if (cb != null) {
  416. cb(null, true);
  417. }
  418. return true;
  419. } else {
  420. if (cb != null) {
  421. // return false if key has not been found
  422. cb(null, false);
  423. }
  424. return false;
  425. }
  426. }
  427. getTtl(key, cb) {
  428. var _ttl, err;
  429. boundMethodCheck(this, NodeCache);
  430. if (!key) {
  431. if (cb != null) {
  432. cb(null, void 0);
  433. }
  434. return void 0;
  435. }
  436. // handle invalid key types
  437. if ((err = this._isInvalidKey(key)) != null) {
  438. if (cb != null) {
  439. cb(err);
  440. return;
  441. } else {
  442. throw err;
  443. }
  444. }
  445. // check for existant data and update the ttl value
  446. if ((this.data[key] != null) && this._check(key, this.data[key])) {
  447. _ttl = this.data[key].t;
  448. if (cb != null) {
  449. cb(null, _ttl);
  450. }
  451. return _ttl;
  452. } else {
  453. if (cb != null) {
  454. // return undefined if key has not been found
  455. cb(null, void 0);
  456. }
  457. return void 0;
  458. }
  459. }
  460. keys(cb) {
  461. var _keys;
  462. boundMethodCheck(this, NodeCache);
  463. _keys = Object.keys(this.data);
  464. if (cb != null) {
  465. cb(null, _keys);
  466. }
  467. return _keys;
  468. }
  469. getStats() {
  470. boundMethodCheck(this, NodeCache);
  471. return this.stats;
  472. }
  473. flushAll(_startPeriod = true) {
  474. boundMethodCheck(this, NodeCache);
  475. // parameter just for testing
  476. // set data empty
  477. this.data = {};
  478. // reset stats
  479. this.stats = {
  480. hits: 0,
  481. misses: 0,
  482. keys: 0,
  483. ksize: 0,
  484. vsize: 0
  485. };
  486. // reset check period
  487. this._killCheckPeriod();
  488. this._checkData(_startPeriod);
  489. this.emit("flush");
  490. }
  491. close() {
  492. boundMethodCheck(this, NodeCache);
  493. this._killCheckPeriod();
  494. }
  495. _checkData(startPeriod = true) {
  496. var key, ref, value;
  497. boundMethodCheck(this, NodeCache);
  498. ref = this.data;
  499. // run the housekeeping method
  500. for (key in ref) {
  501. value = ref[key];
  502. this._check(key, value);
  503. }
  504. if (startPeriod && this.options.checkperiod > 0) {
  505. this.checkTimeout = setTimeout(this._checkData, this.options.checkperiod * 1000, startPeriod);
  506. if (this.checkTimeout.unref != null) {
  507. this.checkTimeout.unref();
  508. }
  509. }
  510. }
  511. // ## _killCheckPeriod
  512. // stop the checkdata period. Only needed to abort the script in testing mode.
  513. _killCheckPeriod() {
  514. if (this.checkTimeout != null) {
  515. return clearTimeout(this.checkTimeout);
  516. }
  517. }
  518. _check(key, data) {
  519. var _retval;
  520. boundMethodCheck(this, NodeCache);
  521. _retval = true;
  522. // data is invalid if the ttl is too old and is not 0
  523. // console.log data.t < Date.now(), data.t, Date.now()
  524. if (data.t !== 0 && data.t < Date.now()) {
  525. if (this.options.deleteOnExpire) {
  526. _retval = false;
  527. this.del(key);
  528. }
  529. this.emit("expired", key, this._unwrap(data));
  530. }
  531. return _retval;
  532. }
  533. _isInvalidKey(key) {
  534. var ref;
  535. boundMethodCheck(this, NodeCache);
  536. if (ref = typeof key, indexOf.call(this.validKeyTypes, ref) < 0) {
  537. return this._error("EKEYTYPE", {
  538. type: typeof key
  539. });
  540. }
  541. }
  542. _wrap(value, ttl, asClone = true) {
  543. var livetime, now, oReturn, ttlMultiplicator;
  544. boundMethodCheck(this, NodeCache);
  545. if (!this.options.useClones) {
  546. asClone = false;
  547. }
  548. // define the time to live
  549. now = Date.now();
  550. livetime = 0;
  551. ttlMultiplicator = 1000;
  552. // use given ttl
  553. if (ttl === 0) {
  554. livetime = 0;
  555. } else if (ttl) {
  556. livetime = now + (ttl * ttlMultiplicator);
  557. } else {
  558. // use standard ttl
  559. if (this.options.stdTTL === 0) {
  560. livetime = this.options.stdTTL;
  561. } else {
  562. livetime = now + (this.options.stdTTL * ttlMultiplicator);
  563. }
  564. }
  565. // return the wrapped value
  566. return oReturn = {
  567. t: livetime,
  568. v: asClone ? clone(value) : value
  569. };
  570. }
  571. // ## _unwrap
  572. // internal method to extract get the value out of the wrapped value
  573. _unwrap(value, asClone = true) {
  574. if (!this.options.useClones) {
  575. asClone = false;
  576. }
  577. if (value.v != null) {
  578. if (asClone) {
  579. return clone(value.v);
  580. } else {
  581. return value.v;
  582. }
  583. }
  584. return null;
  585. }
  586. // ## _getKeyLength
  587. // internal method the calculate the key length
  588. _getKeyLength(key) {
  589. return key.length;
  590. }
  591. _getValLength(value) {
  592. boundMethodCheck(this, NodeCache);
  593. if (_isString(value)) {
  594. // if the value is a String get the real length
  595. return value.length;
  596. } else if (this.options.forceString) {
  597. // force string if it's defined and not passed
  598. return JSON.stringify(value).length;
  599. } else if (_isArray(value)) {
  600. // if the data is an Array multiply each element with a defined default length
  601. return this.options.arrayValueSize * value.length;
  602. } else if (_isNumber(value)) {
  603. return 8;
  604. } else if (typeof (value != null ? value.then : void 0) === "function") {
  605. // if the data is a Promise, use defined default
  606. // (can't calculate actual/resolved value size synchronously)
  607. return this.options.promiseValueSize;
  608. } else if (_isObject(value)) {
  609. // if the data is an Object multiply each element with a defined default length
  610. return this.options.objectValueSize * _size(value);
  611. } else {
  612. // default fallback
  613. return 0;
  614. }
  615. }
  616. _error(type, data = {}, cb) {
  617. var error;
  618. boundMethodCheck(this, NodeCache);
  619. // generate the error object
  620. error = new Error();
  621. error.name = type;
  622. error.errorcode = type;
  623. error.message = this.ERRORS[type] != null ? this.ERRORS[type](data) : "-";
  624. error.data = data;
  625. if (cb && _isFunction(cb)) {
  626. // return the error
  627. cb(error, null);
  628. } else {
  629. // if no callback is defined return the error object
  630. return error;
  631. }
  632. }
  633. _initErrors() {
  634. var _errMsg, _errT, ref;
  635. boundMethodCheck(this, NodeCache);
  636. this.ERRORS = {};
  637. ref = this._ERRORS;
  638. for (_errT in ref) {
  639. _errMsg = ref[_errT];
  640. this.ERRORS[_errT] = _template(_errMsg);
  641. }
  642. }
  643. };
  644. NodeCache.prototype._ERRORS = {
  645. "ENOTFOUND": "Key `<%= key %>` not found",
  646. "EKEYTYPE": "The key argument has to be of type `string` or `number`. Found: `<%= type %>`",
  647. "EKEYSTYPE": "The keys argument has to be an array."
  648. };
  649. return NodeCache;
  650. }).call(this);
  651. }).call(this);