@lowerdeck/cache 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # `@lowerdeck/cache`
2
+
3
+ Multi-layer caching with Redis and in-memory LRU cache. Supports tag-based invalidation, dynamic TTL, and local-only caching for optimal performance.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @lowerdeck/cache
9
+ yarn add @lowerdeck/cache
10
+ bun add @lowerdeck/cache
11
+ pnpm add @lowerdeck/cache
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ### Redis-backed cache
17
+
18
+ ```typescript
19
+ import { createCachedFunction } from '@lowerdeck/cache';
20
+
21
+ const getUserProfile = createCachedFunction({
22
+ name: 'user-profile',
23
+ redisUrl: 'redis://localhost:6379',
24
+ getHash: (userId: string) => userId,
25
+ provider: async (userId, { setTTL }) => {
26
+ const user = await database.getUser(userId);
27
+
28
+ // Dynamically adjust TTL based on result
29
+ if (user.isPremium) {
30
+ setTTL(3600); // 1 hour for premium users
31
+ }
32
+
33
+ return user;
34
+ },
35
+ ttlSeconds: 300, // default 5 minutes
36
+ getTags: (user, userId) => [`user:${userId}`, `role:${user.role}`]
37
+ });
38
+
39
+ // Fetch user (checks in-memory cache -> Redis -> provider)
40
+ const user = await getUserProfile('123');
41
+
42
+ // Clear specific user cache
43
+ await getUserProfile.clear('123');
44
+
45
+ // Clear all users with a specific role
46
+ await getUserProfile.clearByTag('role:admin');
47
+
48
+ // Wait for cache clear to complete
49
+ await getUserProfile.clearAndWait('123');
50
+ await getUserProfile.clearByTagAndWait('role:admin');
51
+ ```
52
+
53
+ ### Local-only cache
54
+
55
+ ```typescript
56
+ import { createLocallyCachedFunction } from '@lowerdeck/cache';
57
+
58
+ const computeHash = createLocallyCachedFunction({
59
+ getHash: (input: string) => input,
60
+ provider: async (input) => {
61
+ return expensiveHashComputation(input);
62
+ },
63
+ ttlSeconds: 60
64
+ });
65
+
66
+ // First call computes the result
67
+ const hash1 = await computeHash('data');
68
+
69
+ // Second call returns cached result
70
+ const hash2 = await computeHash('data');
71
+
72
+ // Clear cache for specific input
73
+ await computeHash.clear('data');
74
+ await computeHash.clearAndWait('data');
75
+ ```
76
+
77
+ ## License
78
+
79
+ This project is licensed under the Apache License 2.0.
80
+
81
+ <div align="center">
82
+ <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
83
+ </div>
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ var e=require("@lowerdeck/redis"),r=require("@lowerdeck/sentry"),t=require("lru-cache");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=/*#__PURE__*/n(require("superjson")),i=r.getSentry(),u="undefined"!=typeof Symbol?Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator")):"@@iterator";function c(e,r,t){if(!e.s){if(t instanceof s){if(!t.s)return void(t.o=c.bind(null,e,r));1&r&&(r=t.s),t=t.v}if(t&&t.then)return void t.then(c.bind(null,e,r),c.bind(null,e,2));e.s=r,e.v=t;var n=e.o;n&&n(e)}}var s=/*#__PURE__*/function(){function e(){}return e.prototype.then=function(r,t){var n=new e,o=this.s;if(o){var i=1&o?r:t;if(i){try{c(n,1,i(this.v))}catch(e){c(n,2,e)}return n}return this}return this.o=function(e){try{var o=e.v;1&e.s?c(n,1,r?r(o):o):t?c(n,1,t(o)):c(n,2,o)}catch(e){c(n,2,e)}},n},e}();function a(e){return e instanceof s&&1&e.s}var f=new t.LRUCache({max:500,ttl:15});function l(e,r,t){if("function"==typeof e[u]){var n,o,i,f=function(e){try{for(;!((n=l.next()).done||t&&t());)if((e=r(n.value))&&e.then){if(!a(e))return void e.then(f,i||(i=c.bind(null,o=new s,2)));e=e.v}o?c(o,1,e):o=e}catch(e){c(o||(o=new s),2,e)}},l=e[u]();if(f(),l.return){var h=function(e){try{n.done||l.return()}catch(e){}return e};if(o&&o.then)return o.then(h,function(e){throw h(e)});h()}return o}if(!("length"in e))throw new TypeError("Object is not iterable");for(var v=[],m=0;m<e.length;m++)v.push(e[m]);return function(e,r,t){var n,o,i=-1;return function u(f){try{for(;++i<e.length&&(!t||!t());)if((f=r(i))&&f.then){if(!a(f))return void f.then(u,o||(o=c.bind(null,n=new s,2)));f=f.v}n?c(n,1,f):n=f}catch(e){c(n||(n=new s),2,e)}}(),n}(v,function(e){return r(v[e])},t)}exports.createCachedFunction=function(r){var t=new Map,n=e.createRedisClient({redisUrl:r.redisUrl}).lazy(),u=function(e){return"cache:0:"+r.name+":val:"+r.getHash(e)},c=function(e){return e.map(function(e){return"cache:0:tag:"+e})},s=function(e){try{var t=u(e);return Promise.resolve(n()).then(function(n){return Promise.resolve(n.get(t)).then(function(u){if(u)return Promise.resolve(n.del(t)).then(function(){return f.delete(t),function(){if(r.getTags){var s=r.getTags(o.default.parse(u),e);return l(c(s),function(e){var r=function(r,o){try{var i=Promise.resolve(n.sRem(e,t)).then(function(){})}catch(e){return o(e)}return i&&i.then?i.then(void 0,o):i}(0,function(e){console.error("Error removing tag:",e),i.captureException(e)});if(r&&r.then)return r.then(function(){})})}}()})})})}catch(e){return Promise.reject(e)}},a=function(e){try{return Promise.resolve(n()).then(function(r){var t=c([e])[0];return Promise.resolve(r.sMembers(t)).then(function(e){function n(){return Promise.resolve(r.del(t)).then(function(){})}var o=l(e,function(e){return Promise.resolve(r.del(e)).then(function(){f.delete(e)})});return o&&o.then?o.then(n):n()})})}catch(e){return Promise.reject(e)}};return Object.assign(function(e){try{var i,s=function(u){if(i)return u;if(f.has(a))return f.get(a);var s=function(){try{return Promise.resolve(n()).then(function(t){return Promise.resolve(t.get(a)).then(function(n){if(n)return o.default.parse(n);var i={current:r.ttlSeconds};return Promise.resolve(r.provider(e,{setTTL:function(e){return i.current=e}})).then(function(n){return Promise.resolve(t.set(a,o.default.stringify(n),{EX:i.current})).then(function(){if(r.getTags){var o=r.getTags(n,e),u=c(o);Promise.all(u.map(function(e){try{return Promise.resolve(t.sAdd(e,a)).then(function(){return Promise.resolve(t.expire(e,i.current)).then(function(){})})}catch(e){return Promise.reject(e)}})).catch(console.error)}return f.set(a,n),n})})})})}catch(e){return Promise.reject(e)}}();return t.set(a,s),s.finally(function(){return t.delete(a)}),Promise.resolve(s)},a=u(e),l=function(){if(t.has(a))return Promise.resolve(t.get(a)).then(function(e){return i=1,e})}();return Promise.resolve(l&&l.then?l.then(s):s(l))}catch(e){return Promise.reject(e)}},{clear:function(e){try{return s(e).catch(function(e){console.error(e),i.captureException(e)}),Promise.resolve()}catch(e){return Promise.reject(e)}},clearByTag:function(e){try{return a(e).catch(function(e){console.error(e),i.captureException(e)}),Promise.resolve()}catch(e){return Promise.reject(e)}},clearAndWait:s,clearByTagAndWait:a})},exports.createLocallyCachedFunction=function(e){var r=new t.LRUCache({max:500,ttl:e.ttlSeconds}),n=new Map,o=function(r){return e.getHash(r)},u=function(e){try{var t=o(e);return r.delete(t),Promise.resolve()}catch(e){return Promise.reject(e)}};return Object.assign(function(t){try{var i,u=function(o){if(i)return o;if(r.has(c))return r.get(c);var u=function(){try{return Promise.resolve(e.provider(t)).then(function(e){return r.set(c,e),e})}catch(e){return Promise.reject(e)}}();return n.set(c,u),u.finally(function(){return n.delete(c)}),Promise.resolve(u)},c=o(t),s=function(){if(n.has(c))return Promise.resolve(n.get(c)).then(function(e){return i=1,e})}();return Promise.resolve(s&&s.then?s.then(u):u(s))}catch(e){return Promise.reject(e)}},{clear:function(e){try{return u(e).catch(function(e){console.error(e),i.captureException(e)}),Promise.resolve()}catch(e){return Promise.reject(e)}},clearAndWait:u})};
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import { createRedisClient } from '@lowerdeck/redis';\nimport { getSentry } from '@lowerdeck/sentry';\nimport { LRUCache } from 'lru-cache';\n\n// @ts-ignore\nimport SuperJson from 'superjson';\n\nlet Sentry = getSentry();\n\nlet cache = new LRUCache<string, any>({ max: 500, ttl: 15 });\n\nlet version = 0;\n\nexport let createCachedFunction = <I, O>(opts: {\n name: string;\n redisUrl: string;\n getHash: (i: I) => string;\n provider: (i: I, opts: { setTTL: (ttl: number) => void }) => Promise<O>;\n ttlSeconds: number;\n getTags?: (o: O, i: I) => string[];\n}) => {\n let active = new Map<string, Promise<O>>();\n\n let useRedisClient = createRedisClient({ redisUrl: opts.redisUrl }).lazy();\n\n let getHash = (i: I) => `cache:${version}:${opts.name}:val:${opts.getHash(i)}`;\n let getTagKeys = (tags: string[]) => tags.map(tag => `cache:${version}:tag:${tag}`);\n\n let run = async (i: I): Promise<O> => {\n let hash = getHash(i);\n if (active.has(hash)) return await active.get(hash)!;\n if (cache.has(hash)) return cache.get(hash)!;\n\n let promise = (async () => {\n let redis = await useRedisClient();\n let value = await redis.get(hash);\n if (value) return SuperJson.parse(value);\n\n let ttl = { current: opts.ttlSeconds };\n let setTTL = (newTTL: number) => (ttl.current = newTTL);\n\n let result = await opts.provider(i, { setTTL });\n await redis.set(hash, SuperJson.stringify(result), {\n EX: ttl.current\n });\n\n if (opts.getTags) {\n let tags = opts.getTags(result, i);\n let tagKeys = getTagKeys(tags);\n Promise.all(\n tagKeys.map(async tagKey => {\n await redis.sAdd(tagKey, hash);\n await redis.expire(tagKey, ttl.current);\n })\n ).catch(console.error);\n }\n\n cache.set(hash, result);\n\n return result as any;\n })();\n\n active.set(hash, promise);\n promise.finally(() => active.delete(hash));\n\n return await promise;\n };\n\n let clearAndWait = async (i: I) => {\n let hash = getHash(i);\n let redis = await useRedisClient();\n\n let data = await redis.get(hash);\n if (!data) return;\n\n await redis.del(hash);\n cache.delete(hash);\n\n if (opts.getTags) {\n let tags = opts.getTags(SuperJson.parse(data), i);\n let tagKeys = getTagKeys(tags);\n for (let tagKey of tagKeys) {\n try {\n await redis.sRem(tagKey, hash);\n } catch (e) {\n console.error('Error removing tag:', e);\n Sentry.captureException(e);\n }\n }\n }\n };\n\n let clearByTagAndWait = async (tag: string) => {\n let redis = await useRedisClient();\n let tagKey = getTagKeys([tag])[0];\n\n let hashes = await redis.sMembers(tagKey);\n\n for (let hash of hashes) {\n await redis.del(hash);\n cache.delete(hash);\n }\n\n await redis.del(tagKey);\n };\n\n let clear = async (i: I) => {\n clearAndWait(i).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n let clearByTag = async (tag: string) => {\n clearByTagAndWait(tag).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n return Object.assign(run, { clear, clearByTag, clearAndWait, clearByTagAndWait });\n};\n\nexport let createLocallyCachedFunction = <I, O>(opts: {\n getHash: (i: I) => string;\n provider: (i: I) => Promise<O>;\n ttlSeconds: number;\n}) => {\n let cache = new LRUCache<string, any>({ max: 500, ttl: opts.ttlSeconds });\n\n let active = new Map<string, Promise<O>>();\n\n let getHash = (i: I) => opts.getHash(i);\n\n let run = async (i: I): Promise<O> => {\n let hash = getHash(i);\n if (active.has(hash)) return await active.get(hash)!;\n if (cache.has(hash)) return cache.get(hash)!;\n\n let promise = (async () => {\n let result = await opts.provider(i);\n cache.set(hash, result);\n return result;\n })();\n\n active.set(hash, promise);\n promise.finally(() => active.delete(hash));\n\n return await promise;\n };\n\n let clearAndWait = async (i: I) => {\n let hash = getHash(i);\n cache.delete(hash);\n };\n\n let clear = async (i: I) => {\n clearAndWait(i).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n return Object.assign(run, { clear, clearAndWait });\n};\n"],"names":["Sentry","getSentry","_settle","state","value","_Pact","s","o","bind","pact","v","then","prototype","onFulfilled","onRejected","result","this","callback","e","_this","_isSettledPact","thenable","cache","LRUCache","max","ttl","array","body","check","reject","_cycle","i","length","opts","active","Map","useRedisClient","createRedisClient","redisUrl","lazy","getHash","name","getTagKeys","tags","map","tag","clearAndWait","hash","Promise","resolve","redis","get","data","del","getTags","SuperJson","parse","_forOf","tagKey","_temp3","sRem","_catch","console","error","captureException","clearByTagAndWait","sMembers","hashes","_temp5","_temp4","Object","assign","_exit","_temp2","_result","has","promise","current","ttlSeconds","provider","setTTL","newTTL","set","stringify","EX","tagKeys","all","sAdd","expire","_temp","_await$active$get","clear","err","clearByTag","_exit2","_temp7","_result2","_temp6","_await$active$get2"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export declare let createCachedFunction: <I, O>(opts: {
2
+ name: string;
3
+ redisUrl: string;
4
+ getHash: (i: I) => string;
5
+ provider: (i: I, opts: {
6
+ setTTL: (ttl: number) => void;
7
+ }) => Promise<O>;
8
+ ttlSeconds: number;
9
+ getTags?: (o: O, i: I) => string[];
10
+ }) => ((i: I) => Promise<O>) & {
11
+ clear: (i: I) => Promise<void>;
12
+ clearByTag: (tag: string) => Promise<void>;
13
+ clearAndWait: (i: I) => Promise<void>;
14
+ clearByTagAndWait: (tag: string) => Promise<void>;
15
+ };
16
+ export declare let createLocallyCachedFunction: <I, O>(opts: {
17
+ getHash: (i: I) => string;
18
+ provider: (i: I) => Promise<O>;
19
+ ttlSeconds: number;
20
+ }) => ((i: I) => Promise<O>) & {
21
+ clear: (i: I) => Promise<void>;
22
+ clearAndWait: (i: I) => Promise<void>;
23
+ };
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,eAAO,IAAI,oBAAoB,GAAI,CAAC,EAAE,CAAC,EAAE,MAAM;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IAC1B,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE;QAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC;CACpC,UAQqB,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC;eA8EZ,CAAC;sBAOM,MAAM;sBA7CN,CAAC;6BAwBM,MAAM;CA6B3C,CAAC;AAEF,eAAO,IAAI,2BAA2B,GAAI,CAAC,EAAE,CAAC,EAAE,MAAM;IACpD,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IAC1B,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,UAOqB,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC;eAsBZ,CAAC;sBALM,CAAC;CAa/B,CAAC"}
@@ -0,0 +1,2 @@
1
+ import{createRedisClient as r}from"@lowerdeck/redis";import{getSentry as e}from"@lowerdeck/sentry";import{LRUCache as t}from"lru-cache";import n from"superjson";var o=e(),i="undefined"!=typeof Symbol?Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator")):"@@iterator";function u(r,e,t){if(!r.s){if(t instanceof c){if(!t.s)return void(t.o=u.bind(null,r,e));1&e&&(e=t.s),t=t.v}if(t&&t.then)return void t.then(u.bind(null,r,e),u.bind(null,r,2));r.s=e,r.v=t;var n=r.o;n&&n(r)}}var c=/*#__PURE__*/function(){function r(){}return r.prototype.then=function(e,t){var n=new r,o=this.s;if(o){var i=1&o?e:t;if(i){try{u(n,1,i(this.v))}catch(r){u(n,2,r)}return n}return this}return this.o=function(r){try{var o=r.v;1&r.s?u(n,1,e?e(o):o):t?u(n,1,t(o)):u(n,2,o)}catch(r){u(n,2,r)}},n},r}();function s(r){return r instanceof c&&1&r.s}var f=new t({max:500,ttl:15});function a(r,e,t){if("function"==typeof r[i]){var n,o,f,a=function(r){try{for(;!((n=l.next()).done||t&&t());)if((r=e(n.value))&&r.then){if(!s(r))return void r.then(a,f||(f=u.bind(null,o=new c,2)));r=r.v}o?u(o,1,r):o=r}catch(r){u(o||(o=new c),2,r)}},l=r[i]();if(a(),l.return){var h=function(r){try{n.done||l.return()}catch(r){}return r};if(o&&o.then)return o.then(h,function(r){throw h(r)});h()}return o}if(!("length"in r))throw new TypeError("Object is not iterable");for(var v=[],m=0;m<r.length;m++)v.push(r[m]);return function(r,e,t){var n,o,i=-1;return function f(a){try{for(;++i<r.length&&(!t||!t());)if((a=e(i))&&a.then){if(!s(a))return void a.then(f,o||(o=u.bind(null,n=new c,2)));a=a.v}n?u(n,1,a):n=a}catch(r){u(n||(n=new c),2,r)}}(),n}(v,function(r){return e(v[r])},t)}var l=function(e){var t=new Map,i=r({redisUrl:e.redisUrl}).lazy(),u=function(r){return"cache:0:"+e.name+":val:"+e.getHash(r)},c=function(r){return r.map(function(r){return"cache:0:tag:"+r})},s=function(r){try{var t=u(r);return Promise.resolve(i()).then(function(i){return Promise.resolve(i.get(t)).then(function(u){if(u)return Promise.resolve(i.del(t)).then(function(){return f.delete(t),function(){if(e.getTags){var s=e.getTags(n.parse(u),r);return a(c(s),function(r){var e=function(e,n){try{var o=Promise.resolve(i.sRem(r,t)).then(function(){})}catch(r){return n(r)}return o&&o.then?o.then(void 0,n):o}(0,function(r){console.error("Error removing tag:",r),o.captureException(r)});if(e&&e.then)return e.then(function(){})})}}()})})})}catch(r){return Promise.reject(r)}},l=function(r){try{return Promise.resolve(i()).then(function(e){var t=c([r])[0];return Promise.resolve(e.sMembers(t)).then(function(r){function n(){return Promise.resolve(e.del(t)).then(function(){})}var o=a(r,function(r){return Promise.resolve(e.del(r)).then(function(){f.delete(r)})});return o&&o.then?o.then(n):n()})})}catch(r){return Promise.reject(r)}};return Object.assign(function(r){try{var o,s=function(u){if(o)return u;if(f.has(a))return f.get(a);var s=function(){try{return Promise.resolve(i()).then(function(t){return Promise.resolve(t.get(a)).then(function(o){if(o)return n.parse(o);var i={current:e.ttlSeconds};return Promise.resolve(e.provider(r,{setTTL:function(r){return i.current=r}})).then(function(o){return Promise.resolve(t.set(a,n.stringify(o),{EX:i.current})).then(function(){if(e.getTags){var n=e.getTags(o,r),u=c(n);Promise.all(u.map(function(r){try{return Promise.resolve(t.sAdd(r,a)).then(function(){return Promise.resolve(t.expire(r,i.current)).then(function(){})})}catch(r){return Promise.reject(r)}})).catch(console.error)}return f.set(a,o),o})})})})}catch(r){return Promise.reject(r)}}();return t.set(a,s),s.finally(function(){return t.delete(a)}),Promise.resolve(s)},a=u(r),l=function(){if(t.has(a))return Promise.resolve(t.get(a)).then(function(r){return o=1,r})}();return Promise.resolve(l&&l.then?l.then(s):s(l))}catch(r){return Promise.reject(r)}},{clear:function(r){try{return s(r).catch(function(r){console.error(r),o.captureException(r)}),Promise.resolve()}catch(r){return Promise.reject(r)}},clearByTag:function(r){try{return l(r).catch(function(r){console.error(r),o.captureException(r)}),Promise.resolve()}catch(r){return Promise.reject(r)}},clearAndWait:s,clearByTagAndWait:l})},h=function(r){var e=new t({max:500,ttl:r.ttlSeconds}),n=new Map,i=function(e){return r.getHash(e)},u=function(r){try{var t=i(r);return e.delete(t),Promise.resolve()}catch(r){return Promise.reject(r)}};return Object.assign(function(t){try{var o,u=function(i){if(o)return i;if(e.has(c))return e.get(c);var u=function(){try{return Promise.resolve(r.provider(t)).then(function(r){return e.set(c,r),r})}catch(r){return Promise.reject(r)}}();return n.set(c,u),u.finally(function(){return n.delete(c)}),Promise.resolve(u)},c=i(t),s=function(){if(n.has(c))return Promise.resolve(n.get(c)).then(function(r){return o=1,r})}();return Promise.resolve(s&&s.then?s.then(u):u(s))}catch(r){return Promise.reject(r)}},{clear:function(r){try{return u(r).catch(function(r){console.error(r),o.captureException(r)}),Promise.resolve()}catch(r){return Promise.reject(r)}},clearAndWait:u})};export{l as createCachedFunction,h as createLocallyCachedFunction};
2
+ //# sourceMappingURL=index.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.module.js","sources":["../src/index.ts"],"sourcesContent":["import { createRedisClient } from '@lowerdeck/redis';\nimport { getSentry } from '@lowerdeck/sentry';\nimport { LRUCache } from 'lru-cache';\n\n// @ts-ignore\nimport SuperJson from 'superjson';\n\nlet Sentry = getSentry();\n\nlet cache = new LRUCache<string, any>({ max: 500, ttl: 15 });\n\nlet version = 0;\n\nexport let createCachedFunction = <I, O>(opts: {\n name: string;\n redisUrl: string;\n getHash: (i: I) => string;\n provider: (i: I, opts: { setTTL: (ttl: number) => void }) => Promise<O>;\n ttlSeconds: number;\n getTags?: (o: O, i: I) => string[];\n}) => {\n let active = new Map<string, Promise<O>>();\n\n let useRedisClient = createRedisClient({ redisUrl: opts.redisUrl }).lazy();\n\n let getHash = (i: I) => `cache:${version}:${opts.name}:val:${opts.getHash(i)}`;\n let getTagKeys = (tags: string[]) => tags.map(tag => `cache:${version}:tag:${tag}`);\n\n let run = async (i: I): Promise<O> => {\n let hash = getHash(i);\n if (active.has(hash)) return await active.get(hash)!;\n if (cache.has(hash)) return cache.get(hash)!;\n\n let promise = (async () => {\n let redis = await useRedisClient();\n let value = await redis.get(hash);\n if (value) return SuperJson.parse(value);\n\n let ttl = { current: opts.ttlSeconds };\n let setTTL = (newTTL: number) => (ttl.current = newTTL);\n\n let result = await opts.provider(i, { setTTL });\n await redis.set(hash, SuperJson.stringify(result), {\n EX: ttl.current\n });\n\n if (opts.getTags) {\n let tags = opts.getTags(result, i);\n let tagKeys = getTagKeys(tags);\n Promise.all(\n tagKeys.map(async tagKey => {\n await redis.sAdd(tagKey, hash);\n await redis.expire(tagKey, ttl.current);\n })\n ).catch(console.error);\n }\n\n cache.set(hash, result);\n\n return result as any;\n })();\n\n active.set(hash, promise);\n promise.finally(() => active.delete(hash));\n\n return await promise;\n };\n\n let clearAndWait = async (i: I) => {\n let hash = getHash(i);\n let redis = await useRedisClient();\n\n let data = await redis.get(hash);\n if (!data) return;\n\n await redis.del(hash);\n cache.delete(hash);\n\n if (opts.getTags) {\n let tags = opts.getTags(SuperJson.parse(data), i);\n let tagKeys = getTagKeys(tags);\n for (let tagKey of tagKeys) {\n try {\n await redis.sRem(tagKey, hash);\n } catch (e) {\n console.error('Error removing tag:', e);\n Sentry.captureException(e);\n }\n }\n }\n };\n\n let clearByTagAndWait = async (tag: string) => {\n let redis = await useRedisClient();\n let tagKey = getTagKeys([tag])[0];\n\n let hashes = await redis.sMembers(tagKey);\n\n for (let hash of hashes) {\n await redis.del(hash);\n cache.delete(hash);\n }\n\n await redis.del(tagKey);\n };\n\n let clear = async (i: I) => {\n clearAndWait(i).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n let clearByTag = async (tag: string) => {\n clearByTagAndWait(tag).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n return Object.assign(run, { clear, clearByTag, clearAndWait, clearByTagAndWait });\n};\n\nexport let createLocallyCachedFunction = <I, O>(opts: {\n getHash: (i: I) => string;\n provider: (i: I) => Promise<O>;\n ttlSeconds: number;\n}) => {\n let cache = new LRUCache<string, any>({ max: 500, ttl: opts.ttlSeconds });\n\n let active = new Map<string, Promise<O>>();\n\n let getHash = (i: I) => opts.getHash(i);\n\n let run = async (i: I): Promise<O> => {\n let hash = getHash(i);\n if (active.has(hash)) return await active.get(hash)!;\n if (cache.has(hash)) return cache.get(hash)!;\n\n let promise = (async () => {\n let result = await opts.provider(i);\n cache.set(hash, result);\n return result;\n })();\n\n active.set(hash, promise);\n promise.finally(() => active.delete(hash));\n\n return await promise;\n };\n\n let clearAndWait = async (i: I) => {\n let hash = getHash(i);\n cache.delete(hash);\n };\n\n let clear = async (i: I) => {\n clearAndWait(i).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n return Object.assign(run, { clear, clearAndWait });\n};\n"],"names":["Sentry","getSentry","_settle","state","value","_Pact","s","o","bind","pact","v","then","prototype","onFulfilled","onRejected","result","this","callback","e","_this","_isSettledPact","thenable","cache","LRUCache","max","ttl","array","body","check","reject","_cycle","i","length","createCachedFunction","opts","active","Map","useRedisClient","createRedisClient","redisUrl","lazy","getHash","name","getTagKeys","tags","map","tag","clearAndWait","hash","Promise","resolve","redis","get","data","del","getTags","SuperJson","parse","_forOf","tagKey","_temp3","sRem","_catch","console","error","captureException","clearByTagAndWait","sMembers","hashes","_temp5","_temp4","Object","assign","_exit","_temp2","_result","has","promise","current","ttlSeconds","provider","setTTL","newTTL","set","stringify","EX","tagKeys","all","sAdd","expire","_temp","_await$active$get","clear","err","clearByTag","createLocallyCachedFunction","_exit2","_temp7","_result2","_temp6","_await$active$get2"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@lowerdeck/redis"),require("@lowerdeck/sentry"),require("lru-cache"),require("superjson")):"function"==typeof define&&define.amd?define(["exports","@lowerdeck/redis","@lowerdeck/sentry","lru-cache","superjson"],r):r((e||self).cache={},e.redis,e.sentry,e.lruCache,e.superjson)}(this,function(e,r,t,n,o){function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var u=/*#__PURE__*/i(o),c=t.getSentry(),s="undefined"!=typeof Symbol?Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator")):"@@iterator";function f(e,r,t){if(!e.s){if(t instanceof a){if(!t.s)return void(t.o=f.bind(null,e,r));1&r&&(r=t.s),t=t.v}if(t&&t.then)return void t.then(f.bind(null,e,r),f.bind(null,e,2));e.s=r,e.v=t;var n=e.o;n&&n(e)}}var a=/*#__PURE__*/function(){function e(){}return e.prototype.then=function(r,t){var n=new e,o=this.s;if(o){var i=1&o?r:t;if(i){try{f(n,1,i(this.v))}catch(e){f(n,2,e)}return n}return this}return this.o=function(e){try{var o=e.v;1&e.s?f(n,1,r?r(o):o):t?f(n,1,t(o)):f(n,2,o)}catch(e){f(n,2,e)}},n},e}();function l(e){return e instanceof a&&1&e.s}var h=new n.LRUCache({max:500,ttl:15});function v(e,r,t){if("function"==typeof e[s]){var n,o,i,u=function(e){try{for(;!((n=c.next()).done||t&&t());)if((e=r(n.value))&&e.then){if(!l(e))return void e.then(u,i||(i=f.bind(null,o=new a,2)));e=e.v}o?f(o,1,e):o=e}catch(e){f(o||(o=new a),2,e)}},c=e[s]();if(u(),c.return){var h=function(e){try{n.done||c.return()}catch(e){}return e};if(o&&o.then)return o.then(h,function(e){throw h(e)});h()}return o}if(!("length"in e))throw new TypeError("Object is not iterable");for(var v=[],d=0;d<e.length;d++)v.push(e[d]);return function(e,r,t){var n,o,i=-1;return function u(c){try{for(;++i<e.length&&(!t||!t());)if((c=r(i))&&c.then){if(!l(c))return void c.then(u,o||(o=f.bind(null,n=new a,2)));c=c.v}n?f(n,1,c):n=c}catch(e){f(n||(n=new a),2,e)}}(),n}(v,function(e){return r(v[e])},t)}e.createCachedFunction=function(e){var t=new Map,n=r.createRedisClient({redisUrl:e.redisUrl}).lazy(),o=function(r){return"cache:0:"+e.name+":val:"+e.getHash(r)},i=function(e){return e.map(function(e){return"cache:0:tag:"+e})},s=function(r){try{var t=o(r);return Promise.resolve(n()).then(function(n){return Promise.resolve(n.get(t)).then(function(o){if(o)return Promise.resolve(n.del(t)).then(function(){return h.delete(t),function(){if(e.getTags){var s=e.getTags(u.default.parse(o),r);return v(i(s),function(e){var r=function(r,o){try{var i=Promise.resolve(n.sRem(e,t)).then(function(){})}catch(e){return o(e)}return i&&i.then?i.then(void 0,o):i}(0,function(e){console.error("Error removing tag:",e),c.captureException(e)});if(r&&r.then)return r.then(function(){})})}}()})})})}catch(e){return Promise.reject(e)}},f=function(e){try{return Promise.resolve(n()).then(function(r){var t=i([e])[0];return Promise.resolve(r.sMembers(t)).then(function(e){function n(){return Promise.resolve(r.del(t)).then(function(){})}var o=v(e,function(e){return Promise.resolve(r.del(e)).then(function(){h.delete(e)})});return o&&o.then?o.then(n):n()})})}catch(e){return Promise.reject(e)}};return Object.assign(function(r){try{var c,s=function(o){if(c)return o;if(h.has(f))return h.get(f);var s=function(){try{return Promise.resolve(n()).then(function(t){return Promise.resolve(t.get(f)).then(function(n){if(n)return u.default.parse(n);var o={current:e.ttlSeconds};return Promise.resolve(e.provider(r,{setTTL:function(e){return o.current=e}})).then(function(n){return Promise.resolve(t.set(f,u.default.stringify(n),{EX:o.current})).then(function(){if(e.getTags){var u=e.getTags(n,r),c=i(u);Promise.all(c.map(function(e){try{return Promise.resolve(t.sAdd(e,f)).then(function(){return Promise.resolve(t.expire(e,o.current)).then(function(){})})}catch(e){return Promise.reject(e)}})).catch(console.error)}return h.set(f,n),n})})})})}catch(e){return Promise.reject(e)}}();return t.set(f,s),s.finally(function(){return t.delete(f)}),Promise.resolve(s)},f=o(r),a=function(){if(t.has(f))return Promise.resolve(t.get(f)).then(function(e){return c=1,e})}();return Promise.resolve(a&&a.then?a.then(s):s(a))}catch(e){return Promise.reject(e)}},{clear:function(e){try{return s(e).catch(function(e){console.error(e),c.captureException(e)}),Promise.resolve()}catch(e){return Promise.reject(e)}},clearByTag:function(e){try{return f(e).catch(function(e){console.error(e),c.captureException(e)}),Promise.resolve()}catch(e){return Promise.reject(e)}},clearAndWait:s,clearByTagAndWait:f})},e.createLocallyCachedFunction=function(e){var r=new n.LRUCache({max:500,ttl:e.ttlSeconds}),t=new Map,o=function(r){return e.getHash(r)},i=function(e){try{var t=o(e);return r.delete(t),Promise.resolve()}catch(e){return Promise.reject(e)}};return Object.assign(function(n){try{var i,u=function(o){if(i)return o;if(r.has(c))return r.get(c);var u=function(){try{return Promise.resolve(e.provider(n)).then(function(e){return r.set(c,e),e})}catch(e){return Promise.reject(e)}}();return t.set(c,u),u.finally(function(){return t.delete(c)}),Promise.resolve(u)},c=o(n),s=function(){if(t.has(c))return Promise.resolve(t.get(c)).then(function(e){return i=1,e})}();return Promise.resolve(s&&s.then?s.then(u):u(s))}catch(e){return Promise.reject(e)}},{clear:function(e){try{return i(e).catch(function(e){console.error(e),c.captureException(e)}),Promise.resolve()}catch(e){return Promise.reject(e)}},clearAndWait:i})}});
2
+ //# sourceMappingURL=index.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.js","sources":["../src/index.ts"],"sourcesContent":["import { createRedisClient } from '@lowerdeck/redis';\nimport { getSentry } from '@lowerdeck/sentry';\nimport { LRUCache } from 'lru-cache';\n\n// @ts-ignore\nimport SuperJson from 'superjson';\n\nlet Sentry = getSentry();\n\nlet cache = new LRUCache<string, any>({ max: 500, ttl: 15 });\n\nlet version = 0;\n\nexport let createCachedFunction = <I, O>(opts: {\n name: string;\n redisUrl: string;\n getHash: (i: I) => string;\n provider: (i: I, opts: { setTTL: (ttl: number) => void }) => Promise<O>;\n ttlSeconds: number;\n getTags?: (o: O, i: I) => string[];\n}) => {\n let active = new Map<string, Promise<O>>();\n\n let useRedisClient = createRedisClient({ redisUrl: opts.redisUrl }).lazy();\n\n let getHash = (i: I) => `cache:${version}:${opts.name}:val:${opts.getHash(i)}`;\n let getTagKeys = (tags: string[]) => tags.map(tag => `cache:${version}:tag:${tag}`);\n\n let run = async (i: I): Promise<O> => {\n let hash = getHash(i);\n if (active.has(hash)) return await active.get(hash)!;\n if (cache.has(hash)) return cache.get(hash)!;\n\n let promise = (async () => {\n let redis = await useRedisClient();\n let value = await redis.get(hash);\n if (value) return SuperJson.parse(value);\n\n let ttl = { current: opts.ttlSeconds };\n let setTTL = (newTTL: number) => (ttl.current = newTTL);\n\n let result = await opts.provider(i, { setTTL });\n await redis.set(hash, SuperJson.stringify(result), {\n EX: ttl.current\n });\n\n if (opts.getTags) {\n let tags = opts.getTags(result, i);\n let tagKeys = getTagKeys(tags);\n Promise.all(\n tagKeys.map(async tagKey => {\n await redis.sAdd(tagKey, hash);\n await redis.expire(tagKey, ttl.current);\n })\n ).catch(console.error);\n }\n\n cache.set(hash, result);\n\n return result as any;\n })();\n\n active.set(hash, promise);\n promise.finally(() => active.delete(hash));\n\n return await promise;\n };\n\n let clearAndWait = async (i: I) => {\n let hash = getHash(i);\n let redis = await useRedisClient();\n\n let data = await redis.get(hash);\n if (!data) return;\n\n await redis.del(hash);\n cache.delete(hash);\n\n if (opts.getTags) {\n let tags = opts.getTags(SuperJson.parse(data), i);\n let tagKeys = getTagKeys(tags);\n for (let tagKey of tagKeys) {\n try {\n await redis.sRem(tagKey, hash);\n } catch (e) {\n console.error('Error removing tag:', e);\n Sentry.captureException(e);\n }\n }\n }\n };\n\n let clearByTagAndWait = async (tag: string) => {\n let redis = await useRedisClient();\n let tagKey = getTagKeys([tag])[0];\n\n let hashes = await redis.sMembers(tagKey);\n\n for (let hash of hashes) {\n await redis.del(hash);\n cache.delete(hash);\n }\n\n await redis.del(tagKey);\n };\n\n let clear = async (i: I) => {\n clearAndWait(i).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n let clearByTag = async (tag: string) => {\n clearByTagAndWait(tag).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n return Object.assign(run, { clear, clearByTag, clearAndWait, clearByTagAndWait });\n};\n\nexport let createLocallyCachedFunction = <I, O>(opts: {\n getHash: (i: I) => string;\n provider: (i: I) => Promise<O>;\n ttlSeconds: number;\n}) => {\n let cache = new LRUCache<string, any>({ max: 500, ttl: opts.ttlSeconds });\n\n let active = new Map<string, Promise<O>>();\n\n let getHash = (i: I) => opts.getHash(i);\n\n let run = async (i: I): Promise<O> => {\n let hash = getHash(i);\n if (active.has(hash)) return await active.get(hash)!;\n if (cache.has(hash)) return cache.get(hash)!;\n\n let promise = (async () => {\n let result = await opts.provider(i);\n cache.set(hash, result);\n return result;\n })();\n\n active.set(hash, promise);\n promise.finally(() => active.delete(hash));\n\n return await promise;\n };\n\n let clearAndWait = async (i: I) => {\n let hash = getHash(i);\n cache.delete(hash);\n };\n\n let clear = async (i: I) => {\n clearAndWait(i).catch(err => {\n console.error(err);\n Sentry.captureException(err);\n });\n };\n\n return Object.assign(run, { clear, clearAndWait });\n};\n"],"names":["Sentry","getSentry","_settle","state","value","_Pact","s","o","bind","pact","v","then","prototype","onFulfilled","onRejected","result","this","callback","e","_this","_isSettledPact","thenable","cache","LRUCache","max","ttl","array","body","check","reject","_cycle","i","length","opts","active","Map","useRedisClient","createRedisClient","redisUrl","lazy","getHash","name","getTagKeys","tags","map","tag","clearAndWait","hash","Promise","resolve","redis","get","data","del","getTags","SuperJson","parse","_forOf","tagKey","_temp3","sRem","_catch","console","error","captureException","clearByTagAndWait","sMembers","hashes","_temp5","_temp4","Object","assign","_exit","_temp2","_result","has","promise","current","ttlSeconds","provider","setTTL","newTTL","set","stringify","EX","tagKeys","all","sAdd","expire","_temp","_await$active$get","clear","err","clearByTag","_exit2","_temp7","_result2","_temp6","_await$active$get2"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@lowerdeck/cache",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "files": [
8
+ "src/**",
9
+ "dist/**",
10
+ "README.md",
11
+ "package.json"
12
+ ],
13
+ "author": "Tobias Herber",
14
+ "license": "Apache 2",
15
+ "type": "module",
16
+ "source": "src/index.ts",
17
+ "exports": {
18
+ "types": "./dist/index.d.ts",
19
+ "require": "./dist/index.cjs",
20
+ "import": "./dist/index.module.js",
21
+ "default": "./dist/index.module.js"
22
+ },
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.module.js",
25
+ "types": "dist/index.d.ts",
26
+ "unpkg": "./dist/index.umd.js",
27
+ "scripts": {
28
+ "test": "vitest run --passWithNoTests",
29
+ "lint": "prettier src/**/*.ts --check",
30
+ "build": "rm -rf ./dist && microbundle"
31
+ },
32
+ "dependencies": {
33
+ "@lowerdeck/redis": "^1.0.3",
34
+ "@lowerdeck/sentry": "^1.0.2",
35
+ "lru-cache": "^11.2.4",
36
+ "superjson": "^2.2.6"
37
+ },
38
+ "devDependencies": {
39
+ "@lowerdeck/tsconfig": "^1.0.1",
40
+ "typescript": "5.8.2",
41
+ "microbundle": "^0.15.1",
42
+ "vitest": "^3.1.2"
43
+ }
44
+ }
package/src/index.ts ADDED
@@ -0,0 +1,165 @@
1
+ import { createRedisClient } from '@lowerdeck/redis';
2
+ import { getSentry } from '@lowerdeck/sentry';
3
+ import { LRUCache } from 'lru-cache';
4
+
5
+ // @ts-ignore
6
+ import SuperJson from 'superjson';
7
+
8
+ let Sentry = getSentry();
9
+
10
+ let cache = new LRUCache<string, any>({ max: 500, ttl: 15 });
11
+
12
+ let version = 0;
13
+
14
+ export let createCachedFunction = <I, O>(opts: {
15
+ name: string;
16
+ redisUrl: string;
17
+ getHash: (i: I) => string;
18
+ provider: (i: I, opts: { setTTL: (ttl: number) => void }) => Promise<O>;
19
+ ttlSeconds: number;
20
+ getTags?: (o: O, i: I) => string[];
21
+ }) => {
22
+ let active = new Map<string, Promise<O>>();
23
+
24
+ let useRedisClient = createRedisClient({ redisUrl: opts.redisUrl }).lazy();
25
+
26
+ let getHash = (i: I) => `cache:${version}:${opts.name}:val:${opts.getHash(i)}`;
27
+ let getTagKeys = (tags: string[]) => tags.map(tag => `cache:${version}:tag:${tag}`);
28
+
29
+ let run = async (i: I): Promise<O> => {
30
+ let hash = getHash(i);
31
+ if (active.has(hash)) return await active.get(hash)!;
32
+ if (cache.has(hash)) return cache.get(hash)!;
33
+
34
+ let promise = (async () => {
35
+ let redis = await useRedisClient();
36
+ let value = await redis.get(hash);
37
+ if (value) return SuperJson.parse(value);
38
+
39
+ let ttl = { current: opts.ttlSeconds };
40
+ let setTTL = (newTTL: number) => (ttl.current = newTTL);
41
+
42
+ let result = await opts.provider(i, { setTTL });
43
+ await redis.set(hash, SuperJson.stringify(result), {
44
+ EX: ttl.current
45
+ });
46
+
47
+ if (opts.getTags) {
48
+ let tags = opts.getTags(result, i);
49
+ let tagKeys = getTagKeys(tags);
50
+ Promise.all(
51
+ tagKeys.map(async tagKey => {
52
+ await redis.sAdd(tagKey, hash);
53
+ await redis.expire(tagKey, ttl.current);
54
+ })
55
+ ).catch(console.error);
56
+ }
57
+
58
+ cache.set(hash, result);
59
+
60
+ return result as any;
61
+ })();
62
+
63
+ active.set(hash, promise);
64
+ promise.finally(() => active.delete(hash));
65
+
66
+ return await promise;
67
+ };
68
+
69
+ let clearAndWait = async (i: I) => {
70
+ let hash = getHash(i);
71
+ let redis = await useRedisClient();
72
+
73
+ let data = await redis.get(hash);
74
+ if (!data) return;
75
+
76
+ await redis.del(hash);
77
+ cache.delete(hash);
78
+
79
+ if (opts.getTags) {
80
+ let tags = opts.getTags(SuperJson.parse(data), i);
81
+ let tagKeys = getTagKeys(tags);
82
+ for (let tagKey of tagKeys) {
83
+ try {
84
+ await redis.sRem(tagKey, hash);
85
+ } catch (e) {
86
+ console.error('Error removing tag:', e);
87
+ Sentry.captureException(e);
88
+ }
89
+ }
90
+ }
91
+ };
92
+
93
+ let clearByTagAndWait = async (tag: string) => {
94
+ let redis = await useRedisClient();
95
+ let tagKey = getTagKeys([tag])[0];
96
+
97
+ let hashes = await redis.sMembers(tagKey);
98
+
99
+ for (let hash of hashes) {
100
+ await redis.del(hash);
101
+ cache.delete(hash);
102
+ }
103
+
104
+ await redis.del(tagKey);
105
+ };
106
+
107
+ let clear = async (i: I) => {
108
+ clearAndWait(i).catch(err => {
109
+ console.error(err);
110
+ Sentry.captureException(err);
111
+ });
112
+ };
113
+
114
+ let clearByTag = async (tag: string) => {
115
+ clearByTagAndWait(tag).catch(err => {
116
+ console.error(err);
117
+ Sentry.captureException(err);
118
+ });
119
+ };
120
+
121
+ return Object.assign(run, { clear, clearByTag, clearAndWait, clearByTagAndWait });
122
+ };
123
+
124
+ export let createLocallyCachedFunction = <I, O>(opts: {
125
+ getHash: (i: I) => string;
126
+ provider: (i: I) => Promise<O>;
127
+ ttlSeconds: number;
128
+ }) => {
129
+ let cache = new LRUCache<string, any>({ max: 500, ttl: opts.ttlSeconds });
130
+
131
+ let active = new Map<string, Promise<O>>();
132
+
133
+ let getHash = (i: I) => opts.getHash(i);
134
+
135
+ let run = async (i: I): Promise<O> => {
136
+ let hash = getHash(i);
137
+ if (active.has(hash)) return await active.get(hash)!;
138
+ if (cache.has(hash)) return cache.get(hash)!;
139
+
140
+ let promise = (async () => {
141
+ let result = await opts.provider(i);
142
+ cache.set(hash, result);
143
+ return result;
144
+ })();
145
+
146
+ active.set(hash, promise);
147
+ promise.finally(() => active.delete(hash));
148
+
149
+ return await promise;
150
+ };
151
+
152
+ let clearAndWait = async (i: I) => {
153
+ let hash = getHash(i);
154
+ cache.delete(hash);
155
+ };
156
+
157
+ let clear = async (i: I) => {
158
+ clearAndWait(i).catch(err => {
159
+ console.error(err);
160
+ Sentry.captureException(err);
161
+ });
162
+ };
163
+
164
+ return Object.assign(run, { clear, clearAndWait });
165
+ };