-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfetch.mts
More file actions
97 lines (75 loc) · 2.94 KB
/
fetch.mts
File metadata and controls
97 lines (75 loc) · 2.94 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
import { HttpCache, isResponse, intoFastSlowStreams, isRevalidationRequest } from './source/index.mts';
export const storage = new Map<string, any>();
export const globalCache = new HttpCache(storage, storage);
// For debugging purposes
globalCache.onError = console.error;
const fromCache = new WeakSet();
export const isFromCache = (response: Response): boolean => fromCache.has(response);
type HttpCacheRequestInit = Omit<RequestInit, 'headers' | 'cache'> & {
headers?: Record<string, string>,
waitForCache?: boolean,
cache?: HttpCache,
};
export const f = async (requestInfo: string, requestInit?: HttpCacheRequestInit): Promise<Response> => {
const cache = requestInit?.cache ?? globalCache;
const requestHeaders = requestInit?.headers ?? {};
const method = requestInit?.method ?? 'GET';
const requestTime = Date.now();
const cached = await cache.get(requestInfo, method, requestHeaders);
if (isResponse(cached)) {
const response = new Response(cached.body, {
status: cached.status,
headers: cached.headers,
});
fromCache.add(response);
return response;
}
const revalidationHeaders = isRevalidationRequest(cached) ? {
...requestInit?.headers,
...cached.revalidationHeaders,
} : undefined;
let revalidationFailed = false;
while (true) {
const response = await fetch(requestInfo, {
...requestInit,
cache: 'no-store',
headers: revalidationFailed ? requestHeaders : (revalidationHeaders ?? requestHeaders),
});
const responseTime = Date.now();
const [fastBody, slowBody] = response.body === null ? [null, null] : intoFastSlowStreams(response.body.getReader());
const cachePromise = cache.onResponse(
requestInfo,
method,
response.status,
requestHeaders,
response.headers,
requestTime,
responseTime,
slowBody === null ? null : {
[Symbol.asyncIterator]: () => slowBody[Symbol.asyncIterator](),
[Symbol.asyncDispose]: () => slowBody.cancel(),
},
);
if (revalidationHeaders && response.status === 304 && !revalidationFailed) {
await cachePromise;
const cached = await cache.get(requestInfo, method, requestHeaders);
if (isResponse(cached)) {
return new Response(cached.body, {
status: cached.status,
headers: cached.headers,
});
}
// Stored response got invalidated
revalidationFailed = true;
continue;
}
if (requestInit?.waitForCache) {
await cachePromise;
}
return new Response(fastBody, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
});
}
};