infinibrowser
Version:
A TypeScript wrapper for the Infinibrowser API
179 lines (178 loc) • 3.82 kB
JavaScript
//#region src/client.ts
const 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;
}
function handleError(error) {
if (error instanceof SyntaxError) return {
ok: false,
error_code: "SYNTAX_ERROR",
error
};
if (error instanceof DOMException) {
if (error.name === "AbortError") return {
ok: false,
error_code: "TIMEOUT",
error
};
return {
ok: false,
error_code: "UNKNOWN_ERROR",
error
};
}
return {
ok: false,
error_code: "UNKNOWN_ERROR",
error
};
}
var Infinibrowser = class Infinibrowser {
$config;
constructor(config) {
this.$config = config;
}
$refined(config) {
return new Infinibrowser({
...this.$config,
...config
});
}
async #fetchWithTimeout(url, init = {}) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.$config.timeout);
try {
const requestInit = mergeRequests(this.$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$1 = await response.clone().text();
return {
ok,
error_code: "NOT_OK",
response,
data: JSON.parse(text$1)
};
}
const text = await response.clone().text();
return {
ok,
data: JSON.parse(text),
response
};
} catch (error) {
return handleError(error);
} finally {
clearTimeout(timeout);
}
}
async #get(options) {
const url = buildUrl({
API_URL: this.$config.API_URL,
...options
});
return this.#fetchWithTimeout(url, {
method: "GET",
headers: { Accept: "application/json" }
});
}
async #post(options) {
const url = buildUrl({
API_URL: this.$config.API_URL,
...options
});
return this.#fetchWithTimeout(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(options.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 resultElement = lastStep[2];
const payload = {
id: resultElement.id,
emoji: resultElement.emoji,
steps
};
return this.#post({
path,
payload
});
}
};
const API_URL = "https://infinibrowser.wiki/api";
const DEFAULT_OPTIONS = {
API_URL,
timeout: 1e3
};
const ib = new Infinibrowser(DEFAULT_OPTIONS);
//#endregion
export { API_URL, DEFAULT_OPTIONS, Infinibrowser, ib };