@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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/_internal/streams.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create a ReadableStream from a Uint8Array.
|
|
4
|
+
*
|
|
5
|
+
* The stream will emit the entire byte array as a single chunk,
|
|
6
|
+
* then close immediately.
|
|
7
|
+
*
|
|
8
|
+
* @param bytes - The byte array to wrap.
|
|
9
|
+
* @returns A ReadableStream that emits the bytes.
|
|
10
|
+
*/
|
|
11
|
+
function bytesToStream(bytes) {
|
|
12
|
+
return new ReadableStream({ start(controller) {
|
|
13
|
+
controller.enqueue(bytes);
|
|
14
|
+
controller.close();
|
|
15
|
+
} });
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A TransformStream that limits the total number of bytes passed through.
|
|
19
|
+
*
|
|
20
|
+
* It accumulates the byte count from incoming chunks and enqueues them
|
|
21
|
+
* if the total remains within the limit; otherwise, it errors.
|
|
22
|
+
*
|
|
23
|
+
* @param maxBytes - The maximum allowed bytes before erroring.
|
|
24
|
+
*/
|
|
25
|
+
var MaxBytesTransformStream = class extends TransformStream {
|
|
26
|
+
constructor(maxBytes) {
|
|
27
|
+
if (!(maxBytes >= 0)) throw new TypeError("maxBytes must be a non-negative number");
|
|
28
|
+
let bytesRead = 0;
|
|
29
|
+
super({ transform: (chunk, ctrl) => {
|
|
30
|
+
if ((bytesRead += chunk.length) <= maxBytes) ctrl.enqueue(chunk);
|
|
31
|
+
else ctrl.error(/* @__PURE__ */ new Error("Response too large"));
|
|
32
|
+
} });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { MaxBytesTransformStream, bytesToStream };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const bodyErrorMapperSymbol: unique symbol;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const bodyErrorMapperSymbol: unique symbol;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
//#endregion
|
|
23
|
+
exports.__toESM = __toESM;
|
package/agent-pool.cjs
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_errors = require("./errors.cjs");
|
|
3
|
+
const require_agent = require("./agent.cjs");
|
|
4
|
+
const require_tcp = require("./dialers/tcp.cjs");
|
|
5
|
+
let generic_pool = require("generic-pool");
|
|
6
|
+
//#region src/agent-pool.ts
|
|
7
|
+
var defaultEvictionInterval = 1e4;
|
|
8
|
+
var defaultMax = Number.MAX_SAFE_INTEGER;
|
|
9
|
+
var defaultIdleTimeout = 3e4;
|
|
10
|
+
function createAgentPool(baseUrl, options = {}) {
|
|
11
|
+
const poolUrl = new URL(baseUrl);
|
|
12
|
+
const evictionRunIntervalMillis = options.poolIdleTimeout !== false ? Math.min(options.poolIdleTimeout || defaultEvictionInterval, defaultEvictionInterval) : 0;
|
|
13
|
+
const max = options.poolMaxPerHost ? Math.max(1, options.poolMaxPerHost) : defaultMax;
|
|
14
|
+
const softIdleTimeoutMillis = options.poolIdleTimeout !== false ? Math.max(1, options.poolIdleTimeout || defaultIdleTimeout) : -1;
|
|
15
|
+
const min = softIdleTimeoutMillis > 0 && options.poolMaxIdlePerHost ? Math.max(0, options.poolMaxIdlePerHost) : 0;
|
|
16
|
+
if (poolUrl.protocol !== "http:" && poolUrl.protocol !== "https:") throw new require_errors.UnsupportedProtocolError(poolUrl.protocol, {
|
|
17
|
+
origin: poolUrl.origin,
|
|
18
|
+
scheme: poolUrl.protocol,
|
|
19
|
+
host: poolUrl.hostname,
|
|
20
|
+
port: poolUrl.port ? Number.parseInt(poolUrl.port, 10) : void 0
|
|
21
|
+
});
|
|
22
|
+
const dialer = options.dialer ?? new require_tcp.AutoDialer();
|
|
23
|
+
const connectOptions = options.connect ?? {};
|
|
24
|
+
const ioOptions = options.io;
|
|
25
|
+
const pool = (0, generic_pool.createPool)({
|
|
26
|
+
async create() {
|
|
27
|
+
return require_agent.createAgent(dialer, baseUrl, {
|
|
28
|
+
connect: connectOptions,
|
|
29
|
+
io: ioOptions
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
async destroy(agent) {
|
|
33
|
+
agent.close();
|
|
34
|
+
}
|
|
35
|
+
}, {
|
|
36
|
+
autostart: false,
|
|
37
|
+
evictionRunIntervalMillis,
|
|
38
|
+
softIdleTimeoutMillis,
|
|
39
|
+
max,
|
|
40
|
+
min
|
|
41
|
+
});
|
|
42
|
+
let releaseAgentFns = [];
|
|
43
|
+
let closePromise;
|
|
44
|
+
async function send(sendOptions) {
|
|
45
|
+
let agent;
|
|
46
|
+
let agentReleased = false;
|
|
47
|
+
const releaseAgentFn = async (forceClose = false) => {
|
|
48
|
+
if (!agent || agentReleased) return;
|
|
49
|
+
agentReleased = true;
|
|
50
|
+
releaseAgentFns = releaseAgentFns.filter((release) => release !== releaseAgentFn);
|
|
51
|
+
if (forceClose) agent.close();
|
|
52
|
+
if (pool.isBorrowedResource(agent)) await pool.release(agent);
|
|
53
|
+
};
|
|
54
|
+
releaseAgentFns.push(releaseAgentFn);
|
|
55
|
+
try {
|
|
56
|
+
agent = await pool.acquire();
|
|
57
|
+
const responsePromise = agent.send(sendOptions);
|
|
58
|
+
agent.whenIdle().then(() => releaseAgentFn(), () => releaseAgentFn(true));
|
|
59
|
+
return responsePromise;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
await releaseAgentFn(true);
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function close() {
|
|
66
|
+
if (closePromise) return closePromise;
|
|
67
|
+
const promise = (async () => {
|
|
68
|
+
const pendingReleases = releaseAgentFns;
|
|
69
|
+
releaseAgentFns = [];
|
|
70
|
+
const errors = (await Promise.allSettled([...pendingReleases.map((release) => release(true)), (async () => {
|
|
71
|
+
try {
|
|
72
|
+
await pool.drain();
|
|
73
|
+
} finally {
|
|
74
|
+
await pool.clear();
|
|
75
|
+
}
|
|
76
|
+
})()])).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
77
|
+
if (errors.length === 1) throw errors[0];
|
|
78
|
+
if (errors.length > 1) throw new AggregateError(errors, "Failed to close agent pool cleanly");
|
|
79
|
+
})();
|
|
80
|
+
closePromise = promise;
|
|
81
|
+
try {
|
|
82
|
+
await promise;
|
|
83
|
+
} finally {
|
|
84
|
+
if (closePromise === promise) closePromise = void 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
[Symbol.asyncDispose]: close,
|
|
89
|
+
close,
|
|
90
|
+
hostname: poolUrl.hostname,
|
|
91
|
+
port: poolUrl.port ? Number.parseInt(poolUrl.port, 10) : poolUrl.protocol === "https:" ? 443 : 80,
|
|
92
|
+
send
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
exports.createAgentPool = createAgentPool;
|
package/agent-pool.d.cts
ADDED
package/agent-pool.d.ts
ADDED
package/agent-pool.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { UnsupportedProtocolError } from "./errors.js";
|
|
2
|
+
import { createAgent } from "./agent.js";
|
|
3
|
+
import { AutoDialer } from "./dialers/tcp.js";
|
|
4
|
+
import { createPool } from "generic-pool";
|
|
5
|
+
//#region src/agent-pool.ts
|
|
6
|
+
var defaultEvictionInterval = 1e4;
|
|
7
|
+
var defaultMax = Number.MAX_SAFE_INTEGER;
|
|
8
|
+
var defaultIdleTimeout = 3e4;
|
|
9
|
+
function createAgentPool(baseUrl, options = {}) {
|
|
10
|
+
const poolUrl = new URL(baseUrl);
|
|
11
|
+
const evictionRunIntervalMillis = options.poolIdleTimeout !== false ? Math.min(options.poolIdleTimeout || defaultEvictionInterval, defaultEvictionInterval) : 0;
|
|
12
|
+
const max = options.poolMaxPerHost ? Math.max(1, options.poolMaxPerHost) : defaultMax;
|
|
13
|
+
const softIdleTimeoutMillis = options.poolIdleTimeout !== false ? Math.max(1, options.poolIdleTimeout || defaultIdleTimeout) : -1;
|
|
14
|
+
const min = softIdleTimeoutMillis > 0 && options.poolMaxIdlePerHost ? Math.max(0, options.poolMaxIdlePerHost) : 0;
|
|
15
|
+
if (poolUrl.protocol !== "http:" && poolUrl.protocol !== "https:") throw new UnsupportedProtocolError(poolUrl.protocol, {
|
|
16
|
+
origin: poolUrl.origin,
|
|
17
|
+
scheme: poolUrl.protocol,
|
|
18
|
+
host: poolUrl.hostname,
|
|
19
|
+
port: poolUrl.port ? Number.parseInt(poolUrl.port, 10) : void 0
|
|
20
|
+
});
|
|
21
|
+
const dialer = options.dialer ?? new AutoDialer();
|
|
22
|
+
const connectOptions = options.connect ?? {};
|
|
23
|
+
const ioOptions = options.io;
|
|
24
|
+
const pool = createPool({
|
|
25
|
+
async create() {
|
|
26
|
+
return createAgent(dialer, baseUrl, {
|
|
27
|
+
connect: connectOptions,
|
|
28
|
+
io: ioOptions
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
async destroy(agent) {
|
|
32
|
+
agent.close();
|
|
33
|
+
}
|
|
34
|
+
}, {
|
|
35
|
+
autostart: false,
|
|
36
|
+
evictionRunIntervalMillis,
|
|
37
|
+
softIdleTimeoutMillis,
|
|
38
|
+
max,
|
|
39
|
+
min
|
|
40
|
+
});
|
|
41
|
+
let releaseAgentFns = [];
|
|
42
|
+
let closePromise;
|
|
43
|
+
async function send(sendOptions) {
|
|
44
|
+
let agent;
|
|
45
|
+
let agentReleased = false;
|
|
46
|
+
const releaseAgentFn = async (forceClose = false) => {
|
|
47
|
+
if (!agent || agentReleased) return;
|
|
48
|
+
agentReleased = true;
|
|
49
|
+
releaseAgentFns = releaseAgentFns.filter((release) => release !== releaseAgentFn);
|
|
50
|
+
if (forceClose) agent.close();
|
|
51
|
+
if (pool.isBorrowedResource(agent)) await pool.release(agent);
|
|
52
|
+
};
|
|
53
|
+
releaseAgentFns.push(releaseAgentFn);
|
|
54
|
+
try {
|
|
55
|
+
agent = await pool.acquire();
|
|
56
|
+
const responsePromise = agent.send(sendOptions);
|
|
57
|
+
agent.whenIdle().then(() => releaseAgentFn(), () => releaseAgentFn(true));
|
|
58
|
+
return responsePromise;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
await releaseAgentFn(true);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function close() {
|
|
65
|
+
if (closePromise) return closePromise;
|
|
66
|
+
const promise = (async () => {
|
|
67
|
+
const pendingReleases = releaseAgentFns;
|
|
68
|
+
releaseAgentFns = [];
|
|
69
|
+
const errors = (await Promise.allSettled([...pendingReleases.map((release) => release(true)), (async () => {
|
|
70
|
+
try {
|
|
71
|
+
await pool.drain();
|
|
72
|
+
} finally {
|
|
73
|
+
await pool.clear();
|
|
74
|
+
}
|
|
75
|
+
})()])).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
76
|
+
if (errors.length === 1) throw errors[0];
|
|
77
|
+
if (errors.length > 1) throw new AggregateError(errors, "Failed to close agent pool cleanly");
|
|
78
|
+
})();
|
|
79
|
+
closePromise = promise;
|
|
80
|
+
try {
|
|
81
|
+
await promise;
|
|
82
|
+
} finally {
|
|
83
|
+
if (closePromise === promise) closePromise = void 0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
[Symbol.asyncDispose]: close,
|
|
88
|
+
close,
|
|
89
|
+
hostname: poolUrl.hostname,
|
|
90
|
+
port: poolUrl.port ? Number.parseInt(poolUrl.port, 10) : poolUrl.protocol === "https:" ? 443 : 80,
|
|
91
|
+
send
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { createAgentPool };
|
package/agent.cjs
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_errors = require("./errors.cjs");
|
|
3
|
+
const require_error_mapping = require("./_internal/error-mapping.cjs");
|
|
4
|
+
const require_promises = require("./_internal/promises.cjs");
|
|
5
|
+
const require_symbols = require("./_internal/symbols.cjs");
|
|
6
|
+
const require_io = require("./io/io.cjs");
|
|
7
|
+
let _fuman_utils = require("@fuman/utils");
|
|
8
|
+
//#region src/agent.ts
|
|
9
|
+
var PORT_MAP = {
|
|
10
|
+
"http:": 80,
|
|
11
|
+
"https:": 443
|
|
12
|
+
};
|
|
13
|
+
var DEFAULT_ALPN_PROTOCOLS = ["http/1.1"];
|
|
14
|
+
function resolvedDeferred() {
|
|
15
|
+
const deferred = new _fuman_utils.Deferred();
|
|
16
|
+
deferred.resolve();
|
|
17
|
+
return deferred;
|
|
18
|
+
}
|
|
19
|
+
function withSignal(promise, signal) {
|
|
20
|
+
return signal ? require_promises.raceSignal(promise, signal) : promise;
|
|
21
|
+
}
|
|
22
|
+
function isTlsConnection(conn) {
|
|
23
|
+
return "getAlpnProtocol" in conn && typeof conn.getAlpnProtocol === "function";
|
|
24
|
+
}
|
|
25
|
+
function createAgent(dialer, baseUrl, options = {}) {
|
|
26
|
+
const base = new URL(baseUrl);
|
|
27
|
+
if (base.protocol !== "http:" && base.protocol !== "https:") throw new require_errors.UnsupportedProtocolError(base.protocol, {
|
|
28
|
+
origin: base.origin,
|
|
29
|
+
scheme: base.protocol,
|
|
30
|
+
host: base.hostname,
|
|
31
|
+
port: base.port ? Number.parseInt(base.port, 10) : void 0,
|
|
32
|
+
url: base.toString()
|
|
33
|
+
});
|
|
34
|
+
const secure = base.protocol === "https:";
|
|
35
|
+
const hostname = base.hostname;
|
|
36
|
+
const port = base.port ? Number.parseInt(base.port, 10) : PORT_MAP[base.protocol];
|
|
37
|
+
if (!Number.isFinite(port) || port <= 0 || port > 65535) throw new TypeError(`Invalid port in base URL: ${baseUrl}`);
|
|
38
|
+
const target = {
|
|
39
|
+
address: hostname,
|
|
40
|
+
port,
|
|
41
|
+
secure,
|
|
42
|
+
sni: secure ? hostname : void 0,
|
|
43
|
+
alpnProtocols: secure ? [...DEFAULT_ALPN_PROTOCOLS] : void 0
|
|
44
|
+
};
|
|
45
|
+
const connectOptions = options.connect ?? {};
|
|
46
|
+
const readerOptions = options.io?.reader ?? {};
|
|
47
|
+
const writerOptions = options.io?.writer ?? {};
|
|
48
|
+
let conn;
|
|
49
|
+
let connectPromise;
|
|
50
|
+
let closed = false;
|
|
51
|
+
let isBusy = false;
|
|
52
|
+
let lastUsedTime = Date.now();
|
|
53
|
+
let idleDeferred = resolvedDeferred();
|
|
54
|
+
function createBaseErrorContext() {
|
|
55
|
+
return {
|
|
56
|
+
origin: base.origin,
|
|
57
|
+
scheme: base.protocol,
|
|
58
|
+
host: hostname,
|
|
59
|
+
port
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function createRequestErrorContext(url, method) {
|
|
63
|
+
return {
|
|
64
|
+
...createBaseErrorContext(),
|
|
65
|
+
url: url.toString(),
|
|
66
|
+
method
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function markIdle() {
|
|
70
|
+
isBusy = false;
|
|
71
|
+
lastUsedTime = Date.now();
|
|
72
|
+
idleDeferred.resolve();
|
|
73
|
+
}
|
|
74
|
+
function disposeConn() {
|
|
75
|
+
const current = conn;
|
|
76
|
+
conn = void 0;
|
|
77
|
+
if (!current) return;
|
|
78
|
+
try {
|
|
79
|
+
current.close();
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
function forceClose() {
|
|
83
|
+
if (closed) return;
|
|
84
|
+
closed = true;
|
|
85
|
+
disposeConn();
|
|
86
|
+
if (!isBusy) markIdle();
|
|
87
|
+
}
|
|
88
|
+
function assertUsable() {
|
|
89
|
+
if (closed) throw new require_errors.AgentClosedError(createBaseErrorContext());
|
|
90
|
+
}
|
|
91
|
+
function assertSameOrigin(url) {
|
|
92
|
+
if (url.origin !== base.origin) throw new require_errors.OriginMismatchError(base.origin, url.origin, {
|
|
93
|
+
...createBaseErrorContext(),
|
|
94
|
+
url: url.toString()
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function configureConnection(nextConn) {
|
|
98
|
+
nextConn.setNoDelay(connectOptions.noDelay ?? true);
|
|
99
|
+
if (connectOptions.keepAlive !== null) nextConn.setKeepAlive(connectOptions.keepAlive ?? true);
|
|
100
|
+
}
|
|
101
|
+
async function connect(signal) {
|
|
102
|
+
assertUsable();
|
|
103
|
+
if (conn) return conn;
|
|
104
|
+
if (connectPromise) return withSignal(connectPromise, signal);
|
|
105
|
+
let timedOut = false;
|
|
106
|
+
let timeoutId;
|
|
107
|
+
const abortController = new AbortController();
|
|
108
|
+
const onAbort = () => abortController.abort(signal?.reason);
|
|
109
|
+
const cleanup = () => {
|
|
110
|
+
if (timeoutId !== void 0) {
|
|
111
|
+
clearTimeout(timeoutId);
|
|
112
|
+
timeoutId = void 0;
|
|
113
|
+
}
|
|
114
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
115
|
+
};
|
|
116
|
+
if (signal) if (signal.aborted) abortController.abort(signal.reason);
|
|
117
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
118
|
+
if (connectOptions.timeout != null && Number.isFinite(connectOptions.timeout) && connectOptions.timeout > 0) timeoutId = setTimeout(() => {
|
|
119
|
+
timedOut = true;
|
|
120
|
+
abortController.abort(new DOMException("Connection timed out", "TimeoutError"));
|
|
121
|
+
}, connectOptions.timeout);
|
|
122
|
+
connectPromise = (async () => {
|
|
123
|
+
try {
|
|
124
|
+
const nextConn = await dialer.dial(target, { signal: abortController.signal });
|
|
125
|
+
if (closed) {
|
|
126
|
+
try {
|
|
127
|
+
nextConn.close();
|
|
128
|
+
} catch {}
|
|
129
|
+
throw new require_errors.AgentClosedError(createBaseErrorContext());
|
|
130
|
+
}
|
|
131
|
+
configureConnection(nextConn);
|
|
132
|
+
if (secure && isTlsConnection(nextConn)) {
|
|
133
|
+
const alpn = nextConn.getAlpnProtocol();
|
|
134
|
+
if (alpn != null && alpn !== "" && alpn !== "http/1.1") {
|
|
135
|
+
try {
|
|
136
|
+
nextConn.close();
|
|
137
|
+
} catch {}
|
|
138
|
+
throw new require_errors.UnsupportedAlpnProtocolError(alpn, createBaseErrorContext());
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
conn = nextConn;
|
|
142
|
+
return nextConn;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw require_error_mapping.toConnectError(error, {
|
|
145
|
+
signal,
|
|
146
|
+
timedOut,
|
|
147
|
+
context: createBaseErrorContext()
|
|
148
|
+
});
|
|
149
|
+
} finally {
|
|
150
|
+
cleanup();
|
|
151
|
+
connectPromise = void 0;
|
|
152
|
+
}
|
|
153
|
+
})();
|
|
154
|
+
return withSignal(connectPromise, signal);
|
|
155
|
+
}
|
|
156
|
+
async function executeRequest(sendOptions, mapBodyError) {
|
|
157
|
+
assertUsable();
|
|
158
|
+
const url = typeof sendOptions.url === "string" ? new URL(sendOptions.url) : sendOptions.url;
|
|
159
|
+
const method = sendOptions.method.toUpperCase();
|
|
160
|
+
const errorContext = createRequestErrorContext(url, method);
|
|
161
|
+
if (sendOptions.signal?.aborted) throw new require_errors.RequestAbortedError(sendOptions.signal.reason, errorContext);
|
|
162
|
+
if (isBusy) throw new require_errors.AgentBusyError(errorContext);
|
|
163
|
+
assertSameOrigin(url);
|
|
164
|
+
if (method === "CONNECT") throw new require_errors.UnsupportedMethodError("CONNECT", errorContext);
|
|
165
|
+
isBusy = true;
|
|
166
|
+
idleDeferred = new _fuman_utils.Deferred();
|
|
167
|
+
let finalized = false;
|
|
168
|
+
let activeConn;
|
|
169
|
+
const finalize = (reusable) => {
|
|
170
|
+
if (finalized) return;
|
|
171
|
+
finalized = true;
|
|
172
|
+
if (!reusable || closed) {
|
|
173
|
+
if (conn === activeConn) disposeConn();
|
|
174
|
+
else if (activeConn) try {
|
|
175
|
+
activeConn.close();
|
|
176
|
+
} catch {}
|
|
177
|
+
}
|
|
178
|
+
markIdle();
|
|
179
|
+
};
|
|
180
|
+
const abortListener = () => {
|
|
181
|
+
if (activeConn) {
|
|
182
|
+
if (conn === activeConn) conn = void 0;
|
|
183
|
+
try {
|
|
184
|
+
activeConn.close();
|
|
185
|
+
} catch {}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
try {
|
|
189
|
+
activeConn = await connect(sendOptions.signal);
|
|
190
|
+
sendOptions.signal?.addEventListener("abort", abortListener, { once: true });
|
|
191
|
+
try {
|
|
192
|
+
await withSignal(require_io.writeRequest(activeConn, {
|
|
193
|
+
url,
|
|
194
|
+
method,
|
|
195
|
+
headers: sendOptions.headers,
|
|
196
|
+
body: sendOptions.body ?? null,
|
|
197
|
+
signal: sendOptions.signal
|
|
198
|
+
}, writerOptions), sendOptions.signal);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
throw require_error_mapping.toSendError(error, {
|
|
201
|
+
signal: sendOptions.signal,
|
|
202
|
+
context: errorContext,
|
|
203
|
+
phase: "request"
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const isHeadRequest = method === "HEAD";
|
|
207
|
+
const shouldIgnoreBody = (status) => isHeadRequest || status >= 100 && status < 200 || status === 204 || status === 304;
|
|
208
|
+
let response;
|
|
209
|
+
try {
|
|
210
|
+
response = await withSignal(require_io.readResponse(activeConn, readerOptions, shouldIgnoreBody, (reusable) => {
|
|
211
|
+
sendOptions.signal?.removeEventListener("abort", abortListener);
|
|
212
|
+
finalize(reusable);
|
|
213
|
+
}, mapBodyError), sendOptions.signal);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
throw require_error_mapping.toSendError(error, {
|
|
216
|
+
signal: sendOptions.signal,
|
|
217
|
+
context: errorContext,
|
|
218
|
+
phase: "response"
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return response;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
sendOptions.signal?.removeEventListener("abort", abortListener);
|
|
224
|
+
if (activeConn) {
|
|
225
|
+
if (conn === activeConn) conn = void 0;
|
|
226
|
+
try {
|
|
227
|
+
activeConn.close();
|
|
228
|
+
} catch {}
|
|
229
|
+
}
|
|
230
|
+
finalize(false);
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function send(sendOptions) {
|
|
235
|
+
const errorContext = createRequestErrorContext(typeof sendOptions.url === "string" ? new URL(sendOptions.url) : sendOptions.url, sendOptions.method.toUpperCase());
|
|
236
|
+
return executeRequest(sendOptions, sendOptions[require_symbols.bodyErrorMapperSymbol] ?? ((error) => require_error_mapping.toSendError(error, {
|
|
237
|
+
signal: sendOptions.signal,
|
|
238
|
+
context: errorContext,
|
|
239
|
+
phase: "body"
|
|
240
|
+
})));
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
[Symbol.dispose]: forceClose,
|
|
244
|
+
close: forceClose,
|
|
245
|
+
hostname,
|
|
246
|
+
port,
|
|
247
|
+
send,
|
|
248
|
+
whenIdle() {
|
|
249
|
+
return idleDeferred.promise;
|
|
250
|
+
},
|
|
251
|
+
get isIdle() {
|
|
252
|
+
return !isBusy;
|
|
253
|
+
},
|
|
254
|
+
get lastUsed() {
|
|
255
|
+
return lastUsedTime;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
exports.createAgent = createAgent;
|
package/agent.d.cts
ADDED
package/agent.d.ts
ADDED