@graphql-mesh/plugin-http-cache
Version:
129 lines (124 loc) • 5.82 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
const utils = require('@graphql-mesh/utils');
const CachePolicy = _interopDefault(require('http-cache-semantics'));
const fetch = require('@whatwg-node/fetch');
function useHTTPCache({ cache }) {
return {
async onFetch({ url, options, fetchFn, setFetchFn }) {
if (options.cache === 'no-cache') {
return () => { };
}
const reqHeaders = utils.getHeadersObj(options.headers);
const policyRequest = {
url,
method: options.method,
headers: reqHeaders,
};
const cacheEntry = (await cache.get(url));
if (cacheEntry) {
const policy = CachePolicy.fromObject(cacheEntry.policy);
setFetchFn(async (url, options, context, info) => {
if (options.cache !== 'reload' && (policy === null || policy === void 0 ? void 0 : policy.satisfiesWithoutRevalidation(policyRequest))) {
const resHeaders = {};
const policyHeaders = policy.responseHeaders();
for (const key in policyHeaders) {
const value = policyHeaders[key];
if (Array.isArray(value)) {
resHeaders[key] = value.join(', ');
}
else {
resHeaders[key] = value;
}
}
const response = new fetch.Response(cacheEntry.body, {
status: cacheEntry.response.status,
headers: resHeaders,
});
return response;
}
const policyHeaders = policy.revalidationHeaders(policyRequest);
const reqHeaders = {};
for (const key in policyHeaders) {
const value = policyHeaders[key];
if (Array.isArray(value)) {
reqHeaders[key] = value.join(', ');
}
else {
reqHeaders[key] = value;
}
}
const revalidationRequest = {
url,
method: options.method,
headers: reqHeaders,
};
const revalidationResponse = await fetchFn(url, {
...options,
method: revalidationRequest.method,
headers: {
...options.headers,
...revalidationRequest.headers,
},
}, context, info);
const { policy: revalidatedPolicy, modified } = policy.revalidatedPolicy(revalidationRequest, {
status: revalidationResponse.status,
headers: utils.getHeadersObj(revalidationResponse.headers),
});
const newBody = await revalidationResponse.text();
const resHeaders = {};
const resPolicyHeaders = revalidatedPolicy.responseHeaders();
for (const key in resPolicyHeaders) {
const value = resPolicyHeaders[key];
if (Array.isArray(value)) {
resHeaders[key] = value.join(', ');
}
else {
resHeaders[key] = value;
}
}
return new fetch.Response(modified ? newBody : cacheEntry.body, {
status: revalidationResponse.status,
headers: resHeaders,
});
});
}
if (options.cache === 'no-store') {
return () => { };
}
return async ({ response, setResponse }) => {
const resHeaders = utils.getHeadersObj(response.headers);
const policyResponse = {
status: response.status,
headers: resHeaders,
};
const policy = new CachePolicy(policyRequest, policyResponse);
if (policy.storable()) {
const resText = await response.text();
const cacheEntry = {
policy: policy.toObject(),
response: policyResponse,
body: resText,
};
let ttl = Math.round(policy.timeToLive() / 1000);
if (ttl > 0) {
// If a response can be revalidated, we don't want to remove it from the cache right after it expires.
// We may be able to use better heuristics here, but for now we'll take the max-age times 2.
if (canBeRevalidated(response)) {
ttl *= 2;
}
await cache.set(url, cacheEntry, { ttl });
setResponse(new fetch.Response(resText, {
status: response.status,
headers: resHeaders,
}));
}
}
};
},
};
}
function canBeRevalidated(response) {
return response.headers.has('ETag');
}
module.exports = useHTTPCache;