viem
Version:
82 lines (68 loc) • 2.29 kB
text/typescript
import type { ErrorType } from '../../errors/utils.js'
/** @internal */
export const promiseCache = /*#__PURE__*/ new Map()
/** @internal */
export const responseCache = /*#__PURE__*/ new Map()
export type GetCacheErrorType = ErrorType
export function getCache<data>(cacheKey: string) {
const buildCache = <data>(cacheKey: string, cache: Map<string, data>) => ({
clear: () => cache.delete(cacheKey),
get: () => cache.get(cacheKey),
set: (data: data) => cache.set(cacheKey, data),
})
const promise = buildCache<Promise<data>>(cacheKey, promiseCache)
const response = buildCache<{ created: Date; data: data }>(
cacheKey,
responseCache,
)
return {
clear: () => {
promise.clear()
response.clear()
},
promise,
response,
}
}
type WithCacheParameters = {
/** The key to cache the data against. */
cacheKey: string
/** The time that cached data will remain in memory. Default: Infinity (no expiry) */
cacheTime?: number | undefined
}
/**
* @description Returns the result of a given promise, and caches the result for
* subsequent invocations against a provided cache key.
*/
export async function withCache<data>(
fn: () => Promise<data>,
{ cacheKey, cacheTime = Number.POSITIVE_INFINITY }: WithCacheParameters,
) {
const cache = getCache<data>(cacheKey)
// If a response exists in the cache, and it's not expired, return it
// and do not invoke the promise.
// If the max age is 0, the cache is disabled.
const response = cache.response.get()
if (response && cacheTime > 0) {
const age = new Date().getTime() - response.created.getTime()
if (age < cacheTime) return response.data
}
let promise = cache.promise.get()
if (!promise) {
promise = fn()
// Store the promise in the cache so that subsequent invocations
// will wait for the same promise to resolve (deduping).
cache.promise.set(promise)
}
try {
const data = await promise
// Store the response in the cache so that subsequent invocations
// will return the same response.
cache.response.set({ created: new Date(), data })
return data
} finally {
// Clear the promise cache so that subsequent invocations will
// invoke the promise again.
cache.promise.clear()
}
}