aboutsummaryrefslogtreecommitdiff
path: root/packages/bun-release/src/fetch.ts
blob: d9c85a5f3e0db9951da88a72f1c737a33227ff35 (plain) (blame)
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import { debug, isDebug } from "./console";

export const fetch = "fetch" in globalThis ? webFetch : nodeFetch;

type Options = RequestInit & { assert?: boolean };

async function webFetch(url: string, options: Options = {}): Promise<Response> {
  debug("fetch request", url, options);
  const response = await globalThis.fetch(url, options, { verbose: isDebug });
  debug("fetch response", response);
  if (options?.assert !== false && !isOk(response.status)) {
    try {
      debug(await response.text());
    } catch {}
    throw new Error(`${response.status}: ${url}`);
  }
  return response;
}

async function nodeFetch(
  url: string,
  options: Options = {},
): Promise<Response> {
  const { get } = await import("node:http");
  return new Promise((resolve, reject) => {
    get(url, (response) => {
      debug("http.get", url, response.statusCode);
      const status = response.statusCode ?? 501;
      if (response.headers.location && isRedirect(status)) {
        return nodeFetch(url).then(resolve, reject);
      }
      if (options?.assert !== false && !isOk(status)) {
        return reject(new Error(`${status}: ${url}`));
      }
      const body: Buffer[] = [];
      response.on("data", (chunk) => {
        body.push(chunk);
      });
      response.on("end", () => {
        resolve({
          ok: isOk(status),
          status,
          async arrayBuffer() {
            return Buffer.concat(body).buffer as ArrayBuffer;
          },
          async text() {
            return Buffer.concat(body).toString("utf-8");
          },
          async json() {
            const text = Buffer.concat(body).toString("utf-8");
            return JSON.parse(text);
          },
        } as Response);
      });
    }).on("error", reject);
  });
}

function isOk(status: number): boolean {
  return status >= 200 && status <= 204;
}

function isRedirect(status: number): boolean {
  switch (status) {
    case 301: // Moved Permanently
    case 308: // Permanent Redirect
    case 302: // Found
    case 307: // Temporary Redirect
    case 303: // See Other
      return true;
  }
  return false;
}