Ctrl K
ComunidadGuías…

lib/client-read.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { HttpResponseError } from "./backend-availability";
import { withClientRequestDeadline } from "./client-request-deadline";
import { confirmSessionAfterUnauthorized, isSessionInvalidResponse } from "./session-revocation";
import { getSupabase } from "./supabase";
import { takeServerSeed } from "./server-seed";

export const CLIENT_READ_TIMEOUT_MS = 30_000;

/**
 * Renew browser cookies before protected reads. Foreground requests can race
 * the auth refresh ticker; preparing here lets concurrent reads share renewal.
 * The SDK shares concurrent renewals; getSession is not an authorization check.
 */
export async function prepareClientSession(): Promise<string> {
  const { data, error } = await getSupabase().auth.getSession();
  if (error) throw error;
  if (!data.session) throw new HttpResponseError("Unauthorized", 401);
  return data.session.user.id;
}

/** Protected reads only. Archive transfers may opt out of the read deadline. */
export function fetchClientRead(
  path: string,
  init?: RequestInit,
  { timeoutMs = CLIENT_READ_TIMEOUT_MS }: { timeoutMs?: number | null } = {},
): Promise<Response> {
  if (!path.startsWith("/api/") || (init?.method ?? "GET").toUpperCase() !== "GET") {
    throw new Error("Client reads require a relative API path and GET method");
  }
  return withClientRequestDeadline(async (signal) => {
    // Unbounded archive transfers still bound both session preparations.
    const prepare = () => timeoutMs === null
      ? withClientRequestDeadline(prepareClientSession, CLIENT_READ_TIMEOUT_MS, signal)
      : prepareClientSession();
    const owner = await prepare();
    signal.throwIfAborted();
    // The document may already carry this read (MIN-880, lib/server-seed.ts).
    const seeded = takeServerSeed(path, owner);
    const response = (seeded && await seeded) || await fetch(path, { ...init, signal });
    // Include body consumption in the deadline: headers alone do not finish a read.
    const body = await response.arrayBuffer();
    signal.throwIfAborted();
    if (await prepare() !== owner) throw new Error("Local account changed");
    signal.throwIfAborted();
    if (isSessionInvalidResponse(response.status, body)) void confirmSessionAfterUnauthorized(path);
    return new Response(response.status === 204 || response.status === 304 ? null : body, {
      status: response.status, statusText: response.statusText, headers: response.headers,
    });
  }, timeoutMs, init?.signal);
}