/** * HttpInterceptor — wraps the global fetch to automatically report * HTTP error responses (4xx / 5xx) through XLog.captureError. * * Usage: * HttpInterceptor.start(XLog.captureError.bind(XLog)) */ type OnError = (error: unknown, meta?: Record) => void let _originalFetch: typeof fetch | null = null export const HttpInterceptor = { start(onError: OnError): void { if (_originalFetch) return // already installed _originalFetch = global.fetch // eslint-disable-next-line @typescript-eslint/no-explicit-any ;(global as any).fetch = async ( input: RequestInfo | URL, init?: RequestInit, ): Promise => { const original = _originalFetch! try { const res = await original(input, init) if (!res.ok) { const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url onError(new Error(`HTTP ${res.status} ${res.statusText}`), { type: 'api_error', url, status: res.status, }) } return res } catch (e) { const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url onError(e, { type: 'api_error', url }) throw e } } }, stop(): void { if (_originalFetch) { // eslint-disable-next-line @typescript-eslint/no-explicit-any ;(global as any).fetch = _originalFetch _originalFetch = null } }, }