SPB Git
1.9 KB · 23 lines typescript
1"use client";2export async function api<T = any>(url: string, init: RequestInit & { json?: unknown } = {}): Promise<T> {3  const { json, ...rest } = init;4  const res = await fetch(url, { ...rest, headers: { ...(json !== undefined ? { "content-type": "application/json" } : {}), ...(rest.headers ?? {}) }, body: json !== undefined ? JSON.stringify(json) : rest.body, credentials: "same-origin" });5  if (res.status === 401) { if (typeof window !== "undefined") window.location.href = `/login?next=${encodeURIComponent(location.pathname)}`; throw new Error("Unauthenticated"); }6  const ct = res.headers.get("content-type") ?? "";7  const body = ct.includes("application/json") ? await res.json() : await res.text();8  if (!res.ok) throw new Error((body && body.error) || `HTTP ${res.status}`);9  return body as T;10}11/** Consume a server-sent events stream (data: JSON lines, [DONE] terminator). */12export async function sse(url: string, json: unknown, onEvent: (ev: any) => void, signal?: AbortSignal) {13  const res = await fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(json), signal, credentials: "same-origin" });14  if (res.status === 401) { window.location.href = `/login?next=${encodeURIComponent(location.pathname)}`; return; }15  if (!res.ok || !res.body) { let msg = `HTTP ${res.status}`; try { msg = (await res.json()).error ?? msg; } catch {} onEvent({ type: "error", message: msg }); return; }16  const reader = res.body.getReader(); const dec = new TextDecoder(); let buf = "";17  for (;;) {18    const { value, done } = await reader.read(); if (done) break;19    buf += dec.decode(value, { stream: true });20    let i; while ((i = buf.indexOf("\n\n")) >= 0) { const line = buf.slice(0, i).trim(); buf = buf.slice(i + 2); if (!line.startsWith("data: ")) continue; const data = line.slice(6); if (data === "[DONE]") return; try { onEvent(JSON.parse(data)); } catch {} }21  }22}23
Files · 2c37cb5a3e2af18e5ba361996d453cab97da82c2
Menu

Theme