@npy/fetch 0.1.2 → 0.1.3
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/README.md +143 -50
- package/bun.lock +68 -0
- package/examples/custom-proxy-client.ts +32 -0
- package/examples/http-client.ts +47 -0
- package/examples/proxy.ts +16 -0
- package/examples/simple.ts +15 -0
- package/package.json +25 -30
- package/src/_internal/consts.ts +3 -0
- package/{_internal/decode-stream-error.d.cts → src/_internal/decode-stream-error.ts} +7 -2
- package/src/_internal/error-mapping.ts +160 -0
- package/src/_internal/guards.ts +78 -0
- package/src/_internal/net.ts +173 -0
- package/src/_internal/promises.ts +22 -0
- package/src/_internal/streams.ts +52 -0
- package/src/_internal/symbols.ts +1 -0
- package/src/agent-pool.ts +157 -0
- package/src/agent.ts +408 -0
- package/src/body.ts +179 -0
- package/src/dialers/index.ts +3 -0
- package/src/dialers/proxy.ts +102 -0
- package/src/dialers/tcp.ts +162 -0
- package/src/encoding.ts +222 -0
- package/src/errors.ts +357 -0
- package/src/fetch.ts +626 -0
- package/src/http-client.ts +111 -0
- package/src/index.ts +14 -0
- package/src/io/_utils.ts +82 -0
- package/src/io/buf-writer.ts +183 -0
- package/src/io/io.ts +322 -0
- package/src/io/readers.ts +576 -0
- package/src/io/writers.ts +331 -0
- package/{types/agent.d.cts → src/types/agent.ts} +47 -21
- package/{types/dialer.d.cts → src/types/dialer.ts} +19 -9
- package/src/types/index.ts +2 -0
- package/tests/agent-pool.test.ts +111 -0
- package/tests/agent.test.ts +134 -0
- package/tests/body.test.ts +228 -0
- package/tests/errors.test.ts +152 -0
- package/tests/fetch.test.ts +421 -0
- package/tests/io-options.test.ts +127 -0
- package/tests/multipart.test.ts +348 -0
- package/tests/test-utils.ts +335 -0
- package/tsconfig.json +15 -0
- package/LICENSE +0 -21
- package/_internal/consts.cjs +0 -4
- package/_internal/consts.d.cts +0 -3
- package/_internal/consts.d.ts +0 -3
- package/_internal/consts.js +0 -4
- package/_internal/decode-stream-error.cjs +0 -18
- package/_internal/decode-stream-error.d.ts +0 -11
- package/_internal/decode-stream-error.js +0 -18
- package/_internal/error-mapping.cjs +0 -44
- package/_internal/error-mapping.d.cts +0 -15
- package/_internal/error-mapping.d.ts +0 -15
- package/_internal/error-mapping.js +0 -41
- package/_internal/guards.cjs +0 -23
- package/_internal/guards.d.cts +0 -15
- package/_internal/guards.d.ts +0 -15
- package/_internal/guards.js +0 -15
- package/_internal/net.cjs +0 -95
- package/_internal/net.d.cts +0 -11
- package/_internal/net.d.ts +0 -11
- package/_internal/net.js +0 -92
- package/_internal/promises.cjs +0 -18
- package/_internal/promises.d.cts +0 -1
- package/_internal/promises.d.ts +0 -1
- package/_internal/promises.js +0 -18
- package/_internal/streams.cjs +0 -37
- package/_internal/streams.d.cts +0 -21
- package/_internal/streams.d.ts +0 -21
- package/_internal/streams.js +0 -36
- package/_internal/symbols.cjs +0 -4
- package/_internal/symbols.d.cts +0 -1
- package/_internal/symbols.d.ts +0 -1
- package/_internal/symbols.js +0 -4
- package/_virtual/_rolldown/runtime.cjs +0 -23
- package/agent-pool.cjs +0 -96
- package/agent-pool.d.cts +0 -2
- package/agent-pool.d.ts +0 -2
- package/agent-pool.js +0 -95
- package/agent.cjs +0 -260
- package/agent.d.cts +0 -3
- package/agent.d.ts +0 -3
- package/agent.js +0 -259
- package/body.cjs +0 -105
- package/body.d.cts +0 -12
- package/body.d.ts +0 -12
- package/body.js +0 -102
- package/dialers/index.d.cts +0 -3
- package/dialers/index.d.ts +0 -3
- package/dialers/proxy.cjs +0 -56
- package/dialers/proxy.d.cts +0 -27
- package/dialers/proxy.d.ts +0 -27
- package/dialers/proxy.js +0 -55
- package/dialers/tcp.cjs +0 -92
- package/dialers/tcp.d.cts +0 -57
- package/dialers/tcp.d.ts +0 -57
- package/dialers/tcp.js +0 -89
- package/encoding.cjs +0 -114
- package/encoding.d.cts +0 -35
- package/encoding.d.ts +0 -35
- package/encoding.js +0 -110
- package/errors.cjs +0 -275
- package/errors.d.cts +0 -110
- package/errors.d.ts +0 -110
- package/errors.js +0 -259
- package/fetch.cjs +0 -353
- package/fetch.d.cts +0 -58
- package/fetch.d.ts +0 -58
- package/fetch.js +0 -350
- package/http-client.cjs +0 -75
- package/http-client.d.cts +0 -39
- package/http-client.d.ts +0 -39
- package/http-client.js +0 -75
- package/index.cjs +0 -49
- package/index.d.cts +0 -14
- package/index.d.ts +0 -14
- package/index.js +0 -11
- package/io/_utils.cjs +0 -56
- package/io/_utils.d.cts +0 -10
- package/io/_utils.d.ts +0 -10
- package/io/_utils.js +0 -51
- package/io/buf-writer.cjs +0 -149
- package/io/buf-writer.d.cts +0 -13
- package/io/buf-writer.d.ts +0 -13
- package/io/buf-writer.js +0 -148
- package/io/io.cjs +0 -199
- package/io/io.d.cts +0 -5
- package/io/io.d.ts +0 -5
- package/io/io.js +0 -198
- package/io/readers.cjs +0 -337
- package/io/readers.d.cts +0 -69
- package/io/readers.d.ts +0 -69
- package/io/readers.js +0 -333
- package/io/writers.cjs +0 -196
- package/io/writers.d.cts +0 -22
- package/io/writers.d.ts +0 -22
- package/io/writers.js +0 -195
- package/types/agent.d.ts +0 -72
- package/types/dialer.d.ts +0 -30
- package/types/index.d.cts +0 -2
- package/types/index.d.ts +0 -2
package/agent.js
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import { AgentBusyError, AgentClosedError, OriginMismatchError, RequestAbortedError, UnsupportedAlpnProtocolError, UnsupportedMethodError, UnsupportedProtocolError } from "./errors.js";
|
|
2
|
-
import { toConnectError, toSendError } from "./_internal/error-mapping.js";
|
|
3
|
-
import { raceSignal } from "./_internal/promises.js";
|
|
4
|
-
import { bodyErrorMapperSymbol } from "./_internal/symbols.js";
|
|
5
|
-
import { readResponse, writeRequest } from "./io/io.js";
|
|
6
|
-
import { Deferred } from "@fuman/utils";
|
|
7
|
-
//#region src/agent.ts
|
|
8
|
-
var PORT_MAP = {
|
|
9
|
-
"http:": 80,
|
|
10
|
-
"https:": 443
|
|
11
|
-
};
|
|
12
|
-
var DEFAULT_ALPN_PROTOCOLS = ["http/1.1"];
|
|
13
|
-
function resolvedDeferred() {
|
|
14
|
-
const deferred = new Deferred();
|
|
15
|
-
deferred.resolve();
|
|
16
|
-
return deferred;
|
|
17
|
-
}
|
|
18
|
-
function withSignal(promise, signal) {
|
|
19
|
-
return signal ? raceSignal(promise, signal) : promise;
|
|
20
|
-
}
|
|
21
|
-
function isTlsConnection(conn) {
|
|
22
|
-
return "getAlpnProtocol" in conn && typeof conn.getAlpnProtocol === "function";
|
|
23
|
-
}
|
|
24
|
-
function createAgent(dialer, baseUrl, options = {}) {
|
|
25
|
-
const base = new URL(baseUrl);
|
|
26
|
-
if (base.protocol !== "http:" && base.protocol !== "https:") throw new UnsupportedProtocolError(base.protocol, {
|
|
27
|
-
origin: base.origin,
|
|
28
|
-
scheme: base.protocol,
|
|
29
|
-
host: base.hostname,
|
|
30
|
-
port: base.port ? Number.parseInt(base.port, 10) : void 0,
|
|
31
|
-
url: base.toString()
|
|
32
|
-
});
|
|
33
|
-
const secure = base.protocol === "https:";
|
|
34
|
-
const hostname = base.hostname;
|
|
35
|
-
const port = base.port ? Number.parseInt(base.port, 10) : PORT_MAP[base.protocol];
|
|
36
|
-
if (!Number.isFinite(port) || port <= 0 || port > 65535) throw new TypeError(`Invalid port in base URL: ${baseUrl}`);
|
|
37
|
-
const target = {
|
|
38
|
-
address: hostname,
|
|
39
|
-
port,
|
|
40
|
-
secure,
|
|
41
|
-
sni: secure ? hostname : void 0,
|
|
42
|
-
alpnProtocols: secure ? [...DEFAULT_ALPN_PROTOCOLS] : void 0
|
|
43
|
-
};
|
|
44
|
-
const connectOptions = options.connect ?? {};
|
|
45
|
-
const readerOptions = options.io?.reader ?? {};
|
|
46
|
-
const writerOptions = options.io?.writer ?? {};
|
|
47
|
-
let conn;
|
|
48
|
-
let connectPromise;
|
|
49
|
-
let closed = false;
|
|
50
|
-
let isBusy = false;
|
|
51
|
-
let lastUsedTime = Date.now();
|
|
52
|
-
let idleDeferred = resolvedDeferred();
|
|
53
|
-
function createBaseErrorContext() {
|
|
54
|
-
return {
|
|
55
|
-
origin: base.origin,
|
|
56
|
-
scheme: base.protocol,
|
|
57
|
-
host: hostname,
|
|
58
|
-
port
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function createRequestErrorContext(url, method) {
|
|
62
|
-
return {
|
|
63
|
-
...createBaseErrorContext(),
|
|
64
|
-
url: url.toString(),
|
|
65
|
-
method
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function markIdle() {
|
|
69
|
-
isBusy = false;
|
|
70
|
-
lastUsedTime = Date.now();
|
|
71
|
-
idleDeferred.resolve();
|
|
72
|
-
}
|
|
73
|
-
function disposeConn() {
|
|
74
|
-
const current = conn;
|
|
75
|
-
conn = void 0;
|
|
76
|
-
if (!current) return;
|
|
77
|
-
try {
|
|
78
|
-
current.close();
|
|
79
|
-
} catch {}
|
|
80
|
-
}
|
|
81
|
-
function forceClose() {
|
|
82
|
-
if (closed) return;
|
|
83
|
-
closed = true;
|
|
84
|
-
disposeConn();
|
|
85
|
-
if (!isBusy) markIdle();
|
|
86
|
-
}
|
|
87
|
-
function assertUsable() {
|
|
88
|
-
if (closed) throw new AgentClosedError(createBaseErrorContext());
|
|
89
|
-
}
|
|
90
|
-
function assertSameOrigin(url) {
|
|
91
|
-
if (url.origin !== base.origin) throw new OriginMismatchError(base.origin, url.origin, {
|
|
92
|
-
...createBaseErrorContext(),
|
|
93
|
-
url: url.toString()
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
function configureConnection(nextConn) {
|
|
97
|
-
nextConn.setNoDelay(connectOptions.noDelay ?? true);
|
|
98
|
-
if (connectOptions.keepAlive !== null) nextConn.setKeepAlive(connectOptions.keepAlive ?? true);
|
|
99
|
-
}
|
|
100
|
-
async function connect(signal) {
|
|
101
|
-
assertUsable();
|
|
102
|
-
if (conn) return conn;
|
|
103
|
-
if (connectPromise) return withSignal(connectPromise, signal);
|
|
104
|
-
let timedOut = false;
|
|
105
|
-
let timeoutId;
|
|
106
|
-
const abortController = new AbortController();
|
|
107
|
-
const onAbort = () => abortController.abort(signal?.reason);
|
|
108
|
-
const cleanup = () => {
|
|
109
|
-
if (timeoutId !== void 0) {
|
|
110
|
-
clearTimeout(timeoutId);
|
|
111
|
-
timeoutId = void 0;
|
|
112
|
-
}
|
|
113
|
-
if (signal) signal.removeEventListener("abort", onAbort);
|
|
114
|
-
};
|
|
115
|
-
if (signal) if (signal.aborted) abortController.abort(signal.reason);
|
|
116
|
-
else signal.addEventListener("abort", onAbort, { once: true });
|
|
117
|
-
if (connectOptions.timeout != null && Number.isFinite(connectOptions.timeout) && connectOptions.timeout > 0) timeoutId = setTimeout(() => {
|
|
118
|
-
timedOut = true;
|
|
119
|
-
abortController.abort(new DOMException("Connection timed out", "TimeoutError"));
|
|
120
|
-
}, connectOptions.timeout);
|
|
121
|
-
connectPromise = (async () => {
|
|
122
|
-
try {
|
|
123
|
-
const nextConn = await dialer.dial(target, { signal: abortController.signal });
|
|
124
|
-
if (closed) {
|
|
125
|
-
try {
|
|
126
|
-
nextConn.close();
|
|
127
|
-
} catch {}
|
|
128
|
-
throw new AgentClosedError(createBaseErrorContext());
|
|
129
|
-
}
|
|
130
|
-
configureConnection(nextConn);
|
|
131
|
-
if (secure && isTlsConnection(nextConn)) {
|
|
132
|
-
const alpn = nextConn.getAlpnProtocol();
|
|
133
|
-
if (alpn != null && alpn !== "" && alpn !== "http/1.1") {
|
|
134
|
-
try {
|
|
135
|
-
nextConn.close();
|
|
136
|
-
} catch {}
|
|
137
|
-
throw new UnsupportedAlpnProtocolError(alpn, createBaseErrorContext());
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
conn = nextConn;
|
|
141
|
-
return nextConn;
|
|
142
|
-
} catch (error) {
|
|
143
|
-
throw toConnectError(error, {
|
|
144
|
-
signal,
|
|
145
|
-
timedOut,
|
|
146
|
-
context: createBaseErrorContext()
|
|
147
|
-
});
|
|
148
|
-
} finally {
|
|
149
|
-
cleanup();
|
|
150
|
-
connectPromise = void 0;
|
|
151
|
-
}
|
|
152
|
-
})();
|
|
153
|
-
return withSignal(connectPromise, signal);
|
|
154
|
-
}
|
|
155
|
-
async function executeRequest(sendOptions, mapBodyError) {
|
|
156
|
-
assertUsable();
|
|
157
|
-
const url = typeof sendOptions.url === "string" ? new URL(sendOptions.url) : sendOptions.url;
|
|
158
|
-
const method = sendOptions.method.toUpperCase();
|
|
159
|
-
const errorContext = createRequestErrorContext(url, method);
|
|
160
|
-
if (sendOptions.signal?.aborted) throw new RequestAbortedError(sendOptions.signal.reason, errorContext);
|
|
161
|
-
if (isBusy) throw new AgentBusyError(errorContext);
|
|
162
|
-
assertSameOrigin(url);
|
|
163
|
-
if (method === "CONNECT") throw new UnsupportedMethodError("CONNECT", errorContext);
|
|
164
|
-
isBusy = true;
|
|
165
|
-
idleDeferred = new Deferred();
|
|
166
|
-
let finalized = false;
|
|
167
|
-
let activeConn;
|
|
168
|
-
const finalize = (reusable) => {
|
|
169
|
-
if (finalized) return;
|
|
170
|
-
finalized = true;
|
|
171
|
-
if (!reusable || closed) {
|
|
172
|
-
if (conn === activeConn) disposeConn();
|
|
173
|
-
else if (activeConn) try {
|
|
174
|
-
activeConn.close();
|
|
175
|
-
} catch {}
|
|
176
|
-
}
|
|
177
|
-
markIdle();
|
|
178
|
-
};
|
|
179
|
-
const abortListener = () => {
|
|
180
|
-
if (activeConn) {
|
|
181
|
-
if (conn === activeConn) conn = void 0;
|
|
182
|
-
try {
|
|
183
|
-
activeConn.close();
|
|
184
|
-
} catch {}
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
try {
|
|
188
|
-
activeConn = await connect(sendOptions.signal);
|
|
189
|
-
sendOptions.signal?.addEventListener("abort", abortListener, { once: true });
|
|
190
|
-
try {
|
|
191
|
-
await withSignal(writeRequest(activeConn, {
|
|
192
|
-
url,
|
|
193
|
-
method,
|
|
194
|
-
headers: sendOptions.headers,
|
|
195
|
-
body: sendOptions.body ?? null,
|
|
196
|
-
signal: sendOptions.signal
|
|
197
|
-
}, writerOptions), sendOptions.signal);
|
|
198
|
-
} catch (error) {
|
|
199
|
-
throw toSendError(error, {
|
|
200
|
-
signal: sendOptions.signal,
|
|
201
|
-
context: errorContext,
|
|
202
|
-
phase: "request"
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
const isHeadRequest = method === "HEAD";
|
|
206
|
-
const shouldIgnoreBody = (status) => isHeadRequest || status >= 100 && status < 200 || status === 204 || status === 304;
|
|
207
|
-
let response;
|
|
208
|
-
try {
|
|
209
|
-
response = await withSignal(readResponse(activeConn, readerOptions, shouldIgnoreBody, (reusable) => {
|
|
210
|
-
sendOptions.signal?.removeEventListener("abort", abortListener);
|
|
211
|
-
finalize(reusable);
|
|
212
|
-
}, mapBodyError), sendOptions.signal);
|
|
213
|
-
} catch (error) {
|
|
214
|
-
throw toSendError(error, {
|
|
215
|
-
signal: sendOptions.signal,
|
|
216
|
-
context: errorContext,
|
|
217
|
-
phase: "response"
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
return response;
|
|
221
|
-
} catch (error) {
|
|
222
|
-
sendOptions.signal?.removeEventListener("abort", abortListener);
|
|
223
|
-
if (activeConn) {
|
|
224
|
-
if (conn === activeConn) conn = void 0;
|
|
225
|
-
try {
|
|
226
|
-
activeConn.close();
|
|
227
|
-
} catch {}
|
|
228
|
-
}
|
|
229
|
-
finalize(false);
|
|
230
|
-
throw error;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
async function send(sendOptions) {
|
|
234
|
-
const errorContext = createRequestErrorContext(typeof sendOptions.url === "string" ? new URL(sendOptions.url) : sendOptions.url, sendOptions.method.toUpperCase());
|
|
235
|
-
return executeRequest(sendOptions, sendOptions[bodyErrorMapperSymbol] ?? ((error) => toSendError(error, {
|
|
236
|
-
signal: sendOptions.signal,
|
|
237
|
-
context: errorContext,
|
|
238
|
-
phase: "body"
|
|
239
|
-
})));
|
|
240
|
-
}
|
|
241
|
-
return {
|
|
242
|
-
[Symbol.dispose]: forceClose,
|
|
243
|
-
close: forceClose,
|
|
244
|
-
hostname,
|
|
245
|
-
port,
|
|
246
|
-
send,
|
|
247
|
-
whenIdle() {
|
|
248
|
-
return idleDeferred.promise;
|
|
249
|
-
},
|
|
250
|
-
get isIdle() {
|
|
251
|
-
return !isBusy;
|
|
252
|
-
},
|
|
253
|
-
get lastUsed() {
|
|
254
|
-
return lastUsedTime;
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
//#endregion
|
|
259
|
-
export { createAgent };
|
package/body.cjs
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
-
const require_guards = require("./_internal/guards.cjs");
|
|
3
|
-
let _fuman_utils = require("@fuman/utils");
|
|
4
|
-
let node_stream = require("node:stream");
|
|
5
|
-
let _fuman_io = require("@fuman/io");
|
|
6
|
-
let node_crypto = require("node:crypto");
|
|
7
|
-
let node_util_types = require("node:util/types");
|
|
8
|
-
//#region src/body.ts
|
|
9
|
-
var BOUNDARY = "-".repeat(2);
|
|
10
|
-
var makeFormBoundary = () => `formdata-${(0, node_crypto.randomBytes)(8).toString("hex")}`;
|
|
11
|
-
var getFormHeader = (boundary, name, field) => {
|
|
12
|
-
let header = `${BOUNDARY}${boundary}\r
|
|
13
|
-
`;
|
|
14
|
-
header += `Content-Disposition: form-data; name="${name}"`;
|
|
15
|
-
if (require_guards.isBlob(field)) {
|
|
16
|
-
header += `; filename="${field.name ?? "blob"}"\r
|
|
17
|
-
`;
|
|
18
|
-
header += `Content-Type: ${field.type || "application/octet-stream"}`;
|
|
19
|
-
}
|
|
20
|
-
return `${header}\r
|
|
21
|
-
\r
|
|
22
|
-
`;
|
|
23
|
-
};
|
|
24
|
-
var getFormFooter = (boundary) => `${BOUNDARY}${boundary}${BOUNDARY}\r
|
|
25
|
-
\r
|
|
26
|
-
`;
|
|
27
|
-
var getFormDataLength = (form, boundary) => {
|
|
28
|
-
let length = Buffer.byteLength(getFormFooter(boundary));
|
|
29
|
-
for (const [name, value] of form) length += Buffer.byteLength(getFormHeader(boundary, name, value)) + (require_guards.isBlob(value) ? value.size : Buffer.byteLength(`${value}`)) + 2;
|
|
30
|
-
return length;
|
|
31
|
-
};
|
|
32
|
-
async function* generatorOfFormData(form, boundary) {
|
|
33
|
-
for (const [name, value] of form) if (require_guards.isBlob(value)) {
|
|
34
|
-
yield _fuman_utils.utf8.encoder.encode(getFormHeader(boundary, name, value));
|
|
35
|
-
for await (const chunk of value.stream()) yield chunk;
|
|
36
|
-
yield _fuman_utils.utf8.encoder.encode("\r\n");
|
|
37
|
-
} else yield _fuman_utils.utf8.encoder.encode(getFormHeader(boundary, name, value) + value + "\r\n");
|
|
38
|
-
yield _fuman_utils.utf8.encoder.encode(getFormFooter(boundary));
|
|
39
|
-
}
|
|
40
|
-
var extractBody = (object) => {
|
|
41
|
-
let type = null;
|
|
42
|
-
let body;
|
|
43
|
-
let size = null;
|
|
44
|
-
if (object == null) {
|
|
45
|
-
body = null;
|
|
46
|
-
size = 0;
|
|
47
|
-
} else if (typeof object === "string") {
|
|
48
|
-
const bytes = _fuman_utils.utf8.encoder.encode(`${object}`);
|
|
49
|
-
type = "text/plain;charset=UTF-8";
|
|
50
|
-
size = bytes.byteLength;
|
|
51
|
-
body = bytes;
|
|
52
|
-
} else if (require_guards.isURLSearchParameters(object)) {
|
|
53
|
-
const bytes = _fuman_utils.utf8.encoder.encode(object.toString());
|
|
54
|
-
body = bytes;
|
|
55
|
-
size = bytes.byteLength;
|
|
56
|
-
type = "application/x-www-form-urlencoded;charset=UTF-8";
|
|
57
|
-
} else if (require_guards.isBlob(object)) {
|
|
58
|
-
size = object.size;
|
|
59
|
-
type = object.type || null;
|
|
60
|
-
body = object.stream();
|
|
61
|
-
} else if (object instanceof Uint8Array) {
|
|
62
|
-
body = object;
|
|
63
|
-
size = object.byteLength;
|
|
64
|
-
} else if ((0, node_util_types.isAnyArrayBuffer)(object)) {
|
|
65
|
-
const bytes = new Uint8Array(object);
|
|
66
|
-
body = bytes;
|
|
67
|
-
size = bytes.byteLength;
|
|
68
|
-
} else if (ArrayBuffer.isView(object)) {
|
|
69
|
-
const bytes = new Uint8Array(object.buffer, object.byteOffset, object.byteLength);
|
|
70
|
-
body = bytes;
|
|
71
|
-
size = bytes.byteLength;
|
|
72
|
-
} else if (require_guards.isReadableStream(object)) body = object;
|
|
73
|
-
else if (require_guards.isFumanReadable(object)) body = object;
|
|
74
|
-
else if (require_guards.isFormData(object)) {
|
|
75
|
-
const boundary = makeFormBoundary();
|
|
76
|
-
type = `multipart/form-data; boundary=${boundary}`;
|
|
77
|
-
size = getFormDataLength(object, boundary);
|
|
78
|
-
body = node_stream.Readable.from(generatorOfFormData(object, boundary));
|
|
79
|
-
} else if (require_guards.isMultipartFormDataStream(object)) {
|
|
80
|
-
type = `multipart/form-data; boundary=${object.getBoundary()}`;
|
|
81
|
-
size = object.hasKnownLength() ? object.getLengthSync() : null;
|
|
82
|
-
body = object;
|
|
83
|
-
} else if (require_guards.isReadable(object)) body = object;
|
|
84
|
-
else if (require_guards.isIterable(object)) body = node_stream.Readable.from(object);
|
|
85
|
-
else {
|
|
86
|
-
const bytes = _fuman_utils.utf8.encoder.encode(`${object}`);
|
|
87
|
-
type = "text/plain;charset=UTF-8";
|
|
88
|
-
body = bytes;
|
|
89
|
-
size = bytes.byteLength;
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
contentLength: size,
|
|
93
|
-
contentType: type,
|
|
94
|
-
body
|
|
95
|
-
};
|
|
96
|
-
};
|
|
97
|
-
function fromRequestBody(request) {
|
|
98
|
-
if (request.bodyUsed) throw new TypeError("Request body has already been used");
|
|
99
|
-
if (request.body == null) return null;
|
|
100
|
-
return (0, _fuman_io.webReadableToFuman)(request.body);
|
|
101
|
-
}
|
|
102
|
-
//#endregion
|
|
103
|
-
exports.extractBody = extractBody;
|
|
104
|
-
exports.fromRequestBody = fromRequestBody;
|
|
105
|
-
exports.getFormDataLength = getFormDataLength;
|
package/body.d.cts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Readable } from 'node:stream';
|
|
2
|
-
import { IClosable, IReadable } from '@fuman/io';
|
|
3
|
-
import { FormDataPolyfill } from './_internal/guards';
|
|
4
|
-
export type BodyInit = Exclude<RequestInit["body"], undefined | null> | FormDataPolyfill | Readable | (IReadable & IClosable);
|
|
5
|
-
export interface BodyState {
|
|
6
|
-
contentLength: number | null;
|
|
7
|
-
contentType: string | null;
|
|
8
|
-
body: Readable | ReadableStream | Uint8Array | (IReadable & IClosable) | null;
|
|
9
|
-
}
|
|
10
|
-
export declare const getFormDataLength: (form: FormData, boundary: string) => number;
|
|
11
|
-
export declare const extractBody: (object: BodyInit | null) => BodyState;
|
|
12
|
-
export declare function fromRequestBody(request: Request): BodyInit | null;
|
package/body.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Readable } from 'node:stream';
|
|
2
|
-
import { IClosable, IReadable } from '@fuman/io';
|
|
3
|
-
import { FormDataPolyfill } from './_internal/guards';
|
|
4
|
-
export type BodyInit = Exclude<RequestInit["body"], undefined | null> | FormDataPolyfill | Readable | (IReadable & IClosable);
|
|
5
|
-
export interface BodyState {
|
|
6
|
-
contentLength: number | null;
|
|
7
|
-
contentType: string | null;
|
|
8
|
-
body: Readable | ReadableStream | Uint8Array | (IReadable & IClosable) | null;
|
|
9
|
-
}
|
|
10
|
-
export declare const getFormDataLength: (form: FormData, boundary: string) => number;
|
|
11
|
-
export declare const extractBody: (object: BodyInit | null) => BodyState;
|
|
12
|
-
export declare function fromRequestBody(request: Request): BodyInit | null;
|
package/body.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { isBlob, isFormData, isFumanReadable, isIterable, isMultipartFormDataStream, isReadable, isReadableStream, isURLSearchParameters } from "./_internal/guards.js";
|
|
2
|
-
import { utf8 } from "@fuman/utils";
|
|
3
|
-
import { Readable } from "node:stream";
|
|
4
|
-
import { webReadableToFuman } from "@fuman/io";
|
|
5
|
-
import { randomBytes } from "node:crypto";
|
|
6
|
-
import { isAnyArrayBuffer } from "node:util/types";
|
|
7
|
-
//#region src/body.ts
|
|
8
|
-
var BOUNDARY = "-".repeat(2);
|
|
9
|
-
var makeFormBoundary = () => `formdata-${randomBytes(8).toString("hex")}`;
|
|
10
|
-
var getFormHeader = (boundary, name, field) => {
|
|
11
|
-
let header = `${BOUNDARY}${boundary}\r
|
|
12
|
-
`;
|
|
13
|
-
header += `Content-Disposition: form-data; name="${name}"`;
|
|
14
|
-
if (isBlob(field)) {
|
|
15
|
-
header += `; filename="${field.name ?? "blob"}"\r
|
|
16
|
-
`;
|
|
17
|
-
header += `Content-Type: ${field.type || "application/octet-stream"}`;
|
|
18
|
-
}
|
|
19
|
-
return `${header}\r
|
|
20
|
-
\r
|
|
21
|
-
`;
|
|
22
|
-
};
|
|
23
|
-
var getFormFooter = (boundary) => `${BOUNDARY}${boundary}${BOUNDARY}\r
|
|
24
|
-
\r
|
|
25
|
-
`;
|
|
26
|
-
var getFormDataLength = (form, boundary) => {
|
|
27
|
-
let length = Buffer.byteLength(getFormFooter(boundary));
|
|
28
|
-
for (const [name, value] of form) length += Buffer.byteLength(getFormHeader(boundary, name, value)) + (isBlob(value) ? value.size : Buffer.byteLength(`${value}`)) + 2;
|
|
29
|
-
return length;
|
|
30
|
-
};
|
|
31
|
-
async function* generatorOfFormData(form, boundary) {
|
|
32
|
-
for (const [name, value] of form) if (isBlob(value)) {
|
|
33
|
-
yield utf8.encoder.encode(getFormHeader(boundary, name, value));
|
|
34
|
-
for await (const chunk of value.stream()) yield chunk;
|
|
35
|
-
yield utf8.encoder.encode("\r\n");
|
|
36
|
-
} else yield utf8.encoder.encode(getFormHeader(boundary, name, value) + value + "\r\n");
|
|
37
|
-
yield utf8.encoder.encode(getFormFooter(boundary));
|
|
38
|
-
}
|
|
39
|
-
var extractBody = (object) => {
|
|
40
|
-
let type = null;
|
|
41
|
-
let body;
|
|
42
|
-
let size = null;
|
|
43
|
-
if (object == null) {
|
|
44
|
-
body = null;
|
|
45
|
-
size = 0;
|
|
46
|
-
} else if (typeof object === "string") {
|
|
47
|
-
const bytes = utf8.encoder.encode(`${object}`);
|
|
48
|
-
type = "text/plain;charset=UTF-8";
|
|
49
|
-
size = bytes.byteLength;
|
|
50
|
-
body = bytes;
|
|
51
|
-
} else if (isURLSearchParameters(object)) {
|
|
52
|
-
const bytes = utf8.encoder.encode(object.toString());
|
|
53
|
-
body = bytes;
|
|
54
|
-
size = bytes.byteLength;
|
|
55
|
-
type = "application/x-www-form-urlencoded;charset=UTF-8";
|
|
56
|
-
} else if (isBlob(object)) {
|
|
57
|
-
size = object.size;
|
|
58
|
-
type = object.type || null;
|
|
59
|
-
body = object.stream();
|
|
60
|
-
} else if (object instanceof Uint8Array) {
|
|
61
|
-
body = object;
|
|
62
|
-
size = object.byteLength;
|
|
63
|
-
} else if (isAnyArrayBuffer(object)) {
|
|
64
|
-
const bytes = new Uint8Array(object);
|
|
65
|
-
body = bytes;
|
|
66
|
-
size = bytes.byteLength;
|
|
67
|
-
} else if (ArrayBuffer.isView(object)) {
|
|
68
|
-
const bytes = new Uint8Array(object.buffer, object.byteOffset, object.byteLength);
|
|
69
|
-
body = bytes;
|
|
70
|
-
size = bytes.byteLength;
|
|
71
|
-
} else if (isReadableStream(object)) body = object;
|
|
72
|
-
else if (isFumanReadable(object)) body = object;
|
|
73
|
-
else if (isFormData(object)) {
|
|
74
|
-
const boundary = makeFormBoundary();
|
|
75
|
-
type = `multipart/form-data; boundary=${boundary}`;
|
|
76
|
-
size = getFormDataLength(object, boundary);
|
|
77
|
-
body = Readable.from(generatorOfFormData(object, boundary));
|
|
78
|
-
} else if (isMultipartFormDataStream(object)) {
|
|
79
|
-
type = `multipart/form-data; boundary=${object.getBoundary()}`;
|
|
80
|
-
size = object.hasKnownLength() ? object.getLengthSync() : null;
|
|
81
|
-
body = object;
|
|
82
|
-
} else if (isReadable(object)) body = object;
|
|
83
|
-
else if (isIterable(object)) body = Readable.from(object);
|
|
84
|
-
else {
|
|
85
|
-
const bytes = utf8.encoder.encode(`${object}`);
|
|
86
|
-
type = "text/plain;charset=UTF-8";
|
|
87
|
-
body = bytes;
|
|
88
|
-
size = bytes.byteLength;
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
contentLength: size,
|
|
92
|
-
contentType: type,
|
|
93
|
-
body
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
function fromRequestBody(request) {
|
|
97
|
-
if (request.bodyUsed) throw new TypeError("Request body has already been used");
|
|
98
|
-
if (request.body == null) return null;
|
|
99
|
-
return webReadableToFuman(request.body);
|
|
100
|
-
}
|
|
101
|
-
//#endregion
|
|
102
|
-
export { extractBody, fromRequestBody, getFormDataLength };
|
package/dialers/index.d.cts
DELETED
package/dialers/index.d.ts
DELETED
package/dialers/proxy.cjs
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
-
const require_net = require("../_internal/net.cjs");
|
|
3
|
-
let _npy_proxy_kit = require("@npy/proxy-kit");
|
|
4
|
-
//#region src/dialers/proxy.ts
|
|
5
|
-
var DEFAULT_HTTP_ALPN_PROTOCOLS = ["http/1.1"];
|
|
6
|
-
function normalizeProxy(proxy) {
|
|
7
|
-
if (typeof proxy !== "string") return proxy;
|
|
8
|
-
const parsed = (0, _npy_proxy_kit.parse)(proxy, { strict: true });
|
|
9
|
-
if (parsed == null) throw new TypeError(`Invalid proxy string: ${proxy}`);
|
|
10
|
-
return parsed;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Dialer that routes connections through an HTTP, HTTPS or SOCKS proxy.
|
|
14
|
-
*
|
|
15
|
-
* @remarks
|
|
16
|
-
* Secure targets are tunneled and then upgraded to TLS after the proxy connection
|
|
17
|
-
* has been established.
|
|
18
|
-
*/
|
|
19
|
-
var ProxyDialer = class {
|
|
20
|
-
proxy;
|
|
21
|
-
#options;
|
|
22
|
-
#connectThroughProxy;
|
|
23
|
-
constructor(proxy, options = {}) {
|
|
24
|
-
this.proxy = normalizeProxy(proxy);
|
|
25
|
-
this.#options = { ...options };
|
|
26
|
-
this.#connectThroughProxy = (0, _npy_proxy_kit.createProxyConnection)({
|
|
27
|
-
proxy: this.proxy,
|
|
28
|
-
connectionFn: require_net.connectTcp
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
async dial(target, options = {}) {
|
|
32
|
-
const tunneled = await this.#connectThroughProxy({
|
|
33
|
-
address: target.address,
|
|
34
|
-
port: target.port,
|
|
35
|
-
signal: options.signal
|
|
36
|
-
});
|
|
37
|
-
if (!target.secure) return tunneled;
|
|
38
|
-
return this.#upgradeSecureTarget(tunneled, target, options.signal);
|
|
39
|
-
}
|
|
40
|
-
async #upgradeSecureTarget(conn, target, signal) {
|
|
41
|
-
const sni = target.sni ?? this.#options.sni ?? target.address;
|
|
42
|
-
const extraOptions = this.#options.extraOptions || target.extraOptions ? {
|
|
43
|
-
...this.#options.extraOptions,
|
|
44
|
-
...target.extraOptions
|
|
45
|
-
} : void 0;
|
|
46
|
-
return require_net.upgradeTls(conn, {
|
|
47
|
-
signal,
|
|
48
|
-
caCerts: this.#options.caCerts,
|
|
49
|
-
sni,
|
|
50
|
-
alpnProtocols: target.alpnProtocols ?? this.#options.alpnProtocols ?? [...DEFAULT_HTTP_ALPN_PROTOCOLS],
|
|
51
|
-
extraOptions
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
//#endregion
|
|
56
|
-
exports.ProxyDialer = ProxyDialer;
|
package/dialers/proxy.d.cts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { NodeTlsUpgradeOptions } from '@fuman/node';
|
|
2
|
-
import { createProxyConnection, ProxyInfo } from '@npy/proxy-kit';
|
|
3
|
-
import { Dialer } from '../types/dialer';
|
|
4
|
-
type ResolvedProxy = Parameters<typeof createProxyConnection>[0]["proxy"];
|
|
5
|
-
/**
|
|
6
|
-
* Dialer that routes connections through an HTTP, HTTPS or SOCKS proxy.
|
|
7
|
-
*
|
|
8
|
-
* @remarks
|
|
9
|
-
* Secure targets are tunneled and then upgraded to TLS after the proxy connection
|
|
10
|
-
* has been established.
|
|
11
|
-
*/
|
|
12
|
-
export declare class ProxyDialer implements Dialer {
|
|
13
|
-
#private;
|
|
14
|
-
readonly proxy: ResolvedProxy;
|
|
15
|
-
constructor(proxy: ProxyDialer.Input, options?: ProxyDialer.Options);
|
|
16
|
-
dial(target: Dialer.Target, options?: Dialer.Options): Promise<Dialer.ConnectionLike>;
|
|
17
|
-
}
|
|
18
|
-
export declare namespace ProxyDialer {
|
|
19
|
-
type Input = string | ProxyInfo;
|
|
20
|
-
interface Options {
|
|
21
|
-
caCerts?: string[];
|
|
22
|
-
sni?: string;
|
|
23
|
-
alpnProtocols?: string[];
|
|
24
|
-
extraOptions?: NodeTlsUpgradeOptions["extraOptions"];
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export {};
|
package/dialers/proxy.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { NodeTlsUpgradeOptions } from '@fuman/node';
|
|
2
|
-
import { createProxyConnection, ProxyInfo } from '@npy/proxy-kit';
|
|
3
|
-
import { Dialer } from '../types/dialer';
|
|
4
|
-
type ResolvedProxy = Parameters<typeof createProxyConnection>[0]["proxy"];
|
|
5
|
-
/**
|
|
6
|
-
* Dialer that routes connections through an HTTP, HTTPS or SOCKS proxy.
|
|
7
|
-
*
|
|
8
|
-
* @remarks
|
|
9
|
-
* Secure targets are tunneled and then upgraded to TLS after the proxy connection
|
|
10
|
-
* has been established.
|
|
11
|
-
*/
|
|
12
|
-
export declare class ProxyDialer implements Dialer {
|
|
13
|
-
#private;
|
|
14
|
-
readonly proxy: ResolvedProxy;
|
|
15
|
-
constructor(proxy: ProxyDialer.Input, options?: ProxyDialer.Options);
|
|
16
|
-
dial(target: Dialer.Target, options?: Dialer.Options): Promise<Dialer.ConnectionLike>;
|
|
17
|
-
}
|
|
18
|
-
export declare namespace ProxyDialer {
|
|
19
|
-
type Input = string | ProxyInfo;
|
|
20
|
-
interface Options {
|
|
21
|
-
caCerts?: string[];
|
|
22
|
-
sni?: string;
|
|
23
|
-
alpnProtocols?: string[];
|
|
24
|
-
extraOptions?: NodeTlsUpgradeOptions["extraOptions"];
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export {};
|
package/dialers/proxy.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { connectTcp, upgradeTls } from "../_internal/net.js";
|
|
2
|
-
import { createProxyConnection, parse } from "@npy/proxy-kit";
|
|
3
|
-
//#region src/dialers/proxy.ts
|
|
4
|
-
var DEFAULT_HTTP_ALPN_PROTOCOLS = ["http/1.1"];
|
|
5
|
-
function normalizeProxy(proxy) {
|
|
6
|
-
if (typeof proxy !== "string") return proxy;
|
|
7
|
-
const parsed = parse(proxy, { strict: true });
|
|
8
|
-
if (parsed == null) throw new TypeError(`Invalid proxy string: ${proxy}`);
|
|
9
|
-
return parsed;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Dialer that routes connections through an HTTP, HTTPS or SOCKS proxy.
|
|
13
|
-
*
|
|
14
|
-
* @remarks
|
|
15
|
-
* Secure targets are tunneled and then upgraded to TLS after the proxy connection
|
|
16
|
-
* has been established.
|
|
17
|
-
*/
|
|
18
|
-
var ProxyDialer = class {
|
|
19
|
-
proxy;
|
|
20
|
-
#options;
|
|
21
|
-
#connectThroughProxy;
|
|
22
|
-
constructor(proxy, options = {}) {
|
|
23
|
-
this.proxy = normalizeProxy(proxy);
|
|
24
|
-
this.#options = { ...options };
|
|
25
|
-
this.#connectThroughProxy = createProxyConnection({
|
|
26
|
-
proxy: this.proxy,
|
|
27
|
-
connectionFn: connectTcp
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
async dial(target, options = {}) {
|
|
31
|
-
const tunneled = await this.#connectThroughProxy({
|
|
32
|
-
address: target.address,
|
|
33
|
-
port: target.port,
|
|
34
|
-
signal: options.signal
|
|
35
|
-
});
|
|
36
|
-
if (!target.secure) return tunneled;
|
|
37
|
-
return this.#upgradeSecureTarget(tunneled, target, options.signal);
|
|
38
|
-
}
|
|
39
|
-
async #upgradeSecureTarget(conn, target, signal) {
|
|
40
|
-
const sni = target.sni ?? this.#options.sni ?? target.address;
|
|
41
|
-
const extraOptions = this.#options.extraOptions || target.extraOptions ? {
|
|
42
|
-
...this.#options.extraOptions,
|
|
43
|
-
...target.extraOptions
|
|
44
|
-
} : void 0;
|
|
45
|
-
return upgradeTls(conn, {
|
|
46
|
-
signal,
|
|
47
|
-
caCerts: this.#options.caCerts,
|
|
48
|
-
sni,
|
|
49
|
-
alpnProtocols: target.alpnProtocols ?? this.#options.alpnProtocols ?? [...DEFAULT_HTTP_ALPN_PROTOCOLS],
|
|
50
|
-
extraOptions
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
//#endregion
|
|
55
|
-
export { ProxyDialer };
|