tunnelfetch 1.11.0 → 1.13.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.
@@ -0,0 +1,78 @@
1
+ /** @typedef {import('./proxy/index.js').ConnectFn} ConnectFn */
2
+ /** @typedef {import('./proxy/index.js').Duplex} Duplex */
3
+ /**
4
+ * @typedef {object} ResolveConnectOptions
5
+ * @property {ConnectFn} [connect] an already-obtained socket factory. Checked first and returned
6
+ * as-is, so the recommended path costs no import and cannot be defeated by a bundler.
7
+ * @property {string[]} [specifiers] module specifiers to try, in order. The first module whose
8
+ * `exportName` is callable wins.
9
+ * @property {(specifier: string) => Promise<object>} [importModule] the importer. Defaults to a
10
+ * dynamic `import()`; override it to hand the bundler a literal specifier.
11
+ * @property {string} [exportName] the export to read off each module. Default 'connect'.
12
+ */
13
+ /**
14
+ * Is `value` shaped like a socket factory? Only callability can be checked without dialling, so
15
+ * that is what this checks — the duplex contract is enforced when a socket is actually opened.
16
+ * @param {unknown} value
17
+ * @returns {value is ConnectFn}
18
+ */
19
+ export function isConnectFn(value: unknown): value is ConnectFn;
20
+ /**
21
+ * Resolve a ConnectFn from an injected factory or from the first module specifier that yields one.
22
+ *
23
+ * Resolution order, and nothing else is consulted:
24
+ * 1. `options.connect`, when callable.
25
+ * 2. each entry of `options.specifiers`, in order.
26
+ *
27
+ * Throws ConfigError (CONFIG_UNSATISFIABLE) when nothing resolves, listing every specifier tried
28
+ * with the reason it failed. A socket factory that cannot be found is a deployment mistake, and a
29
+ * deployment mistake deserves to name the thing that was missing rather than surface later as
30
+ * "connect is not a function" from inside the TLS layer.
31
+ *
32
+ * @param {ResolveConnectOptions} [options]
33
+ * @returns {Promise<ConnectFn>}
34
+ */
35
+ export function resolveConnect(options?: ResolveConnectOptions): Promise<ConnectFn>;
36
+ /**
37
+ * Flatten a runtime socket into a plain duplex object.
38
+ *
39
+ * Worth its own function because of a trap that has already cost this package a bug: on the edge
40
+ * runtime a socket's `readable` and `writable` are ACCESSORS ON THE PROTOTYPE, so `{ ...socket }`
41
+ * copies neither and the first read fails far away, inside the TLS layer, with "Cannot read
42
+ * properties of undefined (reading 'getReader')". Reading the properties explicitly — which is
43
+ * what this does — is the only spread-safe way to hand a host socket to code that may copy it.
44
+ *
45
+ * @param {Duplex} socket
46
+ * @returns {Duplex}
47
+ */
48
+ export function normaliseSocket(socket: Duplex): Duplex;
49
+ /**
50
+ * Wrap a ConnectFn so every socket it returns is flattened by normaliseSocket. Useful when the
51
+ * factory comes from a runtime whose sockets are host objects rather than plain records.
52
+ * @param {ConnectFn} connect
53
+ * @returns {ConnectFn}
54
+ */
55
+ export function normalisingConnect(connect: ConnectFn): ConnectFn;
56
+ export type ConnectFn = import("./proxy/index.js").ConnectFn;
57
+ export type Duplex = import("./proxy/index.js").Duplex;
58
+ export type ResolveConnectOptions = {
59
+ /**
60
+ * an already-obtained socket factory. Checked first and returned
61
+ * as-is, so the recommended path costs no import and cannot be defeated by a bundler.
62
+ */
63
+ connect?: import("./proxy/index.js").ConnectFn | undefined;
64
+ /**
65
+ * module specifiers to try, in order. The first module whose
66
+ * `exportName` is callable wins.
67
+ */
68
+ specifiers?: string[] | undefined;
69
+ /**
70
+ * the importer. Defaults to a
71
+ * dynamic `import()`; override it to hand the bundler a literal specifier.
72
+ */
73
+ importModule?: ((specifier: string) => Promise<object>) | undefined;
74
+ /**
75
+ * the export to read off each module. Default 'connect'.
76
+ */
77
+ exportName?: string | undefined;
78
+ };
package/types/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { Client, createFetch, install } from "./client.js";
13
13
  export { ConnectionPool, poolKey } from "./pool.js";
14
14
  export { openConnection, targetFromUrl, nativeFetchCanServe } from "./transport.js";
15
15
  export { openTunnel, parseProxy } from "./proxy/index.js";
16
+ export { resolveConnect, isConnectFn, normaliseSocket, normalisingConnect } from "./connect.js";
16
17
  export { verifyChain, rootStoreProvenance } from "./trust/index.js";
17
18
  export { TunnelFetchError, ProxyError, HttpError, TlsError, TlsUnsupportedError, Http2Error, CertificateError, TimeoutError, LimitError, ConfigError, codes } from "./errors.js";
18
19
  export { profiles, curl, chrome, applyProfile } from "./profiles.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Wrap a proxy handshake's buffered reader plus its socket as one byte stream.
3
+ *
4
+ * @param {{ readable: ReadableStream<Uint8Array> }} socket the raw transport
5
+ * @param {import('../util/bytes.js').ByteReader} reader the handshake's reader, possibly holding
6
+ * bytes that belong to the tunnel
7
+ * @returns {ReadableStream<Uint8Array>}
8
+ */
9
+ export function tunnelReadable(socket: {
10
+ readable: ReadableStream<Uint8Array>;
11
+ }, reader: import("../util/bytes.js").ByteReader): ReadableStream<Uint8Array>;
@@ -74,21 +74,25 @@ export class UnexpectedEofError extends TunnelFetchError {
74
74
  * be retained beyond the caller's immediate use if memory matters.
75
75
  *
76
76
  * When the source is a byte stream — on the target runtime, a socket's readable is one — the
77
- * reader pulls with BYOB reads into large fresh views instead of taking the source's own
78
- * chunking. This is measured, not stylistic: the runtime delivers socket data in chunks of at
79
- * most 4096 bytes, ~1200 of them for a 4 MB body, and every chunk is a runtime/JS boundary
80
- * crossing; a BYOB read hands over everything the transport has buffered (up to the view size)
81
- * in one crossing, and resolves with a partial fill the instant anything at all is available,
82
- * so delivery latency is unchanged. Sources that are not byte streams (every in-process
77
+ * reader pulls with BYOB reads instead of taking the source's own chunking. This is measured, not
78
+ * stylistic: the runtime delivers socket data in chunks of at most 4096 bytes, ~1200 of them for a
79
+ * 4 MB body, and every chunk is a runtime/JS boundary crossing; a BYOB read collects several of
80
+ * them into one.
81
+ *
82
+ * How MANY it collects is the transport's decision, not the view's. A BYOB read resolves the
83
+ * instant any byte is available and never waits to fill, so the view is a ceiling that is normally
84
+ * not reached: measured over a 4 MB body, 37 KB average fill on a direct socket and 8 KB through a
85
+ * proxy, whatever the view size. Sizing the view far above that buys nothing and costs the
86
+ * allocation — see BYOB_PULL_BYTES. Sources that are not byte streams (every in-process
83
87
  * ReadableStream in this package and its tests) take the default-reader path unchanged.
84
88
  */
85
89
  export class ByteReader {
86
90
  /**
87
91
  * @param {ReadableStream<Uint8Array>} readable
88
- * @param {number} [pullBytes] size of each BYOB view pulled from the source. Tunable because it
89
- * decides how many times a body crosses the runtime boundary on the way in, and that turned out
90
- * to be the largest single cost in a large response42 ms of a 106 ms 4 MB request is socket
91
- * reads and record decryption, of which the AEAD itself is under 2 ms.
92
+ * @param {number} [pullBytes] size of each BYOB view pulled from the source. Tunable because the
93
+ * right value depends on how much the transport hands over per read, and that differs by a
94
+ * factor of four between a direct socket and a proxied onesee BYOB_PULL_BYTES for the sweep.
95
+ * Ignored on sources that are not byte streams, which take the default-reader path.
92
96
  */
93
97
  constructor(readable: ReadableStream<Uint8Array>, pullBytes?: number);
94
98
  _pullBytes: number;