@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.
Files changed (43) 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/{ExpiryCacheBase.d.ts → base/ExpiryCacheBase.d.ts} +27 -29
  15. package/dist/{ExpiryCacheBase.js → base/ExpiryCacheBase.js} +57 -22
  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/types.d.ts +0 -2
  43. /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
+ }
@@ -1,10 +1,15 @@
1
+ import { TypedEmitterProtected } from "@darco2903/typed-emitter";
1
2
  import { Time, Millisecond } from "@darco2903/secondthought";
2
- import type { RefreshFunction, RefreshFunctionAsync } from "./types.js";
3
- export declare abstract class ExpiryCacheBase<T, U extends RefreshFunction<T> | RefreshFunctionAsync<T>> {
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 callback: U;
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 callback The function to refresh the cached data.
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, callback: U, expirationTime?: number | Time);
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 timestamp The expiration timestamp in milliseconds.
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(timestamp: number | Time): void;
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 raw cached data without checking expiration.
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
- * Refreshes the cached data using the callback function.
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 refreshExpiresAt(expiresAt: number | Time, ...args: Parameters<U>): void | Promise<void>;
107
+ abstract refresh(...args: Parameters<F>): CacheReturnType<T, E>;
109
108
  /**
110
- * Refreshes the cached data and sets a new expiration time.
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 refreshExpiresIn(expirationTime: number | Time, ...args: Parameters<U>): void | Promise<void>;
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
- export class ExpiryCacheBase {
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 callback The function to refresh the cached data.
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, callback, expirationTime = new Minute(1)) {
70
- const expTime = this.argToMs(expirationTime);
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.callback = callback;
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 = this.argToMs(ms);
97
- this._expiresAt = this.expIn(t);
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 timestamp The expiration timestamp in milliseconds.
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(timestamp) {
104
- this._expiresAt = this.argToMs(timestamp);
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
- this.setDataExpiresIn(data, this._expirationTime);
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._expiresAt = expiresAt;
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.setDataExpiresAt(data, this.expIn(expirationTime));
159
+ this.data = data;
160
+ this.setExpiresIn(expirationTime);
126
161
  }
127
162
  /**
128
- * Gets the raw cached data without checking expiration.
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
+ }
@@ -0,0 +1,3 @@
1
+ import { ExpiryCacheBase } from "./ExpiryCacheBase.js";
2
+ export class ExpiryCacheSyncBase extends ExpiryCacheBase {
3
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./ExpiryCacheAsyncBase.js";
2
+ export * from "./ExpiryCacheSyncBase.js";
@@ -0,0 +1,2 @@
1
+ export * from "./ExpiryCacheAsyncBase.js";
2
+ export * from "./ExpiryCacheSyncBase.js";
package/dist/index.d.ts CHANGED
@@ -1,2 +1,6 @@
1
1
  export * from "./ExpiryCache.js";
2
2
  export * from "./ExpiryCacheAsync.js";
3
+ export * from "./ExpiryCacheSafe.js";
4
+ export * from "./ExpiryCacheSafeAsync.js";
5
+ export type * from "./types/index.js";
6
+ export { ReturnOptions } from "./ReturnOptions.js";
package/dist/index.js CHANGED
@@ -1,2 +1,5 @@
1
1
  export * from "./ExpiryCache.js";
2
2
  export * from "./ExpiryCacheAsync.js";
3
+ export * from "./ExpiryCacheSafe.js";
4
+ export * from "./ExpiryCacheSafeAsync.js";
5
+ export { ReturnOptions } from "./ReturnOptions.js";
@@ -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,2 @@
1
+ export class IExpiryCacheAsync {
2
+ }
@@ -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,2 @@
1
+ export class IExpiryCacheSafe {
2
+ }
@@ -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,2 @@
1
+ export class IExpiryCacheSync {
2
+ }
@@ -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,2 @@
1
+ export class IExpiryCacheUnsafe {
2
+ }
@@ -0,0 +1,4 @@
1
+ export type * from "./ExpiryCacheAsync.js";
2
+ export type * from "./ExpiryCacheSafe.js";
3
+ export type * from "./ExpiryCacheSync.js";
4
+ export type * from "./ExpiryCacheUnsafe.js";
@@ -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,8 @@
1
+ export interface CacheEventsUnsafe<T> {
2
+ expired: [];
3
+ refreshed: [T];
4
+ }
5
+ export interface CacheEventsSafe<T, E> extends CacheEventsUnsafe<T> {
6
+ error: [E];
7
+ }
8
+ export type CacheEvents<T, E = never> = CacheEventsSafe<T, E> | CacheEventsUnsafe<T>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { RefreshFunctionUnsafeSync, RefreshFunctionUnsafeAsync, RefreshFunctionSafeSync, RefreshFunctionSafeAsync, } from "./RefreshFunction.js";
2
+ export { ReturnTypeUnsafeSync, ReturnTypeUnsafeAsync, ReturnTypeSafeSync, ReturnTypeSafeAsync, } from "./ReturnType.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -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": "2.1.0",
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
- "@vitest/ui": "^3.2.4",
23
- "rimraf": "^6.1.0",
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
@@ -1,2 +0,0 @@
1
- export type RefreshFunction<T> = (...args: any[]) => T;
2
- export type RefreshFunctionAsync<T> = (...args: any[]) => Promise<T>;
File without changes