@autofleet/matmon 2.4.18 → 3.0.1

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/lib/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=s(require(`lru-cache`)),l=s(require(`async-mutex`)),u=s(require(`dotenv`)),d=s(require(`@autofleet/logger`)),f=s(require(`redis`)),p=s(require(`redis-lock`)),m=s(require(`util`)),h=3e3,ee=`mutex - locking timeout`,g=async(e,t)=>{let n=await e.acquire();try{await t()}finally{n()}},_=()=>(0,l.withTimeout)(new l.Mutex,3e3,Error(`mutex - locking timeout`)),v=(0,d.default)();var y=v;u.default.config();const te=300,ne=1e5,b={},x=({lifeTimeInSec:e,size:t=300})=>({max:Math.min(t,1e5),maxAge:process.env.NODE_ENV===`test`?0:1e3*e}),S=e=>(b[e]??=_(),b[e]),C=e=>{b[e]&&delete b[e]},w=(e,t)=>new c.default(x({lifeTimeInSec:e,size:t})),T=process.env.IS_IN_MATMON_TESTING===`true`,E=process.env.NODE_ENV===`test`&&!T,D=async e=>{try{await e()}catch(e){y.error(`Failed to set in cache`,e)}},O=async({cacheKey:e,cacheGet:t,cacheSet:n,fetching:r,skipCache:i})=>{if(i||E){let e=await r();return await D(()=>n(e)),e}let a=null;try{await g(S(e),async()=>{a=await t(),a||(a=await r(),await D(()=>n(a))),C(e)})}catch{a=await r(),await D(()=>n(a)),C(e)}return a},k=(e,t)=>typeof e==`string`?e:e[t],A=({getFromCache:e,multiGetterFromCache:t,setInCache:n,setMultiInCache:r,getter:i,multiGetter:a,idField:o=`id`})=>async s=>{let c=new Map(s.filter(Boolean).map(e=>[k(e,o),e])),l=new Map,u=[...c.values()],d=await(t?.(u)??Promise.all(u.map(t=>e(t))));if(d.filter(Boolean).forEach(e=>{c.delete(k(e,o)),l.set(k(e,o),e)}),c.size>0){let e=await(a?.([...c.values()])??Promise.all([...c.values()].map(e=>i(e))));if(r&&e.length>0){let t=e.reduce((e,t)=>(e[k(t,o)]=t,e),{});await D(()=>r(t))}e.forEach(e=>{e&&(r||D(()=>n(e[o],e)),l.set(k(e,o),e))})}return s.map(e=>e?l.get(k(e,o)):null)};var j=class extends f.RedisError{constructor(e,t){super(),this.innerError=t,this.message=e}toString(){return`${this.message}: ${this.innerError}`}},M=class extends j{constructor(e,t,n){super(e,t),this.key=n}toString(){return`${this.message} (${this.key}): ${this.innerError}`}};const N=e=>{Object.keys(e).forEach(t=>{typeof e[t]==`function`&&(e[`${t}Async`]=(0,m.promisify)(e[t]),Object.defineProperty(e[`${t}Async`],`__isPromisified__`,{value:!0,configurable:!0,enumerable:!1,writable:!0}))})};N(f.default.RedisClient.prototype),N(f.default.Multi.prototype);const{env:P}=process,F=P.REDIS_HOST_NAME||`127.0.0.1`,I=P.REDIS_HOST_PORT||6379,L=P.AF_SERVICE_NAME,re=5e3,ie=1e3,ae=3600,R=L+`:`;if(!L)throw Error(`SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME`);var z=class{constructor(e){this.locks={},this.keyPrefix=R,this.client=f.default.createClient({host:e.host||F,port:e.port||I}),this.locker=(0,m.promisify)((0,p.default)(this.client,e.lockRetries??10)),this.lockTimeout=e.lockTimeout??5e3,this.lockDuration=e.lockDuration??1e3,this.baseTTL=e.ttl??3600,this.useLock=!!e.useLock}async get(e){let t=R+e,n;try{n=await this.client.getAsync(t)}catch(e){throw new j(`Failed to get a value`,e)}if(this.useLock){let e;try{e=await this.lock(t)}catch(e){throw new M(`Failed to lock key`,e,t)}this.locks[t]=e}return JSON.parse(n)}async getMultiple(e){let t=e.map(e=>R+e),n;try{if(t.length===0)return[];n=await this.client.mgetAsync(t)}catch(e){throw new j(`Failed to get a value`,e)}return n.map(e=>JSON.parse(e))}async set(e,t){let n=R+e,r=parseInt(String(this.baseTTL*(Math.random()+1)),10);try{await this.client.setAsync(n,JSON.stringify(t),`EX`,r),this.locks[n]&&(await this.locks[n](),delete this.locks[n])}catch(e){throw new j(`Failed to set a key-value pair`,e)}}async setMultiple(e){if(typeof e!=`object`){let e=new j(`keyValues must be an object`,Error(`keyValues must be an object`));throw y.error(`keyValues must be an object`,{error:e}),e}if(e==null||Object.keys(e).length===0)return;let t=Object.entries(e).map(([e,t])=>[R+e,JSON.stringify(t)]),n=Math.trunc(this.baseTTL*(Math.random()+1));try{let e=this.client.multi(),r=e.msetAsync(...t.flat());return t.map(([t])=>e.expireAsync(t,n)),await e.exec(),r}catch(e){throw new j(`Failed to set multiple key-value pairs`,e)}}async remove(e){let t=R+e;try{this.locks[t]&&(await this.locks[t](),delete this.locks[t]),await this.client.delAsync(t)}catch(e){throw new j(`Failed to delete key ${t}`,e)}}async removeMultiple(e){let t=e.map(e=>R+e);try{await this.client.delAsync(t)}catch(e){throw new j(`Failed to delete multiple keys ${t.join(`|`)}`,e)}}getClient(){return this.client}lock(e){return Promise.race([this.locker(e,this.lockDuration),new Promise((e,t)=>setTimeout(()=>t(),this.lockTimeout))])}},B=z;const{AF_SERVICE_NAME:V}=process.env,H=`ormCache`,U=[`afterSave`,`afterUpdate`,`afterDestroy`],W=[`beforeBulkUpdate`,`beforeBulkDestroy`],G=(e,t)=>`${V}:${H}:${e}_${t}`,K=(e,t,n)=>`${V}:${H}:${e}_${t}_${n}_DEPENDENCIES`,q=(e,t)=>{let n=e.associations.filter(e=>t[e.alias]).map(n=>{let r=n.accessKey,i=[];return Array.isArray(t[n.alias])?(t[n.alias].forEach(t=>{i.push(K(e.name,n.name,t[r])),n.innerAssociation&&t[n.innerAssociation.alias]&&i.push(K(e.name,n.innerAssociation.name,t[n.innerAssociation.alias][n.innerAssociation.accessKey]))}),i):[K(e.name,n.name,t[n.alias][r])]});return n.reduce((e,t)=>e.concat(t),[])},J=(e,t,n)=>{let{transaction:r}=t;r?r.afterCommit(()=>n(e)):n(e)};var Y=class{constructor(e,t){this.ormInstance=e,this.debugMode=t}getModel(e){return this.ormInstance.models[e]}getModelDependencies(e){let{associations:t}=this.ormInstance.models[e];return[...Object.keys(t).map(e=>{let n=t[e],r={alias:n.as,name:n.target.name,accessKey:n.target.primaryKeyAttribute},{through:i}=n;if(i){let e=i.model;r.innerAssociation={alias:e.name,name:e.name,accessKey:e.primaryKeyAttribute}}return r})]}debug(e,t){this.debugMode&&y.info(`[ORM_CACHE Debug] ${e}`,t)}injectGetWithCacheFunction(e,t){let n=async n=>{let r=q(t,n),i=G(t.name,n.id);this.debug(`Adding dependencies`,{instanceKey:i,dependencyKeys:r});let a=e.getClient().multi(),o=(0,m.promisify)(a.exec).bind(a);return r.reduce((e,t)=>e.sadd(t,i),a),o()},r=this.getModel(t.name);r.findByPkCached=async(i,a,o)=>{let s=G(t.name,i),c=JSON.parse(await e.getClient().getAsync(s));return c?(c=this.getModel(t.name).build(c,{isNewRecord:!1,include:o.include}),this.debug(`Found cached value`,{value:c,id:i,cacheKey:s})):(this.debug(`Value not found in cache, looking in db`,{id:i,cacheKey:s}),c=await r.scope(a).findByPk(i,o),this.debug(`Value from DB`,{value:c||`not found`,cacheKey:s}),await Promise.all([e.getClient().setAsync(s,JSON.stringify(c)),c&&n(c)])),c}}addInvalidationHooks(e,t){let n=async n=>{let r=q(t,n),i=G(t.name,n.id);this.debug(`Removing dependencies`,{instance:n,instanceKey:i,dependencyKeys:r});let a=e.getClient().multi(),o=(0,m.promisify)(a.exec).bind(a);return r.map(e=>a.srem(e,i)),a.del(i),o()},r=async(n,r)=>{let i=await e.getClient().smembersAsync(K(t.name,n,r));this.debug(`Invalidating dependent instances`,{dependentInstancesKeys:i});let a=e.getClient().multi(),o=(0,m.promisify)(a.exec).bind(a),s=await Promise.all(i.map(async n=>{let r=JSON.parse(await e.getClient().getAsync(n));if(!r)return[];let i=q(t,r);return i.reduce((e,t)=>e.srem(t,n),a),a.del(n),i}));return this.debug(`Removing dependencies`,{dependentInstancesKeys:i,dependenciesToRemove:s}),o()},i=this.getModel(t.name);U.map(e=>i.addHook(e,(e,t)=>J(e,t,e=>n(e)))),W.map(e=>i.addHook(e,e=>{e.individualHook=!0})),t.associations=this.getModelDependencies(t.name),this.debug(`Adding Invalidations Hooks to ${t.name}'s associations`,{associations:t.associations}),t.associations.forEach(e=>{let t=this.getModel(e.name);if(U.forEach(n=>t.addHook(n,(t,n)=>J(t,n,t=>r(e.name,t[e.accessKey])))),W.forEach(e=>t.addHook(e,e=>{e.individualHook=!0})),e.innerAssociation){let t=this.getModel(e.innerAssociation.name);U.forEach(n=>t.addHook(n,(t,n)=>J(t,n,t=>r(e.innerAssociation.name,t[e.innerAssociation.accessKey])))),W.map(e=>t.addHook(e,e=>{e.individualHook=!0}))}})}},X=class extends Error{};let Z=function(e){return e.SEQUELIZE=`sequelize`,e}({});const Q=e=>{switch(e.type){case Z.SEQUELIZE:return new Y(e.ormInstance,e.debug);default:throw new X(`ORM type ${e.type} is unsupported at the moment`)}},$=e=>{let{models:t}=e;y.info(`Starting ORM Cache`,{options:e});let n=Q(e),r=new B({host:process.env.REDIS_HOST,port:Number.parseInt(process.env.REDIS_PORT,10)});t.map(e=>{n.addInvalidationHooks(r,e),n.injectGetWithCacheFunction(r,e)})};exports.ORMCache=$,exports.ORMTypes=Z,exports.RedisCache=B,exports.getMultipleWithCache=A,exports.getNewLRU=w,exports.getWithCacheSupport=O;
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const l=c(require(`lru-cache`)),u=c(require(`async-mutex`)),d=c(require(`node:process`)),f=c(require(`redis`)),p=c(require(`redis-lock`));var m=o(((exports,t)=>{function n(){var e=typeof SuppressedError==`function`?SuppressedError:function(e,t){var n=Error();return n.name=`SuppressedError`,n.error=e,n.suppressed=t,n},t={},n=[];function r(e,t){if(t!=null){if(Object(t)!==t)throw TypeError(`using declarations can only be used with objects, functions, null, or undefined.`);if(e)var r=t[Symbol.asyncDispose||Symbol.for(`Symbol.asyncDispose`)];if(r===void 0&&(r=t[Symbol.dispose||Symbol.for(`Symbol.dispose`)],e))var i=r;if(typeof r!=`function`)throw TypeError(`Object is not disposable.`);i&&(r=function(){try{i.call(t)}catch(e){return Promise.reject(e)}}),n.push({v:t,d:r,a:e})}else e&&n.push({d:t,a:e});return t}return{e:t,u:r.bind(null,!1),a:r.bind(null,!0),d:function(){var r,i=this.e,a=0;function o(){for(;r=n.pop();)try{if(!r.a&&a===1)return a=0,n.push(r),Promise.resolve().then(o);if(r.d){var e=r.d.call(r.v);if(r.a)return a|=2,Promise.resolve(e).then(o,s)}else a|=1}catch(e){return s(e)}if(a===1)return i===t?Promise.resolve():Promise.reject(i);if(i!==t)throw i}function s(n){return i=i===t?n:new e(n,i),o()}return o()}}}t.exports=n,t.exports.__esModule=!0,t.exports.default=t.exports})),h=c(m(),1);const g=1e5,_=300,v=3e3,y=`mutex - locking timeout`,b={},x=({lifeTimeInSec:e,size:t=300})=>({max:Math.min(t,1e5),maxAge:process.env.NODE_ENV===`test`?0:1e3*e});typeof Symbol.dispose!=`symbol`&&Object.defineProperty(Symbol,`dispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.dispose`),writable:!1});const S=e=>{b[e]??=(0,u.withTimeout)(new u.Mutex,3e3,Error(`mutex - locking timeout`));let t=b[e];return Object.assign(t,{[Symbol.dispose]:()=>{t.release(),b[e]&&delete b[e]}})},C=(e,t)=>new l.default(x({lifeTimeInSec:e,size:t})),w=process.env.IS_IN_MATMON_TESTING===`true`,T=process.env.NODE_ENV===`test`&&!w,E=async(e,t)=>{try{await e()}catch(e){t.error(`Failed to set in cache`,e)}},D=async({cacheKey:e,cacheGet:t,cacheSet:n,fetching:r,skipCache:i,logger:a})=>{try{var o=(0,h.default)();if(i||T){let t=await r();return await E(()=>n(e,t),a),t}let s=null,c=o.u(S(e));try{await c.acquire(),s=await t(e),s||(s=await r(),await E(()=>n(e,s),a))}catch{s=await r(),await E(()=>n(e,s),a)}return s}catch(e){o.e=e}finally{o.d()}},O=(e,t)=>typeof e==`string`?e:e[t],k=({getFromCache:e,multiGetterFromCache:t,setInCache:n,setMultiInCache:r,getter:i,multiGetter:a,idField:o=`id`,logger:s})=>async c=>{let l=new Map(c.filter(Boolean).map(e=>[O(e,o),e])),u=new Map,d=[...l.values()],f=await(t?.(d)??Promise.all(d.map(t=>e(t))));if(f.filter(Boolean).forEach(e=>{l.delete(O(e,o)),u.set(O(e,o),e)}),l.size>0){let e=await(a?.([...l.values()])??Promise.all([...l.values()].map(e=>i(e))));if(r&&e.length>0){let t=e.reduce((e,t)=>(e[O(t,o)]=t,e),{});await E(()=>r(t),s)}e.forEach(e=>{e&&(r||E(()=>n(e[o],e),s),u.set(O(e,o),e))})}return c.map(e=>e?u.get(O(e,o)):null)},A=d.env.REDIS_HOST_NAME||`127.0.0.1`,j=Number.parseInt(d.env.REDIS_HOST_PORT||``,10)||6379,M=d.env.AF_SERVICE_NAME,N=5e3,P=1e3,F=3600,I=M+`:`;if(!M)throw Error(`SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME`);var L=class{client;locker;lockTimeout;lockDuration;logger;locks={};baseTTL;keyPrefix=I;useLock;constructor(e){this.logger=e.logger,this.client=e.client??f.default.createClient({socket:{host:e.host||A,port:e.port||j}}).on(`error`,e=>{this.logger.error(`matmon: Redis error`,{err:e})}),e.client||this.client.connect().catch(e=>{this.logger.error(`matmon: Failed to connect to Redis`,{err:e})}),this.locker=(0,p.default)(this.client,e.lockRetries??10),this.lockTimeout=e.lockTimeout??5e3,this.lockDuration=e.lockDuration??1e3,this.baseTTL=e.ttl??3600,this.useLock=!!e.useLock}get=async e=>{let t=I+e,n;try{n=await this.client.get(t)}catch(e){throw Error(`Failed to get a value`,{cause:e})}if(this.useLock){let e;try{e=await this.lock(t)}catch(e){throw Error(`Failed to lock key (${t})`,{cause:e})}this.locks[t]=e}return JSON.parse(n)};getMultiple=async e=>{let t=e.map(e=>I+e),n;try{if(t.length===0)return[];n=await this.client.mGet(t)}catch(e){throw Error(`Failed to get a value`,{cause:e})}return n.map(e=>JSON.parse(e))};set=async(e,t)=>{let n=I+e,r=parseInt(String(this.baseTTL+(Math.random()+1)),10);try{await this.client.set(n,JSON.stringify(t),{EX:r}),this.locks[n]&&(await this.locks[n](),delete this.locks[n])}catch(e){throw Error(`Failed to set a key-value pair`,{cause:e})}};setMultiple=async e=>{if(typeof e!=`object`){let e=Error(`keyValues must be an object`);throw this.logger.error(`keyValues must be an object`,{error:e}),e}if(e==null||Object.keys(e).length===0)return;let t=Object.entries(e).map(([e,t])=>[I+e,JSON.stringify(t)]),n=Math.trunc(this.baseTTL*(Math.random()+1));try{let e=this.client.multi(),r=e.mSet(t);return t.map(([t])=>e.expire(t,n)),await e.exec(),r}catch(e){throw Error(`Failed to set multiple key-value pairs`,{cause:e})}};remove=async e=>{let t=I+e;try{this.locks[t]&&(await this.locks[t](),delete this.locks[t]),await this.client.del(t)}catch(e){throw Error(`Failed to delete key ${t}`,{cause:e})}};removeMultiple=async e=>{let t=e.map(e=>I+e);try{await this.client.del(t)}catch(e){throw Error(`Failed to delete multiple keys ${t.join(`|`)}`,{cause:e})}};getClient=()=>this.client;lock(e){return Promise.race([this.locker(e,this.lockDuration),new Promise((e,t)=>setTimeout(()=>t(),this.lockTimeout))])}},R=L;exports.RedisCache=R,exports.getMultipleWithCache=k,exports.getNewLRU=C,exports.getWithCacheSupport=D;
2
2
  //# sourceMappingURL=index.cjs.map
package/lib/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Mutex","logger: LoggerInstanceManager","LRU","valueToReturn: Awaited<V> | null","RedisError","depKeys: string[]","ormInstance: Sequelize","debugMode: boolean","associationOptions: AssociationOptions","instance","RedisCache"],"sources":["../src/locking.ts","../src/logger.ts","../src/cache.ts","../src/redis/errors.ts","../src/promise-utils.ts","../src/redis/index.ts","../src/orm-cache/sequelize-adapter.ts","../src/orm-cache/errors.ts","../src/orm-cache/index.ts"],"sourcesContent":["import { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\n\nconst CACHE_LOCK_TIMEOUT_MILIS = 3000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\n\nexport const wrapWithMutex = async (mutex: MutexInterface, funcToRun: () => PromiseLike<void>): Promise<void> => {\n const release = await mutex.acquire();\n try {\n await funcToRun();\n } finally {\n release();\n }\n};\n\nexport const getMutex = (): MutexInterface => withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n","import CustomLogger, { type LoggerInstanceManager } from '@autofleet/logger';\n\nconst logger: LoggerInstanceManager = CustomLogger();\n\nexport default logger;\n","import LRU from 'lru-cache';\n\nimport * as locking from './locking';\nimport dotenv from 'dotenv';\nimport type { MutexInterface } from 'async-mutex';\nimport logger from './logger';\n\ndotenv.config();\n\nconst DEFAULT_CACHE_SIZE = 300;\nconst MAX_SIZE = 100000;\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface => {\n MUTEX_MAP[key] ??= locking.getMutex();\n return MUTEX_MAP[key];\n};\n\nconst deleteMutexByCacheKey = (key: keyof any): void => {\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU<K, V>(getOptions<K, V>({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: () => Promise<V>;\n cacheSet: (value: V) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(res));\n return res;\n }\n let valueToReturn: Awaited<V> | null = null;\n try {\n await locking.wrapWithMutex(getMutexByCacheKey(cacheKey), async () => {\n valueToReturn = await cacheGet();\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(valueToReturn!));\n } else {\n // logger.info('get value from cache');\n }\n deleteMutexByCacheKey(cacheKey);\n });\n // retry without locking if failed\n } catch {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(valueToReturn!));\n deleteMutexByCacheKey(cacheKey);\n }\n return valueToReturn!;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject));\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value));\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { RedisError } from 'redis';\n\nclass RedisCacheError extends RedisError {\n innerError: any;\n message: string;\n constructor(msg: string, err: any) {\n super();\n this.innerError = err;\n this.message = msg;\n }\n\n toString(): string {\n return `${this.message}: ${this.innerError}`;\n }\n}\n\nclass RedisLockError extends RedisCacheError {\n key: string;\n constructor(msg: string, err: any, key: string) {\n super(msg, err);\n this.key = key;\n }\n\n toString(): string {\n return `${this.message} (${this.key}): ${this.innerError}`;\n }\n}\n\nexport {\n RedisCacheError,\n RedisLockError,\n};\n","import { promisify } from 'util';\n\nexport const promisifyAll = (obj: object): void => {\n Object.keys(obj).forEach((key) => {\n if (typeof obj[key] === 'function') {\n obj[`${key}Async`] = promisify(obj[key]);\n\n // To support the `isPromisified` check in `bluebird`, we need to set the `__isPromisified__` property to `true`.\n Object.defineProperty(obj[`${key}Async`], '__isPromisified__', {\n value: true,\n configurable: true,\n enumerable: false,\n writable: true,\n });\n }\n });\n};\n","import redis from 'redis';\nimport redisLock from 'redis-lock';\nimport { promisify } from 'util';\nimport { RedisCacheError, RedisLockError } from './errors';\nimport logger from '../logger';\nimport { promisifyAll } from '../promise-utils';\n\npromisifyAll(redis.RedisClient.prototype);\npromisifyAll(redis.Multi.prototype);\n\nconst { env } = process;\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = env.REDIS_HOST_PORT || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5000;\nconst DEFAULT_LOCK_DURATION = 1000;\nconst DEFAULT_BASE_TTL = 3600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n lockTimeout?: number;\n lockDuration?: number;\n ttl?: number;\n useLock?: boolean;\n}\n\nclass RedisCache {\n // node redis client instance.\n private client: any;\n\n // A redis-lock instance.\n private locker: (key: string, duration: number) => Promise<() => Promise<void>>;\n\n // The time that the cache will wait until a lock\n // will be considered as failed (in milliseconds).\n private lockTimeout: number;\n\n // The time that a key will be locked.\n private lockDuration: number;\n\n // A dictionary of key and its lock.\n locks: Record<string, () => Promise<void> | undefined> = {};\n\n // The time that a key-value pair will exist in redis.\n // To this base time will be added between 1 to 2 milliseconds.\n private baseTTL: number;\n\n // The number of retries that we will try to lock a key in case of failure.\n private lockRetries: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.client = redis.createClient({\n host: options.host || HOST,\n port: options.port || PORT,\n });\n this.locker = promisify(redisLock(this.client, options.lockRetries ?? 10));\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n async get<T = any>(key: string): Promise<T> {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.getAsync(keyWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to get a value', err);\n }\n\n if (this.useLock) {\n let lock;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisLockError('Failed to lock key', err, keyWithPrefix);\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n }\n\n async getMultiple<T = any>(keys: string[]): Promise<T[]> {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mgetAsync(keysWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to get a value', err);\n }\n return values.map(value => JSON.parse(value));\n }\n\n async set<T = any>(key: string, value: T): Promise<void> {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL * (Math.random() + 1)), 10);\n try {\n await this.client.setAsync(keyWithPrefix, JSON.stringify(value), 'EX', ttl);\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to set a key-value pair', err);\n }\n }\n\n async setMultiple<T = any>(keyValues: Record<string, T>): Promise<void> {\n if (typeof keyValues !== 'object') {\n const error = new RedisCacheError('keyValues must be an object', new Error('keyValues must be an object'));\n logger.error('keyValues must be an object', { error });\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.msetAsync(...keyValuesWithPrefix.flat());\n keyValuesWithPrefix.map(([key]) => multi.expireAsync(key, ttl));\n await multi.exec();\n return setPromise;\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to set multiple key-value pairs', err);\n }\n }\n\n async remove(key: string): Promise<void> {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.delAsync(keyWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError(`Failed to delete key ${keyWithPrefix}`, err);\n }\n }\n\n async removeMultiple(keys: string[]): Promise<void> {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.delAsync(keysWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, err);\n }\n }\n\n getClient(): any {\n return this.client;\n }\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n","import type { Attributes, Model, ModelStatic, NonNullFindOptions, Sequelize } from 'sequelize';\nimport type { Adapter, AssociationOptions, ModelOptions } from './adapter';\nimport type RedisCache from '../redis';\nimport logger from '../logger';\nimport { promisify } from 'util';\n\nconst { AF_SERVICE_NAME } = process.env;\nconst ORM_CACHE_PREFIX = 'ormCache';\nconst INVALIDATION_HOOKS = ['afterSave', 'afterUpdate', 'afterDestroy'] as const;\nconst BULK_HOOKS = ['beforeBulkUpdate', 'beforeBulkDestroy'] as const;\n\nconst generateInstanceKey = (name: string, id: string): string =>\n `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${name}_${id}`;\n\nconst generateDependencyKey = (modelName: string, associationName: string, associationId: string): string =>\n `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${modelName}_${associationName}_${associationId}_DEPENDENCIES`;\n\nconst getInstanceDependencyKeys = (modelOptions: ModelOptions, instance): string[] => {\n const keys = modelOptions.associations!\n .filter((associationOptions: AssociationOptions) => instance[associationOptions.alias])\n .map((associationOptions: AssociationOptions): string[] => {\n const accessKey = associationOptions.accessKey!;\n const depKeys: string[] = [];\n if (Array.isArray(instance[associationOptions.alias])) {\n instance[associationOptions.alias].forEach((associationInstance) => {\n depKeys.push(generateDependencyKey(\n modelOptions.name,\n associationOptions.name,\n associationInstance[accessKey],\n ));\n if (associationOptions.innerAssociation && associationInstance[associationOptions.innerAssociation.alias]) {\n depKeys.push(generateDependencyKey(\n modelOptions.name,\n associationOptions.innerAssociation.name,\n associationInstance[associationOptions.innerAssociation.alias][associationOptions.innerAssociation.accessKey!],\n ));\n }\n });\n return depKeys;\n }\n return [\n generateDependencyKey(\n modelOptions.name,\n associationOptions.name,\n instance[associationOptions.alias][accessKey],\n ),\n ];\n });\n return keys.reduce((flattenArray, array) => flattenArray.concat(array), []);\n};\n\nconst handleTransactionHook = (instance, options, func) => {\n const { transaction } = options;\n if (transaction) {\n transaction.afterCommit(() => func(instance));\n } else {\n func(instance);\n }\n};\n\ndeclare module 'sequelize' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Model {\n function findByPkCached<M extends Model>(\n this: ModelStatic<M>,\n identifier: string,\n scopes: string[],\n options: Omit<NonNullFindOptions<Attributes<M>>, 'where'>\n ): Promise<M>;\n }\n}\n\nexport default class SequelizeAdapter implements Adapter {\n constructor(public ormInstance: Sequelize, public debugMode: boolean) {\n }\n\n getModel(modelName: string): ModelStatic<Model> {\n return this.ormInstance.models[modelName];\n }\n\n getModelDependencies(modelName: string): AssociationOptions[] {\n const { associations } = this.ormInstance.models[modelName];\n return [\n ...Object.keys(associations).map((association) => {\n const sequelizeAssociation = associations[association];\n const associationOptions: AssociationOptions = {\n alias: sequelizeAssociation.as,\n name: sequelizeAssociation.target.name,\n accessKey: sequelizeAssociation.target.primaryKeyAttribute,\n };\n\n // @ts-expect-error through is not defined in the typings\n const { through } = sequelizeAssociation;\n if (through) {\n const relationModel = through.model;\n associationOptions.innerAssociation = {\n alias: relationModel.name,\n name: relationModel.name,\n accessKey: relationModel.primaryKeyAttribute,\n };\n }\n\n return associationOptions;\n }),\n ];\n }\n\n debug(message: string, payload?: unknown): void {\n if (this.debugMode) {\n logger.info(`[ORM_CACHE Debug] ${message}`, payload);\n }\n }\n\n injectGetWithCacheFunction(cache: RedisCache, modelOptions: ModelOptions): void {\n const addDependencies = async (instance) => {\n const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);\n const instanceKey = generateInstanceKey(modelOptions.name, instance.id);\n this.debug('Adding dependencies', { instanceKey, dependencyKeys });\n const addDependenciesMulti = cache.getClient().multi();\n const addDependenciesMultiAsync = promisify(addDependenciesMulti.exec).bind(addDependenciesMulti);\n dependencyKeys.reduce((multi, key) => multi.sadd(key, instanceKey), addDependenciesMulti);\n return addDependenciesMultiAsync();\n };\n\n const model = this.getModel(modelOptions.name);\n model.findByPkCached = async <M extends Model>(id: string, scopes: string[], options: Omit<NonNullFindOptions<Attributes<M>>, 'where'>): Promise<M> => {\n const cacheKey = generateInstanceKey(modelOptions.name, id);\n let value = JSON.parse(await cache.getClient().getAsync(cacheKey));\n if (!value) {\n this.debug('Value not found in cache, looking in db', { id, cacheKey });\n value = await model.scope(scopes).findByPk(id, options);\n this.debug('Value from DB', { value: value || 'not found', cacheKey });\n await Promise.all([\n cache.getClient().setAsync(cacheKey, JSON.stringify(value)),\n value && addDependencies(value),\n ]);\n } else {\n value = this.getModel(modelOptions.name).build(value, { isNewRecord: false, include: options.include });\n this.debug('Found cached value', { value, id, cacheKey });\n }\n return value;\n };\n }\n\n addInvalidationHooks(cache: RedisCache, modelOptions: ModelOptions): void {\n const invalidateModelInstance = async (instance) => {\n const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);\n const instanceKey = generateInstanceKey(modelOptions.name, instance.id);\n this.debug('Removing dependencies', { instance, instanceKey, dependencyKeys });\n const removeMulti = cache.getClient().multi();\n const removeMultiAsync = promisify(removeMulti.exec).bind(removeMulti);\n dependencyKeys.map(key => removeMulti.srem(key, instanceKey));\n removeMulti.del(instanceKey);\n return removeMultiAsync();\n };\n\n const invalidateModelInstanceByAssociation = async (association, associationId) => {\n const dependentInstancesKeys = await cache.getClient().smembersAsync(generateDependencyKey(\n modelOptions.name,\n association,\n associationId,\n ));\n this.debug('Invalidating dependent instances', { dependentInstancesKeys });\n const removeMulti = cache.getClient().multi();\n const removeMultiAsync = promisify(removeMulti.exec).bind(removeMulti);\n const dependenciesToRemove = await Promise.all(dependentInstancesKeys.map(async (instanceKey) => {\n const instance = JSON.parse(await cache.getClient().getAsync(instanceKey));\n if (!instance) {\n return [];\n }\n const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);\n dependencyKeys.reduce((multi, key) => multi.srem(key, instanceKey), removeMulti);\n removeMulti.del(instanceKey);\n return dependencyKeys;\n }));\n this.debug('Removing dependencies', { dependentInstancesKeys, dependenciesToRemove });\n return removeMultiAsync();\n };\n\n const model = this.getModel(modelOptions.name);\n INVALIDATION_HOOKS.map(hook => model.addHook(hook, (instance, options) =>\n handleTransactionHook(instance, options, instance => invalidateModelInstance(instance))));\n BULK_HOOKS.map(hook => model.addHook(hook, (options) => {\n options.individualHook = true;\n }));\n\n modelOptions.associations = this.getModelDependencies(modelOptions.name);\n this.debug(`Adding Invalidations Hooks to ${modelOptions.name}'s associations`, { associations: modelOptions.associations });\n modelOptions.associations.forEach((associationOptions) => {\n const associationModel = this.getModel(associationOptions.name);\n INVALIDATION_HOOKS.forEach(hook => associationModel.addHook(hook, (instance, options) =>\n handleTransactionHook(\n instance,\n options,\n associationInstance => invalidateModelInstanceByAssociation(\n associationOptions.name,\n associationInstance[associationOptions.accessKey!],\n ),\n ),\n ));\n BULK_HOOKS.forEach(hook => associationModel.addHook(hook, (options) => {\n options.individualHook = true;\n }));\n\n if (associationOptions.innerAssociation) {\n const innerAssociationModel = this.getModel(associationOptions.innerAssociation.name);\n INVALIDATION_HOOKS.forEach(hook => innerAssociationModel.addHook(hook, (instance, options) =>\n handleTransactionHook(\n instance,\n options,\n innerAssociationInstance => invalidateModelInstanceByAssociation(\n associationOptions.innerAssociation!.name,\n innerAssociationInstance[associationOptions.innerAssociation!.accessKey!],\n ),\n ),\n ));\n BULK_HOOKS.map(hook => innerAssociationModel.addHook(hook, (options) => {\n options.individualHook = true;\n }));\n }\n });\n }\n}\n","export class UnsupportedOrmTypeError extends Error {}\n","import type { Adapter, ModelOptions } from './adapter';\nimport SequelizeAdapter from './sequelize-adapter';\nimport { UnsupportedOrmTypeError } from './errors';\nimport RedisCache from '../redis';\nimport logger from '../logger';\n\nexport enum ORMTypes {\n SEQUELIZE = 'sequelize',\n}\n\ninterface ORMCacheOptions {\n type: ORMTypes;\n models: ModelOptions[];\n ormInstance: any;\n debug: boolean;\n}\n\nconst ORMInstanceFactory = (options: ORMCacheOptions): Adapter => {\n switch (options.type) {\n case ORMTypes.SEQUELIZE: {\n return new SequelizeAdapter(options.ormInstance, options.debug);\n }\n default: {\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n throw new UnsupportedOrmTypeError(`ORM type ${options.type} is unsupported at the moment`);\n }\n }\n};\n\nexport const ORMCache = (options: ORMCacheOptions): void => {\n const { models } = options;\n logger.info('Starting ORM Cache', { options });\n const adapter = ORMInstanceFactory(options);\n const cache = new RedisCache({\n host: process.env.REDIS_HOST,\n port: Number.parseInt(process.env.REDIS_PORT!, 10),\n });\n // eslint-disable-next-line array-callback-return\n models.map((modelOptions): void => {\n adapter.addInvalidationHooks(cache, modelOptions);\n adapter.injectGetWithCacheFunction(cache, modelOptions);\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["n","r","e","o","Mutex","LRU","valueToReturn: V | null","env","lock: Awaited<ReturnType<typeof this.lock>> | undefined"],"sources":["../../../node_modules/@oxc-project/runtime/src/helpers/usingCtx.js","../src/cache.ts","../src/redis/index.ts"],"sourcesContent":["function _usingCtx() {\n var r = \"function\" == typeof SuppressedError ? SuppressedError : function (r, e) {\n var n = Error();\n return n.name = \"SuppressedError\", n.error = r, n.suppressed = e, n;\n },\n e = {},\n n = [];\n function using(r, e) {\n if (null != e) {\n if (Object(e) !== e) throw new TypeError(\"using declarations can only be used with objects, functions, null, or undefined.\");\n if (r) var o = e[Symbol.asyncDispose || Symbol[\"for\"](\"Symbol.asyncDispose\")];\n if (void 0 === o && (o = e[Symbol.dispose || Symbol[\"for\"](\"Symbol.dispose\")], r)) var t = o;\n if (\"function\" != typeof o) throw new TypeError(\"Object is not disposable.\");\n t && (o = function o() {\n try {\n t.call(e);\n } catch (r) {\n return Promise.reject(r);\n }\n }), n.push({\n v: e,\n d: o,\n a: r\n });\n } else r && n.push({\n d: e,\n a: r\n });\n return e;\n }\n return {\n e: e,\n u: using.bind(null, !1),\n a: using.bind(null, !0),\n d: function d() {\n var o,\n t = this.e,\n s = 0;\n function next() {\n for (; o = n.pop();) try {\n if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);\n if (o.d) {\n var r = o.d.call(o.v);\n if (o.a) return s |= 2, Promise.resolve(r).then(next, err);\n } else s |= 1;\n } catch (r) {\n return err(r);\n }\n if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();\n if (t !== e) throw t;\n }\n function err(n) {\n return t = t !== e ? new r(n, t) : n, next();\n }\n return next();\n }\n };\n}\nmodule.exports = _usingCtx, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","import LRU from 'lru-cache';\nimport { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\nimport type { LoggerInstanceManager } from '@autofleet/logger/src';\n\nconst MAX_SIZE = 100_000;\nconst DEFAULT_CACHE_SIZE = 300;\nconst CACHE_LOCK_TIMEOUT_MILIS = 3_000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface & { [Symbol.dispose]: () => void; } => {\n MUTEX_MAP[key] ??= withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n const mutex = MUTEX_MAP[key];\n return Object.assign(mutex, {\n [Symbol.dispose]: () => {\n mutex.release();\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n },\n });\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU<K, V>(getOptions<K, V>({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;\n cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n logger: LoggerInstanceManager;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>, logger: LoggerInstanceManager): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n logger,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, res), logger);\n return res;\n }\n let valueToReturn: V | null = null;\n using mutex = getMutexByCacheKey(cacheKey);\n try {\n await mutex.acquire();\n valueToReturn = await cacheGet(cacheKey);\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n } else {\n // logger.info('get value from cache');\n }\n } catch {\n // retry without locking if failed\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n }\n return valueToReturn;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n logger: LoggerInstanceManager;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n logger,\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject), logger);\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value), logger);\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { env } from 'node:process';\nimport redis from 'redis';\nimport redisLock from 'redis-lock';\nimport type { LoggerInstanceManager } from '@autofleet/logger/src';\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = Number.parseInt(env.REDIS_HOST_PORT || '', 10) || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5_000;\nconst DEFAULT_LOCK_DURATION = 1_000;\nconst DEFAULT_BASE_TTL = 3_600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */\n lockTimeout?: number;\n /** The time that a key will be locked. */\n lockDuration?: number;\n /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */\n ttl?: number;\n /** @default false */\n useLock?: boolean;\n logger: LoggerInstanceManager;\n /** Redis client to allow reusing connections. */\n client?: ReturnType<typeof redis.createClient>;\n}\n\nclass RedisCache {\n private readonly client: ReturnType<typeof redis.createClient>;\n private readonly locker: ReturnType<typeof redisLock>;\n private readonly lockTimeout: number;\n private readonly lockDuration: number;\n private readonly logger: LoggerInstanceManager;\n\n // A dictionary of key and its lock.\n locks: Record<string, Awaited<ReturnType<typeof this.lock>> | undefined> = {};\n\n private readonly baseTTL: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.logger = options.logger;\n this.client = options.client ?? redis.createClient({\n socket: {\n host: options.host || HOST,\n port: options.port || PORT,\n },\n }).on('error', (err) => {\n this.logger.error('matmon: Redis error', { err });\n });\n if (!options.client) {\n this.client.connect().catch((err) => {\n this.logger.error('matmon: Failed to connect to Redis', { err });\n });\n }\n this.locker = redisLock(this.client, options.lockRetries ?? 10);\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n public readonly get = async <T = any>(key: string): Promise<T> => {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.get(keyWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n\n if (this.useLock) {\n let lock: Awaited<ReturnType<typeof this.lock>> | undefined;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to lock key (${keyWithPrefix})`, { cause: err });\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n };\n\n public readonly getMultiple = async <T = any>(keys: string[]): Promise<T[]> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mGet(keysWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n return values.map(value => JSON.parse(value));\n };\n\n public readonly set = async <T = any>(key: string, value: T): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL + (Math.random() + 1)), 10);\n try {\n await this.client.set(keyWithPrefix, JSON.stringify(value), { EX: ttl });\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n throw new Error('Failed to set a key-value pair', { cause: err });\n }\n };\n\n public readonly setMultiple = async <T = any>(keyValues: Record<string, T>): Promise<void> => {\n if (typeof keyValues !== 'object') {\n const error = new Error('keyValues must be an object');\n this.logger.error('keyValues must be an object', { error });\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map<[string, string]>(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.mSet(keyValuesWithPrefix);\n keyValuesWithPrefix.map(([key]) => multi.expire(key, ttl));\n await multi.exec();\n // @ts-expect-error we are returning a value although the type definition says void\n return setPromise;\n } catch (err) {\n throw new Error('Failed to set multiple key-value pairs', { cause: err });\n }\n };\n\n public readonly remove = async (key: string): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.del(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete key ${keyWithPrefix}`, { cause: err });\n }\n };\n\n public readonly removeMultiple = async (keys: string[]): Promise<void> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.del(keysWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, { cause: err });\n }\n };\n\n public readonly getClient = (): typeof this.client => this.client;\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n"],"x_google_ignoreList":[0],"mappings":"yrBAAA,SAAS,GAAY,CACnB,IAAI,EAAkB,OAAO,iBAArB,WAAuC,gBAAkB,SAAU,EAAG,EAAG,CAC7E,IAAIA,EAAI,QACR,MAAO,GAAE,KAAO,kBAAmB,EAAE,MAAQC,EAAG,EAAE,WAAaC,EAAGF,GAEpE,EAAI,GACJ,EAAI,GACN,SAAS,EAAM,EAAG,EAAG,CACnB,GAAYE,GAAR,KAAW,CACb,GAAI,OAAOA,KAAOA,EAAG,MAAU,UAAU,oFACzC,GAAID,EAAG,IAAI,EAAIC,EAAE,OAAO,cAAgB,OAAO,IAAO,wBACtD,GAAe,IAAX,IAAK,KAAY,EAAIA,EAAE,OAAO,SAAW,OAAO,IAAO,mBAAoBD,GAAI,IAAI,EAAI,EAC3F,GAAkB,OAAO,GAArB,WAAwB,MAAU,UAAU,6BAChD,IAAM,EAAI,UAAa,CACrB,GAAI,CACF,EAAE,KAAKC,SACAD,EAAG,CACV,OAAO,QAAQ,OAAOA,MAEtB,EAAE,KAAK,CACT,EAAGC,EACH,EAAG,EACH,EAAGD,SAEA,GAAK,EAAE,KAAK,CACjB,EAAGC,EACH,EAAGD,IAEL,OAAOC,EAET,MAAO,CACF,IACH,EAAG,EAAM,KAAK,KAAM,CAAC,GACrB,EAAG,EAAM,KAAK,KAAM,CAAC,GACrB,EAAG,UAAa,CACd,IAAI,EACF,EAAI,KAAK,EACT,EAAI,EACN,SAAS,GAAO,CACd,KAAO,EAAI,EAAE,OAAQ,GAAI,CACvB,GAAI,CAAC,EAAE,GAAW,IAAN,EAAS,MAAO,GAAI,EAAG,EAAE,KAAK,GAAI,QAAQ,UAAU,KAAK,GACrE,GAAI,EAAE,EAAG,CACP,IAAID,EAAI,EAAE,EAAE,KAAK,EAAE,GACnB,GAAI,EAAE,EAAG,MAAO,IAAK,EAAG,QAAQ,QAAQA,GAAG,KAAK,EAAM,QACjD,GAAK,QACLA,EAAG,CACV,OAAO,EAAIA,GAEb,GAAU,IAAN,EAAS,OAAO,IAAM,EAAwB,QAAQ,UAA5B,QAAQ,OAAO,GAC7C,GAAI,IAAM,EAAG,MAAM,EAErB,SAAS,EAAI,EAAG,CACd,MAAO,GAAI,IAAM,EAAkBD,EAAd,IAAI,EAAEA,EAAG,GAAQ,IAExC,OAAO,MAIb,EAAO,QAAU,EAAW,EAAO,QAAQ,WAAa,GAAM,EAAO,QAAQ,QAAa,EAAO,sBCtDjG,MAAM,EAAW,IACX,EAAqB,IACrB,EAA2B,IAC3B,EAAuB,0BACvB,EAAY,GAOZ,GAAoB,CACxB,gBACA,OAAO,QAC0B,CACjC,IAAK,KAAK,IAAI,EAAM,KACpB,OAAQ,QAAQ,IAAI,WAAa,OAAgC,EAAvB,IAAO,IAG/C,OAAO,OAAO,SAAY,UAE5B,OAAO,eAAe,OAAQ,UAAW,CAEvC,UAAW,KACX,aAAc,GACd,WAAY,GACZ,MAAO,OAAO,IAAI,kBAClB,SAAU,KAId,MAAM,EAAsB,GAAuE,CACjG,EAAU,MAAA,EAAA,EAAA,aAAqB,IAAII,EAAAA,MAAS,IAA8B,MAAM,4BAChF,IAAM,EAAQ,EAAU,GACxB,OAAO,OAAO,OAAO,EAAO,EACzB,OAAO,aAAgB,CACtB,EAAM,UACF,EAAU,IACZ,OAAO,EAAU,OAMZ,GAAuC,EAAyC,IAAsC,IAAIC,EAAAA,QAAU,EAAiB,CAChK,gBACA,UAYI,EAAgB,QAAQ,IAAI,uBAAyB,OACrD,EAAqB,QAAQ,IAAI,WAAa,QAAU,CAAC,EAEzD,EAAkB,MAAO,EAA0C,IAAiD,CACxH,GAAI,CACF,MAAM,UACC,EAAG,CACV,EAAO,MAAM,yBAA0B,KAI9B,EAAsB,MAAoB,CACrD,WACA,WACA,WACA,WACA,YACA,YACwC,2BACxC,GAAI,GAAa,EAAoB,CACnC,IAAM,EAAM,MAAM,IAElB,OADA,MAAM,MAAsB,EAAS,EAAU,GAAM,GAC9C,EAET,IAAIC,EAA0B,KACxB,EAAA,EAAA,EAAQ,EAAmB,IACjC,GAAI,CACF,MAAM,EAAM,UACZ,EAAgB,MAAM,EAAS,GAC1B,IACH,EAAgB,MAAM,IACtB,MAAM,MAAsB,EAAS,EAAU,GAAiB,SAI5D,CAEN,EAAgB,MAAM,IACtB,MAAM,MAAsB,EAAS,EAAU,GAAiB,GAElE,OAAO,iCAGH,GAAc,EAAwB,IACtC,OAAO,GAAU,SACZ,EAEF,EAAM,GAcF,GAAqC,CAChD,eACA,uBACA,aACA,kBACA,SACA,cACA,UAAU,KACV,YAC2F,KAAO,IAAY,CAC9G,IAAM,EAAa,IAAI,IAAI,EAAQ,OAAO,SAAS,IAAI,GAAS,CAAC,EAAW,EAAO,GAAU,KACvF,EAAY,IAAI,IAEhB,EAAwB,CAAC,GAAG,EAAW,UAEvC,EAAkB,MACtB,IAAuB,IACpB,QAAQ,IAAI,EAAsB,IAAI,GAAS,EAAc,MAQlE,GALA,EAAgB,OAAO,SAAS,QAAS,GAAU,CACjD,EAAW,OAAO,EAAW,EAAiB,IAC9C,EAAU,IAAI,EAAW,EAAiB,GAAU,KAGlD,EAAW,KAAO,EAAG,CACvB,IAAM,EAAmB,MACvB,IAAc,CAAC,GAAG,EAAW,YAC1B,QAAQ,IAAI,CAAC,GAAG,EAAW,UAAU,IAAI,GAAM,EAAQ,MAG5D,GAAI,GAAmB,EAAiB,OAAS,EAAG,CAClD,IAAM,EAAiB,EAAiB,QAAQ,EAAK,KACnD,EAAI,EAAW,EAAiB,IAAY,EACrC,GACN,IACH,MAAM,MAAsB,EAAgB,GAAiB,GAG/D,EAAiB,QAAS,GAAU,CAC7B,IAGA,GACE,MAAsB,EAAW,EAAM,GAAU,GAAQ,GAEhE,EAAU,IAAI,EAAW,EAAiB,GAAU,MAIxD,OAAO,EAAQ,IAAK,GACb,EAGE,EAAU,IAAI,EAAW,EAAO,IAF9B,OC1KP,EAAOC,EAAAA,IAAI,iBAAmB,YAC9B,EAAO,OAAO,SAASA,EAAAA,IAAI,iBAAmB,GAAI,KAAO,KACzD,EAAeA,EAAAA,IAAI,gBACnB,EAAuB,IACvB,EAAwB,IACxB,EAAmB,KACnB,EAAa,EAAe,IAElC,GAAI,CAAC,EACH,MAAU,MAAM,sEAoBlB,IAAM,EAAN,KAAiB,CACf,OACA,OACA,YACA,aACA,OAGA,MAA2E,GAE3E,QAEA,UAAoB,EAEpB,QAEA,YAAY,EAA4B,CACtC,KAAK,OAAS,EAAQ,OACtB,KAAK,OAAS,EAAQ,QAAU,EAAA,QAAM,aAAa,CACjD,OAAQ,CACN,KAAM,EAAQ,MAAQ,EACtB,KAAM,EAAQ,MAAQ,KAEvB,GAAG,QAAU,GAAQ,CACtB,KAAK,OAAO,MAAM,sBAAuB,CAAE,UAExC,EAAQ,QACX,KAAK,OAAO,UAAU,MAAO,GAAQ,CACnC,KAAK,OAAO,MAAM,qCAAsC,CAAE,UAG9D,KAAK,QAAA,EAAA,EAAA,SAAmB,KAAK,OAAQ,EAAQ,aAAe,IAC5D,KAAK,YAAc,EAAQ,aAAe,IAC1C,KAAK,aAAe,EAAQ,cAAgB,IAC5C,KAAK,QAAU,EAAQ,KAAO,KAC9B,KAAK,QAAU,CAAC,CAAC,EAAQ,QAG3B,IAAsB,KAAgB,IAA4B,CAChE,IAAM,EAAgB,EAAa,EAE/B,EACJ,GAAI,CAEF,EAAQ,MAAM,KAAK,OAAO,IAAI,SACvB,EAAK,CACZ,MAAU,MAAM,wBAAyB,CAAE,MAAO,IAGpD,GAAI,KAAK,QAAS,CAChB,IAAIC,EACJ,GAAI,CAEF,EAAO,MAAM,KAAK,KAAK,SAChB,EAAK,CACZ,MAAU,MAAM,uBAAuB,EAAc,GAAI,CAAE,MAAO,IAIpE,KAAK,MAAM,GAAiB,EAG9B,OAAO,KAAK,MAAM,IAGpB,YAA8B,KAAgB,IAAiC,CAC7E,IAAM,EAAiB,EAAK,IAAI,GAAO,EAAa,GAChD,EACJ,GAAI,CACF,GAAI,EAAe,SAAW,EAC5B,MAAO,GAGT,EAAS,MAAM,KAAK,OAAO,KAAK,SACzB,EAAK,CACZ,MAAU,MAAM,wBAAyB,CAAE,MAAO,IAEpD,OAAO,EAAO,IAAI,GAAS,KAAK,MAAM,KAGxC,IAAsB,MAAgB,EAAa,IAA4B,CAC7E,IAAM,EAAgB,EAAa,EAC7B,EAAM,SAAS,OAAO,KAAK,SAAW,KAAK,SAAW,IAAK,IACjE,GAAI,CACF,MAAM,KAAK,OAAO,IAAI,EAAe,KAAK,UAAU,GAAQ,CAAE,GAAI,IAC9D,KAAK,MAAM,KACb,MAAM,KAAK,MAAM,KACjB,OAAO,KAAK,MAAM,UAEb,EAAK,CACZ,MAAU,MAAM,iCAAkC,CAAE,MAAO,MAI/D,YAA8B,KAAgB,IAAgD,CAC5F,GAAI,OAAO,GAAc,SAAU,CACjC,IAAM,EAAY,MAAM,+BAExB,MADA,KAAK,OAAO,MAAM,8BAA+B,CAAE,UAC7C,EAGR,GAAI,GAAc,MAAmC,OAAO,KAAK,GAAW,SAAW,EACrF,OAGF,IAAM,EAAsB,OAAO,QAAQ,GAAW,KAAuB,CAAC,EAAK,KAAW,CAAC,EAAa,EAAK,KAAK,UAAU,KAC1H,EAAM,KAAK,MAAM,KAAK,SAAW,KAAK,SAAW,IACvD,GAAI,CACF,IAAM,EAAQ,KAAK,OAAO,QACpB,EAAa,EAAM,KAAK,GAI9B,OAHA,EAAoB,KAAK,CAAC,KAAS,EAAM,OAAO,EAAK,IACrD,MAAM,EAAM,OAEL,QACA,EAAK,CACZ,MAAU,MAAM,yCAA0C,CAAE,MAAO,MAIvE,OAAyB,KAAO,IAA+B,CAC7D,IAAM,EAAgB,EAAa,EACnC,GAAI,CACE,KAAK,MAAM,KACb,MAAM,KAAK,MAAM,KACjB,OAAO,KAAK,MAAM,IAGpB,MAAM,KAAK,OAAO,IAAI,SACf,EAAK,CACZ,MAAU,MAAM,wBAAwB,IAAiB,CAAE,MAAO,MAItE,eAAiC,KAAO,IAAkC,CACxE,IAAM,EAAiB,EAAK,IAAI,GAAO,EAAa,GACpD,GAAI,CACF,MAAM,KAAK,OAAO,IAAI,SACf,EAAK,CACZ,MAAU,MAAM,kCAAkC,EAAe,KAAK,OAAQ,CAAE,MAAO,MAI3F,cAAsD,KAAK,OAE3D,KAAa,EAA2C,CACtD,OAAO,QAAQ,KAAK,CAClB,KAAK,OAAO,EAAK,KAAK,cAEtB,IAAI,SAAgB,EAAU,IAAW,eAAiB,IAAU,KAAK,kBAK/E,EAAe"}
package/lib/index.d.cts CHANGED
@@ -1,4 +1,6 @@
1
1
  import LRU from "lru-cache";
2
+ import { LoggerInstanceManager } from "@autofleet/logger/src";
3
+ import redis from "redis";
2
4
 
3
5
  //#region src/cache.d.ts
4
6
  interface Options {
@@ -8,17 +10,19 @@ interface Options {
8
10
  declare const getNewLRU: <K = unknown, V = unknown>(lifeTimeInSec: Options["lifeTimeInSec"], size?: Options["size"]) => LRU<K, V>;
9
11
  interface GetWithCacheOptions<V = unknown> {
10
12
  cacheKey: string;
11
- cacheGet: () => Promise<V>;
12
- cacheSet: (value: V) => Promise<void>;
13
+ cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;
14
+ cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;
13
15
  fetching: () => Promise<V>;
14
16
  skipCache?: boolean;
17
+ logger: LoggerInstanceManager;
15
18
  }
16
19
  declare const getWithCacheSupport: <V = unknown>({
17
20
  cacheKey,
18
21
  cacheGet,
19
22
  cacheSet,
20
23
  fetching,
21
- skipCache
24
+ skipCache,
25
+ logger
22
26
  }: GetWithCacheOptions<V>) => Promise<V>;
23
27
  interface GetMultipleWithCacheOptions<V = unknown> {
24
28
  getFromCache?: (query: any) => Promise<V>;
@@ -28,6 +32,7 @@ interface GetMultipleWithCacheOptions<V = unknown> {
28
32
  multiGetter?: (queries: any[]) => Promise<V[]>;
29
33
  setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;
30
34
  idField?: string;
35
+ logger: LoggerInstanceManager;
31
36
  }
32
37
  declare const getMultipleWithCache: <V = unknown>({
33
38
  getFromCache,
@@ -36,7 +41,8 @@ declare const getMultipleWithCache: <V = unknown>({
36
41
  setMultiInCache,
37
42
  getter,
38
43
  multiGetter,
39
- idField
44
+ idField,
45
+ logger
40
46
  }: GetMultipleWithCacheOptions<V>) => (queries: any[]) => Promise<(V | null | undefined)[]>;
41
47
  //#endregion
42
48
  //#region src/redis/index.d.ts
@@ -44,55 +50,38 @@ interface RedisCacheOptions {
44
50
  host?: string;
45
51
  port?: number;
46
52
  lockRetries?: number;
53
+ /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */
47
54
  lockTimeout?: number;
55
+ /** The time that a key will be locked. */
48
56
  lockDuration?: number;
57
+ /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */
49
58
  ttl?: number;
59
+ /** @default false */
50
60
  useLock?: boolean;
61
+ logger: LoggerInstanceManager;
62
+ /** Redis client to allow reusing connections. */
63
+ client?: ReturnType<typeof redis.createClient>;
51
64
  }
52
65
  declare class RedisCache {
53
- private client;
54
- private locker;
55
- private lockTimeout;
56
- private lockDuration;
57
- locks: Record<string, () => Promise<void> | undefined>;
58
- private baseTTL;
59
- private lockRetries;
66
+ private readonly client;
67
+ private readonly locker;
68
+ private readonly lockTimeout;
69
+ private readonly lockDuration;
70
+ private readonly logger;
71
+ locks: Record<string, Awaited<ReturnType<typeof (void 0).lock>> | undefined>;
72
+ private readonly baseTTL;
60
73
  keyPrefix: string;
61
74
  useLock: boolean;
62
75
  constructor(options: RedisCacheOptions);
63
- get<T = any>(key: string): Promise<T>;
64
- getMultiple<T = any>(keys: string[]): Promise<T[]>;
65
- set<T = any>(key: string, value: T): Promise<void>;
66
- setMultiple<T = any>(keyValues: Record<string, T>): Promise<void>;
67
- remove(key: string): Promise<void>;
68
- removeMultiple(keys: string[]): Promise<void>;
69
- getClient(): any;
76
+ readonly get: <T = any>(key: string) => Promise<T>;
77
+ readonly getMultiple: <T = any>(keys: string[]) => Promise<T[]>;
78
+ readonly set: <T = any>(key: string, value: T) => Promise<void>;
79
+ readonly setMultiple: <T = any>(keyValues: Record<string, T>) => Promise<void>;
80
+ readonly remove: (key: string) => Promise<void>;
81
+ readonly removeMultiple: (keys: string[]) => Promise<void>;
82
+ readonly getClient: () => typeof (void 0).client;
70
83
  private lock;
71
84
  }
72
85
  //#endregion
73
- //#region src/orm-cache/adapter.d.ts
74
- interface ModelOptions {
75
- name: string;
76
- associations?: AssociationOptions[];
77
- }
78
- interface AssociationOptions {
79
- name: string;
80
- alias: string;
81
- accessKey?: string;
82
- innerAssociation?: AssociationOptions;
83
- }
84
- //#endregion
85
- //#region src/orm-cache/index.d.ts
86
- declare enum ORMTypes {
87
- SEQUELIZE = "sequelize",
88
- }
89
- interface ORMCacheOptions {
90
- type: ORMTypes;
91
- models: ModelOptions[];
92
- ormInstance: any;
93
- debug: boolean;
94
- }
95
- declare const ORMCache: (options: ORMCacheOptions) => void;
96
- //#endregion
97
- export { ORMCache, ORMTypes, RedisCache, getMultipleWithCache, getNewLRU, getWithCacheSupport };
86
+ export { RedisCache, getMultipleWithCache, getNewLRU, getWithCacheSupport };
98
87
  //# sourceMappingURL=index.d.cts.map
package/lib/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import LRU from "lru-cache";
2
+ import redis from "redis";
3
+ import { LoggerInstanceManager } from "@autofleet/logger/src";
2
4
 
3
5
  //#region src/cache.d.ts
4
6
  interface Options {
@@ -8,17 +10,19 @@ interface Options {
8
10
  declare const getNewLRU: <K = unknown, V = unknown>(lifeTimeInSec: Options["lifeTimeInSec"], size?: Options["size"]) => LRU<K, V>;
9
11
  interface GetWithCacheOptions<V = unknown> {
10
12
  cacheKey: string;
11
- cacheGet: () => Promise<V>;
12
- cacheSet: (value: V) => Promise<void>;
13
+ cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;
14
+ cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;
13
15
  fetching: () => Promise<V>;
14
16
  skipCache?: boolean;
17
+ logger: LoggerInstanceManager;
15
18
  }
16
19
  declare const getWithCacheSupport: <V = unknown>({
17
20
  cacheKey,
18
21
  cacheGet,
19
22
  cacheSet,
20
23
  fetching,
21
- skipCache
24
+ skipCache,
25
+ logger
22
26
  }: GetWithCacheOptions<V>) => Promise<V>;
23
27
  interface GetMultipleWithCacheOptions<V = unknown> {
24
28
  getFromCache?: (query: any) => Promise<V>;
@@ -28,6 +32,7 @@ interface GetMultipleWithCacheOptions<V = unknown> {
28
32
  multiGetter?: (queries: any[]) => Promise<V[]>;
29
33
  setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;
30
34
  idField?: string;
35
+ logger: LoggerInstanceManager;
31
36
  }
32
37
  declare const getMultipleWithCache: <V = unknown>({
33
38
  getFromCache,
@@ -36,7 +41,8 @@ declare const getMultipleWithCache: <V = unknown>({
36
41
  setMultiInCache,
37
42
  getter,
38
43
  multiGetter,
39
- idField
44
+ idField,
45
+ logger
40
46
  }: GetMultipleWithCacheOptions<V>) => (queries: any[]) => Promise<(V | null | undefined)[]>;
41
47
  //#endregion
42
48
  //#region src/redis/index.d.ts
@@ -44,55 +50,38 @@ interface RedisCacheOptions {
44
50
  host?: string;
45
51
  port?: number;
46
52
  lockRetries?: number;
53
+ /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */
47
54
  lockTimeout?: number;
55
+ /** The time that a key will be locked. */
48
56
  lockDuration?: number;
57
+ /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */
49
58
  ttl?: number;
59
+ /** @default false */
50
60
  useLock?: boolean;
61
+ logger: LoggerInstanceManager;
62
+ /** Redis client to allow reusing connections. */
63
+ client?: ReturnType<typeof redis.createClient>;
51
64
  }
52
65
  declare class RedisCache {
53
- private client;
54
- private locker;
55
- private lockTimeout;
56
- private lockDuration;
57
- locks: Record<string, () => Promise<void> | undefined>;
58
- private baseTTL;
59
- private lockRetries;
66
+ private readonly client;
67
+ private readonly locker;
68
+ private readonly lockTimeout;
69
+ private readonly lockDuration;
70
+ private readonly logger;
71
+ locks: Record<string, Awaited<ReturnType<typeof (void 0).lock>> | undefined>;
72
+ private readonly baseTTL;
60
73
  keyPrefix: string;
61
74
  useLock: boolean;
62
75
  constructor(options: RedisCacheOptions);
63
- get<T = any>(key: string): Promise<T>;
64
- getMultiple<T = any>(keys: string[]): Promise<T[]>;
65
- set<T = any>(key: string, value: T): Promise<void>;
66
- setMultiple<T = any>(keyValues: Record<string, T>): Promise<void>;
67
- remove(key: string): Promise<void>;
68
- removeMultiple(keys: string[]): Promise<void>;
69
- getClient(): any;
76
+ readonly get: <T = any>(key: string) => Promise<T>;
77
+ readonly getMultiple: <T = any>(keys: string[]) => Promise<T[]>;
78
+ readonly set: <T = any>(key: string, value: T) => Promise<void>;
79
+ readonly setMultiple: <T = any>(keyValues: Record<string, T>) => Promise<void>;
80
+ readonly remove: (key: string) => Promise<void>;
81
+ readonly removeMultiple: (keys: string[]) => Promise<void>;
82
+ readonly getClient: () => typeof (void 0).client;
70
83
  private lock;
71
84
  }
72
85
  //#endregion
73
- //#region src/orm-cache/adapter.d.ts
74
- interface ModelOptions {
75
- name: string;
76
- associations?: AssociationOptions[];
77
- }
78
- interface AssociationOptions {
79
- name: string;
80
- alias: string;
81
- accessKey?: string;
82
- innerAssociation?: AssociationOptions;
83
- }
84
- //#endregion
85
- //#region src/orm-cache/index.d.ts
86
- declare enum ORMTypes {
87
- SEQUELIZE = "sequelize",
88
- }
89
- interface ORMCacheOptions {
90
- type: ORMTypes;
91
- models: ModelOptions[];
92
- ormInstance: any;
93
- debug: boolean;
94
- }
95
- declare const ORMCache: (options: ORMCacheOptions) => void;
96
- //#endregion
97
- export { ORMCache, ORMTypes, RedisCache, getMultipleWithCache, getNewLRU, getWithCacheSupport };
86
+ export { RedisCache, getMultipleWithCache, getNewLRU, getWithCacheSupport };
98
87
  //# sourceMappingURL=index.d.ts.map
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"lru-cache";import{Mutex as t,withTimeout as n}from"async-mutex";import r from"dotenv";import i from"@autofleet/logger";import a,{RedisError as o}from"redis";import s from"redis-lock";import{promisify as c}from"util";const l=async(e,t)=>{let n=await e.acquire();try{await t()}finally{n()}},u=()=>n(new t,3e3,Error(`mutex - locking timeout`)),d=i();var f=d;r.config();const p={},m=({lifeTimeInSec:e,size:t=300})=>({max:Math.min(t,1e5),maxAge:process.env.NODE_ENV===`test`?0:1e3*e}),h=e=>(p[e]??=u(),p[e]),g=e=>{p[e]&&delete p[e]},_=(t,n)=>new e(m({lifeTimeInSec:t,size:n})),v=process.env.IS_IN_MATMON_TESTING===`true`,y=process.env.NODE_ENV===`test`&&!v,b=async e=>{try{await e()}catch(e){f.error(`Failed to set in cache`,e)}},x=async({cacheKey:e,cacheGet:t,cacheSet:n,fetching:r,skipCache:i})=>{if(i||y){let e=await r();return await b(()=>n(e)),e}let a=null;try{await l(h(e),async()=>{a=await t(),a||(a=await r(),await b(()=>n(a))),g(e)})}catch{a=await r(),await b(()=>n(a)),g(e)}return a},S=(e,t)=>typeof e==`string`?e:e[t],C=({getFromCache:e,multiGetterFromCache:t,setInCache:n,setMultiInCache:r,getter:i,multiGetter:a,idField:o=`id`})=>async s=>{let c=new Map(s.filter(Boolean).map(e=>[S(e,o),e])),l=new Map,u=[...c.values()],d=await(t?.(u)??Promise.all(u.map(t=>e(t))));if(d.filter(Boolean).forEach(e=>{c.delete(S(e,o)),l.set(S(e,o),e)}),c.size>0){let e=await(a?.([...c.values()])??Promise.all([...c.values()].map(e=>i(e))));if(r&&e.length>0){let t=e.reduce((e,t)=>(e[S(t,o)]=t,e),{});await b(()=>r(t))}e.forEach(e=>{e&&(r||b(()=>n(e[o],e)),l.set(S(e,o),e))})}return s.map(e=>e?l.get(S(e,o)):null)};var w=class extends o{constructor(e,t){super(),this.innerError=t,this.message=e}toString(){return`${this.message}: ${this.innerError}`}},T=class extends w{constructor(e,t,n){super(e,t),this.key=n}toString(){return`${this.message} (${this.key}): ${this.innerError}`}};const E=e=>{Object.keys(e).forEach(t=>{typeof e[t]==`function`&&(e[`${t}Async`]=c(e[t]),Object.defineProperty(e[`${t}Async`],`__isPromisified__`,{value:!0,configurable:!0,enumerable:!1,writable:!0}))})};E(a.RedisClient.prototype),E(a.Multi.prototype);const{env:D}=process,O=D.REDIS_HOST_NAME||`127.0.0.1`,k=D.REDIS_HOST_PORT||6379,A=D.AF_SERVICE_NAME,j=A+`:`;if(!A)throw Error(`SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME`);var M=class{constructor(e){this.locks={},this.keyPrefix=j,this.client=a.createClient({host:e.host||O,port:e.port||k}),this.locker=c(s(this.client,e.lockRetries??10)),this.lockTimeout=e.lockTimeout??5e3,this.lockDuration=e.lockDuration??1e3,this.baseTTL=e.ttl??3600,this.useLock=!!e.useLock}async get(e){let t=j+e,n;try{n=await this.client.getAsync(t)}catch(e){throw new w(`Failed to get a value`,e)}if(this.useLock){let e;try{e=await this.lock(t)}catch(e){throw new T(`Failed to lock key`,e,t)}this.locks[t]=e}return JSON.parse(n)}async getMultiple(e){let t=e.map(e=>j+e),n;try{if(t.length===0)return[];n=await this.client.mgetAsync(t)}catch(e){throw new w(`Failed to get a value`,e)}return n.map(e=>JSON.parse(e))}async set(e,t){let n=j+e,r=parseInt(String(this.baseTTL*(Math.random()+1)),10);try{await this.client.setAsync(n,JSON.stringify(t),`EX`,r),this.locks[n]&&(await this.locks[n](),delete this.locks[n])}catch(e){throw new w(`Failed to set a key-value pair`,e)}}async setMultiple(e){if(typeof e!=`object`){let e=new w(`keyValues must be an object`,Error(`keyValues must be an object`));throw f.error(`keyValues must be an object`,{error:e}),e}if(e==null||Object.keys(e).length===0)return;let t=Object.entries(e).map(([e,t])=>[j+e,JSON.stringify(t)]),n=Math.trunc(this.baseTTL*(Math.random()+1));try{let e=this.client.multi(),r=e.msetAsync(...t.flat());return t.map(([t])=>e.expireAsync(t,n)),await e.exec(),r}catch(e){throw new w(`Failed to set multiple key-value pairs`,e)}}async remove(e){let t=j+e;try{this.locks[t]&&(await this.locks[t](),delete this.locks[t]),await this.client.delAsync(t)}catch(e){throw new w(`Failed to delete key ${t}`,e)}}async removeMultiple(e){let t=e.map(e=>j+e);try{await this.client.delAsync(t)}catch(e){throw new w(`Failed to delete multiple keys ${t.join(`|`)}`,e)}}getClient(){return this.client}lock(e){return Promise.race([this.locker(e,this.lockDuration),new Promise((e,t)=>setTimeout(()=>t(),this.lockTimeout))])}},N=M;const{AF_SERVICE_NAME:P}=process.env,F=`ormCache`,I=[`afterSave`,`afterUpdate`,`afterDestroy`],L=[`beforeBulkUpdate`,`beforeBulkDestroy`],R=(e,t)=>`${P}:${F}:${e}_${t}`,z=(e,t,n)=>`${P}:${F}:${e}_${t}_${n}_DEPENDENCIES`,B=(e,t)=>{let n=e.associations.filter(e=>t[e.alias]).map(n=>{let r=n.accessKey,i=[];return Array.isArray(t[n.alias])?(t[n.alias].forEach(t=>{i.push(z(e.name,n.name,t[r])),n.innerAssociation&&t[n.innerAssociation.alias]&&i.push(z(e.name,n.innerAssociation.name,t[n.innerAssociation.alias][n.innerAssociation.accessKey]))}),i):[z(e.name,n.name,t[n.alias][r])]});return n.reduce((e,t)=>e.concat(t),[])},V=(e,t,n)=>{let{transaction:r}=t;r?r.afterCommit(()=>n(e)):n(e)};var H=class{constructor(e,t){this.ormInstance=e,this.debugMode=t}getModel(e){return this.ormInstance.models[e]}getModelDependencies(e){let{associations:t}=this.ormInstance.models[e];return[...Object.keys(t).map(e=>{let n=t[e],r={alias:n.as,name:n.target.name,accessKey:n.target.primaryKeyAttribute},{through:i}=n;if(i){let e=i.model;r.innerAssociation={alias:e.name,name:e.name,accessKey:e.primaryKeyAttribute}}return r})]}debug(e,t){this.debugMode&&f.info(`[ORM_CACHE Debug] ${e}`,t)}injectGetWithCacheFunction(e,t){let n=async n=>{let r=B(t,n),i=R(t.name,n.id);this.debug(`Adding dependencies`,{instanceKey:i,dependencyKeys:r});let a=e.getClient().multi(),o=c(a.exec).bind(a);return r.reduce((e,t)=>e.sadd(t,i),a),o()},r=this.getModel(t.name);r.findByPkCached=async(i,a,o)=>{let s=R(t.name,i),c=JSON.parse(await e.getClient().getAsync(s));return c?(c=this.getModel(t.name).build(c,{isNewRecord:!1,include:o.include}),this.debug(`Found cached value`,{value:c,id:i,cacheKey:s})):(this.debug(`Value not found in cache, looking in db`,{id:i,cacheKey:s}),c=await r.scope(a).findByPk(i,o),this.debug(`Value from DB`,{value:c||`not found`,cacheKey:s}),await Promise.all([e.getClient().setAsync(s,JSON.stringify(c)),c&&n(c)])),c}}addInvalidationHooks(e,t){let n=async n=>{let r=B(t,n),i=R(t.name,n.id);this.debug(`Removing dependencies`,{instance:n,instanceKey:i,dependencyKeys:r});let a=e.getClient().multi(),o=c(a.exec).bind(a);return r.map(e=>a.srem(e,i)),a.del(i),o()},r=async(n,r)=>{let i=await e.getClient().smembersAsync(z(t.name,n,r));this.debug(`Invalidating dependent instances`,{dependentInstancesKeys:i});let a=e.getClient().multi(),o=c(a.exec).bind(a),s=await Promise.all(i.map(async n=>{let r=JSON.parse(await e.getClient().getAsync(n));if(!r)return[];let i=B(t,r);return i.reduce((e,t)=>e.srem(t,n),a),a.del(n),i}));return this.debug(`Removing dependencies`,{dependentInstancesKeys:i,dependenciesToRemove:s}),o()},i=this.getModel(t.name);I.map(e=>i.addHook(e,(e,t)=>V(e,t,e=>n(e)))),L.map(e=>i.addHook(e,e=>{e.individualHook=!0})),t.associations=this.getModelDependencies(t.name),this.debug(`Adding Invalidations Hooks to ${t.name}'s associations`,{associations:t.associations}),t.associations.forEach(e=>{let t=this.getModel(e.name);if(I.forEach(n=>t.addHook(n,(t,n)=>V(t,n,t=>r(e.name,t[e.accessKey])))),L.forEach(e=>t.addHook(e,e=>{e.individualHook=!0})),e.innerAssociation){let t=this.getModel(e.innerAssociation.name);I.forEach(n=>t.addHook(n,(t,n)=>V(t,n,t=>r(e.innerAssociation.name,t[e.innerAssociation.accessKey])))),L.map(e=>t.addHook(e,e=>{e.individualHook=!0}))}})}},U=class extends Error{};let W=function(e){return e.SEQUELIZE=`sequelize`,e}({});const G=e=>{switch(e.type){case W.SEQUELIZE:return new H(e.ormInstance,e.debug);default:throw new U(`ORM type ${e.type} is unsupported at the moment`)}},K=e=>{let{models:t}=e;f.info(`Starting ORM Cache`,{options:e});let n=G(e),r=new N({host:process.env.REDIS_HOST,port:Number.parseInt(process.env.REDIS_PORT,10)});t.map(e=>{n.addInvalidationHooks(r,e),n.injectGetWithCacheFunction(r,e)})};export{K as ORMCache,W as ORMTypes,N as RedisCache,C as getMultipleWithCache,_ as getNewLRU,x as getWithCacheSupport};
1
+ import e from"lru-cache";import{Mutex as t,withTimeout as n}from"async-mutex";import{env as r}from"node:process";import i from"redis";import a from"redis-lock";var o=Object.create,s=Object.defineProperty,c=Object.getOwnPropertyDescriptor,l=Object.getOwnPropertyNames,u=Object.getPrototypeOf,d=Object.prototype.hasOwnProperty,f=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),p=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=l(t),a=0,o=i.length,u;a<o;a++)u=i[a],!d.call(e,u)&&u!==n&&s(e,u,{get:(e=>t[e]).bind(null,u),enumerable:!(r=c(t,u))||r.enumerable});return e},m=(e,t,n)=>(n=e==null?{}:o(u(e)),p(t||!e||!e.__esModule?s(n,`default`,{value:e,enumerable:!0}):n,e)),h=f(((exports,t)=>{function n(){var e=typeof SuppressedError==`function`?SuppressedError:function(e,t){var n=Error();return n.name=`SuppressedError`,n.error=e,n.suppressed=t,n},t={},n=[];function r(e,t){if(t!=null){if(Object(t)!==t)throw TypeError(`using declarations can only be used with objects, functions, null, or undefined.`);if(e)var r=t[Symbol.asyncDispose||Symbol.for(`Symbol.asyncDispose`)];if(r===void 0&&(r=t[Symbol.dispose||Symbol.for(`Symbol.dispose`)],e))var i=r;if(typeof r!=`function`)throw TypeError(`Object is not disposable.`);i&&(r=function(){try{i.call(t)}catch(e){return Promise.reject(e)}}),n.push({v:t,d:r,a:e})}else e&&n.push({d:t,a:e});return t}return{e:t,u:r.bind(null,!1),a:r.bind(null,!0),d:function(){var r,i=this.e,a=0;function o(){for(;r=n.pop();)try{if(!r.a&&a===1)return a=0,n.push(r),Promise.resolve().then(o);if(r.d){var e=r.d.call(r.v);if(r.a)return a|=2,Promise.resolve(e).then(o,s)}else a|=1}catch(e){return s(e)}if(a===1)return i===t?Promise.resolve():Promise.reject(i);if(i!==t)throw i}function s(n){return i=i===t?n:new e(n,i),o()}return o()}}}t.exports=n,t.exports.__esModule=!0,t.exports.default=t.exports})),g=m(h(),1);const _={},v=({lifeTimeInSec:e,size:t=300})=>({max:Math.min(t,1e5),maxAge:process.env.NODE_ENV===`test`?0:1e3*e});typeof Symbol.dispose!=`symbol`&&Object.defineProperty(Symbol,`dispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.dispose`),writable:!1});const y=e=>{_[e]??=n(new t,3e3,Error(`mutex - locking timeout`));let r=_[e];return Object.assign(r,{[Symbol.dispose]:()=>{r.release(),_[e]&&delete _[e]}})},b=(t,n)=>new e(v({lifeTimeInSec:t,size:n})),x=process.env.IS_IN_MATMON_TESTING===`true`,S=process.env.NODE_ENV===`test`&&!x,C=async(e,t)=>{try{await e()}catch(e){t.error(`Failed to set in cache`,e)}},w=async({cacheKey:e,cacheGet:t,cacheSet:n,fetching:r,skipCache:i,logger:a})=>{try{var o=(0,g.default)();if(i||S){let t=await r();return await C(()=>n(e,t),a),t}let s=null,c=o.u(y(e));try{await c.acquire(),s=await t(e),s||(s=await r(),await C(()=>n(e,s),a))}catch{s=await r(),await C(()=>n(e,s),a)}return s}catch(e){o.e=e}finally{o.d()}},T=(e,t)=>typeof e==`string`?e:e[t],E=({getFromCache:e,multiGetterFromCache:t,setInCache:n,setMultiInCache:r,getter:i,multiGetter:a,idField:o=`id`,logger:s})=>async c=>{let l=new Map(c.filter(Boolean).map(e=>[T(e,o),e])),u=new Map,d=[...l.values()],f=await(t?.(d)??Promise.all(d.map(t=>e(t))));if(f.filter(Boolean).forEach(e=>{l.delete(T(e,o)),u.set(T(e,o),e)}),l.size>0){let e=await(a?.([...l.values()])??Promise.all([...l.values()].map(e=>i(e))));if(r&&e.length>0){let t=e.reduce((e,t)=>(e[T(t,o)]=t,e),{});await C(()=>r(t),s)}e.forEach(e=>{e&&(r||C(()=>n(e[o],e),s),u.set(T(e,o),e))})}return c.map(e=>e?u.get(T(e,o)):null)},D=r.REDIS_HOST_NAME||`127.0.0.1`,O=Number.parseInt(r.REDIS_HOST_PORT||``,10)||6379,k=r.AF_SERVICE_NAME,A=k+`:`;if(!k)throw Error(`SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME`);var j=class{client;locker;lockTimeout;lockDuration;logger;locks={};baseTTL;keyPrefix=A;useLock;constructor(e){this.logger=e.logger,this.client=e.client??i.createClient({socket:{host:e.host||D,port:e.port||O}}).on(`error`,e=>{this.logger.error(`matmon: Redis error`,{err:e})}),e.client||this.client.connect().catch(e=>{this.logger.error(`matmon: Failed to connect to Redis`,{err:e})}),this.locker=a(this.client,e.lockRetries??10),this.lockTimeout=e.lockTimeout??5e3,this.lockDuration=e.lockDuration??1e3,this.baseTTL=e.ttl??3600,this.useLock=!!e.useLock}get=async e=>{let t=A+e,n;try{n=await this.client.get(t)}catch(e){throw Error(`Failed to get a value`,{cause:e})}if(this.useLock){let e;try{e=await this.lock(t)}catch(e){throw Error(`Failed to lock key (${t})`,{cause:e})}this.locks[t]=e}return JSON.parse(n)};getMultiple=async e=>{let t=e.map(e=>A+e),n;try{if(t.length===0)return[];n=await this.client.mGet(t)}catch(e){throw Error(`Failed to get a value`,{cause:e})}return n.map(e=>JSON.parse(e))};set=async(e,t)=>{let n=A+e,r=parseInt(String(this.baseTTL+(Math.random()+1)),10);try{await this.client.set(n,JSON.stringify(t),{EX:r}),this.locks[n]&&(await this.locks[n](),delete this.locks[n])}catch(e){throw Error(`Failed to set a key-value pair`,{cause:e})}};setMultiple=async e=>{if(typeof e!=`object`){let e=Error(`keyValues must be an object`);throw this.logger.error(`keyValues must be an object`,{error:e}),e}if(e==null||Object.keys(e).length===0)return;let t=Object.entries(e).map(([e,t])=>[A+e,JSON.stringify(t)]),n=Math.trunc(this.baseTTL*(Math.random()+1));try{let e=this.client.multi(),r=e.mSet(t);return t.map(([t])=>e.expire(t,n)),await e.exec(),r}catch(e){throw Error(`Failed to set multiple key-value pairs`,{cause:e})}};remove=async e=>{let t=A+e;try{this.locks[t]&&(await this.locks[t](),delete this.locks[t]),await this.client.del(t)}catch(e){throw Error(`Failed to delete key ${t}`,{cause:e})}};removeMultiple=async e=>{let t=e.map(e=>A+e);try{await this.client.del(t)}catch(e){throw Error(`Failed to delete multiple keys ${t.join(`|`)}`,{cause:e})}};getClient=()=>this.client;lock(e){return Promise.race([this.locker(e,this.lockDuration),new Promise((e,t)=>setTimeout(()=>t(),this.lockTimeout))])}},M=j;export{M as RedisCache,E as getMultipleWithCache,b as getNewLRU,w as getWithCacheSupport};
2
2
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["logger: LoggerInstanceManager","valueToReturn: Awaited<V> | null","depKeys: string[]","ormInstance: Sequelize","debugMode: boolean","associationOptions: AssociationOptions","instance","RedisCache"],"sources":["../src/locking.ts","../src/logger.ts","../src/cache.ts","../src/redis/errors.ts","../src/promise-utils.ts","../src/redis/index.ts","../src/orm-cache/sequelize-adapter.ts","../src/orm-cache/errors.ts","../src/orm-cache/index.ts"],"sourcesContent":["import { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\n\nconst CACHE_LOCK_TIMEOUT_MILIS = 3000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\n\nexport const wrapWithMutex = async (mutex: MutexInterface, funcToRun: () => PromiseLike<void>): Promise<void> => {\n const release = await mutex.acquire();\n try {\n await funcToRun();\n } finally {\n release();\n }\n};\n\nexport const getMutex = (): MutexInterface => withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n","import CustomLogger, { type LoggerInstanceManager } from '@autofleet/logger';\n\nconst logger: LoggerInstanceManager = CustomLogger();\n\nexport default logger;\n","import LRU from 'lru-cache';\n\nimport * as locking from './locking';\nimport dotenv from 'dotenv';\nimport type { MutexInterface } from 'async-mutex';\nimport logger from './logger';\n\ndotenv.config();\n\nconst DEFAULT_CACHE_SIZE = 300;\nconst MAX_SIZE = 100000;\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface => {\n MUTEX_MAP[key] ??= locking.getMutex();\n return MUTEX_MAP[key];\n};\n\nconst deleteMutexByCacheKey = (key: keyof any): void => {\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU<K, V>(getOptions<K, V>({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: () => Promise<V>;\n cacheSet: (value: V) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(res));\n return res;\n }\n let valueToReturn: Awaited<V> | null = null;\n try {\n await locking.wrapWithMutex(getMutexByCacheKey(cacheKey), async () => {\n valueToReturn = await cacheGet();\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(valueToReturn!));\n } else {\n // logger.info('get value from cache');\n }\n deleteMutexByCacheKey(cacheKey);\n });\n // retry without locking if failed\n } catch {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(valueToReturn!));\n deleteMutexByCacheKey(cacheKey);\n }\n return valueToReturn!;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject));\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value));\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { RedisError } from 'redis';\n\nclass RedisCacheError extends RedisError {\n innerError: any;\n message: string;\n constructor(msg: string, err: any) {\n super();\n this.innerError = err;\n this.message = msg;\n }\n\n toString(): string {\n return `${this.message}: ${this.innerError}`;\n }\n}\n\nclass RedisLockError extends RedisCacheError {\n key: string;\n constructor(msg: string, err: any, key: string) {\n super(msg, err);\n this.key = key;\n }\n\n toString(): string {\n return `${this.message} (${this.key}): ${this.innerError}`;\n }\n}\n\nexport {\n RedisCacheError,\n RedisLockError,\n};\n","import { promisify } from 'util';\n\nexport const promisifyAll = (obj: object): void => {\n Object.keys(obj).forEach((key) => {\n if (typeof obj[key] === 'function') {\n obj[`${key}Async`] = promisify(obj[key]);\n\n // To support the `isPromisified` check in `bluebird`, we need to set the `__isPromisified__` property to `true`.\n Object.defineProperty(obj[`${key}Async`], '__isPromisified__', {\n value: true,\n configurable: true,\n enumerable: false,\n writable: true,\n });\n }\n });\n};\n","import redis from 'redis';\nimport redisLock from 'redis-lock';\nimport { promisify } from 'util';\nimport { RedisCacheError, RedisLockError } from './errors';\nimport logger from '../logger';\nimport { promisifyAll } from '../promise-utils';\n\npromisifyAll(redis.RedisClient.prototype);\npromisifyAll(redis.Multi.prototype);\n\nconst { env } = process;\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = env.REDIS_HOST_PORT || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5000;\nconst DEFAULT_LOCK_DURATION = 1000;\nconst DEFAULT_BASE_TTL = 3600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n lockTimeout?: number;\n lockDuration?: number;\n ttl?: number;\n useLock?: boolean;\n}\n\nclass RedisCache {\n // node redis client instance.\n private client: any;\n\n // A redis-lock instance.\n private locker: (key: string, duration: number) => Promise<() => Promise<void>>;\n\n // The time that the cache will wait until a lock\n // will be considered as failed (in milliseconds).\n private lockTimeout: number;\n\n // The time that a key will be locked.\n private lockDuration: number;\n\n // A dictionary of key and its lock.\n locks: Record<string, () => Promise<void> | undefined> = {};\n\n // The time that a key-value pair will exist in redis.\n // To this base time will be added between 1 to 2 milliseconds.\n private baseTTL: number;\n\n // The number of retries that we will try to lock a key in case of failure.\n private lockRetries: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.client = redis.createClient({\n host: options.host || HOST,\n port: options.port || PORT,\n });\n this.locker = promisify(redisLock(this.client, options.lockRetries ?? 10));\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n async get<T = any>(key: string): Promise<T> {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.getAsync(keyWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to get a value', err);\n }\n\n if (this.useLock) {\n let lock;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisLockError('Failed to lock key', err, keyWithPrefix);\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n }\n\n async getMultiple<T = any>(keys: string[]): Promise<T[]> {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mgetAsync(keysWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to get a value', err);\n }\n return values.map(value => JSON.parse(value));\n }\n\n async set<T = any>(key: string, value: T): Promise<void> {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL * (Math.random() + 1)), 10);\n try {\n await this.client.setAsync(keyWithPrefix, JSON.stringify(value), 'EX', ttl);\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to set a key-value pair', err);\n }\n }\n\n async setMultiple<T = any>(keyValues: Record<string, T>): Promise<void> {\n if (typeof keyValues !== 'object') {\n const error = new RedisCacheError('keyValues must be an object', new Error('keyValues must be an object'));\n logger.error('keyValues must be an object', { error });\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.msetAsync(...keyValuesWithPrefix.flat());\n keyValuesWithPrefix.map(([key]) => multi.expireAsync(key, ttl));\n await multi.exec();\n return setPromise;\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError('Failed to set multiple key-value pairs', err);\n }\n }\n\n async remove(key: string): Promise<void> {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.delAsync(keyWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError(`Failed to delete key ${keyWithPrefix}`, err);\n }\n }\n\n async removeMultiple(keys: string[]): Promise<void> {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.delAsync(keysWithPrefix);\n } catch (err) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw new RedisCacheError(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, err);\n }\n }\n\n getClient(): any {\n return this.client;\n }\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n","import type { Attributes, Model, ModelStatic, NonNullFindOptions, Sequelize } from 'sequelize';\nimport type { Adapter, AssociationOptions, ModelOptions } from './adapter';\nimport type RedisCache from '../redis';\nimport logger from '../logger';\nimport { promisify } from 'util';\n\nconst { AF_SERVICE_NAME } = process.env;\nconst ORM_CACHE_PREFIX = 'ormCache';\nconst INVALIDATION_HOOKS = ['afterSave', 'afterUpdate', 'afterDestroy'] as const;\nconst BULK_HOOKS = ['beforeBulkUpdate', 'beforeBulkDestroy'] as const;\n\nconst generateInstanceKey = (name: string, id: string): string =>\n `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${name}_${id}`;\n\nconst generateDependencyKey = (modelName: string, associationName: string, associationId: string): string =>\n `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${modelName}_${associationName}_${associationId}_DEPENDENCIES`;\n\nconst getInstanceDependencyKeys = (modelOptions: ModelOptions, instance): string[] => {\n const keys = modelOptions.associations!\n .filter((associationOptions: AssociationOptions) => instance[associationOptions.alias])\n .map((associationOptions: AssociationOptions): string[] => {\n const accessKey = associationOptions.accessKey!;\n const depKeys: string[] = [];\n if (Array.isArray(instance[associationOptions.alias])) {\n instance[associationOptions.alias].forEach((associationInstance) => {\n depKeys.push(generateDependencyKey(\n modelOptions.name,\n associationOptions.name,\n associationInstance[accessKey],\n ));\n if (associationOptions.innerAssociation && associationInstance[associationOptions.innerAssociation.alias]) {\n depKeys.push(generateDependencyKey(\n modelOptions.name,\n associationOptions.innerAssociation.name,\n associationInstance[associationOptions.innerAssociation.alias][associationOptions.innerAssociation.accessKey!],\n ));\n }\n });\n return depKeys;\n }\n return [\n generateDependencyKey(\n modelOptions.name,\n associationOptions.name,\n instance[associationOptions.alias][accessKey],\n ),\n ];\n });\n return keys.reduce((flattenArray, array) => flattenArray.concat(array), []);\n};\n\nconst handleTransactionHook = (instance, options, func) => {\n const { transaction } = options;\n if (transaction) {\n transaction.afterCommit(() => func(instance));\n } else {\n func(instance);\n }\n};\n\ndeclare module 'sequelize' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Model {\n function findByPkCached<M extends Model>(\n this: ModelStatic<M>,\n identifier: string,\n scopes: string[],\n options: Omit<NonNullFindOptions<Attributes<M>>, 'where'>\n ): Promise<M>;\n }\n}\n\nexport default class SequelizeAdapter implements Adapter {\n constructor(public ormInstance: Sequelize, public debugMode: boolean) {\n }\n\n getModel(modelName: string): ModelStatic<Model> {\n return this.ormInstance.models[modelName];\n }\n\n getModelDependencies(modelName: string): AssociationOptions[] {\n const { associations } = this.ormInstance.models[modelName];\n return [\n ...Object.keys(associations).map((association) => {\n const sequelizeAssociation = associations[association];\n const associationOptions: AssociationOptions = {\n alias: sequelizeAssociation.as,\n name: sequelizeAssociation.target.name,\n accessKey: sequelizeAssociation.target.primaryKeyAttribute,\n };\n\n // @ts-expect-error through is not defined in the typings\n const { through } = sequelizeAssociation;\n if (through) {\n const relationModel = through.model;\n associationOptions.innerAssociation = {\n alias: relationModel.name,\n name: relationModel.name,\n accessKey: relationModel.primaryKeyAttribute,\n };\n }\n\n return associationOptions;\n }),\n ];\n }\n\n debug(message: string, payload?: unknown): void {\n if (this.debugMode) {\n logger.info(`[ORM_CACHE Debug] ${message}`, payload);\n }\n }\n\n injectGetWithCacheFunction(cache: RedisCache, modelOptions: ModelOptions): void {\n const addDependencies = async (instance) => {\n const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);\n const instanceKey = generateInstanceKey(modelOptions.name, instance.id);\n this.debug('Adding dependencies', { instanceKey, dependencyKeys });\n const addDependenciesMulti = cache.getClient().multi();\n const addDependenciesMultiAsync = promisify(addDependenciesMulti.exec).bind(addDependenciesMulti);\n dependencyKeys.reduce((multi, key) => multi.sadd(key, instanceKey), addDependenciesMulti);\n return addDependenciesMultiAsync();\n };\n\n const model = this.getModel(modelOptions.name);\n model.findByPkCached = async <M extends Model>(id: string, scopes: string[], options: Omit<NonNullFindOptions<Attributes<M>>, 'where'>): Promise<M> => {\n const cacheKey = generateInstanceKey(modelOptions.name, id);\n let value = JSON.parse(await cache.getClient().getAsync(cacheKey));\n if (!value) {\n this.debug('Value not found in cache, looking in db', { id, cacheKey });\n value = await model.scope(scopes).findByPk(id, options);\n this.debug('Value from DB', { value: value || 'not found', cacheKey });\n await Promise.all([\n cache.getClient().setAsync(cacheKey, JSON.stringify(value)),\n value && addDependencies(value),\n ]);\n } else {\n value = this.getModel(modelOptions.name).build(value, { isNewRecord: false, include: options.include });\n this.debug('Found cached value', { value, id, cacheKey });\n }\n return value;\n };\n }\n\n addInvalidationHooks(cache: RedisCache, modelOptions: ModelOptions): void {\n const invalidateModelInstance = async (instance) => {\n const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);\n const instanceKey = generateInstanceKey(modelOptions.name, instance.id);\n this.debug('Removing dependencies', { instance, instanceKey, dependencyKeys });\n const removeMulti = cache.getClient().multi();\n const removeMultiAsync = promisify(removeMulti.exec).bind(removeMulti);\n dependencyKeys.map(key => removeMulti.srem(key, instanceKey));\n removeMulti.del(instanceKey);\n return removeMultiAsync();\n };\n\n const invalidateModelInstanceByAssociation = async (association, associationId) => {\n const dependentInstancesKeys = await cache.getClient().smembersAsync(generateDependencyKey(\n modelOptions.name,\n association,\n associationId,\n ));\n this.debug('Invalidating dependent instances', { dependentInstancesKeys });\n const removeMulti = cache.getClient().multi();\n const removeMultiAsync = promisify(removeMulti.exec).bind(removeMulti);\n const dependenciesToRemove = await Promise.all(dependentInstancesKeys.map(async (instanceKey) => {\n const instance = JSON.parse(await cache.getClient().getAsync(instanceKey));\n if (!instance) {\n return [];\n }\n const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);\n dependencyKeys.reduce((multi, key) => multi.srem(key, instanceKey), removeMulti);\n removeMulti.del(instanceKey);\n return dependencyKeys;\n }));\n this.debug('Removing dependencies', { dependentInstancesKeys, dependenciesToRemove });\n return removeMultiAsync();\n };\n\n const model = this.getModel(modelOptions.name);\n INVALIDATION_HOOKS.map(hook => model.addHook(hook, (instance, options) =>\n handleTransactionHook(instance, options, instance => invalidateModelInstance(instance))));\n BULK_HOOKS.map(hook => model.addHook(hook, (options) => {\n options.individualHook = true;\n }));\n\n modelOptions.associations = this.getModelDependencies(modelOptions.name);\n this.debug(`Adding Invalidations Hooks to ${modelOptions.name}'s associations`, { associations: modelOptions.associations });\n modelOptions.associations.forEach((associationOptions) => {\n const associationModel = this.getModel(associationOptions.name);\n INVALIDATION_HOOKS.forEach(hook => associationModel.addHook(hook, (instance, options) =>\n handleTransactionHook(\n instance,\n options,\n associationInstance => invalidateModelInstanceByAssociation(\n associationOptions.name,\n associationInstance[associationOptions.accessKey!],\n ),\n ),\n ));\n BULK_HOOKS.forEach(hook => associationModel.addHook(hook, (options) => {\n options.individualHook = true;\n }));\n\n if (associationOptions.innerAssociation) {\n const innerAssociationModel = this.getModel(associationOptions.innerAssociation.name);\n INVALIDATION_HOOKS.forEach(hook => innerAssociationModel.addHook(hook, (instance, options) =>\n handleTransactionHook(\n instance,\n options,\n innerAssociationInstance => invalidateModelInstanceByAssociation(\n associationOptions.innerAssociation!.name,\n innerAssociationInstance[associationOptions.innerAssociation!.accessKey!],\n ),\n ),\n ));\n BULK_HOOKS.map(hook => innerAssociationModel.addHook(hook, (options) => {\n options.individualHook = true;\n }));\n }\n });\n }\n}\n","export class UnsupportedOrmTypeError extends Error {}\n","import type { Adapter, ModelOptions } from './adapter';\nimport SequelizeAdapter from './sequelize-adapter';\nimport { UnsupportedOrmTypeError } from './errors';\nimport RedisCache from '../redis';\nimport logger from '../logger';\n\nexport enum ORMTypes {\n SEQUELIZE = 'sequelize',\n}\n\ninterface ORMCacheOptions {\n type: ORMTypes;\n models: ModelOptions[];\n ormInstance: any;\n debug: boolean;\n}\n\nconst ORMInstanceFactory = (options: ORMCacheOptions): Adapter => {\n switch (options.type) {\n case ORMTypes.SEQUELIZE: {\n return new SequelizeAdapter(options.ormInstance, options.debug);\n }\n default: {\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n throw new UnsupportedOrmTypeError(`ORM type ${options.type} is unsupported at the moment`);\n }\n }\n};\n\nexport const ORMCache = (options: ORMCacheOptions): void => {\n const { models } = options;\n logger.info('Starting ORM Cache', { options });\n const adapter = ORMInstanceFactory(options);\n const cache = new RedisCache({\n host: process.env.REDIS_HOST,\n port: Number.parseInt(process.env.REDIS_PORT!, 10),\n });\n // eslint-disable-next-line array-callback-return\n models.map((modelOptions): void => {\n adapter.addInvalidationHooks(cache, modelOptions);\n adapter.injectGetWithCacheFunction(cache, modelOptions);\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["n","r","e","o","valueToReturn: V | null","lock: Awaited<ReturnType<typeof this.lock>> | undefined"],"sources":["../../../node_modules/@oxc-project/runtime/src/helpers/usingCtx.js","../src/cache.ts","../src/redis/index.ts"],"sourcesContent":["function _usingCtx() {\n var r = \"function\" == typeof SuppressedError ? SuppressedError : function (r, e) {\n var n = Error();\n return n.name = \"SuppressedError\", n.error = r, n.suppressed = e, n;\n },\n e = {},\n n = [];\n function using(r, e) {\n if (null != e) {\n if (Object(e) !== e) throw new TypeError(\"using declarations can only be used with objects, functions, null, or undefined.\");\n if (r) var o = e[Symbol.asyncDispose || Symbol[\"for\"](\"Symbol.asyncDispose\")];\n if (void 0 === o && (o = e[Symbol.dispose || Symbol[\"for\"](\"Symbol.dispose\")], r)) var t = o;\n if (\"function\" != typeof o) throw new TypeError(\"Object is not disposable.\");\n t && (o = function o() {\n try {\n t.call(e);\n } catch (r) {\n return Promise.reject(r);\n }\n }), n.push({\n v: e,\n d: o,\n a: r\n });\n } else r && n.push({\n d: e,\n a: r\n });\n return e;\n }\n return {\n e: e,\n u: using.bind(null, !1),\n a: using.bind(null, !0),\n d: function d() {\n var o,\n t = this.e,\n s = 0;\n function next() {\n for (; o = n.pop();) try {\n if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);\n if (o.d) {\n var r = o.d.call(o.v);\n if (o.a) return s |= 2, Promise.resolve(r).then(next, err);\n } else s |= 1;\n } catch (r) {\n return err(r);\n }\n if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();\n if (t !== e) throw t;\n }\n function err(n) {\n return t = t !== e ? new r(n, t) : n, next();\n }\n return next();\n }\n };\n}\nmodule.exports = _usingCtx, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","import LRU from 'lru-cache';\nimport { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\nimport type { LoggerInstanceManager } from '@autofleet/logger/src';\n\nconst MAX_SIZE = 100_000;\nconst DEFAULT_CACHE_SIZE = 300;\nconst CACHE_LOCK_TIMEOUT_MILIS = 3_000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface & { [Symbol.dispose]: () => void; } => {\n MUTEX_MAP[key] ??= withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n const mutex = MUTEX_MAP[key];\n return Object.assign(mutex, {\n [Symbol.dispose]: () => {\n mutex.release();\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n },\n });\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU<K, V>(getOptions<K, V>({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;\n cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n logger: LoggerInstanceManager;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>, logger: LoggerInstanceManager): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n logger,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, res), logger);\n return res;\n }\n let valueToReturn: V | null = null;\n using mutex = getMutexByCacheKey(cacheKey);\n try {\n await mutex.acquire();\n valueToReturn = await cacheGet(cacheKey);\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n } else {\n // logger.info('get value from cache');\n }\n } catch {\n // retry without locking if failed\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n }\n return valueToReturn;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n logger: LoggerInstanceManager;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n logger,\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject), logger);\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value), logger);\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { env } from 'node:process';\nimport redis from 'redis';\nimport redisLock from 'redis-lock';\nimport type { LoggerInstanceManager } from '@autofleet/logger/src';\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = Number.parseInt(env.REDIS_HOST_PORT || '', 10) || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5_000;\nconst DEFAULT_LOCK_DURATION = 1_000;\nconst DEFAULT_BASE_TTL = 3_600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */\n lockTimeout?: number;\n /** The time that a key will be locked. */\n lockDuration?: number;\n /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */\n ttl?: number;\n /** @default false */\n useLock?: boolean;\n logger: LoggerInstanceManager;\n /** Redis client to allow reusing connections. */\n client?: ReturnType<typeof redis.createClient>;\n}\n\nclass RedisCache {\n private readonly client: ReturnType<typeof redis.createClient>;\n private readonly locker: ReturnType<typeof redisLock>;\n private readonly lockTimeout: number;\n private readonly lockDuration: number;\n private readonly logger: LoggerInstanceManager;\n\n // A dictionary of key and its lock.\n locks: Record<string, Awaited<ReturnType<typeof this.lock>> | undefined> = {};\n\n private readonly baseTTL: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.logger = options.logger;\n this.client = options.client ?? redis.createClient({\n socket: {\n host: options.host || HOST,\n port: options.port || PORT,\n },\n }).on('error', (err) => {\n this.logger.error('matmon: Redis error', { err });\n });\n if (!options.client) {\n this.client.connect().catch((err) => {\n this.logger.error('matmon: Failed to connect to Redis', { err });\n });\n }\n this.locker = redisLock(this.client, options.lockRetries ?? 10);\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n public readonly get = async <T = any>(key: string): Promise<T> => {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.get(keyWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n\n if (this.useLock) {\n let lock: Awaited<ReturnType<typeof this.lock>> | undefined;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to lock key (${keyWithPrefix})`, { cause: err });\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n };\n\n public readonly getMultiple = async <T = any>(keys: string[]): Promise<T[]> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mGet(keysWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n return values.map(value => JSON.parse(value));\n };\n\n public readonly set = async <T = any>(key: string, value: T): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL + (Math.random() + 1)), 10);\n try {\n await this.client.set(keyWithPrefix, JSON.stringify(value), { EX: ttl });\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n throw new Error('Failed to set a key-value pair', { cause: err });\n }\n };\n\n public readonly setMultiple = async <T = any>(keyValues: Record<string, T>): Promise<void> => {\n if (typeof keyValues !== 'object') {\n const error = new Error('keyValues must be an object');\n this.logger.error('keyValues must be an object', { error });\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map<[string, string]>(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.mSet(keyValuesWithPrefix);\n keyValuesWithPrefix.map(([key]) => multi.expire(key, ttl));\n await multi.exec();\n // @ts-expect-error we are returning a value although the type definition says void\n return setPromise;\n } catch (err) {\n throw new Error('Failed to set multiple key-value pairs', { cause: err });\n }\n };\n\n public readonly remove = async (key: string): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.del(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete key ${keyWithPrefix}`, { cause: err });\n }\n };\n\n public readonly removeMultiple = async (keys: string[]): Promise<void> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.del(keysWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, { cause: err });\n }\n };\n\n public readonly getClient = (): typeof this.client => this.client;\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n"],"x_google_ignoreList":[0],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/matmon",
3
- "version": "2.4.18",
3
+ "version": "3.0.1",
4
4
  "description": "manage cache",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -40,19 +40,18 @@
40
40
  },
41
41
  "homepage": "https://github.com/Autofleet/autorepo/tree/master/packages/matmon#readme",
42
42
  "dependencies": {
43
- "async-mutex": "^0.2.6",
44
- "dotenv": "^8.2.0",
43
+ "async-mutex": "^0.5.0",
45
44
  "lru-cache": "^6.0.0",
46
- "redis": "^3.0.2",
47
- "redis-lock": "^0.1.4",
45
+ "redis": "^4.7.1",
46
+ "redis-lock": "^1.0.0",
48
47
  "sequelize": "^6.6.2",
49
48
  "sequelize-typescript": "^2.1.0"
50
49
  },
51
50
  "devDependencies": {
52
51
  "@autofleet/logger": "^4.0.3",
52
+ "@oxc-project/runtime": "^0.82.3",
53
53
  "@types/lru-cache": "^5.1.1",
54
- "@types/node": "^20.17.10",
55
- "@types/redis": "^4.0.11"
54
+ "@types/node": "^20.17.10"
56
55
  },
57
56
  "peerDependencies": {
58
57
  "@autofleet/logger": "^4"