erlc-api
Version:
An ER:LC API wrapper for JS/TS
159 lines (142 loc) • 4.83 kB
JavaScript
const ErlcError = require("../errors/ErlcError.js");
const {
getErrorInfo,
getSuggestedActions,
isRetryableError,
} = require("../errors/errorCodes.js");
/**
* Handles API response errors and creates appropriate ErlcError instances
* @param {Response} response - The fetch response object
* @param {Object} errorData - The parsed error data from response
* @returns {ErlcError} Formatted ERLC error
*/
function handleApiError(response, errorData) {
const status = response.status;
// Check if error data contains ERLC error code
if (errorData && typeof errorData.code === "number") {
const errorInfo = getErrorInfo(errorData.code);
const suggestions = getSuggestedActions(errorData.code);
const message = `${errorInfo.message}: ${errorInfo.description}`;
const error = new ErlcError(message, errorData.code, status);
error.category = errorInfo.category;
error.severity = errorInfo.severity;
error.suggestions = suggestions;
error.retryable = isRetryableError(errorData.code);
return error;
}
// Handle HTTP status codes when no ERLC error code is provided
let message, code;
switch (status) {
case 400:
message = "Bad Request: The request was invalid or malformed";
code = "HTTP_400";
break;
case 401:
message = "Unauthorized: Authentication failed or missing credentials";
code = "HTTP_401";
break;
case 403:
message = "Forbidden: Access denied or insufficient permissions";
code = "HTTP_403";
break;
case 404:
message = "Not Found: The requested resource was not found";
code = "HTTP_404";
break;
case 422:
message = "Unprocessable Entity: The server has no players";
code = "HTTP_422";
break;
case 429:
message = "Too Many Requests: Rate limit exceeded";
code = "HTTP_429";
break;
case 500:
message = "Internal Server Error: Problem communicating with Roblox";
code = "HTTP_500";
break;
case 502:
message = "Bad Gateway: Server received invalid response";
code = "HTTP_502";
break;
case 503:
message = "Service Unavailable: Server temporarily unavailable";
code = "HTTP_503";
break;
default:
message = `HTTP Error ${status}: ${
errorData?.error || response.statusText || "Unknown error"
}`;
code = `HTTP_${status}`;
}
const error = new ErlcError(message, code, status);
error.category = "HTTP_ERROR";
error.severity = status >= 500 ? "HIGH" : "MEDIUM";
error.retryable = [429, 500, 502, 503].includes(status);
return error;
}
/**
* Handles network and other non-API errors
* @param {Error} originalError - The original error object
* @returns {ErlcError} Formatted ERLC error
*/
function handleNetworkError(originalError) {
let message, code, category;
if (originalError.code === "ENOTFOUND") {
message =
"Network Error: Unable to connect to ERLC API (DNS resolution failed)";
code = "NETWORK_DNS_ERROR";
category = "NETWORK_ERROR";
} else if (originalError.code === "ECONNREFUSED") {
message = "Network Error: Connection refused by ERLC API server";
code = "NETWORK_CONNECTION_REFUSED";
category = "NETWORK_ERROR";
} else if (
originalError.code === "ETIMEDOUT" ||
originalError.name === "AbortError"
) {
message = "Request Timeout: API took too long to respond";
code = "REQUEST_TIMEOUT";
category = "TIMEOUT_ERROR";
} else if (originalError.message?.includes("JSON")) {
message = "Parse Error: Invalid JSON response from API";
code = "JSON_PARSE_ERROR";
category = "PARSE_ERROR";
} else {
message = `Unexpected Error: ${originalError.message}`;
code = "UNEXPECTED_ERROR";
category = "UNKNOWN_ERROR";
}
const error = new ErlcError(message, code, null, originalError);
error.category = category;
error.severity = "MEDIUM";
error.retryable = [
"NETWORK_DNS_ERROR",
"REQUEST_TIMEOUT",
"NETWORK_CONNECTION_REFUSED",
].includes(code);
return error;
}
/**
* Main error handler that processes any error and returns a standardized ErlcError
* @param {Error|Response} error - The error to handle
* @param {Object} [errorData] - Additional error data if available
* @returns {ErlcError} Standardized ERLC error
*/
async function processError(error, errorData = null) {
// If it's already an ErlcError, return as-is
if (error instanceof ErlcError) {
return error;
}
// If it's a Response object (from fetch), handle as API error
if (error && typeof error.status === "number") {
return handleApiError(error, errorData);
}
// Handle as network/system error
return handleNetworkError(error);
}
module.exports = {
handleApiError,
handleNetworkError,
processError,
};