@twin.org/core 0.0.3-next.24 → 0.0.3-next.26

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.
@@ -16,84 +16,88 @@ export class AsyncCache {
16
16
  */
17
17
  static exec(key, ttlMs, requestMethod, cacheFailures) {
18
18
  const cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;
19
- if (cacheEnabled) {
20
- AsyncCache.cleanupExpired();
21
- const cache = AsyncCache.getSharedCache();
22
- // Do we have a cache entry for the key
23
- if (cache[key]) {
24
- if (!Is.empty(cache[key].result)) {
25
- // If the cache has already resulted in a value, resolve it
26
- return Promise.resolve(cache[key].result);
27
- }
28
- else if (!Is.empty(cache[key].error)) {
29
- // If the cache has already resulted in an error, reject it
30
- return Promise.reject(cache[key].error);
31
- }
32
- // Otherwise create a promise to return and store the resolver
33
- // and rejector in the cache entry, so that we can call then
34
- // when the request is done
35
- let storedResolve;
36
- let storedReject;
37
- const wait = new Promise((resolve, reject) => {
38
- storedResolve = resolve;
39
- storedReject = reject;
19
+ if (!cacheEnabled) {
20
+ // No caching, just execute the request method
21
+ return requestMethod();
22
+ }
23
+ AsyncCache.cleanupExpired();
24
+ const cache = AsyncCache.getSharedCache();
25
+ // Do we have a cache entry for the key
26
+ if (cache[key]) {
27
+ if (!Is.empty(cache[key].result)) {
28
+ // If the cache has already resulted in a value, resolve it
29
+ return Promise.resolve(cache[key].result);
30
+ }
31
+ else if (!Is.empty(cache[key].error)) {
32
+ // If the cache has already resulted in an error, reject it
33
+ return Promise.reject(cache[key].error);
34
+ }
35
+ // Otherwise create a promise to return and store the resolver
36
+ // and rejector in the cache entry, so that we can call then
37
+ // when the request is done
38
+ let storedResolve;
39
+ let storedReject;
40
+ const wait = new Promise((resolve, reject) => {
41
+ storedResolve = resolve;
42
+ storedReject = reject;
43
+ });
44
+ if (!Is.empty(storedResolve) && !Is.empty(storedReject)) {
45
+ cache[key].promiseQueue.push({
46
+ requestMethod,
47
+ resolve: storedResolve,
48
+ reject: storedReject
40
49
  });
41
- if (!Is.empty(storedResolve) && !Is.empty(storedReject)) {
42
- cache[key].promiseQueue.push({
43
- requestMethod,
44
- resolve: storedResolve,
45
- reject: storedReject
46
- });
47
- }
48
- return wait;
49
50
  }
50
- // If we don't have a cache entry, create a new one
51
- cache[key] = {
52
- promiseQueue: [],
53
- expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
54
- };
55
- // Return a promise that wraps the original request method
56
- // so that we can store any results or errors in the cache
57
- return new Promise((resolve, reject) => {
58
- // Call the request method and store the result
59
- requestMethod()
60
- // eslint-disable-next-line promise/prefer-await-to-then
61
- .then(res => {
62
- // If the request was successful, store the result
63
- cache[key].result = res;
64
- // and resolve both this promise and all the waiters
65
- resolve(res);
51
+ return wait;
52
+ }
53
+ // If we don't have a cache entry, create a new one
54
+ cache[key] = {
55
+ promiseQueue: [],
56
+ expires: ttlMs === 0 ? 0 : Date.now() + ttlMs
57
+ };
58
+ // Return a promise that wraps the original request method
59
+ // so that we can store any results or errors in the cache
60
+ return new Promise((resolve, reject) => {
61
+ // Call the request method and store the result
62
+ requestMethod()
63
+ // eslint-disable-next-line promise/prefer-await-to-then
64
+ .then(res => {
65
+ // If the request was successful, store the result
66
+ cache[key].result = res;
67
+ // and resolve both this promise and all the waiters
68
+ resolve(res);
69
+ for (const wait of cache[key].promiseQueue) {
70
+ wait.resolve(res);
71
+ }
72
+ cache[key].promiseQueue = [];
73
+ return res;
74
+ })
75
+ // eslint-disable-next-line promise/prefer-await-to-then
76
+ .catch((err) => {
77
+ // Reject the promise
78
+ reject(err);
79
+ // Handle the waiters based on the cacheFailures flag
80
+ if (cacheFailures ?? false) {
81
+ // If we are caching failures, store the error and reject the waiters
82
+ cache[key].error = err;
66
83
  for (const wait of cache[key].promiseQueue) {
67
- wait.resolve(res);
68
- }
69
- return res;
70
- })
71
- // eslint-disable-next-line promise/prefer-await-to-then
72
- .catch((err) => {
73
- // Reject the promise
74
- reject(err);
75
- // Handle the waiters based on the cacheFailures flag
76
- if (cacheFailures ?? false) {
77
- // If we are caching failures, store the error and reject the waiters
78
- cache[key].error = err;
79
- for (const wait of cache[key].promiseQueue) {
80
- wait.reject(err);
81
- }
82
- // Clear the waiters so we don't call them again
83
- cache[key].promiseQueue = [];
84
+ wait.reject(err);
84
85
  }
85
- else {
86
- // If not caching failures for any queued requests we
87
- // have no value to either resolve or reject, so we
88
- // just resolve with the original request method
89
- for (const wait of cache[key].promiseQueue) {
90
- wait.resolve(wait.requestMethod());
91
- }
92
- delete cache[key];
86
+ // Clear the waiters so we don't call them again
87
+ cache[key].promiseQueue = [];
88
+ }
89
+ else {
90
+ // If not caching failures for any queued requests we
91
+ // have no value to either resolve or reject, so we
92
+ // just resolve with the original request method
93
+ for (const wait of cache[key].promiseQueue) {
94
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
95
+ AsyncCache.resolveWaiter(wait.requestMethod, wait.resolve, wait.reject);
93
96
  }
94
- });
97
+ delete cache[key];
98
+ }
95
99
  });
96
- }
100
+ });
97
101
  }
98
102
  /**
99
103
  * Get an entry from the cache.
@@ -102,11 +106,11 @@ export class AsyncCache {
102
106
  */
103
107
  static async get(key) {
104
108
  const cache = AsyncCache.getSharedCache();
105
- if (!Is.empty(cache[key].result)) {
109
+ if (!Is.empty(cache[key]?.result)) {
106
110
  // If the cache has already resulted in a value, resolve it
107
111
  return cache[key].result;
108
112
  }
109
- else if (!Is.empty(cache[key].error)) {
113
+ if (!Is.empty(cache[key]?.error)) {
110
114
  // If the cache has already resulted in an error, reject it
111
115
  throw cache[key].error;
112
116
  }
@@ -175,5 +179,19 @@ export class AsyncCache {
175
179
  }
176
180
  return sharedCache;
177
181
  }
182
+ /**
183
+ * Resolve a waiter by re-running its request method safely.
184
+ * @param requestMethod The method to execute.
185
+ * @param resolve The resolver for the waiter.
186
+ * @param reject The rejector for the waiter.
187
+ */
188
+ static async resolveWaiter(requestMethod, resolve, reject) {
189
+ try {
190
+ resolve(await requestMethod());
191
+ }
192
+ catch (waitErr) {
193
+ reject(waitErr);
194
+ }
195
+ }
178
196
  }
179
197
  //# sourceMappingURL=asyncCache.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"asyncCache.js","sourceRoot":"","sources":["../../../src/utils/asyncCache.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"./is.js\";\nimport { SharedStore } from \"./sharedStore.js\";\n\n/**\n * Cache the results from asynchronous requests.\n */\nexport class AsyncCache {\n\t/**\n\t * Execute an async request and cache the result.\n\t * @param key The key for the entry in the cache.\n\t * @param ttlMs The TTL of the entry in the cache.\n\t * @param requestMethod The method to call if not cached.\n\t * @param cacheFailures Cache failure results, defaults to false.\n\t * @returns The response.\n\t */\n\tpublic static exec<T = unknown>(\n\t\tkey: string,\n\t\tttlMs: number | undefined,\n\t\trequestMethod: () => Promise<T>,\n\t\tcacheFailures?: boolean\n\t): Promise<T> | undefined {\n\t\tconst cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;\n\t\tif (cacheEnabled) {\n\t\t\tAsyncCache.cleanupExpired();\n\n\t\t\tconst cache = AsyncCache.getSharedCache<T>();\n\n\t\t\t// Do we have a cache entry for the key\n\t\t\tif (cache[key]) {\n\t\t\t\tif (!Is.empty(cache[key].result)) {\n\t\t\t\t\t// If the cache has already resulted in a value, resolve it\n\t\t\t\t\treturn Promise.resolve(cache[key].result);\n\t\t\t\t} else if (!Is.empty(cache[key].error)) {\n\t\t\t\t\t// If the cache has already resulted in an error, reject it\n\t\t\t\t\treturn Promise.reject(cache[key].error);\n\t\t\t\t}\n\n\t\t\t\t// Otherwise create a promise to return and store the resolver\n\t\t\t\t// and rejector in the cache entry, so that we can call then\n\t\t\t\t// when the request is done\n\n\t\t\t\tlet storedResolve: ((value: T | PromiseLike<T>) => void) | undefined;\n\t\t\t\tlet storedReject: ((reason?: unknown) => void) | undefined;\n\t\t\t\tconst wait = new Promise<T>((resolve, reject) => {\n\t\t\t\t\tstoredResolve = resolve;\n\t\t\t\t\tstoredReject = reject;\n\t\t\t\t});\n\t\t\t\tif (!Is.empty(storedResolve) && !Is.empty(storedReject)) {\n\t\t\t\t\tcache[key].promiseQueue.push({\n\t\t\t\t\t\trequestMethod,\n\t\t\t\t\t\tresolve: storedResolve,\n\t\t\t\t\t\treject: storedReject\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn wait;\n\t\t\t}\n\n\t\t\t// If we don't have a cache entry, create a new one\n\t\t\tcache[key] = {\n\t\t\t\tpromiseQueue: [],\n\t\t\t\texpires: ttlMs === 0 ? 0 : Date.now() + ttlMs\n\t\t\t};\n\n\t\t\t// Return a promise that wraps the original request method\n\t\t\t// so that we can store any results or errors in the cache\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// Call the request method and store the result\n\t\t\t\trequestMethod()\n\t\t\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t\t\t\t.then(res => {\n\t\t\t\t\t\t// If the request was successful, store the result\n\t\t\t\t\t\tcache[key].result = res;\n\n\t\t\t\t\t\t// and resolve both this promise and all the waiters\n\t\t\t\t\t\tresolve(res);\n\t\t\t\t\t\tfor (const wait of cache[key].promiseQueue) {\n\t\t\t\t\t\t\twait.resolve(res);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn res;\n\t\t\t\t\t})\n\t\t\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t// Reject the promise\n\t\t\t\t\t\treject(err);\n\n\t\t\t\t\t\t// Handle the waiters based on the cacheFailures flag\n\t\t\t\t\t\tif (cacheFailures ?? false) {\n\t\t\t\t\t\t\t// If we are caching failures, store the error and reject the waiters\n\t\t\t\t\t\t\tcache[key].error = err;\n\t\t\t\t\t\t\tfor (const wait of cache[key].promiseQueue) {\n\t\t\t\t\t\t\t\twait.reject(err);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Clear the waiters so we don't call them again\n\t\t\t\t\t\t\tcache[key].promiseQueue = [];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// If not caching failures for any queued requests we\n\t\t\t\t\t\t\t// have no value to either resolve or reject, so we\n\t\t\t\t\t\t\t// just resolve with the original request method\n\t\t\t\t\t\t\tfor (const wait of cache[key].promiseQueue) {\n\t\t\t\t\t\t\t\twait.resolve(wait.requestMethod());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdelete cache[key];\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Get an entry from the cache.\n\t * @param key The key to get from the cache.\n\t * @returns The item from the cache if it exists.\n\t */\n\tpublic static async get<T = unknown>(key: string): Promise<T | undefined> {\n\t\tconst cache = AsyncCache.getSharedCache<T>();\n\t\tif (!Is.empty(cache[key].result)) {\n\t\t\t// If the cache has already resulted in a value, resolve it\n\t\t\treturn cache[key].result;\n\t\t} else if (!Is.empty(cache[key].error)) {\n\t\t\t// If the cache has already resulted in an error, reject it\n\t\t\tthrow cache[key].error;\n\t\t}\n\t}\n\n\t/**\n\t * Set an entry into the cache.\n\t * @param key The key to set in the cache.\n\t * @param value The value to set in the cache.\n\t * @param ttlMs The TTL of the entry in the cache in ms, defaults to 1s.\n\t * @returns Nothing.\n\t */\n\tpublic static async set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tcache[key] = {\n\t\t\tresult: value,\n\t\t\tpromiseQueue: [],\n\t\t\texpires: Date.now() + (ttlMs ?? 1000)\n\t\t};\n\t}\n\n\t/**\n\t * Remove an entry from the cache.\n\t * @param key The key to remove from the cache.\n\t */\n\tpublic static remove(key: string): void {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tdelete cache[key];\n\t}\n\n\t/**\n\t * Clear the cache.\n\t * @param prefix Optional prefix to clear only entries with that prefix.\n\t */\n\tpublic static clearCache(prefix?: string): void {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tif (Is.stringValue(prefix)) {\n\t\t\tfor (const entry in cache) {\n\t\t\t\tif (entry.startsWith(prefix)) {\n\t\t\t\t\tdelete cache[entry];\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tSharedStore.set(\"asyncCache\", {});\n\t\t}\n\t}\n\n\t/**\n\t * Perform a cleanup of the expired entries in the cache.\n\t */\n\tpublic static cleanupExpired(): void {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tfor (const entry in cache) {\n\t\t\tif (cache[entry].expires > 0 && cache[entry].expires < Date.now()) {\n\t\t\t\tdelete cache[entry];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the shared cache.\n\t * @returns The shared cache.\n\t * @internal\n\t */\n\tprivate static getSharedCache<T = unknown>(): {\n\t\t[url: string]: {\n\t\t\tresult?: T;\n\t\t\terror?: Error;\n\t\t\tpromiseQueue: {\n\t\t\t\trequestMethod: () => Promise<T>;\n\t\t\t\tresolve: (value: T | PromiseLike<T>) => void;\n\t\t\t\treject: (reason?: unknown) => void;\n\t\t\t}[];\n\t\t\texpires: number;\n\t\t};\n\t} {\n\t\tlet sharedCache = SharedStore.get<{\n\t\t\t[url: string]: {\n\t\t\t\tresult?: T;\n\t\t\t\terror?: Error;\n\t\t\t\tpromiseQueue: {\n\t\t\t\t\trequestMethod: () => Promise<T>;\n\t\t\t\t\tresolve: (value: T | PromiseLike<T>) => void;\n\t\t\t\t\treject: (reason?: unknown) => void;\n\t\t\t\t}[];\n\t\t\t\texpires: number;\n\t\t\t};\n\t\t}>(\"asyncCache\");\n\n\t\tif (Is.undefined(sharedCache)) {\n\t\t\tsharedCache = {};\n\t\t\tSharedStore.set(\"asyncCache\", sharedCache);\n\t\t}\n\n\t\treturn sharedCache;\n\t}\n}\n"]}
1
+ {"version":3,"file":"asyncCache.js","sourceRoot":"","sources":["../../../src/utils/asyncCache.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"./is.js\";\nimport { SharedStore } from \"./sharedStore.js\";\n\n/**\n * Cache the results from asynchronous requests.\n */\nexport class AsyncCache {\n\t/**\n\t * Execute an async request and cache the result.\n\t * @param key The key for the entry in the cache.\n\t * @param ttlMs The TTL of the entry in the cache.\n\t * @param requestMethod The method to call if not cached.\n\t * @param cacheFailures Cache failure results, defaults to false.\n\t * @returns The response.\n\t */\n\tpublic static exec<T = unknown>(\n\t\tkey: string,\n\t\tttlMs: number | undefined,\n\t\trequestMethod: () => Promise<T>,\n\t\tcacheFailures?: boolean\n\t): Promise<T> | undefined {\n\t\tconst cacheEnabled = Is.integer(ttlMs) && ttlMs >= 0;\n\t\tif (!cacheEnabled) {\n\t\t\t// No caching, just execute the request method\n\t\t\treturn requestMethod();\n\t\t}\n\n\t\tAsyncCache.cleanupExpired();\n\n\t\tconst cache = AsyncCache.getSharedCache<T>();\n\n\t\t// Do we have a cache entry for the key\n\t\tif (cache[key]) {\n\t\t\tif (!Is.empty(cache[key].result)) {\n\t\t\t\t// If the cache has already resulted in a value, resolve it\n\t\t\t\treturn Promise.resolve(cache[key].result);\n\t\t\t} else if (!Is.empty(cache[key].error)) {\n\t\t\t\t// If the cache has already resulted in an error, reject it\n\t\t\t\treturn Promise.reject(cache[key].error);\n\t\t\t}\n\n\t\t\t// Otherwise create a promise to return and store the resolver\n\t\t\t// and rejector in the cache entry, so that we can call then\n\t\t\t// when the request is done\n\n\t\t\tlet storedResolve: ((value: T | PromiseLike<T>) => void) | undefined;\n\t\t\tlet storedReject: ((reason?: unknown) => void) | undefined;\n\t\t\tconst wait = new Promise<T>((resolve, reject) => {\n\t\t\t\tstoredResolve = resolve;\n\t\t\t\tstoredReject = reject;\n\t\t\t});\n\t\t\tif (!Is.empty(storedResolve) && !Is.empty(storedReject)) {\n\t\t\t\tcache[key].promiseQueue.push({\n\t\t\t\t\trequestMethod,\n\t\t\t\t\tresolve: storedResolve,\n\t\t\t\t\treject: storedReject\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn wait;\n\t\t}\n\n\t\t// If we don't have a cache entry, create a new one\n\t\tcache[key] = {\n\t\t\tpromiseQueue: [],\n\t\t\texpires: ttlMs === 0 ? 0 : Date.now() + ttlMs\n\t\t};\n\n\t\t// Return a promise that wraps the original request method\n\t\t// so that we can store any results or errors in the cache\n\t\treturn new Promise((resolve, reject) => {\n\t\t\t// Call the request method and store the result\n\t\t\trequestMethod()\n\t\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t\t\t.then(res => {\n\t\t\t\t\t// If the request was successful, store the result\n\t\t\t\t\tcache[key].result = res;\n\n\t\t\t\t\t// and resolve both this promise and all the waiters\n\t\t\t\t\tresolve(res);\n\t\t\t\t\tfor (const wait of cache[key].promiseQueue) {\n\t\t\t\t\t\twait.resolve(res);\n\t\t\t\t\t}\n\t\t\t\t\tcache[key].promiseQueue = [];\n\t\t\t\t\treturn res;\n\t\t\t\t})\n\t\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\t// Reject the promise\n\t\t\t\t\treject(err);\n\n\t\t\t\t\t// Handle the waiters based on the cacheFailures flag\n\t\t\t\t\tif (cacheFailures ?? false) {\n\t\t\t\t\t\t// If we are caching failures, store the error and reject the waiters\n\t\t\t\t\t\tcache[key].error = err;\n\t\t\t\t\t\tfor (const wait of cache[key].promiseQueue) {\n\t\t\t\t\t\t\twait.reject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Clear the waiters so we don't call them again\n\t\t\t\t\t\tcache[key].promiseQueue = [];\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If not caching failures for any queued requests we\n\t\t\t\t\t\t// have no value to either resolve or reject, so we\n\t\t\t\t\t\t// just resolve with the original request method\n\t\t\t\t\t\tfor (const wait of cache[key].promiseQueue) {\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\t\t\t\t\t\tAsyncCache.resolveWaiter(wait.requestMethod, wait.resolve, wait.reject);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdelete cache[key];\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Get an entry from the cache.\n\t * @param key The key to get from the cache.\n\t * @returns The item from the cache if it exists.\n\t */\n\tpublic static async get<T = unknown>(key: string): Promise<T | undefined> {\n\t\tconst cache = AsyncCache.getSharedCache<T>();\n\t\tif (!Is.empty(cache[key]?.result)) {\n\t\t\t// If the cache has already resulted in a value, resolve it\n\t\t\treturn cache[key].result;\n\t\t}\n\n\t\tif (!Is.empty(cache[key]?.error)) {\n\t\t\t// If the cache has already resulted in an error, reject it\n\t\t\tthrow cache[key].error as Error;\n\t\t}\n\t}\n\n\t/**\n\t * Set an entry into the cache.\n\t * @param key The key to set in the cache.\n\t * @param value The value to set in the cache.\n\t * @param ttlMs The TTL of the entry in the cache in ms, defaults to 1s.\n\t * @returns Nothing.\n\t */\n\tpublic static async set<T = unknown>(key: string, value: T, ttlMs?: number): Promise<void> {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tcache[key] = {\n\t\t\tresult: value,\n\t\t\tpromiseQueue: [],\n\t\t\texpires: Date.now() + (ttlMs ?? 1000)\n\t\t};\n\t}\n\n\t/**\n\t * Remove an entry from the cache.\n\t * @param key The key to remove from the cache.\n\t */\n\tpublic static remove(key: string): void {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tdelete cache[key];\n\t}\n\n\t/**\n\t * Clear the cache.\n\t * @param prefix Optional prefix to clear only entries with that prefix.\n\t */\n\tpublic static clearCache(prefix?: string): void {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tif (Is.stringValue(prefix)) {\n\t\t\tfor (const entry in cache) {\n\t\t\t\tif (entry.startsWith(prefix)) {\n\t\t\t\t\tdelete cache[entry];\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tSharedStore.set(\"asyncCache\", {});\n\t\t}\n\t}\n\n\t/**\n\t * Perform a cleanup of the expired entries in the cache.\n\t */\n\tpublic static cleanupExpired(): void {\n\t\tconst cache = AsyncCache.getSharedCache();\n\t\tfor (const entry in cache) {\n\t\t\tif (cache[entry].expires > 0 && cache[entry].expires < Date.now()) {\n\t\t\t\tdelete cache[entry];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the shared cache.\n\t * @returns The shared cache.\n\t * @internal\n\t */\n\tprivate static getSharedCache<T = unknown>(): {\n\t\t[url: string]: {\n\t\t\tresult?: T;\n\t\t\terror?: unknown;\n\t\t\tpromiseQueue: {\n\t\t\t\trequestMethod: () => Promise<T>;\n\t\t\t\tresolve: (value: T | PromiseLike<T>) => void;\n\t\t\t\treject: (reason?: unknown) => void;\n\t\t\t}[];\n\t\t\texpires: number;\n\t\t};\n\t} {\n\t\tlet sharedCache = SharedStore.get<{\n\t\t\t[url: string]: {\n\t\t\t\tresult?: T;\n\t\t\t\terror?: unknown;\n\t\t\t\tpromiseQueue: {\n\t\t\t\t\trequestMethod: () => Promise<T>;\n\t\t\t\t\tresolve: (value: T | PromiseLike<T>) => void;\n\t\t\t\t\treject: (reason?: unknown) => void;\n\t\t\t\t}[];\n\t\t\t\texpires: number;\n\t\t\t};\n\t\t}>(\"asyncCache\");\n\n\t\tif (Is.undefined(sharedCache)) {\n\t\t\tsharedCache = {};\n\t\t\tSharedStore.set(\"asyncCache\", sharedCache);\n\t\t}\n\n\t\treturn sharedCache;\n\t}\n\n\t/**\n\t * Resolve a waiter by re-running its request method safely.\n\t * @param requestMethod The method to execute.\n\t * @param resolve The resolver for the waiter.\n\t * @param reject The rejector for the waiter.\n\t */\n\tprivate static async resolveWaiter<T>(\n\t\trequestMethod: () => Promise<T>,\n\t\tresolve: (value: T | PromiseLike<T>) => void,\n\t\treject: (reason?: unknown) => void\n\t): Promise<void> {\n\t\ttry {\n\t\t\tresolve(await requestMethod());\n\t\t} catch (waitErr) {\n\t\t\treject(waitErr);\n\t\t}\n\t}\n}\n"]}
@@ -39,4 +39,11 @@ export declare class AsyncCache {
39
39
  * Perform a cleanup of the expired entries in the cache.
40
40
  */
41
41
  static cleanupExpired(): void;
42
+ /**
43
+ * Resolve a waiter by re-running its request method safely.
44
+ * @param requestMethod The method to execute.
45
+ * @param resolve The resolver for the waiter.
46
+ * @param reject The rejector for the waiter.
47
+ */
48
+ private static resolveWaiter;
42
49
  }
package/docs/changelog.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.26](https://github.com/twinfoundation/framework/compare/core-v0.0.3-next.25...core-v0.0.3-next.26) (2026-03-24)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **core:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/nameof bumped from 0.0.3-next.25 to 0.0.3-next.26
16
+ * devDependencies
17
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.25 to 0.0.3-next.26
18
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.25 to 0.0.3-next.26
19
+
20
+ ## [0.0.3-next.25](https://github.com/twinfoundation/framework/compare/core-v0.0.3-next.24...core-v0.0.3-next.25) (2026-03-23)
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * improve exception handling in asyncCache ([c81b29b](https://github.com/twinfoundation/framework/commit/c81b29b660b152d2f0757d323430287e6491bf59))
26
+
27
+
28
+ ### Dependencies
29
+
30
+ * The following workspace dependencies were updated
31
+ * dependencies
32
+ * @twin.org/nameof bumped from 0.0.3-next.24 to 0.0.3-next.25
33
+ * devDependencies
34
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.24 to 0.0.3-next.25
35
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.24 to 0.0.3-next.25
36
+
3
37
  ## [0.0.3-next.24](https://github.com/twinfoundation/framework/compare/core-v0.0.3-next.23...core-v0.0.3-next.24) (2026-03-19)
4
38
 
5
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/core",
3
- "version": "0.0.3-next.24",
3
+ "version": "0.0.3-next.26",
4
4
  "description": "Helper methods/classes for data type checking/validation/guarding/error handling",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/nameof": "0.0.3-next.24",
17
+ "@twin.org/nameof": "0.0.3-next.26",
18
18
  "intl-messageformat": "11.1.3",
19
19
  "rfc6902": "5.2.0"
20
20
  },