-
Notifications
You must be signed in to change notification settings - Fork 201
Expand file tree
/
Copy pathfetch.ts
More file actions
106 lines (92 loc) · 2.83 KB
/
fetch.ts
File metadata and controls
106 lines (92 loc) · 2.83 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
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import type { Cacheable } from "cacheable";
import {
type RequestInit,
type Response as UndiciResponse,
fetch as undiciFetch,
} from "undici";
export type FetchOptions = Omit<RequestInit, "cache"> & {
cache: Cacheable;
};
/**
* Fetch data from a URL with optional request options.
* @param {string} url The URL to fetch.
* @param {FetchOptions} options Optional request options. The `cacheable` property is required and should be an
* instance of `Cacheable` or a `CacheableOptions` object.
* @returns {Promise<UndiciResponse>} The response from the fetch.
*/
export async function fetch(
url: string,
options: FetchOptions,
): Promise<UndiciResponse> {
if (!options.cache) {
throw new Error("Fetch options must include a cache instance or options.");
}
const fetchOptions: RequestInit = {
...options,
cache: "no-cache",
};
// Create a cache key that includes the method
const cacheKey = `${options.method || "GET"}:${url}`;
const cachedData = await options.cache.getOrSet(cacheKey, async () => {
// Perform the fetch operation
const response = await undiciFetch(url, fetchOptions);
/* c8 ignore next 3 */
if (!response.ok) {
throw new Error(`Fetch failed with status ${response.status}`);
}
// Convert response to cacheable format
const body = await response.text();
return {
body,
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()),
};
});
// Reconstruct Response object from cached data
/* c8 ignore next 3 */
if (!cachedData) {
throw new Error("Failed to get or set cache data");
}
return new Response(cachedData.body, {
status: cachedData.status,
statusText: cachedData.statusText,
headers: cachedData.headers,
}) as UndiciResponse;
}
export type GetResponse<T = unknown> = {
data: T;
response: UndiciResponse;
};
/**
* Perform a GET request to a URL with optional request options.
* @param {string} url The URL to fetch.
* @param {Omit<FetchOptions, 'method'>} options Optional request options. The `cache` property is required.
* @returns {Promise<GetResponse<T>>} The typed data and response from the fetch.
*/
export async function get<T = unknown>(
url: string,
options: Omit<FetchOptions, "method">,
): Promise<GetResponse<T>> {
const response = await fetch(url, { ...options, method: "GET" });
const text = await response.text();
let data: T;
try {
data = JSON.parse(text) as T;
} catch {
// If not JSON, return as is
data = text as T;
}
// Create a new response with the text already consumed
const newResponse = new Response(text, {
status: response.status,
statusText: response.statusText,
headers: response.headers as HeadersInit,
}) as UndiciResponse;
return {
data,
response: newResponse,
};
}
export type Response = UndiciResponse;
export type { RequestInit as FetchRequestInit } from "undici";