-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fetch-error.ts
89 lines (77 loc) · 2.29 KB
/
fetch-error.ts
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
interface HeadersLike {
get(key: string): string | null;
}
interface FetchErrorOptions {
readonly method?: string | null;
readonly url: string;
readonly status: number;
readonly headers?: Record<string, string> | { get(key: string): string | null } | null;
readonly id?: string | null;
readonly code?: number | string | null;
readonly reason?: unknown;
}
const RESPONSE_HEADER_KEYS = [
'allow',
'content-range',
'proxy-authenticate',
'retry-after',
'upgrade',
'www-authenticate',
] as const;
const isHeadersLike = (headers: HeadersLike | Record<string, string> | null | undefined): headers is HeadersLike => {
return headers != null && 'get' in headers && typeof headers.get === 'function';
};
class FetchError extends Error {
public name = 'FetchError';
/**
* Request method (if available).
*/
public method: string | undefined;
/**
* Response or request URL.
*/
public url: string;
/**
* HTTP response status code.
*/
public status: number;
/**
* Response headers (limited safe set).
*/
public headers: Readonly<Partial<Record<(typeof RESPONSE_HEADER_KEYS)[number], string>>>;
/**
* Request ID which is attached to log messages related to the request.
*/
public id: string | undefined;
/**
* Well known error code constant.
*/
public code: number | string;
/**
* Error that triggered this error (if any).
*/
public reason: unknown;
constructor({ method, url, status, headers, id, code, reason }: FetchErrorOptions) {
method = method?.toUpperCase() ?? undefined;
headers = headers ?? undefined;
id = id ?? undefined;
code = code ?? 'unknown_error';
super(`Failed fetching ${method ? `${method} ` : ''}${url} (${status}, ${code}${id ? `, ${id}` : ''})`);
this.method = method;
this.url = url;
this.status = status;
this.code = code;
this.id = id;
this.reason = reason;
const allHeaders = isHeadersLike(headers) ? headers : new Headers(headers);
const safeHeaders: Partial<Record<(typeof RESPONSE_HEADER_KEYS)[number], string>> = {};
for (const key of RESPONSE_HEADER_KEYS) {
const value = allHeaders.get(key);
if (value) {
safeHeaders[key] = value;
}
}
this.headers = safeHeaders;
}
}
export { FetchError, type FetchErrorOptions };