| import { CHROME_UA } from './constants'; |
| import type { UsageHook } from './redis'; |
| import { buildUpstreamEvent, getUsageScope, sendToAxiom } from './usage'; |
|
|
| interface FetchJsonOptions { |
| timeoutMs?: number; |
| headers?: Record<string, string>; |
| |
| |
| |
| |
| |
| provider?: string; |
| operation?: string; |
| |
| usage?: UsageHook; |
| } |
|
|
| export async function fetchJson<T>( |
| url: string, |
| options: FetchJsonOptions = {}, |
| ): Promise<T | null> { |
| const t0 = Date.now(); |
| let status = 0; |
| let responseBytes = 0; |
| try { |
| const response = await fetch(url, { |
| headers: { |
| Accept: 'application/json', |
| 'User-Agent': CHROME_UA, |
| ...(options.headers || {}), |
| }, |
| signal: AbortSignal.timeout(options.timeoutMs ?? 8_000), |
| }); |
| status = response.status; |
| if (!response.ok) return null; |
| const text = await response.text(); |
| responseBytes = text.length; |
| return JSON.parse(text) as T; |
| } catch { |
| return null; |
| } finally { |
| |
| |
| const provider = options.usage?.provider ?? options.provider; |
| const operation = options.usage?.operation ?? options.operation ?? 'fetch'; |
| if (provider) { |
| const durationMs = Date.now() - t0; |
| const explicit = options.usage; |
| const host = explicit?.host ?? safeHost(url); |
| |
| |
| |
| |
| const scope = getUsageScope(); |
| const ctx = explicit?.ctx ?? scope?.ctx; |
| if (ctx) { |
| const event = buildUpstreamEvent({ |
| requestId: explicit?.requestId ?? scope?.requestId ?? '', |
| customerId: explicit?.customerId ?? scope?.customerId ?? null, |
| route: explicit?.route ?? scope?.route ?? '', |
| tier: explicit?.tier ?? scope?.tier ?? 0, |
| provider, |
| operation, |
| host, |
| status, |
| durationMs, |
| requestBytes: 0, |
| responseBytes, |
| cacheStatus: 'miss', |
| }); |
| try { |
| ctx.waitUntil(sendToAxiom([event])); |
| } catch { |
| |
| } |
| } |
| } |
| } |
| } |
|
|
| function safeHost(url: string): string { |
| try { |
| return new URL(url).host; |
| } catch { |
| return ''; |
| } |
| } |
|
|