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 });
