@jameskh/proxywire 1.0.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 proxywire contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # proxywire
2
+
3
+ Proxy list parsing and proxy-aware `fetch` for Node and Bun.
4
+
5
+ Two jobs, both of which tend to get reinvented badly:
6
+
7
+ 1. **Turn whatever your provider exported into a URL you can dial.** Residential
8
+ proxy vendors ship lists in at least five mutually incompatible notations.
9
+ `proxywire` accepts all of them and hands back one normalized URL.
10
+ 2. **Give you a `fetch` that actually goes through the proxy.** On both runtimes.
11
+ This is less obvious than it sounds — see [Bun](#bun) below.
12
+
13
+ No framework, no globals, no logging to the console. Node 18+ or Bun.
14
+
15
+ ## Install
16
+
17
+ ```sh
18
+ bun add @jameskh/proxywire # or: npm i @jameskh/proxywire
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```ts
24
+ import { loadProxyFile, makeFetcher, maskProxy, shuffleProxies } from "@jameskh/proxywire";
25
+
26
+ const proxies = shuffleProxies(loadProxyFile("./proxies.txt"));
27
+
28
+ for (const proxy of proxies) {
29
+ const { fetch, close } = makeFetcher(proxy, { timeoutMs: 45_000 });
30
+ try {
31
+ const response = await fetch("https://api.ipify.org?format=json");
32
+ console.log(maskProxy(proxy), await response.json());
33
+ break;
34
+ } finally {
35
+ await close();
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## Proxy notations
41
+
42
+ `normalizeProxy` accepts every shape below and returns a dialable URL. Anything
43
+ it cannot understand throws a `ProxyParseError` naming the source and line.
44
+
45
+ | Input | Result |
46
+ | --- | --- |
47
+ | `socks5://bob:pw@1.2.3.4:1080` | unchanged |
48
+ | `socks5://1.2.3.4:1080:bob:pw` | `socks5://bob:pw@1.2.3.4:1080` |
49
+ | `bob:pw@1.2.3.4:1080` | `socks5://bob:pw@1.2.3.4:1080` |
50
+ | `1.2.3.4:1080@bob:pw` | `socks5://bob:pw@1.2.3.4:1080` |
51
+ | `bob:pw:1.2.3.4:1080` | `socks5://bob:pw@1.2.3.4:1080` |
52
+ | `1.2.3.4:1080:bob:pw` | `socks5://bob:pw@1.2.3.4:1080` |
53
+
54
+ The last four carry no scheme, so one is applied — `socks5` unless you say
55
+ otherwise. Host and credentials are told apart by which field looks like a port,
56
+ and credentials are percent-encoded so a password containing `@` or `:` cannot
57
+ silently rewrite the host.
58
+
59
+ ```ts
60
+ normalizeProxy("bob:pw@1.2.3.4:1080", { defaultProtocol: "http" });
61
+
62
+ // Resolve the scheme lazily when it comes from the environment, so a variable
63
+ // set after import time is still honoured.
64
+ normalizeProxy(line, { defaultProtocol: () => process.env.PROXY_PROTOCOL ?? "socks5" });
65
+ ```
66
+
67
+ Supported schemes: `http`, `https`, `socks4`, `socks5`.
68
+
69
+ ## Reading a list
70
+
71
+ ```ts
72
+ import { loadProxyFile, parseProxyList } from "@jameskh/proxywire";
73
+
74
+ loadProxyFile("./config/proxies.txt"); // [] when the file is absent
75
+ parseProxyList(text, { source: "PROXY_ENV" }); // same parser, any string
76
+ ```
77
+
78
+ Blank lines and `#` comments are skipped, but line numbers are counted against
79
+ the original text, so errors point at the line you actually have to fix.
80
+
81
+ Lists still holding the placeholder rows that ship in example files
82
+ (`user:pass@host:port` and friends) are rejected outright — running against a
83
+ template is a configuration mistake, and failing on it beats a confusing DNS
84
+ error later. Pass `rejectPlaceholders: false` to skip the check, or
85
+ `commentPrefix` to use something other than `#`.
86
+
87
+ ## Making requests
88
+
89
+ ```ts
90
+ const { fetch, dispatcher, close } = makeFetcher(proxy, {
91
+ timeoutMs: 30_000, // applied unless you pass your own signal
92
+ socksConnectTimeoutMs: 15_000,
93
+ minTlsVersion: "TLSv1.2",
94
+ logger: { debug: (m) => log.debug(m) },
95
+ });
96
+ ```
97
+
98
+ Call `close()` when you are finished. Without it every request cycle leaks an
99
+ undici `Agent` and its sockets, which exhausts file descriptors in any
100
+ long-running process. `makeFetcher()` with no proxy returns a plain `fetch` and
101
+ a `close()` that does nothing, so the calling code needs no branching.
102
+
103
+ SOCKS proxies get an undici `Agent` whose `connect` opens the tunnel by hand and
104
+ starts TLS over it, since undici has no SOCKS support of its own. HTTP and HTTPS
105
+ proxies go to `ProxyAgent`.
106
+
107
+ ### Bun
108
+
109
+ Bun ignores undici's `dispatcher` option. A request aimed at a dead proxy still
110
+ succeeds and reports the machine's own address — every "proxied" call silently
111
+ goes out unproxied, which is the worst possible failure mode for anything that
112
+ uses proxies for a reason. Bun's own `fetch` takes a `proxy` option that it does
113
+ honour, so `makeFetcher` uses that path under Bun and the dispatcher path under
114
+ Node.
115
+
116
+ Bun's `proxy` option cannot do SOCKS. Rather than fall through to a dispatcher
117
+ Bun will ignore, `makeFetcher` throws:
118
+
119
+ ```
120
+ SOCKS proxies are not supported when running under Bun (1.2.3.4:1080).
121
+ Use an http:// proxy, or set allowSocksUnderBun to opt out of this check.
122
+ ```
123
+
124
+ The proxy is masked to `host:port` in that message — credentials in a proxy URL
125
+ are as sensitive as any other password and must never reach a log.
126
+
127
+ ## Diagnosing failures
128
+
129
+ `fetch` throws a bare `TypeError: fetch failed` for every network failure and
130
+ puts the real reason in `cause`, so a dead proxy, a bad credential and a slow
131
+ host are indistinguishable. `describeError` flattens the chain:
132
+
133
+ ```ts
134
+ describeError(error); // "fetch failed -> connection refused (ECONNREFUSED)"
135
+ ```
136
+
137
+ `retryOnTransportFailure` reruns an operation once on failure, which is enough
138
+ for the connection drops a rotating pool produces on its own:
139
+
140
+ ```ts
141
+ await retryOnTransportFailure("token request", () => fetch(url, init), { logger });
142
+ ```
143
+
144
+ ## API
145
+
146
+ | Export | Purpose |
147
+ | --- | --- |
148
+ | `makeFetcher(proxy?, options?)` | `{ fetch, dispatcher?, close() }` bound to one proxy |
149
+ | `makeProxyDispatcher(proxy, options?)` | The undici dispatcher on its own |
150
+ | `closeDispatcher(dispatcher?)` | Release a dispatcher's socket pool |
151
+ | `withRequestTimeout(init, ms)` | Attach a timeout signal unless one exists |
152
+ | `retryOnTransportFailure(label, run, options?)` | Run once more on failure |
153
+ | `normalizeProxy(input, options?)` | Any notation to a dialable URL |
154
+ | `parseProxyList(text, options?)` | Newline-separated list to URLs |
155
+ | `loadProxyFile(path, options?)` | Same, from a file; `[]` when absent |
156
+ | `parseSchemelessProxy(input)` | The four schemeless shapes, as parts |
157
+ | `isPlaceholderProxy(input)` | True for example-file template rows |
158
+ | `maskProxy(proxy)` | `host:port`, safe to log |
159
+ | `shuffleProxies(list)` | Fisher-Yates copy, to spread load |
160
+ | `describeError(error)` | Flatten an error and its `cause` chain |
161
+ | `IS_BUN` | Runtime check used for the paths above |
162
+ | `SUPPORTED_PROXY_PROTOCOLS` | `["http", "https", "socks4", "socks5"]` |
163
+ | `ProxyParseError` | Carries `source` and `lineNumber` |
164
+
165
+ Types: `Fetcher`, `FetcherOptions`, `ProxyDispatcher`, `ProxyFetch`,
166
+ `ProxyListOptions`, `ProxyLogger`, `ProxyParseOptions`, `ProxyProtocol`.
167
+
168
+ ## Runtime support
169
+
170
+ The published package ships both compiled JavaScript and its TypeScript source.
171
+ Node resolves `dist/` with full `.d.ts` declarations; Bun resolves the `bun`
172
+ export condition and reads `src/` directly, so a linked checkout picks up edits
173
+ with no build step.
174
+
175
+ ## Development
176
+
177
+ ```sh
178
+ bun install
179
+ bun test
180
+ bun run typecheck
181
+ bun run build # emits dist/ for Node consumers
182
+ ```
183
+
184
+ Relative imports carry explicit `.js` extensions so the emitted ESM resolves
185
+ under Node. TypeScript maps `./errors.js` back to `errors.ts` at compile time.
186
+
187
+ ## License
188
+
189
+ MIT
@@ -0,0 +1,25 @@
1
+ /** Thrown when a proxy string cannot be understood, with the offending location. */
2
+ export declare class ProxyParseError extends Error {
3
+ readonly source?: string;
4
+ readonly lineNumber?: number;
5
+ constructor(message: string, location?: {
6
+ source?: string;
7
+ lineNumber?: number;
8
+ });
9
+ }
10
+ /** Renders `in <source> on line <n>` for error messages, omitting what is unknown. */
11
+ export declare function describeLocation(location: {
12
+ source?: string;
13
+ lineNumber?: number;
14
+ }): string;
15
+ /**
16
+ * Flattens an error and its `cause` chain into one readable line.
17
+ *
18
+ * `fetch` throws a bare `TypeError: fetch failed` for every underlying network
19
+ * failure - DNS, TLS, proxy handshake, timeout, refused connection - and puts
20
+ * the real reason in `cause`. Without walking that chain there is no way to
21
+ * tell a dead proxy from a bad credential from a slow host, which is exactly
22
+ * the distinction anyone debugging a proxy pool needs.
23
+ */
24
+ export declare function describeError(error: unknown): string;
25
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO;CAMrF;AAED,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAW3F;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAwBpD"}
package/dist/errors.js ADDED
@@ -0,0 +1,53 @@
1
+ /** Thrown when a proxy string cannot be understood, with the offending location. */
2
+ export class ProxyParseError extends Error {
3
+ source;
4
+ lineNumber;
5
+ constructor(message, location = {}) {
6
+ super(message);
7
+ this.name = "ProxyParseError";
8
+ this.source = location.source;
9
+ this.lineNumber = location.lineNumber;
10
+ }
11
+ }
12
+ /** Renders `in <source> on line <n>` for error messages, omitting what is unknown. */
13
+ export function describeLocation(location) {
14
+ const parts = [];
15
+ if (location.source) {
16
+ parts.push(` in ${location.source}`);
17
+ }
18
+ if (location.lineNumber !== undefined) {
19
+ parts.push(` on line ${location.lineNumber}`);
20
+ }
21
+ return parts.join("");
22
+ }
23
+ /**
24
+ * Flattens an error and its `cause` chain into one readable line.
25
+ *
26
+ * `fetch` throws a bare `TypeError: fetch failed` for every underlying network
27
+ * failure - DNS, TLS, proxy handshake, timeout, refused connection - and puts
28
+ * the real reason in `cause`. Without walking that chain there is no way to
29
+ * tell a dead proxy from a bad credential from a slow host, which is exactly
30
+ * the distinction anyone debugging a proxy pool needs.
31
+ */
32
+ export function describeError(error) {
33
+ const parts = [];
34
+ let current = error;
35
+ const seen = new Set();
36
+ while (current && !seen.has(current)) {
37
+ seen.add(current);
38
+ if (current instanceof Error) {
39
+ const code = current.code;
40
+ const detail = code && !current.message.includes(code) ? `${current.message} (${code})` : current.message;
41
+ if (!parts.includes(detail)) {
42
+ parts.push(detail);
43
+ }
44
+ current = current.cause;
45
+ }
46
+ else {
47
+ parts.push(String(current));
48
+ break;
49
+ }
50
+ }
51
+ return parts.length > 0 ? parts.join(" -> ") : "Unknown error";
52
+ }
53
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,MAAM,CAAU;IAChB,UAAU,CAAU;IAE7B,YAAY,OAAe,EAAE,WAAqD,EAAE;QAClF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;IACxC,CAAC;CACF;AAED,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,QAAkD;IACjF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,YAAY,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAW,CAAC;IAEhC,OAAO,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAElB,IAAI,OAAO,YAAY,KAAK,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAI,OAAiC,CAAC,IAAI,CAAC;YACrD,MAAM,MAAM,GACV,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YAC7F,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;YAED,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5B,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;AACjE,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { FetcherOptions, ProxyDispatcher, ProxyFetch } from "./types.js";
2
+ /** True when running under Bun rather than Node. */
3
+ export declare const IS_BUN: boolean;
4
+ /**
5
+ * Builds an undici dispatcher for a proxy URL.
6
+ *
7
+ * SOCKS gets a plain `Agent` whose `connect` opens the SOCKS tunnel by hand and
8
+ * then starts TLS over it, because undici has no SOCKS support of its own.
9
+ * HTTP/HTTPS proxies go to `ProxyAgent`, which already speaks CONNECT.
10
+ */
11
+ export declare function makeProxyDispatcher(proxy: string, options?: FetcherOptions): ProxyDispatcher;
12
+ /** Applies the request budget, but never overrides a signal the caller supplied. */
13
+ export declare function withRequestTimeout(options: RequestInit, timeoutMs: number): RequestInit;
14
+ /**
15
+ * Runs `run` again once if it fails.
16
+ *
17
+ * Proxy pools drop the occasional connection for reasons that have nothing to
18
+ * do with the request - a rotating exit going away mid-handshake, most often -
19
+ * and a single immediate retry turns most of those into a success.
20
+ */
21
+ export declare function retryOnTransportFailure<T>(describe: string, run: () => Promise<T>, options?: FetcherOptions): Promise<T>;
22
+ export interface Fetcher {
23
+ /** A `fetch` that routes through the proxy this fetcher was built for. */
24
+ fetch: ProxyFetch;
25
+ /** The dispatcher backing it, absent when proxyless or on Bun's native path. */
26
+ dispatcher?: ProxyDispatcher;
27
+ /** Releases the dispatcher's socket pool. Safe to call more than once. */
28
+ close(): Promise<void>;
29
+ }
30
+ /**
31
+ * A `fetch` bound to one proxy, or an unproxied one when `proxy` is omitted.
32
+ *
33
+ * The two runtimes need different mechanisms and this is the whole reason the
34
+ * function exists. Bun ignores undici's `dispatcher` option outright, so a
35
+ * request aimed at a dead proxy still succeeds and reports the machine's own
36
+ * address - every "proxied" call silently goes out unproxied. Bun's own `fetch`
37
+ * takes a `proxy` option that it does honour, so that path is used there, and
38
+ * the dispatcher path is kept for Node.
39
+ */
40
+ export declare function makeFetcher(proxy?: string, options?: FetcherOptions): Fetcher;
41
+ /**
42
+ * Releases a dispatcher's connection pool.
43
+ *
44
+ * Without this every request cycle leaks an `Agent`/`ProxyAgent` and its
45
+ * sockets, which exhausts file descriptors in any long-running process.
46
+ */
47
+ export declare function closeDispatcher(dispatcher?: ProxyDispatcher): Promise<void>;
48
+ //# sourceMappingURL=fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../src/fetcher.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAO9E,oDAAoD;AACpD,eAAO,MAAM,MAAM,SAA+D,CAAC;AAEnF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,eAAe,CAqDhG;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAEvF;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACrB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,MAAM,WAAW,OAAO;IACtB,0EAA0E;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,gFAAgF;IAChF,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,0EAA0E;IAC1E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CA0CjF;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,UAAU,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjF"}
@@ -0,0 +1,147 @@
1
+ import * as tls from "node:tls";
2
+ import { SocksClient } from "socks";
3
+ import { Agent, fetch as undiciFetch, ProxyAgent } from "undici";
4
+ import { describeError } from "./errors.js";
5
+ import { maskProxy } from "./parse.js";
6
+ const DEFAULT_TIMEOUT_MS = 30_000;
7
+ const DEFAULT_SOCKS_CONNECT_TIMEOUT_MS = 15_000;
8
+ const DEFAULT_SOCKS_PORT = 1080;
9
+ const DEFAULT_DESTINATION_PORT = 443;
10
+ /** True when running under Bun rather than Node. */
11
+ export const IS_BUN = typeof globalThis.Bun !== "undefined";
12
+ /**
13
+ * Builds an undici dispatcher for a proxy URL.
14
+ *
15
+ * SOCKS gets a plain `Agent` whose `connect` opens the SOCKS tunnel by hand and
16
+ * then starts TLS over it, because undici has no SOCKS support of its own.
17
+ * HTTP/HTTPS proxies go to `ProxyAgent`, which already speaks CONNECT.
18
+ */
19
+ export function makeProxyDispatcher(proxy, options = {}) {
20
+ if (proxy.startsWith("socks5://") || proxy.startsWith("socks4://")) {
21
+ const url = new URL(proxy);
22
+ const proxyHost = url.hostname;
23
+ const proxyPort = Number(url.port) || DEFAULT_SOCKS_PORT;
24
+ const proxyType = proxy.startsWith("socks5://") ? 5 : 4;
25
+ const auth = url.username
26
+ ? {
27
+ username: decodeURIComponent(url.username),
28
+ password: decodeURIComponent(url.password),
29
+ }
30
+ : undefined;
31
+ return new Agent({
32
+ connect: async (connectOptions, callback) => {
33
+ try {
34
+ const destinationPort = Number(connectOptions.port) || DEFAULT_DESTINATION_PORT;
35
+ const { socket } = await SocksClient.createConnection({
36
+ proxy: {
37
+ host: proxyHost,
38
+ port: proxyPort,
39
+ type: proxyType,
40
+ ...(auth
41
+ ? {
42
+ userId: auth.username,
43
+ password: auth.password,
44
+ }
45
+ : {}),
46
+ },
47
+ command: "connect",
48
+ destination: {
49
+ host: connectOptions.hostname,
50
+ port: destinationPort,
51
+ },
52
+ timeout: options.socksConnectTimeoutMs ?? DEFAULT_SOCKS_CONNECT_TIMEOUT_MS,
53
+ });
54
+ const tlsSocket = tls.connect({
55
+ socket: socket,
56
+ servername: connectOptions.hostname,
57
+ minVersion: options.minTlsVersion ?? "TLSv1.2",
58
+ });
59
+ tlsSocket.on("secureConnect", () => callback(null, tlsSocket));
60
+ tlsSocket.on("error", (error) => callback(error, null));
61
+ }
62
+ catch (error) {
63
+ callback(error, null);
64
+ }
65
+ },
66
+ });
67
+ }
68
+ return new ProxyAgent(proxy);
69
+ }
70
+ /** Applies the request budget, but never overrides a signal the caller supplied. */
71
+ export function withRequestTimeout(options, timeoutMs) {
72
+ return options.signal ? options : { ...options, signal: AbortSignal.timeout(timeoutMs) };
73
+ }
74
+ /**
75
+ * Runs `run` again once if it fails.
76
+ *
77
+ * Proxy pools drop the occasional connection for reasons that have nothing to
78
+ * do with the request - a rotating exit going away mid-handshake, most often -
79
+ * and a single immediate retry turns most of those into a success.
80
+ */
81
+ export async function retryOnTransportFailure(describe, run, options = {}) {
82
+ try {
83
+ return await run();
84
+ }
85
+ catch (error) {
86
+ const detail = describeError(error);
87
+ options.logger?.debug?.(`${describe} failed at the transport level, retrying once - ${detail.slice(0, 120)}`);
88
+ return run();
89
+ }
90
+ }
91
+ /**
92
+ * A `fetch` bound to one proxy, or an unproxied one when `proxy` is omitted.
93
+ *
94
+ * The two runtimes need different mechanisms and this is the whole reason the
95
+ * function exists. Bun ignores undici's `dispatcher` option outright, so a
96
+ * request aimed at a dead proxy still succeeds and reports the machine's own
97
+ * address - every "proxied" call silently goes out unproxied. Bun's own `fetch`
98
+ * takes a `proxy` option that it does honour, so that path is used there, and
99
+ * the dispatcher path is kept for Node.
100
+ */
101
+ export function makeFetcher(proxy, options = {}) {
102
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
103
+ if (proxy && IS_BUN && !options.allowSocksUnderBun) {
104
+ if (!/^https?:\/\//iu.test(proxy)) {
105
+ // Bun's fetch answers UnsupportedProxyProtocol for socks, and the
106
+ // dispatcher fallback below does nothing there, so refuse rather than
107
+ // run unproxied without saying so.
108
+ throw new Error(`SOCKS proxies are not supported when running under Bun (${maskProxy(proxy)}). ` +
109
+ `Use an http:// proxy, or set allowSocksUnderBun to opt out of this check.`);
110
+ }
111
+ const fetcher = async (url, requestOptions = {}) => fetch(url, { ...withRequestTimeout(requestOptions, timeoutMs), proxy });
112
+ return { fetch: fetcher, close: async () => { } };
113
+ }
114
+ const dispatcher = proxy ? makeProxyDispatcher(proxy, options) : undefined;
115
+ const fetcher = async (url, requestOptions = {}) => {
116
+ const withTimeout = withRequestTimeout(requestOptions, timeoutMs);
117
+ if (dispatcher) {
118
+ // On Node the global fetch is backed by whatever undici ships inside the
119
+ // running binary, which is often a different major from the "undici"
120
+ // package the dispatcher came from, and the handler interfaces are not
121
+ // compatible across majors. Using undici's own fetch keeps the
122
+ // dispatcher and the implementation on the same version.
123
+ const response = await undiciFetch(url, { ...withTimeout, dispatcher });
124
+ return response;
125
+ }
126
+ return fetch(url, withTimeout);
127
+ };
128
+ return { fetch: fetcher, dispatcher, close: () => closeDispatcher(dispatcher) };
129
+ }
130
+ /**
131
+ * Releases a dispatcher's connection pool.
132
+ *
133
+ * Without this every request cycle leaks an `Agent`/`ProxyAgent` and its
134
+ * sockets, which exhausts file descriptors in any long-running process.
135
+ */
136
+ export async function closeDispatcher(dispatcher) {
137
+ if (!dispatcher) {
138
+ return;
139
+ }
140
+ try {
141
+ await dispatcher.close();
142
+ }
143
+ catch {
144
+ // Already closed, or could not close cleanly - nothing actionable either way.
145
+ }
146
+ }
147
+ //# sourceMappingURL=fetcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../src/fetcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { describeError, ProxyParseError } from "./errors.js";
2
+ export { closeDispatcher, IS_BUN, makeFetcher, makeProxyDispatcher, retryOnTransportFailure, withRequestTimeout, } from "./fetcher.js";
3
+ export type { Fetcher } from "./fetcher.js";
4
+ export { loadProxyFile, parseProxyList } from "./list.js";
5
+ export { isPlaceholderProxy, maskProxy, normalizeProxy, parseSchemelessProxy, shuffleProxies, SUPPORTED_PROXY_PROTOCOLS, } from "./parse.js";
6
+ export type { ProxyProtocol } from "./parse.js";
7
+ export type { FetcherOptions, ProxyDispatcher, ProxyFetch, ProxyListOptions, ProxyLogger, ProxyParseOptions, } from "./types.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,eAAe,EACf,MAAM,EACN,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EACV,cAAc,EACd,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { describeError, ProxyParseError } from "./errors.js";
2
+ export { closeDispatcher, IS_BUN, makeFetcher, makeProxyDispatcher, retryOnTransportFailure, withRequestTimeout, } from "./fetcher.js";
3
+ export { loadProxyFile, parseProxyList } from "./list.js";
4
+ export { isPlaceholderProxy, maskProxy, normalizeProxy, parseSchemelessProxy, shuffleProxies, SUPPORTED_PROXY_PROTOCOLS, } from "./parse.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,eAAe,EACf,MAAM,EACN,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
package/dist/list.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { ProxyListOptions } from "./types.js";
2
+ /**
3
+ * Parses a newline-separated proxy list into dialable URLs.
4
+ *
5
+ * Line numbers are captured before blanks and comments are filtered out, so an
6
+ * error still points at the right line of the original text. Comments are
7
+ * skipped for a practical reason: example files are usually all comments, and
8
+ * the placeholder text inside them would otherwise read as proxies and trip the
9
+ * placeholder check. Skipping them also lets a real list carry notes.
10
+ */
11
+ export declare function parseProxyList(text: string, options?: ProxyListOptions): string[];
12
+ /**
13
+ * Reads and parses a proxy list file, returning an empty list when the file is
14
+ * absent. A missing file means "no proxies configured", which is a valid state
15
+ * for anything that treats proxying as optional.
16
+ */
17
+ export declare function loadProxyFile(path: string, options?: ProxyListOptions): string[];
18
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,EAAE,CA4BrF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,EAAE,CAMpF"}
package/dist/list.js ADDED
@@ -0,0 +1,44 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { ProxyParseError } from "./errors.js";
3
+ import { isPlaceholderProxy, normalizeProxy } from "./parse.js";
4
+ const DEFAULT_COMMENT_PREFIX = "#";
5
+ /**
6
+ * Parses a newline-separated proxy list into dialable URLs.
7
+ *
8
+ * Line numbers are captured before blanks and comments are filtered out, so an
9
+ * error still points at the right line of the original text. Comments are
10
+ * skipped for a practical reason: example files are usually all comments, and
11
+ * the placeholder text inside them would otherwise read as proxies and trip the
12
+ * placeholder check. Skipping them also lets a real list carry notes.
13
+ */
14
+ export function parseProxyList(text, options = {}) {
15
+ const commentPrefix = options.commentPrefix ?? DEFAULT_COMMENT_PREFIX;
16
+ const entries = text
17
+ .split(/\r?\n/u)
18
+ .map((line, index) => ({ proxy: line.trim(), lineNumber: index + 1 }))
19
+ .filter(({ proxy }) => proxy.length > 0 && (commentPrefix === "" || !proxy.startsWith(commentPrefix)));
20
+ if (entries.length === 0) {
21
+ return [];
22
+ }
23
+ if (options.rejectPlaceholders !== false) {
24
+ const placeholders = entries.filter(({ proxy }) => isPlaceholderProxy(proxy));
25
+ if (placeholders.length > 0) {
26
+ const location = { source: options.source, lineNumber: placeholders[0].lineNumber };
27
+ throw new ProxyParseError(`Placeholder proxies detected${options.source ? ` in ${options.source}` : ""}. ` +
28
+ `Replace the placeholder entries with real proxies.`, location);
29
+ }
30
+ }
31
+ return entries.map(({ proxy, lineNumber }) => normalizeProxy(proxy, { ...options, lineNumber }));
32
+ }
33
+ /**
34
+ * Reads and parses a proxy list file, returning an empty list when the file is
35
+ * absent. A missing file means "no proxies configured", which is a valid state
36
+ * for anything that treats proxying as optional.
37
+ */
38
+ export function loadProxyFile(path, options = {}) {
39
+ if (!existsSync(path)) {
40
+ return [];
41
+ }
42
+ return parseProxyList(readFileSync(path, "utf8"), { source: path, ...options });
43
+ }
44
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGhE,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAEnC;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,UAA4B,EAAE;IACzE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC;IAEtE,MAAM,OAAO,GAAG,IAAI;SACjB,KAAK,CAAC,QAAQ,CAAC;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;SACrE,MAAM,CACL,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CACZ,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,aAAa,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CACjF,CAAC;IAEJ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,OAAO,CAAC,kBAAkB,KAAK,KAAK,EAAE,CAAC;QACzC,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9E,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAE,CAAC,UAAU,EAAE,CAAC;YACrF,MAAM,IAAI,eAAe,CACvB,+BAA+B,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI;gBAC9E,oDAAoD,EACtD,QAAQ,CACT,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,UAA4B,EAAE;IACxE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,cAAc,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;AAClF,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { ProxyParseOptions } from "./types.js";
2
+ /** Schemes that can actually be dialled by `makeProxyDispatcher`. */
3
+ export declare const SUPPORTED_PROXY_PROTOCOLS: readonly ["http", "https", "socks4", "socks5"];
4
+ export type ProxyProtocol = (typeof SUPPORTED_PROXY_PROTOCOLS)[number];
5
+ /** True when a line is one of the template entries rather than a real proxy. */
6
+ export declare function isPlaceholderProxy(proxy: string): boolean;
7
+ /**
8
+ * A proxy reduced to `host:port`, safe to put in a log line or an error
9
+ * message. Credentials in a proxy URL are as sensitive as any other password
10
+ * and must never reach a log file.
11
+ */
12
+ export declare function maskProxy(proxy: string): string;
13
+ /** Fisher-Yates. Spreads load across a pool instead of hammering the first entry. */
14
+ export declare function shuffleProxies<T>(proxies: readonly T[]): T[];
15
+ /**
16
+ * Parses the four schemeless shapes providers export, told apart by which
17
+ * field looks like a port:
18
+ * login:password@ip:port
19
+ * ip:port@login:password
20
+ * login:password:ip:port
21
+ * ip:port:login:password
22
+ * The scheme is supplied separately - see `defaultProtocol`.
23
+ */
24
+ export declare function parseSchemelessProxy(proxy: string): {
25
+ host: string;
26
+ port: string;
27
+ username: string;
28
+ password: string;
29
+ } | undefined;
30
+ /**
31
+ * Turns any supported proxy notation into a dialable URL, leaving an already
32
+ * valid URL untouched. Throws a `ProxyParseError` naming the source and line
33
+ * when nothing matches.
34
+ */
35
+ export declare function normalizeProxy(proxy: string, options?: ProxyParseOptions & {
36
+ lineNumber?: number;
37
+ }): string;
38
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,gDAAiD,CAAC;AAExF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAuCvE,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAMzD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAO5D;AA+CD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,GACZ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAgDhF;AAqCD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,iBAAiB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,MAAM,CAmBR"}