@extra-memoize/memory-cache 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/lib/es2015/caches/cache.d.ts +8 -0
- package/lib/es2015/caches/cache.js +28 -0
- package/lib/es2015/caches/cache.js.map +1 -0
- package/lib/es2015/caches/index.d.ts +1 -0
- package/lib/es2015/caches/index.js +1 -0
- package/lib/es2015/caches/index.js.map +1 -1
- package/lib/es2018/caches/cache.d.ts +8 -0
- package/lib/es2018/caches/cache.js +28 -0
- package/lib/es2018/caches/cache.js.map +1 -0
- package/lib/es2018/caches/index.d.ts +1 -0
- package/lib/es2018/caches/index.js +1 -0
- package/lib/es2018/caches/index.js.map +1 -1
- package/package.json +2 -2
- package/src/caches/cache.ts +25 -0
- package/src/caches/expirable-cache-with-stale-if-error.ts +39 -0
- package/src/caches/expirable-cache-with-stale-while-revalidate-and-stale-if-error.ts +51 -0
- package/src/caches/expirable-cache-with-stale-while-revalidate.ts +40 -0
- package/src/caches/expirable-cache.ts +26 -0
- package/src/caches/index.ts +13 -0
- package/src/caches/lru-cache.ts +26 -0
- package/src/caches/tlru-cache-with-stale-if-error.ts +43 -0
- package/src/caches/tlru-cache-with-stale-while-revalidate-and-stale-if-error.ts +52 -0
- package/src/caches/tlru-cache-with-stale-while-revalidate.ts +44 -0
- package/src/caches/tlru-cache.ts +26 -0
- package/src/index.ts +1 -0
- package/CHANGELOG.md +0 -42
- package/dist/es2015/index.min.mjs +0 -2
- package/dist/es2015/index.min.mjs.map +0 -1
- package/dist/es2015/index.mjs +0 -22850
- package/dist/es2015/index.mjs.map +0 -1
- package/dist/es2015/index.umd.js +0 -22868
- package/dist/es2015/index.umd.js.map +0 -1
- package/dist/es2015/index.umd.min.js +0 -2
- package/dist/es2015/index.umd.min.js.map +0 -1
- package/dist/es2018/index.min.mjs +0 -2
- package/dist/es2018/index.min.mjs.map +0 -1
- package/dist/es2018/index.mjs +0 -22850
- package/dist/es2018/index.mjs.map +0 -1
- package/dist/es2018/index.umd.js +0 -22868
- package/dist/es2018/index.umd.js.map +0 -1
- package/dist/es2018/index.umd.min.js +0 -2
- package/dist/es2018/index.umd.min.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,11 +7,19 @@ yarn add @extra-memoize/memory-cache
|
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## API
|
|
10
|
+
### Cache
|
|
11
|
+
```ts
|
|
12
|
+
class Cache<T> implements ICache<T> {
|
|
13
|
+
clear(): void
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
10
17
|
### LRUCache
|
|
11
18
|
```ts
|
|
12
19
|
class LRUCache<T> implements ICache<T> {
|
|
13
20
|
constructor(limit: number)
|
|
14
21
|
|
|
22
|
+
delete(key: string): void
|
|
15
23
|
clear(): void
|
|
16
24
|
}
|
|
17
25
|
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Cache = void 0;
|
|
4
|
+
const extra_memoize_1 = require("extra-memoize");
|
|
5
|
+
class Cache {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.map = new Map();
|
|
8
|
+
}
|
|
9
|
+
set(key, value) {
|
|
10
|
+
this.map.set(key, value);
|
|
11
|
+
}
|
|
12
|
+
get(key) {
|
|
13
|
+
if (this.map.has(key)) {
|
|
14
|
+
return [extra_memoize_1.State.Hit, this.map.get(key)];
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return [extra_memoize_1.State.Miss];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
delete(key) {
|
|
21
|
+
this.map.delete(key);
|
|
22
|
+
}
|
|
23
|
+
clear() {
|
|
24
|
+
this.map.clear();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.Cache = Cache;
|
|
28
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/caches/cache.ts"],"names":[],"mappings":";;;AAAA,iDAA6C;AAE7C,MAAa,KAAK;IAAlB;QACU,QAAG,GAAG,IAAI,GAAG,EAAE,CAAA;IAqBzB,CAAC;IAnBC,GAAG,CAAC,GAAW,EAAE,KAAQ;QACvB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC1B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YACrB,OAAO,CAAC,qBAAK,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;SACtC;aAAM;YACL,OAAO,CAAC,qBAAK,CAAC,IAAI,CAAC,CAAA;SACpB;IACH,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;IAClB,CAAC;CACF;AAtBD,sBAsBC"}
|
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./cache"), exports);
|
|
17
18
|
__exportStar(require("./lru-cache"), exports);
|
|
18
19
|
__exportStar(require("./expirable-cache"), exports);
|
|
19
20
|
__exportStar(require("./expirable-cache-with-stale-while-revalidate"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/caches/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B;AAE3B,oDAAiC;AACjC,gFAA6D;AAC7D,wEAAqD;AACrD,mGAAgF;AAEhF,+CAA4B;AAC5B,2EAAwD;AACxD,mEAAgD;AAChD,8FAA2E"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/caches/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAuB;AAEvB,8CAA2B;AAE3B,oDAAiC;AACjC,gFAA6D;AAC7D,wEAAqD;AACrD,mGAAgF;AAEhF,+CAA4B;AAC5B,2EAAwD;AACxD,mEAAgD;AAChD,8FAA2E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Cache = void 0;
|
|
4
|
+
const extra_memoize_1 = require("extra-memoize");
|
|
5
|
+
class Cache {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.map = new Map();
|
|
8
|
+
}
|
|
9
|
+
set(key, value) {
|
|
10
|
+
this.map.set(key, value);
|
|
11
|
+
}
|
|
12
|
+
get(key) {
|
|
13
|
+
if (this.map.has(key)) {
|
|
14
|
+
return [extra_memoize_1.State.Hit, this.map.get(key)];
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return [extra_memoize_1.State.Miss];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
delete(key) {
|
|
21
|
+
this.map.delete(key);
|
|
22
|
+
}
|
|
23
|
+
clear() {
|
|
24
|
+
this.map.clear();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.Cache = Cache;
|
|
28
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/caches/cache.ts"],"names":[],"mappings":";;;AAAA,iDAA6C;AAE7C,MAAa,KAAK;IAAlB;QACU,QAAG,GAAG,IAAI,GAAG,EAAE,CAAA;IAqBzB,CAAC;IAnBC,GAAG,CAAC,GAAW,EAAE,KAAQ;QACvB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC1B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YACrB,OAAO,CAAC,qBAAK,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;SACtC;aAAM;YACL,OAAO,CAAC,qBAAK,CAAC,IAAI,CAAC,CAAA;SACpB;IACH,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;IAClB,CAAC;CACF;AAtBD,sBAsBC"}
|
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./cache"), exports);
|
|
17
18
|
__exportStar(require("./lru-cache"), exports);
|
|
18
19
|
__exportStar(require("./expirable-cache"), exports);
|
|
19
20
|
__exportStar(require("./expirable-cache-with-stale-while-revalidate"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/caches/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA2B;AAE3B,oDAAiC;AACjC,gFAA6D;AAC7D,wEAAqD;AACrD,mGAAgF;AAEhF,+CAA4B;AAC5B,2EAAwD;AACxD,mEAAgD;AAChD,8FAA2E"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/caches/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAuB;AAEvB,8CAA2B;AAE3B,oDAAiC;AACjC,gFAA6D;AAC7D,wEAAqD;AACrD,mGAAgF;AAEhF,+CAA4B;AAC5B,2EAAwD;AACxD,mEAAgD;AAChD,8FAA2E"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ICache, State } from 'extra-memoize'
|
|
2
|
+
|
|
3
|
+
export class Cache<T> implements ICache<T> {
|
|
4
|
+
private map = new Map()
|
|
5
|
+
|
|
6
|
+
set(key: string, value: T): void {
|
|
7
|
+
this.map.set(key, value)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get(key: string): [State.Miss] | [State.Hit, T] {
|
|
11
|
+
if (this.map.has(key)) {
|
|
12
|
+
return [State.Hit, this.map.get(key)]
|
|
13
|
+
} else {
|
|
14
|
+
return [State.Miss]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
delete(key: string): void {
|
|
19
|
+
this.map.delete(key)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
clear(): void {
|
|
23
|
+
this.map.clear()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ExpirableCache } from './expirable-cache'
|
|
2
|
+
import { IStaleIfErrorCache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
interface IRecord<T> {
|
|
5
|
+
updatedAt: number
|
|
6
|
+
value: T
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class ExpirableCacheWithStaleIfError<T> implements IStaleIfErrorCache<T> {
|
|
10
|
+
private cache: ExpirableCache<IRecord<T>>
|
|
11
|
+
|
|
12
|
+
constructor(private timeToLive: number, private staleIfError: number) {
|
|
13
|
+
this.cache = new ExpirableCache(timeToLive + staleIfError)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
set(key: string, value: T): void {
|
|
17
|
+
this.cache.set(key, {
|
|
18
|
+
value
|
|
19
|
+
, updatedAt: Date.now()
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get(key: string): [State.Miss] | [State.Hit | State.StaleIfError, T] {
|
|
24
|
+
const [state, record] = this.cache.get(key)
|
|
25
|
+
if (state === State.Miss) {
|
|
26
|
+
return [State.Miss]
|
|
27
|
+
} else {
|
|
28
|
+
const elapsed = Date.now() - record.updatedAt
|
|
29
|
+
if (elapsed <= this.timeToLive) {
|
|
30
|
+
return [State.Hit, record.value]
|
|
31
|
+
} else if (elapsed <= this.timeToLive + this.staleIfError) {
|
|
32
|
+
return [State.StaleIfError, record.value]
|
|
33
|
+
} else {
|
|
34
|
+
// just in case
|
|
35
|
+
return [State.Miss]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ExpirableCache } from './expirable-cache'
|
|
2
|
+
import { IStaleWhileRevalidateAndStaleIfErrorCache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
interface IRecord<T> {
|
|
5
|
+
updatedAt: number
|
|
6
|
+
value: T
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class ExpirableCacheWithStaleWhileRevalidateAndStaleIfError<T> implements IStaleWhileRevalidateAndStaleIfErrorCache<T> {
|
|
10
|
+
private cache: ExpirableCache<IRecord<T>>
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
private timeToLive: number
|
|
14
|
+
, private staleWhileRevalidate: number
|
|
15
|
+
, private staleIfError: number
|
|
16
|
+
) {
|
|
17
|
+
this.cache = new ExpirableCache(timeToLive + staleWhileRevalidate + staleIfError)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
set(key: string, value: T): void {
|
|
21
|
+
this.cache.set(key, {
|
|
22
|
+
value
|
|
23
|
+
, updatedAt: Date.now()
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get(key: string): [State.Miss]
|
|
28
|
+
| [
|
|
29
|
+
| State.Hit
|
|
30
|
+
| State.StaleWhileRevalidate
|
|
31
|
+
| State.StaleIfError
|
|
32
|
+
, T
|
|
33
|
+
] {
|
|
34
|
+
const [state, record] = this.cache.get(key)
|
|
35
|
+
if (state === State.Miss) {
|
|
36
|
+
return [State.Miss]
|
|
37
|
+
} else {
|
|
38
|
+
const elapsed = Date.now() - record.updatedAt
|
|
39
|
+
if (elapsed <= this.timeToLive) {
|
|
40
|
+
return [State.Hit, record.value]
|
|
41
|
+
} else if (elapsed <= this.timeToLive + this.staleWhileRevalidate) {
|
|
42
|
+
return [State.StaleWhileRevalidate, record.value]
|
|
43
|
+
} else if (elapsed <= this.timeToLive + this.staleWhileRevalidate + this.staleIfError) {
|
|
44
|
+
return [State.StaleIfError, record.value]
|
|
45
|
+
} else {
|
|
46
|
+
// just in case
|
|
47
|
+
return [State.Miss]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ExpirableCache } from './expirable-cache'
|
|
2
|
+
import { IStaleWhileRevalidateCache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
interface IRecord<T> {
|
|
5
|
+
updatedAt: number
|
|
6
|
+
value: T
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class ExpirableCacheWithStaleWhileRevalidate<T> implements IStaleWhileRevalidateCache<T> {
|
|
10
|
+
private cache: ExpirableCache<IRecord<T>>
|
|
11
|
+
|
|
12
|
+
constructor(private timeToLive: number, private staleWhileRevalidate: number) {
|
|
13
|
+
this.cache = new ExpirableCache(timeToLive + staleWhileRevalidate)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
set(key: string, value: T): void {
|
|
17
|
+
this.cache.set(key, {
|
|
18
|
+
value
|
|
19
|
+
, updatedAt: Date.now()
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get(key: string): [State.Miss] | [State.Hit | State.StaleWhileRevalidate, T] {
|
|
24
|
+
const [state, record] = this.cache.get(key)
|
|
25
|
+
if (state === State.Miss) {
|
|
26
|
+
return [State.Miss]
|
|
27
|
+
} else {
|
|
28
|
+
if (this.isStaleWhileRevalidate(record)) {
|
|
29
|
+
return [State.StaleWhileRevalidate, record.value]
|
|
30
|
+
} else {
|
|
31
|
+
return [State.Hit, record.value]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private isStaleWhileRevalidate(record: IRecord<T>): boolean {
|
|
37
|
+
return Date.now() - record.updatedAt > this.timeToLive
|
|
38
|
+
&& Date.now() - record.updatedAt <= this.timeToLive + this.staleWhileRevalidate
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ExpirableMap } from '@blackglory/structures'
|
|
2
|
+
import { ICache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
export class ExpirableCache<T> implements ICache<T> {
|
|
5
|
+
private map: ExpirableMap<string, T>
|
|
6
|
+
|
|
7
|
+
constructor(private timeToLive: number) {
|
|
8
|
+
this.map = new ExpirableMap()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get(key: string): [State.Miss] | [State.Hit, T] {
|
|
12
|
+
if (this.map.has(key)) {
|
|
13
|
+
return [State.Hit, this.map.get(key)!]
|
|
14
|
+
} else {
|
|
15
|
+
return [State.Miss]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
set(key: string, value: T): void {
|
|
20
|
+
this.map.set(key, value, this.timeToLive)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
clear(): void {
|
|
24
|
+
this.map.clear()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './cache'
|
|
2
|
+
|
|
3
|
+
export * from './lru-cache'
|
|
4
|
+
|
|
5
|
+
export * from './expirable-cache'
|
|
6
|
+
export * from './expirable-cache-with-stale-while-revalidate'
|
|
7
|
+
export * from './expirable-cache-with-stale-if-error'
|
|
8
|
+
export * from './expirable-cache-with-stale-while-revalidate-and-stale-if-error'
|
|
9
|
+
|
|
10
|
+
export * from './tlru-cache'
|
|
11
|
+
export * from './tlru-cache-with-stale-while-revalidate'
|
|
12
|
+
export * from './tlru-cache-with-stale-if-error'
|
|
13
|
+
export * from './tlru-cache-with-stale-while-revalidate-and-stale-if-error'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LRUMap } from '@blackglory/structures'
|
|
2
|
+
import { ICache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
export class LRUCache<T> implements ICache<T> {
|
|
5
|
+
private map: LRUMap<string, T>
|
|
6
|
+
|
|
7
|
+
constructor(limit: number) {
|
|
8
|
+
this.map = new LRUMap(limit)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
set(key: string, value: T): void {
|
|
12
|
+
this.map.set(key, value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get(key: string): [State.Miss] | [State.Hit, T] {
|
|
16
|
+
if (this.map.has(key)) {
|
|
17
|
+
return [State.Hit, this.map.get(key)!]
|
|
18
|
+
} else {
|
|
19
|
+
return [State.Miss]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
clear(): void {
|
|
24
|
+
this.map.clear()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { TLRUCache } from './tlru-cache'
|
|
2
|
+
import { IStaleIfErrorCache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
interface IRecord<T> {
|
|
5
|
+
updatedAt: number
|
|
6
|
+
value: T
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class TLRUCacheWithStaleIfError<T> implements IStaleIfErrorCache<T> {
|
|
10
|
+
private cache: TLRUCache<IRecord<T>>
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
limit: number
|
|
14
|
+
, private timeToLive: number
|
|
15
|
+
, private staleIfError: number
|
|
16
|
+
) {
|
|
17
|
+
this.cache = new TLRUCache(limit, timeToLive + staleIfError)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
set(key: string, value: T): void {
|
|
21
|
+
this.cache.set(key, {
|
|
22
|
+
value
|
|
23
|
+
, updatedAt: Date.now()
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get(key: string): [State.Miss] | [State.Hit | State.StaleIfError, T] {
|
|
28
|
+
const [state, record] = this.cache.get(key)
|
|
29
|
+
if (state === State.Miss) {
|
|
30
|
+
return [State.Miss]
|
|
31
|
+
} else {
|
|
32
|
+
const elapsed = Date.now() - record.updatedAt
|
|
33
|
+
if (elapsed <= this.timeToLive) {
|
|
34
|
+
return [State.Hit, record.value]
|
|
35
|
+
} else if (elapsed <= this.timeToLive + this.staleIfError) {
|
|
36
|
+
return [State.StaleIfError, record.value]
|
|
37
|
+
} else {
|
|
38
|
+
// just in case
|
|
39
|
+
return [State.Miss]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { TLRUCache } from './tlru-cache'
|
|
2
|
+
import { IStaleWhileRevalidateAndStaleIfErrorCache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
interface IRecord<T> {
|
|
5
|
+
updatedAt: number
|
|
6
|
+
value: T
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class TLRUCacheWithStaleWhileRevalidateAndStaleIfError<T> implements IStaleWhileRevalidateAndStaleIfErrorCache<T> {
|
|
10
|
+
private cache: TLRUCache<IRecord<T>>
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
limit: number
|
|
14
|
+
, private timeToLive: number
|
|
15
|
+
, private staleWhileRevalidate: number
|
|
16
|
+
, private staleIfError: number
|
|
17
|
+
) {
|
|
18
|
+
this.cache = new TLRUCache(limit, timeToLive + staleWhileRevalidate + staleIfError)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
set(key: string, value: T): void {
|
|
22
|
+
this.cache.set(key, {
|
|
23
|
+
value
|
|
24
|
+
, updatedAt: Date.now()
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get(key: string): [State.Miss]
|
|
29
|
+
| [
|
|
30
|
+
| State.Hit
|
|
31
|
+
| State.StaleWhileRevalidate
|
|
32
|
+
| State.StaleIfError
|
|
33
|
+
, T
|
|
34
|
+
] {
|
|
35
|
+
const [state, record] = this.cache.get(key)
|
|
36
|
+
if (state === State.Miss) {
|
|
37
|
+
return [State.Miss]
|
|
38
|
+
} else {
|
|
39
|
+
const elapsed = Date.now() - record.updatedAt
|
|
40
|
+
if (elapsed <= this.timeToLive) {
|
|
41
|
+
return [State.Hit, record.value]
|
|
42
|
+
} else if (elapsed <= this.timeToLive + this.staleWhileRevalidate) {
|
|
43
|
+
return [State.StaleWhileRevalidate, record.value]
|
|
44
|
+
} else if (elapsed <= this.timeToLive + this.staleWhileRevalidate + this.staleIfError) {
|
|
45
|
+
return [State.StaleIfError, record.value]
|
|
46
|
+
} else {
|
|
47
|
+
// just in case
|
|
48
|
+
return [State.Miss]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TLRUCache } from './tlru-cache'
|
|
2
|
+
import { IStaleWhileRevalidateCache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
interface IRecord<T> {
|
|
5
|
+
updatedAt: number
|
|
6
|
+
value: T
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class TLRUCacheWithStaleWhileRevalidate<T> implements IStaleWhileRevalidateCache<T> {
|
|
10
|
+
private cache: TLRUCache<IRecord<T>>
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
limit: number
|
|
14
|
+
, private timeToLive: number
|
|
15
|
+
, private staleWhileRevalidate: number
|
|
16
|
+
) {
|
|
17
|
+
this.cache = new TLRUCache(limit, timeToLive + staleWhileRevalidate)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
set(key: string, value: T): void {
|
|
21
|
+
this.cache.set(key, {
|
|
22
|
+
value
|
|
23
|
+
, updatedAt: Date.now()
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get(key: string): [State.Miss] | [State.Hit | State.StaleWhileRevalidate, T] {
|
|
28
|
+
const [state, record] = this.cache.get(key)
|
|
29
|
+
if (state === State.Miss) {
|
|
30
|
+
return [State.Miss]
|
|
31
|
+
} else {
|
|
32
|
+
if (this.isStaleWhileRevalidate(record)) {
|
|
33
|
+
return [State.StaleWhileRevalidate, record.value]
|
|
34
|
+
} else {
|
|
35
|
+
return [State.Hit, record.value]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isStaleWhileRevalidate(record: IRecord<T>): boolean {
|
|
41
|
+
return Date.now() - record.updatedAt > this.timeToLive
|
|
42
|
+
&& Date.now() - record.updatedAt <= this.timeToLive + this.staleWhileRevalidate
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TLRUMap } from '@blackglory/structures'
|
|
2
|
+
import { ICache, State } from 'extra-memoize'
|
|
3
|
+
|
|
4
|
+
export class TLRUCache<T> implements ICache<T> {
|
|
5
|
+
private map: TLRUMap<string, T>
|
|
6
|
+
|
|
7
|
+
constructor(limit: number, private timeToLive: number) {
|
|
8
|
+
this.map = new TLRUMap(limit)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
set(key: string, value: T): void {
|
|
12
|
+
this.map.set(key, value, this.timeToLive)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get(key: string): [State.Miss] | [State.Hit, T] {
|
|
16
|
+
if (this.map.has(key)) {
|
|
17
|
+
return [State.Hit, this.map.get(key)!]
|
|
18
|
+
} else {
|
|
19
|
+
return [State.Miss]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
clear(): void {
|
|
24
|
+
this.map.clear()
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@caches'
|
package/CHANGELOG.md
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
|
-
|
|
5
|
-
### [0.2.8](https://github.com/extra-memoize/memory-cache/compare/v0.2.7...v0.2.8) (2023-01-28)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
### Bug Fixes
|
|
9
|
-
|
|
10
|
-
* dependencies ([814b9df](https://github.com/extra-memoize/memory-cache/commit/814b9df8512f4b4dc6470b1fa1ec1ed31dbd3f83))
|
|
11
|
-
|
|
12
|
-
### [0.2.7](https://github.com/extra-memoize/memory-cache/compare/v0.2.6...v0.2.7) (2023-01-28)
|
|
13
|
-
|
|
14
|
-
### [0.2.6](https://github.com/extra-memoize/memory-cache/compare/v0.2.5...v0.2.6) (2023-01-21)
|
|
15
|
-
|
|
16
|
-
### [0.2.5](https://github.com/extra-memoize/memory-cache/compare/v0.2.4...v0.2.5) (2022-11-03)
|
|
17
|
-
|
|
18
|
-
### [0.2.4](https://github.com/extra-memoize/memory-cache/compare/v0.2.3...v0.2.4) (2022-09-09)
|
|
19
|
-
|
|
20
|
-
### [0.2.3](https://github.com/extra-memoize/memory-cache/compare/v0.2.2...v0.2.3) (2022-08-06)
|
|
21
|
-
|
|
22
|
-
### [0.2.2](https://github.com/extra-memoize/memory-cache/compare/v0.2.1...v0.2.2) (2022-08-04)
|
|
23
|
-
|
|
24
|
-
### [0.2.1](https://github.com/extra-memoize/memory-cache/compare/v0.2.0...v0.2.1) (2022-06-26)
|
|
25
|
-
|
|
26
|
-
## [0.2.0](https://github.com/extra-memoize/memory-cache/compare/v0.1.0...v0.2.0) (2022-05-10)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
### ⚠ BREAKING CHANGES
|
|
30
|
-
|
|
31
|
-
* use extra-memoize@0.6
|
|
32
|
-
|
|
33
|
-
### Features
|
|
34
|
-
|
|
35
|
-
* use extra-memoize@0.6 ([9e72236](https://github.com/extra-memoize/memory-cache/commit/9e72236114a1764166d479afe384193081c727b3))
|
|
36
|
-
|
|
37
|
-
## 0.1.0 (2022-04-08)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
### Features
|
|
41
|
-
|
|
42
|
-
* init ([5c4d170](https://github.com/extra-memoize/memory-cache/commit/5c4d170569bb5aec61987ebac77f770d45a6cff7))
|