cc-backend/web/frontend/src/cache-exchange.js
2022-06-22 11:20:57 +02:00

73 lines
2.5 KiB
JavaScript

import { filter, map, merge, pipe, share, tap } from 'wonka';
/*
* Alternative to the default cacheExchange from urql (A GraphQL client).
* Mutations do not invalidate cached results, so in that regard, this
* implementation is inferior to the default one. Most people should probably
* use the standard cacheExchange and @urql/exchange-request-policy. This cache
* also ignores the 'network-and-cache' request policy.
*
* Options:
* ttl: How long queries are allowed to be cached (in milliseconds)
* maxSize: Max number of results cached. The oldest queries are removed first.
*/
export const expiringCacheExchange = ({ ttl, maxSize }) => ({ forward }) => {
const cache = new Map();
const isCached = (operation) => {
if (operation.kind !== 'query' || operation.context.requestPolicy === 'network-only')
return false;
if (!cache.has(operation.key))
return false;
let cacheEntry = cache.get(operation.key);
return Date.now() < cacheEntry.expiresAt;
};
return operations => {
let shared = share(operations);
return merge([
pipe(
shared,
filter(operation => isCached(operation)),
map(operation => cache.get(operation.key).response)
),
pipe(
shared,
filter(operation => !isCached(operation)),
forward,
tap(response => {
if (!response.operation || response.operation.kind !== 'query')
return;
if (!response.data)
return;
let now = Date.now();
for (let cacheEntry of cache.values()) {
if (cacheEntry.expiresAt < now) {
cache.delete(cacheEntry.response.operation.key);
}
}
if (cache.size > maxSize) {
let n = cache.size - maxSize + 1;
for (let key of cache.keys()) {
if (n-- == 0)
break;
cache.delete(key);
}
}
cache.set(response.operation.key, {
expiresAt: now + ttl,
response: response
});
})
)
]);
};
};