@npy/fetch 0.1.0 → 0.1.1

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.
Files changed (98) hide show
  1. package/_internal/consts.cjs +4 -0
  2. package/_internal/consts.js +4 -0
  3. package/_internal/error-adapters.cjs +146 -0
  4. package/_internal/error-adapters.js +142 -0
  5. package/_internal/guards.cjs +24 -0
  6. package/_internal/guards.js +17 -0
  7. package/_internal/net.cjs +95 -0
  8. package/_internal/net.js +92 -0
  9. package/_internal/promises.cjs +18 -0
  10. package/_internal/promises.js +18 -0
  11. package/_internal/streams.cjs +37 -0
  12. package/_internal/streams.js +36 -0
  13. package/_virtual/_rolldown/runtime.cjs +23 -0
  14. package/agent-pool.cjs +78 -0
  15. package/agent-pool.js +77 -0
  16. package/agent.cjs +257 -0
  17. package/agent.js +256 -0
  18. package/body.cjs +154 -0
  19. package/body.js +151 -0
  20. package/dialers/proxy.cjs +49 -0
  21. package/dialers/proxy.js +48 -0
  22. package/dialers/tcp.cjs +70 -0
  23. package/dialers/tcp.js +67 -0
  24. package/encoding.cjs +95 -0
  25. package/encoding.js +91 -0
  26. package/errors.cjs +275 -0
  27. package/errors.js +259 -0
  28. package/fetch.cjs +117 -0
  29. package/fetch.js +115 -0
  30. package/http-client.cjs +33 -0
  31. package/http-client.js +33 -0
  32. package/index.cjs +45 -0
  33. package/index.d.cts +1 -0
  34. package/index.d.ts +1 -0
  35. package/index.js +9 -0
  36. package/io/_utils.cjs +56 -0
  37. package/io/_utils.js +51 -0
  38. package/io/buf-writer.cjs +149 -0
  39. package/io/buf-writer.js +148 -0
  40. package/io/io.cjs +135 -0
  41. package/io/io.js +134 -0
  42. package/io/readers.cjs +377 -0
  43. package/io/readers.js +373 -0
  44. package/io/writers.cjs +191 -0
  45. package/io/writers.js +190 -0
  46. package/package.json +7 -10
  47. package/src/_internal/consts.d.cts +3 -0
  48. package/src/_internal/consts.d.ts +3 -0
  49. package/src/_internal/error-adapters.d.cts +22 -0
  50. package/src/_internal/error-adapters.d.ts +22 -0
  51. package/src/_internal/guards.d.cts +13 -0
  52. package/src/_internal/guards.d.ts +13 -0
  53. package/src/_internal/net.d.cts +12 -0
  54. package/src/_internal/net.d.ts +12 -0
  55. package/src/_internal/promises.d.cts +1 -0
  56. package/src/_internal/promises.d.ts +1 -0
  57. package/src/_internal/streams.d.cts +21 -0
  58. package/src/_internal/streams.d.ts +21 -0
  59. package/src/agent-pool.d.cts +2 -0
  60. package/src/agent-pool.d.ts +2 -0
  61. package/src/agent.d.cts +3 -0
  62. package/src/agent.d.ts +3 -0
  63. package/src/body.d.cts +23 -0
  64. package/src/body.d.ts +23 -0
  65. package/src/dialers/index.d.cts +3 -0
  66. package/src/dialers/index.d.ts +3 -0
  67. package/src/dialers/proxy.d.cts +19 -0
  68. package/src/dialers/proxy.d.ts +19 -0
  69. package/src/dialers/tcp.d.cts +36 -0
  70. package/src/dialers/tcp.d.ts +36 -0
  71. package/src/encoding.d.cts +24 -0
  72. package/src/encoding.d.ts +24 -0
  73. package/src/errors.d.cts +110 -0
  74. package/src/errors.d.ts +110 -0
  75. package/src/fetch.d.cts +36 -0
  76. package/src/fetch.d.ts +36 -0
  77. package/src/http-client.d.cts +23 -0
  78. package/src/http-client.d.ts +23 -0
  79. package/src/index.d.cts +7 -0
  80. package/src/index.d.ts +7 -0
  81. package/src/io/_utils.d.cts +10 -0
  82. package/src/io/_utils.d.ts +10 -0
  83. package/src/io/buf-writer.d.cts +13 -0
  84. package/src/io/buf-writer.d.ts +13 -0
  85. package/src/io/io.d.cts +5 -0
  86. package/src/io/io.d.ts +5 -0
  87. package/src/io/readers.d.cts +199 -0
  88. package/src/io/readers.d.ts +199 -0
  89. package/src/io/writers.d.cts +22 -0
  90. package/src/io/writers.d.ts +22 -0
  91. package/src/types/agent.d.cts +128 -0
  92. package/src/types/agent.d.ts +128 -0
  93. package/src/types/dialer.d.cts +27 -0
  94. package/src/types/dialer.d.ts +27 -0
  95. package/src/types/index.d.cts +2 -0
  96. package/src/types/index.d.ts +2 -0
  97. package/tests/test-utils.d.cts +8 -0
  98. package/tests/test-utils.d.ts +8 -0
package/body.cjs ADDED
@@ -0,0 +1,154 @@
1
+ require("./_virtual/_rolldown/runtime.cjs");
2
+ const require_guards = require("./_internal/guards.cjs");
3
+ let _fuman_node = require("@fuman/node");
4
+ let node_crypto = require("node:crypto");
5
+ let node_stream = require("node:stream");
6
+ let node_util_types = require("node:util/types");
7
+ let _fuman_utils = require("@fuman/utils");
8
+ //#region src/body.ts
9
+ var BOUNDARY = "-".repeat(2);
10
+ var makeFormBoundary = () => `formdata-${(0, node_crypto.randomBytes)(8).toString("hex")}`;
11
+ var getFormHeader = (boundary, name, field) => {
12
+ let header = `${BOUNDARY}${boundary}\r
13
+ `;
14
+ header += `Content-Disposition: form-data; name="${name}"`;
15
+ if (require_guards.isBlob(field)) {
16
+ header += `; filename="${field.name ?? "blob"}"\r
17
+ `;
18
+ header += `Content-Type: ${field.type || "application/octet-stream"}`;
19
+ }
20
+ return `${header}\r
21
+ \r
22
+ `;
23
+ };
24
+ var getFormFooter = (boundary) => `${BOUNDARY}${boundary}${BOUNDARY}\r
25
+ \r
26
+ `;
27
+ var getFormDataLength = (form, boundary) => {
28
+ let length = Buffer.byteLength(getFormFooter(boundary));
29
+ for (const [name, value] of form) length += Buffer.byteLength(getFormHeader(boundary, name, value)) + (require_guards.isBlob(value) ? value.size : Buffer.byteLength(`${value}`)) + 2;
30
+ return length;
31
+ };
32
+ async function* generatorOfFormData(form, boundary) {
33
+ for (const [name, value] of form) if (require_guards.isBlob(value)) {
34
+ yield _fuman_utils.utf8.encoder.encode(getFormHeader(boundary, name, value));
35
+ for await (const chunk of value.stream()) yield chunk;
36
+ yield _fuman_utils.utf8.encoder.encode("\r\n");
37
+ } else yield _fuman_utils.utf8.encoder.encode(getFormHeader(boundary, name, value) + value + "\r\n");
38
+ yield _fuman_utils.utf8.encoder.encode(getFormFooter(boundary));
39
+ }
40
+ var extractBody = (object) => {
41
+ let type = null;
42
+ let body;
43
+ let size = null;
44
+ if (object == null) {
45
+ body = null;
46
+ size = 0;
47
+ } else if (typeof object === "string") {
48
+ const bytes = _fuman_utils.utf8.encoder.encode(`${object}`);
49
+ type = "text/plain;charset=UTF-8";
50
+ size = bytes.byteLength;
51
+ body = bytes;
52
+ } else if (require_guards.isURLSearchParameters(object)) {
53
+ const bytes = _fuman_utils.utf8.encoder.encode(object.toString());
54
+ body = bytes;
55
+ size = bytes.byteLength;
56
+ type = "application/x-www-form-urlencoded;charset=UTF-8";
57
+ } else if (require_guards.isBlob(object)) {
58
+ size = object.size;
59
+ type = object.type || null;
60
+ body = object.stream();
61
+ } else if (object instanceof Uint8Array) {
62
+ body = object;
63
+ size = object.byteLength;
64
+ } else if ((0, node_util_types.isAnyArrayBuffer)(object)) {
65
+ const bytes = new Uint8Array(object);
66
+ body = bytes;
67
+ size = bytes.byteLength;
68
+ } else if (ArrayBuffer.isView(object)) {
69
+ const bytes = new Uint8Array(object.buffer, object.byteOffset, object.byteLength);
70
+ body = bytes;
71
+ size = bytes.byteLength;
72
+ } else if (require_guards.isReadableStream(object)) body = object;
73
+ else if (require_guards.isFormData(object)) {
74
+ const boundary = makeFormBoundary();
75
+ type = `multipart/form-data; boundary=${boundary}`;
76
+ size = getFormDataLength(object, boundary);
77
+ body = node_stream.Readable.from(generatorOfFormData(object, boundary));
78
+ } else if (require_guards.isMultipartFormDataStream(object)) {
79
+ type = `multipart/form-data; boundary=${object.getBoundary()}`;
80
+ size = object.hasKnownLength() ? object.getLengthSync() : null;
81
+ body = object;
82
+ } else if (require_guards.isReadable(object)) body = object;
83
+ else if (require_guards.isIterable(object)) body = node_stream.Readable.from(object);
84
+ else {
85
+ const bytes = _fuman_utils.utf8.encoder.encode(`${object}`);
86
+ type = "text/plain;charset=UTF-8";
87
+ body = bytes;
88
+ size = bytes.byteLength;
89
+ }
90
+ return {
91
+ contentLength: size,
92
+ contentType: type,
93
+ body
94
+ };
95
+ };
96
+ var kBodyInternals = Symbol("kBodyInternals");
97
+ var toWebBodyInit = (body) => {
98
+ if (body == null) return null;
99
+ if (require_guards.isReadable(body)) return (0, _fuman_node.nodeReadableToWeb)(body);
100
+ return body;
101
+ };
102
+ var bytesToArrayBuffer = (bytes) => {
103
+ const bytesAsArrayBuffer = new ArrayBuffer(bytes.byteLength);
104
+ new Uint8Array(bytesAsArrayBuffer).set(bytes);
105
+ return bytesAsArrayBuffer;
106
+ };
107
+ var Body = class {
108
+ [kBodyInternals];
109
+ constructor(init) {
110
+ this[kBodyInternals] = extractBody(init);
111
+ }
112
+ get body() {
113
+ return this[kBodyInternals].body;
114
+ }
115
+ get bodyUsed() {
116
+ const { body } = this[kBodyInternals];
117
+ if (require_guards.isReadable(body)) return node_stream.Readable.isDisturbed(body);
118
+ if (require_guards.isReadableStream(body)) return body.locked;
119
+ return false;
120
+ }
121
+ async arrayBuffer() {
122
+ const { body } = this[kBodyInternals];
123
+ if (body == null) return /* @__PURE__ */ new ArrayBuffer(0);
124
+ if (body instanceof Uint8Array) return bytesToArrayBuffer(body);
125
+ return new Response(toWebBodyInit(body)).arrayBuffer();
126
+ }
127
+ async formData() {
128
+ const { body, contentLength, contentType } = this[kBodyInternals];
129
+ const headers = {};
130
+ if (contentLength != null) headers["Content-Length"] = String(contentLength);
131
+ if (contentType != null) headers["Content-Type"] = contentType;
132
+ return new Response(toWebBodyInit(body), { headers }).formData();
133
+ }
134
+ async blob() {
135
+ const { body, contentType } = this[kBodyInternals];
136
+ if (body == null) return new Blob([], { type: contentType ?? "" });
137
+ if (body instanceof Uint8Array) return new Blob([new Uint8Array(body.buffer, body.byteOffset, body.byteLength)], { type: contentType ?? "" });
138
+ return new Response(toWebBodyInit(body), { headers: contentType ? { "Content-Type": contentType } : void 0 }).blob();
139
+ }
140
+ async json() {
141
+ const text = await this.text();
142
+ return JSON.parse(text);
143
+ }
144
+ async text() {
145
+ const { body } = this[kBodyInternals];
146
+ if (body == null) return "";
147
+ if (body instanceof Uint8Array) return _fuman_utils.utf8.decoder.decode(body);
148
+ return new Response(toWebBodyInit(body)).text();
149
+ }
150
+ };
151
+ //#endregion
152
+ exports.Body = Body;
153
+ exports.extractBody = extractBody;
154
+ exports.getFormDataLength = getFormDataLength;
package/body.js ADDED
@@ -0,0 +1,151 @@
1
+ import { isBlob, isFormData, isIterable, isMultipartFormDataStream, isReadable, isReadableStream, isURLSearchParameters } from "./_internal/guards.js";
2
+ import { nodeReadableToWeb } from "@fuman/node";
3
+ import { randomBytes } from "node:crypto";
4
+ import { Readable } from "node:stream";
5
+ import { isAnyArrayBuffer } from "node:util/types";
6
+ import { utf8 } from "@fuman/utils";
7
+ //#region src/body.ts
8
+ var BOUNDARY = "-".repeat(2);
9
+ var makeFormBoundary = () => `formdata-${randomBytes(8).toString("hex")}`;
10
+ var getFormHeader = (boundary, name, field) => {
11
+ let header = `${BOUNDARY}${boundary}\r
12
+ `;
13
+ header += `Content-Disposition: form-data; name="${name}"`;
14
+ if (isBlob(field)) {
15
+ header += `; filename="${field.name ?? "blob"}"\r
16
+ `;
17
+ header += `Content-Type: ${field.type || "application/octet-stream"}`;
18
+ }
19
+ return `${header}\r
20
+ \r
21
+ `;
22
+ };
23
+ var getFormFooter = (boundary) => `${BOUNDARY}${boundary}${BOUNDARY}\r
24
+ \r
25
+ `;
26
+ var getFormDataLength = (form, boundary) => {
27
+ let length = Buffer.byteLength(getFormFooter(boundary));
28
+ for (const [name, value] of form) length += Buffer.byteLength(getFormHeader(boundary, name, value)) + (isBlob(value) ? value.size : Buffer.byteLength(`${value}`)) + 2;
29
+ return length;
30
+ };
31
+ async function* generatorOfFormData(form, boundary) {
32
+ for (const [name, value] of form) if (isBlob(value)) {
33
+ yield utf8.encoder.encode(getFormHeader(boundary, name, value));
34
+ for await (const chunk of value.stream()) yield chunk;
35
+ yield utf8.encoder.encode("\r\n");
36
+ } else yield utf8.encoder.encode(getFormHeader(boundary, name, value) + value + "\r\n");
37
+ yield utf8.encoder.encode(getFormFooter(boundary));
38
+ }
39
+ var extractBody = (object) => {
40
+ let type = null;
41
+ let body;
42
+ let size = null;
43
+ if (object == null) {
44
+ body = null;
45
+ size = 0;
46
+ } else if (typeof object === "string") {
47
+ const bytes = utf8.encoder.encode(`${object}`);
48
+ type = "text/plain;charset=UTF-8";
49
+ size = bytes.byteLength;
50
+ body = bytes;
51
+ } else if (isURLSearchParameters(object)) {
52
+ const bytes = utf8.encoder.encode(object.toString());
53
+ body = bytes;
54
+ size = bytes.byteLength;
55
+ type = "application/x-www-form-urlencoded;charset=UTF-8";
56
+ } else if (isBlob(object)) {
57
+ size = object.size;
58
+ type = object.type || null;
59
+ body = object.stream();
60
+ } else if (object instanceof Uint8Array) {
61
+ body = object;
62
+ size = object.byteLength;
63
+ } else if (isAnyArrayBuffer(object)) {
64
+ const bytes = new Uint8Array(object);
65
+ body = bytes;
66
+ size = bytes.byteLength;
67
+ } else if (ArrayBuffer.isView(object)) {
68
+ const bytes = new Uint8Array(object.buffer, object.byteOffset, object.byteLength);
69
+ body = bytes;
70
+ size = bytes.byteLength;
71
+ } else if (isReadableStream(object)) body = object;
72
+ else if (isFormData(object)) {
73
+ const boundary = makeFormBoundary();
74
+ type = `multipart/form-data; boundary=${boundary}`;
75
+ size = getFormDataLength(object, boundary);
76
+ body = Readable.from(generatorOfFormData(object, boundary));
77
+ } else if (isMultipartFormDataStream(object)) {
78
+ type = `multipart/form-data; boundary=${object.getBoundary()}`;
79
+ size = object.hasKnownLength() ? object.getLengthSync() : null;
80
+ body = object;
81
+ } else if (isReadable(object)) body = object;
82
+ else if (isIterable(object)) body = Readable.from(object);
83
+ else {
84
+ const bytes = utf8.encoder.encode(`${object}`);
85
+ type = "text/plain;charset=UTF-8";
86
+ body = bytes;
87
+ size = bytes.byteLength;
88
+ }
89
+ return {
90
+ contentLength: size,
91
+ contentType: type,
92
+ body
93
+ };
94
+ };
95
+ var kBodyInternals = Symbol("kBodyInternals");
96
+ var toWebBodyInit = (body) => {
97
+ if (body == null) return null;
98
+ if (isReadable(body)) return nodeReadableToWeb(body);
99
+ return body;
100
+ };
101
+ var bytesToArrayBuffer = (bytes) => {
102
+ const bytesAsArrayBuffer = new ArrayBuffer(bytes.byteLength);
103
+ new Uint8Array(bytesAsArrayBuffer).set(bytes);
104
+ return bytesAsArrayBuffer;
105
+ };
106
+ var Body = class {
107
+ [kBodyInternals];
108
+ constructor(init) {
109
+ this[kBodyInternals] = extractBody(init);
110
+ }
111
+ get body() {
112
+ return this[kBodyInternals].body;
113
+ }
114
+ get bodyUsed() {
115
+ const { body } = this[kBodyInternals];
116
+ if (isReadable(body)) return Readable.isDisturbed(body);
117
+ if (isReadableStream(body)) return body.locked;
118
+ return false;
119
+ }
120
+ async arrayBuffer() {
121
+ const { body } = this[kBodyInternals];
122
+ if (body == null) return /* @__PURE__ */ new ArrayBuffer(0);
123
+ if (body instanceof Uint8Array) return bytesToArrayBuffer(body);
124
+ return new Response(toWebBodyInit(body)).arrayBuffer();
125
+ }
126
+ async formData() {
127
+ const { body, contentLength, contentType } = this[kBodyInternals];
128
+ const headers = {};
129
+ if (contentLength != null) headers["Content-Length"] = String(contentLength);
130
+ if (contentType != null) headers["Content-Type"] = contentType;
131
+ return new Response(toWebBodyInit(body), { headers }).formData();
132
+ }
133
+ async blob() {
134
+ const { body, contentType } = this[kBodyInternals];
135
+ if (body == null) return new Blob([], { type: contentType ?? "" });
136
+ if (body instanceof Uint8Array) return new Blob([new Uint8Array(body.buffer, body.byteOffset, body.byteLength)], { type: contentType ?? "" });
137
+ return new Response(toWebBodyInit(body), { headers: contentType ? { "Content-Type": contentType } : void 0 }).blob();
138
+ }
139
+ async json() {
140
+ const text = await this.text();
141
+ return JSON.parse(text);
142
+ }
143
+ async text() {
144
+ const { body } = this[kBodyInternals];
145
+ if (body == null) return "";
146
+ if (body instanceof Uint8Array) return utf8.decoder.decode(body);
147
+ return new Response(toWebBodyInit(body)).text();
148
+ }
149
+ };
150
+ //#endregion
151
+ export { Body, extractBody, getFormDataLength };
@@ -0,0 +1,49 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_net = require("../_internal/net.cjs");
3
+ let _npy_proxy_kit = require("@npy/proxy-kit");
4
+ //#region src/dialers/proxy.ts
5
+ var DEFAULT_HTTP_ALPN_PROTOCOLS = ["http/1.1"];
6
+ function normalizeProxy(proxy) {
7
+ if (typeof proxy !== "string") return proxy;
8
+ const parsed = (0, _npy_proxy_kit.parse)(proxy, { strict: true });
9
+ if (parsed == null) throw new TypeError(`Invalid proxy string: ${proxy}`);
10
+ return parsed;
11
+ }
12
+ var ProxyDialer = class {
13
+ proxy;
14
+ #options;
15
+ #connectThroughProxy;
16
+ constructor(proxy, options = {}) {
17
+ this.proxy = normalizeProxy(proxy);
18
+ this.#options = { ...options };
19
+ this.#connectThroughProxy = (0, _npy_proxy_kit.createProxyConnection)({
20
+ proxy: this.proxy,
21
+ connectionFn: require_net.connectTcp
22
+ });
23
+ }
24
+ async dial(target, options = {}) {
25
+ const tunneled = await this.#connectThroughProxy({
26
+ address: target.address,
27
+ port: target.port,
28
+ signal: options.signal
29
+ });
30
+ if (!target.secure) return tunneled;
31
+ return this.#upgradeSecureTarget(tunneled, target, options.signal);
32
+ }
33
+ async #upgradeSecureTarget(conn, target, signal) {
34
+ const sni = target.sni ?? this.#options.sni ?? target.address;
35
+ const extraOptions = this.#options.extraOptions || target.extraOptions ? {
36
+ ...this.#options.extraOptions,
37
+ ...target.extraOptions
38
+ } : void 0;
39
+ return require_net.upgradeTls(conn, {
40
+ signal,
41
+ caCerts: this.#options.caCerts,
42
+ sni,
43
+ alpnProtocols: target.alpnProtocols ?? this.#options.alpnProtocols ?? [...DEFAULT_HTTP_ALPN_PROTOCOLS],
44
+ extraOptions
45
+ });
46
+ }
47
+ };
48
+ //#endregion
49
+ exports.ProxyDialer = ProxyDialer;
@@ -0,0 +1,48 @@
1
+ import { connectTcp, upgradeTls } from "../_internal/net.js";
2
+ import { createProxyConnection, parse } from "@npy/proxy-kit";
3
+ //#region src/dialers/proxy.ts
4
+ var DEFAULT_HTTP_ALPN_PROTOCOLS = ["http/1.1"];
5
+ function normalizeProxy(proxy) {
6
+ if (typeof proxy !== "string") return proxy;
7
+ const parsed = parse(proxy, { strict: true });
8
+ if (parsed == null) throw new TypeError(`Invalid proxy string: ${proxy}`);
9
+ return parsed;
10
+ }
11
+ var ProxyDialer = class {
12
+ proxy;
13
+ #options;
14
+ #connectThroughProxy;
15
+ constructor(proxy, options = {}) {
16
+ this.proxy = normalizeProxy(proxy);
17
+ this.#options = { ...options };
18
+ this.#connectThroughProxy = createProxyConnection({
19
+ proxy: this.proxy,
20
+ connectionFn: connectTcp
21
+ });
22
+ }
23
+ async dial(target, options = {}) {
24
+ const tunneled = await this.#connectThroughProxy({
25
+ address: target.address,
26
+ port: target.port,
27
+ signal: options.signal
28
+ });
29
+ if (!target.secure) return tunneled;
30
+ return this.#upgradeSecureTarget(tunneled, target, options.signal);
31
+ }
32
+ async #upgradeSecureTarget(conn, target, signal) {
33
+ const sni = target.sni ?? this.#options.sni ?? target.address;
34
+ const extraOptions = this.#options.extraOptions || target.extraOptions ? {
35
+ ...this.#options.extraOptions,
36
+ ...target.extraOptions
37
+ } : void 0;
38
+ return upgradeTls(conn, {
39
+ signal,
40
+ caCerts: this.#options.caCerts,
41
+ sni,
42
+ alpnProtocols: target.alpnProtocols ?? this.#options.alpnProtocols ?? [...DEFAULT_HTTP_ALPN_PROTOCOLS],
43
+ extraOptions
44
+ });
45
+ }
46
+ };
47
+ //#endregion
48
+ export { ProxyDialer };
@@ -0,0 +1,70 @@
1
+ const require_net = require("../_internal/net.cjs");
2
+ //#region src/dialers/tcp.ts
3
+ var DEFAULT_TCP_PORT = 80;
4
+ var DEFAULT_TLS_PORT = 443;
5
+ var DEFAULT_HTTP_ALPN_PROTOCOLS = ["http/1.1"];
6
+ function parsePort(value) {
7
+ if (typeof value === "number") {
8
+ if (!Number.isInteger(value) || value <= 0 || value > 65535) throw new TypeError(`Invalid port: ${String(value)}`);
9
+ return value;
10
+ }
11
+ if (!/^\d+$/.test(value)) throw new TypeError(`Invalid port: ${JSON.stringify(value)}`);
12
+ const parsed = Number.parseInt(value, 10);
13
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) throw new TypeError(`Invalid port: ${JSON.stringify(value)}`);
14
+ return parsed;
15
+ }
16
+ function resolveHostPort(target, defaultPort) {
17
+ const address = target instanceof URL ? target.hostname : target.address;
18
+ if (!address) throw new TypeError("Target address is required");
19
+ return {
20
+ address,
21
+ port: target instanceof URL ? parsePort(target.port || String(defaultPort)) : parsePort(target.port || defaultPort)
22
+ };
23
+ }
24
+ var TcpDialer = class {
25
+ async dial(target, options = {}) {
26
+ if (target.secure) throw new Error("TcpDialer cannot dial a secure target");
27
+ return require_net.connectTcp({
28
+ ...resolveHostPort(target, DEFAULT_TCP_PORT),
29
+ signal: options.signal
30
+ });
31
+ }
32
+ };
33
+ var TlsDialer = class {
34
+ #options;
35
+ constructor(options = {}) {
36
+ this.#options = { ...options };
37
+ }
38
+ async dial(target, options = {}) {
39
+ if (!target.secure) throw new Error("TlsDialer cannot dial an insecure target");
40
+ const endpoint = resolveHostPort(target, DEFAULT_TLS_PORT);
41
+ const extraOptions = this.#options.extraOptions || target.extraOptions ? {
42
+ ...this.#options.extraOptions,
43
+ ...target.extraOptions
44
+ } : void 0;
45
+ return require_net.connectTls({
46
+ ...endpoint,
47
+ signal: options.signal,
48
+ caCerts: this.#options.caCerts,
49
+ sni: target.sni ?? this.#options.sni ?? endpoint.address,
50
+ alpnProtocols: target.alpnProtocols ?? this.#options.alpnProtocols ?? [...DEFAULT_HTTP_ALPN_PROTOCOLS],
51
+ extraOptions
52
+ });
53
+ }
54
+ };
55
+ var AutoDialer = class {
56
+ tcpDialer;
57
+ tlsDialer;
58
+ constructor(options = {}) {
59
+ this.tcpDialer = options.tcp ?? new TcpDialer();
60
+ this.tlsDialer = options.tls ?? new TlsDialer();
61
+ }
62
+ dial(target, options = {}) {
63
+ return target.secure ? this.tlsDialer.dial(target, options) : this.tcpDialer.dial(target, options);
64
+ }
65
+ };
66
+ //#endregion
67
+ exports.AutoDialer = AutoDialer;
68
+ exports.TcpDialer = TcpDialer;
69
+ exports.TlsDialer = TlsDialer;
70
+ exports.resolveHostPort = resolveHostPort;
package/dialers/tcp.js ADDED
@@ -0,0 +1,67 @@
1
+ import { connectTcp, connectTls } from "../_internal/net.js";
2
+ //#region src/dialers/tcp.ts
3
+ var DEFAULT_TCP_PORT = 80;
4
+ var DEFAULT_TLS_PORT = 443;
5
+ var DEFAULT_HTTP_ALPN_PROTOCOLS = ["http/1.1"];
6
+ function parsePort(value) {
7
+ if (typeof value === "number") {
8
+ if (!Number.isInteger(value) || value <= 0 || value > 65535) throw new TypeError(`Invalid port: ${String(value)}`);
9
+ return value;
10
+ }
11
+ if (!/^\d+$/.test(value)) throw new TypeError(`Invalid port: ${JSON.stringify(value)}`);
12
+ const parsed = Number.parseInt(value, 10);
13
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) throw new TypeError(`Invalid port: ${JSON.stringify(value)}`);
14
+ return parsed;
15
+ }
16
+ function resolveHostPort(target, defaultPort) {
17
+ const address = target instanceof URL ? target.hostname : target.address;
18
+ if (!address) throw new TypeError("Target address is required");
19
+ return {
20
+ address,
21
+ port: target instanceof URL ? parsePort(target.port || String(defaultPort)) : parsePort(target.port || defaultPort)
22
+ };
23
+ }
24
+ var TcpDialer = class {
25
+ async dial(target, options = {}) {
26
+ if (target.secure) throw new Error("TcpDialer cannot dial a secure target");
27
+ return connectTcp({
28
+ ...resolveHostPort(target, DEFAULT_TCP_PORT),
29
+ signal: options.signal
30
+ });
31
+ }
32
+ };
33
+ var TlsDialer = class {
34
+ #options;
35
+ constructor(options = {}) {
36
+ this.#options = { ...options };
37
+ }
38
+ async dial(target, options = {}) {
39
+ if (!target.secure) throw new Error("TlsDialer cannot dial an insecure target");
40
+ const endpoint = resolveHostPort(target, DEFAULT_TLS_PORT);
41
+ const extraOptions = this.#options.extraOptions || target.extraOptions ? {
42
+ ...this.#options.extraOptions,
43
+ ...target.extraOptions
44
+ } : void 0;
45
+ return connectTls({
46
+ ...endpoint,
47
+ signal: options.signal,
48
+ caCerts: this.#options.caCerts,
49
+ sni: target.sni ?? this.#options.sni ?? endpoint.address,
50
+ alpnProtocols: target.alpnProtocols ?? this.#options.alpnProtocols ?? [...DEFAULT_HTTP_ALPN_PROTOCOLS],
51
+ extraOptions
52
+ });
53
+ }
54
+ };
55
+ var AutoDialer = class {
56
+ tcpDialer;
57
+ tlsDialer;
58
+ constructor(options = {}) {
59
+ this.tcpDialer = options.tcp ?? new TcpDialer();
60
+ this.tlsDialer = options.tls ?? new TlsDialer();
61
+ }
62
+ dial(target, options = {}) {
63
+ return target.secure ? this.tlsDialer.dial(target, options) : this.tcpDialer.dial(target, options);
64
+ }
65
+ };
66
+ //#endregion
67
+ export { AutoDialer, TcpDialer, TlsDialer, resolveHostPort };
package/encoding.cjs ADDED
@@ -0,0 +1,95 @@
1
+ require("./_virtual/_rolldown/runtime.cjs");
2
+ let _fuman_node = require("@fuman/node");
3
+ let node_stream = require("node:stream");
4
+ //#region src/encoding.ts
5
+ function applyTransforms(stream, contentEncoding, factory) {
6
+ const transforms = factory(contentEncoding);
7
+ if (transforms.length === 0) return stream;
8
+ let result;
9
+ if (stream instanceof ReadableStream) result = stream;
10
+ else result = (0, _fuman_node.nodeReadableToWeb)(node_stream.Readable.from(stream));
11
+ for (const t of transforms) result = result.pipeThrough(t);
12
+ return result;
13
+ }
14
+ function decodeStream(stream, contentEncoding) {
15
+ return applyTransforms(stream, contentEncoding, createDecoders);
16
+ }
17
+ function encodeStream(stream, contentEncoding) {
18
+ return applyTransforms(stream, contentEncoding, createEncoders);
19
+ }
20
+ /**
21
+ * Create a series of decoding streams based on the content-encoding header. The
22
+ * resulting streams should be piped together to decode the content.
23
+ *
24
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc9110#section-8.4.1}
25
+ */
26
+ function createDecoders(contentEncoding) {
27
+ const decoders = [];
28
+ if (contentEncoding?.length) {
29
+ const encodings = Array.isArray(contentEncoding) ? contentEncoding.flatMap(commaSplit) : contentEncoding.split(",");
30
+ for (const encoding of encodings) {
31
+ const normalizedEncoding = normalizeEncoding(encoding);
32
+ if (normalizedEncoding === "identity") continue;
33
+ decoders.push(createDecoder(normalizedEncoding));
34
+ }
35
+ }
36
+ return decoders.reverse();
37
+ }
38
+ /**
39
+ * Create a series of encoding streams based on the content-encoding header (or
40
+ * transfer-coding list).
41
+ *
42
+ * The resulting streams should be piped together to apply the encoding in the
43
+ * declared order.
44
+ *
45
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc9110#section-8.4.1}
46
+ */
47
+ function createEncoders(contentEncoding) {
48
+ const encoders = [];
49
+ if (contentEncoding?.length) {
50
+ const encodings = Array.isArray(contentEncoding) ? contentEncoding.flatMap(commaSplit) : contentEncoding.split(",");
51
+ for (const encoding of encodings) {
52
+ const normalizedEncoding = normalizeEncoding(encoding);
53
+ if (normalizedEncoding === "identity") continue;
54
+ encoders.push(createEncoder(normalizedEncoding));
55
+ }
56
+ }
57
+ return encoders;
58
+ }
59
+ function commaSplit(header) {
60
+ return header.split(",");
61
+ }
62
+ function normalizeEncoding(encoding) {
63
+ return encoding.trim().toLowerCase();
64
+ }
65
+ function createDecoder(normalizedEncoding) {
66
+ switch (normalizedEncoding) {
67
+ case "gzip":
68
+ case "x-gzip": return new DecompressionStream("gzip");
69
+ case "deflate":
70
+ case "x-deflate": return new DecompressionStream("deflate");
71
+ case "zstd":
72
+ case "x-zstd": return new DecompressionStream("zstd");
73
+ case "br": return new DecompressionStream("brotli");
74
+ case "identity": return new TransformStream();
75
+ default: throw new TypeError(`Unsupported content-encoding: "${normalizedEncoding}"`);
76
+ }
77
+ }
78
+ function createEncoder(normalizedEncoding) {
79
+ switch (normalizedEncoding) {
80
+ case "gzip":
81
+ case "x-gzip": return new CompressionStream("gzip");
82
+ case "deflate":
83
+ case "x-deflate": return new CompressionStream("deflate");
84
+ case "zstd":
85
+ case "x-zstd": return new CompressionStream("zstd");
86
+ case "br": return new CompressionStream("brotli");
87
+ case "identity": return new TransformStream();
88
+ default: throw new TypeError(`Unsupported content-encoding: "${normalizedEncoding}"`);
89
+ }
90
+ }
91
+ //#endregion
92
+ exports.createDecoders = createDecoders;
93
+ exports.createEncoders = createEncoders;
94
+ exports.decodeStream = decodeStream;
95
+ exports.encodeStream = encodeStream;