import { AsyncLocalStorage } from "node:async_hooks"; import type { RequestHandler } from "express"; const timings = new AsyncLocalStorage<{ upstreamMs: number; calls: number }>(); /** Measures remote response-header wait, without logging tokens or query data. */ export const timedFetch: typeof fetch = async (input, init) => { const timing = timings.getStore(); const started = performance.now(); try { return await fetch(input, init); } finally { if (timing) { timing.calls += 1; timing.upstreamMs += performance.now() - started; } } }; // Bound identity-service stalls so a shared token check cannot stay pending forever. export const timedAuthFetch: typeof fetch = (input, init) => { const deadline = AbortSignal.timeout(8_000); const signal = init?.signal ?? (input instanceof Request ? input.signal : undefined); return timedFetch(input, { ...init, signal: signal ? AbortSignal.any([signal, deadline]) : deadline }); }; export const requestTiming: RequestHandler = (req, res, next) => { const timing = { upstreamMs: 0, calls: 0 }; const started = performance.now(); const json = res.json.bind(res); res.json = (body) => { res.setHeader("Server-Timing", `app;dur=${(performance.now() - started).toFixed(1)}, supabase;dur=${timing.upstreamMs.toFixed(1)};desc="${timing.calls} upstream calls"`); return json(body); }; res.once("finish", () => { const duration = performance.now() - started; if (duration >= 1000) console.info("slow_request", { method: req.method, route: req.route?.path ?? "(middleware)", status: res.statusCode, durationMs: Math.round(duration), upstreamMs: Math.round(timing.upstreamMs), upstreamCalls: timing.calls, }); }); timings.run(timing, next); };