UNPKG

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