{"version":3,"sources":["../../../../../src/cache/adapters/in-memory/index.ts"],"sourcesContent":["import { v } from 'valleyed'\n\nimport { Instance } from '../../../instance'\nimport { configurable } from '../../../utilities'\nimport { Cache } from '../base'\n\ntype CacheEntry = {\n\tdata: string\n\texpiredAt?: number\n}\n\nexport const inMemoryCacheConfigPipe = () => v.object({})\n\nexport class InMemoryCache extends configurable(inMemoryCacheConfigPipe, Cache) {\n\t#caches = new Map<string, CacheEntry>()\n\t#interval?: ReturnType<typeof setInterval>\n\n\tprotected constructor(config: typeof InMemoryCache.Config) {\n\t\tsuper(config)\n\t\tInstance.on('start', async () => {\n\t\t\tthis.#interval = setInterval(() => {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tfor (const [key, record] of this.#caches.entries()) {\n\t\t\t\t\tif (record.expiredAt && record.expiredAt <= now) this.#caches.delete(key)\n\t\t\t\t}\n\t\t\t}, 5000)\n\t\t}, { class: InMemoryCache })\n\t\tInstance.on('close', async () => clearInterval(this.#interval), { class: InMemoryCache })\n\t}\n\n\tasync delete(key: string) {\n\t\tthis.#caches.delete(this.getScopedKey(key))\n\t}\n\tasync get(key: string) {\n\t\tconst record = this.#caches.get(this.getScopedKey(key))\n\t\tif (record && (record.expiredAt === undefined || record.expiredAt > Date.now())) return record.data\n\t\treturn null\n\t}\n\tasync set(key: string, data: string, ttlInSecs?: number) {\n\t\tthis.#caches.set(this.getScopedKey(key), { data, expiredAt: ttlInSecs ? Date.now() + ttlInSecs * 1000 : undefined })\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf, vi, beforeEach, afterEach } = import.meta.vitest\n\n\tlet hookCalls: Array<{ event: string; options?: any }>\n\n\tbeforeEach(() => {\n\t\thookCalls = []\n\t\tvi.spyOn(Instance, 'on').mockImplementation((event, _cb, options) => {\n\t\t\thookCalls.push({ event, options })\n\t\t})\n\t\tvi.spyOn(Instance, 'get').mockReturnValue({ getScopedName: (_name: string, _sep: string) => `test:${_name}` } as any)\n\t})\n\n\tafterEach(() => {\n\t\tvi.restoreAllMocks()\n\t})\n\n\tdescribe('InMemoryCache', () => {\n\t\ttest('create() produces a working instance', () => {\n\t\t\tconst cache = InMemoryCache.create({})\n\t\t\texpect(cache).toBeInstanceOf(InMemoryCache)\n\t\t})\n\n\t\ttest('new InMemoryCache() is a compile error', () => {\n\t\t\t// @ts-expect-error — external `new` on a class with protected constructor is a compile error\n\t\t\tvoid (() => new InMemoryCache({}))\n\t\t})\n\n\t\ttest('Instance.on calls use { class: InMemoryCache }', () => {\n\t\t\tInMemoryCache.create({})\n\t\t\tconst startHook = hookCalls.find((h) => h.event === 'start')\n\t\t\tconst closeHook = hookCalls.find((h) => h.event === 'close')\n\t\t\texpect(startHook?.options).toEqual({ class: InMemoryCache })\n\t\t\texpect(closeHook?.options).toEqual({ class: InMemoryCache })\n\t\t})\n\n\t\ttest('set/get/delete work correctly', async () => {\n\t\t\tconst cache = InMemoryCache.create({})\n\t\t\tawait cache.set('key1', 'value1')\n\t\t\texpect(await cache.get('key1')).toBe('value1')\n\t\t\tawait cache.delete('key1')\n\t\t\texpect(await cache.get('key1')).toBeNull()\n\t\t})\n\n\t\ttest('get returns null after ttl expires', async () => {\n\t\t\tconst now = Date.now()\n\t\t\tvi.spyOn(Date, 'now').mockReturnValue(now)\n\t\t\tconst cache = InMemoryCache.create({})\n\t\t\tawait cache.set('key1', 'value1', 1)\n\t\t\tvi.spyOn(Date, 'now').mockReturnValue(now + 1001)\n\t\t\texpect(await cache.get('key1')).toBeNull()\n\t\t})\n\n\t\ttest('Config type is accessible', () => {\n\t\t\ttype PipeOutput<T> = import('valleyed').PipeOutput<T>\n\t\t\ttype Expected = PipeOutput<ReturnType<typeof inMemoryCacheConfigPipe>>\n\t\t\texpectTypeOf<typeof InMemoryCache.Config>().toEqualTypeOf<Expected>()\n\t\t})\n\t})\n}\n"],"mappings":"oRAAA,OAAS,KAAAA,MAAS,WAWX,IAAMC,EAA0B,IAAMC,EAAE,OAAO,CAAC,CAAC,EAE3CC,EAAN,MAAMC,UAAsBC,EAAaJ,EAAyBK,CAAK,CAAE,CAC/EC,GAAU,IAAI,IACdC,GAEU,YAAYC,EAAqC,CAC1D,MAAMA,CAAM,EACZC,EAAS,GAAG,QAAS,SAAY,CAChC,KAAKF,GAAY,YAAY,IAAM,CAClC,IAAMG,EAAM,KAAK,IAAI,EACrB,OAAW,CAACC,EAAKC,CAAM,IAAK,KAAKN,GAAQ,QAAQ,EAC5CM,EAAO,WAAaA,EAAO,WAAaF,GAAK,KAAKJ,GAAQ,OAAOK,CAAG,CAE1E,EAAG,GAAI,CACR,EAAG,CAAE,MAAOR,CAAc,CAAC,EAC3BM,EAAS,GAAG,QAAS,SAAY,cAAc,KAAKF,EAAS,EAAG,CAAE,MAAOJ,CAAc,CAAC,CACzF,CAEA,MAAM,OAAOQ,EAAa,CACzB,KAAKL,GAAQ,OAAO,KAAK,aAAaK,CAAG,CAAC,CAC3C,CACA,MAAM,IAAIA,EAAa,CACtB,IAAMC,EAAS,KAAKN,GAAQ,IAAI,KAAK,aAAaK,CAAG,CAAC,EACtD,OAAIC,IAAWA,EAAO,YAAc,QAAaA,EAAO,UAAY,KAAK,IAAI,GAAWA,EAAO,KACxF,IACR,CACA,MAAM,IAAID,EAAaE,EAAcC,EAAoB,CACxD,KAAKR,GAAQ,IAAI,KAAK,aAAaK,CAAG,EAAG,CAAE,KAAAE,EAAM,UAAWC,EAAY,KAAK,IAAI,EAAIA,EAAY,IAAO,MAAU,CAAC,CACpH,CACD","names":["v","inMemoryCacheConfigPipe","v","InMemoryCache","_InMemoryCache","configurable","Cache","#caches","#interval","config","Instance","now","key","record","data","ttlInSecs"]}