@gera2ld/async-memo 1.0.0 → 2.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 +11 -3
- package/dist/index.d.ts +34 -22
- package/dist/index.js +136 -101
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ const myApi = asyncMemo(myApiCall, {
|
|
|
18
18
|
|
|
19
19
|
// Call from anywhere
|
|
20
20
|
async function someAction() {
|
|
21
|
-
const response = await myApi(params);
|
|
21
|
+
const response = await myApi.call(params);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// Get the current value anytime
|
|
@@ -26,12 +26,13 @@ function getValueSync() {
|
|
|
26
26
|
return myApi.get(params);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
//
|
|
29
|
+
// Prepare a bound context to avoid passing args around
|
|
30
30
|
function otherPlace() {
|
|
31
|
-
const ctx = myApi.
|
|
31
|
+
const ctx = myApi.prepare(params);
|
|
32
32
|
ctx.get(); // sync get
|
|
33
33
|
ctx.reload(); // refresh data
|
|
34
34
|
ctx.isSettled(); // check loading state
|
|
35
|
+
ctx.set(value); // override cache
|
|
35
36
|
}
|
|
36
37
|
```
|
|
37
38
|
|
|
@@ -95,6 +96,7 @@ function createCache<T>() {
|
|
|
95
96
|
return {
|
|
96
97
|
get(cacheKey: string) { ... },
|
|
97
98
|
set(cacheKey: string, data?: T) { ... },
|
|
99
|
+
keys(): IterableIterator<string> { ... },
|
|
98
100
|
clear() { ... },
|
|
99
101
|
};
|
|
100
102
|
}
|
|
@@ -120,6 +122,9 @@ function createCache<T>() {
|
|
|
120
122
|
set(cacheKey: string, data?: T) {
|
|
121
123
|
target.value = { ...target.value, [cacheKey]: data };
|
|
122
124
|
},
|
|
125
|
+
*keys() {
|
|
126
|
+
yield* Object.keys(target.value);
|
|
127
|
+
},
|
|
123
128
|
clear() {
|
|
124
129
|
target.value = {};
|
|
125
130
|
},
|
|
@@ -144,6 +149,9 @@ function createCache<T>() {
|
|
|
144
149
|
set(cacheKey: string, data?: T) {
|
|
145
150
|
target.value = { ...target.value, [cacheKey]: data };
|
|
146
151
|
},
|
|
152
|
+
*keys() {
|
|
153
|
+
yield* Object.keys(target.value);
|
|
154
|
+
},
|
|
147
155
|
clear() {
|
|
148
156
|
target.value = {};
|
|
149
157
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -13,25 +13,6 @@ export interface CacheContext<U> {
|
|
|
13
13
|
/** Whether the value returned by `get` is fresh. */
|
|
14
14
|
isFresh(): boolean;
|
|
15
15
|
}
|
|
16
|
-
export interface CachedFunction<T extends unknown[], U, S extends CacheStorage> {
|
|
17
|
-
(...args: T): Promise<U>;
|
|
18
|
-
/** Return the currently cached value immediately. */
|
|
19
|
-
get(...args: T): U | undefined;
|
|
20
|
-
/** Delete the currently cached value. */
|
|
21
|
-
delete(...args: T): void;
|
|
22
|
-
/** Invalidate the current cached value and send a new request without deleting the old value. */
|
|
23
|
-
reload(...args: T): Promise<U>;
|
|
24
|
-
/** Whether the latest request is settled. */
|
|
25
|
-
isSettled(...args: T): boolean;
|
|
26
|
-
/** Whether the value returned by `get` is fresh. */
|
|
27
|
-
isFresh(...args: T): boolean;
|
|
28
|
-
/** Clear cache. */
|
|
29
|
-
clear(): void;
|
|
30
|
-
/** Get the cache context so we don't need to pass `args` around. */
|
|
31
|
-
context(...args: T): CacheContext<U>;
|
|
32
|
-
/** The cache storage, only used for testing purpose. */
|
|
33
|
-
cache: S;
|
|
34
|
-
}
|
|
35
16
|
type CachePrimitiveKey = string | number | undefined;
|
|
36
17
|
export interface CacheOptions<T extends unknown[]> {
|
|
37
18
|
/**
|
|
@@ -49,7 +30,7 @@ export interface CacheOptions<T extends unknown[]> {
|
|
|
49
30
|
*/
|
|
50
31
|
mustRevalidate: boolean;
|
|
51
32
|
/**
|
|
52
|
-
* Time to live, -1 for always. Default as `-1`.
|
|
33
|
+
* Time to live in ms, -1 for always. Default as `-1`.
|
|
53
34
|
*/
|
|
54
35
|
ttl: number;
|
|
55
36
|
}
|
|
@@ -63,9 +44,40 @@ export interface CacheData<U = unknown> {
|
|
|
63
44
|
export interface CacheStorage<U = unknown> {
|
|
64
45
|
get(cacheGroup: string): CacheData<U> | undefined;
|
|
65
46
|
set(cacheGroup: string, data?: CacheData<U>): void;
|
|
47
|
+
keys(): IterableIterator<string>;
|
|
66
48
|
clear(): void;
|
|
67
49
|
}
|
|
68
50
|
export declare function createAsyncMemoStorage(): Map<string, CacheData<unknown>>;
|
|
69
|
-
export declare
|
|
70
|
-
|
|
51
|
+
export declare class AsyncMemoContext<T extends unknown[], U, S extends CacheStorage = ReturnType<typeof createAsyncMemoStorage>> {
|
|
52
|
+
cache: S;
|
|
53
|
+
private fn;
|
|
54
|
+
private mustRevalidate;
|
|
55
|
+
private resolver;
|
|
56
|
+
private ttl;
|
|
57
|
+
constructor(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>>, cacheFactory?: () => S);
|
|
58
|
+
private resolveKey;
|
|
59
|
+
private getCachedData;
|
|
60
|
+
private isSettledByKey;
|
|
61
|
+
private isFreshByKey;
|
|
62
|
+
private getByKey;
|
|
63
|
+
private computeExpiresAt;
|
|
64
|
+
private setByKey;
|
|
65
|
+
private removeByKey;
|
|
66
|
+
private createPendingEntry;
|
|
67
|
+
private onSettled;
|
|
68
|
+
private reloadByKey;
|
|
69
|
+
private callByKey;
|
|
70
|
+
isSettled(...args: T): boolean;
|
|
71
|
+
isFresh(...args: T): boolean;
|
|
72
|
+
get(...args: T): U | undefined;
|
|
73
|
+
remove(...args: T): void;
|
|
74
|
+
reload(...args: T): Promise<U>;
|
|
75
|
+
call(...args: T): Promise<U>;
|
|
76
|
+
getContext(...args: T): CacheContext<U>;
|
|
77
|
+
prepare(...args: T): CacheContext<U>;
|
|
78
|
+
clear(): void;
|
|
79
|
+
prune(): void;
|
|
80
|
+
}
|
|
81
|
+
export declare function createAsyncMemo<S extends CacheStorage = ReturnType<typeof createAsyncMemoStorage>>(cacheFactory?: () => S): <U, T extends unknown[]>(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>>) => AsyncMemoContext<T, U, S>;
|
|
82
|
+
export declare const asyncMemo: <U, T extends unknown[]>(fn: (...args: T) => Promise<U>, options?: Partial<CacheOptions<T>> | undefined) => AsyncMemoContext<T, U, Map<string, CacheData<unknown>>>;
|
|
71
83
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,104 +1,139 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
//#region src/index.ts
|
|
2
|
+
var e = -1, t = {
|
|
3
|
+
mustRevalidate: !1,
|
|
4
|
+
resolver: () => "",
|
|
5
|
+
ttl: e
|
|
5
6
|
};
|
|
6
|
-
function
|
|
7
|
-
|
|
7
|
+
function n() {
|
|
8
|
+
return /* @__PURE__ */ new Map();
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
10
|
+
var r = class {
|
|
11
|
+
cache;
|
|
12
|
+
fn;
|
|
13
|
+
mustRevalidate;
|
|
14
|
+
resolver;
|
|
15
|
+
ttl;
|
|
16
|
+
constructor(e, r, i) {
|
|
17
|
+
this.fn = e, this.cache = (i || n)();
|
|
18
|
+
let a = {
|
|
19
|
+
...t,
|
|
20
|
+
...r
|
|
21
|
+
};
|
|
22
|
+
this.mustRevalidate = a.mustRevalidate, this.resolver = a.resolver, this.ttl = a.ttl;
|
|
23
|
+
}
|
|
24
|
+
resolveKey(...e) {
|
|
25
|
+
let t = this.resolver(...e);
|
|
26
|
+
return (Array.isArray(t) ? t : [t, t]).map((e) => `${e ?? ""}`);
|
|
27
|
+
}
|
|
28
|
+
getCachedData([e, t]) {
|
|
29
|
+
let n = this.cache.get(e);
|
|
30
|
+
return n?.key === t ? n : void 0;
|
|
31
|
+
}
|
|
32
|
+
isSettledByKey(e) {
|
|
33
|
+
return this.getCachedData(e)?.settled ?? !1;
|
|
34
|
+
}
|
|
35
|
+
isFreshByKey(e) {
|
|
36
|
+
let t = this.getCachedData(e);
|
|
37
|
+
return t?.settled ? t.expiresAt < 0 || t.expiresAt > Date.now() : !1;
|
|
38
|
+
}
|
|
39
|
+
getByKey(e) {
|
|
40
|
+
let t = this.getCachedData(e);
|
|
41
|
+
if (t && (!this.mustRevalidate || this.isFreshByKey(e))) return t.value;
|
|
42
|
+
}
|
|
43
|
+
computeExpiresAt(e) {
|
|
44
|
+
return e < 0 ? e : Date.now() + e;
|
|
45
|
+
}
|
|
46
|
+
setByKey([e, t], n, r) {
|
|
47
|
+
let i = r ?? this.ttl;
|
|
48
|
+
this.cache.set(e, {
|
|
49
|
+
key: t,
|
|
50
|
+
...this.cache.get(e),
|
|
51
|
+
promise: n == null ? Promise.reject() : Promise.resolve(n),
|
|
52
|
+
value: n,
|
|
53
|
+
expiresAt: this.computeExpiresAt(i),
|
|
54
|
+
settled: !0
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
removeByKey([e]) {
|
|
58
|
+
this.cache.set(e);
|
|
59
|
+
}
|
|
60
|
+
createPendingEntry([t, n], r, i) {
|
|
61
|
+
let a = {
|
|
62
|
+
...i,
|
|
63
|
+
key: n,
|
|
64
|
+
promise: r,
|
|
65
|
+
expiresAt: e,
|
|
66
|
+
settled: !1
|
|
67
|
+
};
|
|
68
|
+
return this.cache.set(t, a), a;
|
|
69
|
+
}
|
|
70
|
+
onSettled([e], t, n, r) {
|
|
71
|
+
this.cache.get(e) === t && this.cache.set(e, {
|
|
72
|
+
...t,
|
|
73
|
+
value: r,
|
|
74
|
+
expiresAt: n ? 0 : this.computeExpiresAt(this.ttl),
|
|
75
|
+
settled: !0
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
reloadByKey(e, t) {
|
|
79
|
+
let n = this.fn(...t), r = this.createPendingEntry(e, n, this.getCachedData(e));
|
|
80
|
+
return n.then((t) => this.onSettled(e, r, !1, t), () => this.onSettled(e, r, !0)), n;
|
|
81
|
+
}
|
|
82
|
+
callByKey(e, t) {
|
|
83
|
+
let n = this.getCachedData(e);
|
|
84
|
+
return n && (this.isFreshByKey(e) || !this.isSettledByKey(e)) ? n.promise : this.reloadByKey(e, t);
|
|
85
|
+
}
|
|
86
|
+
isSettled(...e) {
|
|
87
|
+
return this.isSettledByKey(this.resolveKey(...e));
|
|
88
|
+
}
|
|
89
|
+
isFresh(...e) {
|
|
90
|
+
return this.isFreshByKey(this.resolveKey(...e));
|
|
91
|
+
}
|
|
92
|
+
get(...e) {
|
|
93
|
+
return this.getByKey(this.resolveKey(...e));
|
|
94
|
+
}
|
|
95
|
+
remove(...e) {
|
|
96
|
+
this.removeByKey(this.resolveKey(...e));
|
|
97
|
+
}
|
|
98
|
+
reload(...e) {
|
|
99
|
+
let t = this.resolveKey(...e);
|
|
100
|
+
return this.reloadByKey(t, e);
|
|
101
|
+
}
|
|
102
|
+
call(...e) {
|
|
103
|
+
let t = this.resolveKey(...e);
|
|
104
|
+
return this.callByKey(t, e);
|
|
105
|
+
}
|
|
106
|
+
getContext(...t) {
|
|
107
|
+
let n = this.resolveKey(...t);
|
|
108
|
+
return {
|
|
109
|
+
call: () => this.callByKey(n, t),
|
|
110
|
+
get: () => this.getByKey(n),
|
|
111
|
+
set: (t, r = e) => {
|
|
112
|
+
this.setByKey(n, t, r);
|
|
113
|
+
},
|
|
114
|
+
delete: () => this.removeByKey(n),
|
|
115
|
+
reload: () => this.reloadByKey(n, t),
|
|
116
|
+
isSettled: () => this.isSettledByKey(n),
|
|
117
|
+
isFresh: () => this.isFreshByKey(n)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
prepare(...e) {
|
|
121
|
+
return this.getContext(...e);
|
|
122
|
+
}
|
|
123
|
+
clear() {
|
|
124
|
+
this.cache.clear();
|
|
125
|
+
}
|
|
126
|
+
prune() {
|
|
127
|
+
let e = Date.now();
|
|
128
|
+
for (let t of this.cache.keys()) {
|
|
129
|
+
let n = this.cache.get(t);
|
|
130
|
+
n && n.settled && n.expiresAt > 0 && n.expiresAt < e && this.cache.set(t);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
104
133
|
};
|
|
134
|
+
function i(e) {
|
|
135
|
+
return (t, n) => new r(t, n, e);
|
|
136
|
+
}
|
|
137
|
+
var a = i();
|
|
138
|
+
//#endregion
|
|
139
|
+
export { r as AsyncMemoContext, a as asyncMemo, i as createAsyncMemo, n as createAsyncMemoStorage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gera2ld/async-memo",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Cache asynchronous functions where they should be",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build:js": "vite build",
|
|
20
|
+
"build:types": "tsc",
|
|
21
|
+
"build": "pnpm run build:js && pnpm run build:types",
|
|
22
|
+
"test": "vitest --run",
|
|
23
|
+
"prepublishOnly": "pnpm run build"
|
|
24
|
+
},
|
|
18
25
|
"publishConfig": {
|
|
19
26
|
"access": "public",
|
|
20
27
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -31,14 +38,8 @@
|
|
|
31
38
|
"url": "https://github.com/gera2ld/async-memo.git"
|
|
32
39
|
},
|
|
33
40
|
"devDependencies": {
|
|
34
|
-
"typescript": "^
|
|
35
|
-
"vite": "^
|
|
36
|
-
"vitest": "^4.
|
|
37
|
-
},
|
|
38
|
-
"scripts": {
|
|
39
|
-
"build:js": "vite build",
|
|
40
|
-
"build:types": "tsc",
|
|
41
|
-
"build": "pnpm run /^build:/",
|
|
42
|
-
"test": "vitest --run"
|
|
41
|
+
"typescript": "^6.0.3",
|
|
42
|
+
"vite": "^8.1.0",
|
|
43
|
+
"vitest": "^4.1.9"
|
|
43
44
|
}
|
|
44
|
-
}
|
|
45
|
+
}
|