fork from scoresaber-reloaded

This commit is contained in:
Lee
2023-10-17 21:42:37 +01:00
commit cc884eec07
229 changed files with 31236 additions and 0 deletions

View File

@ -0,0 +1,112 @@
import {default as createQueue, PRIORITY as QUEUE_PRIORITY} from '../../utils/queue';
import {SsrError, SsrTimeoutError} from '../../others/errors'
import {SsrHttpRateLimitError, SsrHttpResponseError, SsrNetworkError, SsrNetworkTimeoutError} from '../errors'
import {fetchHtml, fetchJson} from '../fetch';
import makePendingPromisePool from '../../utils/pending-promises'
import {AbortError} from '../../utils/promise'
const DEFAULT_RETRIES = 2;
export const PRIORITY = {
FG_HIGH: QUEUE_PRIORITY.HIGHEST,
FG_LOW: QUEUE_PRIORITY.HIGH,
BG_HIGH: QUEUE_PRIORITY.NORMAL,
BG_NORMAL: QUEUE_PRIORITY.LOW,
BG_LOW: QUEUE_PRIORITY.LOWEST,
}
const resolvePromiseOrWaitForPending = makePendingPromisePool();
export default (options = {}) => {
const {retries, rateLimitTick, ...queueOptions} = {retries: DEFAULT_RETRIES, rateLimitTick: 500, ...options};
const queue = createQueue(queueOptions);
const {add, emitter, ...queueToReturn} = queue;
let lastRateLimitError = null;
let rateLimitTimerId = null;
let currentRateLimit = {waiting: 0, remaining: null, limit: null, resetAt: null};
const rateLimitTicker = () => {
const expiresInMs = lastRateLimitError && lastRateLimitError.resetAt ? lastRateLimitError.resetAt - new Date() + 1000 : 0;
if (expiresInMs <= 0) {
emitter.emit('waiting', {waiting: 0, remaining: null, limit: null, resetAt: null});
if (rateLimitTimerId) clearTimeout(rateLimitTimerId);
return;
}
const {remaining, limit, resetAt} = lastRateLimitError;
emitter.emit('waiting', {waiting: expiresInMs, remaining, limit, resetAt});
if (rateLimitTimerId) clearTimeout(rateLimitTimerId);
rateLimitTimerId = setTimeout(rateLimitTicker, rateLimitTick);
}
const retriedFetch = async (fetchFunc, url, options, priority = PRIORITY.FG_LOW) => {
for (let i = 0; i <= retries; i++) {
try {
return await add(async () => {
if (lastRateLimitError) {
await lastRateLimitError.waitBeforeRetry();
lastRateLimitError = null;
}
return fetchFunc(url, options)
.then(response => {
currentRateLimit = {...response.rateLimit, waiting: 0};
return response;
})
.catch(err => {
if (err instanceof SsrTimeoutError) throw new SsrNetworkTimeoutError(err.timeout);
throw err;
})
},
priority,
)
} catch (err) {
if (err instanceof SsrHttpResponseError) {
const {remaining, limit, resetAt} = err;
currentRateLimit = {waiting: 0, remaining, limit, resetAt};
}
if (err instanceof SsrNetworkError) {
const shouldRetry = err.shouldRetry();
if (!shouldRetry || i === retries) throw err;
if (err instanceof SsrHttpRateLimitError) {
if (err.remaining <= 0 && err.resetAt && (!lastRateLimitError || !lastRateLimitError.resetAt || lastRateLimitError.resetAt < err.resetAt)) {
lastRateLimitError = err;
rateLimitTicker();
}
} else {
lastRateLimitError = null;
}
} else if (!(err instanceof DOMException)) {
throw err;
} else {
throw AbortError();
}
}
}
throw new SsrError('Unknown error');
}
const queuedFetchJson = async (url, options, priority = PRIORITY.FG_LOW) => resolvePromiseOrWaitForPending(url, () => retriedFetch(fetchJson, url, options, priority));
const queuedFetchHtml = async (url, options, priority = PRIORITY.FG_LOW) => resolvePromiseOrWaitForPending(url, () => retriedFetch(fetchHtml, url, options, priority));
const getRateLimit = () => currentRateLimit;
return {
fetchJson: queuedFetchJson,
fetchHtml: queuedFetchHtml,
getRateLimit,
...queueToReturn,
}
}