quick-lru
Advanced tools
Comparing version 5.1.1 to 6.0.0
@@ -1,19 +0,28 @@ | ||
declare namespace QuickLRU { | ||
interface Options<KeyType, ValueType> { | ||
/** | ||
The maximum number of items before evicting the least recently used items. | ||
*/ | ||
readonly maxSize: number; | ||
export interface Options<KeyType, ValueType> { | ||
/** | ||
The maximum number of milliseconds an item should remain in the cache. | ||
/** | ||
Called right before an item is evicted from the cache. | ||
@default Infinity | ||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`). | ||
*/ | ||
onEviction?: (key: KeyType, value: ValueType) => void; | ||
} | ||
By default, `maxAge` will be `Infinity`, which means that items will never expire. | ||
Lazy expiration upon the next write or read call. | ||
Individual expiration of an item can be specified by the `set(key, value, maxAge)` method. | ||
*/ | ||
readonly maxAge?: number; | ||
/** | ||
The maximum number of items before evicting the least recently used items. | ||
*/ | ||
readonly maxSize: number; | ||
/** | ||
Called right before an item is evicted from the cache. | ||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`). | ||
*/ | ||
onEviction?: (key: KeyType, value: ValueType) => void; | ||
} | ||
declare class QuickLRU<KeyType, ValueType> | ||
implements Iterable<[KeyType, ValueType]> { | ||
export default class QuickLRU<KeyType, ValueType> implements Iterable<[KeyType, ValueType]> { | ||
/** | ||
@@ -27,7 +36,7 @@ The stored item count. | ||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop. | ||
The instance is an [`Iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) of `[key, value]` pairs so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop. | ||
@example | ||
``` | ||
import QuickLRU = require('quick-lru'); | ||
import QuickLRU from 'quick-lru'; | ||
@@ -45,3 +54,3 @@ const lru = new QuickLRU({maxSize: 1000}); | ||
*/ | ||
constructor(options: QuickLRU.Options<KeyType, ValueType>); | ||
constructor(options: Options<KeyType, ValueType>); | ||
@@ -51,7 +60,9 @@ [Symbol.iterator](): IterableIterator<[KeyType, ValueType]>; | ||
/** | ||
Set an item. | ||
Set an item. Returns the instance. | ||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified in the constructor, otherwise the item will never expire. | ||
@returns The list instance. | ||
*/ | ||
set(key: KeyType, value: ValueType): this; | ||
set(key: KeyType, value: ValueType, options?: {maxAge?: number}): this; | ||
@@ -90,2 +101,9 @@ /** | ||
/** | ||
Update the `maxSize` in-place, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee. | ||
Useful for on-the-fly tuning of cache sizes in live systems. | ||
*/ | ||
resize(maxSize: number): void; | ||
/** | ||
Iterable for all the keys. | ||
@@ -99,4 +117,12 @@ */ | ||
values(): IterableIterator<ValueType>; | ||
/** | ||
Iterable for all entries, starting with the oldest (ascending in recency). | ||
*/ | ||
entriesAscending(): IterableIterator<[KeyType, ValueType]>; | ||
/** | ||
Iterable for all entries, starting with the newest (descending in recency). | ||
*/ | ||
entriesDescending(): IterableIterator<[KeyType, ValueType]>; | ||
} | ||
export = QuickLRU; |
190
index.js
@@ -1,4 +0,2 @@ | ||
'use strict'; | ||
class QuickLRU { | ||
export default class QuickLRU { | ||
constructor(options = {}) { | ||
@@ -9,3 +7,9 @@ if (!(options.maxSize && options.maxSize > 0)) { | ||
if (typeof options.maxAge === 'number' && options.maxAge === 0) { | ||
throw new TypeError('`maxAge` must be a number greater than 0'); | ||
} | ||
// TODO: Use private class fields when ESLint supports them. | ||
this.maxSize = options.maxSize; | ||
this.maxAge = options.maxAge || Number.POSITIVE_INFINITY; | ||
this.onEviction = options.onEviction; | ||
@@ -17,2 +21,42 @@ this.cache = new Map(); | ||
// TODO: Use private class methods when targeting Node.js 16. | ||
_emitEvictions(cache) { | ||
if (typeof this.onEviction !== 'function') { | ||
return; | ||
} | ||
for (const [key, item] of cache) { | ||
this.onEviction(key, item.value); | ||
} | ||
} | ||
_deleteIfExpired(key, item) { | ||
if (typeof item.expiry === 'number' && item.expiry <= Date.now()) { | ||
if (typeof this.onEviction === 'function') { | ||
this.onEviction(key, item.value); | ||
} | ||
return this.delete(key); | ||
} | ||
return false; | ||
} | ||
_getOrDeleteIfExpired(key, item) { | ||
const deleted = this._deleteIfExpired(key, item); | ||
if (deleted === false) { | ||
return item.value; | ||
} | ||
} | ||
_getItemValue(key, item) { | ||
return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value; | ||
} | ||
_peek(key, cache) { | ||
const item = cache.get(key); | ||
return this._getItemValue(key, item); | ||
} | ||
_set(key, value) { | ||
@@ -24,11 +68,30 @@ this.cache.set(key, value); | ||
this._size = 0; | ||
this._emitEvictions(this.oldCache); | ||
this.oldCache = this.cache; | ||
this.cache = new Map(); | ||
} | ||
} | ||
if (typeof this.onEviction === 'function') { | ||
for (const [key, value] of this.oldCache.entries()) { | ||
this.onEviction(key, value); | ||
_moveToRecent(key, item) { | ||
this.oldCache.delete(key); | ||
this._set(key, item); | ||
} | ||
* _entriesAscending() { | ||
for (const item of this.oldCache) { | ||
const [key, value] = item; | ||
if (!this.cache.has(key)) { | ||
const deleted = this._deleteIfExpired(key, value); | ||
if (deleted === false) { | ||
yield item; | ||
} | ||
} | ||
} | ||
this.oldCache = this.cache; | ||
this.cache = new Map(); | ||
for (const item of this.cache) { | ||
const [key, value] = item; | ||
const deleted = this._deleteIfExpired(key, value); | ||
if (deleted === false) { | ||
yield item; | ||
} | ||
} | ||
@@ -39,25 +102,37 @@ } | ||
if (this.cache.has(key)) { | ||
return this.cache.get(key); | ||
const item = this.cache.get(key); | ||
return this._getItemValue(key, item); | ||
} | ||
if (this.oldCache.has(key)) { | ||
const value = this.oldCache.get(key); | ||
this.oldCache.delete(key); | ||
this._set(key, value); | ||
return value; | ||
const item = this.oldCache.get(key); | ||
if (this._deleteIfExpired(key, item) === false) { | ||
this._moveToRecent(key, item); | ||
return item.value; | ||
} | ||
} | ||
} | ||
set(key, value) { | ||
set(key, value, {maxAge = this.maxAge === Number.POSITIVE_INFINITY ? undefined : Date.now() + this.maxAge} = {}) { | ||
if (this.cache.has(key)) { | ||
this.cache.set(key, value); | ||
this.cache.set(key, { | ||
value, | ||
maxAge | ||
}); | ||
} else { | ||
this._set(key, value); | ||
this._set(key, {value, expiry: maxAge}); | ||
} | ||
return this; | ||
} | ||
has(key) { | ||
return this.cache.has(key) || this.oldCache.has(key); | ||
if (this.cache.has(key)) { | ||
return !this._deleteIfExpired(key, this.cache.get(key)); | ||
} | ||
if (this.oldCache.has(key)) { | ||
return !this._deleteIfExpired(key, this.oldCache.get(key)); | ||
} | ||
return false; | ||
} | ||
@@ -67,7 +142,7 @@ | ||
if (this.cache.has(key)) { | ||
return this.cache.get(key); | ||
return this._peek(key, this.cache); | ||
} | ||
if (this.oldCache.has(key)) { | ||
return this.oldCache.get(key); | ||
return this._peek(key, this.oldCache); | ||
} | ||
@@ -91,2 +166,26 @@ } | ||
resize(newSize) { | ||
if (!(newSize && newSize > 0)) { | ||
throw new TypeError('`maxSize` must be a number greater than 0'); | ||
} | ||
const items = [...this._entriesAscending()]; | ||
const removeCount = items.length - newSize; | ||
if (removeCount < 0) { | ||
this.cache = new Map(items); | ||
this.oldCache = new Map(); | ||
this._size = items.length; | ||
} else { | ||
if (removeCount > 0) { | ||
this._emitEvictions(items.slice(0, removeCount)); | ||
} | ||
this.oldCache = new Map(items.slice(removeCount)); | ||
this.cache = new Map(); | ||
this._size = 0; | ||
} | ||
this.maxSize = newSize; | ||
} | ||
* keys() { | ||
@@ -106,9 +205,16 @@ for (const [key] of this) { | ||
for (const item of this.cache) { | ||
yield item; | ||
const [key, value] = item; | ||
const deleted = this._deleteIfExpired(key, value); | ||
if (deleted === false) { | ||
yield [key, value.value]; | ||
} | ||
} | ||
for (const item of this.oldCache) { | ||
const [key] = item; | ||
const [key, value] = item; | ||
if (!this.cache.has(key)) { | ||
yield item; | ||
const deleted = this._deleteIfExpired(key, value); | ||
if (deleted === false) { | ||
yield [key, value.value]; | ||
} | ||
} | ||
@@ -118,3 +224,37 @@ } | ||
* entriesDescending() { | ||
let items = [...this.cache]; | ||
for (let i = items.length - 1; i >= 0; --i) { | ||
const item = items[i]; | ||
const [key, value] = item; | ||
const deleted = this._deleteIfExpired(key, value); | ||
if (deleted === false) { | ||
yield [key, value.value]; | ||
} | ||
} | ||
items = [...this.oldCache]; | ||
for (let i = items.length - 1; i >= 0; --i) { | ||
const item = items[i]; | ||
const [key, value] = item; | ||
if (!this.cache.has(key)) { | ||
const deleted = this._deleteIfExpired(key, value); | ||
if (deleted === false) { | ||
yield [key, value.value]; | ||
} | ||
} | ||
} | ||
} | ||
* entriesAscending() { | ||
for (const [key, value] of this._entriesAscending()) { | ||
yield [key, value.value]; | ||
} | ||
} | ||
get size() { | ||
if (!this._size) { | ||
return this.oldCache.size; | ||
} | ||
let oldCacheSize = 0; | ||
@@ -130,3 +270,1 @@ for (const key of this.oldCache.keys()) { | ||
} | ||
module.exports = QuickLRU; |
{ | ||
"name": "quick-lru", | ||
"version": "5.1.1", | ||
"version": "6.0.0", | ||
"description": "Simple “Least Recently Used” (LRU) cache", | ||
@@ -13,7 +13,10 @@ "license": "MIT", | ||
}, | ||
"type": "module", | ||
"exports": "./index.js", | ||
"engines": { | ||
"node": ">=10" | ||
"node": ">=12" | ||
}, | ||
"scripts": { | ||
"test": "xo && nyc ava && tsd" | ||
"//test": "xo && nyc ava && tsd", | ||
"test": "xo && ava && tsd" | ||
}, | ||
@@ -38,8 +41,13 @@ "files": [ | ||
"devDependencies": { | ||
"ava": "^2.0.0", | ||
"coveralls": "^3.0.3", | ||
"nyc": "^15.0.0", | ||
"tsd": "^0.11.0", | ||
"xo": "^0.26.0" | ||
"ava": "^3.15.0", | ||
"nyc": "^15.1.0", | ||
"tsd": "^0.14.0", | ||
"xo": "^0.37.1" | ||
}, | ||
"nyc": { | ||
"reporter": [ | ||
"text", | ||
"lcov" | ||
] | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
# quick-lru [![Build Status](https://travis-ci.org/sindresorhus/quick-lru.svg?branch=master)](https://travis-ci.org/sindresorhus/quick-lru) [![Coverage Status](https://coveralls.io/repos/github/sindresorhus/quick-lru/badge.svg?branch=master)](https://coveralls.io/github/sindresorhus/quick-lru?branch=master) | ||
# quick-lru [![Coverage Status](https://codecov.io/gh/sindresorhus/quick-lru/branch/master/graph/badge.svg)](https://codecov.io/gh/sindresorhus/quick-lru/branch/master) | ||
@@ -18,3 +18,3 @@ > Simple [“Least Recently Used” (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29) | ||
```js | ||
const QuickLRU = require('quick-lru'); | ||
import QuickLRU from 'quick-lru'; | ||
@@ -49,2 +49,14 @@ const lru = new QuickLRU({maxSize: 1000}); | ||
#### maxAge | ||
Type: `number`\ | ||
Default: `Infinity` | ||
The maximum number of milliseconds an item should remain in cache. | ||
By default maxAge will be Infinity, which means that items will never expire. | ||
Lazy expiration happens upon the next `write` or `read` call. | ||
Individual expiration of an item can be specified by the `set(key, value, options)` method. | ||
#### onEviction | ||
@@ -61,10 +73,12 @@ | ||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop. | ||
The instance is an [`Iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) of `[key, value]` pairs so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop. | ||
Both `key` and `value` can be of any type. | ||
#### .set(key, value) | ||
#### .set(key, value, options?) | ||
Set an item. Returns the instance. | ||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified on the constructor, otherwise the item will never expire. | ||
#### .get(key) | ||
@@ -92,2 +106,8 @@ | ||
#### .resize(maxSize) | ||
Update the `maxSize`, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee. | ||
Useful for on-the-fly tuning of cache sizes in live systems. | ||
#### .keys() | ||
@@ -101,2 +121,10 @@ | ||
#### .entriesAscending() | ||
Iterable for all entries, starting with the oldest (ascending in recency). | ||
#### .entriesDescending() | ||
Iterable for all entries, starting with the newest (descending in recency). | ||
#### .size | ||
@@ -106,2 +134,6 @@ | ||
## Related | ||
- [yocto-queue](https://github.com/sindresorhus/yocto-queue) - Tiny queue data structure | ||
--- | ||
@@ -108,0 +140,0 @@ |
Sorry, the diff of this file is not supported yet
14276
4
310
144
Yes