@twin.org/core 0.0.3-next.23 → 0.0.3-next.25

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.25](https://github.com/twinfoundation/framework/compare/core-v0.0.3-next.24...core-v0.0.3-next.25) (2026-03-23)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * improve exception handling in asyncCache ([c81b29b](https://github.com/twinfoundation/framework/commit/c81b29b660b152d2f0757d323430287e6491bf59))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/nameof bumped from 0.0.3-next.24 to 0.0.3-next.25
16
+ * devDependencies
17
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.24 to 0.0.3-next.25
18
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.24 to 0.0.3-next.25
19
+
20
+ ## [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)
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * ensure __decorate is defined for decorators ([103a563](https://github.com/twinfoundation/framework/commit/103a563ce01ebdef6240d2e590e7b026e8692684))
26
+
27
+
28
+ ### Dependencies
29
+
30
+ * The following workspace dependencies were updated
31
+ * dependencies
32
+ * @twin.org/nameof bumped from 0.0.3-next.23 to 0.0.3-next.24
33
+ * devDependencies
34
+ * @twin.org/nameof-transformer bumped from 0.0.3-next.23 to 0.0.3-next.24
35
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.3-next.23 to 0.0.3-next.24
36
+
3
37
  ## [0.0.3-next.23](https://github.com/twinfoundation/framework/compare/core-v0.0.3-next.22...core-v0.0.3-next.23) (2026-03-17)
4
38
 
5
39
 
@@ -64,7 +64,7 @@ Runtime name for the class.
64
64
 
65
65
  ### source? {#source}
66
66
 
67
- > `optional` **source**: `string`
67
+ > `optional` **source?**: `string`
68
68
 
69
69
  The source of the error.
70
70
 
@@ -76,7 +76,7 @@ The source of the error.
76
76
 
77
77
  ### properties? {#properties}
78
78
 
79
- > `optional` **properties**: `object`
79
+ > `optional` **properties?**: `object`
80
80
 
81
81
  Any additional information for the error.
82
82
 
@@ -92,7 +92,7 @@ Any additional information for the error.
92
92
 
93
93
  ### cause? {#cause}
94
94
 
95
- > `optional` **cause**: [`IError`](../interfaces/IError.md)
95
+ > `optional` **cause?**: [`IError`](../interfaces/IError.md)
96
96
 
97
97
  The cause of the error.
98
98
 
@@ -164,9 +164,9 @@ Expand an error tree.
164
164
 
165
165
  ##### errors
166
166
 
167
- The list of errors to expand.
167
+ [`IError`](../interfaces/IError.md)[] \| `undefined`
168
168
 
169
- [`IError`](../interfaces/IError.md)[] | `undefined`
169
+ The list of errors to expand.
170
170
 
171
171
  #### Returns
172
172
 
@@ -196,9 +196,9 @@ The error to test.
196
196
 
197
197
  ##### name
198
198
 
199
- The name to check for.
199
+ `string` \| `RegExp`
200
200
 
201
- `string` | `RegExp`
201
+ The name to check for.
202
202
 
203
203
  #### Returns
204
204
 
@@ -228,9 +228,9 @@ The error to test.
228
228
 
229
229
  ##### message
230
230
 
231
- The message to check for.
231
+ `string` \| `RegExp`
232
232
 
233
- `string` | `RegExp`
233
+ The message to check for.
234
234
 
235
235
  #### Returns
236
236
 
@@ -260,9 +260,9 @@ The error to test.
260
260
 
261
261
  ##### code
262
262
 
263
- The code to check for.
263
+ `string` \| `RegExp`
264
264
 
265
- `string` | `RegExp`
265
+ The code to check for.
266
266
 
267
267
  #### Returns
268
268
 
@@ -292,9 +292,9 @@ The error to test.
292
292
 
293
293
  ##### name
294
294
 
295
- The name to check for.
295
+ `string` \| `RegExp`
296
296
 
297
- `string` | `RegExp`
297
+ The name to check for.
298
298
 
299
299
  #### Returns
300
300
 
@@ -324,9 +324,9 @@ The error to test.
324
324
 
325
325
  ##### message
326
326
 
327
- The message to check for.
327
+ `string` \| `RegExp`
328
328
 
329
- `string` | `RegExp`
329
+ The message to check for.
330
330
 
331
331
  #### Returns
332
332
 
@@ -388,9 +388,9 @@ The error to test.
388
388
 
389
389
  ##### code
390
390
 
391
- The code to check for.
391
+ `string` \| `RegExp`
392
392
 
393
- `string` | `RegExp`
393
+ The code to check for.
394
394
 
395
395
  #### Returns
396
396
 
@@ -36,9 +36,9 @@ The key for the entry in the cache.
36
36
 
37
37
  ##### ttlMs
38
38
 
39
- The TTL of the entry in the cache.
39
+ `number` \| `undefined`
40
40
 
41
- `number` | `undefined`
41
+ The TTL of the entry in the cache.
42
42
 
43
43
  ##### requestMethod
44
44
 
@@ -73,7 +73,7 @@ The cause of error if we have wrapped another error.
73
73
 
74
74
  ### source? {#source}
75
75
 
76
- > `optional` **source**: `string`
76
+ > `optional` **source?**: `string`
77
77
 
78
78
  The source of the error.
79
79
 
@@ -85,7 +85,7 @@ The source of the error.
85
85
 
86
86
  ### properties? {#properties}
87
87
 
88
- > `optional` **properties**: `object`
88
+ > `optional` **properties?**: `object`
89
89
 
90
90
  Any additional information for the error.
91
91
 
@@ -101,7 +101,7 @@ Any additional information for the error.
101
101
 
102
102
  ### cause? {#cause}
103
103
 
104
- > `optional` **cause**: [`IError`](../interfaces/IError.md)
104
+ > `optional` **cause?**: [`IError`](../interfaces/IError.md)
105
105
 
106
106
  The cause of the error.
107
107
 
@@ -169,9 +169,9 @@ Expand an error tree.
169
169
 
170
170
  ##### errors
171
171
 
172
- The list of errors to expand.
172
+ [`IError`](../interfaces/IError.md)[] \| `undefined`
173
173
 
174
- [`IError`](../interfaces/IError.md)[] | `undefined`
174
+ The list of errors to expand.
175
175
 
176
176
  #### Returns
177
177
 
@@ -197,9 +197,9 @@ The error to test.
197
197
 
198
198
  ##### name
199
199
 
200
- The name to check for.
200
+ `string` \| `RegExp`
201
201
 
202
- `string` | `RegExp`
202
+ The name to check for.
203
203
 
204
204
  #### Returns
205
205
 
@@ -225,9 +225,9 @@ The error to test.
225
225
 
226
226
  ##### message
227
227
 
228
- The message to check for.
228
+ `string` \| `RegExp`
229
229
 
230
- `string` | `RegExp`
230
+ The message to check for.
231
231
 
232
232
  #### Returns
233
233
 
@@ -253,9 +253,9 @@ The error to test.
253
253
 
254
254
  ##### code
255
255
 
256
- The code to check for.
256
+ `string` \| `RegExp`
257
257
 
258
- `string` | `RegExp`
258
+ The code to check for.
259
259
 
260
260
  #### Returns
261
261
 
@@ -281,9 +281,9 @@ The error to test.
281
281
 
282
282
  ##### name
283
283
 
284
- The name to check for.
284
+ `string` \| `RegExp`
285
285
 
286
- `string` | `RegExp`
286
+ The name to check for.
287
287
 
288
288
  #### Returns
289
289
 
@@ -309,9 +309,9 @@ The error to test.
309
309
 
310
310
  ##### message
311
311
 
312
- The message to check for.
312
+ `string` \| `RegExp`
313
313
 
314
- `string` | `RegExp`
314
+ The message to check for.
315
315
 
316
316
  #### Returns
317
317
 
@@ -365,9 +365,9 @@ The error to test.
365
365
 
366
366
  ##### code
367
367
 
368
- The code to check for.
368
+ `string` \| `RegExp`
369
369
 
370
- `string` | `RegExp`
370
+ The code to check for.
371
371
 
372
372
  #### Returns
373
373
 
@@ -62,7 +62,7 @@ The cause or the error if we have wrapped another error.
62
62
 
63
63
  ### source? {#source}
64
64
 
65
- > `optional` **source**: `string`
65
+ > `optional` **source?**: `string`
66
66
 
67
67
  The source of the error.
68
68
 
@@ -74,7 +74,7 @@ The source of the error.
74
74
 
75
75
  ### properties? {#properties}
76
76
 
77
- > `optional` **properties**: `object`
77
+ > `optional` **properties?**: `object`
78
78
 
79
79
  Any additional information for the error.
80
80
 
@@ -90,7 +90,7 @@ Any additional information for the error.
90
90
 
91
91
  ### cause? {#cause}
92
92
 
93
- > `optional` **cause**: [`IError`](../interfaces/IError.md)
93
+ > `optional` **cause?**: [`IError`](../interfaces/IError.md)
94
94
 
95
95
  The cause of the error.
96
96
 
@@ -170,9 +170,9 @@ Expand an error tree.
170
170
 
171
171
  ##### errors
172
172
 
173
- The list of errors to expand.
173
+ [`IError`](../interfaces/IError.md)[] \| `undefined`
174
174
 
175
- [`IError`](../interfaces/IError.md)[] | `undefined`
175
+ The list of errors to expand.
176
176
 
177
177
  #### Returns
178
178
 
@@ -202,9 +202,9 @@ The error to test.
202
202
 
203
203
  ##### name
204
204
 
205
- The name to check for.
205
+ `string` \| `RegExp`
206
206
 
207
- `string` | `RegExp`
207
+ The name to check for.
208
208
 
209
209
  #### Returns
210
210
 
@@ -234,9 +234,9 @@ The error to test.
234
234
 
235
235
  ##### message
236
236
 
237
- The message to check for.
237
+ `string` \| `RegExp`
238
238
 
239
- `string` | `RegExp`
239
+ The message to check for.
240
240
 
241
241
  #### Returns
242
242
 
@@ -266,9 +266,9 @@ The error to test.
266
266
 
267
267
  ##### code
268
268
 
269
- The code to check for.
269
+ `string` \| `RegExp`
270
270
 
271
- `string` | `RegExp`
271
+ The code to check for.
272
272
 
273
273
  #### Returns
274
274
 
@@ -298,9 +298,9 @@ The error to test.
298
298
 
299
299
  ##### name
300
300
 
301
- The name to check for.
301
+ `string` \| `RegExp`
302
302
 
303
- `string` | `RegExp`
303
+ The name to check for.
304
304
 
305
305
  #### Returns
306
306
 
@@ -330,9 +330,9 @@ The error to test.
330
330
 
331
331
  ##### message
332
332
 
333
- The message to check for.
333
+ `string` \| `RegExp`
334
334
 
335
- `string` | `RegExp`
335
+ The message to check for.
336
336
 
337
337
  #### Returns
338
338
 
@@ -394,9 +394,9 @@ The error to test.
394
394
 
395
395
  ##### code
396
396
 
397
- The code to check for.
397
+ `string` \| `RegExp`
398
398
 
399
- `string` | `RegExp`
399
+ The code to check for.
400
400
 
401
401
  #### Returns
402
402