@darco2903/expiry-cache 2.0.2 → 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.
Files changed (45) hide show
  1. package/README.md +133 -25
  2. package/dist/ExpiryCache.d.ts +9 -22
  3. package/dist/ExpiryCache.js +8 -29
  4. package/dist/ExpiryCacheAsync.d.ts +11 -24
  5. package/dist/ExpiryCacheAsync.js +10 -34
  6. package/dist/ExpiryCacheSafe.d.ts +18 -0
  7. package/dist/ExpiryCacheSafe.js +31 -0
  8. package/dist/ExpiryCacheSafeAsync.d.ts +20 -0
  9. package/dist/ExpiryCacheSafeAsync.js +40 -0
  10. package/dist/ReturnOptions.d.ts +15 -0
  11. package/dist/ReturnOptions.js +24 -0
  12. package/dist/base/ExpiryCacheAsyncBase.d.ts +13 -0
  13. package/dist/base/ExpiryCacheAsyncBase.js +11 -0
  14. package/dist/base/ExpiryCacheBase.d.ts +112 -0
  15. package/dist/base/ExpiryCacheBase.js +177 -0
  16. package/dist/base/ExpiryCacheSyncBase.d.ts +5 -0
  17. package/dist/base/ExpiryCacheSyncBase.js +3 -0
  18. package/dist/base/index.d.ts +2 -0
  19. package/dist/base/index.js +2 -0
  20. package/dist/index.d.ts +4 -0
  21. package/dist/index.js +3 -0
  22. package/dist/interface/ExpiryCacheAsync.d.ts +6 -0
  23. package/dist/interface/ExpiryCacheAsync.js +2 -0
  24. package/dist/interface/ExpiryCacheSafe.d.ts +6 -0
  25. package/dist/interface/ExpiryCacheSafe.js +2 -0
  26. package/dist/interface/ExpiryCacheSync.d.ts +6 -0
  27. package/dist/interface/ExpiryCacheSync.js +2 -0
  28. package/dist/interface/ExpiryCacheUnsafe.d.ts +6 -0
  29. package/dist/interface/ExpiryCacheUnsafe.js +2 -0
  30. package/dist/interface/index.d.ts +4 -0
  31. package/dist/types/RefreshFunction.d.ts +11 -0
  32. package/dist/types/RefreshFunction.js +1 -0
  33. package/dist/types/ReturnType.d.ts +10 -0
  34. package/dist/types/ReturnType.js +1 -0
  35. package/dist/types/events.d.ts +8 -0
  36. package/dist/types/events.js +1 -0
  37. package/dist/types/index.d.ts +2 -0
  38. package/dist/types/index.js +1 -0
  39. package/dist/utils.d.ts +10 -0
  40. package/dist/utils.js +17 -0
  41. package/package.json +8 -5
  42. package/dist/ExpiryCacheBase.d.ts +0 -95
  43. package/dist/ExpiryCacheBase.js +0 -115
  44. package/dist/types.d.ts +0 -2
  45. /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
- - **Raw Data Access**: Access the raw cached data even if it has expired.
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); // inferring to ExpiryCache<string, () => string>
32
- console.log(cache.getData()); // Outputs: initial
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); // Outputs: true
36
- console.log(cache.getData()); // Outputs: null
37
- console.log(cache.getRawData()); // Outputs: initial (returns the raw data even if expired)
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()); // Outputs: refreshed
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); // Outputs: true
44
- console.log(cache.getDataOrRefresh()); // Outputs: refreshed (refreshes the cache and returns the new value)
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); // Outputs: 1000
47
- console.log(cache.expiresAt); // Outputs: current timestamp + 1000 milliseconds
48
- console.log(cache.timeToLive); // Outputs: time remaining until expiration in milliseconds
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
- console.log(cache.expire()); // Manually expire the cache
51
- console.log(cache.isExpired); // Outputs: true
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()); // Outputs: fetched data from API
89
+ console.log(cache.getData()); // fetched data from API
72
90
  ```
73
91
 
74
- ### With Parameters
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 { ExpiryCache } from "@darco2903/expiry-cache";
99
+ import { ExpiryCacheSafe } from "@darco2903/expiry-cache";
100
+ import { err, ok } from "neverthrow";
78
101
 
79
- const cache = new ExpiryCache(10, (a: number, b: number) => a + b, 200);
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
- cache.refresh(5, 7); // typed: refresh(a: number, b: number)
82
- console.log(cache.getData()); // Outputs: 12
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
- ### Setting Expiry Manually
121
+ ### Event Emission
86
122
 
87
123
  ```ts
88
124
  import { ExpiryCache } from "@darco2903/expiry-cache";
89
125
 
90
- const cache = new ExpiryCache(0, () => 0); // Cache with no expiry by default
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
- cache.refreshExpiresAt(Date.now());
94
- cache.refreshExpiresIn(1000);
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
  ```
@@ -1,27 +1,14 @@
1
- import { type Time } from "@darco2903/secondthought";
2
- import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
3
- import type { RefreshFunction } from "./types.js";
4
- export declare class ExpiryCache<T, U extends RefreshFunction<T>> extends ExpiryCacheBase<T, U> {
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
- * Creates an instance of ExpiryCache.
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
- constructor(data: T, callback: U, expirationTime?: number | Time);
9
+ refresh(...args: Parameters<F>): T;
12
10
  /**
13
- * Refreshes the cached data using the callback function.
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
- refresh(...args: Parameters<U>): void;
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
  }
@@ -1,41 +1,20 @@
1
- import { Minute } from "@darco2903/secondthought";
2
- import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
3
- export class ExpiryCache extends ExpiryCacheBase {
1
+ import { ExpiryCacheSyncBase } from "./base/index.js";
2
+ export class ExpiryCache extends ExpiryCacheSyncBase {
4
3
  /**
5
- * Creates an instance of ExpiryCache.
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
- constructor(data, callback, expirationTime = new Minute(1)) {
11
- super(data, callback, expirationTime);
6
+ refresh(...args) {
7
+ this.setData(this.refreshFn(...args));
8
+ this._emit("refreshed", this.data);
9
+ return this.data;
12
10
  }
13
11
  /**
14
- * Refreshes the cached data using the callback function.
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 { type Time } from "@darco2903/secondthought";
2
- import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
3
- import type { RefreshFunctionAsync } from "./types.js";
4
- export declare class ExpiryCacheAsync<T, U extends RefreshFunctionAsync<T>> extends ExpiryCacheBase<T, U> {
5
- protected _refreshCb: Promise<T> | null;
6
- get refreshing(): boolean;
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
- * 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.
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
- constructor(data: T, callback: U, expirationTime?: number | Time);
11
+ refresh(...args: Parameters<F>): Promise<T>;
14
12
  /**
15
- * Refreshes the cached data using the callback function.
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
- refresh(...args: Parameters<U>): Promise<void>;
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
  }
@@ -1,52 +1,28 @@
1
- import { Minute } from "@darco2903/secondthought";
2
- import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
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 cached data using the callback function.
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.callback(...args);
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
+ }