2022-06-17 15:00:55 +08:00

127 lines
3.2 KiB
TypeScript

import('../lib/fetch.js')
const TIMEOUT_ERROR = new Error("timeout");
const hexRe = /^[0-9A-Fa-f]+$/gu;
/**
* Execute fetch and verify that the response was successful.
*
* @param request - Request information.
* @param options - Fetch options.
* @returns The fetch response.
*/
export async function successfulFetch(request: string, options?: RequestInit) {
const response = await fetch(request, options);
if (!response.ok) {
throw new Error(
`Fetch failed with status '${response.status}' for request '${request}'`
);
}
return response;
}
/**
* Execute fetch and return object response.
*
* @param request - The request information.
* @param options - The fetch options.
* @returns The fetch response JSON data.
*/
export async function handleFetch(request: string, options?: RequestInit) {
const response = await successfulFetch(request, options);
const object = await response.json();
return object;
}
/**
* Execute fetch and return object response, log if known error thrown, otherwise rethrow error.
*
* @param request - the request options object
* @param request.url - The request url to query.
* @param request.options - The fetch options.
* @param request.timeout - Timeout to fail request
* @param request.errorCodesToCatch - array of error codes for errors we want to catch in a particular context
* @returns The fetch response JSON data or undefined (if error occurs).
*/
export async function fetchWithErrorHandling({
url,
options,
timeout,
errorCodesToCatch,
}: {
url: string;
options?: RequestInit;
timeout?: number;
errorCodesToCatch?: number[];
}) {
let result;
try {
if (timeout) {
result = Promise.race([
await handleFetch(url, options),
new Promise<Response>((_, reject) =>
setTimeout(() => {
reject(TIMEOUT_ERROR);
}, timeout)
),
]);
} else {
result = await handleFetch(url, options);
}
} catch (e) {
logOrRethrowError(e, errorCodesToCatch);
}
return result;
}
/**
* Fetch that fails after timeout.
*
* @param url - Url to fetch.
* @param options - Options to send with the request.
* @param timeout - Timeout to fail request.
* @returns Promise resolving the request.
*/
export async function timeoutFetch(
url: string,
options?: RequestInit,
timeout = 500
): Promise<Response> {
return Promise.race([
successfulFetch(url, options),
new Promise<Response>((_, reject) =>
setTimeout(() => {
reject(TIMEOUT_ERROR);
}, timeout)
),
]);
}
/**
* Utility method to log if error is a common fetch error and otherwise rethrow it.
*
* @param error - Caught error that we should either rethrow or log to console
* @param codesToCatch - array of error codes for errors we want to catch and log in a particular context
*/
function logOrRethrowError(error: any, codesToCatch: number[] = []) {
if (!error) {
return;
}
const includesErrorCodeToCatch = codesToCatch.some((code) =>
error.message.includes(`Fetch failed with status '${code}'`)
);
if (
error instanceof Error &&
(includesErrorCodeToCatch ||
error.message.includes("Failed to fetch") ||
error === TIMEOUT_ERROR)
) {
console.error(error);
} else {
throw error;
}
}