import { Log } from '../native_adapters/configuration' const SHARED_TAGS = { team: 'chat', package: 'chat-react-native', } as const const SKIPPED_STATUSES = [401, 403, 404] export async function instrumentedFetch(url: string, init: RequestInit): Promise { const method = init.method ?? 'GET' try { const response = await fetch(url, init) if (!response.ok) reportHttpError(response, method, url) return response } catch (networkError) { warnNetworkError(networkError as Error, method, url) throw networkError } } function reportHttpError(response: Response, method: string, url: string) { const { status } = response if (SKIPPED_STATUSES.includes(status)) return const path = templatePath(url) const error = new Error(`HTTP ${status} ${method} ${path}`) error.name = `HTTPError${status}` Log.reportError(error, { scope: 'http', tags: { ...SHARED_TAGS, 'http.status': String(status), 'http.method': method, 'http.path': path, }, }) } function warnNetworkError(networkError: Error, method: string, url: string) { const path = templatePath(url) console.warn(`Network failure ${method} ${path}: ${networkError.message}`, { scope: 'http', tags: { ...SHARED_TAGS, 'http.method': method, 'http.path': path, 'http.error': 'network', }, }) } function templatePath(url: string): string { let path: string try { path = new URL(url).pathname } catch { path = url } return path .split('/') .map(segment => (/^\d+$/.test(segment) ? ':id' : segment)) .join('/') }