export async function fetchJson(url, options = {}) { const { timeoutMs = 15_000, ...fetchOptions } = options; let timeout = null; let controller = null; if (!fetchOptions.signal && timeoutMs > 0) { controller = new AbortController(); fetchOptions.signal = controller.signal; timeout = globalThis.setTimeout(() => controller.abort(), timeoutMs); } try { const response = await fetch(url, fetchOptions); const text = await response.text(); const data = text ? JSON.parse(text) : null; if (!response.ok) { throw new Error(data?.error || `HTTP ${response.status}`); } return data; } catch (error) { if (controller?.signal.aborted) { throw new Error(`Request timed out after ${timeoutMs}ms`); } throw error; } finally { if (timeout) globalThis.clearTimeout(timeout); } }