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.
Files
scoresaber-reloaded/src/network/fetch.js
Liam 375820c945
All checks were successful
deploy / deploy (push) Successful in 47s
remove debug
2023-10-18 03:15:49 +01:00

178 lines
4.5 KiB
JavaScript

import { SsrDataFormatError } from "../others/errors";
import ssrConfig from "../ssr-config";
import { MINUTE } from "../utils/date";
import createNetworkCache from "./cache";
import {
SsrHttpClientError,
SsrHttpNotFoundError,
SsrHttpRateLimitError,
SsrHttpResponseError,
SsrHttpServerError,
SsrHttpUnauthenticatedError,
SsrHttpUnauthorizedError,
SsrHttpUnprocessableEntityError,
SsrNetworkError,
} from "./errors";
import { parseRateLimitHeaders } from "./utils";
const networkCache = createNetworkCache();
const checkResponse = (response) => {
if (response.ok) {
// response.status >= 200 && response.status < 300
return response;
}
switch (true) {
case response.status === 401:
throw new SsrHttpUnauthenticatedError(response);
case response.status === 403:
throw new SsrHttpUnauthorizedError(response);
case response.status === 404:
throw new SsrHttpNotFoundError(response);
case response.status === 422:
throw new SsrHttpUnprocessableEntityError(response);
case response.status === 429:
throw new SsrHttpRateLimitError(response);
case response.status >= 400 && response.status < 500:
throw new SsrHttpClientError(response);
case response.status >= 500:
throw new SsrHttpServerError(response);
default:
throw new SsrHttpResponseError(response);
}
};
const getOptionsWithCacheKey = (url, options, cacheType = null) => {
if (options && options.cacheKey) {
if (!options.cacheTtl) options.cacheTtl = MINUTE;
return options;
}
if (options && options.method && options.method.toLowerCase() !== "get") {
delete options.cacheKey;
delete options.cacheTtl;
return options;
}
const newOptions = options ? { ...options } : {};
if (!newOptions || !newOptions.hasOwnProperty("cacheTtl")) {
newOptions.cacheTtl = MINUTE;
}
return {
...newOptions,
cacheKey: `${cacheType ? cacheType + ":" : ""}${url}`,
};
};
const setCacheIfNeeded = (response, cacheKey, cacheTtl) => {
if (cacheKey && cacheTtl) networkCache.set(cacheKey, response, cacheTtl);
return { ...response, cached: false };
};
export async function fetchUrl(url, options = {}, cors = true) {
try {
const response = await fetch(url, {
...options,
headers: {
"x-requested-with": ssrConfig.name,
},
...(cors ? { mode: "cors" } : null),
});
return checkResponse(response);
} catch (err) {
if (err instanceof TypeError) throw new SsrNetworkError("Network error");
throw err;
}
}
export async function fetchJson(
url,
{ cacheTtl = null, maxAge = null, ...restOptions } = {}
) {
const options = getOptionsWithCacheKey(
url,
{ cacheTtl, maxAge, ...restOptions },
"json"
);
const {
cacheKey: fetchCacheKey,
cacheTtl: fetchCacheTtl,
maxAge: fetchMaxAge,
...fetchOptions
} = getOptionsWithCacheKey(url, options, "json");
if (fetchCacheKey && fetchCacheTtl) {
const cachedResponse = networkCache.get(fetchCacheKey, fetchMaxAge);
if (cachedResponse !== undefined)
return { ...cachedResponse, cached: true };
}
return fetchUrl(url, fetchOptions)
.then(async (response) => {
const body = await response.json();
return setCacheIfNeeded(
{
headers: response.headers,
rateLimit: parseRateLimitHeaders(response),
body,
},
fetchCacheKey,
fetchCacheTtl
);
})
.catch((err) => {
throw err instanceof SyntaxError
? new SsrDataFormatError("JSON parse error", err)
: err;
});
}
export async function fetchHtml(
url,
{ cacheTtl = null, maxAge = null, ...restOptions } = {}
) {
const options = getOptionsWithCacheKey(
url,
{ cacheTtl, maxAge, ...restOptions },
"json"
);
const {
cacheKey: fetchCacheKey,
cacheTtl: fetchCacheTtl,
maxAge: fetchMaxAge,
...fetchOptions
} = getOptionsWithCacheKey(url, options, "html");
if (fetchCacheKey && fetchCacheTtl) {
const cachedResponse = networkCache.get(fetchCacheKey, fetchMaxAge);
if (cachedResponse !== undefined)
return { ...cachedResponse, cached: true };
}
return fetchUrl(url, fetchOptions).then(async (response) => {
const body = await response.text();
return setCacheIfNeeded(
{
headers: response.headers,
rateLimit: parseRateLimitHeaders(response),
body: new DOMParser().parseFromString(body, "text/html"),
},
fetchCacheKey,
fetchCacheTtl
);
});
}