stealth-fetch 0.1.2

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.

Potentially problematic release.


This version of stealth-fetch might be problematic. Click here for more details.

Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +183 -0
  3. package/dist/client.d.ts +99 -0
  4. package/dist/client.js +1117 -0
  5. package/dist/compat/web.d.ts +15 -0
  6. package/dist/compat/web.js +31 -0
  7. package/dist/connection-pool.d.ts +39 -0
  8. package/dist/connection-pool.js +84 -0
  9. package/dist/dns-cache.d.ts +25 -0
  10. package/dist/dns-cache.js +44 -0
  11. package/dist/http1/chunked.d.ts +35 -0
  12. package/dist/http1/chunked.js +87 -0
  13. package/dist/http1/client.d.ts +28 -0
  14. package/dist/http1/client.js +289 -0
  15. package/dist/http1/parser.d.ts +29 -0
  16. package/dist/http1/parser.js +78 -0
  17. package/dist/http2/client.d.ts +64 -0
  18. package/dist/http2/client.js +97 -0
  19. package/dist/http2/connection.d.ts +125 -0
  20. package/dist/http2/connection.js +666 -0
  21. package/dist/http2/constants.d.ts +72 -0
  22. package/dist/http2/constants.js +74 -0
  23. package/dist/http2/flow-control.d.ts +32 -0
  24. package/dist/http2/flow-control.js +76 -0
  25. package/dist/http2/framer.d.ts +47 -0
  26. package/dist/http2/framer.js +133 -0
  27. package/dist/http2/hpack.d.ts +54 -0
  28. package/dist/http2/hpack.js +186 -0
  29. package/dist/http2/parser.d.ts +35 -0
  30. package/dist/http2/parser.js +72 -0
  31. package/dist/http2/stream.d.ts +72 -0
  32. package/dist/http2/stream.js +252 -0
  33. package/dist/index.d.ts +18 -0
  34. package/dist/index.js +33 -0
  35. package/dist/protocol-cache.d.ts +14 -0
  36. package/dist/protocol-cache.js +29 -0
  37. package/dist/socket/adapter.d.ts +59 -0
  38. package/dist/socket/adapter.js +145 -0
  39. package/dist/socket/nat64.d.ts +69 -0
  40. package/dist/socket/nat64.js +196 -0
  41. package/dist/socket/tls.d.ts +28 -0
  42. package/dist/socket/tls.js +33 -0
  43. package/dist/socket/wasm-pkg/wasm_tls.d.ts +107 -0
  44. package/dist/socket/wasm-pkg/wasm_tls.js +568 -0
  45. package/dist/socket/wasm-pkg/wasm_tls_bg.wasm +0 -0
  46. package/dist/socket/wasm-pkg/wasm_tls_bg.wasm.d.ts +20 -0
  47. package/dist/socket/wasm-tls-adapter.d.ts +40 -0
  48. package/dist/socket/wasm-tls-adapter.js +102 -0
  49. package/dist/socket/wasm-tls-bridge.d.ts +30 -0
  50. package/dist/socket/wasm-tls-bridge.js +187 -0
  51. package/dist/utils/headers.d.ts +21 -0
  52. package/dist/utils/headers.js +36 -0
  53. package/dist/utils/url.d.ts +16 -0
  54. package/dist/utils/url.js +12 -0
  55. package/package.json +87 -0
@@ -0,0 +1,145 @@
1
+ import { Duplex } from "node:stream";
2
+ class CloudflareSocketAdapter extends Duplex {
3
+ reader = null;
4
+ writer = null;
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ cfSocket = null;
7
+ reading = false;
8
+ connected = false;
9
+ hostname;
10
+ port;
11
+ useTls;
12
+ constructor(options) {
13
+ super();
14
+ this.hostname = options.hostname;
15
+ this.port = options.port;
16
+ this.useTls = options.tls;
17
+ }
18
+ /**
19
+ * Establish the underlying cloudflare:sockets connection.
20
+ * Must be called before using the stream.
21
+ */
22
+ async connect() {
23
+ const { connect } = await import("cloudflare:sockets");
24
+ const address = { hostname: this.hostname, port: this.port };
25
+ console.debug(`[socket] connect(${this.hostname}:${this.port} tls=${this.useTls})`);
26
+ this.cfSocket = this.useTls ? connect(address, { secureTransport: "on" }) : connect(address);
27
+ this.writer = this.cfSocket.writable.getWriter();
28
+ this.reader = this.cfSocket.readable.getReader();
29
+ this.connected = true;
30
+ this.cfSocket.closed.then(() => {
31
+ console.debug(`[socket] closed(${this.hostname}:${this.port}) destroyed=${this.destroyed}`);
32
+ this.connected = false;
33
+ if (!this.destroyed) {
34
+ this.push(null);
35
+ this.emit("close");
36
+ }
37
+ }).catch((err) => {
38
+ console.debug(
39
+ `[socket] closed-error(${this.hostname}:${this.port}) destroyed=${this.destroyed} err=${err.message}`
40
+ );
41
+ this.connected = false;
42
+ if (!this.destroyed) {
43
+ this.destroy(err);
44
+ }
45
+ });
46
+ this.emit("connect");
47
+ }
48
+ /**
49
+ * Node.js Duplex _read: pull data from the CF socket's ReadableStream.
50
+ * Starts a read loop that pushes data into the Duplex readable side.
51
+ */
52
+ _read() {
53
+ if (this.reading || !this.reader) return;
54
+ this.reading = true;
55
+ this.readLoop();
56
+ }
57
+ async readLoop() {
58
+ const reader = this.reader;
59
+ if (!reader) {
60
+ this.reading = false;
61
+ return;
62
+ }
63
+ try {
64
+ while (this.reading) {
65
+ const { done, value } = await reader.read();
66
+ if (done) {
67
+ this.reading = false;
68
+ this.push(null);
69
+ return;
70
+ }
71
+ if (!this.push(value)) {
72
+ this.reading = false;
73
+ return;
74
+ }
75
+ }
76
+ } catch (err) {
77
+ this.reading = false;
78
+ if (!this.destroyed) {
79
+ this.destroy(err);
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Node.js Duplex _write: write data to the CF socket's WritableStream.
85
+ */
86
+ _write(chunk, _encoding, callback) {
87
+ if (!this.writer || !this.connected) {
88
+ callback(new Error("Socket not connected"));
89
+ return;
90
+ }
91
+ const data = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
92
+ this.writer.write(data).then(
93
+ () => callback(),
94
+ (err) => callback(err)
95
+ );
96
+ }
97
+ /**
98
+ * Node.js Duplex _final: signal end of writes.
99
+ */
100
+ _final(callback) {
101
+ if (this.writer) {
102
+ this.writer.close().then(
103
+ () => callback(),
104
+ (err) => callback(err)
105
+ );
106
+ } else {
107
+ callback();
108
+ }
109
+ }
110
+ /**
111
+ * Node.js Duplex _destroy: clean up all resources.
112
+ * Uses synchronous close() calls to avoid leaving pending promises
113
+ * that can corrupt CF Workers isolate state.
114
+ */
115
+ _destroy(error, callback) {
116
+ console.debug(
117
+ `[socket] _destroy(${this.hostname}:${this.port}) error=${error?.message ?? "none"}`
118
+ );
119
+ this.reading = false;
120
+ this.connected = false;
121
+ if (this.reader) {
122
+ this.reader.cancel().catch(() => {
123
+ });
124
+ this.reader = null;
125
+ }
126
+ if (this.writer) {
127
+ this.writer.abort().catch(() => {
128
+ });
129
+ this.writer = null;
130
+ }
131
+ if (this.cfSocket) {
132
+ this.cfSocket.close().catch(() => {
133
+ });
134
+ this.cfSocket = null;
135
+ }
136
+ callback(error);
137
+ }
138
+ /** Whether the socket is currently connected */
139
+ get isConnected() {
140
+ return this.connected;
141
+ }
142
+ }
143
+ export {
144
+ CloudflareSocketAdapter
145
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * NAT64 address resolution for bypassing Cloudflare Workers
3
+ * outbound socket restrictions.
4
+ *
5
+ * Converts IPv4 targets to NAT64 IPv6 addresses using public NAT64 gateways.
6
+ * Prefixes verified via RFC 7050 (querying DNS64 servers for ipv4only.arpa).
7
+ */
8
+ /**
9
+ * NAT64 /96 prefixes, ordered by reliability.
10
+ * Format: full expanded prefix ending with ":" (no trailing "::").
11
+ * The last 32 bits (2 hextets) are filled with the embedded IPv4 address.
12
+ *
13
+ * To convert: prefix + hex(octet1)hex(octet2):hex(octet3)hex(octet4)
14
+ * Example: "2602:fc59:b0:64::" + 108.160.165.8 → [2602:fc59:b0:64::6ca0:a508]
15
+ */
16
+ declare const NAT64_PREFIXES: string[];
17
+
18
+ interface DnsARecord {
19
+ ipv4: string;
20
+ ttl: number;
21
+ }
22
+ /**
23
+ * Resolve hostname to IPv4 via DNS-over-HTTPS (Cloudflare 1.1.1.1).
24
+ * Uses fetch() which is always available in CF Workers.
25
+ */
26
+ declare function resolveIPv4(hostname: string): Promise<DnsARecord | null>;
27
+ /**
28
+ * Convert IPv4 string to NAT64 IPv6 address (bracketed for connect()).
29
+ *
30
+ * Handles two prefix formats:
31
+ * - Ending with "::" (short prefix, e.g. "2602:fc59:b0:64::")
32
+ * → [2602:fc59:b0:64::6ca0:a508]
33
+ * - Ending with ":" (full prefix, e.g. "2a00:1098:2b:0:0:1:")
34
+ * → [2a00:1098:2b:0:0:1:6ca0:a508]
35
+ */
36
+ declare function ipv4ToNAT64(ipv4: string, prefix: string): string;
37
+ interface BypassResult {
38
+ strategy: "nat64";
39
+ /** The hostname to pass to connect() */
40
+ connectHostname: string;
41
+ /** NAT64 prefix used */
42
+ nat64Prefix: string;
43
+ /** Original IPv4 resolved from DNS */
44
+ resolvedIPv4: string;
45
+ }
46
+ /**
47
+ * Generate NAT64 bypass hostname candidates for a given domain.
48
+ * Returns an array of { connectHostname, nat64Prefix } to try in order.
49
+ */
50
+ declare function generateBypassCandidates(hostname: string): Promise<BypassResult[]>;
51
+ /** Check if an error is a CF Workers network restriction error */
52
+ declare function isCloudflareNetworkError(err: unknown): boolean;
53
+ declare function isCloudflareIPv4(ipv4: string): boolean;
54
+ declare function isCloudflareIPv6(ipv6: string): boolean;
55
+ interface CfCheckResult {
56
+ isCf: boolean;
57
+ ipv4: string | null;
58
+ ipv6: string | null;
59
+ dnsMs: number;
60
+ /** Minimum TTL from A/AAAA records (seconds). 0 if no records found. */
61
+ ttl: number;
62
+ }
63
+ /**
64
+ * Resolve hostname via DoH (parallel A + AAAA), check if IP is in CF ranges.
65
+ * Used to pre-detect CF CDN targets that need NAT64 bypass.
66
+ */
67
+ declare function resolveAndCheckCloudflare(hostname: string): Promise<CfCheckResult>;
68
+
69
+ export { type BypassResult, type CfCheckResult, type DnsARecord, NAT64_PREFIXES, generateBypassCandidates, ipv4ToNAT64, isCloudflareIPv4, isCloudflareIPv6, isCloudflareNetworkError, resolveAndCheckCloudflare, resolveIPv4 };
@@ -0,0 +1,196 @@
1
+ const NAT64_PREFIXES = [
2
+ // === Verified working from CF Workers (deploy tested) ===
3
+ "2602:fc59:b0:64::",
4
+ // ZTVI/ForwardingPlane, Fremont CA, USA — 12ms
5
+ "2602:fc59:11:64::",
6
+ // ZTVI/ForwardingPlane, Chicago, USA — 60ms
7
+ "2a00:1098:2b:0:0:1:",
8
+ // Kasper Dupont (nat64.net), Amsterdam — 150ms
9
+ "2a00:1098:2c:0:0:5:",
10
+ // Kasper Dupont (nat64.net), London — 140ms
11
+ "2a02:898:146:64::",
12
+ // IPng Networks, Netherlands — 155ms
13
+ "2001:67c:2b0:db32::",
14
+ // Trex, Tampere, Finland — 195ms
15
+ // === Additional ZTVI/ForwardingPlane prefixes ===
16
+ "2602:fc59:20::",
17
+ // ZTVI/ForwardingPlane, unknown location
18
+ // === Kasper Dupont (nat64.net) additional locations ===
19
+ "2a00:1098:2c:1::",
20
+ // nat64.net, London (official /96 prefix)
21
+ "2a01:4f8:c2c:123f:64:5:",
22
+ // nat64.net, Nuremberg, Germany
23
+ "2a01:4f8:c2c:123f:64::",
24
+ // nat64.net, Nuremberg (alternate form)
25
+ "2a01:4f9:c010:3f02:64:0:",
26
+ // nat64.net, Helsinki, Finland
27
+ "2a01:4f9:c010:3f02:64::",
28
+ // nat64.net, Helsinki (alternate form)
29
+ // === level66.network ===
30
+ "2001:67c:2960:6464::",
31
+ // level66.network, Germany (Anycast)
32
+ "2a09:11c0:f1:be00::",
33
+ // level66.network, Frankfurt
34
+ // === go6Labs, Slovenia ===
35
+ "2001:67c:27e4:642::",
36
+ // go6Labs, Slovenia
37
+ "2001:67c:27e4:64::",
38
+ // go6Labs, Slovenia
39
+ "2001:67c:27e4:1064::",
40
+ // go6Labs, Slovenia
41
+ "2001:67c:27e4:11::",
42
+ // go6Labs, Slovenia
43
+ // === Other providers ===
44
+ "2a03:7900:6446::",
45
+ // Tuxis, Netherlands
46
+ "2001:67c:2b0:db32:0:1:"
47
+ // Trex, second prefix, Finland
48
+ ];
49
+ async function resolveIPv4(hostname) {
50
+ const resp = await fetch(
51
+ `https://1.1.1.1/dns-query?name=${encodeURIComponent(hostname)}&type=A`,
52
+ { headers: { Accept: "application/dns-json" }, signal: AbortSignal.timeout(3e3) }
53
+ );
54
+ if (!resp.ok) return null;
55
+ const data = await resp.json();
56
+ if (!data.Answer) return null;
57
+ const aRecord = data.Answer.find((r) => r.type === 1);
58
+ if (!aRecord) return null;
59
+ return { ipv4: aRecord.data, ttl: aRecord.TTL };
60
+ }
61
+ function ipv4ToNAT64(ipv4, prefix) {
62
+ const parts = ipv4.split(".");
63
+ if (parts.length !== 4) throw new Error(`Invalid IPv4: ${ipv4}`);
64
+ const hex = parts.map((p) => {
65
+ const n = parseInt(p, 10);
66
+ if (n < 0 || n > 255) throw new Error(`Invalid IPv4 octet: ${p}`);
67
+ return n.toString(16).padStart(2, "0");
68
+ });
69
+ const suffix = `${hex[0]}${hex[1]}:${hex[2]}${hex[3]}`;
70
+ return `[${prefix}${suffix}]`;
71
+ }
72
+ async function generateBypassCandidates(hostname) {
73
+ const dns = await resolveIPv4(hostname);
74
+ if (!dns) return [];
75
+ return NAT64_PREFIXES.map((prefix) => ({
76
+ strategy: "nat64",
77
+ connectHostname: ipv4ToNAT64(dns.ipv4, prefix),
78
+ nat64Prefix: prefix,
79
+ resolvedIPv4: dns.ipv4
80
+ }));
81
+ }
82
+ function isCloudflareNetworkError(err) {
83
+ const msg = err instanceof Error ? err.message : String(err);
84
+ return msg.includes("cannot connect to the specified address") || msg.includes("A network issue was detected") || msg.includes("TCP Loop detected");
85
+ }
86
+ const CF_IPV4_RANGES = (() => {
87
+ const toU32 = (ip) => {
88
+ const p = ip.split(".").map(Number);
89
+ return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]) >>> 0;
90
+ };
91
+ return [
92
+ [toU32("173.245.48.0"), toU32("173.245.63.255")],
93
+ // /20
94
+ [toU32("103.21.244.0"), toU32("103.21.247.255")],
95
+ // /22
96
+ [toU32("103.22.200.0"), toU32("103.22.203.255")],
97
+ // /22
98
+ [toU32("103.31.4.0"), toU32("103.31.7.255")],
99
+ // /22
100
+ [toU32("141.101.64.0"), toU32("141.101.127.255")],
101
+ // /18
102
+ [toU32("108.162.192.0"), toU32("108.162.255.255")],
103
+ // /18
104
+ [toU32("190.93.240.0"), toU32("190.93.255.255")],
105
+ // /20
106
+ [toU32("188.114.96.0"), toU32("188.114.111.255")],
107
+ // /20
108
+ [toU32("197.234.240.0"), toU32("197.234.243.255")],
109
+ // /22
110
+ [toU32("198.41.128.0"), toU32("198.41.255.255")],
111
+ // /17
112
+ [toU32("162.158.0.0"), toU32("162.159.255.255")],
113
+ // /15
114
+ [toU32("104.16.0.0"), toU32("104.27.255.255")],
115
+ // /12
116
+ [toU32("172.64.0.0"), toU32("172.71.255.255")],
117
+ // /13
118
+ [toU32("131.0.72.0"), toU32("131.0.75.255")]
119
+ // /22
120
+ ];
121
+ })();
122
+ const CF_IPV6_PREFIXES = [
123
+ "2400:cb00",
124
+ // 2400:cb00::/32
125
+ "2606:4700",
126
+ // 2606:4700::/32
127
+ "2803:f800",
128
+ // 2803:f800::/32
129
+ "2405:8100",
130
+ // 2405:8100::/32
131
+ "2a06:98c0",
132
+ // 2a06:98c0::/29
133
+ "2c0f:f248"
134
+ // 2c0f:f248::/32
135
+ ];
136
+ function ipv4ToUint32(ip) {
137
+ const p = ip.split(".").map(Number);
138
+ return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]) >>> 0;
139
+ }
140
+ function isCloudflareIPv4(ipv4) {
141
+ const num = ipv4ToUint32(ipv4);
142
+ return CF_IPV4_RANGES.some(([start, end]) => num >= start && num <= end);
143
+ }
144
+ function isCloudflareIPv6(ipv6) {
145
+ const lower = ipv6.toLowerCase();
146
+ return CF_IPV6_PREFIXES.some((prefix) => lower.startsWith(prefix));
147
+ }
148
+ async function resolveAndCheckCloudflare(hostname) {
149
+ const t0 = Date.now();
150
+ const dnsSignal = AbortSignal.timeout(3e3);
151
+ const [aResp, aaaaResp] = await Promise.all([
152
+ fetch(`https://1.1.1.1/dns-query?name=${encodeURIComponent(hostname)}&type=A`, {
153
+ headers: { Accept: "application/dns-json" },
154
+ signal: dnsSignal
155
+ }),
156
+ fetch(`https://1.1.1.1/dns-query?name=${encodeURIComponent(hostname)}&type=AAAA`, {
157
+ headers: { Accept: "application/dns-json" },
158
+ signal: dnsSignal
159
+ })
160
+ ]);
161
+ const dnsMs = Date.now() - t0;
162
+ let ipv4 = null;
163
+ let ipv6 = null;
164
+ let isCf = false;
165
+ const ttls = [];
166
+ if (aResp.ok) {
167
+ const data = await aResp.json();
168
+ const aRecord = data.Answer?.find((r) => r.type === 1);
169
+ if (aRecord) {
170
+ ipv4 = aRecord.data;
171
+ ttls.push(aRecord.TTL);
172
+ if (isCloudflareIPv4(ipv4)) isCf = true;
173
+ }
174
+ }
175
+ if (aaaaResp.ok) {
176
+ const data = await aaaaResp.json();
177
+ const aaaaRecord = data.Answer?.find((r) => r.type === 28);
178
+ if (aaaaRecord) {
179
+ ipv6 = aaaaRecord.data;
180
+ ttls.push(aaaaRecord.TTL);
181
+ if (isCloudflareIPv6(ipv6)) isCf = true;
182
+ }
183
+ }
184
+ const ttl = ttls.length > 0 ? Math.min(...ttls) : 0;
185
+ return { isCf, ipv4, ipv6, dnsMs, ttl };
186
+ }
187
+ export {
188
+ NAT64_PREFIXES,
189
+ generateBypassCandidates,
190
+ ipv4ToNAT64,
191
+ isCloudflareIPv4,
192
+ isCloudflareIPv6,
193
+ isCloudflareNetworkError,
194
+ resolveAndCheckCloudflare,
195
+ resolveIPv4
196
+ };
@@ -0,0 +1,28 @@
1
+ import { CloudflareSocketAdapter } from './adapter.js';
2
+ import { WasmTlsSocketAdapter } from './wasm-tls-adapter.js';
3
+ import 'node:stream';
4
+
5
+ /**
6
+ * TLS/TCP connection factory functions.
7
+ * Creates CloudflareSocketAdapter instances with appropriate TLS settings.
8
+ */
9
+
10
+ /** Create a TLS-encrypted socket connection (using CF built-in TLS, no ALPN control) */
11
+ declare function createTLSSocket(hostname: string, port?: number): Promise<CloudflareSocketAdapter>;
12
+ /** Create a plain TCP socket connection (no TLS) */
13
+ declare function createPlainSocket(hostname: string, port?: number): Promise<CloudflareSocketAdapter>;
14
+ /** Create a socket with auto TLS based on port/protocol */
15
+ declare function createSocket(hostname: string, port: number, tls: boolean): Promise<CloudflareSocketAdapter>;
16
+ /**
17
+ * Create a WASM TLS socket with full ALPN control.
18
+ * Uses raw TCP (secureTransport: "off") + rustls in WASM for TLS.
19
+ * This allows ALPN negotiation (required for HTTP/2 over TLS).
20
+ * @param hostname - Target hostname (used for TLS SNI)
21
+ * @param port - Target port
22
+ * @param alpnProtocols - ALPN protocol list
23
+ * @param connectHostname - Optional override for TCP connection hostname
24
+ * (e.g. NAT64 IPv6 address). TLS SNI will still use `hostname`.
25
+ */
26
+ declare function createWasmTLSSocket(hostname: string, port?: number, alpnProtocols?: string[], connectHostname?: string, signal?: AbortSignal): Promise<WasmTlsSocketAdapter>;
27
+
28
+ export { createPlainSocket, createSocket, createTLSSocket, createWasmTLSSocket };
@@ -0,0 +1,33 @@
1
+ import { CloudflareSocketAdapter } from "./adapter.js";
2
+ import { WasmTlsSocketAdapter } from "./wasm-tls-adapter.js";
3
+ async function createTLSSocket(hostname, port = 443) {
4
+ const socket = new CloudflareSocketAdapter({ hostname, port, tls: true });
5
+ await socket.connect();
6
+ return socket;
7
+ }
8
+ async function createPlainSocket(hostname, port = 80) {
9
+ const socket = new CloudflareSocketAdapter({ hostname, port, tls: false });
10
+ await socket.connect();
11
+ return socket;
12
+ }
13
+ async function createSocket(hostname, port, tls) {
14
+ const socket = new CloudflareSocketAdapter({ hostname, port, tls });
15
+ await socket.connect();
16
+ return socket;
17
+ }
18
+ async function createWasmTLSSocket(hostname, port = 443, alpnProtocols = ["h2", "http/1.1"], connectHostname, signal) {
19
+ const socket = new WasmTlsSocketAdapter({
20
+ hostname,
21
+ port,
22
+ alpnProtocols,
23
+ connectHostname
24
+ });
25
+ await socket.connect(signal);
26
+ return socket;
27
+ }
28
+ export {
29
+ createPlainSocket,
30
+ createSocket,
31
+ createTLSSocket,
32
+ createWasmTLSSocket
33
+ };
@@ -0,0 +1,107 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * TLS connection state, exposed to JS via wasm-bindgen.
6
+ * Uses rustls with buffer-based sync IO — JS layer drives socket IO asynchronously.
7
+ */
8
+ export class TlsConnection {
9
+ free(): void;
10
+ [Symbol.dispose](): void;
11
+ /**
12
+ * Feed ciphertext received from the network into the TLS engine.
13
+ * Returns true if rustls has outgoing data to send (call `flush_outgoing_tls`).
14
+ */
15
+ feed_ciphertext(data: Uint8Array): boolean;
16
+ /**
17
+ * Flush ciphertext produced by rustls (to be sent over the network).
18
+ * Returns the ciphertext bytes as a Vec<u8> (becomes Uint8Array in JS).
19
+ */
20
+ flush_outgoing_tls(): Uint8Array;
21
+ /**
22
+ * Whether the TLS handshake is still in progress.
23
+ */
24
+ is_handshaking(): boolean;
25
+ /**
26
+ * Get the negotiated ALPN protocol (e.g. "h2" or "http/1.1").
27
+ * Returns null if no ALPN was negotiated.
28
+ */
29
+ negotiated_alpn(): string | undefined;
30
+ /**
31
+ * Create a new TLS client connection.
32
+ * `hostname`: server hostname for SNI
33
+ * `alpn_protocols`: comma-separated ALPN protocol list, e.g. "h2,http/1.1"
34
+ */
35
+ constructor(hostname: string, alpn_protocols: string);
36
+ /**
37
+ * Send a TLS close_notify alert.
38
+ */
39
+ send_close_notify(): void;
40
+ /**
41
+ * Take decrypted plaintext data (for the upper layer to consume).
42
+ */
43
+ take_plaintext(): Uint8Array;
44
+ /**
45
+ * Whether rustls needs more data from the network.
46
+ */
47
+ wants_read(): boolean;
48
+ /**
49
+ * Whether rustls has data to write to the network.
50
+ */
51
+ wants_write(): boolean;
52
+ /**
53
+ * Write plaintext data (from the upper layer) into the TLS engine for encryption.
54
+ * Returns true if rustls has outgoing data to send.
55
+ */
56
+ write_plaintext(data: Uint8Array): boolean;
57
+ }
58
+
59
+ /**
60
+ * Get the library version string (for verification).
61
+ */
62
+ export function wasm_tls_version(): string;
63
+
64
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
65
+
66
+ export interface InitOutput {
67
+ readonly memory: WebAssembly.Memory;
68
+ readonly __wbg_tlsconnection_free: (a: number, b: number) => void;
69
+ readonly tlsconnection_feed_ciphertext: (a: number, b: number, c: number, d: number) => void;
70
+ readonly tlsconnection_flush_outgoing_tls: (a: number, b: number) => void;
71
+ readonly tlsconnection_is_handshaking: (a: number) => number;
72
+ readonly tlsconnection_negotiated_alpn: (a: number, b: number) => void;
73
+ readonly tlsconnection_new: (a: number, b: number, c: number, d: number, e: number) => void;
74
+ readonly tlsconnection_send_close_notify: (a: number) => void;
75
+ readonly tlsconnection_take_plaintext: (a: number, b: number) => void;
76
+ readonly tlsconnection_wants_read: (a: number) => number;
77
+ readonly tlsconnection_wants_write: (a: number) => number;
78
+ readonly tlsconnection_write_plaintext: (a: number, b: number, c: number, d: number) => void;
79
+ readonly wasm_tls_version: (a: number) => void;
80
+ readonly __wbindgen_export: (a: number) => void;
81
+ readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
82
+ readonly __wbindgen_export2: (a: number, b: number) => number;
83
+ readonly __wbindgen_export3: (a: number, b: number, c: number) => void;
84
+ readonly __wbindgen_export4: (a: number, b: number, c: number, d: number) => number;
85
+ }
86
+
87
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
88
+
89
+ /**
90
+ * Instantiates the given `module`, which can either be bytes or
91
+ * a precompiled `WebAssembly.Module`.
92
+ *
93
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
94
+ *
95
+ * @returns {InitOutput}
96
+ */
97
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
98
+
99
+ /**
100
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
101
+ * for everything else, calls `WebAssembly.instantiate` directly.
102
+ *
103
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
104
+ *
105
+ * @returns {Promise<InitOutput>}
106
+ */
107
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;