Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ttl-mem-cache

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ttl-mem-cache - npm Package Compare versions

Comparing version 2.3.1 to 3.0.0

.nyc_output/8009cef4a8173a09c38c4e615d4cde8f.json

12

experiment/server.js

@@ -40,10 +40,10 @@ 'use strict';

transform(chunk, enc, next) {
chunk.toString().split('\n\n').sort().filter((item, index, arr) => {
return item && (!index || item != arr[index - 1]);
}).forEach((entry, x) => {
const obj = JSON.parse(entry);
console.log(x, obj);
this.push(obj);
});
})
.forEach((entry, x) => {
const obj = JSON.parse(entry);
console.log(x, obj);
this.push(obj);
});
next();

@@ -50,0 +50,0 @@ }

@@ -6,2 +6,3 @@ 'use strict';

const utils = require('./utils');
const Entry = require('./entry');

@@ -13,3 +14,3 @@ const _set = Symbol('_set');

constructor({
maxAge = 5 * 60 * 1000, stale = false, changefeed = false, id = undefined
ttl = 5 * 60 * 1000, stale = false, changefeed = false, id = undefined
} = {}) {

@@ -20,4 +21,4 @@ super({

Object.defineProperty(this, 'maxAge', {
value: maxAge,
Object.defineProperty(this, 'ttl', {
value: ttl,
enumerable: true,

@@ -45,10 +46,5 @@ });

this.on('broadcast', (obj, options) => {
this.on('broadcast', (entry) => {
if (this._readableState.flowing) {
const origin = options.origin ? options.origin : this.id;
this.push({
key: obj.key,
value: obj.value,
origin,
});
this.push(entry);
}

@@ -70,19 +66,15 @@ });

[_set](key, value, options = {}) {
if (key === null || key === undefined) {
[_set]({
key, value, ttl = this.ttl, origin = this.id, expires
}) {
if (utils.isEmpty(key)) {
throw new Error('Argument "key" cannot be null or undefined');
}
if (value === null || value === undefined) {
if (utils.isEmpty(value)) {
throw new Error('Argument "value" cannot be null or undefined');
}
const expires = utils.calculateExpire((options.maxAge || this.maxAge));
const eventObj = {
key,
value
};
let item = value;
if (this.changefeed) {
eventObj.value = {
item = {
oldVal: this.get(key),

@@ -93,16 +85,19 @@ newVal: value

this.store.set(key, { value, expires });
this.emit('broadcast', eventObj, options);
this.emit('set', eventObj);
const entry = new Entry({
key, value, ttl, origin, expires
});
this.store.set(key, entry);
this.emit('broadcast', entry);
this.emit('set', key, item);
return value;
}
[_del](key, options = {}) {
[_del]({ key }) {
const item = this.store.get(key);
const success = this.store.delete(key);
if (item) {
this.emit('broadcast', {
key,
value: null
}, options);
if (utils.isNotEmpty(item)) {
this.emit('broadcast', new Entry({ key }));
this.emit('dispose', key, item.value);

@@ -119,12 +114,18 @@ }

const item = this.store.get(key);
if (item) {
if (utils.validate(item)) {
if (utils.isNotEmpty(item)) {
const expired = item.expired();
if (expired) {
this.del(key);
}
if (expired && this.stale) {
return item.value;
}
this.del(key);
if (expired) {
return null;
}
if (this.stale) {
return item.value;
}
return item.value;
}

@@ -134,14 +135,12 @@ return null;

set(key, value, maxAge) {
return this[_set](key, value, {
maxAge
});
set(key, value, ttl) {
return this[_set]({ key, value, ttl });
}
del(key) {
return this[_del](key);
return this[_del]({ key });
}
entries(mutator) {
const mutate = (typeof mutator === 'function');
const mutate = utils.isFunction(mutator);
const now = Date.now();

@@ -151,9 +150,3 @@ const arr = [];

this.store.forEach((item, key) => {
if (utils.validate(item, now)) {
if (mutate) {
arr.push(mutator(item.value));
} else {
arr.push(item.value);
}
} else {
if (item.expired(now)) {
if (this.stale) {

@@ -167,2 +160,6 @@ if (mutate) {

this.del(key);
} else if (mutate) {
arr.push(mutator(item.value));
} else {
arr.push(item.value);
}

@@ -177,3 +174,3 @@ });

this.store.forEach((item, key) => {
if (!utils.validate(item, now)) {
if (item.expired(now)) {
this.del(key);

@@ -198,5 +195,6 @@ }

return items.map((item) => {
if (item[0] && item[1] && item[1].value && item[1].expires) {
this.store.set(item[0], item[1]);
return item[0];
if (Entry.assertStrict(item[1])) {
const entry = new Entry(item[1]);
this.store.set(entry.key, entry);
return entry.key;
}

@@ -216,17 +214,13 @@ return undefined;

_write(obj, enc, next) {
const options = {
origin: obj.origin ? obj.origin : this.id
};
if (obj.origin === this.id) {
if (utils.isNotEmpty(obj.origin) && (obj.origin === this.id)) {
return next();
}
if (obj.key && obj.value) {
this[_set](obj.key, obj.value, options);
if (Entry.assertLoose(obj)) {
this[_set](obj);
return next();
}
if (obj.key && (obj.value === null || obj.value === undefined)) {
this[_del](obj.key, options);
if (utils.isNotEmpty(obj.key) && utils.isEmpty(obj.value)) {
this[_del](obj);
return next();

@@ -233,0 +227,0 @@ }

'use strict';
module.exports.calculateExpire = (maxAge = 0) => {
if (maxAge === Infinity) {
return maxAge;
module.exports.calculateExpire = (ttl = 0) => {
if (ttl === Infinity) {
return ttl;
}
return Date.now() + maxAge;
return Date.now() + ttl;
};
module.exports.validate = (item = { expires: 0 }, now = Date.now()) => {
if (item.expires === Infinity) {
module.exports.expired = (expires = 0, now = Date.now()) => {
if (expires === Infinity) {
return false;
}
return (item.expires > now);
return expires < now;
};
module.exports.isEmpty = (value) => {
return (value === null || value === undefined);
};
module.exports.isNotEmpty = (value) => {
return !this.isEmpty(value);
};
module.exports.isFunction = (value) => {
return ({}.toString.call(value) === '[object Function]');
};
{
"name": "ttl-mem-cache",
"version": "2.3.1",
"version": "3.0.0",
"description": "A in memory time to live cache with streaming support.",

@@ -5,0 +5,0 @@ "main": "lib/cache.js",

@@ -58,3 +58,3 @@ # ttl-mem-cache

* maxAge - `Number` - Default max age in milliseconds all items in the cache should be cached before expiering.
* ttl - `Number` - Default time to live in milliseconds all items in the cache should be cached before expiering.
* stale - `Boolean` - If expired items in cache should be returned when pruned from the cache. Default: `false`.

@@ -64,7 +64,7 @@ * changelog - `Boolean` - If emitted `set` event and stream should contain both old and new value. Default: `false`.

If an option Object with a `maxAge` is not provided all items in the cache will by
default cached for 5 minutes before they expire.
If an option Object with a `ttl` is not provided all items in the cache will by default
cached for 5 minutes before they expire.
Items can be cached forever by setting `maxAge` to `Infinity`. Items cached forever
can be overwritten and manually deleted.
Items can be cached forever by setting `ttl` to `Infinity`. Items cached forever can
be overwritten and manually deleted.

@@ -88,3 +88,3 @@ Pruning of items from the cache happend when they are touched by one of the methods

### .set(key, value, maxAge)
### .set(key, value, ttl)

@@ -103,5 +103,5 @@ Set an item in the cache on a given key.

* value - The value to store on the key in the cache. Required.
* maxAge - Max age before this item should expire. Uses default if not given. Optional.
* ttl - Time to live before the item should expire. Uses default if not given. Optional.
An item can be cached forever by setting `maxAge` to `Infinity`.
An item can be cached forever by setting `ttl` to `Infinity`.

@@ -124,3 +124,3 @@

Triggering `.get()` will check the expire on the item. If the item is older than
the max age set on it, the item will be removed from the cache and this method
the time to live set on it, the item will be removed from the cache and this method
will return `null` unless `stale` is set to `true` on the constructor. Then the

@@ -157,3 +157,3 @@ expired item will be returned before its removed from the cache.

Triggering `.entries()` will check the expire on the items. If an item is older than
the max age set on it, the item will be removed from the cache and it will not be
the time to live set on it, the item will be removed from the cache and it will not be
included in the returned value of this methid unless `stale` is set to `true` on the

@@ -166,6 +166,2 @@ constructor. Then the expired item will be included before its removed from the cache.

Triggering `.entries()` will check the expire on all the items. If an item is older
than the max age set on it, the item will be removed from the cache and it will not
be part of the returned Array.
The mutator attribute can be used to change the structure of the returned items. It

@@ -242,4 +238,4 @@ takes a function which will be called with an Object with the `key` and `value`. This

const cache = new Cache();
cache.on('set', (item) => {
console.log(item); // outputs: {key: 'a', value: {foo: 'bar'}}
cache.on('set', (key, item) => {
console.log(key, item); // outputs: "a, {foo: 'bar'}"
});

@@ -350,3 +346,6 @@ cache.set('a', {foo: 'bar'});

value: 'item value',
origin: 'cache instance ID'
origin: 'cache instance ID',
ttl: 'time to live',
expires: 'time stamp',
type: 'TtlMemCacheEntry'
}

@@ -389,11 +388,8 @@ ```

If `changefeed` is set to be `true` on the constructor, the emitted Object in the Readable stream
will hold both old and new value for the key. See "changelog" for further info.
## Changelog
If the attribute `changelog` is set to `true` on the constructor, some emitted events will
emit an object holding both old and new values for the key.
If the attribute `changelog` is set to `true` on the constructor, the `set` events will emit an
object holding both old and new values for the key.

@@ -404,7 +400,4 @@ The emitted object looks like this:

{
key: 'a',
value: {
oldVal: 'foo',
newVal: 'bar'
}
oldVal: 'foo',
newVal: 'bar'
}

@@ -419,3 +412,3 @@ ```

const cache = new Cache({ changefeed: true });
cache.on('set', (item) => {
cache.on('set', (key, item) => {
// item will be in the format above

@@ -422,0 +415,0 @@ });

@@ -50,12 +50,12 @@ 'use strict';

tap.test('cache() - without maxAge - should set default maxAge', (t) => {
tap.test('cache() - without ttl - should set default ttl', (t) => {
const cache = new Cache();
t.equal(cache.maxAge, (5 * 60 * 1000));
t.equal(cache.ttl, (5 * 60 * 1000));
t.end();
});
tap.test('cache() - with maxAge - should set default maxAge', (t) => {
const maxAge = (60 * 60 * 1000);
const cache = new Cache({ maxAge });
t.equal(cache.maxAge, maxAge);
tap.test('cache() - with ttl - should set default ttl', (t) => {
const ttl = (60 * 60 * 1000);
const cache = new Cache({ ttl });
t.equal(cache.ttl, ttl);
t.end();

@@ -113,3 +113,3 @@ });

tap.test('cache.set() - without maxAge - should set default maxAge', (t) => {
tap.test('cache.set() - without ttl - should set default ttl', (t) => {
const clock = lolex.install();

@@ -120,3 +120,3 @@ clock.tick(100000);

cache.set('foo', 'bar');
t.equal(cache.store.get('foo').expires, 400000); // default maxAge + lolex tick time
t.equal(cache.store.get('foo').expires, 400000); // default ttl + lolex tick time

@@ -127,3 +127,3 @@ clock.uninstall();

tap.test('cache.set() - with maxAge - should set maxAge', (t) => {
tap.test('cache.set() - with ttl - should set ttl', (t) => {
const clock = lolex.install();

@@ -150,5 +150,5 @@ clock.tick(100000);

const cache = new Cache();
cache.on('set', (item) => {
t.equal(item.key, 'foo');
t.equal(item.value, 'xyz');
cache.on('set', (key, value) => {
t.equal(key, 'foo');
t.equal(value, 'xyz');
t.end();

@@ -162,6 +162,6 @@ });

cache.set('foo', 'bar');
cache.on('set', (item) => {
t.equal(item.key, 'foo');
t.equal(item.value.oldVal, 'bar');
t.equal(item.value.newVal, 'xyz');
cache.on('set', (key, value) => {
t.equal(key, 'foo');
t.equal(value.oldVal, 'bar');
t.equal(value.newVal, 'xyz');
t.end();

@@ -174,6 +174,6 @@ });

const cache = new Cache({ changefeed: true });
cache.on('set', (item) => {
t.equal(item.key, 'foo');
t.equal(item.value.oldVal, null);
t.equal(item.value.newVal, 'xyz');
cache.on('set', (key, value) => {
t.equal(key, 'foo');
t.equal(value.oldVal, null);
t.equal(value.newVal, 'xyz');
t.end();

@@ -186,9 +186,9 @@ });

const clock = lolex.install();
const cache = new Cache({ maxAge: 2 * 1000, changefeed: true });
const cache = new Cache({ ttl: 2 * 1000, changefeed: true });
cache.set('foo', 'bar');
cache.on('set', (item) => {
t.equal(item.key, 'foo');
t.equal(item.value.oldVal, null);
t.equal(item.value.newVal, 'xyz');
cache.on('set', (key, value) => {
t.equal(key, 'foo');
t.equal(value.oldVal, null);
t.equal(value.newVal, 'xyz');
clock.uninstall();

@@ -205,9 +205,9 @@ t.end();

const clock = lolex.install();
const cache = new Cache({ maxAge: 2 * 1000, stale: true, changefeed: true });
const cache = new Cache({ ttl: 2 * 1000, stale: true, changefeed: true });
cache.set('foo', 'bar');
cache.on('set', (item) => {
t.equal(item.key, 'foo');
t.equal(item.value.oldVal, 'bar');
t.equal(item.value.newVal, 'xyz');
cache.on('set', (key, value) => {
t.equal(key, 'foo');
t.equal(value.oldVal, 'bar');
t.equal(value.newVal, 'xyz');
clock.uninstall();

@@ -223,3 +223,2 @@ t.end();

/**

@@ -238,3 +237,3 @@ * .get()

const clock = lolex.install();
const cache = new Cache({ maxAge: 2 * 1000 });
const cache = new Cache({ ttl: 2 * 1000 });

@@ -260,3 +259,3 @@ const key = 'foo';

const cache = new Cache({ maxAge: 2 * 1000 });
const cache = new Cache({ ttl: 2 * 1000 });
cache.on('dispose', (key) => {

@@ -277,3 +276,3 @@ t.equal(key, 'foo');

const clock = lolex.install();
const cache = new Cache({ maxAge: 2 * 1000, stale: true });
const cache = new Cache({ ttl: 2 * 1000, stale: true });

@@ -551,3 +550,3 @@ const key = 'foo';

tap.test('cache.dump() - entries in the dumped Array - should be an Array with the "key" as first item and "value" as second item', (t) => {
tap.test('cache.dump() - entries in the dumped Array - should be an Array with the "key" as first item and "entry" Object as second item', (t) => {
const cache = new Cache();

@@ -571,3 +570,3 @@ cache.set('a', 'bar');

tap.test('cache.dump() - dumped entries - should have "value" and "expires" attributes', (t) => {
tap.test('cache.dump() - dumped entries - should have all properties of the Entry object', (t) => {
const cache = new Cache();

@@ -579,5 +578,14 @@ cache.set('a', 'bar');

t.equal(dump[0][1].key, 'a');
t.equal(dump[1][1].key, 'b');
t.equal(dump[0][1].value, 'bar');
t.equal(dump[1][1].value, 'foo');
t.type(dump[0][1].ttl, 'number');
t.type(dump[1][1].ttl, 'number');
t.type(dump[0][1].origin, 'string');
t.type(dump[1][1].origin, 'string');
t.type(dump[0][1].expires, 'number');

@@ -606,4 +614,8 @@ t.type(dump[1][1].expires, 'number');

const dump = [
['a', { value: 'bar', expires: 2000 }],
['b', { value: 'foo', expires: 2000 }],
['a', {
key: 'a', value: 'bar', ttl: 2000, origin: 'org', expires: 2000
}],
['b', {
key: 'b', value: 'foo', ttl: 2000, origin: 'org', expires: 2000
}],
];

@@ -621,6 +633,10 @@

tap.test('cache.load() - load entries - should Array of keys inserted into cache', (t) => {
tap.test('cache.load() - load entries - should return Array of keys inserted into cache', (t) => {
const dump = [
['a', { value: 'bar', expires: 2000 }],
['b', { value: 'foo', expires: 2000 }],
['a', {
key: 'a', value: 'bar', ttl: 2000, origin: 'org', expires: 2000
}],
['b', {
key: 'b', value: 'foo', ttl: 2000, origin: 'org', expires: 2000
}],
];

@@ -639,5 +655,11 @@

const dump = [
['a', { value: 'bar', expires: 2000 }],
[{ value: 'foo', expires: 2000 }],
['c', { value: 'xyz', expires: 2000 }],
['a', {
key: 'a', value: 'bar', ttl: 2000, origin: 'org', expires: 2000
}],
[{
key: 'b', value: 'foo', ttl: 2000, origin: 'org', expires: 2000
}],
['c', {
key: 'c', value: 'xyz', ttl: 2000, origin: 'org', expires: 2000
}],
];

@@ -657,5 +679,9 @@

const dump = [
['a', { value: 'bar', expires: 2000 }],
['a', {
key: 'a', value: 'bar', ttl: 2000, origin: 'org', expires: 2000
}],
['b'],
['c', { value: 'xyz', expires: 2000 }],
['c', {
key: 'c', value: 'xyz', ttl: 2000, origin: 'org', expires: 2000
}],
];

@@ -675,5 +701,11 @@

const dump = [
['a', { value: 'bar', expires: 2000 }],
['b', { expires: 2000 }],
['c', { value: 'xyz', expires: 2000 }],
['a', {
key: 'a', value: 'bar', ttl: 2000, origin: 'org', expires: 2000
}],
['b', {
key: 'b', ttl: 2000, origin: 'org', expires: 2000
}],
['c', {
key: 'c', value: 'xyz', ttl: 2000, origin: 'org', expires: 2000
}],
];

@@ -691,7 +723,13 @@

tap.test('cache.load() - one entry is missing "values.expires" - should set valid entries in cache', (t) => {
tap.test('cache.load() - one entry is missing "values.ttl" - should set valid entries in cache', (t) => {
const dump = [
['a', { value: 'bar', expires: 2000 }],
['b', { value: 'foo' }],
['c', { value: 'xyz', expires: 2000 }],
['a', {
key: 'a', value: 'bar', ttl: 2000, origin: 'org', expires: 2000
}],
['b', {
key: 'b', value: 'bar', origin: 'org'
}],
['c', {
key: 'c', value: 'xyz', ttl: 2000, origin: 'org', expires: 2000
}],
];

@@ -903,23 +941,2 @@

tap.test('_read() - pipe with "changefeed: true" - should emit changefeed objects', (t) => {
const cache = new Cache({ changefeed: true });
const dest = destStream((arr) => {
t.equal(arr[0].key, 'a');
t.equal(arr[0].value.newVal, 'foo');
t.equal(arr[0].value.oldVal, null);
t.equal(arr[1].key, 'a');
t.equal(arr[1].value.newVal, 'bar');
t.equal(arr[1].value.oldVal, 'foo');
t.end();
});
cache.pipe(dest);
cache.set('a', 'foo');
cache.set('a', 'bar');
setImmediate(() => {
dest.end();
});
});
tap.test('_read() - set item when no stream is attached to Readable stream - should be no items in internal stream buffer', (t) => {

@@ -926,0 +943,0 @@ const cache = new Cache();

@@ -34,28 +34,96 @@ 'use strict';

/**
* .validate()
* .expired()
*/
tap.test('utils.validate() - empty argument - should return false', (t) => {
t.equal(utils.validate(), false);
tap.test('utils.expired() - empty argument - should return true', (t) => {
t.true(utils.expired());
t.end();
});
tap.test('utils.validate() - "expires" is Infinity - should return false', (t) => {
tap.test('utils.expired() - "expires" is Infinity - should return false', (t) => {
const expires = Infinity;
t.equal(utils.validate({ expires }), false);
t.false(utils.expired(expires));
t.end();
});
tap.test('utils.validate() - "expires" is behind Date.now() - should return false', (t) => {
tap.test('utils.expired() - "expires" is behind Date.now() - should return true', (t) => {
const expires = Date.now() - 100000;
t.equal(utils.validate({ expires }), false);
t.true(utils.expired(expires));
t.end();
});
tap.test('utils.validate() - "expires" is in front of Date.now() - should return true', (t) => {
tap.test('utils.expired() - "expires" is in front of Date.now() - should return false', (t) => {
const expires = Date.now() + 100000;
t.equal(utils.validate({ expires }), true);
t.false(utils.expired(expires));
t.end();
});
/**
* .isEmpty()
*/
tap.test('utils.isEmpty() - value is not empty - should return false', (t) => {
t.false(utils.isEmpty('foo'));
t.false(utils.isEmpty(1));
t.false(utils.isEmpty({}));
t.false(utils.isEmpty([1]));
t.end();
});
tap.test('utils.isEmpty() - value is "null" - should return true', (t) => {
t.true(utils.isEmpty(null));
t.end();
});
tap.test('utils.isEmpty() - value is "undefined" - should return true', (t) => {
t.true(utils.isEmpty(undefined));
t.end();
});
/**
* .isNotEmpty()
*/
tap.test('utils.isNotEmpty() - value is not empty - should return true', (t) => {
t.true(utils.isNotEmpty('foo'));
t.true(utils.isNotEmpty(1));
t.true(utils.isNotEmpty({}));
t.true(utils.isNotEmpty([1]));
t.end();
});
tap.test('utils.isNotEmpty() - value is "null" - should return false', (t) => {
t.false(utils.isNotEmpty(null));
t.end();
});
tap.test('utils.isNotEmpty() - value is "undefined" - should return false', (t) => {
t.false(utils.isNotEmpty(undefined));
t.end();
});
/**
* .isFunction()
*/
tap.test('utils.isFunction() - value is not a function - should return false', (t) => {
t.false(utils.isFunction());
t.false(utils.isFunction('foo'));
t.false(utils.isFunction(1));
t.false(utils.isFunction({}));
t.false(utils.isFunction([1]));
t.end();
});
tap.test('utils.isFunction() - value is a function - should return true', (t) => {
const fn = function fn(x) {
return x;
};
const arr = (x) => {
return x;
};
t.true(utils.isFunction(fn));
t.true(utils.isFunction(arr));
t.end();
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc