-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfetch.ts
More file actions
36 lines (32 loc) · 1.39 KB
/
fetch.ts
File metadata and controls
36 lines (32 loc) · 1.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* fetch() wrapper that emits consistent debug logs for the request and,
* on a non-ok response, the response body. The caller still owns error
* construction and body parsing.
*
* All outbound HTTP calls in library code must go through this helper so
* that `--verbose` surfaces the URL, method, and server response body for
* every network error. See `.claude/rules/debug-logging.md`.
*/
import { log } from "./log.ts";
import { withNetworkAccess } from "./host-execution.ts";
import { buildUserAgent } from "./user-agent.ts";
const USER_AGENT = buildUserAgent();
export type LoggedFetchInit = RequestInit & { tag: string };
export async function loggedFetch(url: URL | string, options: LoggedFetchInit): Promise<Response> {
const { tag, ...init } = options;
const method = init.method ?? "GET";
const urlStr = url.toString();
const headers = new Headers(init.headers);
if (!headers.has("user-agent")) headers.set("User-Agent", USER_AGENT);
log.debug(`${tag}: ${method} ${urlStr}`);
const response = await withNetworkAccess(
{ operation: "connect", target: urlStr, label: tag },
async () => fetch(url, { ...init, headers }),
);
if (!response.ok) {
// Clone so the caller can still consume the body for error construction.
const body = await response.clone().text();
log.debug(`${tag}: ${response.status} ${method} ${urlStr} — ${body}`);
}
return response;
}