@darco2903/expiry-cache 2.1.0 → 3.0.0
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 +133 -25
- package/dist/ExpiryCache.d.ts +9 -22
- package/dist/ExpiryCache.js +8 -29
- package/dist/ExpiryCacheAsync.d.ts +11 -24
- package/dist/ExpiryCacheAsync.js +10 -34
- package/dist/ExpiryCacheSafe.d.ts +18 -0
- package/dist/ExpiryCacheSafe.js +31 -0
- package/dist/ExpiryCacheSafeAsync.d.ts +20 -0
- package/dist/ExpiryCacheSafeAsync.js +40 -0
- package/dist/ReturnOptions.d.ts +15 -0
- package/dist/ReturnOptions.js +24 -0
- package/dist/base/ExpiryCacheAsyncBase.d.ts +13 -0
- package/dist/base/ExpiryCacheAsyncBase.js +11 -0
- package/dist/{ExpiryCacheBase.d.ts → base/ExpiryCacheBase.d.ts} +27 -29
- package/dist/{ExpiryCacheBase.js → base/ExpiryCacheBase.js} +57 -22
- package/dist/base/ExpiryCacheSyncBase.d.ts +5 -0
- package/dist/base/ExpiryCacheSyncBase.js +3 -0
- package/dist/base/index.d.ts +2 -0
- package/dist/base/index.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/interface/ExpiryCacheAsync.d.ts +6 -0
- package/dist/interface/ExpiryCacheAsync.js +2 -0
- package/dist/interface/ExpiryCacheSafe.d.ts +6 -0
- package/dist/interface/ExpiryCacheSafe.js +2 -0
- package/dist/interface/ExpiryCacheSync.d.ts +6 -0
- package/dist/interface/ExpiryCacheSync.js +2 -0
- package/dist/interface/ExpiryCacheUnsafe.d.ts +6 -0
- package/dist/interface/ExpiryCacheUnsafe.js +2 -0
- package/dist/interface/index.d.ts +4 -0
- package/dist/types/RefreshFunction.d.ts +11 -0
- package/dist/types/RefreshFunction.js +1 -0
- package/dist/types/ReturnType.d.ts +10 -0
- package/dist/types/ReturnType.js +1 -0
- package/dist/types/events.d.ts +8 -0
- package/dist/types/events.js +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +17 -0
- package/package.json +8 -5
- package/dist/types.d.ts +0 -2
- /package/dist/{types.js → interface/index.js} +0 -0
package/README.md
CHANGED
|
@@ -12,8 +12,11 @@ A simple in-memory cache with expiry functionality. It allows you to store data
|
|
|
12
12
|
- **Automatic Expiry**: Cache entries automatically expire after a specified duration.
|
|
13
13
|
- **Manual Expiry**: Manually expire cache entries when needed.
|
|
14
14
|
- **Synchronous and Asynchronous Fetching**: Support for both synchronous and asynchronous data fetching.
|
|
15
|
-
- **
|
|
15
|
+
- **Safe Fetching**: Safe cache fetching that returns a `Result` type, allowing for error handling without throwing exceptions.
|
|
16
|
+
- **Stale Data Access**: Option to access stale data even after it has expired.
|
|
16
17
|
- **Parameterized Fetching**: Support for fetcher functions that require parameters.
|
|
18
|
+
- **Return Options**: Allow to set expiry time when refreshing the cache. For exemple API tokens that return their expiry time.
|
|
19
|
+
- **Event Emission**: Emit events on cache refresh and expiry for better integration with other parts of your application.
|
|
17
20
|
|
|
18
21
|
## Installation
|
|
19
22
|
|
|
@@ -28,31 +31,46 @@ npm install @darco2903/expiry-cache
|
|
|
28
31
|
```ts
|
|
29
32
|
import { ExpiryCache } from "@darco2903/expiry-cache";
|
|
30
33
|
|
|
31
|
-
const cache = new ExpiryCache("initial", () => "refreshed", 1000); //
|
|
32
|
-
console.log(cache.getData()); //
|
|
34
|
+
const cache = new ExpiryCache("initial", () => "refreshed", 1000); // ExpiryCache<string, () => string>
|
|
35
|
+
console.log(cache.getData()); // initial
|
|
33
36
|
|
|
34
37
|
await new Promise((resolve) => setTimeout(resolve, 1100)); // wait for cache to expire
|
|
35
|
-
console.log(cache.isExpired); //
|
|
36
|
-
console.log(cache.getData()); //
|
|
37
|
-
console.log(cache.getRawData()); //
|
|
38
|
+
console.log(cache.isExpired); // true
|
|
39
|
+
console.log(cache.getData()); // null
|
|
40
|
+
console.log(cache.getRawData()); // initial (returns the raw data even if expired)
|
|
38
41
|
|
|
39
42
|
cache.refresh();
|
|
40
|
-
console.log(cache.getData()); //
|
|
43
|
+
console.log(cache.getData()); // refreshed
|
|
41
44
|
|
|
42
45
|
await new Promise((resolve) => setTimeout(resolve, 1100)); // wait for cache to expire
|
|
43
|
-
console.log(cache.isExpired); //
|
|
44
|
-
console.log(cache.getDataOrRefresh()); //
|
|
46
|
+
console.log(cache.isExpired); // true
|
|
47
|
+
console.log(cache.getDataOrRefresh()); // refreshed (refreshes the cache and returns the new value)
|
|
45
48
|
|
|
46
|
-
console.log(cache.expirationTime); //
|
|
47
|
-
console.log(cache.expiresAt); //
|
|
48
|
-
console.log(cache.timeToLive); //
|
|
49
|
+
console.log(cache.expirationTime); // 1000
|
|
50
|
+
console.log(cache.expiresAt); // current timestamp + 1000 milliseconds
|
|
51
|
+
console.log(cache.timeToLive); // time remaining until expiration in milliseconds
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
console.log(cache.isExpired); //
|
|
53
|
+
cache.expire(); // Manually expire the cache
|
|
54
|
+
console.log(cache.isExpired); // true
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### With Parameters
|
|
58
|
+
|
|
59
|
+
You can also use the cache with a fetcher function that requires parameters. In this case, the `refresh` method accepts the necessary parameters to fetch the new data.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { ExpiryCache } from "@darco2903/expiry-cache";
|
|
63
|
+
|
|
64
|
+
const cache = new ExpiryCache(10, (a: number, b: number) => a + b, 200); // ExpiryCache<number, (a: number, b: number) => number>
|
|
65
|
+
|
|
66
|
+
cache.refresh(5, 7); // typed: refresh(a: number, b: number)
|
|
67
|
+
console.log(cache.getData()); // 12
|
|
52
68
|
```
|
|
53
69
|
|
|
54
70
|
### With Async Fetcher
|
|
55
71
|
|
|
72
|
+
Same as the ExpiryCache, but with an asynchronous fetcher function. The `refresh` method will return a promise that resolves when the cache has been updated.
|
|
73
|
+
|
|
56
74
|
```ts
|
|
57
75
|
import { ExpiryCacheAsync } from "@darco2903/expiry-cache";
|
|
58
76
|
|
|
@@ -68,37 +86,127 @@ async function getApiData(): Promise<string> {
|
|
|
68
86
|
const cache = new ExpiryCacheAsync("initial data", getApiData, 5000);
|
|
69
87
|
|
|
70
88
|
await cache.refresh();
|
|
71
|
-
console.log(cache.getData()); //
|
|
89
|
+
console.log(cache.getData()); // fetched data from API
|
|
72
90
|
```
|
|
73
91
|
|
|
74
|
-
### With
|
|
92
|
+
### With Result
|
|
93
|
+
|
|
94
|
+
Allows to handle errors without throwing exceptions, returning a `Result` type instead. The cache will be updated only if the result is `Ok`, otherwise nothing is changed and the cache is not refreshed when the result is `Err`.
|
|
95
|
+
|
|
96
|
+
In this example, we have a fetcher function that performs a division operation. If the divisor is zero, it returns an error instead of throwing an exception.
|
|
75
97
|
|
|
76
98
|
```ts
|
|
77
|
-
import {
|
|
99
|
+
import { ExpiryCacheSafe } from "@darco2903/expiry-cache";
|
|
100
|
+
import { err, ok } from "neverthrow";
|
|
78
101
|
|
|
79
|
-
const cache = new
|
|
102
|
+
const cache = new ExpiryCacheSafe(1, (a: number, b: number) => (b === 0 ? err("Division by zero") : ok(a / b)), 100);
|
|
103
|
+
console.log(cache.getData()); // 1
|
|
104
|
+
console.log(cache.timeToLiveAsTime?.toStringWithUnit()); // ~100ms
|
|
80
105
|
|
|
81
|
-
|
|
82
|
-
console.log(cache.
|
|
106
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
107
|
+
console.log(cache.isExpired); // true
|
|
108
|
+
|
|
109
|
+
cache.refresh(4, 2); // ok(2)
|
|
110
|
+
console.log(cache.getData()); // 2
|
|
111
|
+
console.log(cache.timeToLiveAsTime?.toStringWithUnit()); // ~100ms
|
|
112
|
+
|
|
113
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
114
|
+
console.log(cache.isExpired); // true
|
|
115
|
+
|
|
116
|
+
cache.refresh(4, 0); // err("Division by zero")
|
|
117
|
+
console.log(cache.getData()); // null
|
|
118
|
+
console.log(cache.isExpired); // true
|
|
83
119
|
```
|
|
84
120
|
|
|
85
|
-
###
|
|
121
|
+
### Event Emission
|
|
86
122
|
|
|
87
123
|
```ts
|
|
88
124
|
import { ExpiryCache } from "@darco2903/expiry-cache";
|
|
89
125
|
|
|
90
|
-
const cache = new ExpiryCache(0, () =>
|
|
126
|
+
const cache = new ExpiryCache(0, () => 10, 1000);
|
|
127
|
+
cache.on("expired", () => {
|
|
128
|
+
console.log("Cache expired");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
cache.on("refreshed", (value) => {
|
|
132
|
+
console.log("Cache refreshed with value:", value);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
cache.expire(); // trigger the expired event
|
|
136
|
+
|
|
137
|
+
cache.refresh(); // trigger the refreshed event
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Setting expiry time when refreshing
|
|
141
|
+
|
|
142
|
+
#### ExpiresIn
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { ExpiryCache, ReturnOptions } from "@darco2903/expiry-cache";
|
|
146
|
+
|
|
147
|
+
const cache = new ExpiryCache(0, () => ReturnOptions.ExpiresIn(0, 200), 1000);
|
|
148
|
+
console.log(cache.expirationTime); // 1000
|
|
91
149
|
|
|
92
150
|
cache.refresh();
|
|
93
|
-
|
|
94
|
-
|
|
151
|
+
console.log(cache.expirationTime); // 200
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### ExpiresAt
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { ExpiryCache, ReturnOptions } from "@darco2903/expiry-cache";
|
|
158
|
+
|
|
159
|
+
const cache = new ExpiryCache(0, () => ReturnOptions.ExpiresAt(0, Date.now() + 200), 1000);
|
|
160
|
+
console.log(cache.expiresAt - Date.now()); // ~1000
|
|
161
|
+
|
|
162
|
+
cache.refresh();
|
|
163
|
+
console.log(cache.expiresAt - Date.now()); // ~200
|
|
95
164
|
```
|
|
96
165
|
|
|
97
166
|
### SecondThought Integration
|
|
98
167
|
|
|
99
168
|
```ts
|
|
100
169
|
import { ExpiryCache } from "@darco2903/expiry-cache";
|
|
101
|
-
import { Minute } from "@darco2903/secondthought";
|
|
170
|
+
import { Millisecond, Second, Minute, Hour } from "@darco2903/secondthought";
|
|
102
171
|
|
|
103
172
|
const cache = new ExpiryCache("data", () => "data", new Minute(5)); // Cache expires in 5 minutes
|
|
173
|
+
|
|
174
|
+
cache.setExpiresIn(new Hour(1)); // Update the cache to expire in 1 hour
|
|
175
|
+
cache.setExpiresAt(Millisecond.now().add(new Second(2))); // Set the cache to expire in 2 seconds
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Full Example with Async Fetcher, Result and ReturnOptions
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { ExpiryCacheSafeAsync, ReturnOptions } from "@darco2903/expiry-cache";
|
|
182
|
+
import { Second } from "@darco2903/secondthought";
|
|
183
|
+
import { ResultAsync, okAsync } from "neverthrow";
|
|
184
|
+
|
|
185
|
+
type TokenData = {
|
|
186
|
+
token: string;
|
|
187
|
+
expiresIn: number;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
function fetchApiToken(): ResultAsync<TokenData, string> {
|
|
191
|
+
return ResultAsync.fromPromise(
|
|
192
|
+
fetch("https://example.com/api/token").then((res) => res.json()),
|
|
193
|
+
(err) => String(err),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const cache = new ExpiryCacheSafeAsync("", () => {
|
|
198
|
+
return fetchApiToken().andThen((res) => {
|
|
199
|
+
return okAsync(ReturnOptions.ExpiresIn(res.token, new Second(res.expiresIn)));
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
cache.expire(); // expire the cache immediately to force a refresh on the first call
|
|
203
|
+
|
|
204
|
+
await cache.refresh().match(
|
|
205
|
+
(token) => {
|
|
206
|
+
console.log("Fetched API token:", token);
|
|
207
|
+
},
|
|
208
|
+
(err) => {
|
|
209
|
+
console.error("Error fetching API token:", err);
|
|
210
|
+
},
|
|
211
|
+
);
|
|
104
212
|
```
|
package/dist/ExpiryCache.d.ts
CHANGED
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
1
|
+
import { ExpiryCacheSyncBase } from "./base/index.js";
|
|
2
|
+
import type { RefreshFunctionUnsafeSync } from "./types/index.js";
|
|
3
|
+
import type { IExpiryCacheSync, IExpiryCacheUnsafe } from "./interface/index.js";
|
|
4
|
+
import type { CacheEventsUnsafe } from "./types/events.js";
|
|
5
|
+
export declare class ExpiryCache<T, F extends RefreshFunctionUnsafeSync<T>> extends ExpiryCacheSyncBase<T, F, CacheEventsUnsafe<T>> implements IExpiryCacheSync<T, F>, IExpiryCacheUnsafe<T, F> {
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @param data The initial data to be cached.
|
|
8
|
-
* @param callback The function to refresh the cached data.
|
|
9
|
-
* @param expirationTime The time in milliseconds after which the cache expires. Defaults to 60_000 (1 minute). If set to 0, the cache will never expire.
|
|
7
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data. Returns the updated cache data.
|
|
10
8
|
*/
|
|
11
|
-
|
|
9
|
+
refresh(...args: Parameters<F>): T;
|
|
12
10
|
/**
|
|
13
|
-
*
|
|
11
|
+
* Returns the cached data if it is not expired. If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the updated cache data.
|
|
14
12
|
*/
|
|
15
|
-
|
|
16
|
-
getDataOrRefresh(...args: Parameters<U>): T;
|
|
17
|
-
/**
|
|
18
|
-
* Refreshes the cached data and sets a new expiration timestamp.
|
|
19
|
-
* @param expiresAt The new expiration timestamp in milliseconds.
|
|
20
|
-
*/
|
|
21
|
-
refreshExpiresAt(expiresAt: number | Time, ...args: Parameters<U>): void;
|
|
22
|
-
/**
|
|
23
|
-
* Refreshes the cached data and sets a new expiration time.
|
|
24
|
-
* @param expirationTime The new expiration time in milliseconds.
|
|
25
|
-
*/
|
|
26
|
-
refreshExpiresIn(expirationTime: number | Time, ...args: Parameters<U>): void;
|
|
13
|
+
getDataOrRefresh(...args: Parameters<F>): T;
|
|
27
14
|
}
|
package/dist/ExpiryCache.js
CHANGED
|
@@ -1,41 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export class ExpiryCache extends ExpiryCacheBase {
|
|
1
|
+
import { ExpiryCacheSyncBase } from "./base/index.js";
|
|
2
|
+
export class ExpiryCache extends ExpiryCacheSyncBase {
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
6
|
-
* @param data The initial data to be cached.
|
|
7
|
-
* @param callback The function to refresh the cached data.
|
|
8
|
-
* @param expirationTime The time in milliseconds after which the cache expires. Defaults to 60_000 (1 minute). If set to 0, the cache will never expire.
|
|
4
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data. Returns the updated cache data.
|
|
9
5
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
refresh(...args) {
|
|
7
|
+
this.setData(this.refreshFn(...args));
|
|
8
|
+
this._emit("refreshed", this.data);
|
|
9
|
+
return this.data;
|
|
12
10
|
}
|
|
13
11
|
/**
|
|
14
|
-
*
|
|
12
|
+
* Returns the cached data if it is not expired. If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the updated cache data.
|
|
15
13
|
*/
|
|
16
|
-
refresh(...args) {
|
|
17
|
-
this.setData(this.callback(...args));
|
|
18
|
-
}
|
|
19
14
|
getDataOrRefresh(...args) {
|
|
20
15
|
if (this.isExpired) {
|
|
21
16
|
this.refresh(...args);
|
|
22
17
|
}
|
|
23
18
|
return this.data;
|
|
24
19
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Refreshes the cached data and sets a new expiration timestamp.
|
|
27
|
-
* @param expiresAt The new expiration timestamp in milliseconds.
|
|
28
|
-
*/
|
|
29
|
-
refreshExpiresAt(expiresAt, ...args) {
|
|
30
|
-
const expAt = this.argToMs(expiresAt);
|
|
31
|
-
this.setDataExpiresAt(this.callback(...args), expAt);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Refreshes the cached data and sets a new expiration time.
|
|
35
|
-
* @param expirationTime The new expiration time in milliseconds.
|
|
36
|
-
*/
|
|
37
|
-
refreshExpiresIn(expirationTime, ...args) {
|
|
38
|
-
const expTime = this.argToMs(expirationTime);
|
|
39
|
-
this.setDataExpiresIn(this.callback(...args), expTime);
|
|
40
|
-
}
|
|
41
20
|
}
|
|
@@ -1,29 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { ExpiryCacheAsyncBase } from "./base/index.js";
|
|
2
|
+
import type { ReturnOptions } from "./ReturnOptions.js";
|
|
3
|
+
import type { RefreshFunctionUnsafeAsync, ReturnTypeUnsafeAsync } from "./types/index.js";
|
|
4
|
+
import type { IExpiryCacheAsync, IExpiryCacheUnsafe } from "./interface/index.js";
|
|
5
|
+
import type { CacheEventsUnsafe } from "./types/events.js";
|
|
6
|
+
export declare class ExpiryCacheAsync<T, F extends RefreshFunctionUnsafeAsync<T>> extends ExpiryCacheAsyncBase<T, F, ReturnTypeUnsafeAsync<T | ReturnOptions<T>>, CacheEventsUnsafe<T>> implements IExpiryCacheAsync<T, F>, IExpiryCacheUnsafe<T, F> {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @param callback The function to refresh the cached data.
|
|
11
|
-
* @param expirationTime The time in milliseconds after which the cache expires. Defaults to 60_000 (1 minute). If set to 0, the cache will never expire.
|
|
8
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data. Returns the updated cache data.
|
|
9
|
+
* If a refresh is already in progress, it returns the existing refresh promise instead of calling the callback again.
|
|
12
10
|
*/
|
|
13
|
-
|
|
11
|
+
refresh(...args: Parameters<F>): Promise<T>;
|
|
14
12
|
/**
|
|
15
|
-
*
|
|
13
|
+
* Returns the cached data if it is not expired. If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the updated cache data.
|
|
16
14
|
*/
|
|
17
|
-
|
|
18
|
-
getDataOrRefresh(...args: Parameters<U>): Promise<T>;
|
|
19
|
-
/**
|
|
20
|
-
* Refreshes the cached data and sets a new expiration timestamp.
|
|
21
|
-
* @param expiresAt The new expiration timestamp in milliseconds.
|
|
22
|
-
*/
|
|
23
|
-
refreshExpiresAt(expiresAt: number | Time, ...args: Parameters<U>): Promise<void>;
|
|
24
|
-
/**
|
|
25
|
-
* Refreshes the cached data and sets a new expiration time.
|
|
26
|
-
* @param expirationTime The new expiration time in milliseconds.
|
|
27
|
-
*/
|
|
28
|
-
refreshExpiresIn(expirationTime: number | Time, ...args: Parameters<U>): Promise<void>;
|
|
15
|
+
getDataOrRefresh(...args: Parameters<F>): Promise<T>;
|
|
29
16
|
}
|
package/dist/ExpiryCacheAsync.js
CHANGED
|
@@ -1,52 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export class ExpiryCacheAsync extends ExpiryCacheBase {
|
|
4
|
-
get refreshing() {
|
|
5
|
-
return this._refreshCb !== null;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Creates an instance of ExpiryCache.
|
|
9
|
-
* @param data The initial data to be cached.
|
|
10
|
-
* @param callback The function to refresh the cached data.
|
|
11
|
-
* @param expirationTime The time in milliseconds after which the cache expires. Defaults to 60_000 (1 minute). If set to 0, the cache will never expire.
|
|
12
|
-
*/
|
|
13
|
-
constructor(data, callback, expirationTime = new Minute(1)) {
|
|
14
|
-
super(data, callback, expirationTime);
|
|
15
|
-
this._refreshCb = null;
|
|
16
|
-
}
|
|
1
|
+
import { ExpiryCacheAsyncBase } from "./base/index.js";
|
|
2
|
+
export class ExpiryCacheAsync extends ExpiryCacheAsyncBase {
|
|
17
3
|
/**
|
|
18
|
-
* Refreshes the
|
|
4
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data. Returns the updated cache data.
|
|
5
|
+
* If a refresh is already in progress, it returns the existing refresh promise instead of calling the callback again.
|
|
19
6
|
*/
|
|
20
7
|
async refresh(...args) {
|
|
21
8
|
if (this.refreshing) {
|
|
22
9
|
await this._refreshCb;
|
|
23
10
|
}
|
|
24
11
|
else {
|
|
25
|
-
this._refreshCb = this.
|
|
12
|
+
this._refreshCb = this.refreshFn(...args);
|
|
26
13
|
this.setData(await this._refreshCb);
|
|
14
|
+
this._emit("refreshed", this.data);
|
|
27
15
|
this._refreshCb = null;
|
|
28
16
|
}
|
|
17
|
+
return this.data;
|
|
29
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the cached data if it is not expired. If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the updated cache data.
|
|
21
|
+
*/
|
|
30
22
|
async getDataOrRefresh(...args) {
|
|
31
23
|
if (this.isExpired) {
|
|
32
24
|
await this.refresh(...args);
|
|
33
25
|
}
|
|
34
26
|
return this.data;
|
|
35
27
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Refreshes the cached data and sets a new expiration timestamp.
|
|
38
|
-
* @param expiresAt The new expiration timestamp in milliseconds.
|
|
39
|
-
*/
|
|
40
|
-
async refreshExpiresAt(expiresAt, ...args) {
|
|
41
|
-
const expAt = this.argToMs(expiresAt);
|
|
42
|
-
this.setDataExpiresAt(await this.callback(...args), expAt);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Refreshes the cached data and sets a new expiration time.
|
|
46
|
-
* @param expirationTime The new expiration time in milliseconds.
|
|
47
|
-
*/
|
|
48
|
-
async refreshExpiresIn(expirationTime, ...args) {
|
|
49
|
-
const expTime = this.argToMs(expirationTime);
|
|
50
|
-
this.setDataExpiresIn(await this.callback(...args), expTime);
|
|
51
|
-
}
|
|
52
28
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Result } from "neverthrow";
|
|
2
|
+
import { ExpiryCacheSyncBase } from "./base/index.js";
|
|
3
|
+
import type { RefreshFunctionSafeSync } from "./types/index.js";
|
|
4
|
+
import type { IExpiryCacheSafe, IExpiryCacheSync } from "./interface/index.js";
|
|
5
|
+
import type { CacheEventsSafe } from "./types/events.js";
|
|
6
|
+
export declare class ExpiryCacheSafe<T, E, F extends RefreshFunctionSafeSync<T, E>> extends ExpiryCacheSyncBase<T, F, CacheEventsSafe<T, E>, E> implements IExpiryCacheSync<T, F, E>, IExpiryCacheSafe<T, F, E> {
|
|
7
|
+
/**
|
|
8
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data if the result is Ok, otherwise it does not update the cache data.
|
|
9
|
+
* Returns the refresh function result.
|
|
10
|
+
*/
|
|
11
|
+
refresh(...args: Parameters<F>): Result<T, E>;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the cached data if it is not expired.
|
|
14
|
+
* If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the refresh function result.
|
|
15
|
+
* It behaves the same as the refresh method if the cache is expired, otherwise it returns the cached data wrapped in an Ok result.
|
|
16
|
+
*/
|
|
17
|
+
getDataOrRefresh(...args: Parameters<F>): Result<T, E>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ok } from "neverthrow";
|
|
2
|
+
import { ExpiryCacheSyncBase } from "./base/index.js";
|
|
3
|
+
import { mapRefreshReturn } from "./utils.js";
|
|
4
|
+
export class ExpiryCacheSafe extends ExpiryCacheSyncBase {
|
|
5
|
+
/**
|
|
6
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data if the result is Ok, otherwise it does not update the cache data.
|
|
7
|
+
* Returns the refresh function result.
|
|
8
|
+
*/
|
|
9
|
+
refresh(...args) {
|
|
10
|
+
return this.refreshFn(...args)
|
|
11
|
+
.andTee((data) => this.setData(data))
|
|
12
|
+
.map((mapRefreshReturn))
|
|
13
|
+
.andTee((data) => {
|
|
14
|
+
this._emit("refreshed", data);
|
|
15
|
+
})
|
|
16
|
+
.orTee((err) => {
|
|
17
|
+
this._emit("error", err);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns the cached data if it is not expired.
|
|
22
|
+
* If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the refresh function result.
|
|
23
|
+
* It behaves the same as the refresh method if the cache is expired, otherwise it returns the cached data wrapped in an Ok result.
|
|
24
|
+
*/
|
|
25
|
+
getDataOrRefresh(...args) {
|
|
26
|
+
if (this.isExpired) {
|
|
27
|
+
return this.refresh(...args);
|
|
28
|
+
}
|
|
29
|
+
return ok(this.data);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ResultAsync } from "neverthrow";
|
|
2
|
+
import { ExpiryCacheAsyncBase } from "./base/index.js";
|
|
3
|
+
import type { ReturnOptions } from "./ReturnOptions.js";
|
|
4
|
+
import type { RefreshFunctionSafeAsync, ReturnTypeSafeAsync } from "./types/index.js";
|
|
5
|
+
import type { IExpiryCacheAsync, IExpiryCacheSafe } from "./interface/index.js";
|
|
6
|
+
import type { CacheEventsSafe } from "./types/events.js";
|
|
7
|
+
export declare class ExpiryCacheSafeAsync<T, E, F extends RefreshFunctionSafeAsync<T, E>> extends ExpiryCacheAsyncBase<T, F, ReturnTypeSafeAsync<T | ReturnOptions<T>, E>, CacheEventsSafe<T, E>, E> implements IExpiryCacheAsync<T, F, E>, IExpiryCacheSafe<T, F, E> {
|
|
8
|
+
/**
|
|
9
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data if the result is Ok, otherwise it does not update the cache data.
|
|
10
|
+
* Returns the refresh function result.
|
|
11
|
+
* If a refresh is already in progress, it returns the existing refresh promise instead of calling the callback again.
|
|
12
|
+
*/
|
|
13
|
+
refresh(...args: Parameters<F>): ResultAsync<T, E>;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the cached data if it is not expired.
|
|
16
|
+
* If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the refresh function result.
|
|
17
|
+
* It behaves the same as the refresh method if the cache is expired, otherwise it returns the cached data wrapped in an Ok result.
|
|
18
|
+
*/
|
|
19
|
+
getDataOrRefresh(...args: Parameters<F>): ResultAsync<T, E>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { okAsync } from "neverthrow";
|
|
2
|
+
import { ExpiryCacheAsyncBase } from "./base/index.js";
|
|
3
|
+
import { mapRefreshReturn } from "./utils.js";
|
|
4
|
+
export class ExpiryCacheSafeAsync extends ExpiryCacheAsyncBase {
|
|
5
|
+
/**
|
|
6
|
+
* Refreshes the cache by calling the refresh function with the provided arguments and updates the cache data if the result is Ok, otherwise it does not update the cache data.
|
|
7
|
+
* Returns the refresh function result.
|
|
8
|
+
* If a refresh is already in progress, it returns the existing refresh promise instead of calling the callback again.
|
|
9
|
+
*/
|
|
10
|
+
refresh(...args) {
|
|
11
|
+
if (this._refreshCb !== null) {
|
|
12
|
+
return this._refreshCb.map((mapRefreshReturn));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
this._refreshCb = this.refreshFn(...args);
|
|
16
|
+
return this._refreshCb
|
|
17
|
+
.andTee((res) => this.setData(res))
|
|
18
|
+
.map((mapRefreshReturn))
|
|
19
|
+
.andTee((data) => {
|
|
20
|
+
this._refreshCb = null;
|
|
21
|
+
this._emit("refreshed", data);
|
|
22
|
+
})
|
|
23
|
+
.orTee((err) => {
|
|
24
|
+
this._refreshCb = null;
|
|
25
|
+
this._emit("error", err);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns the cached data if it is not expired.
|
|
31
|
+
* If the cache is expired, it refreshes the cache by calling the refresh function with the provided arguments and returns the refresh function result.
|
|
32
|
+
* It behaves the same as the refresh method if the cache is expired, otherwise it returns the cached data wrapped in an Ok result.
|
|
33
|
+
*/
|
|
34
|
+
getDataOrRefresh(...args) {
|
|
35
|
+
if (this.isExpired) {
|
|
36
|
+
return this.refresh(...args);
|
|
37
|
+
}
|
|
38
|
+
return okAsync(this.data);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Millisecond, type Time } from "@darco2903/secondthought";
|
|
2
|
+
export declare abstract class ReturnOptions<T> {
|
|
3
|
+
readonly data: T;
|
|
4
|
+
constructor(data: T);
|
|
5
|
+
static ExpiresAt<T>(data: T, expiresAt: number | Time): ReturnOptionsExpiresAtClass<T>;
|
|
6
|
+
static ExpiresIn<T>(data: T, expiresIn: number | Time): ReturnOptionsExpiresInClass<T>;
|
|
7
|
+
}
|
|
8
|
+
export declare class ReturnOptionsExpiresInClass<T> extends ReturnOptions<T> {
|
|
9
|
+
readonly expiresIn: Millisecond;
|
|
10
|
+
constructor(data: T, expiresIn: number | Time);
|
|
11
|
+
}
|
|
12
|
+
export declare class ReturnOptionsExpiresAtClass<T> extends ReturnOptions<T> {
|
|
13
|
+
readonly expiresAt: Millisecond;
|
|
14
|
+
constructor(data: T, expiresAt: number | Time);
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { argToMs } from "./utils.js";
|
|
2
|
+
export class ReturnOptions {
|
|
3
|
+
constructor(data) {
|
|
4
|
+
this.data = data;
|
|
5
|
+
}
|
|
6
|
+
static ExpiresAt(data, expiresAt) {
|
|
7
|
+
return new ReturnOptionsExpiresAtClass(data, expiresAt);
|
|
8
|
+
}
|
|
9
|
+
static ExpiresIn(data, expiresIn) {
|
|
10
|
+
return new ReturnOptionsExpiresInClass(data, expiresIn);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class ReturnOptionsExpiresInClass extends ReturnOptions {
|
|
14
|
+
constructor(data, expiresIn) {
|
|
15
|
+
super(data);
|
|
16
|
+
this.expiresIn = argToMs(expiresIn);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class ReturnOptionsExpiresAtClass extends ReturnOptions {
|
|
20
|
+
constructor(data, expiresAt) {
|
|
21
|
+
super(data);
|
|
22
|
+
this.expiresAt = argToMs(expiresAt);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Time } from "@darco2903/secondthought";
|
|
2
|
+
import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
|
|
3
|
+
import type { ReturnOptions } from "../ReturnOptions.js";
|
|
4
|
+
import type { RefreshFunctionAsync } from "../types/RefreshFunction.js";
|
|
5
|
+
import type { ReturnTypeAsync } from "../types/ReturnType.js";
|
|
6
|
+
import type { CacheEvents } from "../types/events.js";
|
|
7
|
+
export declare abstract class ExpiryCacheAsyncBase<T, F extends RefreshFunctionAsync<T, E>, V extends ReturnTypeAsync<T | ReturnOptions<T>, E>, M extends CacheEvents<T, E>, E = any> extends ExpiryCacheBase<T, F, M, E> {
|
|
8
|
+
/** The promise of the current refresh operation, or null if no refresh is in progress. */
|
|
9
|
+
protected _refreshCb: V | null;
|
|
10
|
+
/** Indicates whether the cache is currently being refreshed. */
|
|
11
|
+
get refreshing(): boolean;
|
|
12
|
+
constructor(data: T, callback: F, expirationTime?: number | Time);
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
|
|
2
|
+
export class ExpiryCacheAsyncBase extends ExpiryCacheBase {
|
|
3
|
+
/** Indicates whether the cache is currently being refreshed. */
|
|
4
|
+
get refreshing() {
|
|
5
|
+
return this._refreshCb !== null;
|
|
6
|
+
}
|
|
7
|
+
constructor(data, callback, expirationTime) {
|
|
8
|
+
super(data, callback, expirationTime);
|
|
9
|
+
this._refreshCb = null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { TypedEmitterProtected } from "@darco2903/typed-emitter";
|
|
1
2
|
import { Time, Millisecond } from "@darco2903/secondthought";
|
|
2
|
-
import type { RefreshFunction
|
|
3
|
-
|
|
3
|
+
import type { RefreshFunction } from "../types/RefreshFunction.js";
|
|
4
|
+
import type { CacheReturnType } from "../types/ReturnType.js";
|
|
5
|
+
import { ReturnOptions } from "../ReturnOptions.js";
|
|
6
|
+
import type { CacheEvents } from "../types/events.js";
|
|
7
|
+
export declare abstract class ExpiryCacheBase<T, F extends RefreshFunction<T, E>, M extends CacheEvents<T, E>, E = never> extends TypedEmitterProtected<M> {
|
|
4
8
|
/** The cached data. */
|
|
5
9
|
protected data: T;
|
|
6
10
|
/** The function to refresh the cached data. */
|
|
7
|
-
protected
|
|
11
|
+
protected refreshFn: F;
|
|
12
|
+
private _expirationTimeout;
|
|
8
13
|
/** The time in milliseconds after which the cache expires. 0 means never expires. */
|
|
9
14
|
protected _expirationTime: Millisecond;
|
|
10
15
|
/** The expiration timestamp in milliseconds. 0 means never expires, -1 means already expired or no data. */
|
|
@@ -41,17 +46,13 @@ export declare abstract class ExpiryCacheBase<T, U extends RefreshFunction<T> |
|
|
|
41
46
|
* Gets the time to live (TTL) as a Millisecond object. Returns null if the cache does not expire.
|
|
42
47
|
*/
|
|
43
48
|
get timeToLiveAsTime(): Millisecond | null;
|
|
44
|
-
/**
|
|
45
|
-
* Converts a number or Time object to milliseconds.
|
|
46
|
-
*/
|
|
47
|
-
protected argToMs(arg: number | Time): Millisecond;
|
|
48
49
|
/**
|
|
49
50
|
* Creates an instance of ExpiryCache.
|
|
50
51
|
* @param data The initial data to be cached.
|
|
51
|
-
* @param
|
|
52
|
+
* @param refreshFn The function to refresh the cached data.
|
|
52
53
|
* @param expirationTime The time in milliseconds after which the cache expires. Defaults to 60_000 (1 minute). If set to 0, the cache will never expire.
|
|
53
54
|
*/
|
|
54
|
-
constructor(data: T,
|
|
55
|
+
constructor(data: T, refreshFn: F, expirationTime?: number | Time);
|
|
55
56
|
/**
|
|
56
57
|
* Expires the cache immediately.
|
|
57
58
|
*/
|
|
@@ -60,6 +61,11 @@ export declare abstract class ExpiryCacheBase<T, U extends RefreshFunction<T> |
|
|
|
60
61
|
* Sets the cache to never expire.
|
|
61
62
|
*/
|
|
62
63
|
neverExpire(): void;
|
|
64
|
+
private setExpirationTimeout;
|
|
65
|
+
/**
|
|
66
|
+
* Calculates the expiration timestamp based on the current time plus the given milliseconds. If ms is 0, it returns 0 to indicate never expires.
|
|
67
|
+
* @param ms The time in milliseconds after which the cache should expire. If set to 0, the cache will never expire.
|
|
68
|
+
*/
|
|
63
69
|
protected expIn(ms: Millisecond): Millisecond;
|
|
64
70
|
/**
|
|
65
71
|
* Sets the expiration timestamp based on the current time plus the given milliseconds.
|
|
@@ -68,25 +74,27 @@ export declare abstract class ExpiryCacheBase<T, U extends RefreshFunction<T> |
|
|
|
68
74
|
setExpiresIn(ms: number | Time): void;
|
|
69
75
|
/**
|
|
70
76
|
* Sets the expiration timestamp to a specific time.
|
|
71
|
-
* @param
|
|
77
|
+
* @param expiresAt The expiration timestamp in milliseconds or as a Time object. If set to 0, the cache will never expire.
|
|
72
78
|
*/
|
|
73
|
-
setExpiresAt(
|
|
79
|
+
setExpiresAt(expiresAt: number | Time): void;
|
|
80
|
+
protected _setExpiresAt(expiresAt: Millisecond): void;
|
|
74
81
|
/**
|
|
75
82
|
* Sets the cached data and resets the expiration timestamp based on the expiration time.
|
|
83
|
+
* @param data The new data to be cached.
|
|
76
84
|
*/
|
|
77
|
-
protected setData(data: T): void;
|
|
85
|
+
protected setData(data: T | ReturnOptions<T>): void;
|
|
78
86
|
/**
|
|
79
87
|
* Sets the cached data and sets a new expiration timestamp.
|
|
80
|
-
* @param expiresAt The new expiration timestamp in milliseconds.
|
|
88
|
+
* @param expiresAt The new expiration timestamp in milliseconds. If set to 0, the cache will never expire.
|
|
81
89
|
*/
|
|
82
90
|
protected setDataExpiresAt(data: T, expiresAt: Millisecond): void;
|
|
83
91
|
/**
|
|
84
92
|
* Sets the cached data and sets a new expiration time.
|
|
85
|
-
* @param expirationTime The new expiration time in milliseconds.
|
|
93
|
+
* @param expirationTime The new expiration time in milliseconds. If set to 0, the cache will never expire.
|
|
86
94
|
*/
|
|
87
95
|
protected setDataExpiresIn(data: T, expirationTime: Millisecond): void;
|
|
88
96
|
/**
|
|
89
|
-
* Gets the
|
|
97
|
+
* Gets the cached data without checking expiration.
|
|
90
98
|
*/
|
|
91
99
|
getRawData(): T;
|
|
92
100
|
/**
|
|
@@ -94,21 +102,11 @@ export declare abstract class ExpiryCacheBase<T, U extends RefreshFunction<T> |
|
|
|
94
102
|
*/
|
|
95
103
|
getData(): T | null;
|
|
96
104
|
/**
|
|
97
|
-
*
|
|
98
|
-
*/
|
|
99
|
-
abstract refresh(...args: Parameters<U>): void | Promise<void>;
|
|
100
|
-
/**
|
|
101
|
-
* Gets the cached data or refreshes it if expired.
|
|
102
|
-
*/
|
|
103
|
-
abstract getDataOrRefresh(...args: Parameters<U>): T | Promise<T>;
|
|
104
|
-
/**
|
|
105
|
-
* Refreshes the cached data and sets a new expiration timestamp.
|
|
106
|
-
* @param expiresAt The new expiration timestamp in milliseconds.
|
|
105
|
+
* @param args The arguments to pass to the refresh function.
|
|
107
106
|
*/
|
|
108
|
-
abstract
|
|
107
|
+
abstract refresh(...args: Parameters<F>): CacheReturnType<T, E>;
|
|
109
108
|
/**
|
|
110
|
-
*
|
|
111
|
-
* @param expirationTime The new expiration time in milliseconds.
|
|
109
|
+
* @param args The arguments to pass to the refresh function in case the cache is expired.
|
|
112
110
|
*/
|
|
113
|
-
abstract
|
|
111
|
+
abstract getDataOrRefresh(...args: Parameters<F>): CacheReturnType<T, E>;
|
|
114
112
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { TypedEmitterProtected } from "@darco2903/typed-emitter";
|
|
1
2
|
import { Time, Millisecond, Minute } from "@darco2903/secondthought";
|
|
2
|
-
|
|
3
|
+
import { ReturnOptions, ReturnOptionsExpiresAtClass, ReturnOptionsExpiresInClass } from "../ReturnOptions.js";
|
|
4
|
+
import { argToMs } from "../utils.js";
|
|
5
|
+
export class ExpiryCacheBase extends TypedEmitterProtected {
|
|
3
6
|
/**
|
|
4
7
|
* Gets the expiration time in milliseconds. This is the duration after which the cache expires, not the absolute expiration timestamp.
|
|
5
8
|
*/
|
|
@@ -54,30 +57,28 @@ export class ExpiryCacheBase {
|
|
|
54
57
|
}
|
|
55
58
|
return null;
|
|
56
59
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Converts a number or Time object to milliseconds.
|
|
59
|
-
*/
|
|
60
|
-
argToMs(arg) {
|
|
61
|
-
return typeof arg === "number" ? new Millisecond(arg) : arg.toMillisecond();
|
|
62
|
-
}
|
|
63
60
|
/**
|
|
64
61
|
* Creates an instance of ExpiryCache.
|
|
65
62
|
* @param data The initial data to be cached.
|
|
66
|
-
* @param
|
|
63
|
+
* @param refreshFn The function to refresh the cached data.
|
|
67
64
|
* @param expirationTime The time in milliseconds after which the cache expires. Defaults to 60_000 (1 minute). If set to 0, the cache will never expire.
|
|
68
65
|
*/
|
|
69
|
-
constructor(data,
|
|
70
|
-
|
|
66
|
+
constructor(data, refreshFn, expirationTime = new Minute(1)) {
|
|
67
|
+
super();
|
|
68
|
+
const expTime = argToMs(expirationTime);
|
|
71
69
|
this.data = data;
|
|
72
70
|
this._expiresAt = expTime.time === 0 ? expTime : Millisecond.now().add(expTime);
|
|
73
71
|
this._expirationTime = expTime;
|
|
74
|
-
this.
|
|
72
|
+
this.refreshFn = refreshFn;
|
|
73
|
+
this._expirationTimeout = null;
|
|
74
|
+
this.setExpirationTimeout();
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* Expires the cache immediately.
|
|
78
78
|
*/
|
|
79
79
|
expire() {
|
|
80
80
|
this._expiresAt = new Millisecond(-1);
|
|
81
|
+
this._emit("expired");
|
|
81
82
|
}
|
|
82
83
|
/**
|
|
83
84
|
* Sets the cache to never expire.
|
|
@@ -85,6 +86,22 @@ export class ExpiryCacheBase {
|
|
|
85
86
|
neverExpire() {
|
|
86
87
|
this._expiresAt = new Millisecond(0);
|
|
87
88
|
}
|
|
89
|
+
setExpirationTimeout() {
|
|
90
|
+
if (this._expirationTimeout) {
|
|
91
|
+
clearTimeout(this._expirationTimeout);
|
|
92
|
+
}
|
|
93
|
+
if (this.timeToLive !== null) {
|
|
94
|
+
this._expirationTimeout = setTimeout(() => {
|
|
95
|
+
this._expirationTimeout = null;
|
|
96
|
+
this._emit("expired");
|
|
97
|
+
}, this.timeToLive);
|
|
98
|
+
this._expirationTimeout.unref();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Calculates the expiration timestamp based on the current time plus the given milliseconds. If ms is 0, it returns 0 to indicate never expires.
|
|
103
|
+
* @param ms The time in milliseconds after which the cache should expire. If set to 0, the cache will never expire.
|
|
104
|
+
*/
|
|
88
105
|
expIn(ms) {
|
|
89
106
|
return ms.time === 0 ? ms : Millisecond.now().add(ms);
|
|
90
107
|
}
|
|
@@ -93,39 +110,57 @@ export class ExpiryCacheBase {
|
|
|
93
110
|
* @param ms The time in milliseconds after which the cache should expire. If set to 0, the cache will never expire.
|
|
94
111
|
*/
|
|
95
112
|
setExpiresIn(ms) {
|
|
96
|
-
const t =
|
|
97
|
-
this.
|
|
113
|
+
const t = argToMs(ms);
|
|
114
|
+
this._expirationTime = t;
|
|
115
|
+
this._setExpiresAt(this.expIn(t));
|
|
98
116
|
}
|
|
99
117
|
/**
|
|
100
118
|
* Sets the expiration timestamp to a specific time.
|
|
101
|
-
* @param
|
|
119
|
+
* @param expiresAt The expiration timestamp in milliseconds or as a Time object. If set to 0, the cache will never expire.
|
|
102
120
|
*/
|
|
103
|
-
setExpiresAt(
|
|
104
|
-
|
|
121
|
+
setExpiresAt(expiresAt) {
|
|
122
|
+
const t = argToMs(expiresAt);
|
|
123
|
+
this._setExpiresAt(t);
|
|
124
|
+
}
|
|
125
|
+
_setExpiresAt(expiresAt) {
|
|
126
|
+
this._expiresAt = expiresAt;
|
|
127
|
+
this.setExpirationTimeout();
|
|
105
128
|
}
|
|
106
129
|
/**
|
|
107
130
|
* Sets the cached data and resets the expiration timestamp based on the expiration time.
|
|
131
|
+
* @param data The new data to be cached.
|
|
108
132
|
*/
|
|
109
133
|
setData(data) {
|
|
110
|
-
|
|
134
|
+
if (data instanceof ReturnOptions) {
|
|
135
|
+
if (data instanceof ReturnOptionsExpiresInClass) {
|
|
136
|
+
this.setDataExpiresIn(data.data, data.expiresIn);
|
|
137
|
+
}
|
|
138
|
+
else if (data instanceof ReturnOptionsExpiresAtClass) {
|
|
139
|
+
this.setDataExpiresAt(data.data, data.expiresAt);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.setDataExpiresIn(data, this._expirationTime);
|
|
144
|
+
}
|
|
111
145
|
}
|
|
112
146
|
/**
|
|
113
147
|
* Sets the cached data and sets a new expiration timestamp.
|
|
114
|
-
* @param expiresAt The new expiration timestamp in milliseconds.
|
|
148
|
+
* @param expiresAt The new expiration timestamp in milliseconds. If set to 0, the cache will never expire.
|
|
115
149
|
*/
|
|
116
150
|
setDataExpiresAt(data, expiresAt) {
|
|
117
151
|
this.data = data;
|
|
118
|
-
this.
|
|
152
|
+
this._setExpiresAt(expiresAt);
|
|
119
153
|
}
|
|
120
154
|
/**
|
|
121
155
|
* Sets the cached data and sets a new expiration time.
|
|
122
|
-
* @param expirationTime The new expiration time in milliseconds.
|
|
156
|
+
* @param expirationTime The new expiration time in milliseconds. If set to 0, the cache will never expire.
|
|
123
157
|
*/
|
|
124
158
|
setDataExpiresIn(data, expirationTime) {
|
|
125
|
-
this.
|
|
159
|
+
this.data = data;
|
|
160
|
+
this.setExpiresIn(expirationTime);
|
|
126
161
|
}
|
|
127
162
|
/**
|
|
128
|
-
* Gets the
|
|
163
|
+
* Gets the cached data without checking expiration.
|
|
129
164
|
*/
|
|
130
165
|
getRawData() {
|
|
131
166
|
return this.data;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
|
|
2
|
+
import type { RefreshFunctionSync } from "../types/RefreshFunction.js";
|
|
3
|
+
import type { CacheEvents } from "../types/events.js";
|
|
4
|
+
export declare abstract class ExpiryCacheSyncBase<T, F extends RefreshFunctionSync<T, E>, M extends CacheEvents<T, E>, E = never> extends ExpiryCacheBase<T, F, M, E> {
|
|
5
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReturnTypeAsync } from "../types/ReturnType.js";
|
|
2
|
+
import type { RefreshFunctionAsync } from "../types/RefreshFunction.js";
|
|
3
|
+
export declare abstract class IExpiryCacheAsync<T, F extends RefreshFunctionAsync<T, E>, E = any> {
|
|
4
|
+
abstract refresh(...args: Parameters<F>): ReturnTypeAsync<T, E>;
|
|
5
|
+
abstract getDataOrRefresh(...args: Parameters<F>): ReturnTypeAsync<T, E>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReturnTypeSafe } from "../types/ReturnType.js";
|
|
2
|
+
import type { RefreshFunctionSafe } from "../types/RefreshFunction.js";
|
|
3
|
+
export declare abstract class IExpiryCacheSafe<T, F extends RefreshFunctionSafe<T, E>, E> {
|
|
4
|
+
abstract refresh(...args: Parameters<F>): ReturnTypeSafe<T, E>;
|
|
5
|
+
abstract getDataOrRefresh(...args: Parameters<F>): ReturnTypeSafe<T, E>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReturnTypeSync } from "../types/ReturnType.js";
|
|
2
|
+
import type { RefreshFunctionSync } from "../types/RefreshFunction.js";
|
|
3
|
+
export declare abstract class IExpiryCacheSync<T, F extends RefreshFunctionSync<T, E>, E = any> {
|
|
4
|
+
abstract refresh(...args: Parameters<F>): ReturnTypeSync<T, E>;
|
|
5
|
+
abstract getDataOrRefresh(...args: Parameters<F>): ReturnTypeSync<T, E>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReturnTypeUnsafe } from "../types/ReturnType.js";
|
|
2
|
+
import type { RefreshFunctionUnsafe } from "../types/RefreshFunction.js";
|
|
3
|
+
export declare abstract class IExpiryCacheUnsafe<T, F extends RefreshFunctionUnsafe<T>> {
|
|
4
|
+
abstract refresh(...args: Parameters<F>): ReturnTypeUnsafe<T>;
|
|
5
|
+
abstract getDataOrRefresh(...args: Parameters<F>): ReturnTypeUnsafe<T>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReturnTypeSafeAsync, ReturnTypeSafeSync, ReturnTypeUnsafeAsync, ReturnTypeUnsafeSync } from "./ReturnType.js";
|
|
2
|
+
import type { ReturnOptions } from "../ReturnOptions.js";
|
|
3
|
+
export type RefreshFunctionUnsafeSync<T> = (...args: any[]) => ReturnTypeUnsafeSync<T | ReturnOptions<T>>;
|
|
4
|
+
export type RefreshFunctionUnsafeAsync<T> = (...args: any[]) => ReturnTypeUnsafeAsync<T | ReturnOptions<T>>;
|
|
5
|
+
export type RefreshFunctionSafeSync<T, E> = (...args: any[]) => ReturnTypeSafeSync<T | ReturnOptions<T>, E>;
|
|
6
|
+
export type RefreshFunctionSafeAsync<T, E> = (...args: any[]) => ReturnTypeSafeAsync<T | ReturnOptions<T>, E>;
|
|
7
|
+
export type RefreshFunctionUnsafe<T> = RefreshFunctionUnsafeSync<T> | RefreshFunctionUnsafeAsync<T>;
|
|
8
|
+
export type RefreshFunctionSafe<T, E> = RefreshFunctionSafeSync<T, E> | RefreshFunctionSafeAsync<T, E>;
|
|
9
|
+
export type RefreshFunctionSync<T, E = never> = RefreshFunctionUnsafeSync<T> | RefreshFunctionSafeSync<T, E>;
|
|
10
|
+
export type RefreshFunctionAsync<T, E = never> = RefreshFunctionUnsafeAsync<T> | RefreshFunctionSafeAsync<T, E>;
|
|
11
|
+
export type RefreshFunction<T, E = never> = RefreshFunctionUnsafe<T> | RefreshFunctionSafe<T, E>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Result, ResultAsync } from "neverthrow";
|
|
2
|
+
export type ReturnTypeUnsafeSync<T> = T;
|
|
3
|
+
export type ReturnTypeUnsafeAsync<T> = Promise<T>;
|
|
4
|
+
export type ReturnTypeSafeSync<T, U> = Result<T, U>;
|
|
5
|
+
export type ReturnTypeSafeAsync<T, U> = ResultAsync<T, U>;
|
|
6
|
+
export type ReturnTypeUnsafe<T> = ReturnTypeUnsafeSync<T> | ReturnTypeUnsafeAsync<T>;
|
|
7
|
+
export type ReturnTypeSafe<T, U> = ReturnTypeSafeSync<T, U> | ReturnTypeSafeAsync<T, U>;
|
|
8
|
+
export type ReturnTypeSync<T, U> = ReturnTypeUnsafeSync<T> | ReturnTypeSafeSync<T, U>;
|
|
9
|
+
export type ReturnTypeAsync<T, U> = ReturnTypeUnsafeAsync<T> | ReturnTypeSafeAsync<T, U>;
|
|
10
|
+
export type CacheReturnType<T, U> = ReturnTypeUnsafe<T> | ReturnTypeSafe<T, U>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Time, Millisecond } from "@darco2903/secondthought";
|
|
2
|
+
import { ReturnOptions } from "./ReturnOptions.js";
|
|
3
|
+
/**
|
|
4
|
+
* Converts a number or Time object to milliseconds.
|
|
5
|
+
*/
|
|
6
|
+
export declare function argToMs(arg: number | Time): Millisecond;
|
|
7
|
+
/**
|
|
8
|
+
* Maps the return value of the refresh function to the actual data type. If the result is an instance of ReturnOptions, it extracts the data from it. Otherwise, it returns the result directly.
|
|
9
|
+
*/
|
|
10
|
+
export declare function mapRefreshReturn<T>(result: T | ReturnOptions<T>): T;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Millisecond } from "@darco2903/secondthought";
|
|
2
|
+
import { ReturnOptions } from "./ReturnOptions.js";
|
|
3
|
+
/**
|
|
4
|
+
* Converts a number or Time object to milliseconds.
|
|
5
|
+
*/
|
|
6
|
+
export function argToMs(arg) {
|
|
7
|
+
return typeof arg === "number" ? new Millisecond(arg) : arg.toMillisecond();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Maps the return value of the refresh function to the actual data type. If the result is an instance of ReturnOptions, it extracts the data from it. Otherwise, it returns the result directly.
|
|
11
|
+
*/
|
|
12
|
+
export function mapRefreshReturn(result) {
|
|
13
|
+
if (result instanceof ReturnOptions) {
|
|
14
|
+
return result.data;
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darco2903/expiry-cache",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"author": "Darco2903",
|
|
5
5
|
"description": "",
|
|
6
6
|
"keywords": [],
|
|
@@ -14,20 +14,23 @@
|
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public"
|
|
16
16
|
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@darco2903/typed-emitter": "^1.3.1"
|
|
19
|
+
},
|
|
17
20
|
"peerDependencies": {
|
|
18
|
-
"@darco2903/secondthought": "^1.3.0"
|
|
21
|
+
"@darco2903/secondthought": "^1.3.0",
|
|
22
|
+
"neverthrow": "^8.2.0"
|
|
19
23
|
},
|
|
20
24
|
"devDependencies": {
|
|
21
25
|
"@darco2903/web-common": "^0.7.0",
|
|
22
|
-
"@
|
|
23
|
-
"rimraf": "^6.1.
|
|
26
|
+
"@types/node": "^25.5.0",
|
|
27
|
+
"rimraf": "^6.1.3",
|
|
24
28
|
"typescript": "^5.9.3",
|
|
25
29
|
"vitest": "^3.2.4"
|
|
26
30
|
},
|
|
27
31
|
"scripts": {
|
|
28
32
|
"build": "tsc",
|
|
29
33
|
"test": "vitest",
|
|
30
|
-
"test:ui": "vitest --ui",
|
|
31
34
|
"clean": "rimraf -g dist darco2903-expiry-cache-*.tgz"
|
|
32
35
|
}
|
|
33
36
|
}
|
package/dist/types.d.ts
DELETED
|
File without changes
|