Loading cell…
Loading cell…
Write a TypeScript function named createIdempotencyGuard.
Requirements:
key, ttlMs, and an async handler function.Output format: { "code": "...", "explanation": "...", "complexity": { "time": "...", "space": "..." }, "tests": ["...", "...", "...", "...", "..."] }
{
"code": "type IdempotencyGuardOptions = {\n key: string;\n ttlMs: number;\n handler: () => Promise;\n};\n\ninterface CacheEntry {\n result: T;\n expiresAt: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\nconst inFlight = new Map<string, Promise>();\n\nfunction createIdempotencyGuard({\n key,\n ttlMs,\n handler,\n}: IdempotencyGuardOptions): Promise {\n // Check cache first\n const cached = cache.get(key) as CacheEntry | undefined;\n if (cached && cached.expiresAt > Date.now()) {\n return Promise.resolve(cached.result);\n }\n\n // Check if already in-flight\n const existing = inFlight.get(key) as Promise | undefined;\n if (existing) {\n return existing;\n }\n\n // Run handler and track in-flight promise\n const promise = handler().then((result) => {\n cache.set(key, { result, expiresAt: Date.now() + ttlMs });\n return result;\n }).finally(() => {\n inFlight.delete(key);\n });\n\n inFlight.set(key, promise);\n return promise;\n}",
"explanation": "The function uses two Maps: cache stores successful results with expiration timestamps, and inFlight tracks ongoing handler executions. When called, it first checks the cache for a valid (non-expired) result and returns it immediately if found. If no cached result exists, it checks whether the same key is currently being processed (in-flight). If so, it returns the existing Promise to avoid duplicate execution. Otherwise, it runs the handler, caches the result on success (with TTL), and removes the in-flight entry in a finally block. On handler failure, the result is not cached, allowing future retries. The in-flight entry is always cleaned up via finally, ensuring subsequent calls can retry after errors.",
"complexity": {
"time": "O(1) for cache lookup, in-flight check, and map operations (Map.get/set/delete are O(1) average). Handler execution time is external and not counted.",
"space": "O(n) where n is the number of unique keys currently cached or in-flight. Each cache entry stores the result and a timestamp; in-flight entries store a Promise reference."
},
"tests": [
"Concurrency: Call createIdempotencyGuard twice with the same key simultaneously and verify handler is invoked only once, with both calls receiving the same result.",
"Caching: Call createIdempotencyGuard with a key, then immediately call again with the same key and verify the cached result is returned without invoking handler a second time.",
"Expiry: Call createIdempotencyGuard with a short ttlMs, wait for TTL to elapse, then call again and verify handler runs again and a new result is fetched.",
"Error handling: Call createIdempotencyGuard with a handler that throws, verify the rejection propagates, then call again and confirm handler is retried (not cached).",
"Different keys: Call createIdempotencyGuard with two different keys concurrently and verify both handlers run independently and return their respective results."
]
}
8/8 checks passed