import { ICacheService } from "@medusajs/framework/types" import { CacheRecord, InMemoryCacheModuleOptions } from "../types" const DEFAULT_TTL = 30 // seconds type InjectedDependencies = {} /** * Class represents basic, in-memory, cache store. */ class InMemoryCacheService implements ICacheService { protected readonly TTL: number protected readonly store = new Map>() protected readonly timoutRefs = new Map() constructor( deps: InjectedDependencies, options: InMemoryCacheModuleOptions = {} ) { this.TTL = options.ttl ?? DEFAULT_TTL } /** * Retrieve data from the cache. * @param key - cache key */ async get(key: string): Promise { const now = Date.now() const record: CacheRecord | undefined = this.store.get(key) const recordExpire = record?.expire ?? Infinity if (!record || recordExpire < now) { return null } return record.data } /** * Set data to the cache. * @param key - cache key under which the data is stored * @param data - data to be stored in the cache * @param ttl - expiration time in seconds */ async set(key: string, data: T, ttl: number = this.TTL): Promise { if (ttl === 0) { return } const record: CacheRecord = { data, expire: ttl * 1000 + Date.now() } const oldRecord = this.store.get(key) if (oldRecord) { clearTimeout(this.timoutRefs.get(key)) this.timoutRefs.delete(key) } const ref = setTimeout(async () => { await this.invalidate(key) }, ttl * 1000) ref.unref() this.timoutRefs.set(key, ref) this.store.set(key, record) } /** * Delete data from the cache. * Could use wildcard (*) matcher e.g. `invalidate("ps:*")` to delete all keys that start with "ps:" * * @param key - cache key */ async invalidate(key: string): Promise { let keys = [key] if (key.includes("*")) { const regExp = new RegExp(key.replace("*", ".*")) keys = Array.from(this.store.keys()).filter((k) => k.match(regExp)) } keys.forEach((key) => { const timeoutRef = this.timoutRefs.get(key) if (timeoutRef) { clearTimeout(timeoutRef) this.timoutRefs.delete(key) } this.store.delete(key) }) } /** * Delete the entire cache. */ async clear() { this.timoutRefs.forEach((ref) => clearTimeout(ref)) this.timoutRefs.clear() this.store.clear() } } export default InMemoryCacheService