@twin.org/core 0.0.1-next.52 → 0.0.1-next.54
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/dist/cjs/index.cjs
CHANGED
|
@@ -4006,20 +4006,88 @@ class AsyncCache {
|
|
|
4006
4006
|
* @param key The key for the entry in the cache.
|
|
4007
4007
|
* @param ttlMs The TTL of the entry in the cache.
|
|
4008
4008
|
* @param requestMethod The method to call if not cached.
|
|
4009
|
+
* @param cacheFailures Cache failure results, defaults to false.
|
|
4009
4010
|
* @returns The response.
|
|
4010
4011
|
*/
|
|
4011
|
-
static exec(key, ttlMs, requestMethod) {
|
|
4012
|
+
static exec(key, ttlMs, requestMethod, cacheFailures) {
|
|
4012
4013
|
const cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;
|
|
4013
4014
|
if (cacheEnabled) {
|
|
4014
4015
|
AsyncCache.cleanupExpired();
|
|
4015
4016
|
const cache = AsyncCache.getSharedCache();
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4017
|
+
// Do we have a cache entry for the key
|
|
4018
|
+
if (cache[key]) {
|
|
4019
|
+
if (!Is.empty(cache[key].result)) {
|
|
4020
|
+
// If the cache has already resulted in a value, resolve it
|
|
4021
|
+
return Promise.resolve(cache[key].result);
|
|
4022
|
+
}
|
|
4023
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4024
|
+
// If the cache has already resulted in an error, reject it
|
|
4025
|
+
return Promise.reject(cache[key].error);
|
|
4026
|
+
}
|
|
4027
|
+
// Otherwise create a promise to return and store the resolver
|
|
4028
|
+
// and rejector in the cache entry, so that we can call then
|
|
4029
|
+
// when the request is done
|
|
4030
|
+
let storedResolve;
|
|
4031
|
+
let storedReject;
|
|
4032
|
+
const wait = new Promise((resolve, reject) => {
|
|
4033
|
+
storedResolve = resolve;
|
|
4034
|
+
storedReject = reject;
|
|
4035
|
+
});
|
|
4036
|
+
if (!Is.empty(storedResolve) && !Is.empty(storedReject)) {
|
|
4037
|
+
cache[key].promiseQueue.push({
|
|
4038
|
+
requestMethod,
|
|
4039
|
+
resolve: storedResolve,
|
|
4040
|
+
reject: storedReject
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
4043
|
+
return wait;
|
|
4021
4044
|
}
|
|
4022
|
-
|
|
4045
|
+
// If we don't have a cache entry, create a new one
|
|
4046
|
+
cache[key] = {
|
|
4047
|
+
promiseQueue: [],
|
|
4048
|
+
expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
|
|
4049
|
+
};
|
|
4050
|
+
// Return a promise that wraps the original request method
|
|
4051
|
+
// so that we can store any results or errors in the cache
|
|
4052
|
+
return new Promise((resolve, reject) => {
|
|
4053
|
+
// Call the request method and store the result
|
|
4054
|
+
requestMethod()
|
|
4055
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4056
|
+
.then(res => {
|
|
4057
|
+
// If the request was successful, store the result
|
|
4058
|
+
cache[key].result = res;
|
|
4059
|
+
// and resolve both this promise and all the waiters
|
|
4060
|
+
resolve(res);
|
|
4061
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4062
|
+
wait.resolve(res);
|
|
4063
|
+
}
|
|
4064
|
+
return res;
|
|
4065
|
+
})
|
|
4066
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4067
|
+
.catch((err) => {
|
|
4068
|
+
// Reject the promise
|
|
4069
|
+
reject(err);
|
|
4070
|
+
// Handle the waiters based on the cacheFailures flag
|
|
4071
|
+
if (cacheFailures ?? false) {
|
|
4072
|
+
// If we are caching failures, store the error and reject the waiters
|
|
4073
|
+
cache[key].error = err;
|
|
4074
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4075
|
+
wait.reject(err);
|
|
4076
|
+
}
|
|
4077
|
+
// Clear the waiters so we don't call them again
|
|
4078
|
+
cache[key].promiseQueue = [];
|
|
4079
|
+
}
|
|
4080
|
+
else {
|
|
4081
|
+
// If not caching failures for any queued requests we
|
|
4082
|
+
// have no value to either resolve or reject, so we
|
|
4083
|
+
// just resolve with the original request method
|
|
4084
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4085
|
+
wait.resolve(wait.requestMethod());
|
|
4086
|
+
}
|
|
4087
|
+
delete cache[key];
|
|
4088
|
+
}
|
|
4089
|
+
});
|
|
4090
|
+
});
|
|
4023
4091
|
}
|
|
4024
4092
|
}
|
|
4025
4093
|
/**
|
|
@@ -4029,7 +4097,14 @@ class AsyncCache {
|
|
|
4029
4097
|
*/
|
|
4030
4098
|
static async get(key) {
|
|
4031
4099
|
const cache = AsyncCache.getSharedCache();
|
|
4032
|
-
|
|
4100
|
+
if (!Is.empty(cache[key].result)) {
|
|
4101
|
+
// If the cache has already resulted in a value, resolve it
|
|
4102
|
+
return cache[key].result;
|
|
4103
|
+
}
|
|
4104
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4105
|
+
// If the cache has already resulted in an error, reject it
|
|
4106
|
+
throw cache[key].error;
|
|
4107
|
+
}
|
|
4033
4108
|
}
|
|
4034
4109
|
/**
|
|
4035
4110
|
* Set an entry into the cache.
|
|
@@ -4041,7 +4116,8 @@ class AsyncCache {
|
|
|
4041
4116
|
static async set(key, value, ttlMs) {
|
|
4042
4117
|
const cache = AsyncCache.getSharedCache();
|
|
4043
4118
|
cache[key] = {
|
|
4044
|
-
|
|
4119
|
+
result: value,
|
|
4120
|
+
promiseQueue: [],
|
|
4045
4121
|
expires: Date.now() + (ttlMs ?? 1000)
|
|
4046
4122
|
};
|
|
4047
4123
|
}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -4004,20 +4004,88 @@ class AsyncCache {
|
|
|
4004
4004
|
* @param key The key for the entry in the cache.
|
|
4005
4005
|
* @param ttlMs The TTL of the entry in the cache.
|
|
4006
4006
|
* @param requestMethod The method to call if not cached.
|
|
4007
|
+
* @param cacheFailures Cache failure results, defaults to false.
|
|
4007
4008
|
* @returns The response.
|
|
4008
4009
|
*/
|
|
4009
|
-
static exec(key, ttlMs, requestMethod) {
|
|
4010
|
+
static exec(key, ttlMs, requestMethod, cacheFailures) {
|
|
4010
4011
|
const cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;
|
|
4011
4012
|
if (cacheEnabled) {
|
|
4012
4013
|
AsyncCache.cleanupExpired();
|
|
4013
4014
|
const cache = AsyncCache.getSharedCache();
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4015
|
+
// Do we have a cache entry for the key
|
|
4016
|
+
if (cache[key]) {
|
|
4017
|
+
if (!Is.empty(cache[key].result)) {
|
|
4018
|
+
// If the cache has already resulted in a value, resolve it
|
|
4019
|
+
return Promise.resolve(cache[key].result);
|
|
4020
|
+
}
|
|
4021
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4022
|
+
// If the cache has already resulted in an error, reject it
|
|
4023
|
+
return Promise.reject(cache[key].error);
|
|
4024
|
+
}
|
|
4025
|
+
// Otherwise create a promise to return and store the resolver
|
|
4026
|
+
// and rejector in the cache entry, so that we can call then
|
|
4027
|
+
// when the request is done
|
|
4028
|
+
let storedResolve;
|
|
4029
|
+
let storedReject;
|
|
4030
|
+
const wait = new Promise((resolve, reject) => {
|
|
4031
|
+
storedResolve = resolve;
|
|
4032
|
+
storedReject = reject;
|
|
4033
|
+
});
|
|
4034
|
+
if (!Is.empty(storedResolve) && !Is.empty(storedReject)) {
|
|
4035
|
+
cache[key].promiseQueue.push({
|
|
4036
|
+
requestMethod,
|
|
4037
|
+
resolve: storedResolve,
|
|
4038
|
+
reject: storedReject
|
|
4039
|
+
});
|
|
4040
|
+
}
|
|
4041
|
+
return wait;
|
|
4019
4042
|
}
|
|
4020
|
-
|
|
4043
|
+
// If we don't have a cache entry, create a new one
|
|
4044
|
+
cache[key] = {
|
|
4045
|
+
promiseQueue: [],
|
|
4046
|
+
expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
|
|
4047
|
+
};
|
|
4048
|
+
// Return a promise that wraps the original request method
|
|
4049
|
+
// so that we can store any results or errors in the cache
|
|
4050
|
+
return new Promise((resolve, reject) => {
|
|
4051
|
+
// Call the request method and store the result
|
|
4052
|
+
requestMethod()
|
|
4053
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4054
|
+
.then(res => {
|
|
4055
|
+
// If the request was successful, store the result
|
|
4056
|
+
cache[key].result = res;
|
|
4057
|
+
// and resolve both this promise and all the waiters
|
|
4058
|
+
resolve(res);
|
|
4059
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4060
|
+
wait.resolve(res);
|
|
4061
|
+
}
|
|
4062
|
+
return res;
|
|
4063
|
+
})
|
|
4064
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
4065
|
+
.catch((err) => {
|
|
4066
|
+
// Reject the promise
|
|
4067
|
+
reject(err);
|
|
4068
|
+
// Handle the waiters based on the cacheFailures flag
|
|
4069
|
+
if (cacheFailures ?? false) {
|
|
4070
|
+
// If we are caching failures, store the error and reject the waiters
|
|
4071
|
+
cache[key].error = err;
|
|
4072
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4073
|
+
wait.reject(err);
|
|
4074
|
+
}
|
|
4075
|
+
// Clear the waiters so we don't call them again
|
|
4076
|
+
cache[key].promiseQueue = [];
|
|
4077
|
+
}
|
|
4078
|
+
else {
|
|
4079
|
+
// If not caching failures for any queued requests we
|
|
4080
|
+
// have no value to either resolve or reject, so we
|
|
4081
|
+
// just resolve with the original request method
|
|
4082
|
+
for (const wait of cache[key].promiseQueue) {
|
|
4083
|
+
wait.resolve(wait.requestMethod());
|
|
4084
|
+
}
|
|
4085
|
+
delete cache[key];
|
|
4086
|
+
}
|
|
4087
|
+
});
|
|
4088
|
+
});
|
|
4021
4089
|
}
|
|
4022
4090
|
}
|
|
4023
4091
|
/**
|
|
@@ -4027,7 +4095,14 @@ class AsyncCache {
|
|
|
4027
4095
|
*/
|
|
4028
4096
|
static async get(key) {
|
|
4029
4097
|
const cache = AsyncCache.getSharedCache();
|
|
4030
|
-
|
|
4098
|
+
if (!Is.empty(cache[key].result)) {
|
|
4099
|
+
// If the cache has already resulted in a value, resolve it
|
|
4100
|
+
return cache[key].result;
|
|
4101
|
+
}
|
|
4102
|
+
else if (!Is.empty(cache[key].error)) {
|
|
4103
|
+
// If the cache has already resulted in an error, reject it
|
|
4104
|
+
throw cache[key].error;
|
|
4105
|
+
}
|
|
4031
4106
|
}
|
|
4032
4107
|
/**
|
|
4033
4108
|
* Set an entry into the cache.
|
|
@@ -4039,7 +4114,8 @@ class AsyncCache {
|
|
|
4039
4114
|
static async set(key, value, ttlMs) {
|
|
4040
4115
|
const cache = AsyncCache.getSharedCache();
|
|
4041
4116
|
cache[key] = {
|
|
4042
|
-
|
|
4117
|
+
result: value,
|
|
4118
|
+
promiseQueue: [],
|
|
4043
4119
|
expires: Date.now() + (ttlMs ?? 1000)
|
|
4044
4120
|
};
|
|
4045
4121
|
}
|
|
@@ -7,9 +7,10 @@ export declare class AsyncCache {
|
|
|
7
7
|
* @param key The key for the entry in the cache.
|
|
8
8
|
* @param ttlMs The TTL of the entry in the cache.
|
|
9
9
|
* @param requestMethod The method to call if not cached.
|
|
10
|
+
* @param cacheFailures Cache failure results, defaults to false.
|
|
10
11
|
* @returns The response.
|
|
11
12
|
*/
|
|
12
|
-
static exec<T = unknown>(key: string, ttlMs: number | undefined, requestMethod: () => Promise<T
|
|
13
|
+
static exec<T = unknown>(key: string, ttlMs: number | undefined, requestMethod: () => Promise<T>, cacheFailures?: boolean): Promise<T> | undefined;
|
|
13
14
|
/**
|
|
14
15
|
* Get an entry from the cache.
|
|
15
16
|
* @param key The key to get from the cache.
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @twin.org/core - Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.1-next.54](https://github.com/twinfoundation/framework/compare/core-v0.0.1-next.53...core-v0.0.1-next.54) (2025-05-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Miscellaneous Chores
|
|
7
|
+
|
|
8
|
+
* **core:** Synchronize repo versions
|
|
9
|
+
|
|
10
|
+
## [0.0.1-next.53](https://github.com/twinfoundation/framework/compare/core-v0.0.1-next.52...core-v0.0.1-next.53) (2025-05-01)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* async cache don't cache failures unless requested ([658ec4b](https://github.com/twinfoundation/framework/commit/658ec4b67a58a075de4702a3886d151e25ad3ddc))
|
|
16
|
+
|
|
3
17
|
## [0.0.1-next.52](https://github.com/twinfoundation/framework/compare/core-v0.0.1-next.51...core-v0.0.1-next.52) (2025-04-17)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -16,7 +16,7 @@ Cache the results from asynchronous requests.
|
|
|
16
16
|
|
|
17
17
|
### exec()
|
|
18
18
|
|
|
19
|
-
> `static` **exec**\<`T`\>(`key`, `ttlMs`, `requestMethod`): `undefined` \| `Promise`\<`T`\>
|
|
19
|
+
> `static` **exec**\<`T`\>(`key`, `ttlMs`, `requestMethod`, `cacheFailures?`): `undefined` \| `Promise`\<`T`\>
|
|
20
20
|
|
|
21
21
|
Execute an async request and cache the result.
|
|
22
22
|
|
|
@@ -46,6 +46,12 @@ The TTL of the entry in the cache.
|
|
|
46
46
|
|
|
47
47
|
The method to call if not cached.
|
|
48
48
|
|
|
49
|
+
##### cacheFailures?
|
|
50
|
+
|
|
51
|
+
`boolean`
|
|
52
|
+
|
|
53
|
+
Cache failure results, defaults to false.
|
|
54
|
+
|
|
49
55
|
#### Returns
|
|
50
56
|
|
|
51
57
|
`undefined` \| `Promise`\<`T`\>
|