Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 204 additions & 3 deletions packages/node-cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@
[![npm](https://img.shields.io/npm/dm/cacheable.svg)](https://www.npmjs.com/package/cacheable)
[![npm](https://img.shields.io/npm/v/cacheable)](https://www.npmjs.com/package/cacheable)

`@cacheable/node-cache` is compatible with the `node-cache` package with regular maintenance and additional functionality (async/await and storage adapters). The only thing not implemented is the `enableLegacyCallbacks` option and functions. If you need them we are happy to take a PR to add them.
`@cacheable/node-cache` is compatible with the [node-cache](https://www.npmjs.com/package/node-cache) package with regular maintenance and additional functionality (async/await and storage adapters). The only thing not implemented is the `enableLegacyCallbacks` option and functions. If you need them we are happy to take a PR to add them.

* Fully Compatible with `node-cache` using `{NodeCache}`
* Async/Await functionality with `{NodeStorageCache}`
* Storage Adapters via [Keyv](https://keyv.org) with `{NodeStorageCache}`
* Async/Await functionality with `{NodeCacheStore}`
* Storage Adapters via [Keyv](https://keyv.org) with `{NodeCacheStore}`
* Maintained and Updated Regularly! 🎉

Note: `NodeCache` is ready and available for use. `NodeCacheStore` is in progress and will be available soon. Please do not use it until it is released.

## Table of Contents
* [Getting Started](#getting-started)
* [Basic Usage](#basic-usage)
* [Advanced Usage](#advanced-usage)
* [API](#api)
* [How to Contribute](#how-to-contribute)
* [License and Copyright](#license-and-copyright)

## Getting Started

```bash
Expand Down Expand Up @@ -52,6 +62,197 @@ cache.getStats(); // {hits: 1, misses: 1, keys: 1, ksize: 2, vsize: 3}

## API

### `constructor(options?: NodeCacheOptions)`

Create a new cache instance. You can pass in options to set the configuration:

```javascript
export type NodeCacheOptions = {
stdTTL?: number; // The standard ttl as number in seconds for every generated cache element. 0 = unlimited
checkperiod?: number; // Default is 600, 0 means no periodic check
useClones?: boolean; // Default is true
deleteOnExpire?: boolean; // Default is true, if this is set to true it will delete the key when it expires.
maxKeys?: number; // Default is -1 (unlimited). If this is set it will throw and error if you try to set more keys than the max.
};
```

When initializing the cache you can pass in the options to set the configuration like the example below where we set the `stdTTL` to 10 seconds and `checkperiod` to 5 seconds.:

```javascript
const cache = new NodeCache({stdTTL: 10, checkperiod: 5});
```

When setting `deleteOnExpire` to `true` it will delete the key when it expires. If you set it to `false` it will keep the key but the value on `get()` will be `undefined`. You can manage the key with `on('expired')` event.

```javascript
const cache = new NodeCache({deleteOnExpire: false});
cache.on('expired', (key, value) => {
console.log(`Key ${key} has expired with value ${value}`);
});
```

### `.set(key: string | number, value: any, ttl?: number): boolean`

Set a key value pair with an optional ttl (in seconds). Will return true on success. If the ttl is not set it will default to 0 (no ttl).

```javascript
cache.set('foo', 'bar', 10); // true
```

### `.mset(data: Array<NodeCacheItem>): boolean`

Set multiple key value pairs at once. This will take an array of objects with the key, value, and optional ttl.

```javascript
cache.mset([{key: 'foo', value: 'bar', ttl: 10}, {key: 'bar', value: 'baz'}]); // true
```

the `NodeCacheItem` is defined as:

```javascript
export type NodeCacheItem = {
key: string;
value: any;
ttl?: number;
};
```

### `.get(key: string | number): any`

Get a value from the cache by key. If the key does not exist it will return `undefined`.

```javascript
cache.get('foo'); // 'bar'
```

### `mget(keys: Array<string | number>): Record<string, unknown>`

Get multiple values from the cache by keys. This will return an object with the keys and values.

```javascript
const obj = { my: 'value', my2: 'value2' };
const obj2 = { special: 'value3', life: 'value4' };
cache.set('my', obj);
cache.set('my2', obj2);
cache.mget(['my', 'my2']); // { my: { my: 'value', my2: 'value2' }, my2: { special: 'value3', life: 'value4' } }
```

### `take(key: string | number): any`

Get a value from the cache by key and delete it. If the key does not exist it will return `undefined`.

```javascript
cache.set('foo', 'bar');
cache.take('foo'); // 'bar'
cache.get('foo'); // undefined
```

### `del(key: string | number | Array<string | number>): number`

Delete a key from the cache. Will return the number of deleted entries and never fail. You can also pass in an array of keys to delete multiple keys. All examples assume that you have initialized the cache like `const cache = new NodeCache();`.

```javascript
cache.del('foo'); // true
```

passing in an array of keys:

```javascript
cache.del(['foo', 'bar']); // true
```

### `.mdel(keys: Array<string | number>): number`

Delete multiple keys from the cache. Will return the number of deleted entries and never fail.

```javascript
cache.mdel(['foo', 'bar']); // true
```

### `.ttl(key: string | number, ttl?: number): boolean`

Redefine the ttl of a key. Returns true if the key has been found and changed. Otherwise returns false. If the ttl-argument isn't passed the default-TTL will be used.

```javascript
cache.ttl('foo', 10); // true
```

### `getTtl(key: string | number): number | undefined`

Get the ttl expiration from `Date.now()` of a key. If the key does not exist it will return `undefined`.

```javascript
cache.getTtl('foo'); // 1725993344859
```

### `has(key: string | number): boolean`

Check if a key exists in the cache.

```javascript
cache.set('foo', 'bar');
cache.has('foo'); // true
```

### `keys(): Array<string>`

Get all keys from the cache.

```javascript
cache.keys(); // ['foo', 'bar']
```

### `getStats(): NodeCacheStats`

Get the stats of the cache.

```javascript
cache.getStats(); // {hits: 1, misses: 1, keys: 1, ksize: 2, vsize: 3}
```

### `flushAll(): void`

Flush the cache. Will remove all keys and reset the stats.

```javascript
cache.flushAll();
cache.keys(); // []
cache.getStats(); // {hits: 0, misses: 0, keys: 0, ksize: 0, vsize: 0}
```

### `flushStats(): void`

Flush the stats. Will reset the stats but keep the keys.

```javascript
cache.set('foo', 'bar');
cache.flushStats();
cache.getStats(); // {hits: 0, misses: 0, keys: 0, ksize: 0, vsize: 0}
cache.keys(); // ['foo']
```

### `close(): void`

this will stop the interval that is running for the `checkperiod` and `deleteOnExpire` options.

```javascript
cache.close();
```

### `on(event: string, callback: Function): void`

Listen to events. Here are the events that you can listen to:
* `set` - when a key is set and it will pass in the `key` and `value`.
* `expired` - when a key is expired and it will pass in the `key` and `value`.
* `flush` - when the cache is flushed
* `flush_stats` - when the stats are flushed
* `del` - when a key is deleted and it will pass in the `key` and `value`.

```javascript
cache.on('set', (key, value) => {
console.log(`Key ${key} has been set with value ${value}`);
});
```

## How to Contribute

Expand Down
15 changes: 7 additions & 8 deletions packages/node-cache/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ export type NodeCacheStats = {
vsize: number; // global value size count in approximately bytes
};

// eslint-disable-next-line @typescript-eslint/naming-convention
export type NodeCacheMGetReturn = Record<string, NodeCacheItem>;

export default class NodeCache extends eventemitter {
public readonly options: NodeCacheOptions = {
// eslint-disable-next-line @typescript-eslint/naming-convention
Expand Down Expand Up @@ -161,8 +158,8 @@ export default class NodeCache extends eventemitter {
Gets multiple saved values from the cache. Returns an empty object {} if not found or expired.
If the value was found it returns an object with the key value pair.
*/
public mget(keys: Array<string | number>): NodeCacheMGetReturn | Record<string, unknown> {
const result: NodeCacheMGetReturn = {};
public mget(keys: Array<string | number>): Record<string, unknown> {
const result: Record<string, unknown> = {};

for (const key of keys) {
const value = this.get(key);
Expand Down Expand Up @@ -230,10 +227,11 @@ export default class NodeCache extends eventemitter {

// Redefine the ttl of a key. Returns true if the key has been found and changed.
// Otherwise returns false. If the ttl-argument isn't passed the default-TTL will be used.
public ttl(key: string | number, ttl: number): boolean {
public ttl(key: string | number, ttl?: number): boolean {
const result = this.store.get(this.formatKey(key));
if (result) {
result.ttl = this.getExpirationTimestamp(ttl);
const ttlValue = ttl ?? this.options.stdTTL!;
result.ttl = this.getExpirationTimestamp(ttlValue);
this.store.set(this.formatKey(key), result);
return true;
}
Expand Down Expand Up @@ -281,14 +279,15 @@ export default class NodeCache extends eventemitter {
return this.store.has(this.formatKey(key));
}

// Gets the stats of the cache.
public getStats(): NodeCacheStats {
return this._stats;
}

// Flush the whole data.
public flushAll(): void {
this.store.clear();

this.flushStats();
// Event
this.emit('flush');
}
Expand Down
11 changes: 11 additions & 0 deletions packages/node-cache/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ describe('NodeCache', () => {
expect(ttl).toBe(undefined);
});

test('ttl should default to 0 if no ttl is set', () => {
const cache = new NodeCache({checkperiod: 0});
cache.set('foo', 'bar'); // Set to 10 by stdTTL
const ttl = cache.getTtl('foo');
expect(ttl).toBe(0);
cache.ttl('foo');
const ttl2 = cache.getTtl('foo');
expect(ttl2).toBeGreaterThan(ttl!);
});

test('should return 0 if there is no key to delete', () => {
const cache = new NodeCache({checkperiod: 0});
const count = cache.del('foo');
Expand Down Expand Up @@ -203,6 +213,7 @@ describe('NodeCache', () => {
expect(cache.get('n')).toBe(1);
cache.flushAll();
expect(cache.keys()).toEqual([]);
expect(cache.getStats().keys).toBe(0);
});

test('should throw an error on maxKeys', () => {
Expand Down