@npy/fetch 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/_internal/consts.cjs +4 -0
- package/_internal/consts.d.cts +3 -0
- package/_internal/consts.d.ts +3 -0
- package/_internal/consts.js +4 -0
- package/_internal/decode-stream-error.cjs +18 -0
- package/{src/_internal/decode-stream-error.ts → _internal/decode-stream-error.d.cts} +2 -7
- package/_internal/decode-stream-error.d.ts +11 -0
- package/_internal/decode-stream-error.js +18 -0
- package/_internal/error-mapping.cjs +44 -0
- package/_internal/error-mapping.d.cts +15 -0
- package/_internal/error-mapping.d.ts +15 -0
- package/_internal/error-mapping.js +41 -0
- package/_internal/guards.cjs +23 -0
- package/_internal/guards.d.cts +15 -0
- package/_internal/guards.d.ts +15 -0
- package/_internal/guards.js +15 -0
- package/_internal/net.cjs +95 -0
- package/_internal/net.d.cts +11 -0
- package/_internal/net.d.ts +11 -0
- package/_internal/net.js +92 -0
- package/_internal/promises.cjs +18 -0
- package/_internal/promises.d.cts +1 -0
- package/_internal/promises.d.ts +1 -0
- package/_internal/promises.js +18 -0
- package/_internal/streams.cjs +37 -0
- package/_internal/streams.d.cts +21 -0
- package/_internal/streams.d.ts +21 -0
- package/_internal/streams.js +36 -0
- package/_internal/symbols.cjs +4 -0
- package/_internal/symbols.d.cts +1 -0
- package/_internal/symbols.d.ts +1 -0
- package/_internal/symbols.js +4 -0
- package/_virtual/_rolldown/runtime.cjs +23 -0
- package/agent-pool.cjs +96 -0
- package/agent-pool.d.cts +2 -0
- package/agent-pool.d.ts +2 -0
- package/agent-pool.js +95 -0
- package/agent.cjs +260 -0
- package/agent.d.cts +3 -0
- package/agent.d.ts +3 -0
- package/agent.js +259 -0
- package/body.cjs +105 -0
- package/body.d.cts +12 -0
- package/body.d.ts +12 -0
- package/body.js +102 -0
- package/dialers/index.d.cts +3 -0
- package/dialers/index.d.ts +3 -0
- package/dialers/proxy.cjs +56 -0
- package/dialers/proxy.d.cts +27 -0
- package/dialers/proxy.d.ts +27 -0
- package/dialers/proxy.js +55 -0
- package/dialers/tcp.cjs +92 -0
- package/dialers/tcp.d.cts +57 -0
- package/dialers/tcp.d.ts +57 -0
- package/dialers/tcp.js +89 -0
- package/encoding.cjs +114 -0
- package/encoding.d.cts +35 -0
- package/encoding.d.ts +35 -0
- package/encoding.js +110 -0
- package/errors.cjs +275 -0
- package/errors.d.cts +110 -0
- package/errors.d.ts +110 -0
- package/errors.js +259 -0
- package/fetch.cjs +353 -0
- package/fetch.d.cts +58 -0
- package/fetch.d.ts +58 -0
- package/fetch.js +350 -0
- package/http-client.cjs +75 -0
- package/http-client.d.cts +39 -0
- package/http-client.d.ts +39 -0
- package/http-client.js +75 -0
- package/index.cjs +49 -0
- package/index.d.cts +14 -0
- package/index.d.ts +14 -0
- package/index.js +11 -0
- package/io/_utils.cjs +56 -0
- package/io/_utils.d.cts +10 -0
- package/io/_utils.d.ts +10 -0
- package/io/_utils.js +51 -0
- package/io/buf-writer.cjs +149 -0
- package/io/buf-writer.d.cts +13 -0
- package/io/buf-writer.d.ts +13 -0
- package/io/buf-writer.js +148 -0
- package/io/io.cjs +199 -0
- package/io/io.d.cts +5 -0
- package/io/io.d.ts +5 -0
- package/io/io.js +198 -0
- package/io/readers.cjs +337 -0
- package/io/readers.d.cts +69 -0
- package/io/readers.d.ts +69 -0
- package/io/readers.js +333 -0
- package/io/writers.cjs +196 -0
- package/io/writers.d.cts +22 -0
- package/io/writers.d.ts +22 -0
- package/io/writers.js +195 -0
- package/package.json +30 -25
- package/{src/types/agent.ts → types/agent.d.cts} +21 -47
- package/types/agent.d.ts +72 -0
- package/{src/types/dialer.ts → types/dialer.d.cts} +9 -19
- package/types/dialer.d.ts +30 -0
- package/types/index.d.cts +2 -0
- package/types/index.d.ts +2 -0
- package/bun.lock +0 -68
- package/examples/custom-proxy-client.ts +0 -32
- package/examples/http-client.ts +0 -47
- package/examples/proxy.ts +0 -16
- package/examples/simple.ts +0 -15
- package/src/_internal/consts.ts +0 -3
- package/src/_internal/error-mapping.ts +0 -160
- package/src/_internal/guards.ts +0 -78
- package/src/_internal/net.ts +0 -173
- package/src/_internal/promises.ts +0 -22
- package/src/_internal/streams.ts +0 -52
- package/src/_internal/symbols.ts +0 -1
- package/src/agent-pool.ts +0 -157
- package/src/agent.ts +0 -408
- package/src/body.ts +0 -179
- package/src/dialers/index.ts +0 -3
- package/src/dialers/proxy.ts +0 -102
- package/src/dialers/tcp.ts +0 -162
- package/src/encoding.ts +0 -222
- package/src/errors.ts +0 -357
- package/src/fetch.ts +0 -626
- package/src/http-client.ts +0 -111
- package/src/index.ts +0 -14
- package/src/io/_utils.ts +0 -82
- package/src/io/buf-writer.ts +0 -183
- package/src/io/io.ts +0 -322
- package/src/io/readers.ts +0 -576
- package/src/io/writers.ts +0 -331
- package/src/types/index.ts +0 -2
- package/tests/agent-pool.test.ts +0 -111
- package/tests/agent.test.ts +0 -134
- package/tests/body.test.ts +0 -228
- package/tests/errors.test.ts +0 -152
- package/tests/fetch.test.ts +0 -421
- package/tests/io-options.test.ts +0 -127
- package/tests/multipart.test.ts +0 -348
- package/tests/test-utils.ts +0 -335
- package/tsconfig.json +0 -15
package/tests/body.test.ts
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { Readable } from "node:stream";
|
|
3
|
-
import { extractBody, getFormDataLength } from "../src/body";
|
|
4
|
-
|
|
5
|
-
async function readAllBytes(readable: Readable): Promise<Uint8Array> {
|
|
6
|
-
const chunks: Buffer[] = [];
|
|
7
|
-
for await (const chunk of readable) {
|
|
8
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
9
|
-
}
|
|
10
|
-
return new Uint8Array(Buffer.concat(chunks));
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function parseBoundary(contentType: string): string {
|
|
14
|
-
const match = /boundary=([^\s;]+)/.exec(contentType);
|
|
15
|
-
if (!match) throw new Error(`No boundary in: ${contentType}`);
|
|
16
|
-
return match[1]!;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe("extractBody", () => {
|
|
20
|
-
test("null returns empty body with zero content-length", () => {
|
|
21
|
-
const state = extractBody(null);
|
|
22
|
-
expect(state.body).toBeNull();
|
|
23
|
-
expect(state.contentLength).toBe(0);
|
|
24
|
-
expect(state.contentType).toBeNull();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("string sets UTF-8 content-type and correct byte length", () => {
|
|
28
|
-
const state = extractBody("héllo");
|
|
29
|
-
expect(state.body).toBeInstanceOf(Uint8Array);
|
|
30
|
-
expect(state.contentType).toBe("text/plain;charset=UTF-8");
|
|
31
|
-
|
|
32
|
-
expect(state.contentLength).toBe(6);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("Uint8Array passes through with exact size", () => {
|
|
36
|
-
const bytes = new Uint8Array([10, 20, 30, 40]);
|
|
37
|
-
const state = extractBody(bytes);
|
|
38
|
-
expect(state.body).toBe(bytes);
|
|
39
|
-
expect(state.contentLength).toBe(4);
|
|
40
|
-
expect(state.contentType).toBeNull();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test("URLSearchParams sets form-urlencoded content-type", () => {
|
|
44
|
-
const params = new URLSearchParams({ a: "1", b: "2" });
|
|
45
|
-
const state = extractBody(params);
|
|
46
|
-
expect(state.contentType).toContain(
|
|
47
|
-
"application/x-www-form-urlencoded",
|
|
48
|
-
);
|
|
49
|
-
expect(state.body).toBeInstanceOf(Uint8Array);
|
|
50
|
-
const text = new TextDecoder().decode(state.body as Uint8Array);
|
|
51
|
-
expect(text).toBe("a=1&b=2");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("FormData sets multipart content-type with boundary", () => {
|
|
55
|
-
const form = new FormData();
|
|
56
|
-
form.append("key", "value");
|
|
57
|
-
|
|
58
|
-
const state = extractBody(form);
|
|
59
|
-
expect(state.contentType).toContain("multipart/form-data");
|
|
60
|
-
expect(state.contentType).toContain("boundary=");
|
|
61
|
-
expect(state.contentLength).toBeGreaterThan(0);
|
|
62
|
-
expect(state.body).toBeInstanceOf(Readable);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("empty FormData produces a valid (footer-only) body", () => {
|
|
66
|
-
const state = extractBody(new FormData());
|
|
67
|
-
expect(state.contentType).toContain("multipart/form-data");
|
|
68
|
-
expect(state.contentLength).toBeGreaterThan(0);
|
|
69
|
-
|
|
70
|
-
expect(state.body).toBeInstanceOf(Readable);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("ReadableStream body is passed through unchanged", () => {
|
|
74
|
-
const stream = new ReadableStream();
|
|
75
|
-
const state = extractBody(stream);
|
|
76
|
-
expect(state.body).toBe(stream);
|
|
77
|
-
expect(state.contentLength).toBeNull();
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe("getFormDataLength", () => {
|
|
82
|
-
test("matches actual serialized byte count — text-only fields", async () => {
|
|
83
|
-
const form = new FormData();
|
|
84
|
-
form.append("username", "alice");
|
|
85
|
-
form.append("email", "alice@example.com");
|
|
86
|
-
|
|
87
|
-
const state = extractBody(form);
|
|
88
|
-
const boundary = parseBoundary(state.contentType!);
|
|
89
|
-
const declared = getFormDataLength(form, boundary);
|
|
90
|
-
|
|
91
|
-
const actual = await readAllBytes(state.body as Readable);
|
|
92
|
-
expect(actual.byteLength).toBe(declared);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test("matches actual serialized byte count — single Blob field", async () => {
|
|
96
|
-
const form = new FormData();
|
|
97
|
-
form.append(
|
|
98
|
-
"file",
|
|
99
|
-
new Blob(["binary content"], { type: "application/octet-stream" }),
|
|
100
|
-
"data.bin",
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const state = extractBody(form);
|
|
104
|
-
const boundary = parseBoundary(state.contentType!);
|
|
105
|
-
const declared = getFormDataLength(form, boundary);
|
|
106
|
-
|
|
107
|
-
const actual = await readAllBytes(state.body as Readable);
|
|
108
|
-
expect(actual.byteLength).toBe(declared);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("matches actual serialized byte count — mixed text and Blob fields", async () => {
|
|
112
|
-
const form = new FormData();
|
|
113
|
-
form.append("description", "a short text");
|
|
114
|
-
form.append(
|
|
115
|
-
"attachment",
|
|
116
|
-
new Blob(["<html></html>"], { type: "text/html" }),
|
|
117
|
-
"page.html",
|
|
118
|
-
);
|
|
119
|
-
form.append("tag", "important");
|
|
120
|
-
|
|
121
|
-
const state = extractBody(form);
|
|
122
|
-
const boundary = parseBoundary(state.contentType!);
|
|
123
|
-
const declared = getFormDataLength(form, boundary);
|
|
124
|
-
|
|
125
|
-
const actual = await readAllBytes(state.body as Readable);
|
|
126
|
-
expect(actual.byteLength).toBe(declared);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test("matches for empty FormData", async () => {
|
|
130
|
-
const form = new FormData();
|
|
131
|
-
|
|
132
|
-
const state = extractBody(form);
|
|
133
|
-
const boundary = parseBoundary(state.contentType!);
|
|
134
|
-
const declared = getFormDataLength(form, boundary);
|
|
135
|
-
|
|
136
|
-
const actual = await readAllBytes(state.body as Readable);
|
|
137
|
-
expect(actual.byteLength).toBe(declared);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test("matches for multibyte UTF-8 field value", async () => {
|
|
141
|
-
const form = new FormData();
|
|
142
|
-
form.append("greeting", "こんにちは");
|
|
143
|
-
|
|
144
|
-
const state = extractBody(form);
|
|
145
|
-
const boundary = parseBoundary(state.contentType!);
|
|
146
|
-
const declared = getFormDataLength(form, boundary);
|
|
147
|
-
|
|
148
|
-
const actual = await readAllBytes(state.body as Readable);
|
|
149
|
-
expect(actual.byteLength).toBe(declared);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe("multipart wire format", () => {
|
|
154
|
-
test("text field produces valid multipart structure", async () => {
|
|
155
|
-
const form = new FormData();
|
|
156
|
-
form.append("name", "bob");
|
|
157
|
-
|
|
158
|
-
const state = extractBody(form);
|
|
159
|
-
const boundary = parseBoundary(state.contentType!);
|
|
160
|
-
const bytes = await readAllBytes(state.body as Readable);
|
|
161
|
-
const text = new TextDecoder().decode(bytes);
|
|
162
|
-
|
|
163
|
-
expect(text).toContain(`--${boundary}\r\n`);
|
|
164
|
-
expect(text).toContain(`Content-Disposition: form-data; name="name"`);
|
|
165
|
-
expect(text).toContain(`\r\n\r\nbob\r\n`);
|
|
166
|
-
expect(text).toContain(`--${boundary}--`);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("Blob field includes filename and Content-Type headers", async () => {
|
|
170
|
-
const form = new FormData();
|
|
171
|
-
form.append(
|
|
172
|
-
"upload",
|
|
173
|
-
new Blob(["data"], { type: "text/plain" }),
|
|
174
|
-
"notes.txt",
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
const state = extractBody(form);
|
|
178
|
-
const bytes = await readAllBytes(state.body as Readable);
|
|
179
|
-
const text = new TextDecoder().decode(bytes);
|
|
180
|
-
|
|
181
|
-
expect(text).toContain(`filename="notes.txt"`);
|
|
182
|
-
expect(text).toContain(`Content-Type: text/plain`);
|
|
183
|
-
expect(text).toContain(`data`);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test("Blob without explicit filename falls back to 'blob'", async () => {
|
|
187
|
-
const form = new FormData();
|
|
188
|
-
|
|
189
|
-
form.append("f", new Blob(["x"]));
|
|
190
|
-
|
|
191
|
-
const state = extractBody(form);
|
|
192
|
-
const bytes = await readAllBytes(state.body as Readable);
|
|
193
|
-
const text = new TextDecoder().decode(bytes);
|
|
194
|
-
|
|
195
|
-
expect(text).toContain(`filename="blob"`);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test("Blob without type falls back to application/octet-stream", async () => {
|
|
199
|
-
const form = new FormData();
|
|
200
|
-
form.append("f", new Blob(["x"]), "file.dat");
|
|
201
|
-
|
|
202
|
-
const state = extractBody(form);
|
|
203
|
-
const bytes = await readAllBytes(state.body as Readable);
|
|
204
|
-
const text = new TextDecoder().decode(bytes);
|
|
205
|
-
|
|
206
|
-
expect(text).toContain(`Content-Type: application/octet-stream`);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("each field is separated by boundary", async () => {
|
|
210
|
-
const form = new FormData();
|
|
211
|
-
form.append("a", "1");
|
|
212
|
-
form.append("b", "2");
|
|
213
|
-
form.append("c", "3");
|
|
214
|
-
|
|
215
|
-
const state = extractBody(form);
|
|
216
|
-
const boundary = parseBoundary(state.contentType!);
|
|
217
|
-
const bytes = await readAllBytes(state.body as Readable);
|
|
218
|
-
const text = new TextDecoder().decode(bytes);
|
|
219
|
-
|
|
220
|
-
const openings = (
|
|
221
|
-
text.match(new RegExp(`--${boundary}\r\n`, "g")) ?? []
|
|
222
|
-
).length;
|
|
223
|
-
const closing = (text.match(new RegExp(`--${boundary}--`, "g")) ?? [])
|
|
224
|
-
.length;
|
|
225
|
-
expect(openings).toBe(3);
|
|
226
|
-
expect(closing).toBe(1);
|
|
227
|
-
});
|
|
228
|
-
});
|
package/tests/errors.test.ts
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
AgentBusyError,
|
|
4
|
-
ConnectionError,
|
|
5
|
-
ConnectTimeoutError,
|
|
6
|
-
ErrorType,
|
|
7
|
-
FetchError,
|
|
8
|
-
FetchErrorCode,
|
|
9
|
-
HttpStatusError,
|
|
10
|
-
OriginMismatchError,
|
|
11
|
-
RequestAbortedError,
|
|
12
|
-
ResponseBodyError,
|
|
13
|
-
ResponseHeaderError,
|
|
14
|
-
UnsupportedMethodError,
|
|
15
|
-
} from "../src/errors";
|
|
16
|
-
|
|
17
|
-
describe("errors.ts", () => {
|
|
18
|
-
test("base FetchError exposes code, phase, retryable and cause", () => {
|
|
19
|
-
const cause = new Error("boom");
|
|
20
|
-
const error = new ResponseHeaderError(cause, {
|
|
21
|
-
url: "http://example.test/resource",
|
|
22
|
-
method: "GET",
|
|
23
|
-
origin: "http://example.test",
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
expect(error).toBeInstanceOf(FetchError);
|
|
27
|
-
expect(error.name).toBe("ResponseHeaderError");
|
|
28
|
-
expect(error.code).toBe(FetchErrorCode.RESPONSE_HEADERS);
|
|
29
|
-
expect(error.phase).toBe("response");
|
|
30
|
-
expect(error.retryable).toBe(true);
|
|
31
|
-
expect(error.type).toBe(ErrorType.NETWORK);
|
|
32
|
-
expect(error.cause).toBe(cause);
|
|
33
|
-
expect(error.context?.url).toBe("http://example.test/resource");
|
|
34
|
-
expect(error.context?.method).toBe("GET");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("abort/connect/network errors expose the expected classifications", () => {
|
|
38
|
-
const cause = new Error("boom");
|
|
39
|
-
|
|
40
|
-
const aborted = new RequestAbortedError(cause, {
|
|
41
|
-
url: "http://example.test/abort",
|
|
42
|
-
method: "GET",
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const timeout = new ConnectTimeoutError(cause, {
|
|
46
|
-
host: "example.test",
|
|
47
|
-
port: 443,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const network = new ConnectionError(cause, {
|
|
51
|
-
host: "example.test",
|
|
52
|
-
port: 443,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
expect(aborted.code).toBe(FetchErrorCode.ABORTED);
|
|
56
|
-
expect(aborted.phase).toBe("request");
|
|
57
|
-
expect(aborted.retryable).toBe(false);
|
|
58
|
-
expect(aborted.type).toBe(ErrorType.ABORTED);
|
|
59
|
-
|
|
60
|
-
expect(timeout.code).toBe(FetchErrorCode.TIMEOUT);
|
|
61
|
-
expect(timeout.phase).toBe("connect");
|
|
62
|
-
expect(timeout.retryable).toBe(true);
|
|
63
|
-
expect(timeout.type).toBe(ErrorType.TIMEOUT);
|
|
64
|
-
|
|
65
|
-
expect(network.code).toBe(FetchErrorCode.CONNECTION);
|
|
66
|
-
expect(network.phase).toBe("connect");
|
|
67
|
-
expect(network.retryable).toBe(true);
|
|
68
|
-
expect(network.type).toBe(ErrorType.NETWORK);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test("agent/policy errors expose specific metadata", () => {
|
|
72
|
-
const busy = new AgentBusyError({
|
|
73
|
-
origin: "http://example.test",
|
|
74
|
-
host: "example.test",
|
|
75
|
-
port: 80,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const mismatch = new OriginMismatchError(
|
|
79
|
-
"http://a.test",
|
|
80
|
-
"http://b.test",
|
|
81
|
-
{ url: "http://b.test/resource" },
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const unsupportedMethod = new UnsupportedMethodError("CONNECT", {
|
|
85
|
-
url: "http://example.test",
|
|
86
|
-
method: "CONNECT",
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
expect(busy.code).toBe(FetchErrorCode.AGENT_BUSY);
|
|
90
|
-
expect(busy.phase).toBe("agent");
|
|
91
|
-
expect(busy.retryable).toBe(true);
|
|
92
|
-
|
|
93
|
-
expect(mismatch.code).toBe(FetchErrorCode.ORIGIN_MISMATCH);
|
|
94
|
-
expect(mismatch.phase).toBe("policy");
|
|
95
|
-
expect(mismatch.retryable).toBe(false);
|
|
96
|
-
expect(mismatch.context?.details?.expectedOrigin).toBe("http://a.test");
|
|
97
|
-
expect(mismatch.context?.details?.actualOrigin).toBe("http://b.test");
|
|
98
|
-
|
|
99
|
-
expect(unsupportedMethod.code).toBe(FetchErrorCode.UNSUPPORTED_METHOD);
|
|
100
|
-
expect(unsupportedMethod.phase).toBe("policy");
|
|
101
|
-
expect(unsupportedMethod.retryable).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("HTTP status errors classify 4xx and 5xx correctly", () => {
|
|
105
|
-
const http4xx = new HttpStatusError(404, {
|
|
106
|
-
url: "http://example.test/not-found",
|
|
107
|
-
method: "GET",
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const http5xx = new HttpStatusError(503, {
|
|
111
|
-
url: "http://example.test/unavailable",
|
|
112
|
-
method: "GET",
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
expect(http4xx.code).toBe(FetchErrorCode.HTTP_STATUS);
|
|
116
|
-
expect(http4xx.phase).toBe("response");
|
|
117
|
-
expect(http4xx.retryable).toBe(false);
|
|
118
|
-
expect(http4xx.type).toBe(ErrorType.HTTP_CLIENT_ERROR);
|
|
119
|
-
expect(http4xx.statusCode).toBe(404);
|
|
120
|
-
|
|
121
|
-
expect(http5xx.code).toBe(FetchErrorCode.HTTP_STATUS);
|
|
122
|
-
expect(http5xx.phase).toBe("response");
|
|
123
|
-
expect(http5xx.retryable).toBe(true);
|
|
124
|
-
expect(http5xx.type).toBe(ErrorType.HTTP_SERVER_ERROR);
|
|
125
|
-
expect(http5xx.statusCode).toBe(503);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("toJSON exposes the stable diagnostic payload", () => {
|
|
129
|
-
const cause = new Error("boom");
|
|
130
|
-
const error = new ResponseBodyError(cause, {
|
|
131
|
-
url: "http://example.test/stream",
|
|
132
|
-
method: "GET",
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
expect(error.toJSON()).toEqual({
|
|
136
|
-
name: "ResponseBodyError",
|
|
137
|
-
message: "Failed while reading response body",
|
|
138
|
-
code: FetchErrorCode.RESPONSE_BODY,
|
|
139
|
-
phase: "body",
|
|
140
|
-
retryable: true,
|
|
141
|
-
type: ErrorType.NETWORK,
|
|
142
|
-
context: {
|
|
143
|
-
url: "http://example.test/stream",
|
|
144
|
-
method: "GET",
|
|
145
|
-
},
|
|
146
|
-
cause: {
|
|
147
|
-
name: "Error",
|
|
148
|
-
message: "boom",
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
});
|