(url: string, options: OutboundFetchOptions = {})
| 27 | * Bedrock — has uniform metadata to work with. |
| 28 | */ |
| 29 | export async function fetchGo(url: string, options: OutboundFetchOptions = {}): Promise<Response> { |
| 30 | const { |
| 31 | otelContext, |
| 32 | spanName, |
| 33 | operation, |
| 34 | attributes, |
| 35 | headers: providedHeaders, |
| 36 | ...init |
| 37 | } = options |
| 38 | |
| 39 | const parsed = safeParseUrl(url) |
| 40 | const pathname = parsed?.pathname ?? url |
| 41 | const method = (init.method ?? 'GET').toUpperCase() |
| 42 | const parentContext = otelContext ?? context.active() |
| 43 | |
| 44 | const span = getTracer().startSpan( |
| 45 | spanName ?? `sim → go ${pathname}`, |
| 46 | { |
| 47 | attributes: { |
| 48 | [TraceAttr.HttpMethod]: method, |
| 49 | [TraceAttr.HttpUrl]: url, |
| 50 | [TraceAttr.HttpTarget]: pathname, |
| 51 | [TraceAttr.NetPeerName]: parsed?.host ?? '', |
| 52 | [TraceAttr.CopilotLeg]: CopilotLeg.SimToGo, |
| 53 | ...(operation ? { [TraceAttr.CopilotOperation]: operation } : {}), |
| 54 | ...(attributes ?? {}), |
| 55 | }, |
| 56 | }, |
| 57 | parentContext |
| 58 | ) |
| 59 | |
| 60 | const activeContext = trace.setSpan(parentContext, span) |
| 61 | const propagatedHeaders = traceHeaders({}, activeContext) |
| 62 | const mergedHeaders = { |
| 63 | ...(providedHeaders as Record<string, string> | undefined), |
| 64 | ...propagatedHeaders, |
| 65 | } |
| 66 | |
| 67 | const start = performance.now() |
| 68 | try { |
| 69 | const response = await context.with(activeContext, () => |
| 70 | fetch(url, { |
| 71 | ...init, |
| 72 | method, |
| 73 | headers: mergedHeaders, |
| 74 | }) |
| 75 | ) |
| 76 | const elapsedMs = performance.now() - start |
| 77 | const contentLength = Number(response.headers.get('content-length') ?? 0) |
| 78 | span.setAttribute(TraceAttr.HttpStatusCode, response.status) |
| 79 | span.setAttribute(TraceAttr.HttpResponseHeadersMs, Math.round(elapsedMs)) |
| 80 | if (contentLength > 0) { |
| 81 | span.setAttribute(TraceAttr.HttpResponseContentLength, contentLength) |
| 82 | } |
| 83 | // Only mark ERROR for actionable status codes. 4xx that represent |
| 84 | // normal auth/validation rejections (400/401/403/404/405/422/etc.) |
| 85 | // stay UNSET so error dashboards don't drown in expected rejection |
| 86 | // paths. See `isActionableErrorStatus` in Go's telemetry middleware |
no test coverage detected