52 行
1.5 KiB
TypeScript
52 行
1.5 KiB
TypeScript
|
|
/**
|
||
|
|
* 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<string, unknown>) => 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<Response> => {
|
||
|
|
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
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|