MCPcopy
hub / github.com/codeaashu/claude-code / memoizeWithTTL

Function memoizeWithTTL

src/utils/memoize.ts:40–107  ·  view source on GitHub ↗
(
  f: (...args: Args) => Result,
  cacheLifetimeMs: number = 5 * 60 * 1000, // Default 5 minutes
)

Source from the content-addressed store, hash-verified

38 * @returns A memoized version of the function
39 */
40export function memoizeWithTTL<Args extends unknown[], Result>(
41 f: (...args: Args) => Result,
42 cacheLifetimeMs: number = 5 * 60 * 1000, // Default 5 minutes
43): MemoizedFunction<Args, Result> {
44 const cache = new Map<string, CacheEntry<Result>>()
45
46 const memoized = (...args: Args): Result => {
47 const key = jsonStringify(args)
48 const cached = cache.get(key)
49 const now = Date.now()
50
51 // Populate cache
52 if (!cached) {
53 const value = f(...args)
54 cache.set(key, {
55 value,
56 timestamp: now,
57 refreshing: false,
58 })
59 return value
60 }
61
62 // If we have a stale cache entry and it's not already refreshing
63 if (
64 cached &&
65 now - cached.timestamp > cacheLifetimeMs &&
66 !cached.refreshing
67 ) {
68 // Mark as refreshing to prevent multiple parallel refreshes
69 cached.refreshing = true
70
71 // Schedule async refresh (non-blocking). Both .then and .catch are
72 // identity-guarded: a concurrent cache.clear() + cold-miss stores a
73 // newer entry while this microtask is queued. .then overwriting with
74 // the stale refresh's result is worse than .catch deleting (persists
75 // wrong data for full TTL vs. self-correcting on next call).
76 Promise.resolve()
77 .then(() => {
78 const newValue = f(...args)
79 if (cache.get(key) === cached) {
80 cache.set(key, {
81 value: newValue,
82 timestamp: Date.now(),
83 refreshing: false,
84 })
85 }
86 })
87 .catch(e => {
88 logError(e)
89 if (cache.get(key) === cached) {
90 cache.delete(key)
91 }
92 })
93
94 // Return the stale value immediately
95 return cached.value
96 }
97

Callers

nothing calls this directly

Calls 1

clearMethod · 0.45

Tested by

no test coverage detected