@jointly/cache-candidate 2.1.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -26
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/lib.d.ts +1 -2
- package/dist/models.d.ts +1 -2
- package/dist/plugins/index.d.ts +1 -1
- package/package.json +16 -19
- package/dist/index.test.d.ts +0 -1
- package/dist/test/MockClass.d.ts +0 -9
- package/dist/test/MockClass2.d.ts +0 -8
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# What is it?
|
|
2
2
|
|
|
3
|
-
This is a library providing
|
|
3
|
+
This is a library providing a higher-order function to cache the result of a function if given conditions are met.
|
|
4
4
|
|
|
5
5
|
# How do I install it?
|
|
6
6
|
|
|
@@ -84,9 +84,7 @@ await sleep(31000); // <-- This will invalidate the cache because of the ttl
|
|
|
84
84
|
|
|
85
85
|
# How does it work?
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
The library exposes the `cacheCandidate` function which accepts the function to be cached as the first argument and the options as the second argument.
|
|
87
|
+
The library exposes the `cacheCandidate` function which accepts the function to be cached as the first argument and the options as the second argument.
|
|
90
88
|
The returned function is an async function which returns a Promise fulfilled with the cached value if the method has already been called with the same arguments and/or the conditions are met.
|
|
91
89
|
|
|
92
90
|
The options available are:
|
|
@@ -131,30 +129,12 @@ The options available are:
|
|
|
131
129
|
- `plugins` (_optional_): An array of plugins to be used. Default: `[]`.
|
|
132
130
|
Please, refer to the [@jointly/cache-candidate-plugin-base](https://github.com/JointlyTech/cache-candidate-plugin-base) package for more information.
|
|
133
131
|
|
|
134
|
-
##
|
|
135
|
-
|
|
136
|
-
The decorator expects to receive the options as the first argument and works exactly as the higher-order function.
|
|
137
|
-
|
|
138
|
-
### Example
|
|
139
|
-
|
|
140
|
-
```ts
|
|
141
|
-
import { CacheCandidate } from '@jointly/cache-candidate';
|
|
132
|
+
## Key composition
|
|
142
133
|
|
|
143
|
-
|
|
144
|
-
@CacheCandidate({
|
|
145
|
-
requestsThreshold: 3,
|
|
146
|
-
ttl: 30000,
|
|
147
|
-
})
|
|
148
|
-
async getUsers(filters = {}) {
|
|
149
|
-
return db.query('SELECT * FROM users WHERE ?', filters);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
134
|
+
The cache key is composed based on the following criteria:
|
|
152
135
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
await myInstance.getUsers({ name: 'John' }); // <-- This won't be cached, because the requestsThreshold is 3
|
|
156
|
-
await myInstance.getUsers({ name: 'John' }); // <-- This WILL be cached, because the requestsThreshold is 3!
|
|
157
|
-
```
|
|
136
|
+
- The arguments passed to the method (JSON.stringify)
|
|
137
|
+
- `uniqueIdentifier`: A uniqid generated to allow multiple files to contain the same function name
|
|
158
138
|
|
|
159
139
|
## Conditions / Criterias
|
|
160
140
|
The conditions are, within the given `timeFrame`:
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { CacheCandidateOptionsDefault } from './default';
|
|
2
2
|
export { CandidateFunctionOptions, CacheCandidateCacheAdapter, CacheCandidateOptions, DataCacheRecord } from './models';
|
|
3
|
-
export {
|
|
3
|
+
export { cacheCandidate } from './lib';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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),re=(e,t)=>{for(var n in t)O(e,n,{get:t[n],enumerable:!0})},N=(e,t,n,r)=>{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:!(r=ee(t,a))||r.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)),ie=e=>N(O({},"__esModule",{value:!0}),e);var R=I((je,W)=>{"use strict";var b=Object.defineProperty,oe=Object.getOwnPropertyDescriptor,ce=Object.getOwnPropertyNames,de=Object.prototype.hasOwnProperty,ue=(e,t)=>{for(var n in t)b(e,n,{get:t[n],enumerable:!0})},se=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ce(t))!de.call(e,a)&&a!==n&&b(e,a,{get:()=>t[a],enumerable:!(r=oe(t,a))||r.enumerable});return e},le=e=>se(b({},"__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 Q=I((Ue,K)=>{"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,r)=>{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:!(r=he(t,a))||r.enumerable});return e},ge=e=>me(k({},"__esModule",{value:!0}),e),J={};pe(J,{Hook:()=>Te,hook:()=>Ae});K.exports=ge(J);var ye={before:()=>{},after:()=>{}},Ee={before:()=>{},after:()=>{}};function Te(e){return function(t,n,r){let a={},i=r.value,{before:o,after:h}={...ye,...e};return r.value=function(...c){o({context:a,args:c,target:t,propertyKey:n,descriptor:r});let d=i.apply(this,c);return h({context:a,args:c,target:t,propertyKey:n,descriptor:r,result:d}),d},r}}function Ae(e){return function(t){let n={},{before:r,after:a}={...Ee,...e};return function(...i){r({context:n,args:i});let o=t(...i);return a({context:n,args:i,result:o}),o}}}});var _e={};re(_e,{CacheCandidate:()=>z,CacheCandidateOptionsDefault:()=>A,cacheCandidate:()=>G});module.exports=ie(_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"),w=Symbol("RunningQueryRecordNotFound");var S=D(R());var V=require("crypto");var $=D(R()),L=D(Q());async function u(e,t,n){for(let r of t){let a=r.hooks.filter(o=>o.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 i=a[0];if(xe(i))await i.action(n,r.additionalParameters);else throw new Error(`Hook ${e} for plugin ${r.name} is not actionable.`)}}function xe(e){return[e.action!==void 0,typeof e.action=="function"].every(t=>t===!0)}function X(e,t,n,r){return(0,L.hook)({before:()=>{u(e,n.plugins,r)},after:({result:a})=>{u(t,n.plugins,{...r,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(!$.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:r}){t.has(e)?t.get(e).push({executionTime:n,executionEnd:r}):t.set(e,[{executionEnd:r,executionTime:n}])}function Pe({options:e,key:t,timeframeCache:n}){if(n.has(t)){let r=n.get(t),a=r.filter(({executionEnd:i})=>Oe(i,e));r.length!==a.length&&n.set(t,a)}}function Re({key:e,runningQueryCache:t}){return t.has(e)?t.get(e):w}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:r}){if(await e.cache.has(t)){let{result:a,birthTime:i}=await e.cache.get(t);return g({birthTime:i,options:e})?(await m({options:e,key:t,HookPayload:n,result:a,staleMap:r}),P):a}return P}async function E({options:e,key:t,result:n,HookPayload:r}){return await u(l.Hooks.DATACACHE_RECORD_ADD_PRE,e.plugins,r),e.cache.set(t,{result:n,birthTime:Date.now()},e.ttl).then(async()=>{await u(l.Hooks.DATACACHE_RECORD_ADD_POST,e.plugins,{...r,result:n}),e.events.onCacheSet({key:t})})}async function m({options:e,key:t,HookPayload:n,result:r,staleMap:a}){e.fetchingMode==="stale-while-revalidate"&&a.set(t,r);let i=_(e.expirationMode,"call-delete")?e.cache.delete:()=>Promise.resolve();X(l.Hooks.DATACACHE_RECORD_DELETE_PRE,l.Hooks.DATACACHE_RECORD_DELETE_POST,e,n)(i)(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:r,options:a,timeframeCache:i,timeoutCache:o,args:h,staleMap:c,HookPayload:d}){let C=Date.now(),f=C-r;a.events.onAfterFunctionExecution({key:n,executionTime:f}),De({key:n,timeframeCache:i,executionTime:f,executionEnd:C}),await x({options:a,key:n,timeframeCache:i,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(()=>{m({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:r,args:a}){let i=0,o=n.get(t);return e.candidateFunction?i=await we(e,r,a,o):e.millisecondThreshold?i=ve(r,e,o):i=o.length,i}function ve(e,t,n){let r=0;return e>t.millisecondThreshold&&(r=n.filter(a=>a.executionTime>t.millisecondThreshold).length),r}async function we(e,t,n,r){let a=0;return await e.candidateFunction({timeFrameCacheRecords:r,options:e,args:n})&&(a=e.requestsThreshold),a}function be({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:m,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:m,writable:!1}),Object.defineProperty(e,"isDataCacheRecordExpired",{value:g,writable:!1}),e}async function v({options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,args:i,staleMap:o,originalMethod:h}){let c={options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,fnArgs:i,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")&&be({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:r});if(C!==w)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 p=o.get(t);return await u(l.Hooks.CACHE_HIT,e.plugins,{...c,result:p}),e.events.onCacheHit({key:t}),o.delete(t),v({options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,args:i,staleMap:o,originalMethod:h}),Promise.resolve(p)}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(...i);return await u(l.Hooks.EXECUTION_POST,e.plugins,{...c,result:s}),s instanceof Promise?(r.set(t,s),s.then(p=>B({result:p,runningQueryCache:r,key:t,executionStart:f,options:e,timeframeCache:a,timeoutCache:n,args:i,staleMap:o,HookPayload:c})).catch(p=>{throw r.delete(t),p}),s):(B({result:s,runningQueryCache:r,key:t,executionStart:f,options:e,timeframeCache:a,timeoutCache:n,args:i,staleMap:o,HookPayload:c}),s)}function M(e){let t=new Map,n=new Map,r=ke(),a=new Map,i={...A,...e,events:{...A.events,...e.events||{}}};return{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeoutCache:a,options:i,staleMap:new Map}}function z(e={}){let{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeoutCache:a,options:i,staleMap:o}=M(e);return H({options:i}),F(i),u(S.Hooks.SETUP,i.plugins,{options:{...i,plugins:void 0},key:"",timeoutCache:a,runningQueryCache:n,timeframeCache:t,fnArgs:[],internals:{getDataCacheKey:T,getDataCacheRecord:y,addDataCacheRecord:E,deleteDataCacheRecord:m,isDataCacheRecordExpired:g,getExceedingAmount:x}}),function(c,d,C){let f=C.value;C.value=async function(...s){let p=c.constructor.name+JSON.stringify(this),Y=T([d,r,p,JSON.stringify(s)]);return v({options:i,key:Y,timeoutCache:a,runningQueryCache:n,timeframeCache:t,args:s,staleMap:o,originalMethod:f})}}}function G(e,t={}){let{timeframeCache:n,runningQueryCache:r,uniqueIdentifier:a,timeoutCache:i,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:i,runningQueryCache:r,timeframeCache:n,fnArgs:[],internals:{getDataCacheKey:T,getDataCacheRecord:y,addDataCacheRecord:E,deleteDataCacheRecord:m,isDataCacheRecordExpired:g,getExceedingAmount:x}}),function(...d){return v({options:o,key:t.customKeyFunction?t.customKeyFunction(d):T([a,JSON.stringify(d)]),timeoutCache:i,runningQueryCache:r,timeframeCache:n,args:d,staleMap:h,originalMethod:e})}}0&&(module.exports={CacheCandidate,CacheCandidateOptionsDefault,cacheCandidate});
|
|
1
|
+
"use strict";var G=Object.create;var y=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var ee=Object.getPrototypeOf,te=Object.prototype.hasOwnProperty;var _=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),ne=(e,t)=>{for(var n in t)y(e,n,{get:t[n],enumerable:!0})},F=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Z(t))!te.call(e,a)&&a!==n&&y(e,a,{get:()=>t[a],enumerable:!(r=Y(t,a))||r.enumerable});return e};var T=(e,t,n)=>(n=e!=null?G(ee(e)):{},F(t||!e||!e.__esModule?y(n,"default",{value:e,enumerable:!0}):n,e)),ae=e=>F(y({},"__esModule",{value:!0}),e);var x=_((Ie,N)=>{"use strict";var w=Object.defineProperty,re=Object.getOwnPropertyDescriptor,oe=Object.getOwnPropertyNames,ie=Object.prototype.hasOwnProperty,ce=(e,t)=>{for(var n in t)w(e,n,{get:t[n],enumerable:!0})},ue=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of oe(t))!ie.call(e,a)&&a!==n&&w(e,a,{get:()=>t[a],enumerable:!(r=re(t,a))||r.enumerable});return e},se=e=>ue(w({},"__esModule",{value:!0}),e),S={};ce(S,{Hooks:()=>I});N.exports=se(S);var I=(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))(I||{})});var q=_((Ne,U)=>{"use strict";var b=Object.defineProperty,de=Object.getOwnPropertyDescriptor,le=Object.getOwnPropertyNames,he=Object.prototype.hasOwnProperty,Ce=(e,t)=>{for(var n in t)b(e,n,{get:t[n],enumerable:!0})},fe=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of le(t))!he.call(e,a)&&a!==n&&b(e,a,{get:()=>t[a],enumerable:!(r=de(t,a))||r.enumerable});return e},me=e=>fe(b({},"__esModule",{value:!0}),e),j={};Ce(j,{Hook:()=>ye,hook:()=>Te});U.exports=me(j);var pe={before:()=>{},after:()=>{}},ge={before:()=>{},after:()=>{}};function ye(e){return function(t,n,r){let a={},o=r.value,{before:i,after:l}={...pe,...e};return r.value=function(...c){i({context:a,args:c,target:t,propertyKey:n,descriptor:r});let u=o.apply(this,c);return l({context:a,args:c,target:t,propertyKey:n,descriptor:r,result:u}),u},r}}function Te(e){return function(t){let n={},{before:r,after:a}={...ge,...e};return function(...o){r({context:n,args:o});let i=t(...o);return a({context:n,args:o,result:i}),i}}}});var He={};ne(He,{CacheCandidateOptionsDefault:()=>m,cacheCandidate:()=>z});module.exports=ae(He);var M=(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 m={ttl:6e5,timeFrame:3e4,requestsThreshold:1,cache:M(),expirationMode:"default",keepAlive:!1,plugins:[],fetchingMode:"default",events:{onCacheHit:e=>e,onCacheSet:e=>e,onCacheDelete:e=>e,onBeforeFunctionExecution:e=>e,onAfterFunctionExecution:e=>e}};var E=Symbol("DataCacheRecordNotFound"),v=Symbol("RunningQueryRecordNotFound");var V=T(x());var X=require("crypto");var W=T(x()),K=T(q());async function s(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,K.hook)({before:()=>{s(e,n.plugins,r)},after:({result:a})=>{s(t,n.plugins,{...r,result:a})}})}function L({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(!W.Hooks[n.hook])throw new Error(`Invalid hook ${n}`)})})}var d=T(x());function xe(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 Re({options:e,key:t,timeframeCache:n}){if(n.has(t)){let r=n.get(t),a=r.filter(({executionEnd:o})=>xe(o,e));r.length!==a.length&&n.set(t,a)}}function Pe({key:e,runningQueryCache:t}){return t.has(e)?t.get(e):v}function A(e){return(0,X.createHash)("sha256").update(e.join("|")).digest("hex")}function R({birthTime:e,options:t}){return Date.now()-e>=t.ttl}async function P({options:e,key:t,HookPayload:n,staleMap:r}){if(await e.cache.has(t)){let{result:a,birthTime:o}=await e.cache.get(t);return R({birthTime:o,options:e})?(await p({options:e,key:t,HookPayload:n,result:a,staleMap:r}),E):a}return E}async function D({options:e,key:t,result:n,HookPayload:r}){return await s(d.Hooks.DATACACHE_RECORD_ADD_PRE,e.plugins,r),e.cache.set(t,{result:n,birthTime:Date.now()},e.ttl).then(async()=>{await s(d.Hooks.DATACACHE_RECORD_ADD_POST,e.plugins,{...r,result:n}),e.events.onCacheSet({key:t})})}async function p({options:e,key:t,HookPayload:n,result:r,staleMap:a}){e.fetchingMode==="stale-while-revalidate"&&a.set(t,r);let o=H(e.expirationMode,"call-delete")?e.cache.delete:()=>Promise.resolve();$(d.Hooks.DATACACHE_RECORD_DELETE_PRE,d.Hooks.DATACACHE_RECORD_DELETE_POST,e,n)(o)(t).then(()=>{e.events.onCacheDelete({key:t})})}function H(e,t){switch(t){case"set-timeout":return e==="default"||e==="timeout-only";case"call-delete":return e==="default"}}async function Q({result:e,runningQueryCache:t,key:n,executionStart:r,options:a,timeframeCache:o,timeoutCache:i,args:l,staleMap:c,HookPayload:u}){let C=Date.now(),f=C-r;a.events.onAfterFunctionExecution({key:n,executionTime:f}),Ae({key:n,timeframeCache:o,executionTime:f,executionEnd:C}),await O({options:a,key:n,timeframeCache:o,executionTime:f,args:l})>=a.requestsThreshold?D({options:a,key:n,result:e,HookPayload:u}).finally(()=>{t.delete(n),H(a.expirationMode,"set-timeout")&&i.set(n,setTimeout(()=>{p({options:a,key:n,HookPayload:u,result:e,staleMap:c}),i.delete(n)},a.ttl).unref())}):t.delete(n)}async function O({options:e,key:t,timeframeCache:n,executionTime:r,args:a}){let o=0,i=n.get(t);return e.candidateFunction?o=await Oe(e,r,a,i):e.millisecondThreshold?o=De(r,e,i):o=i.length,o}function De(e,t,n){let r=0;return e>t.millisecondThreshold&&(r=n.filter(a=>a.executionTime>t.millisecondThreshold).length),r}async function Oe(e,t,n,r){let a=0;return await e.candidateFunction({timeFrameCacheRecords:r,options:e,args:n})&&(a=e.requestsThreshold),a}function ve({timeoutCache:e,key:t}){e.get(t)?.refresh()}function we(e=10){return Math.random().toString(36).substring(2,e+2)}function J(e){if(e.expirationMode==="eject"&&e.keepAlive===!0)throw new Error(`The cache-candidate can't support the "eject" expiration mode with the "keepAlive" option.`)}function be(){return{getDataCacheKey:A,getDataCacheRecord:P,addDataCacheRecord:D,deleteDataCacheRecord:p,isDataCacheRecordExpired:R,getExceedingAmount:O}}async function k({options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,args:o,staleMap:i,originalMethod:l}){let c={options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,fnArgs:o,internals:be()};await s(d.Hooks.INIT,e.plugins,c);let u=await P({options:e,key:t,HookPayload:c,staleMap:i});if(u!==E)return e.keepAlive&&H(e.expirationMode,"set-timeout")&&ve({timeoutCache:n,key:t}),await s(d.Hooks.CACHE_HIT,e.plugins,{...c,result:u}),e.events.onCacheHit({key:t}),Promise.resolve(u);let C=Pe({key:t,runningQueryCache:r});if(C!==v)return await s(d.Hooks.CACHE_HIT,e.plugins,{...c,result:C}),e.events.onCacheHit({key:t}),C;if(e.fetchingMode==="stale-while-revalidate"&&i.has(t)){let g=i.get(t);return await s(d.Hooks.CACHE_HIT,e.plugins,{...c,result:g}),e.events.onCacheHit({key:t}),i.delete(t),k({options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,args:o,staleMap:i,originalMethod:l}),Promise.resolve(g)}Re({options:e,key:t,timeframeCache:a}),await s(d.Hooks.EXECUTION_PRE,e.plugins,c),e.events.onBeforeFunctionExecution({key:t});let f=Date.now(),h=l(...o);return await s(d.Hooks.EXECUTION_POST,e.plugins,{...c,result:h}),h instanceof Promise?(r.set(t,h),h.then(g=>Q({result:g,runningQueryCache:r,key:t,executionStart:f,options:e,timeframeCache:a,timeoutCache:n,args:o,staleMap:i,HookPayload:c})).catch(()=>{r.delete(t)}),h):(Q({result:h,runningQueryCache:r,key:t,executionStart:f,options:e,timeframeCache:a,timeoutCache:n,args:o,staleMap:i,HookPayload:c}),h)}function B(e){let t=new Map,n=new Map,r=we(),a=new Map,o={...m,...e,events:{...m.events,...e.events||{}}};return{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeoutCache:a,options:o,staleMap:new Map}}function z(e,t={}){let{timeframeCache:n,runningQueryCache:r,uniqueIdentifier:a,timeoutCache:o,options:i,staleMap:l}=B(t);return L({options:i}),J(i),s(V.Hooks.SETUP,i.plugins,{options:{...i,plugins:void 0},key:"",timeoutCache:o,runningQueryCache:r,timeframeCache:n,fnArgs:[],internals:{getDataCacheKey:A,getDataCacheRecord:P,addDataCacheRecord:D,deleteDataCacheRecord:p,isDataCacheRecordExpired:R,getExceedingAmount:O}}),function(...u){return k({options:i,key:t.customKeyFunction?t.customKeyFunction(u):A([a,JSON.stringify(u)]),timeoutCache:o,runningQueryCache:r,timeframeCache:n,args:u,staleMap:l,originalMethod:e})}}0&&(module.exports={CacheCandidateOptionsDefault,cacheCandidate});
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CacheCandidateOptions, TimeoutCache, RunningQueryCache, TimeFrameCache, CacheCandidateInputOptions, StaleMap } from './models';
|
|
2
2
|
import { PluginPayload } from '@jointly/cache-candidate-plugin-base';
|
|
3
|
-
export declare function getDataCacheKey(
|
|
3
|
+
export declare function getDataCacheKey(args: any[]): string;
|
|
4
4
|
export declare function isDataCacheRecordExpired({ birthTime, options }: {
|
|
5
5
|
birthTime: any;
|
|
6
6
|
options: any;
|
package/dist/lib.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { CacheCandidateInputOptions } from './models';
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function cacheCandidate<T extends (...args: any[]) => Promise<any>>(fn: T, _options?: CacheCandidateInputOptions): (...args: Parameters<T>) => ReturnType<T>;
|
|
2
|
+
export declare function cacheCandidate<T extends (...args: any[]) => any>(fn: T, _options?: CacheCandidateInputOptions): (...args: Parameters<T>) => ReturnType<T> extends Promise<any> ? ReturnType<T> : Promise<ReturnType<T>>;
|
package/dist/models.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
import { CacheCandidatePluginWithAdditionalParameters } from '@jointly/cache-candidate-plugin-base';
|
|
3
2
|
export interface CandidateFunctionOptions {
|
|
4
3
|
timeFrameCacheRecords: Array<TimeFrameCacheRecord>;
|
|
@@ -83,7 +82,7 @@ export interface TimeFrameCacheRecord {
|
|
|
83
82
|
}
|
|
84
83
|
export type TimeFrameCache = Map<string, Array<TimeFrameCacheRecord>>;
|
|
85
84
|
export type RunningQueryCache = Map<string, Promise<any>>;
|
|
86
|
-
export type TimeoutCache = Map<string,
|
|
85
|
+
export type TimeoutCache = Map<string, ReturnType<typeof setTimeout>>;
|
|
87
86
|
export type StaleMap = Map<string, unknown>;
|
|
88
87
|
export declare const DataCacheRecordNotFound: unique symbol;
|
|
89
88
|
export declare const RunningQueryRecordNotFound: unique symbol;
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CacheCandidatePluginWithAdditionalParameters, Hooks, PluginPayload } from '@jointly/cache-candidate-plugin-base';
|
|
2
2
|
import { CacheCandidateOptions } from '../models';
|
|
3
3
|
export declare function ExecuteHook(hook: Hooks, plugins: Array<CacheCandidatePluginWithAdditionalParameters>, payload: PluginPayload): Promise<void>;
|
|
4
|
-
export declare function pluginHookWrap(HookBefore: Hooks, HookAfter: Hooks, options: CacheCandidateOptions, payload: PluginPayload): (fn: (...args: any[]) => unknown) => (...args: any[]) => unknown;
|
|
4
|
+
export declare function pluginHookWrap(HookBefore: Hooks, HookAfter: Hooks, options: CacheCandidateOptions, payload: PluginPayload): (fn: (...args: any[]) => unknown | Promise<unknown>) => (...args: any[]) => unknown;
|
|
5
5
|
export declare function checkHooks({ options }: {
|
|
6
6
|
options: CacheCandidateOptions;
|
|
7
7
|
}): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jointly/cache-candidate",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -22,9 +22,8 @@
|
|
|
22
22
|
"build-doctor-bubbleproof": "rimraf dist && NODE_ENV=development node esbuild.js && npx clinic bubbleproof -- node ./doctor-patient.js",
|
|
23
23
|
"prettier": "prettier --write ./src",
|
|
24
24
|
"lint": "eslint ./src --ext .ts",
|
|
25
|
-
"
|
|
26
|
-
"test": "
|
|
27
|
-
"coverage": "jest --coverage",
|
|
25
|
+
"test": "node --import tsx --test-force-exit --test **/index.test.ts",
|
|
26
|
+
"test:coverage": "node --import tsx --test-force-exit --experimental-test-coverage --test **/index.test.ts",
|
|
28
27
|
"benchmark": "node benchmark.js",
|
|
29
28
|
"release:common": "npm run build && git push --follow-tags origin main && npm publish --access public",
|
|
30
29
|
"release:patch": "changelog -p && git add CHANGELOG.md && git commit -m 'docs: updated changelog' && npm version patch && npm run release:common",
|
|
@@ -33,25 +32,23 @@
|
|
|
33
32
|
},
|
|
34
33
|
"prepare": "npm run build",
|
|
35
34
|
"devDependencies": {
|
|
36
|
-
"@commitlint/cli": "^19.
|
|
37
|
-
"@commitlint/config-conventional": "^19.
|
|
35
|
+
"@commitlint/cli": "^19.7.1",
|
|
36
|
+
"@commitlint/config-conventional": "^19.7.1",
|
|
38
37
|
"@jointly/cache-candidate-plugin-base": "^2.0.0",
|
|
39
|
-
"@types/
|
|
40
|
-
"@types/node": "^20.12.10",
|
|
38
|
+
"@types/node": "^22.13.5",
|
|
41
39
|
"@types/redis": "^4.0.11",
|
|
42
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
43
|
-
"@typescript-eslint/parser": "^
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^8.24.1",
|
|
41
|
+
"@typescript-eslint/parser": "^8.24.1",
|
|
44
42
|
"benchmark": "^2.1.4",
|
|
45
|
-
"esbuild": "^0.
|
|
46
|
-
"eslint": "^
|
|
43
|
+
"esbuild": "^0.25.0",
|
|
44
|
+
"eslint": "^9.21.0",
|
|
47
45
|
"generate-changelog": "^1.8.0",
|
|
48
|
-
"husky": "^9.
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"typescript": "^4.9.4"
|
|
46
|
+
"husky": "^9.1.7",
|
|
47
|
+
"lint-staged": "^15.4.3",
|
|
48
|
+
"prettier": "^3.5.2",
|
|
49
|
+
"rimraf": "^6.0.1",
|
|
50
|
+
"tsx": "^4.19.3",
|
|
51
|
+
"typescript": "^5.7.3"
|
|
55
52
|
},
|
|
56
53
|
"lint-staged": {
|
|
57
54
|
"src/**/*.{js,jsx,ts,tsx}": [
|
package/dist/index.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/test/MockClass.d.ts
DELETED