import * as undici from "undici"; export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000; export const HTTP_IDLE_TIMEOUT_CHOICES = [ { label: "30 sec", timeoutMs: 30_000 }, { label: "1 min", timeoutMs: 60_000 }, { label: "2 min", timeoutMs: 120_000 }, { label: "5 min", timeoutMs: 300_000 }, { label: "disabled", timeoutMs: 0 }, ] as const; const originalGlobalFetch = globalThis.fetch; let installedGlobalFetch: typeof globalThis.fetch | undefined; export function parseHttpIdleTimeoutMs(value: unknown): number | undefined { if (typeof value === "string") { const trimmed = value.trim(); if (trimmed.toLowerCase() === "disabled") { return 0; } if (trimmed.length === 0) { return undefined; } return parseHttpIdleTimeoutMs(Number(trimmed)); } if (typeof value !== "number" || !Number.isFinite(value) || value < 0) { return undefined; } return Math.floor(value); } export function formatHttpIdleTimeoutMs(timeoutMs: number): string { const choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs); if (choice) { return choice.label; } return `${timeoutMs / 1000} sec`; } export function applyHttpProxySettings(httpProxy: string | undefined): void { const proxy = httpProxy?.trim(); if (!proxy) return; process.env.HTTP_PROXY ??= proxy; process.env.HTTPS_PROXY ??= proxy; } export function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void { const normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs); if (normalizedTimeoutMs === undefined) { throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`); } undici.setGlobalDispatcher( new undici.EnvHttpProxyAgent({ allowH2: false, bodyTimeout: normalizedTimeoutMs, headersTimeout: normalizedTimeoutMs, }), ); // Keep fetch and the dispatcher on the same undici implementation. Node 26.0's // bundled fetch can otherwise consume compressed responses through npm undici's // dispatcher without decompressing them, causing response.json() failures. // If a caller replaced fetch after module load, preserve that deliberate override. const shouldInstallGlobals = installedGlobalFetch === undefined ? globalThis.fetch === originalGlobalFetch : globalThis.fetch === installedGlobalFetch; if (shouldInstallGlobals) { undici.install?.(); installedGlobalFetch = globalThis.fetch; } }