@jointly/cache-candidate 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Jointly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # What is it?
2
+
3
+ This is a library providing both a decorator and a higher-order function to cache the result of a method if given conditions are met.
4
+
5
+ # How does it work?
6
+
7
+ ## Decorator
8
+
9
+ The decorator expects to receive an object with a partial of the properties contained in the `CacheCandidateOptions` interface.
10
+ Every non-passed property will be set to its default value using the `CacheCandidateOptionsDefault` object.
11
+ The decorator will return a method that will return a Promise fulfilled with the cached value if the method has already been called with the same arguments and the conditions are met.
12
+ The conditions are, within the given `timeFrame`:
13
+
14
+ - If a `candidateFunction` is provided, it returns `true` for at least `requestThreshold` times.
15
+ - If a `millisecondsThreshold` is provided, it passed such threshold (Execution time) at least `requestThreshold` times.
16
+ - If only a `requestThreshold` is provided (default), it is called at least `requestThreshold` times.
17
+
18
+ You can pass an additional `dependencyKeys` property to the decorator options which provides an invalidation mechanism to be called manually in your codebase.
19
+ This property can be either an array of string, a function that returns an array of string or a function that returns a Promise fulfilled with an array of string.
20
+ Both the function and the Promise will receive the result of the method on which the CacheCandidate operates.
21
+ In case of an async method, the promise will be fulfilled before passing the result to the `dependencyKeys` function.
22
+ The `dependencyKeys` function will be called only if the cache adapter correctly sets the value in the cache (i.e. the `.set` method is fulfilled).
23
+
24
+ ## Higher-order function
25
+
26
+ Everything is the same as the decorator, but the function takes the original function as the first argument and the options as the second argument.
27
+ The function returns a new function that will return a Promise fulfilled with the cached value if the method has already been called with the same arguments and the conditions are met.
28
+
29
+ ## Key composition
30
+
31
+ The cache key is composed based on the following criteria:
32
+
33
+ ### Decorator
34
+
35
+ - The class constructor name.
36
+ - The method name.
37
+ - The arguments passed to the method. (JSON.stringify)
38
+ - `instanceIdentifier`: A uniqid generated for each instance of the class. It uses the instance properties to generate the id. (JSON.stringify)
39
+ - `uniqueIdentifier`: A uniqid generated to allow multiple files to contain the same class with the same method.
40
+
41
+
42
+ ### Higher-order function
43
+
44
+ - The arguments passed to the method. (JSON.stringify)
45
+ - `uniqueIdentifier`: A uniqid generated to allow multiple files to contain the same class with the same method.
46
+
47
+
48
+ ## Cache invalidation
49
+
50
+ The cache invalidation is done using the exported `cacheCandidateDependencyManager` object.
51
+ The object exposes the `invalidate` method which accepts a string.
52
+ The string is one of the dependency keys returned by the `dependencyKeys` function/array defined in the decorator options.
53
+
54
+ ### Example
55
+
56
+ ```typescript
57
+ import { cacheCandidate, cacheCandidateDependencyManager } from 'cache-candidate';
58
+
59
+ class MyClass {
60
+ @CacheCandidate({
61
+ dependencyKeys: (users) => {
62
+ return users.map((user) => `users-${user.id}`);
63
+ },
64
+ })
65
+ public async getUsers() {
66
+ // Do something
67
+ return users;
68
+ }
69
+
70
+ public async updateUser(user) {
71
+ // Do something
72
+ cacheCandidateDependencyManager.invalidate(`users-${user.id}`);
73
+ }
74
+ }
75
+
76
+ const myClass = new MyClass();
77
+ const users = await myClass.getUsers();
78
+ users[0].name = 'New name';
79
+ await myClass.updateUser(users[0]); // This will invalidate the cache
80
+ ```
81
+
82
+
83
+ # Other Info
84
+
85
+ ## Constraints
86
+
87
+ - The decorator can only be applied to methods that return a `Promise`.
88
+ - The `candidateFunction` must be synchronous to maintain good performances. If passed an async function bypassing type checking, the candidateFunction will return a Promise thus not working properly.
89
+
90
+ ## Cache Stampede
91
+
92
+ The decorator prevents the cache stampede problem by using a `Map` called `runningQueries` which saves the promise of the method call.
93
+ If multiple calls are made to the same method with the same arguments, the method will be called only once and the other calls will wait for the Promise to finish.
94
+ The `runningQueries` Map will be cleaned after the method execution is finished.
95
+
96
+ ## Considerations on cache operations
97
+
98
+ The decorator doesn't consider the correct execution of the given cache methods.
99
+ It isn't the decorator's responsibility to check if the cache methods are working properly.
100
+ The only consideration done is based on the fact that the `set` method will eventually fulfill or reject the Promise as it uses the `.finally` method to delete the key from the `runningQueries` Map and set the timeout to clean the cache record.
101
+
102
+ ## Problems with synchronous methods
103
+
104
+ If the given method is synchronous, the decorator could not work as expected.
105
+ The reason is that the decorator internally transforms the method to an asynchronous one, so executing the same method multiple times during the same Event Loop tick will prevent the cache from setting the value in time, thus not working as expected.
106
+ The expected result will still be achieved, but please consider the multiple cache set operations during development.
107
+
108
+ ## Considerations on identification
109
+
110
+ The decorator uses the `instanceIdentifier` and `uniqueIdentifier` to identify the class instance and the method.
111
+ The `instanceIdentifier` is generated using the instance properties, so if the instance properties change, the `instanceIdentifier` will change as well, thus changing the data cache key.
112
+ The `uniqueIdentifier` is generated using the class name and the method name.
113
+
114
+ ## Dictionary
115
+
116
+ ### DataCache
117
+
118
+ The effective cache instance.
119
+
120
+ ### TimeFrameCache
121
+
122
+ The Map containing the method executions in a given timeframe.
123
+
124
+ ### RunningQueryCache
125
+
126
+ The Map containing the running queries (Promises fulfilled or yet to be fulfilled).
127
+
128
+ ### Comparation Value
129
+
130
+ The value calculated based on candidate conditions which is then compared to the requestThreshold.
@@ -0,0 +1,2 @@
1
+ import { CacheCandidateCacheAdapter, DataCacheRecord } from '../models';
2
+ export declare const makeAsyncMap: (map?: Map<string, DataCacheRecord>) => CacheCandidateCacheAdapter;
@@ -0,0 +1,3 @@
1
+ import { createClient } from 'redis';
2
+ import { CacheCandidateCacheAdapter } from '../models';
3
+ export declare const makeRedisCache: (client: ReturnType<typeof createClient>) => CacheCandidateCacheAdapter;
@@ -0,0 +1,2 @@
1
+ import { CacheCandidateOptions } from './models';
2
+ export declare const CacheCandidateOptionsDefault: CacheCandidateOptions;
@@ -0,0 +1,3 @@
1
+ export * from './default';
2
+ export * from './models';
3
+ export * from './lib';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var U=Object.create;var f=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var G=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var _=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),q=(e,t)=>{for(var n in t)f(e,n,{get:t[n],enumerable:!0})},x=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of K(t))!j.call(e,a)&&a!==n&&f(e,a,{get:()=>t[a],enumerable:!(r=Q(t,a))||r.enumerable});return e};var H=(e,t,n)=>(n=e!=null?U(G(e)):{},x(t||!e||!e.__esModule?f(n,"default",{value:e,enumerable:!0}):n,e)),J=e=>x(f({},"__esModule",{value:!0}),e);var F=_((ve,v)=>{"use strict";var y=Object.defineProperty,X=Object.getOwnPropertyDescriptor,W=Object.getOwnPropertyNames,$=Object.prototype.hasOwnProperty,B=(e,t)=>{for(var n in t)y(e,n,{get:t[n],enumerable:!0})},Y=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of W(t))!$.call(e,a)&&a!==n&&y(e,a,{get:()=>t[a],enumerable:!(r=X(t,a))||r.enumerable});return e},z=e=>Y(y({},"__esModule",{value:!0}),e),N={};B(N,{Hook:()=>ee,hook:()=>te});v.exports=z(N);var V={before:()=>{},after:()=>{}},Z={before:()=>{},after:()=>{}};function ee(e){return function(t,n,r){let a={},i=r.value,{before:o,after:c}={...V,...e};return r.value=function(...u){o({context:a,args:u,target:t,propertyKey:n,descriptor:r});let s=i.apply(this,u);return c({context:a,args:u,target:t,propertyKey:n,descriptor:r,result:s}),s},r}}function te(e){return function(t){let n={},{before:r,after:a}={...Z,...e};return function(...i){r({context:n,args:i});let o=t(...i);return a({context:n,args:i,result:o}),o}}}});var w=_((be,S)=>{"use strict";var D=Object.defineProperty,re=Object.getOwnPropertyDescriptor,ie=Object.getOwnPropertyNames,oe=Object.prototype.hasOwnProperty,ce=(e,t)=>{for(var n in t)D(e,n,{get:t[n],enumerable:!0})},ue=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ie(t))!oe.call(e,a)&&a!==n&&D(e,a,{get:()=>t[a],enumerable:!(r=re(t,a))||r.enumerable});return e},se=e=>ue(D({},"__esModule",{value:!0}),e),b={};ce(b,{Hooks:()=>I});S.exports=se(b);var I=(e=>(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 Oe={};q(Oe,{CacheCandidate:()=>Ae,CacheCandidateOptionsDefault:()=>T,DataCacheRecordNotFound:()=>h,Events:()=>p,RunningQueryRecordNotFound:()=>g,cacheCandidate:()=>Re});module.exports=J(Oe);var P=(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 T={ttl:6e5,timeFrame:3e4,requestsThreshold:3,cache:P(),keepAlive:!1,plugins:[],events:{onCacheHit:e=>e,onCacheSet:e=>e,onCacheDelete:e=>e,onBeforeFunctionExecution:e=>e,onAfterFunctionExecution:e=>e,onLog:e=>e}};var p=(a=>(a.RUNNING_QUERY="RUNNING_QUERY",a.CHECKING_CANDIDATE_FUNCTION="CHECKING_CANDIDATE_FUNCTION",a.CHECKING_MILLISECOND_THRESHOLD="CHECKING_MILLISECOND_THRESHOLD",a.CHECKING_REQUESTS_THRESHOLD="CHECKING_REQUESTS_THRESHOLD",a))(p||{}),h=Symbol("DataCacheRecordNotFound"),g=Symbol("RunningQueryRecordNotFound");var M=require("crypto");var ne=H(F());async function C(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(ae(i))await i.action(n,r.additionalParameters);else throw new Error(`Hook ${e} for plugin ${r.name} is not actionable.`)}}function ae(e){return[e.action!==void 0,typeof e.action=="function"].every(t=>t===!0)}var l=H(w());function de(e,t){return Date.now()<e+t.timeFrame}function Ce({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 le({options:e,key:t,timeframeCache:n}){if(n.has(t)){let r=n.get(t),a=r.filter(({executionEnd:i})=>de(i,e));r.length!==a.length&&n.set(t,a)}}function me({options:e,key:t,runningQueryCache:n}){return n.has(t)?(e.events.onLog({key:t,event:"RUNNING_QUERY"}),n.get(t)):g}function A(...e){return(0,M.createHash)("sha256").update(e.join("|")).digest("hex")}function he({birthTime:e,options:t}){return Date.now()-e>=t.timeFrame}async function fe({options:e,key:t,HookPayload:n}){if(await e.cache.has(t)){let{result:r,birthTime:a}=await e.cache.get(t);return he({birthTime:a,options:e})?(await k({options:e,key:t,HookPayload:n}),h):r}return h}async function ge({options:e,key:t,result:n}){return e.cache.set(t,{result:n,birthTime:Date.now()},e.ttl)}async function k({options:e,key:t,HookPayload:n}){await C(l.Hooks.DATACACHE_RECORD_DELETE_PRE,e.plugins,n),await e.cache.delete(t),await C(l.Hooks.DATACACHE_RECORD_DELETE_POST,e.plugins,n),e.events.onCacheDelete({key:t})}async function L({result:e,runningQueryCache:t,key:n,executionStart:r,options:a,timeframeCache:i,timeFrameTimeoutCache:o,args:c,HookPayload:u}){let s=Date.now(),m=s-r;a.events.onAfterFunctionExecution({key:n,executionTime:m}),Ce({key:n,timeframeCache:i,executionTime:m,executionEnd:s}),Ee({options:a,key:n,timeframeCache:i,executionTime:m,args:c})>=a.requestsThreshold&&(await C(l.Hooks.DATACACHE_RECORD_ADD_PRE,a.plugins,u),ge({options:a,key:n,result:e}).then(async()=>{await C(l.Hooks.DATACACHE_RECORD_ADD_POST,a.plugins,{...u,result:e}),a.events.onCacheSet({key:n})}).finally(()=>{t.delete(n),o.set(n,setTimeout(()=>{k({options:a,key:n,HookPayload:u})},a.ttl).unref())}))}function Ee({options:e,key:t,timeframeCache:n,executionTime:r,args:a}){let i=0,o=n.get(t);return e.candidateFunction?(e.events.onLog({key:t,event:"CHECKING_CANDIDATE_FUNCTION"}),i=pe(e,r,a,o)):e.millisecondThreshold?(e.events.onLog({key:t,event:"CHECKING_MILLISECOND_THRESHOLD"}),i=Te(r,e,o)):(e.events.onLog({key:t,event:"CHECKING_REQUESTS_THRESHOLD"}),i=o.length),i}function Te(e,t,n){let r=0;return e>t.millisecondThreshold&&(r=n.filter(a=>a.executionTime>t.millisecondThreshold).length),r}function pe(e,t,n,r){let a=0;return e.candidateFunction({timeFrameCacheRecords:r,options:e,args:n})&&(a=e.requestsThreshold),a}function ye({timeFrameTimeoutCache:e,key:t,options:n}){clearTimeout(e.get(t)),e.set(t,setTimeout(()=>{n.cache.delete(t)},n.ttl).unref())}function De(e=10){return Math.random().toString(36).substring(2,e+2)}async function R({options:e,key:t,timeFrameTimeoutCache:n,runningQueryCache:r,timeframeCache:a,args:i,originalMethod:o}){let c={options:{...e,plugins:void 0},key:t,timeFrameTimeoutCache:n,runningQueryCache:r,timeframeCache:a,fnArgs:i};await C(l.Hooks.INIT,e.plugins,c);let u=await fe({options:e,key:t,HookPayload:c});if(u!==h)return e.keepAlive&&ye({timeFrameTimeoutCache:n,key:t,options:e}),await C(l.Hooks.CACHE_HIT,e.plugins,{...c,result:u}),e.events.onCacheHit({key:t}),Promise.resolve(u);let s=me({options:e,key:t,runningQueryCache:r});if(s!==g)return await C(l.Hooks.CACHE_HIT,e.plugins,{...c,result:s}),e.events.onCacheHit({key:t}),s;le({options:e,key:t,timeframeCache:a}),await C(l.Hooks.EXECUTION_PRE,e.plugins,c),e.events.onBeforeFunctionExecution({key:t});let m=Date.now(),d=o(...i);return await C(l.Hooks.EXECUTION_POST,e.plugins,{...c,result:d}),d instanceof Promise?(r.set(t,d),d.then(E=>L({result:E,runningQueryCache:r,key:t,executionStart:m,options:e,timeframeCache:a,timeFrameTimeoutCache:n,args:i,HookPayload:c})),d):(L({result:d,runningQueryCache:r,key:t,executionStart:m,options:e,timeframeCache:a,timeFrameTimeoutCache:n,args:i,HookPayload:c}),d)}function O(e){let t=new Map,n=new Map,r=De(),a=new Map,i={...T,...e};return{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeFrameTimeoutCache:a,options:i}}function Ae(e={}){let{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeFrameTimeoutCache:a,options:i}=O(e);return function(o,c,u){let s=u.value;u.value=async function(...m){let d=o.constructor.name+JSON.stringify(this),E=A([o.constructor.name,c,r,d,JSON.stringify(m)]);return R({options:i,key:E,timeFrameTimeoutCache:a,runningQueryCache:n,timeframeCache:t,args:m,originalMethod:s})}}}function Re(e,t={}){let{timeframeCache:n,runningQueryCache:r,uniqueIdentifier:a,timeFrameTimeoutCache:i,options:o}=O(t);return async(...c)=>R({options:o,key:A([a,JSON.stringify(c)]),timeFrameTimeoutCache:i,runningQueryCache:r,timeframeCache:n,args:c,originalMethod:e})}0&&(module.exports={CacheCandidate,CacheCandidateOptionsDefault,DataCacheRecordNotFound,Events,RunningQueryRecordNotFound,cacheCandidate});
@@ -0,0 +1,19 @@
1
+ import { CacheCandidateOptions, TimeFrameTimeoutCache, RunningQueryCache, TimeFrameCache } from './models';
2
+ export declare function getDataCacheKey(...args: any[]): string;
3
+ export declare function uniqid(length?: number): string;
4
+ export declare function letsCandidate({ options, key, timeFrameTimeoutCache, runningQueryCache, timeframeCache, args, originalMethod }: {
5
+ options: CacheCandidateOptions;
6
+ key: string;
7
+ timeFrameTimeoutCache: TimeFrameTimeoutCache;
8
+ runningQueryCache: RunningQueryCache;
9
+ timeframeCache: TimeFrameCache;
10
+ args: any[];
11
+ originalMethod: (...args: any[]) => Promise<unknown>;
12
+ }): Promise<unknown>;
13
+ export declare function getInitialState(_options: Partial<CacheCandidateOptions>): {
14
+ timeframeCache: TimeFrameCache;
15
+ runningQueryCache: RunningQueryCache;
16
+ uniqueIdentifier: string;
17
+ timeFrameTimeoutCache: TimeFrameTimeoutCache;
18
+ options: CacheCandidateOptions;
19
+ };
package/dist/lib.d.ts ADDED
@@ -0,0 +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(fn: (...args: any[]) => any, _options?: Partial<CacheCandidateOptions>): (...args: any[]) => Promise<unknown>;
@@ -0,0 +1,64 @@
1
+ /// <reference types="node" />
2
+ import { CacheCandidatePluginWithAdditionalParameters } from '@jointly/cache-candidate-plugin-base';
3
+ export interface CandidateFunctionOptions {
4
+ timeFrameCacheRecords: Array<TimeFrameCacheRecord>;
5
+ options: CacheCandidateOptions;
6
+ args: any;
7
+ }
8
+ export interface CacheCandidateCacheAdapter {
9
+ get: (key: string) => Promise<DataCacheRecord | undefined>;
10
+ set: (key: string, value: any, ttl: number) => Promise<any>;
11
+ has: (key: string) => Promise<boolean>;
12
+ delete: (key: string) => Promise<boolean>;
13
+ }
14
+ export interface CacheCandidateOptions {
15
+ ttl: number;
16
+ timeFrame: number;
17
+ candidateFunction?: (CandidateFunctionOptions: any) => boolean;
18
+ millisecondThreshold?: number;
19
+ requestsThreshold: number;
20
+ keepAlive: boolean;
21
+ cache: CacheCandidateCacheAdapter;
22
+ events: {
23
+ onCacheHit: ({ key }: {
24
+ key: string;
25
+ }) => void;
26
+ onCacheSet: ({ key }: {
27
+ key: string;
28
+ }) => void;
29
+ onCacheDelete: ({ key }: {
30
+ key: string;
31
+ }) => void;
32
+ onBeforeFunctionExecution: ({ key }: {
33
+ key: string;
34
+ }) => void;
35
+ onAfterFunctionExecution: ({ key, executionTime }: {
36
+ key: string;
37
+ executionTime: number;
38
+ }) => void;
39
+ onLog: ({ key, event }: {
40
+ key: string;
41
+ event: Events;
42
+ }) => void;
43
+ };
44
+ plugins?: Array<CacheCandidatePluginWithAdditionalParameters>;
45
+ }
46
+ export interface DataCacheRecord {
47
+ result: any;
48
+ birthTime: number;
49
+ }
50
+ export interface TimeFrameCacheRecord {
51
+ executionTime: number;
52
+ executionEnd: Date;
53
+ }
54
+ export type TimeFrameCache = Map<string, Array<TimeFrameCacheRecord>>;
55
+ export type RunningQueryCache = Map<string, Promise<any>>;
56
+ export type TimeFrameTimeoutCache = Map<string, NodeJS.Timeout>;
57
+ export declare enum Events {
58
+ RUNNING_QUERY = "RUNNING_QUERY",
59
+ CHECKING_CANDIDATE_FUNCTION = "CHECKING_CANDIDATE_FUNCTION",
60
+ CHECKING_MILLISECOND_THRESHOLD = "CHECKING_MILLISECOND_THRESHOLD",
61
+ CHECKING_REQUESTS_THRESHOLD = "CHECKING_REQUESTS_THRESHOLD"
62
+ }
63
+ export declare const DataCacheRecordNotFound: unique symbol;
64
+ export declare const RunningQueryRecordNotFound: unique symbol;
@@ -0,0 +1,3 @@
1
+ import { CacheCandidatePluginWithAdditionalParameters, Hooks, PluginPayload } from '@jointly/cache-candidate-plugin-base';
2
+ export declare function ExecuteHook(hook: Hooks, plugins: CacheCandidatePluginWithAdditionalParameters[] | undefined, payload: PluginPayload): Promise<void>;
3
+ export declare function pluginHookWrap(HookBefore: Hooks, HookAfter: Hooks, payload: PluginPayload): (fn: (...args: any[]) => unknown) => (...args: any[]) => unknown;
@@ -0,0 +1,9 @@
1
+ export declare class MockClass {
2
+ a: number;
3
+ b: number;
4
+ aAsync: number;
5
+ bAsync: number;
6
+ constructor(a: number, b: number, aAsync: number, bAsync: number);
7
+ mockAsyncFunction(step: number): Promise<number>;
8
+ mockFunction(step: number): number;
9
+ }
@@ -0,0 +1,8 @@
1
+ export declare class MockClass {
2
+ a: number;
3
+ b: number;
4
+ aAsync: number;
5
+ bAsync: number;
6
+ constructor(a: number, b: number, aAsync: number, bAsync: number);
7
+ mockFunction(step: number): number;
8
+ }
@@ -0,0 +1,9 @@
1
+ import { CacheCandidateOptions } from '../models';
2
+ export declare const sleep: (ms: number) => Promise<unknown>;
3
+ export declare const TTL = 100;
4
+ export declare const EXECUTION_MARGIN = 50;
5
+ export declare const ENOUGH_TIME = 250;
6
+ export declare function step(): () => number;
7
+ export declare const eventHits: Map<string, number>;
8
+ export declare const options: Partial<CacheCandidateOptions>;
9
+ export declare function flushMaps(): void;
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@jointly/cache-candidate",
3
+ "private": false,
4
+ "version": "1.0.0",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "scripts": {
19
+ "ts-types": "tsc --emitDeclarationOnly --outDir dist",
20
+ "check": "npm run prettier && npm run lint && npm test",
21
+ "build": "npm run check && rimraf dist && NODE_ENV=production node esbuild.js && npm run ts-types",
22
+ "build-doctor-bubbleproof": "rimraf dist && NODE_ENV=development node esbuild.js && npx clinic bubbleproof -- node ./doctor-patient.js",
23
+ "prettier": "prettier --write ./src",
24
+ "lint": "eslint ./src --ext .ts",
25
+ "dev": "jest --watch",
26
+ "test": "jest --no-cache",
27
+ "coverage": "jest --coverage",
28
+ "release:common": "npm run build && git push --follow-tags origin main && npm publish --access public",
29
+ "release:patch": "changelog -p && git add CHANGELOG.md && git commit -m 'docs: updated changelog' && npm version patch && npm run release:common",
30
+ "release:minor": "changelog -m && git add CHANGELOG.md && git commit -m 'docs: updated changelog' && npm version minor && npm run release:common",
31
+ "release:major": "changelog -M && git add CHANGELOG.md && git commit -m 'docs: updated changelog' && npm version major && npm run release:common"
32
+ },
33
+ "prepare": "npm run build",
34
+ "devDependencies": {
35
+ "@commitlint/cli": "^17.3.0",
36
+ "@commitlint/config-conventional": "^17.3.0",
37
+ "@types/jest": "^29.2.4",
38
+ "@types/node": "^18.11.12",
39
+ "@types/redis": "^4.0.11",
40
+ "@typescript-eslint/eslint-plugin": "^5.46.0",
41
+ "@typescript-eslint/parser": "^5.46.0",
42
+ "esbuild": "^0.16.4",
43
+ "eslint": "^8.29.0",
44
+ "generate-changelog": "^1.8.0",
45
+ "husky": "^8.0.2",
46
+ "jest": "^29.3.1",
47
+ "lint-staged": "^13.1.0",
48
+ "prettier": "^2.8.1",
49
+ "rimraf": "^3.0.2",
50
+ "ts-jest": "^29.0.3",
51
+ "typescript": "^4.9.4",
52
+ "@jointly/cache-candidate-plugin-base": "^1.0.0"
53
+ },
54
+ "lint-staged": {
55
+ "src/**/*.{js,jsx,ts,tsx}": [
56
+ "npx prettier --write",
57
+ "npx eslint --fix"
58
+ ]
59
+ },
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git://github.com/JointlyTech/cache-candidate.git"
63
+ },
64
+ "license": "MIT",
65
+ "author": "Jointly <dev@jointly.pro>",
66
+ "dependencies": {
67
+ "hook-fn": "^1.0.3"
68
+ }
69
+ }