@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.
Files changed (141) hide show
  1. package/LICENSE +21 -0
  2. package/_internal/consts.cjs +4 -0
  3. package/_internal/consts.d.cts +3 -0
  4. package/_internal/consts.d.ts +3 -0
  5. package/_internal/consts.js +4 -0
  6. package/_internal/decode-stream-error.cjs +18 -0
  7. package/{src/_internal/decode-stream-error.ts → _internal/decode-stream-error.d.cts} +2 -7
  8. package/_internal/decode-stream-error.d.ts +11 -0
  9. package/_internal/decode-stream-error.js +18 -0
  10. package/_internal/error-mapping.cjs +44 -0
  11. package/_internal/error-mapping.d.cts +15 -0
  12. package/_internal/error-mapping.d.ts +15 -0
  13. package/_internal/error-mapping.js +41 -0
  14. package/_internal/guards.cjs +23 -0
  15. package/_internal/guards.d.cts +15 -0
  16. package/_internal/guards.d.ts +15 -0
  17. package/_internal/guards.js +15 -0
  18. package/_internal/net.cjs +95 -0
  19. package/_internal/net.d.cts +11 -0
  20. package/_internal/net.d.ts +11 -0
  21. package/_internal/net.js +92 -0
  22. package/_internal/promises.cjs +18 -0
  23. package/_internal/promises.d.cts +1 -0
  24. package/_internal/promises.d.ts +1 -0
  25. package/_internal/promises.js +18 -0
  26. package/_internal/streams.cjs +37 -0
  27. package/_internal/streams.d.cts +21 -0
  28. package/_internal/streams.d.ts +21 -0
  29. package/_internal/streams.js +36 -0
  30. package/_internal/symbols.cjs +4 -0
  31. package/_internal/symbols.d.cts +1 -0
  32. package/_internal/symbols.d.ts +1 -0
  33. package/_internal/symbols.js +4 -0
  34. package/_virtual/_rolldown/runtime.cjs +23 -0
  35. package/agent-pool.cjs +96 -0
  36. package/agent-pool.d.cts +2 -0
  37. package/agent-pool.d.ts +2 -0
  38. package/agent-pool.js +95 -0
  39. package/agent.cjs +260 -0
  40. package/agent.d.cts +3 -0
  41. package/agent.d.ts +3 -0
  42. package/agent.js +259 -0
  43. package/body.cjs +105 -0
  44. package/body.d.cts +12 -0
  45. package/body.d.ts +12 -0
  46. package/body.js +102 -0
  47. package/dialers/index.d.cts +3 -0
  48. package/dialers/index.d.ts +3 -0
  49. package/dialers/proxy.cjs +56 -0
  50. package/dialers/proxy.d.cts +27 -0
  51. package/dialers/proxy.d.ts +27 -0
  52. package/dialers/proxy.js +55 -0
  53. package/dialers/tcp.cjs +92 -0
  54. package/dialers/tcp.d.cts +57 -0
  55. package/dialers/tcp.d.ts +57 -0
  56. package/dialers/tcp.js +89 -0
  57. package/encoding.cjs +114 -0
  58. package/encoding.d.cts +35 -0
  59. package/encoding.d.ts +35 -0
  60. package/encoding.js +110 -0
  61. package/errors.cjs +275 -0
  62. package/errors.d.cts +110 -0
  63. package/errors.d.ts +110 -0
  64. package/errors.js +259 -0
  65. package/fetch.cjs +353 -0
  66. package/fetch.d.cts +58 -0
  67. package/fetch.d.ts +58 -0
  68. package/fetch.js +350 -0
  69. package/http-client.cjs +75 -0
  70. package/http-client.d.cts +39 -0
  71. package/http-client.d.ts +39 -0
  72. package/http-client.js +75 -0
  73. package/index.cjs +49 -0
  74. package/index.d.cts +14 -0
  75. package/index.d.ts +14 -0
  76. package/index.js +11 -0
  77. package/io/_utils.cjs +56 -0
  78. package/io/_utils.d.cts +10 -0
  79. package/io/_utils.d.ts +10 -0
  80. package/io/_utils.js +51 -0
  81. package/io/buf-writer.cjs +149 -0
  82. package/io/buf-writer.d.cts +13 -0
  83. package/io/buf-writer.d.ts +13 -0
  84. package/io/buf-writer.js +148 -0
  85. package/io/io.cjs +199 -0
  86. package/io/io.d.cts +5 -0
  87. package/io/io.d.ts +5 -0
  88. package/io/io.js +198 -0
  89. package/io/readers.cjs +337 -0
  90. package/io/readers.d.cts +69 -0
  91. package/io/readers.d.ts +69 -0
  92. package/io/readers.js +333 -0
  93. package/io/writers.cjs +196 -0
  94. package/io/writers.d.cts +22 -0
  95. package/io/writers.d.ts +22 -0
  96. package/io/writers.js +195 -0
  97. package/package.json +30 -25
  98. package/{src/types/agent.ts → types/agent.d.cts} +21 -47
  99. package/types/agent.d.ts +72 -0
  100. package/{src/types/dialer.ts → types/dialer.d.cts} +9 -19
  101. package/types/dialer.d.ts +30 -0
  102. package/types/index.d.cts +2 -0
  103. package/types/index.d.ts +2 -0
  104. package/bun.lock +0 -68
  105. package/examples/custom-proxy-client.ts +0 -32
  106. package/examples/http-client.ts +0 -47
  107. package/examples/proxy.ts +0 -16
  108. package/examples/simple.ts +0 -15
  109. package/src/_internal/consts.ts +0 -3
  110. package/src/_internal/error-mapping.ts +0 -160
  111. package/src/_internal/guards.ts +0 -78
  112. package/src/_internal/net.ts +0 -173
  113. package/src/_internal/promises.ts +0 -22
  114. package/src/_internal/streams.ts +0 -52
  115. package/src/_internal/symbols.ts +0 -1
  116. package/src/agent-pool.ts +0 -157
  117. package/src/agent.ts +0 -408
  118. package/src/body.ts +0 -179
  119. package/src/dialers/index.ts +0 -3
  120. package/src/dialers/proxy.ts +0 -102
  121. package/src/dialers/tcp.ts +0 -162
  122. package/src/encoding.ts +0 -222
  123. package/src/errors.ts +0 -357
  124. package/src/fetch.ts +0 -626
  125. package/src/http-client.ts +0 -111
  126. package/src/index.ts +0 -14
  127. package/src/io/_utils.ts +0 -82
  128. package/src/io/buf-writer.ts +0 -183
  129. package/src/io/io.ts +0 -322
  130. package/src/io/readers.ts +0 -576
  131. package/src/io/writers.ts +0 -331
  132. package/src/types/index.ts +0 -2
  133. package/tests/agent-pool.test.ts +0 -111
  134. package/tests/agent.test.ts +0 -134
  135. package/tests/body.test.ts +0 -228
  136. package/tests/errors.test.ts +0 -152
  137. package/tests/fetch.test.ts +0 -421
  138. package/tests/io-options.test.ts +0 -127
  139. package/tests/multipart.test.ts +0 -348
  140. package/tests/test-utils.ts +0 -335
  141. package/tsconfig.json +0 -15
package/src/agent-pool.ts DELETED
@@ -1,157 +0,0 @@
1
- import { createPool } from "generic-pool";
2
- import { createAgent } from "./agent";
3
- import { AutoDialer } from "./dialers";
4
- import { UnsupportedProtocolError } from "./errors";
5
- import type { Agent, AgentPool } from "./types/agent";
6
-
7
- const defaultEvictionInterval = 10_000;
8
- const defaultMax = Number.MAX_SAFE_INTEGER;
9
- const defaultIdleTimeout = 30_000;
10
-
11
- export function createAgentPool(
12
- baseUrl: string,
13
- options: AgentPool.Options = {},
14
- ): AgentPool {
15
- const poolUrl = new URL(baseUrl);
16
-
17
- const evictionRunIntervalMillis =
18
- options.poolIdleTimeout !== false
19
- ? Math.min(
20
- options.poolIdleTimeout || defaultEvictionInterval,
21
- defaultEvictionInterval,
22
- )
23
- : 0;
24
- const max = options.poolMaxPerHost
25
- ? Math.max(1, options.poolMaxPerHost)
26
- : defaultMax;
27
- const softIdleTimeoutMillis =
28
- options.poolIdleTimeout !== false
29
- ? Math.max(1, options.poolIdleTimeout || defaultIdleTimeout)
30
- : -1;
31
- const min =
32
- softIdleTimeoutMillis > 0 && options.poolMaxIdlePerHost
33
- ? Math.max(0, options.poolMaxIdlePerHost)
34
- : 0;
35
-
36
- if (poolUrl.protocol !== "http:" && poolUrl.protocol !== "https:") {
37
- throw new UnsupportedProtocolError(poolUrl.protocol, {
38
- origin: poolUrl.origin,
39
- scheme: poolUrl.protocol,
40
- host: poolUrl.hostname,
41
- port: poolUrl.port ? Number.parseInt(poolUrl.port, 10) : undefined,
42
- });
43
- }
44
-
45
- const dialer = options.dialer ?? new AutoDialer();
46
- const connectOptions = options.connect ?? {};
47
- const ioOptions = options.io;
48
-
49
- const pool = createPool<Agent>(
50
- {
51
- async create() {
52
- return createAgent(dialer, baseUrl, {
53
- connect: connectOptions,
54
- io: ioOptions,
55
- });
56
- },
57
- async destroy(agent) {
58
- agent.close();
59
- },
60
- },
61
- {
62
- autostart: false,
63
- evictionRunIntervalMillis,
64
- softIdleTimeoutMillis,
65
- max,
66
- min,
67
- },
68
- );
69
-
70
- let releaseAgentFns: Array<(forceClose?: boolean) => Promise<void>> = [];
71
- let closePromise: Promise<void> | undefined;
72
-
73
- async function send(sendOptions: Agent.SendOptions): Promise<Response> {
74
- let agent: Agent | undefined;
75
- let agentReleased = false;
76
-
77
- const releaseAgentFn = async (forceClose = false) => {
78
- if (!agent || agentReleased) return;
79
- agentReleased = true;
80
- releaseAgentFns = releaseAgentFns.filter(
81
- (release) => release !== releaseAgentFn,
82
- );
83
- if (forceClose) agent.close();
84
- if (pool.isBorrowedResource(agent)) await pool.release(agent);
85
- };
86
-
87
- releaseAgentFns.push(releaseAgentFn);
88
-
89
- try {
90
- agent = await pool.acquire();
91
- const responsePromise = agent.send(sendOptions);
92
-
93
- void agent.whenIdle().then(
94
- () => releaseAgentFn(),
95
- () => releaseAgentFn(true),
96
- );
97
-
98
- return responsePromise;
99
- } catch (error) {
100
- await releaseAgentFn(true);
101
- throw error;
102
- }
103
- }
104
-
105
- async function close(): Promise<void> {
106
- if (closePromise) return closePromise;
107
-
108
- const promise = (async () => {
109
- const pendingReleases = releaseAgentFns;
110
- releaseAgentFns = [];
111
-
112
- const results = await Promise.allSettled([
113
- ...pendingReleases.map((release) => release(true)),
114
- (async () => {
115
- try {
116
- await pool.drain();
117
- } finally {
118
- await pool.clear();
119
- }
120
- })(),
121
- ]);
122
-
123
- const errors = results.flatMap((result) =>
124
- result.status === "rejected" ? [result.reason] : [],
125
- );
126
-
127
- if (errors.length === 1) throw errors[0];
128
-
129
- if (errors.length > 1) {
130
- throw new AggregateError(
131
- errors,
132
- "Failed to close agent pool cleanly",
133
- );
134
- }
135
- })();
136
-
137
- closePromise = promise;
138
-
139
- try {
140
- await promise;
141
- } finally {
142
- if (closePromise === promise) closePromise = undefined;
143
- }
144
- }
145
-
146
- return {
147
- [Symbol.asyncDispose]: close,
148
- close,
149
- hostname: poolUrl.hostname,
150
- port: poolUrl.port
151
- ? Number.parseInt(poolUrl.port, 10)
152
- : poolUrl.protocol === "https:"
153
- ? 443
154
- : 80,
155
- send,
156
- };
157
- }
package/src/agent.ts DELETED
@@ -1,408 +0,0 @@
1
- import { Deferred } from "@fuman/utils";
2
- import { toConnectError, toSendError } from "./_internal/error-mapping";
3
- import { raceSignal } from "./_internal/promises";
4
- import { bodyErrorMapperSymbol } from "./_internal/symbols";
5
- import {
6
- AgentBusyError,
7
- AgentClosedError,
8
- OriginMismatchError,
9
- RequestAbortedError,
10
- UnsupportedAlpnProtocolError,
11
- UnsupportedMethodError,
12
- UnsupportedProtocolError,
13
- } from "./errors";
14
- import { readResponse, writeRequest } from "./io/io";
15
- import type { Agent } from "./types/agent";
16
- import type { Dialer } from "./types/dialer";
17
-
18
- const PORT_MAP = {
19
- "http:": 80,
20
- "https:": 443,
21
- } as const;
22
-
23
- const DEFAULT_ALPN_PROTOCOLS = ["http/1.1"] as const;
24
-
25
- function resolvedDeferred(): Deferred<void> {
26
- const deferred = new Deferred<void>();
27
- deferred.resolve();
28
- return deferred;
29
- }
30
-
31
- function withSignal<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
32
- return signal ? raceSignal(promise, signal) : promise;
33
- }
34
-
35
- function isTlsConnection(
36
- conn: Dialer.ConnectionLike,
37
- ): conn is Dialer.ConnectionLike & { getAlpnProtocol(): string | null } {
38
- return (
39
- "getAlpnProtocol" in conn && typeof conn.getAlpnProtocol === "function"
40
- );
41
- }
42
-
43
- export function createAgent(
44
- dialer: Dialer,
45
- baseUrl: string,
46
- options: Agent.Options = {},
47
- ): Agent {
48
- const base = new URL(baseUrl);
49
-
50
- if (base.protocol !== "http:" && base.protocol !== "https:") {
51
- throw new UnsupportedProtocolError(base.protocol, {
52
- origin: base.origin,
53
- scheme: base.protocol,
54
- host: base.hostname,
55
- port: base.port ? Number.parseInt(base.port, 10) : undefined,
56
- url: base.toString(),
57
- });
58
- }
59
-
60
- const secure = base.protocol === "https:";
61
- const hostname = base.hostname;
62
- const port = base.port
63
- ? Number.parseInt(base.port, 10)
64
- : PORT_MAP[base.protocol];
65
-
66
- if (!Number.isFinite(port) || port <= 0 || port > 65535) {
67
- throw new TypeError(`Invalid port in base URL: ${baseUrl}`);
68
- }
69
-
70
- const target: Dialer.Target = {
71
- address: hostname,
72
- port,
73
- secure,
74
- sni: secure ? hostname : undefined,
75
- alpnProtocols: secure ? [...DEFAULT_ALPN_PROTOCOLS] : undefined,
76
- };
77
-
78
- const connectOptions = options.connect ?? {};
79
- const readerOptions = options.io?.reader ?? {};
80
- const writerOptions = options.io?.writer ?? {};
81
-
82
- let conn: Dialer.ConnectionLike | undefined;
83
- let connectPromise: Promise<Dialer.ConnectionLike> | undefined;
84
-
85
- let closed = false;
86
- let isBusy = false;
87
- let lastUsedTime = Date.now();
88
- let idleDeferred = resolvedDeferred();
89
-
90
- function createBaseErrorContext() {
91
- return {
92
- origin: base.origin,
93
- scheme: base.protocol,
94
- host: hostname,
95
- port,
96
- } as const;
97
- }
98
-
99
- function createRequestErrorContext(url: URL, method?: string) {
100
- return {
101
- ...createBaseErrorContext(),
102
- url: url.toString(),
103
- method,
104
- } as const;
105
- }
106
-
107
- function markIdle(): void {
108
- isBusy = false;
109
- lastUsedTime = Date.now();
110
- idleDeferred.resolve();
111
- }
112
-
113
- function disposeConn(): void {
114
- const current = conn;
115
- conn = undefined;
116
- if (!current) return;
117
- try {
118
- current.close();
119
- } catch {}
120
- }
121
-
122
- function forceClose(): void {
123
- if (closed) return;
124
- closed = true;
125
- disposeConn();
126
- if (!isBusy) markIdle();
127
- }
128
-
129
- function assertUsable(): void {
130
- if (closed) throw new AgentClosedError(createBaseErrorContext());
131
- }
132
-
133
- function assertSameOrigin(url: URL): void {
134
- if (url.origin !== base.origin) {
135
- throw new OriginMismatchError(base.origin, url.origin, {
136
- ...createBaseErrorContext(),
137
- url: url.toString(),
138
- });
139
- }
140
- }
141
-
142
- function configureConnection(nextConn: Dialer.ConnectionLike): void {
143
- nextConn.setNoDelay(connectOptions.noDelay ?? true);
144
- if (connectOptions.keepAlive !== null) {
145
- nextConn.setKeepAlive(connectOptions.keepAlive ?? true);
146
- }
147
- }
148
-
149
- async function connect(
150
- signal?: AbortSignal,
151
- ): Promise<Dialer.ConnectionLike> {
152
- assertUsable();
153
- if (conn) return conn;
154
- if (connectPromise) return withSignal(connectPromise, signal);
155
-
156
- let timedOut = false;
157
- let timeoutId: ReturnType<typeof setTimeout> | undefined;
158
- const abortController = new AbortController();
159
-
160
- const onAbort = () => abortController.abort(signal?.reason);
161
- const cleanup = () => {
162
- if (timeoutId !== undefined) {
163
- clearTimeout(timeoutId);
164
- timeoutId = undefined;
165
- }
166
- if (signal) signal.removeEventListener("abort", onAbort);
167
- };
168
-
169
- if (signal) {
170
- if (signal.aborted) abortController.abort(signal.reason);
171
- else signal.addEventListener("abort", onAbort, { once: true });
172
- }
173
-
174
- if (
175
- connectOptions.timeout != null &&
176
- Number.isFinite(connectOptions.timeout) &&
177
- connectOptions.timeout > 0
178
- ) {
179
- timeoutId = setTimeout(() => {
180
- timedOut = true;
181
- abortController.abort(
182
- new DOMException("Connection timed out", "TimeoutError"),
183
- );
184
- }, connectOptions.timeout);
185
- }
186
-
187
- connectPromise = (async () => {
188
- try {
189
- const nextConn = await dialer.dial(target, {
190
- signal: abortController.signal,
191
- });
192
-
193
- if (closed) {
194
- try {
195
- nextConn.close();
196
- } catch {}
197
- throw new AgentClosedError(createBaseErrorContext());
198
- }
199
-
200
- configureConnection(nextConn);
201
-
202
- if (secure && isTlsConnection(nextConn)) {
203
- const alpn = nextConn.getAlpnProtocol();
204
- if (alpn != null && alpn !== "" && alpn !== "http/1.1") {
205
- try {
206
- nextConn.close();
207
- } catch {}
208
- throw new UnsupportedAlpnProtocolError(
209
- alpn,
210
- createBaseErrorContext(),
211
- );
212
- }
213
- }
214
-
215
- conn = nextConn;
216
- return nextConn;
217
- } catch (error) {
218
- throw toConnectError(error, {
219
- signal,
220
- timedOut,
221
- context: createBaseErrorContext(),
222
- });
223
- } finally {
224
- cleanup();
225
- connectPromise = undefined;
226
- }
227
- })();
228
-
229
- return withSignal(connectPromise, signal);
230
- }
231
-
232
- async function executeRequest(
233
- sendOptions: Agent.SendOptions,
234
- mapBodyError?: (err: unknown) => unknown,
235
- ): Promise<Response> {
236
- assertUsable();
237
-
238
- const url =
239
- typeof sendOptions.url === "string"
240
- ? new URL(sendOptions.url)
241
- : sendOptions.url;
242
-
243
- const method = sendOptions.method.toUpperCase();
244
- const errorContext = createRequestErrorContext(url, method);
245
-
246
- if (sendOptions.signal?.aborted) {
247
- throw new RequestAbortedError(
248
- sendOptions.signal.reason,
249
- errorContext,
250
- );
251
- }
252
- if (isBusy) throw new AgentBusyError(errorContext);
253
- assertSameOrigin(url);
254
- if (method === "CONNECT")
255
- throw new UnsupportedMethodError("CONNECT", errorContext);
256
-
257
- isBusy = true;
258
- idleDeferred = new Deferred<void>();
259
-
260
- let finalized = false;
261
- let activeConn: Dialer.ConnectionLike | undefined;
262
-
263
- const finalize = (reusable: boolean) => {
264
- if (finalized) return;
265
- finalized = true;
266
- if (!reusable || closed) {
267
- if (conn === activeConn) disposeConn();
268
- else if (activeConn) {
269
- try {
270
- activeConn.close();
271
- } catch {}
272
- }
273
- }
274
- markIdle();
275
- };
276
-
277
- const abortListener = () => {
278
- if (activeConn) {
279
- if (conn === activeConn) conn = undefined;
280
- try {
281
- activeConn.close();
282
- } catch {}
283
- }
284
- };
285
-
286
- try {
287
- activeConn = await connect(sendOptions.signal);
288
-
289
- sendOptions.signal?.addEventListener("abort", abortListener, {
290
- once: true,
291
- });
292
-
293
- try {
294
- await withSignal(
295
- writeRequest(
296
- activeConn,
297
- {
298
- url,
299
- method,
300
- headers: sendOptions.headers,
301
- body: sendOptions.body ?? null,
302
- signal: sendOptions.signal,
303
- },
304
- writerOptions,
305
- ),
306
- sendOptions.signal,
307
- );
308
- } catch (error) {
309
- throw toSendError(error, {
310
- signal: sendOptions.signal,
311
- context: errorContext,
312
- phase: "request",
313
- });
314
- }
315
-
316
- const isHeadRequest = method === "HEAD";
317
- const shouldIgnoreBody = (status: number) =>
318
- isHeadRequest ||
319
- (status >= 100 && status < 200) ||
320
- status === 204 ||
321
- status === 304;
322
-
323
- let response: Response;
324
-
325
- try {
326
- response = await withSignal(
327
- readResponse(
328
- activeConn,
329
- readerOptions,
330
- shouldIgnoreBody,
331
- (reusable) => {
332
- sendOptions.signal?.removeEventListener(
333
- "abort",
334
- abortListener,
335
- );
336
- finalize(reusable);
337
- },
338
- mapBodyError,
339
- ),
340
- sendOptions.signal,
341
- );
342
- } catch (error) {
343
- throw toSendError(error, {
344
- signal: sendOptions.signal,
345
- context: errorContext,
346
- phase: "response",
347
- });
348
- }
349
-
350
- return response;
351
- } catch (error) {
352
- sendOptions.signal?.removeEventListener("abort", abortListener);
353
-
354
- if (activeConn) {
355
- if (conn === activeConn) conn = undefined;
356
- try {
357
- activeConn.close();
358
- } catch {}
359
- }
360
-
361
- finalize(false);
362
- throw error;
363
- }
364
- }
365
-
366
- async function send(sendOptions: Agent.SendOptions): Promise<Response> {
367
- const url =
368
- typeof sendOptions.url === "string"
369
- ? new URL(sendOptions.url)
370
- : sendOptions.url;
371
- const errorContext = createRequestErrorContext(
372
- url,
373
- sendOptions.method.toUpperCase(),
374
- );
375
-
376
- const mapBodyError =
377
- (
378
- sendOptions as {
379
- [bodyErrorMapperSymbol]?: (err: unknown) => unknown;
380
- }
381
- )[bodyErrorMapperSymbol] ??
382
- ((error: unknown) =>
383
- toSendError(error, {
384
- signal: sendOptions.signal,
385
- context: errorContext,
386
- phase: "body",
387
- }));
388
-
389
- return executeRequest(sendOptions, mapBodyError);
390
- }
391
-
392
- return {
393
- [Symbol.dispose]: forceClose,
394
- close: forceClose,
395
- hostname,
396
- port,
397
- send,
398
- whenIdle(): Promise<void> {
399
- return idleDeferred.promise;
400
- },
401
- get isIdle(): boolean {
402
- return !isBusy;
403
- },
404
- get lastUsed(): number {
405
- return lastUsedTime;
406
- },
407
- };
408
- }