infinibrowser
Version:
A TypeScript wrapper for the Infinibrowser API
159 lines (158 loc) • 3.44 kB
JavaScript
//#region src/api.ts
function buildUrl(options) {
const url = new URL(`${options.API_URL}${options.path}`);
if (options.params) {
for (const [key, value] of Object.entries(options.params)) if (value !== null && value !== void 0) url.searchParams.append(key, String(value));
}
return url;
}
function mergeRequests(...requests) {
let mergedResult = {};
const mergedHeaders = new Headers();
for (const request of requests) {
if (!request) continue;
mergedResult = {
...mergedResult,
...request
};
new Headers(request.headers).forEach((value, key) => {
mergedHeaders.set(key, value);
});
}
mergedResult.headers = mergedHeaders;
return mergedResult;
}
async function fetchWithTimeout(config, options, init) {
const url = buildUrl({
API_URL: config.API_URL,
...options
});
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), config.timeout);
try {
const requestInit = mergeRequests(config.request, init, {
signal: controller.signal,
headers: { "Accept-Encoding": "gzip, deflate, identity" }
});
const request = new Request(url, requestInit);
const response = await fetch(request);
const ok = response.ok;
if (!ok) {
const text = await response.clone().text();
return {
ok,
error_code: "NOT_OK",
response,
data: JSON.parse(text)
};
}
const text = await response.clone().text();
return {
ok,
data: JSON.parse(text),
response
};
} catch (error) {
if (error instanceof SyntaxError) return {
ok: false,
error_code: "SYNTAX_ERROR",
error
};
if (error instanceof DOMException && error.name === "AbortError") return {
ok: false,
error_code: "TIMEOUT",
error
};
return {
ok: false,
error_code: "UNKNOWN_ERROR",
error
};
} finally {
clearTimeout(timeout);
}
}
//#endregion
//#region src/client.ts
const API_URL = "https://infinibrowser.wiki/api";
const DEFAULT_OPTIONS = {
API_URL,
timeout: 1e3
};
var Infinibrowser = class {
$config;
constructor(config) {
this.$config = {
...DEFAULT_OPTIONS,
...config
};
}
async #get(options) {
return fetchWithTimeout(this.$config, options, {
method: "GET",
headers: { Accept: "application/json" }
});
}
async #post(options, payload) {
return fetchWithTimeout(this.$config, options, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload ?? {})
});
}
async getItem(id) {
return this.#get({
path: "/item",
params: { id }
});
}
async getRecipes(id, { offset = 0 } = {}) {
return this.#get({
path: "/recipes",
params: {
id,
offset
}
});
}
async getUses(id, { offset = 0 } = {}) {
return this.#get({
path: "/uses",
params: {
id,
offset
}
});
}
async getLineage(id) {
return this.#get({
path: "/recipe",
params: { id }
});
}
async getCustomLineage(id) {
return this.#get({
path: "/recipe/custom",
params: { id }
});
}
async optimizeLineage(id) {
return this.#post({
path: "/optimize-lineage",
params: { id }
});
}
async shareLineage(steps) {
const path = "/analytics/share";
const lastStep = steps.at(-1);
if (!lastStep) throw new Error("Lineage must not be empty");
const payload = {
...lastStep[2],
steps
};
return this.#post({ path }, payload);
}
};
const ib = new Infinibrowser();
//#endregion
export { API_URL, Infinibrowser, ib };