import {Temporal} from "@js-temporal/polyfill"; import {clear_session_data} from "$shared/lib/session"; import {resolve_references} from "$shared/lib/helpers"; import {replace} from "svelte-spa-router"; import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse"; import type {IInternalFetchRequest} from "$shared/lib/models/IInternalFetchRequest"; export async function http_post(url: string, body?: object|string, timeout = -1, skip_401_check = false, abort_signal: AbortSignal = undefined): Promise { const init = { method: "post", } as RequestInit; if (abort_signal) { init.signal = abort_signal; } if (body) { init.headers = { "Content-Type": "application/json;charset=UTF-8", }; init.body = JSON.stringify(body); } const response = await internal_fetch({url, init, timeout}); const result = {} as IInternalFetchResponse; if (!skip_401_check && await is_401(response)) return result; result.ok = response.ok; result.status = response.status; result.http_response = response; if (response.status !== 204) { try { const ct = response.headers.get("Content-Type")?.toString() ?? ""; if (ct.startsWith("application/json")) { const data = await response.json(); result.data = resolve_references(data); } else if (ct.startsWith("text/plain")) { const text = await response.text(); result.data = text as string; } } catch { // Ignored } } return result; } export async function http_get(url: string, timeout = -1, skip_401_check = false, abort_signal: AbortSignal = undefined): Promise { const init = { method: "get", } as RequestInit; if (abort_signal) { init.signal = abort_signal; } const response = await internal_fetch({url, init, timeout}); const result = {} as IInternalFetchResponse; if (!skip_401_check && await is_401(response)) return result; result.ok = response.ok; result.status = response.status; result.http_response = response; if (response.status !== 204) { try { const ct = response.headers.get("Content-Type")?.toString() ?? ""; if (ct.startsWith("application/json")) { const data = await response.json(); result.data = resolve_references(data); } else if (ct.startsWith("text/plain")) { const text = await response.text(); result.data = text as string; } } catch { // Ignored } } return result; } export async function http_delete(url: string, body?: object|string, timeout = -1, skip_401_check = false, abort_signal: AbortSignal = undefined): Promise { const init = { method: "delete", } as RequestInit; if (abort_signal) { init.signal = abort_signal; } if (body) { init.headers = { "Content-Type": "application/json;charset=UTF-8", }; init.body = JSON.stringify(body); } const response = await internal_fetch({url, init, timeout}); const result = {} as IInternalFetchResponse; if (!skip_401_check && await is_401(response)) return result; result.ok = response.ok; result.status = response.status; result.http_response = response; if (response.status !== 204) { try { const ct = response.headers.get("Content-Type")?.toString() ?? ""; if (ct.startsWith("application/json")) { const data = await response.json(); result.data = resolve_references(data); } else if (ct.startsWith("text/plain")) { const text = await response.text(); result.data = text as string; } } catch (error) { // ignored } } return result; } async function internal_fetch(request: IInternalFetchRequest): Promise { if (!request.init) request.init = {}; request.init.credentials = "include"; request.init.headers = { "X-TimeZone": Temporal.Now.timeZone().id, ...request.init.headers }; const fetch_request = new Request(request.url, request.init); let response: any; try { if (request.timeout > 500) { response = await Promise.race([ fetch(fetch_request), new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), request.timeout)) ]); } else { response = await fetch(fetch_request); } } catch (error) { if (error.message === "Timeout") { console.error("Request timed out"); } else if (error.message === "Network request failed") { console.error("No internet connection"); } else { throw error; // rethrow other unexpected errors } } return response; } async function is_401(response: Response): Promise { if (response.status === 401) { clear_session_data(); await replace("/login"); return true; } return false; }