export type QueryParamValue = string | number | string[] | undefined; function joinUrl(baseUrl: string, path: string): string { const base = baseUrl.replace(/\/$/, ''); const endpoint = path.startsWith('/') ? path : `/${path}`; return `${base}${endpoint}`; } function appendQueryParams(url: string, params?: Record): string { const searchParams = new URLSearchParams(); Object.entries(params ?? {}).forEach(([key, value]) => { if (Array.isArray(value)) { value.forEach((item) => searchParams.append(key, item)); return; } if (value !== undefined && value !== '') { searchParams.set(key, String(value)); } }); const query = searchParams.toString(); return query ? `${url}?${query}` : url; } /** Собирает URL API из base, пути и query-параметров. */ export function buildApiUrl( baseUrl: string, path: string, params?: Record, ): string { return appendQueryParams(joinUrl(baseUrl, path), params); } /** Выполняет GET-запрос и добавляет query-параметры в едином формате для всех API. */ export async function getJson( baseUrl: string, path: string, params?: Record, init?: RequestInit, ): Promise { const url = buildApiUrl(baseUrl, path, params); const response = await fetch(url, init); if (!response.ok) { const message = await response.text(); throw new Error(message || `${response.status} ${response.statusText}`); } return response.json() as Promise; }