generated from davidbonnet/foundation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache.ts
87 lines (85 loc) · 2.69 KB
/
cache.ts
1
2
3
4
5
6
7
8
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
import type { Handler, Query, Store } from "../types";
type CachedItem<I extends Query<any>> = {
query: I;
value: any;
};
export function cache<
I extends Query<any> & { type: any },
O,
In extends Query<any>,
On,
>({
itemId = ({ context = {}, method = "read", type }: I) => {
if (method === "read" && context.id) {
return `${String(type)}/${context.id}`;
}
return undefined;
},
store,
invalidatesItem,
extendCachedQuery,
mergeQuery,
mergeItem,
}: {
/**
* Unique identifier for the item to cache.
* Returns `undefined` if the item should not be cached.
*/
itemId?: (query: I) => string | undefined;
/**
* Cache store.
*/
store: Store<CachedItem<I>>;
/**
* Returns `true` if the cached item must be invalidated.
*/
invalidatesItem: (query: I, cachedQuery: I, cachedValue: any) => boolean;
/**
* Returns a query that completes the cached value.
* Returns `undefined` if the query should not be completed.
*/
extendCachedQuery: (query: I, cachedQuery: I) => I | undefined;
/**
* Merges the extended query with the cached query.
* Only called when `extendCachedQuery` returns a query.
*/
mergeQuery: (extendedQuery: I, cachedQuery: I) => I;
/**
* Merges the value from the extended query with the cached value.
* Only called when `extendCachedQuery` returns a query.
*/
mergeItem: (value: any, cachedValue: any, query: I, cachedQuery: I) => any;
}): Handler<I, O, In, On> {
/*
Caches the result of a query if `serialize` returns a non-empty string key. The `engine` should follow the `Map` API. Elements are kept in the cache until the `duration` in milliseconds expires.
Note that a `duration` set to `Infinity` indefinitely keeps items in the cache.
*/
return async (query, next) => {
const id = itemId(query);
if (!id) {
return next(query as unknown as In);
}
if (await store.has(id)) {
const { query: cachedQuery, value: cachedValue } = await store.get(id);
if (invalidatesItem(query, cachedQuery, cachedValue)) {
const value = await next(query as unknown as In);
store.set(id, { query, value });
return value;
}
const extendedQuery = extendCachedQuery(query, cachedQuery);
if (extendedQuery === undefined) {
return cachedValue;
}
const value = await next(extendedQuery as unknown as In);
const extendedValue = mergeItem(value, cachedValue, query, cachedQuery);
store.set(id, {
query: mergeQuery(extendedQuery, cachedQuery),
value: extendedValue,
});
return extendedValue;
}
const value = await next(query as unknown as In);
store.set(id, { query, value });
return value;
};
}