55:async function api(path, opts = {}) { 56- const headers = Object.assign({}, opts.headers || {}); 57- if (opts.json !== undefined) { 58- headers["Content-Type"] = "application/json"; 59- opts.body = JSON.stringify(opts.json); 60- delete opts.json; 61- } 62- if ((opts.method || "GET").toUpperCase() !== "GET") headers["X-CSRF-Token"] = CSRF.get(); 63- const res = await fetch(path, Object.assign({}, opts, { headers, credentials: "same-origin" })); 64- let data = null; 65- try { data = await res.json(); } catch { /* no body */ } 66- if (res.status === 401 && !location.pathname.startsWith("/login")) { 67- location.href = "/login?next=" + encodeURIComponent(location.pathname); 68- throw new Error("Not authenticated"); 69- } 70- if (!res.ok) { 71- const detail = data && (data.detail?.message || data.detail || data.error); 72- const err = new Error(typeof detail === "string" ? detail : (res.status + " " + res.statusText)); 73- err.status = res.status; 74- err.data = data; 75- throw err; 76- } 77- return data; 78-} 79-const GET = p => api(p); 80-const POST = (p, json) => api(p, { method: "POST", json });