This repository has been archived on 2023-10-27. You can view files and clone it, but cannot push or open issues or pull requests.
node-fetch-cache/index.js

116 lines
3.4 KiB
JavaScript
Raw Normal View History

2020-04-17 20:31:22 +00:00
const fetch = require('node-fetch');
const fs = require('fs');
const { URLSearchParams } = require('url');
2020-04-17 20:31:22 +00:00
const crypto = require('crypto');
2020-11-28 04:28:00 +00:00
const Response = require('./classes/response.js');
2021-06-11 15:25:24 +00:00
const MemoryCache = require('./classes/caching/memory_cache.js');
2021-06-12 19:47:06 +00:00
const FileSystemCache = require('./classes/caching/file_system_cache.js');
2020-11-28 04:28:00 +00:00
const CACHE_VERSION = 2;
2020-04-17 20:31:22 +00:00
function md5(str) {
return crypto.createHash('md5').update(str).digest('hex');
}
// Since the bounday in FormData is random,
// we ignore it for purposes of calculating
// the cache key.
2020-11-28 16:43:50 +00:00
function getFormDataCacheKey(formData) {
const cacheKey = { ...formData };
2021-06-11 19:52:15 +00:00
const boundary = formData.getBoundary();
2020-11-28 16:43:50 +00:00
2021-06-11 19:52:15 +00:00
// eslint-disable-next-line no-underscore-dangle
delete cacheKey._boundary;
2020-11-28 16:43:50 +00:00
2021-06-11 19:52:15 +00:00
const boundaryReplaceRegex = new RegExp(boundary, 'g');
2020-11-28 16:43:50 +00:00
2021-06-11 19:52:15 +00:00
// eslint-disable-next-line no-underscore-dangle
cacheKey._streams = cacheKey._streams.map((s) => {
if (typeof s === 'string') {
return s.replace(boundaryReplaceRegex, '');
2020-11-28 16:43:50 +00:00
}
2021-06-11 19:52:15 +00:00
return s;
});
2020-11-28 16:43:50 +00:00
return cacheKey;
}
function getBodyCacheKeyJson(body) {
2020-11-28 16:25:23 +00:00
if (!body) {
return body;
} if (typeof body === 'string') {
return body;
} if (body instanceof URLSearchParams) {
return body.toString();
2020-11-28 16:25:23 +00:00
} if (body instanceof fs.ReadStream) {
return body.path;
2020-11-28 16:43:50 +00:00
} if (body.toString && body.toString() === '[object FormData]') {
return getFormDataCacheKey(body);
}
2021-06-11 19:52:15 +00:00
throw new Error('Unsupported body type. Supported body types are: string, number, undefined, null, url.URLSearchParams, fs.ReadStream, FormData');
}
function getCacheKey(requestArguments) {
const resource = requestArguments[0];
const init = requestArguments[1] || {};
2021-06-11 19:52:15 +00:00
if (typeof resource !== 'string') {
throw new Error('The first argument must be a string (fetch.Request is not supported).');
}
const resourceCacheKeyJson = { url: resource };
const initCacheKeyJson = { ...init };
resourceCacheKeyJson.body = getBodyCacheKeyJson(resourceCacheKeyJson.body);
initCacheKeyJson.body = getBodyCacheKeyJson(initCacheKeyJson.body);
return md5(JSON.stringify([resourceCacheKeyJson, initCacheKeyJson, CACHE_VERSION]));
}
async function createRawResponse(fetchRes) {
const buffer = await fetchRes.buffer();
return {
status: fetchRes.status,
statusText: fetchRes.statusText,
type: fetchRes.type,
url: fetchRes.url,
ok: fetchRes.ok,
2021-06-11 19:25:53 +00:00
headers: fetchRes.headers.raw(),
redirected: fetchRes.redirected,
bodyBuffer: buffer,
};
}
2021-06-11 15:25:24 +00:00
async function getResponse(cache, requestArguments) {
const cacheKey = getCacheKey(requestArguments);
2021-06-11 15:25:24 +00:00
const cachedValue = await cache.get(cacheKey);
2021-06-11 15:25:24 +00:00
const ejectSelfFromCache = () => cache.remove(cacheKey);
if (cachedValue) {
return new Response(cachedValue, ejectSelfFromCache, true);
2020-04-17 20:31:22 +00:00
}
2021-06-11 18:04:44 +00:00
2021-06-11 17:04:55 +00:00
const fetchResponse = await fetch(...requestArguments);
const rawResponse = await createRawResponse(fetchResponse);
await cache.set(cacheKey, rawResponse);
return new Response(rawResponse, ejectSelfFromCache, false);
2020-04-17 20:31:22 +00:00
}
2021-06-11 15:25:24 +00:00
function createFetchWithCache(cache) {
2021-06-11 17:04:55 +00:00
const fetchCache = (...args) => getResponse(cache, args);
fetchCache.withCache = createFetchWithCache;
2020-04-17 20:31:22 +00:00
2021-06-11 17:04:55 +00:00
return fetchCache;
2020-04-17 20:31:22 +00:00
}
2021-06-12 19:47:06 +00:00
const defaultFetch = createFetchWithCache(new MemoryCache());
module.exports = defaultFetch;
module.exports.fetchBuilder = defaultFetch;
module.exports.MemoryCache = MemoryCache;
module.exports.FileSystemCache = FileSystemCache;