UNPKG

@remcostoeten/fync

Version:

A unified TypeScript library for easy access to popular APIs (GitHub, Spotify, GitLab, etc.)

199 lines 7.02 kB
function parseLinkHeader(linkHeader) { const links = {}; const parts = linkHeader.split(","); for (const part of parts) { const section = part.split(";"); if (section.length !== 2) continue; const url = section[0].replace(/<(.*)>/, "$1").trim(); const name = section[1].replace(/rel="(.*)"/, "$1").trim(); links[name] = url; } return links; } function createAuthHeaders() { const headers = { Accept: "application/vnd.github.v3+json", "User-Agent": "github-api-service", }; if (process.env.GITHUB_TOKEN) { headers["Authorization"] = `Bearer ${process.env.GITHUB_TOKEN}`; } return headers; } async function httpRequest(url, query, signal, extraHeaders) { const searchParams = new URLSearchParams(); if (query) { for (const [key, value] of Object.entries(query)) { searchParams.append(key, String(value)); } } const fullUrl = searchParams.toString() ? `${url}?${searchParams.toString()}` : url; const headers = { ...createAuthHeaders(), ...extraHeaders }; const response = await fetch(fullUrl, { method: "GET", headers, signal, }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } const data = await response.json(); return { data, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), }; } function isArrayLike(value) { return (typeof value === "object" && value !== null && "length" in value && typeof value.length === "number"); } async function httpRequestWithPagination(url, query, signal, extraHeaders) { const searchParams = new URLSearchParams(); if (query) { for (const [key, value] of Object.entries(query)) { searchParams.append(key, String(value)); } } const fullUrl = searchParams.toString() ? `${url}?${searchParams.toString()}` : url; const headers = { ...createAuthHeaders(), ...extraHeaders }; const response = await fetch(fullUrl, { method: "GET", headers, signal, }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } const data = await response.json(); const linkHeader = response.headers.get("link"); let hasNext = false; let nextUrl; if (linkHeader) { const links = parseLinkHeader(linkHeader); hasNext = Boolean(links.next); nextUrl = links.next; } return { data: isArrayLike(data) ? data : [data], hasNext, nextUrl, totalCount: (function getTotalCount() { const totalCountHeader = response.headers.get("x-total-count"); return totalCountHeader ? parseInt(totalCountHeader, 10) : undefined; })(), }; } function createHttpClient(config = {}) { const { baseUrl = "https://api.github.com", defaultHeaders = {}, timeout = 10000, } = config; const _defaultHeaders = defaultHeaders; const _timeout = timeout; async function get(endpoint, query, signal) { const url = endpoint.startsWith("http") ? endpoint : baseUrl + endpoint; return httpRequest(url, query, signal, _defaultHeaders); } async function getPaginated(endpoint, query, signal) { const url = endpoint.startsWith("http") ? endpoint : baseUrl + endpoint; return httpRequestWithPagination(url, query, signal, _defaultHeaders); } async function post(endpoint, data) { const url = endpoint.startsWith("http") ? endpoint : baseUrl + endpoint; const headers = { ..._defaultHeaders, "Content-Type": "application/json" }; const response = await fetch(url, { method: "POST", headers, body: data ? JSON.stringify(data) : undefined, }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } const responseData = await response.json(); return { data: responseData, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), }; } async function put(endpoint, data) { const url = endpoint.startsWith("http") ? endpoint : baseUrl + endpoint; const headers = { ..._defaultHeaders, "Content-Type": "application/json" }; const response = await fetch(url, { method: "PUT", headers, body: data ? JSON.stringify(data) : undefined, }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } const responseData = await response.json(); return { data: responseData, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), }; } async function patch(endpoint, data) { const url = endpoint.startsWith("http") ? endpoint : baseUrl + endpoint; const headers = { ..._defaultHeaders, "Content-Type": "application/json" }; const response = await fetch(url, { method: "PATCH", headers, body: data ? JSON.stringify(data) : undefined, }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } const responseData = await response.json(); return { data: responseData, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), }; } async function deleteRequest(endpoint, data) { const url = endpoint.startsWith("http") ? endpoint : baseUrl + endpoint; const headers = { ..._defaultHeaders }; const response = await fetch(url, { method: "DELETE", headers, body: data ? JSON.stringify(data) : undefined, }); if (!response.ok) { throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } let responseData; try { responseData = await response.json(); } catch { responseData = null; } return { data: responseData, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), }; } return { get, getPaginated, post, put, patch, delete: deleteRequest, }; } export { httpRequest, httpRequestWithPagination, createHttpClient }; //# sourceMappingURL=http-client.js.map