@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/fetch.cjs ADDED
@@ -0,0 +1,117 @@
1
+ const require_tcp = require("./dialers/tcp.cjs");
2
+ const require_error_adapters = require("./_internal/error-adapters.cjs");
3
+ const require_http_client = require("./http-client.cjs");
4
+ //#region src/fetch.ts
5
+ function createDefaultHttpClient(options = {}) {
6
+ return new require_http_client.HttpClient({
7
+ ...options,
8
+ dialer: options.dialer ?? new require_tcp.AutoDialer()
9
+ });
10
+ }
11
+ function normalizeHeaders(headers) {
12
+ if (headers instanceof Headers) return headers;
13
+ const normalized = new Headers();
14
+ if (Array.isArray(headers)) {
15
+ headers.forEach(([key, value]) => {
16
+ normalized.append(key, value);
17
+ });
18
+ return normalized;
19
+ }
20
+ if (headers) Object.entries(headers).forEach(([key, value]) => {
21
+ if (Array.isArray(value)) value.forEach((entry) => {
22
+ normalized.append(key, entry);
23
+ });
24
+ else if (value !== void 0) normalized.append(key, value);
25
+ });
26
+ return normalized;
27
+ }
28
+ function resolveUrl(input) {
29
+ if (input instanceof URL) return input;
30
+ if (input instanceof Request) return new URL(input.url);
31
+ return new URL(String(input));
32
+ }
33
+ function resolveMethod(input, init) {
34
+ if (init.method != null) return init.method.toUpperCase();
35
+ if (input instanceof Request) return input.method.toUpperCase();
36
+ return "GET";
37
+ }
38
+ function resolveHeaders(input, init) {
39
+ if (init.headers !== void 0) return normalizeHeaders(init.headers);
40
+ if (input instanceof Request) return normalizeHeaders(input.headers);
41
+ return new Headers();
42
+ }
43
+ function resolveSignal(input, init) {
44
+ return init.signal ?? (input instanceof Request ? input.signal : void 0);
45
+ }
46
+ function resolveBody(input, init) {
47
+ if (init.body !== void 0) return init.body;
48
+ if (!(input instanceof Request)) return;
49
+ if (input.bodyUsed) throw new TypeError("Request body has already been used");
50
+ return input.body;
51
+ }
52
+ function assertValidFetchUrl(url) {
53
+ if (url.username || url.password) throw new TypeError("Request URL must not include embedded credentials");
54
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new TypeError(`fetch failed: unsupported scheme ${url.protocol}`);
55
+ }
56
+ function assertValidFetchBody(method, body) {
57
+ if (body == null) return;
58
+ if (method === "GET" || method === "HEAD") throw new TypeError(`Request with ${method} method cannot have a body`);
59
+ }
60
+ async function fetchImpl(input, init) {
61
+ const url = resolveUrl(input);
62
+ assertValidFetchUrl(url);
63
+ const method = resolveMethod(input, init);
64
+ const headers = resolveHeaders(input, init);
65
+ const body = resolveBody(input, init);
66
+ const signal = resolveSignal(input, init);
67
+ assertValidFetchBody(method, body);
68
+ try {
69
+ return require_error_adapters.wrapResponseBodyErrors(await init.client.send({
70
+ url,
71
+ method,
72
+ headers,
73
+ body: body ?? null,
74
+ signal
75
+ }), (error) => require_error_adapters.toWebBodyReadError(error, signal));
76
+ } catch (error) {
77
+ throw require_error_adapters.toWebFetchError(error, signal);
78
+ }
79
+ }
80
+ function createFetch(client) {
81
+ const defaultHttpClient = client ?? createDefaultHttpClient();
82
+ const fetchLike = (async (input, init = {}) => {
83
+ return fetchImpl(input, init.client == null ? {
84
+ ...init,
85
+ client: defaultHttpClient
86
+ } : init);
87
+ });
88
+ const close = async () => {
89
+ if (client == null) await defaultHttpClient.close();
90
+ };
91
+ Object.defineProperties(fetchLike, {
92
+ client: {
93
+ configurable: false,
94
+ enumerable: false,
95
+ value: defaultHttpClient,
96
+ writable: false
97
+ },
98
+ close: {
99
+ configurable: false,
100
+ enumerable: false,
101
+ value: close,
102
+ writable: false
103
+ },
104
+ [Symbol.asyncDispose]: {
105
+ configurable: false,
106
+ enumerable: false,
107
+ value: close,
108
+ writable: false
109
+ }
110
+ });
111
+ return fetchLike;
112
+ }
113
+ var fetch = createFetch();
114
+ //#endregion
115
+ exports.createFetch = createFetch;
116
+ exports.default = fetch;
117
+ exports.normalizeHeaders = normalizeHeaders;
package/fetch.js ADDED
@@ -0,0 +1,115 @@
1
+ import { AutoDialer } from "./dialers/tcp.js";
2
+ import { toWebBodyReadError, toWebFetchError, wrapResponseBodyErrors } from "./_internal/error-adapters.js";
3
+ import { HttpClient } from "./http-client.js";
4
+ //#region src/fetch.ts
5
+ function createDefaultHttpClient(options = {}) {
6
+ return new HttpClient({
7
+ ...options,
8
+ dialer: options.dialer ?? new AutoDialer()
9
+ });
10
+ }
11
+ function normalizeHeaders(headers) {
12
+ if (headers instanceof Headers) return headers;
13
+ const normalized = new Headers();
14
+ if (Array.isArray(headers)) {
15
+ headers.forEach(([key, value]) => {
16
+ normalized.append(key, value);
17
+ });
18
+ return normalized;
19
+ }
20
+ if (headers) Object.entries(headers).forEach(([key, value]) => {
21
+ if (Array.isArray(value)) value.forEach((entry) => {
22
+ normalized.append(key, entry);
23
+ });
24
+ else if (value !== void 0) normalized.append(key, value);
25
+ });
26
+ return normalized;
27
+ }
28
+ function resolveUrl(input) {
29
+ if (input instanceof URL) return input;
30
+ if (input instanceof Request) return new URL(input.url);
31
+ return new URL(String(input));
32
+ }
33
+ function resolveMethod(input, init) {
34
+ if (init.method != null) return init.method.toUpperCase();
35
+ if (input instanceof Request) return input.method.toUpperCase();
36
+ return "GET";
37
+ }
38
+ function resolveHeaders(input, init) {
39
+ if (init.headers !== void 0) return normalizeHeaders(init.headers);
40
+ if (input instanceof Request) return normalizeHeaders(input.headers);
41
+ return new Headers();
42
+ }
43
+ function resolveSignal(input, init) {
44
+ return init.signal ?? (input instanceof Request ? input.signal : void 0);
45
+ }
46
+ function resolveBody(input, init) {
47
+ if (init.body !== void 0) return init.body;
48
+ if (!(input instanceof Request)) return;
49
+ if (input.bodyUsed) throw new TypeError("Request body has already been used");
50
+ return input.body;
51
+ }
52
+ function assertValidFetchUrl(url) {
53
+ if (url.username || url.password) throw new TypeError("Request URL must not include embedded credentials");
54
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new TypeError(`fetch failed: unsupported scheme ${url.protocol}`);
55
+ }
56
+ function assertValidFetchBody(method, body) {
57
+ if (body == null) return;
58
+ if (method === "GET" || method === "HEAD") throw new TypeError(`Request with ${method} method cannot have a body`);
59
+ }
60
+ async function fetchImpl(input, init) {
61
+ const url = resolveUrl(input);
62
+ assertValidFetchUrl(url);
63
+ const method = resolveMethod(input, init);
64
+ const headers = resolveHeaders(input, init);
65
+ const body = resolveBody(input, init);
66
+ const signal = resolveSignal(input, init);
67
+ assertValidFetchBody(method, body);
68
+ try {
69
+ return wrapResponseBodyErrors(await init.client.send({
70
+ url,
71
+ method,
72
+ headers,
73
+ body: body ?? null,
74
+ signal
75
+ }), (error) => toWebBodyReadError(error, signal));
76
+ } catch (error) {
77
+ throw toWebFetchError(error, signal);
78
+ }
79
+ }
80
+ function createFetch(client) {
81
+ const defaultHttpClient = client ?? createDefaultHttpClient();
82
+ const fetchLike = (async (input, init = {}) => {
83
+ return fetchImpl(input, init.client == null ? {
84
+ ...init,
85
+ client: defaultHttpClient
86
+ } : init);
87
+ });
88
+ const close = async () => {
89
+ if (client == null) await defaultHttpClient.close();
90
+ };
91
+ Object.defineProperties(fetchLike, {
92
+ client: {
93
+ configurable: false,
94
+ enumerable: false,
95
+ value: defaultHttpClient,
96
+ writable: false
97
+ },
98
+ close: {
99
+ configurable: false,
100
+ enumerable: false,
101
+ value: close,
102
+ writable: false
103
+ },
104
+ [Symbol.asyncDispose]: {
105
+ configurable: false,
106
+ enumerable: false,
107
+ value: close,
108
+ writable: false
109
+ }
110
+ });
111
+ return fetchLike;
112
+ }
113
+ var fetch = createFetch();
114
+ //#endregion
115
+ export { createFetch, fetch as default, normalizeHeaders };
@@ -0,0 +1,33 @@
1
+ const require_agent_pool = require("./agent-pool.cjs");
2
+ //#region src/http-client.ts
3
+ var HttpClient = class {
4
+ #agentPools = /* @__PURE__ */ new Map();
5
+ #agentPoolOptions;
6
+ constructor(options = {}) {
7
+ this.#agentPoolOptions = { ...options };
8
+ }
9
+ async send(options) {
10
+ return this.#getOrCreateAgentPool(options.url).send(options);
11
+ }
12
+ async close() {
13
+ const entries = Array.from(this.#agentPools.entries());
14
+ const failed = (await Promise.allSettled(entries.map(([origin, agentPool]) => agentPool.close().then(() => {
15
+ this.#agentPools.delete(origin);
16
+ })))).find((r) => r.status === "rejected");
17
+ if (failed) throw failed.reason;
18
+ }
19
+ async [Symbol.asyncDispose]() {
20
+ await this.close();
21
+ }
22
+ #getOrCreateAgentPool(url) {
23
+ const origin = typeof url === "string" ? new URL(url).origin : url.origin;
24
+ let agentPool = this.#agentPools.get(origin);
25
+ if (!agentPool) {
26
+ agentPool = require_agent_pool.createAgentPool(origin, this.#agentPoolOptions);
27
+ this.#agentPools.set(origin, agentPool);
28
+ }
29
+ return agentPool;
30
+ }
31
+ };
32
+ //#endregion
33
+ exports.HttpClient = HttpClient;
package/http-client.js ADDED
@@ -0,0 +1,33 @@
1
+ import { createAgentPool } from "./agent-pool.js";
2
+ //#region src/http-client.ts
3
+ var HttpClient = class {
4
+ #agentPools = /* @__PURE__ */ new Map();
5
+ #agentPoolOptions;
6
+ constructor(options = {}) {
7
+ this.#agentPoolOptions = { ...options };
8
+ }
9
+ async send(options) {
10
+ return this.#getOrCreateAgentPool(options.url).send(options);
11
+ }
12
+ async close() {
13
+ const entries = Array.from(this.#agentPools.entries());
14
+ const failed = (await Promise.allSettled(entries.map(([origin, agentPool]) => agentPool.close().then(() => {
15
+ this.#agentPools.delete(origin);
16
+ })))).find((r) => r.status === "rejected");
17
+ if (failed) throw failed.reason;
18
+ }
19
+ async [Symbol.asyncDispose]() {
20
+ await this.close();
21
+ }
22
+ #getOrCreateAgentPool(url) {
23
+ const origin = typeof url === "string" ? new URL(url).origin : url.origin;
24
+ let agentPool = this.#agentPools.get(origin);
25
+ if (!agentPool) {
26
+ agentPool = createAgentPool(origin, this.#agentPoolOptions);
27
+ this.#agentPools.set(origin, agentPool);
28
+ }
29
+ return agentPool;
30
+ }
31
+ };
32
+ //#endregion
33
+ export { HttpClient };
package/index.cjs ADDED
@@ -0,0 +1,45 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_net = require("./_internal/net.cjs");
3
+ const require_body = require("./body.cjs");
4
+ const require_proxy = require("./dialers/proxy.cjs");
5
+ const require_tcp = require("./dialers/tcp.cjs");
6
+ const require_encoding = require("./encoding.cjs");
7
+ const require_errors = require("./errors.cjs");
8
+ const require_http_client = require("./http-client.cjs");
9
+ const require_fetch = require("./fetch.cjs");
10
+ exports.AgentBusyError = require_errors.AgentBusyError;
11
+ exports.AgentClosedError = require_errors.AgentClosedError;
12
+ exports.AutoDialer = require_tcp.AutoDialer;
13
+ exports.Body = require_body.Body;
14
+ exports.ConnectTimeoutError = require_errors.ConnectTimeoutError;
15
+ exports.ConnectionError = require_errors.ConnectionError;
16
+ exports.ErrorType = require_errors.ErrorType;
17
+ exports.FetchError = require_errors.FetchError;
18
+ exports.FetchErrorCode = require_errors.FetchErrorCode;
19
+ exports.HttpClient = require_http_client.HttpClient;
20
+ exports.HttpStatusError = require_errors.HttpStatusError;
21
+ exports.OriginMismatchError = require_errors.OriginMismatchError;
22
+ exports.ProxyDialer = require_proxy.ProxyDialer;
23
+ exports.RequestAbortedError = require_errors.RequestAbortedError;
24
+ exports.RequestWriteError = require_errors.RequestWriteError;
25
+ exports.ResponseBodyError = require_errors.ResponseBodyError;
26
+ exports.ResponseDecodeError = require_errors.ResponseDecodeError;
27
+ exports.ResponseHeaderError = require_errors.ResponseHeaderError;
28
+ exports.TcpDialer = require_tcp.TcpDialer;
29
+ exports.TlsDialer = require_tcp.TlsDialer;
30
+ exports.UnsupportedAlpnProtocolError = require_errors.UnsupportedAlpnProtocolError;
31
+ exports.UnsupportedMethodError = require_errors.UnsupportedMethodError;
32
+ exports.UnsupportedProtocolError = require_errors.UnsupportedProtocolError;
33
+ exports.connectTcp = require_net.connectTcp;
34
+ exports.connectTls = require_net.connectTls;
35
+ exports.createDecoders = require_encoding.createDecoders;
36
+ exports.createEncoders = require_encoding.createEncoders;
37
+ exports.createFetch = require_fetch.createFetch;
38
+ exports.decodeStream = require_encoding.decodeStream;
39
+ exports.encodeStream = require_encoding.encodeStream;
40
+ exports.extractBody = require_body.extractBody;
41
+ exports.fetch = require_fetch;
42
+ exports.getFormDataLength = require_body.getFormDataLength;
43
+ exports.normalizeHeaders = require_fetch.normalizeHeaders;
44
+ exports.resolveHostPort = require_tcp.resolveHostPort;
45
+ exports.upgradeTls = require_net.upgradeTls;
package/index.d.cts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index.js"
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index.js"
package/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { connectTcp, connectTls, upgradeTls } from "./_internal/net.js";
2
+ import { Body, extractBody, getFormDataLength } from "./body.js";
3
+ import { ProxyDialer } from "./dialers/proxy.js";
4
+ import { AutoDialer, TcpDialer, TlsDialer, resolveHostPort } from "./dialers/tcp.js";
5
+ import { createDecoders, createEncoders, decodeStream, encodeStream } from "./encoding.js";
6
+ import { AgentBusyError, AgentClosedError, ConnectTimeoutError, ConnectionError, ErrorType, FetchError, FetchErrorCode, HttpStatusError, OriginMismatchError, RequestAbortedError, RequestWriteError, ResponseBodyError, ResponseDecodeError, ResponseHeaderError, UnsupportedAlpnProtocolError, UnsupportedMethodError, UnsupportedProtocolError } from "./errors.js";
7
+ import { HttpClient } from "./http-client.js";
8
+ import fetch, { createFetch, normalizeHeaders } from "./fetch.js";
9
+ export { AgentBusyError, AgentClosedError, AutoDialer, Body, ConnectTimeoutError, ConnectionError, ErrorType, FetchError, FetchErrorCode, HttpClient, HttpStatusError, OriginMismatchError, ProxyDialer, RequestAbortedError, RequestWriteError, ResponseBodyError, ResponseDecodeError, ResponseHeaderError, TcpDialer, TlsDialer, UnsupportedAlpnProtocolError, UnsupportedMethodError, UnsupportedProtocolError, connectTcp, connectTls, createDecoders, createEncoders, createFetch, decodeStream, encodeStream, extractBody, fetch, getFormDataLength, normalizeHeaders, resolveHostPort, upgradeTls };
package/io/_utils.cjs ADDED
@@ -0,0 +1,56 @@
1
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
2
+ let bytes = require("bytes");
3
+ bytes = require_runtime.__toESM(bytes);
4
+ //#region src/io/_utils.ts
5
+ function parseMaxBytes(value) {
6
+ if (value === void 0) return null;
7
+ if (typeof value === "number") {
8
+ if (!Number.isFinite(value) || value < 0) throw new Error(`invalid max size: ${String(value)}`);
9
+ return Math.floor(value);
10
+ }
11
+ const parsed = bytes.default.parse(value);
12
+ if (parsed == null || !Number.isFinite(parsed) || parsed < 0) throw new Error(`invalid max size: ${String(value)}`);
13
+ return parsed;
14
+ }
15
+ function splitTokens(v) {
16
+ if (!v) return [];
17
+ return v.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
18
+ }
19
+ function parseTransferEncoding(headers) {
20
+ const raw = headers.get("transfer-encoding");
21
+ const tks = splitTokens(raw);
22
+ if (tks.length === 0) return {
23
+ has: false,
24
+ chunked: false,
25
+ codings: []
26
+ };
27
+ const chunkedIdx = tks.lastIndexOf("chunked");
28
+ const hasChunked = chunkedIdx !== -1;
29
+ if (hasChunked && chunkedIdx !== tks.length - 1) throw new Error(`Invalid transfer-encoding order: ${raw ?? ""}`);
30
+ if (hasChunked && tks.indexOf("chunked") !== chunkedIdx) throw new Error(`Invalid transfer-encoding (duplicate chunked): ${raw ?? ""}`);
31
+ return {
32
+ has: true,
33
+ chunked: hasChunked,
34
+ codings: tks.filter((t) => t !== "chunked" && t !== "identity")
35
+ };
36
+ }
37
+ function parseContentLength(headers) {
38
+ const raw = headers.get("content-length");
39
+ if (!raw) return null;
40
+ const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
41
+ if (parts.length === 0) return null;
42
+ let value = null;
43
+ for (const p of parts) {
44
+ if (!/^\d+$/.test(p)) throw new Error(`Invalid content-length: ${raw}`);
45
+ const n = Number.parseInt(p, 10);
46
+ if (!Number.isFinite(n) || n < 0) throw new Error(`Invalid content-length: ${raw}`);
47
+ if (value === null) value = n;
48
+ else if (value !== n) throw new Error(`Conflicting content-length values: ${raw}`);
49
+ }
50
+ return value;
51
+ }
52
+ //#endregion
53
+ exports.parseContentLength = parseContentLength;
54
+ exports.parseMaxBytes = parseMaxBytes;
55
+ exports.parseTransferEncoding = parseTransferEncoding;
56
+ exports.splitTokens = splitTokens;
package/io/_utils.js ADDED
@@ -0,0 +1,51 @@
1
+ import bytes from "bytes";
2
+ //#region src/io/_utils.ts
3
+ function parseMaxBytes(value) {
4
+ if (value === void 0) return null;
5
+ if (typeof value === "number") {
6
+ if (!Number.isFinite(value) || value < 0) throw new Error(`invalid max size: ${String(value)}`);
7
+ return Math.floor(value);
8
+ }
9
+ const parsed = bytes.parse(value);
10
+ if (parsed == null || !Number.isFinite(parsed) || parsed < 0) throw new Error(`invalid max size: ${String(value)}`);
11
+ return parsed;
12
+ }
13
+ function splitTokens(v) {
14
+ if (!v) return [];
15
+ return v.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
16
+ }
17
+ function parseTransferEncoding(headers) {
18
+ const raw = headers.get("transfer-encoding");
19
+ const tks = splitTokens(raw);
20
+ if (tks.length === 0) return {
21
+ has: false,
22
+ chunked: false,
23
+ codings: []
24
+ };
25
+ const chunkedIdx = tks.lastIndexOf("chunked");
26
+ const hasChunked = chunkedIdx !== -1;
27
+ if (hasChunked && chunkedIdx !== tks.length - 1) throw new Error(`Invalid transfer-encoding order: ${raw ?? ""}`);
28
+ if (hasChunked && tks.indexOf("chunked") !== chunkedIdx) throw new Error(`Invalid transfer-encoding (duplicate chunked): ${raw ?? ""}`);
29
+ return {
30
+ has: true,
31
+ chunked: hasChunked,
32
+ codings: tks.filter((t) => t !== "chunked" && t !== "identity")
33
+ };
34
+ }
35
+ function parseContentLength(headers) {
36
+ const raw = headers.get("content-length");
37
+ if (!raw) return null;
38
+ const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
39
+ if (parts.length === 0) return null;
40
+ let value = null;
41
+ for (const p of parts) {
42
+ if (!/^\d+$/.test(p)) throw new Error(`Invalid content-length: ${raw}`);
43
+ const n = Number.parseInt(p, 10);
44
+ if (!Number.isFinite(n) || n < 0) throw new Error(`Invalid content-length: ${raw}`);
45
+ if (value === null) value = n;
46
+ else if (value !== n) throw new Error(`Conflicting content-length values: ${raw}`);
47
+ }
48
+ return value;
49
+ }
50
+ //#endregion
51
+ export { parseContentLength, parseMaxBytes, parseTransferEncoding, splitTokens };
@@ -0,0 +1,149 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ let _fuman_utils = require("@fuman/utils");
3
+ //#region src/io/buf-writer.ts
4
+ var DEFAULT_BUF_SIZE = 4096;
5
+ var MIN_BUF_SIZE = 16;
6
+ var BufWriter = class {
7
+ #buffer;
8
+ #writable;
9
+ #writePos = 0;
10
+ #error = null;
11
+ #pending = [];
12
+ #pendingBytes = 0;
13
+ #lastWrite = null;
14
+ constructor(writable, size = DEFAULT_BUF_SIZE) {
15
+ if (size < MIN_BUF_SIZE) size = MIN_BUF_SIZE;
16
+ this.#buffer = _fuman_utils.u8.alloc(size);
17
+ this.#writable = writable;
18
+ }
19
+ get bufferSize() {
20
+ return this.#buffer.byteLength;
21
+ }
22
+ get buffered() {
23
+ return this.#pendingBytes + this.#writePos;
24
+ }
25
+ get available() {
26
+ return this.#buffer.byteLength - this.#writePos;
27
+ }
28
+ reset(writable) {
29
+ this.#error = null;
30
+ this.#writePos = 0;
31
+ this.#pending.length = 0;
32
+ this.#pendingBytes = 0;
33
+ this.#lastWrite = null;
34
+ this.#writable = writable;
35
+ }
36
+ writeSync(bytes) {
37
+ if (this.#error) throw this.#error;
38
+ if (bytes < 0) throw new RangeError("bytes must be >= 0");
39
+ if (this.#lastWrite) throw new Error("disposeWriteSync must be called before the next writeSync");
40
+ if (bytes === 0) {
41
+ const empty = this.#buffer.subarray(this.#writePos, this.#writePos);
42
+ this.#lastWrite = {
43
+ buf: empty,
44
+ size: 0,
45
+ internal: true
46
+ };
47
+ return empty;
48
+ }
49
+ if (bytes > this.available && this.#writePos > 0) {
50
+ const copy = _fuman_utils.u8.allocWith(this.#buffer.subarray(0, this.#writePos));
51
+ this.#pending.push(copy);
52
+ this.#pendingBytes += copy.length;
53
+ this.#writePos = 0;
54
+ }
55
+ if (bytes <= this.#buffer.byteLength) {
56
+ if (bytes > this.available) {
57
+ const copy = _fuman_utils.u8.allocWith(this.#buffer.subarray(0, this.#writePos));
58
+ this.#pending.push(copy);
59
+ this.#pendingBytes += copy.length;
60
+ this.#writePos = 0;
61
+ }
62
+ const start = this.#writePos;
63
+ const end = start + bytes;
64
+ const slice = this.#buffer.subarray(start, end);
65
+ this.#writePos = end;
66
+ this.#lastWrite = {
67
+ buf: slice,
68
+ size: bytes,
69
+ internal: true
70
+ };
71
+ return slice;
72
+ }
73
+ const chunk = _fuman_utils.u8.alloc(bytes);
74
+ this.#lastWrite = {
75
+ buf: chunk,
76
+ size: bytes,
77
+ internal: false
78
+ };
79
+ return chunk;
80
+ }
81
+ disposeWriteSync(written) {
82
+ const lw = this.#lastWrite;
83
+ if (!lw) return;
84
+ const w = written ?? lw.size;
85
+ if (w < 0 || w > lw.size) throw new RangeError(`written out of bounds: ${w} (0..${lw.size})`);
86
+ if (lw.internal) this.#writePos -= lw.size - w;
87
+ else if (w > 0) {
88
+ const chunk = w === lw.size ? lw.buf : lw.buf.subarray(0, w);
89
+ this.#pending.push(chunk);
90
+ this.#pendingBytes += chunk.length;
91
+ }
92
+ this.#lastWrite = null;
93
+ }
94
+ async #flushPending() {
95
+ if (this.#error) throw this.#error;
96
+ if (this.#lastWrite) throw new Error("disposeWriteSync must be called before flush/write");
97
+ while (this.#pending.length > 0) {
98
+ const chunk = this.#pending[0];
99
+ try {
100
+ await this.#writable.write(chunk);
101
+ } catch (e) {
102
+ this.#error = e;
103
+ throw e;
104
+ }
105
+ this.#pending.shift();
106
+ this.#pendingBytes -= chunk.length;
107
+ }
108
+ }
109
+ async flush() {
110
+ await this.#flushPending();
111
+ if (this.#error) throw this.#error;
112
+ if (this.#writePos === 0) return;
113
+ try {
114
+ await this.#writable.write(this.#buffer.subarray(0, this.#writePos));
115
+ } catch (e) {
116
+ this.#error = e;
117
+ throw e;
118
+ }
119
+ this.#writePos = 0;
120
+ }
121
+ async write(bytes) {
122
+ if (this.#error) throw this.#error;
123
+ if (!bytes.length) return;
124
+ await this.#flushPending();
125
+ if (this.#writePos === 0 && bytes.length >= this.#buffer.byteLength) {
126
+ try {
127
+ await this.#writable.write(bytes);
128
+ } catch (e) {
129
+ this.#error = e;
130
+ throw e;
131
+ }
132
+ return;
133
+ }
134
+ let off = 0;
135
+ while (off < bytes.length) {
136
+ if (this.available === 0) {
137
+ await this.flush();
138
+ continue;
139
+ }
140
+ const toCopy = Math.min(this.available, bytes.length - off);
141
+ this.#buffer.set(bytes.subarray(off, off + toCopy), this.#writePos);
142
+ this.#writePos += toCopy;
143
+ off += toCopy;
144
+ if (this.#writePos === this.#buffer.byteLength) await this.flush();
145
+ }
146
+ }
147
+ };
148
+ //#endregion
149
+ exports.BufWriter = BufWriter;