@jointly/cache-candidate 1.5.10 → 2.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/README.md CHANGED
@@ -102,11 +102,20 @@ The options available are:
102
102
  - `args`: The arguments passed to the current function.
103
103
  - `timeFrameCacheRecords`: The cache records of the last `timeFrame` milliseconds.
104
104
  - `millisecondThreshold` (_optional_): The threshold in milliseconds to be considered for the condition checks. If not passed, this criteria will be ignored.
105
- - `requestsThreshold` (_optional_): The number of requests to be considered for the condition checks. Default: `3`.
105
+ - `requestsThreshold` (_optional_): The number of requests to be considered for the condition checks. Default: `1`.
106
+ - `expirationMode` (_optional_): The expiration mode to use. Default: `default`.
107
+ - `default`: The cache-candidate will be responsible for generating timeouts that, when reached, will call the provided delete method of the cache adapter.
108
+ - `timeout-only`: The cache-candidate will be responsible for generating timeouts that, when reached, will *not* call the provided delete method of the cache adapter. This means your cache adapter must have a mechanism to delete the cache record when the timeout is reached (Ex. Redis EX option). Plugins and events will be called as usual.
109
+ - `eject`: The cache-candidate will not be responsible for generating timeouts nor calling the delete method. This means your cache adapter must have a mechanism to delete the cache record when the timeout is reached (Ex. Redis EX option). *Be aware plugins and events will not be called.*
110
+
106
111
  - `keepAlive` (_optional_): If `true`, the cache record will be kept alive at every request. Default: `false`.
107
112
  - `cache` (_optional_): The cache adapter to be used. Defaults to `an in-memory cache based on Maps, but with Promises`.
108
113
  Available adapters are:
109
114
  - `makeRedisCache`: A cache adapter based on Redis. Receives a Redis client as the first and only argument.
115
+ - `fetchingMode` (_optional_): The fetching mode to use.
116
+ Available modes are:
117
+ - `default`: The default behaviour, if an entry is found in the cache, it will be returned.
118
+ - `stale-while-revalidate`: If an entry is found in the cache, it will be returned. Otherwise, a stale result will be returned and a new call to the cache-candidate will be made in background. Be aware this mechanism doesn't grant the result will be cached, depending on your threshold configuration. If, for example, you set a `requestsThreshold` of 3, the revalidation will only call the function once, so it won't be cached.
110
119
  - `events` (_optional_): Listener functions to be called at specific steps of the process.
111
120
  Available events are:
112
121
  - `onCacheHit`: Called when the cache entry is hit.
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var G=Object.create;var A=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var Z=Object.getPrototypeOf,ee=Object.prototype.hasOwnProperty;var F=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),te=(e,t)=>{for(var n in t)A(e,n,{get:t[n],enumerable:!0})},S=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Y(t))!ee.call(e,a)&&a!==n&&A(e,a,{get:()=>t[a],enumerable:!(r=V(t,a))||r.enumerable});return e};var D=(e,t,n)=>(n=e!=null?G(Z(e)):{},S(t||!e||!e.__esModule?A(n,"default",{value:e,enumerable:!0}):n,e)),ne=e=>S(A({},"__esModule",{value:!0}),e);var O=F((Ne,j)=>{"use strict";var b=Object.defineProperty,ae=Object.getOwnPropertyDescriptor,re=Object.getOwnPropertyNames,oe=Object.prototype.hasOwnProperty,ie=(e,t)=>{for(var n in t)b(e,n,{get:t[n],enumerable:!0})},ce=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of re(t))!oe.call(e,a)&&a!==n&&b(e,a,{get:()=>t[a],enumerable:!(r=ae(t,a))||r.enumerable});return e},se=e=>ce(b({},"__esModule",{value:!0}),e),I={};ie(I,{Hooks:()=>M});j.exports=se(I);var M=(e=>(e.SETUP="SETUP",e.INIT="INIT",e.EXECUTION_PRE="EXECUTION_PRE",e.EXECUTION_POST="EXECUTION_POST",e.DATACACHE_RECORD_ADD_PRE="DATACACHE_RECORD_ADD_PRE",e.DATACACHE_RECORD_ADD_POST="DATACACHE_RECORD_ADD_POST",e.DATACACHE_RECORD_DELETE_PRE="DATACACHE_RECORD_DELETE_PRE",e.DATACACHE_RECORD_DELETE_POST="DATACACHE_RECORD_DELETE_POST",e.CACHE_HIT="CACHE_HIT",e))(M||{})});var q=F((Ie,Q)=>{"use strict";var v=Object.defineProperty,ue=Object.getOwnPropertyDescriptor,de=Object.getOwnPropertyNames,le=Object.prototype.hasOwnProperty,Ce=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},he=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of de(t))!le.call(e,a)&&a!==n&&v(e,a,{get:()=>t[a],enumerable:!(r=ue(t,a))||r.enumerable});return e},fe=e=>he(v({},"__esModule",{value:!0}),e),U={};Ce(U,{Hook:()=>ge,hook:()=>ye});Q.exports=fe(U);var me={before:()=>{},after:()=>{}},pe={before:()=>{},after:()=>{}};function ge(e){return function(t,n,r){let a={},o=r.value,{before:i,after:c}={...me,...e};return r.value=function(...s){i({context:a,args:s,target:t,propertyKey:n,descriptor:r});let d=o.apply(this,s);return c({context:a,args:s,target:t,propertyKey:n,descriptor:r,result:d}),d},r}}function ye(e){return function(t){let n={},{before:r,after:a}={...pe,...e};return function(...o){r({context:n,args:o});let i=t(...o);return a({context:n,args:o,result:i}),i}}}});var we={};te(we,{CacheCandidate:()=>K,CacheCandidateOptionsDefault:()=>E,cacheCandidate:()=>B});module.exports=ne(we);var N=(e=new Map)=>({get:async t=>e.get(JSON.stringify(t)),has:async t=>e.has(JSON.stringify(t)),set:async(t,n)=>e.set(JSON.stringify(t),n),delete:async t=>e.delete(JSON.stringify(t))});var E={ttl:6e5,timeFrame:3e4,requestsThreshold:3,cache:N(),keepAlive:!1,plugins:[],events:{onCacheHit:e=>e,onCacheSet:e=>e,onCacheDelete:e=>e,onBeforeFunctionExecution:e=>e,onAfterFunctionExecution:e=>e}};var P=Symbol("DataCacheRecordNotFound"),x=Symbol("RunningQueryRecordNotFound");var k=D(O());var X=require("crypto");var J=D(O()),W=D(q());async function u(e,t,n){for(let r of t){let a=r.hooks.filter(i=>i.hook===e);if(a.length>1)throw new Error(`Only one hook instance per plugin is allowed. ${r.name} has ${a.length} instances of ${e}}`);if(a.length===0)continue;let o=a[0];if(Ee(o))await o.action(n,r.additionalParameters);else throw new Error(`Hook ${e} for plugin ${r.name} is not actionable.`)}}function Ee(e){return[e.action!==void 0,typeof e.action=="function"].every(t=>t===!0)}function $(e,t,n,r){return(0,W.hook)({before:()=>{u(e,n.plugins,r)},after:({result:a})=>{u(t,n.plugins,{...r,result:a})}})}function w({options:e}){e.plugins&&e.plugins.forEach(t=>{if(!t.hooks||!Array.isArray(t.hooks)||t.hooks.length===0)throw new Error(`Plugin ${t.name} has no hooks.`);t.hooks.forEach(n=>{if(!J.Hooks[n.hook])throw new Error(`Invalid hook ${n}`)})})}var C=D(O());function Te(e,t){return Date.now()<e+t.timeFrame}function Ae({key:e,timeframeCache:t,executionTime:n,executionEnd:r}){t.has(e)?t.get(e).push({executionTime:n,executionEnd:r}):t.set(e,[{executionEnd:r,executionTime:n}])}function De({options:e,key:t,timeframeCache:n}){if(n.has(t)){let r=n.get(t),a=r.filter(({executionEnd:o})=>Te(o,e));r.length!==a.length&&n.set(t,a)}}function Pe({key:e,runningQueryCache:t}){return t.has(e)?t.get(e):x}function y(...e){return(0,X.createHash)("sha256").update(e.join("|")).digest("hex")}function m({birthTime:e,options:t}){return Date.now()-e>=t.ttl}async function p({options:e,key:t,HookPayload:n}){if(await e.cache.has(t)){let{result:r,birthTime:a}=await e.cache.get(t);return m({birthTime:a,options:e})?(await f({options:e,key:t,HookPayload:n}),P):r}return P}async function g({options:e,key:t,result:n,HookPayload:r}){return await u(C.Hooks.DATACACHE_RECORD_ADD_PRE,e.plugins,r),e.cache.set(t,{result:n,birthTime:Date.now()},e.ttl).then(async()=>{await u(C.Hooks.DATACACHE_RECORD_ADD_POST,e.plugins,{...r,result:n}),e.events.onCacheSet({key:t})})}async function f({options:e,key:t,HookPayload:n}){$(C.Hooks.DATACACHE_RECORD_DELETE_PRE,C.Hooks.DATACACHE_RECORD_DELETE_POST,e,n)(e.cache.delete)(t).then(()=>{e.events.onCacheDelete({key:t})})}async function L({result:e,runningQueryCache:t,key:n,executionStart:r,options:a,timeframeCache:o,timeoutCache:i,args:c,HookPayload:s}){let d=Date.now(),h=d-r;a.events.onAfterFunctionExecution({key:n,executionTime:h}),Ae({key:n,timeframeCache:o,executionTime:h,executionEnd:d}),await T({options:a,key:n,timeframeCache:o,executionTime:h,args:c})>=a.requestsThreshold?g({options:a,key:n,result:e,HookPayload:s}).finally(()=>{t.delete(n),i.set(n,setTimeout(()=>{f({options:a,key:n,HookPayload:s}),i.delete(n)},a.ttl).unref())}):t.delete(n)}async function T({options:e,key:t,timeframeCache:n,executionTime:r,args:a}){let o=0,i=n.get(t);return e.candidateFunction?o=await Re(e,r,a,i):e.millisecondThreshold?o=Oe(r,e,i):o=i.length,o}function Oe(e,t,n){let r=0;return e>t.millisecondThreshold&&(r=n.filter(a=>a.executionTime>t.millisecondThreshold).length),r}async function Re(e,t,n,r){let a=0;return await e.candidateFunction({timeFrameCacheRecords:r,options:e,args:n})&&(a=e.requestsThreshold),a}function xe({timeoutCache:e,key:t}){e.get(t)?.refresh()}function be(e=10){return Math.random().toString(36).substring(2,e+2)}function ve(){let e={getDataCacheKey:y,getDataCacheRecord:p,addDataCacheRecord:g,deleteDataCacheRecord:f,isDataCacheRecordExpired:m,getExceedingAmount:T};return Object.defineProperty(e,"getDataCacheRecord",{value:p,writable:!1}),Object.defineProperty(e,"addDataCacheRecord",{value:g,writable:!1}),Object.defineProperty(e,"deleteDataCacheRecord",{value:f,writable:!1}),Object.defineProperty(e,"isDataCacheRecordExpired",{value:m,writable:!1}),e}async function _({options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,args:o,originalMethod:i}){let c={options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,fnArgs:o,internals:ve()};await u(C.Hooks.INIT,e.plugins,c);let s=await p({options:e,key:t,HookPayload:c});if(s!==P)return e.keepAlive&&xe({timeoutCache:n,key:t}),await u(C.Hooks.CACHE_HIT,e.plugins,{...c,result:s}),e.events.onCacheHit({key:t}),Promise.resolve(s);let d=Pe({key:t,runningQueryCache:r});if(d!==x)return await u(C.Hooks.CACHE_HIT,e.plugins,{...c,result:d}),d;De({options:e,key:t,timeframeCache:a}),await u(C.Hooks.EXECUTION_PRE,e.plugins,c),e.events.onBeforeFunctionExecution({key:t});let h=Date.now(),l=i(...o);return await u(C.Hooks.EXECUTION_POST,e.plugins,{...c,result:l}),l instanceof Promise?(r.set(t,l),l.then(R=>L({result:R,runningQueryCache:r,key:t,executionStart:h,options:e,timeframeCache:a,timeoutCache:n,args:o,HookPayload:c})),l):(L({result:l,runningQueryCache:r,key:t,executionStart:h,options:e,timeframeCache:a,timeoutCache:n,args:o,HookPayload:c}),l)}function H(e){let t=new Map,n=new Map,r=be(),a=new Map,o={...E,...e,events:{...E.events,...e.events||{}}};return{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeoutCache:a,options:o}}function K(e={}){let{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeoutCache:a,options:o}=H(e);return w({options:o}),u(k.Hooks.SETUP,o.plugins,{options:{...o,plugins:void 0},key:"",timeoutCache:a,runningQueryCache:n,timeframeCache:t,fnArgs:[],internals:{getDataCacheKey:y,getDataCacheRecord:p,addDataCacheRecord:g,deleteDataCacheRecord:f,isDataCacheRecordExpired:m,getExceedingAmount:T}}),function(c,s,d){let h=d.value;d.value=async function(...l){let R=c.constructor.name+JSON.stringify(this),z=y([s,r,R,JSON.stringify(l)]);return _({options:o,key:z,timeoutCache:a,runningQueryCache:n,timeframeCache:t,args:l,originalMethod:h})}}}function B(e,t={}){let{timeframeCache:n,runningQueryCache:r,uniqueIdentifier:a,timeoutCache:o,options:i}=H(t);return w({options:i}),u(k.Hooks.SETUP,i.plugins,{options:{...i,plugins:void 0},key:"",timeoutCache:o,runningQueryCache:r,timeframeCache:n,fnArgs:[],internals:{getDataCacheKey:y,getDataCacheRecord:p,addDataCacheRecord:g,deleteDataCacheRecord:f,isDataCacheRecordExpired:m,getExceedingAmount:T}}),function(...s){return _({options:i,key:y([a,JSON.stringify(s)]),timeoutCache:o,runningQueryCache:r,timeframeCache:n,args:s,originalMethod:e})}}0&&(module.exports={CacheCandidate,CacheCandidateOptionsDefault,cacheCandidate});
1
+ "use strict";var Z=Object.create;var O=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var ne=Object.getPrototypeOf,ae=Object.prototype.hasOwnProperty;var I=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),ie=(e,t)=>{for(var n in t)O(e,n,{get:t[n],enumerable:!0})},N=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of te(t))!ae.call(e,a)&&a!==n&&O(e,a,{get:()=>t[a],enumerable:!(i=ee(t,a))||i.enumerable});return e};var D=(e,t,n)=>(n=e!=null?Z(ne(e)):{},N(t||!e||!e.__esModule?O(n,"default",{value:e,enumerable:!0}):n,e)),re=e=>N(O({},"__esModule",{value:!0}),e);var R=I((je,W)=>{"use strict";var w=Object.defineProperty,oe=Object.getOwnPropertyDescriptor,ce=Object.getOwnPropertyNames,de=Object.prototype.hasOwnProperty,ue=(e,t)=>{for(var n in t)w(e,n,{get:t[n],enumerable:!0})},se=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ce(t))!de.call(e,a)&&a!==n&&w(e,a,{get:()=>t[a],enumerable:!(i=oe(t,a))||i.enumerable});return e},le=e=>se(w({},"__esModule",{value:!0}),e),U={};ue(U,{Hooks:()=>q});W.exports=le(U);var q=(e=>(e.SETUP="SETUP",e.INIT="INIT",e.EXECUTION_PRE="EXECUTION_PRE",e.EXECUTION_POST="EXECUTION_POST",e.DATACACHE_RECORD_ADD_PRE="DATACACHE_RECORD_ADD_PRE",e.DATACACHE_RECORD_ADD_POST="DATACACHE_RECORD_ADD_POST",e.DATACACHE_RECORD_DELETE_PRE="DATACACHE_RECORD_DELETE_PRE",e.DATACACHE_RECORD_DELETE_POST="DATACACHE_RECORD_DELETE_POST",e.CACHE_HIT="CACHE_HIT",e))(q||{})});var $=I((Ue,Q)=>{"use strict";var k=Object.defineProperty,he=Object.getOwnPropertyDescriptor,Ce=Object.getOwnPropertyNames,fe=Object.prototype.hasOwnProperty,pe=(e,t)=>{for(var n in t)k(e,n,{get:t[n],enumerable:!0})},me=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Ce(t))!fe.call(e,a)&&a!==n&&k(e,a,{get:()=>t[a],enumerable:!(i=he(t,a))||i.enumerable});return e},ge=e=>me(k({},"__esModule",{value:!0}),e),J={};pe(J,{Hook:()=>Te,hook:()=>Ae});Q.exports=ge(J);var ye={before:()=>{},after:()=>{}},Ee={before:()=>{},after:()=>{}};function Te(e){return function(t,n,i){let a={},r=i.value,{before:o,after:h}={...ye,...e};return i.value=function(...c){o({context:a,args:c,target:t,propertyKey:n,descriptor:i});let d=r.apply(this,c);return h({context:a,args:c,target:t,propertyKey:n,descriptor:i,result:d}),d},i}}function Ae(e){return function(t){let n={},{before:i,after:a}={...Ee,...e};return function(...r){i({context:n,args:r});let o=t(...r);return a({context:n,args:r,result:o}),o}}}});var _e={};ie(_e,{CacheCandidate:()=>z,CacheCandidateOptionsDefault:()=>A,cacheCandidate:()=>G});module.exports=re(_e);var j=(e=new Map)=>({get:async t=>e.get(JSON.stringify(t)),has:async t=>e.has(JSON.stringify(t)),set:async(t,n)=>e.set(JSON.stringify(t),n),delete:async t=>e.delete(JSON.stringify(t))});var A={ttl:6e5,timeFrame:3e4,requestsThreshold:1,cache:j(),expirationMode:"default",keepAlive:!1,plugins:[],fetchingMode:"default",events:{onCacheHit:e=>e,onCacheSet:e=>e,onCacheDelete:e=>e,onBeforeFunctionExecution:e=>e,onAfterFunctionExecution:e=>e}};var P=Symbol("DataCacheRecordNotFound"),b=Symbol("RunningQueryRecordNotFound");var S=D(R());var V=require("crypto");var L=D(R()),X=D($());async function u(e,t,n){for(let i of t){let a=i.hooks.filter(o=>o.hook===e);if(a.length>1)throw new Error(`Only one hook instance per plugin is allowed. ${i.name} has ${a.length} instances of ${e}}`);if(a.length===0)continue;let r=a[0];if(xe(r))await r.action(n,i.additionalParameters);else throw new Error(`Hook ${e} for plugin ${i.name} is not actionable.`)}}function xe(e){return[e.action!==void 0,typeof e.action=="function"].every(t=>t===!0)}function K(e,t,n,i){return(0,X.hook)({before:()=>{u(e,n.plugins,i)},after:({result:a})=>{u(t,n.plugins,{...i,result:a})}})}function H({options:e}){e.plugins&&e.plugins.forEach(t=>{if(!t.hooks||!Array.isArray(t.hooks)||t.hooks.length===0)throw new Error(`Plugin ${t.name} has no hooks.`);t.hooks.forEach(n=>{if(!L.Hooks[n.hook])throw new Error(`Invalid hook ${n}`)})})}var l=D(R());function Oe(e,t){return Date.now()<e+t.timeFrame}function De({key:e,timeframeCache:t,executionTime:n,executionEnd:i}){t.has(e)?t.get(e).push({executionTime:n,executionEnd:i}):t.set(e,[{executionEnd:i,executionTime:n}])}function Pe({options:e,key:t,timeframeCache:n}){if(n.has(t)){let i=n.get(t),a=i.filter(({executionEnd:r})=>Oe(r,e));i.length!==a.length&&n.set(t,a)}}function Re({key:e,runningQueryCache:t}){return t.has(e)?t.get(e):b}function T(...e){return(0,V.createHash)("sha256").update(e.join("|")).digest("hex")}function g({birthTime:e,options:t}){return Date.now()-e>=t.ttl}async function y({options:e,key:t,HookPayload:n,staleMap:i}){if(await e.cache.has(t)){let{result:a,birthTime:r}=await e.cache.get(t);return g({birthTime:r,options:e})?(await p({options:e,key:t,HookPayload:n,result:a,staleMap:i}),P):a}return P}async function E({options:e,key:t,result:n,HookPayload:i}){return await u(l.Hooks.DATACACHE_RECORD_ADD_PRE,e.plugins,i),e.cache.set(t,{result:n,birthTime:Date.now()},e.ttl).then(async()=>{await u(l.Hooks.DATACACHE_RECORD_ADD_POST,e.plugins,{...i,result:n}),e.events.onCacheSet({key:t})})}async function p({options:e,key:t,HookPayload:n,result:i,staleMap:a}){e.fetchingMode==="stale-while-revalidate"&&a.set(t,i);let r=_(e.expirationMode,"call-delete")?e.cache.delete:()=>Promise.resolve();K(l.Hooks.DATACACHE_RECORD_DELETE_PRE,l.Hooks.DATACACHE_RECORD_DELETE_POST,e,n)(r)(t).then(()=>{e.events.onCacheDelete({key:t})})}function _(e,t){switch(t){case"set-timeout":return e==="default"||e==="timeout-only";case"call-delete":return e==="default"}}async function B({result:e,runningQueryCache:t,key:n,executionStart:i,options:a,timeframeCache:r,timeoutCache:o,args:h,staleMap:c,HookPayload:d}){let C=Date.now(),f=C-i;a.events.onAfterFunctionExecution({key:n,executionTime:f}),De({key:n,timeframeCache:r,executionTime:f,executionEnd:C}),await x({options:a,key:n,timeframeCache:r,executionTime:f,args:h})>=a.requestsThreshold?E({options:a,key:n,result:e,HookPayload:d}).finally(()=>{t.delete(n),_(a.expirationMode,"set-timeout")&&o.set(n,setTimeout(()=>{p({options:a,key:n,HookPayload:d,result:e,staleMap:c}),o.delete(n)},a.ttl).unref())}):t.delete(n)}async function x({options:e,key:t,timeframeCache:n,executionTime:i,args:a}){let r=0,o=n.get(t);return e.candidateFunction?r=await be(e,i,a,o):e.millisecondThreshold?r=ve(i,e,o):r=o.length,r}function ve(e,t,n){let i=0;return e>t.millisecondThreshold&&(i=n.filter(a=>a.executionTime>t.millisecondThreshold).length),i}async function be(e,t,n,i){let a=0;return await e.candidateFunction({timeFrameCacheRecords:i,options:e,args:n})&&(a=e.requestsThreshold),a}function we({timeoutCache:e,key:t}){e.get(t)?.refresh()}function ke(e=10){return Math.random().toString(36).substring(2,e+2)}function F(e){if(e.expirationMode==="eject"&&e.keepAlive===!0)throw new Error(`The cache-candidate currently doesn't support the "eject" expiration mode with the "keepAlive" option.`)}function He(){let e={getDataCacheKey:T,getDataCacheRecord:y,addDataCacheRecord:E,deleteDataCacheRecord:p,isDataCacheRecordExpired:g,getExceedingAmount:x};return Object.defineProperty(e,"getDataCacheRecord",{value:y,writable:!1}),Object.defineProperty(e,"addDataCacheRecord",{value:E,writable:!1}),Object.defineProperty(e,"deleteDataCacheRecord",{value:p,writable:!1}),Object.defineProperty(e,"isDataCacheRecordExpired",{value:g,writable:!1}),e}async function v({options:e,key:t,timeoutCache:n,runningQueryCache:i,timeframeCache:a,args:r,staleMap:o,originalMethod:h}){let c={options:e,key:t,timeoutCache:n,runningQueryCache:i,timeframeCache:a,fnArgs:r,internals:He()};await u(l.Hooks.INIT,e.plugins,c);let d=await y({options:e,key:t,HookPayload:c,staleMap:o});if(d!==P)return e.keepAlive&&_(e.expirationMode,"set-timeout")&&we({timeoutCache:n,key:t}),await u(l.Hooks.CACHE_HIT,e.plugins,{...c,result:d}),e.events.onCacheHit({key:t}),Promise.resolve(d);let C=Re({key:t,runningQueryCache:i});if(C!==b)return await u(l.Hooks.CACHE_HIT,e.plugins,{...c,result:C}),e.events.onCacheHit({key:t}),C;if(e.fetchingMode==="stale-while-revalidate"&&o.has(t)){let m=o.get(t);return await u(l.Hooks.CACHE_HIT,e.plugins,{...c,result:m}),e.events.onCacheHit({key:t}),o.delete(t),v({options:e,key:t,timeoutCache:n,runningQueryCache:i,timeframeCache:a,args:r,staleMap:o,originalMethod:h}),Promise.resolve(m)}Pe({options:e,key:t,timeframeCache:a}),await u(l.Hooks.EXECUTION_PRE,e.plugins,c),e.events.onBeforeFunctionExecution({key:t});let f=Date.now(),s=h(...r);return await u(l.Hooks.EXECUTION_POST,e.plugins,{...c,result:s}),s instanceof Promise?(i.set(t,s),s.then(m=>B({result:m,runningQueryCache:i,key:t,executionStart:f,options:e,timeframeCache:a,timeoutCache:n,args:r,staleMap:o,HookPayload:c})),s):(B({result:s,runningQueryCache:i,key:t,executionStart:f,options:e,timeframeCache:a,timeoutCache:n,args:r,staleMap:o,HookPayload:c}),s)}function M(e){let t=new Map,n=new Map,i=ke(),a=new Map,r={...A,...e,events:{...A.events,...e.events||{}}};return{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:i,timeoutCache:a,options:r,staleMap:new Map}}function z(e={}){let{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:i,timeoutCache:a,options:r,staleMap:o}=M(e);return H({options:r}),F(r),u(S.Hooks.SETUP,r.plugins,{options:{...r,plugins:void 0},key:"",timeoutCache:a,runningQueryCache:n,timeframeCache:t,fnArgs:[],internals:{getDataCacheKey:T,getDataCacheRecord:y,addDataCacheRecord:E,deleteDataCacheRecord:p,isDataCacheRecordExpired:g,getExceedingAmount:x}}),function(c,d,C){let f=C.value;C.value=async function(...s){let m=c.constructor.name+JSON.stringify(this),Y=T([d,i,m,JSON.stringify(s)]);return v({options:r,key:Y,timeoutCache:a,runningQueryCache:n,timeframeCache:t,args:s,staleMap:o,originalMethod:f})}}}function G(e,t={}){let{timeframeCache:n,runningQueryCache:i,uniqueIdentifier:a,timeoutCache:r,options:o,staleMap:h}=M(t);return H({options:o}),F(o),u(S.Hooks.SETUP,o.plugins,{options:{...o,plugins:void 0},key:"",timeoutCache:r,runningQueryCache:i,timeframeCache:n,fnArgs:[],internals:{getDataCacheKey:T,getDataCacheRecord:y,addDataCacheRecord:E,deleteDataCacheRecord:p,isDataCacheRecordExpired:g,getExceedingAmount:x}}),function(...d){return v({options:o,key:T([a,JSON.stringify(d)]),timeoutCache:r,runningQueryCache:i,timeframeCache:n,args:d,staleMap:h,originalMethod:e})}}0&&(module.exports={CacheCandidate,CacheCandidateOptionsDefault,cacheCandidate});
@@ -0,0 +1 @@
1
+ export {};
@@ -1,14 +1,15 @@
1
- import { CacheCandidateOptions, TimeoutCache, RunningQueryCache, TimeFrameCache } from './models';
1
+ import { CacheCandidateOptions, TimeoutCache, RunningQueryCache, TimeFrameCache, CacheCandidateInputOptions, StaleMap } from './models';
2
2
  import { PluginPayload } from '@jointly/cache-candidate-plugin-base';
3
3
  export declare function getDataCacheKey(...args: any[]): string;
4
4
  export declare function isDataCacheRecordExpired({ birthTime, options }: {
5
5
  birthTime: any;
6
6
  options: any;
7
7
  }): boolean;
8
- export declare function getDataCacheRecord({ options, key, HookPayload }: {
8
+ export declare function getDataCacheRecord({ options, key, HookPayload, staleMap }: {
9
9
  options: any;
10
10
  key: any;
11
11
  HookPayload: any;
12
+ staleMap: any;
12
13
  }): Promise<unknown | undefined>;
13
14
  export declare function addDataCacheRecord({ options, key, result, HookPayload }: {
14
15
  options: CacheCandidateOptions;
@@ -16,10 +17,12 @@ export declare function addDataCacheRecord({ options, key, result, HookPayload }
16
17
  result: unknown;
17
18
  HookPayload: PluginPayload;
18
19
  }): Promise<void>;
19
- export declare function deleteDataCacheRecord({ options, key, HookPayload }: {
20
+ export declare function deleteDataCacheRecord({ options, key, HookPayload, result, staleMap }: {
20
21
  options: CacheCandidateOptions;
21
22
  key: string;
22
23
  HookPayload: PluginPayload;
24
+ result: unknown;
25
+ staleMap: StaleMap;
23
26
  }): Promise<void>;
24
27
  export declare function getExceedingAmount({ options, key, timeframeCache, executionTime, args }: {
25
28
  options: any;
@@ -29,19 +32,22 @@ export declare function getExceedingAmount({ options, key, timeframeCache, execu
29
32
  args: any;
30
33
  }): Promise<number>;
31
34
  export declare function uniqid(length?: number): string;
32
- export declare function letsCandidate({ options, key, timeoutCache, runningQueryCache, timeframeCache, args, originalMethod }: {
35
+ export declare function checkExpirationMode(options: CacheCandidateOptions): void;
36
+ export declare function letsCandidate({ options, key, timeoutCache, runningQueryCache, timeframeCache, args, staleMap, originalMethod }: {
33
37
  options: CacheCandidateOptions;
34
38
  key: string;
35
39
  timeoutCache: TimeoutCache;
36
40
  runningQueryCache: RunningQueryCache;
37
41
  timeframeCache: TimeFrameCache;
38
42
  args: any[];
43
+ staleMap: StaleMap;
39
44
  originalMethod: (...args: any[]) => Promise<unknown>;
40
45
  }): Promise<unknown>;
41
- export declare function getInitialState(_options: Partial<CacheCandidateOptions>): {
46
+ export declare function getInitialState(_options: CacheCandidateInputOptions): {
42
47
  timeframeCache: TimeFrameCache;
43
48
  runningQueryCache: RunningQueryCache;
44
49
  uniqueIdentifier: string;
45
50
  timeoutCache: TimeoutCache;
46
51
  options: CacheCandidateOptions;
52
+ staleMap: Map<any, any>;
47
53
  };
package/dist/lib.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { CacheCandidateOptions } from './models';
2
- export declare function CacheCandidate(_options?: Partial<CacheCandidateOptions>): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
3
- export declare function cacheCandidate<T extends (...args: any[]) => Promise<any>>(fn: T, _options?: Partial<CacheCandidateOptions>): (...args: Parameters<T>) => ReturnType<T>;
1
+ import { CacheCandidateInputOptions } from './models';
2
+ export declare function CacheCandidate(_options?: CacheCandidateInputOptions): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
3
+ export declare function cacheCandidate<T extends (...args: any[]) => Promise<any>>(fn: T, _options?: CacheCandidateInputOptions): (...args: Parameters<T>) => ReturnType<T>;
package/dist/models.d.ts CHANGED
@@ -12,14 +12,46 @@ export interface CacheCandidateCacheAdapter {
12
12
  delete: (key: string) => Promise<boolean>;
13
13
  [key: string]: any;
14
14
  }
15
+ export interface CacheCandidateInputOptions {
16
+ ttl?: number;
17
+ timeFrame?: number;
18
+ candidateFunction?: (CandidateFunctionOptions: any) => boolean;
19
+ millisecondThreshold?: number;
20
+ requestsThreshold?: number;
21
+ expirationMode?: 'default' | 'timeout-only' | 'eject';
22
+ keepAlive?: boolean;
23
+ cache?: CacheCandidateCacheAdapter;
24
+ fetchingMode?: 'stale-while-revalidate' | 'default';
25
+ events?: {
26
+ onCacheHit?: ({ key }: {
27
+ key: string;
28
+ }) => void;
29
+ onCacheSet?: ({ key }: {
30
+ key: string;
31
+ }) => void;
32
+ onCacheDelete?: ({ key }: {
33
+ key: string;
34
+ }) => void;
35
+ onBeforeFunctionExecution?: ({ key }: {
36
+ key: string;
37
+ }) => void;
38
+ onAfterFunctionExecution?: ({ key, executionTime }: {
39
+ key: string;
40
+ executionTime: number;
41
+ }) => void;
42
+ };
43
+ plugins?: Array<CacheCandidatePluginWithAdditionalParameters>;
44
+ }
15
45
  export interface CacheCandidateOptions {
16
46
  ttl: number;
17
47
  timeFrame: number;
18
48
  candidateFunction?: (CandidateFunctionOptions: any) => boolean;
19
49
  millisecondThreshold?: number;
20
50
  requestsThreshold: number;
51
+ expirationMode: 'default' | 'timeout-only' | 'eject';
21
52
  keepAlive: boolean;
22
53
  cache: CacheCandidateCacheAdapter;
54
+ fetchingMode: 'stale-while-revalidate' | 'default';
23
55
  events: {
24
56
  onCacheHit: ({ key }: {
25
57
  key: string;
@@ -51,5 +83,6 @@ export interface TimeFrameCacheRecord {
51
83
  export type TimeFrameCache = Map<string, Array<TimeFrameCacheRecord>>;
52
84
  export type RunningQueryCache = Map<string, Promise<any>>;
53
85
  export type TimeoutCache = Map<string, NodeJS.Timeout>;
86
+ export type StaleMap = Map<string, unknown>;
54
87
  export declare const DataCacheRecordNotFound: unique symbol;
55
88
  export declare const RunningQueryRecordNotFound: unique symbol;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jointly/cache-candidate",
3
3
  "private": false,
4
- "version": "1.5.10",
4
+ "version": "2.0.1",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -33,24 +33,24 @@
33
33
  },
34
34
  "prepare": "npm run build",
35
35
  "devDependencies": {
36
- "@commitlint/cli": "^17.3.0",
37
- "@commitlint/config-conventional": "^17.3.0",
38
- "@jointly/cache-candidate-plugin-base": "^1.2.6",
39
- "@types/jest": "^29.2.5",
40
- "@types/node": "^18.11.12",
36
+ "@commitlint/cli": "^19.3.0",
37
+ "@commitlint/config-conventional": "^19.2.2",
38
+ "@jointly/cache-candidate-plugin-base": "^2.0.0",
39
+ "@types/jest": "^29.5.12",
40
+ "@types/node": "^20.12.10",
41
41
  "@types/redis": "^4.0.11",
42
- "@typescript-eslint/eslint-plugin": "^5.46.0",
43
- "@typescript-eslint/parser": "^5.46.0",
42
+ "@typescript-eslint/eslint-plugin": "^7.8.0",
43
+ "@typescript-eslint/parser": "^7.8.0",
44
44
  "benchmark": "^2.1.4",
45
- "esbuild": "^0.16.4",
45
+ "esbuild": "^0.21.0",
46
46
  "eslint": "^8.29.0",
47
47
  "generate-changelog": "^1.8.0",
48
- "husky": "^8.0.2",
49
- "jest": "^29.3.1",
50
- "lint-staged": "^13.1.0",
51
- "prettier": "^2.8.1",
52
- "rimraf": "^3.0.2",
53
- "ts-jest": "^29.0.3",
48
+ "husky": "^9.0.11",
49
+ "jest": "^29.7.0",
50
+ "lint-staged": "^15.2.2",
51
+ "prettier": "^3.2.5",
52
+ "rimraf": "^5.0.5",
53
+ "ts-jest": "^29.1.2",
54
54
  "typescript": "^4.9.4"
55
55
  },
56
56
  "lint-staged": {
@@ -66,6 +66,6 @@
66
66
  "license": "MIT",
67
67
  "author": "Jointly <dev@jointly.pro>",
68
68
  "dependencies": {
69
- "hook-fn": "^1.0.3"
69
+ "hook-fn": "^1.0.6"
70
70
  }
71
71
  }