From 6931f0fb50016dfcb3b3a3c5a402984ebc65884c Mon Sep 17 00:00:00 2001 From: sebslight <19554889+sebslight@users.noreply.github.com> Date: Mon, 16 Feb 2026 08:24:31 -0500 Subject: [PATCH] refactor(telegram): avoid double-wrapping proxy fetch --- src/telegram/fetch.test.ts | 52 +++++++++++++++++++++++++++++++++++--- src/telegram/proxy.ts | 5 ++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/telegram/fetch.test.ts b/src/telegram/fetch.test.ts index c20d3ad5e9a..343908dad5e 100644 --- a/src/telegram/fetch.test.ts +++ b/src/telegram/fetch.test.ts @@ -1,4 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +import { resolveFetch } from "../infra/fetch.js"; import { resetTelegramFetchStateForTests, resolveTelegramFetch } from "./fetch.js"; const setDefaultAutoSelectFamily = vi.hoisted(() => vi.fn()); @@ -29,14 +30,59 @@ describe("resolveTelegramFetch", () => { it("returns wrapped global fetch when available", async () => { const fetchMock = vi.fn(async () => ({})); globalThis.fetch = fetchMock as unknown as typeof fetch; + const resolved = resolveTelegramFetch(); + expect(resolved).toBeTypeOf("function"); + expect(resolved).not.toBe(fetchMock); }); - it("prefers proxy fetch when provided", async () => { - const fetchMock = vi.fn(async () => ({})); - const resolved = resolveTelegramFetch(fetchMock as unknown as typeof fetch); + it("wraps proxy fetches and normalizes foreign signals once", async () => { + let seenSignal: AbortSignal | undefined; + const proxyFetch = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => { + seenSignal = init?.signal as AbortSignal | undefined; + return {} as Response; + }); + + const resolved = resolveTelegramFetch(proxyFetch as unknown as typeof fetch); expect(resolved).toBeTypeOf("function"); + + let abortHandler: (() => void) | null = null; + const addEventListener = vi.fn((event: string, handler: () => void) => { + if (event === "abort") { + abortHandler = handler; + } + }); + const removeEventListener = vi.fn((event: string, handler: () => void) => { + if (event === "abort" && abortHandler === handler) { + abortHandler = null; + } + }); + const fakeSignal = { + aborted: false, + addEventListener, + removeEventListener, + } as AbortSignal; + + if (!resolved) { + throw new Error("expected resolved proxy fetch"); + } + await resolved("https://example.com", { signal: fakeSignal }); + + expect(proxyFetch).toHaveBeenCalledOnce(); + expect(seenSignal).toBeInstanceOf(AbortSignal); + expect(seenSignal).not.toBe(fakeSignal); + expect(addEventListener).toHaveBeenCalledTimes(1); + expect(removeEventListener).toHaveBeenCalledTimes(1); + }); + + it("does not double-wrap an already wrapped proxy fetch", async () => { + const proxyFetch = vi.fn(async () => ({ ok: true }) as Response) as unknown as typeof fetch; + const alreadyWrapped = resolveFetch(proxyFetch); + + const resolved = resolveTelegramFetch(alreadyWrapped); + + expect(resolved).toBe(alreadyWrapped); }); it("honors env enable override", async () => { diff --git a/src/telegram/proxy.ts b/src/telegram/proxy.ts index 1f9c6f2bc35..6aaac004c3f 100644 --- a/src/telegram/proxy.ts +++ b/src/telegram/proxy.ts @@ -1,5 +1,4 @@ import { ProxyAgent, fetch as undiciFetch } from "undici"; -import { wrapFetchWithAbortSignal } from "../infra/fetch.js"; export function makeProxyFetch(proxyUrl: string): typeof fetch { const agent = new ProxyAgent(proxyUrl); @@ -10,5 +9,7 @@ export function makeProxyFetch(proxyUrl: string): typeof fetch { ...(init as Record), dispatcher: agent, }) as unknown as Promise) as typeof fetch; - return wrapFetchWithAbortSignal(fetcher); + // Return raw proxy fetch; call sites that need AbortSignal normalization + // should opt into resolveFetch/wrapFetchWithAbortSignal once at the edge. + return fetcher; }