erlc-api
Version:
An ER:LC API wrapper for JS/TS
186 lines (151 loc) • 4.09 kB
JavaScript
const { BASEURL, LEGACY_BASEURL } = require("../../constants.js");
const { processError } = require("../../utils/errorHandler.js");
const cache = require("../../utils/cache.js");
function assertServerToken(serverToken) {
if (!serverToken || typeof serverToken !== "string") {
throw new Error("Server token is required and must be a string");
}
}
function resolveServerToken(serverToken, config) {
const resolvedToken = serverToken || config?.serverToken;
assertServerToken(resolvedToken);
return resolvedToken;
}
function buildHeaders(serverToken, config, extraHeaders = {}) {
const headers = {
"Server-Key": serverToken,
...extraHeaders,
};
if (config?.globalToken) {
headers["Authorization"] = config.globalToken;
}
return headers;
}
async function getFetch() {
const { config } = require("../../erlc.js");
if (config?.fetch) {
return {
fetch: config.fetch,
config,
};
}
if (typeof globalThis.fetch === "function") {
return {
fetch: globalThis.fetch,
config,
};
}
const fetch = await import("node-fetch");
return {
fetch: fetch.default,
config,
};
}
async function readError(res) {
return res.json().catch(() => ({ error: "Unknown API error" }));
}
function buildServerUrl(includes = []) {
const url = new URL(`${BASEURL}/server`);
for (const include of includes) {
url.searchParams.set(include, "true");
}
return url.toString();
}
async function requestJson(url, options) {
const res = await options.fetch(url, options.init);
if (!res.ok) {
const errorData = await readError(res);
throw await processError(res, errorData);
}
return res.json();
}
async function requestServer(serverToken, options = {}) {
const {
endpoint = "server",
includes = [],
defaultValue = {},
transform = (data) => data,
useCache = true,
} = options;
const { fetch, config } = await getFetch();
const resolvedToken = resolveServerToken(serverToken, config);
const shouldCache = useCache && !!config?.cache?.enabled;
const cacheExtras = includes.length ? includes.join(",") : "";
const key = cache.makeKey(endpoint, resolvedToken, cacheExtras);
if (shouldCache) {
const cached = cache.get(key);
if (cached) {
return cached;
}
}
const data = await requestJson(buildServerUrl(includes), {
fetch,
init: {
headers: buildHeaders(resolvedToken, config),
timeout: 10000,
},
});
const value = (await transform(data)) ?? defaultValue;
if (shouldCache) {
const ttlMs = cache.getTTL(endpoint, config);
cache.set(key, value, ttlMs);
}
return value;
}
async function requestApi(serverToken, path, options = {}) {
const {
baseUrl = BASEURL,
method = "GET",
endpoint = path,
body,
defaultValue = {},
transform = (data) => data,
timeout = 10000,
useCache = true,
} = options;
const { fetch, config } = await getFetch();
const resolvedToken = resolveServerToken(serverToken, config);
const shouldCache = method === "GET" && useCache && !!config?.cache?.enabled;
const key = cache.makeKey(endpoint, resolvedToken);
if (shouldCache) {
const cached = cache.get(key);
if (cached) {
return cached;
}
}
const headers = buildHeaders(
resolvedToken,
config,
body ? { "Content-Type": "application/json" } : {},
);
const data = await requestJson(`${baseUrl}${path}`, {
fetch,
init: {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
timeout,
},
});
const value = (await transform(data)) ?? defaultValue;
if (shouldCache) {
const ttlMs = cache.getTTL(endpoint, config);
cache.set(key, value, ttlMs);
}
return value;
}
async function requestLegacyServer(serverToken, path, options = {}) {
return requestApi(serverToken, path, {
...options,
baseUrl: LEGACY_BASEURL,
});
}
module.exports = {
assertServerToken,
resolveServerToken,
buildHeaders,
getFetch,
requestServer,
requestApi,
requestLegacyServer,
};