{"version":3,"sources":["../../src/plugins/cache.ts"],"sourcesContent":["import { CacheConfig, CacheEntry, Plugin } from \"../types\";\nimport { RequestHandler } from \"express\";\n\nconst store = new Map<string, CacheEntry>();\n\nconst CACHE_KEY = Symbol(\"cache-key\");\nconst CACHE_BODY = Symbol(\"cache-body\");\n\nconst cachePlugin: Plugin<CacheConfig> = {\n    name: \"cache\",\n\n    validateConfig(config) {\n        if (!config || typeof config.ttl !== \"number\" || config.ttl <= 0) {\n            throw new Error(`[cache] \"ttl\" must be a positive number`);\n        }\n\n        if (config.max !== undefined && (typeof config.max !== \"number\" || config.max <= 0)) {\n            throw new Error(`[cache] \"max\" must be a positive number`);\n        }\n    },\n\n    async onRequest(ctx, next) {\n        const key = `${ctx.req.method}:${ctx.req.originalUrl}`;\n        const entry = store.get(key);\n\n        if (entry && entry.expires > Date.now()) {\n            store.delete(key);\n            store.set(key, entry);\n\n            ctx.res.status(entry.status);\n            ctx.res.send(entry.body);\n            return;\n        }\n\n        ctx.state[CACHE_KEY] = key;\n        return next();\n    },\n\n    async onResponse(ctx) {\n        const key = ctx.state[CACHE_KEY];\n        if (!key) return;\n\n        const ttl = ctx.config?.ttl ?? 0;\n        if (!ttl) return;\n\n        const body = (ctx.req as any)[CACHE_BODY];\n        if (body === undefined) return;\n\n        const entry: CacheEntry = {\n            expires: Date.now() + ttl,\n            status: ctx.res.statusCode,\n            body\n        };\n\n        if (store.has(key)) {\n            store.delete(key);\n        }\n\n        store.set(key, entry);\n\n        const max = ctx.config?.max ?? 500;\n        if (max && store.size > max) {\n            const oldestKey = store.keys().next().value;\n            if (oldestKey) store.delete(oldestKey);\n        }\n    },\n\n    wrap(handler: RequestHandler): RequestHandler {\n        return async (req, res, next) => {\n            let body: any;\n\n            const originalSend = res.send.bind(res);\n\n            res.send = (data: any) => {\n                body = data;\n                return originalSend(data);\n            };\n\n            try {\n                await handler(req, res, next);\n            } finally {\n                (req as any)[CACHE_BODY] = body;\n            }\n        };\n    }\n};\n\nexport default cachePlugin;"],"mappings":";;;;;;;;;;;;;;;;;;;;AAGA,MAAM,QAAQ,oBAAI,IAAwB;AAE1C,MAAM,YAAY,OAAO,WAAW;AACpC,MAAM,aAAa,OAAO,YAAY;AAEtC,MAAM,cAAmC;AAAA,EACrC,MAAM;AAAA,EAEN,eAAe,QAAQ;AACnB,QAAI,CAAC,UAAU,OAAO,OAAO,QAAQ,YAAY,OAAO,OAAO,GAAG;AAC9D,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC7D;AAEA,QAAI,OAAO,QAAQ,WAAc,OAAO,OAAO,QAAQ,YAAY,OAAO,OAAO,IAAI;AACjF,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC7D;AAAA,EACJ;AAAA,EAEM,UAAU,KAAK,MAAM;AAAA;AACvB,YAAM,MAAM,GAAG,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,WAAW;AACpD,YAAM,QAAQ,MAAM,IAAI,GAAG;AAE3B,UAAI,SAAS,MAAM,UAAU,KAAK,IAAI,GAAG;AACrC,cAAM,OAAO,GAAG;AAChB,cAAM,IAAI,KAAK,KAAK;AAEpB,YAAI,IAAI,OAAO,MAAM,MAAM;AAC3B,YAAI,IAAI,KAAK,MAAM,IAAI;AACvB;AAAA,MACJ;AAEA,UAAI,MAAM,SAAS,IAAI;AACvB,aAAO,KAAK;AAAA,IAChB;AAAA;AAAA,EAEM,WAAW,KAAK;AAAA;AAtC1B;AAuCQ,YAAM,MAAM,IAAI,MAAM,SAAS;AAC/B,UAAI,CAAC,IAAK;AAEV,YAAM,OAAM,eAAI,WAAJ,mBAAY,QAAZ,YAAmB;AAC/B,UAAI,CAAC,IAAK;AAEV,YAAM,OAAQ,IAAI,IAAY,UAAU;AACxC,UAAI,SAAS,OAAW;AAExB,YAAM,QAAoB;AAAA,QACtB,SAAS,KAAK,IAAI,IAAI;AAAA,QACtB,QAAQ,IAAI,IAAI;AAAA,QAChB;AAAA,MACJ;AAEA,UAAI,MAAM,IAAI,GAAG,GAAG;AAChB,cAAM,OAAO,GAAG;AAAA,MACpB;AAEA,YAAM,IAAI,KAAK,KAAK;AAEpB,YAAM,OAAM,eAAI,WAAJ,mBAAY,QAAZ,YAAmB;AAC/B,UAAI,OAAO,MAAM,OAAO,KAAK;AACzB,cAAM,YAAY,MAAM,KAAK,EAAE,KAAK,EAAE;AACtC,YAAI,UAAW,OAAM,OAAO,SAAS;AAAA,MACzC;AAAA,IACJ;AAAA;AAAA,EAEA,KAAK,SAAyC;AAC1C,WAAO,CAAO,KAAK,KAAK,SAAS;AAC7B,UAAI;AAEJ,YAAM,eAAe,IAAI,KAAK,KAAK,GAAG;AAEtC,UAAI,OAAO,CAAC,SAAc;AACtB,eAAO;AACP,eAAO,aAAa,IAAI;AAAA,MAC5B;AAEA,UAAI;AACA,cAAM,QAAQ,KAAK,KAAK,IAAI;AAAA,MAChC,UAAE;AACE,QAAC,IAAY,UAAU,IAAI;AAAA,MAC/B;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,IAAO,gBAAQ;","names":[]}