@hyperttp/core 1.3.1 → 1.3.3

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.
@@ -1,173 +1,77 @@
1
- import http from "node:http";
2
- import https from "node:https";
3
- import { Readable } from "node:stream";
4
- import { ReadableStream } from "node:stream/web";
5
1
  /**
6
- * @ru Реализация HTTP-транспорта на основе встроенных модулей Node.js (http/https). Поддерживает keep-alive, пулы соединений, автоматический Content-Length, обработку abort-сигналов.
7
- * @en HTTP transport implementation based on Node.js built-in modules (http/https). Supports keep-alive, connection pools, automatic Content-Length, and abort signal handling.
2
+ * @ru Транспорт на основе глобального fetch (Node.js 18+). Обрабатывает нормализацию URL, сериализацию тела и очистку потока.
3
+ * @en Transport based on the global fetch API (Node.js 18+). Handles URL normalisation, body serialisation, and stream cleanup.
4
+ */
5
+ /**
6
+ * @ru Реализация транспорта для Node.js с использованием fetch.
7
+ * @en Node.js transport implementation using fetch.
8
8
  */
9
9
  export class NodeTransport {
10
- /** @ru Конфигурация транспорта. @en Transport configuration. */
10
+ /**
11
+ * @ru Конфигурация клиента.
12
+ * @en Client configuration.
13
+ */
11
14
  config;
12
- httpAgent;
13
- httpsAgent;
15
+ isProduction;
16
+ cleanBaseUrl;
14
17
  /**
15
18
  * @ru Создаёт экземпляр NodeTransport.
16
19
  * @en Creates a NodeTransport instance.
17
- * @param config - Transport configuration.
20
+ * @param config - Transport configuration (may include baseUrl).
18
21
  */
19
22
  constructor(config) {
20
23
  this.config = config;
21
- const agentOptions = {
22
- keepAlive: true,
23
- keepAliveMsecs: config.network?.keepAliveTimeout ?? 30000,
24
- maxSockets: config.network?.maxConcurrent ?? 500,
25
- maxFreeSockets: Math.min(256, Math.floor((config.network?.maxConcurrent ?? 500) / 2)),
26
- scheduling: "lifo",
27
- };
28
- this.httpAgent = new http.Agent(agentOptions);
29
- this.httpsAgent = new https.Agent(agentOptions);
30
- }
31
- get baseUrl() {
32
- return this.config.baseUrl ?? "http://localhost:3000";
24
+ this.isProduction = process.env.NODE_ENV === "production";
25
+ const base = config.baseUrl ?? "http://localhost:3000";
26
+ this.cleanBaseUrl = base.endsWith("/") ? base.slice(0, -1) : base;
33
27
  }
34
28
  /**
35
- * @ru Выполняет HTTP-запрос.
36
- * @en Executes an HTTP request.
37
- * @param req - Request parameters (url, method, headers, body, signal).
38
- * @returns Promise with the transport response.
39
- * @throws If the request is aborted (AbortError).
40
- * @throws If localhost URL is used in production environment.
41
- * @throws On network or protocol errors.
29
+ * @ru Выполняет HTTP-запрос через fetch.
30
+ * @en Executes an HTTP request via fetch.
31
+ * @param req - Transport request object (method, url, headers, body, signal).
32
+ * @returns Promise resolving to a TransportResponse.
33
+ * @throws If a localhost URL is requested in production environment.
42
34
  */
43
35
  async execute(req) {
44
- const fullUrl = new URL(req.url, this.baseUrl);
45
- if (process.env.NODE_ENV === "production" &&
46
- fullUrl.hostname === "localhost") {
36
+ const urlStr = req.url;
37
+ const fullUrl = urlStr.startsWith("http")
38
+ ? urlStr
39
+ : urlStr.charCodeAt(0) === 47
40
+ ? this.cleanBaseUrl + urlStr
41
+ : this.cleanBaseUrl + "/" + urlStr;
42
+ if (this.isProduction && fullUrl.includes("//localhost")) {
47
43
  throw new Error("Localhost URL detected in production environment");
48
44
  }
49
- if (req.signal?.aborted) {
50
- const abortError = req.signal.reason || new Error("The operation was aborted.");
51
- if (abortError instanceof Error && !abortError.name)
52
- abortError.name = "AbortError";
53
- throw abortError;
54
- }
55
- const headers = { ...req.headers };
56
- const hasHeader = (name) => Object.keys(headers).some((k) => k.toLowerCase() === name.toLowerCase());
57
45
  let finalBody = req.body;
58
- if (finalBody !== undefined && finalBody !== null) {
59
- if (Buffer.isBuffer(finalBody)) {
60
- if (!hasHeader("content-length")) {
61
- headers["Content-Length"] = String(finalBody.byteLength);
62
- }
63
- }
64
- else if (typeof finalBody === "string") {
65
- if (!hasHeader("content-length")) {
66
- headers["Content-Length"] = String(Buffer.byteLength(finalBody, "utf-8"));
67
- }
68
- }
69
- else if (!(finalBody instanceof ReadableStream)) {
70
- finalBody = JSON.stringify(finalBody);
71
- if (!hasHeader("content-length")) {
72
- headers["Content-Length"] = String(Buffer.byteLength(finalBody, "utf-8"));
73
- }
74
- }
46
+ if (finalBody !== undefined &&
47
+ finalBody !== null &&
48
+ typeof finalBody === "object" &&
49
+ !Buffer.isBuffer(finalBody) &&
50
+ !(finalBody instanceof ReadableStream)) {
51
+ finalBody = JSON.stringify(finalBody);
75
52
  }
76
- return new Promise((resolve, reject) => {
77
- const isHttps = fullUrl.protocol === "https:";
78
- const requestFn = isHttps ? https.request : http.request;
79
- const agent = isHttps ? this.httpsAgent : this.httpAgent;
80
- let isDone = false;
81
- let nodeResInstance = null;
82
- const reqOptions = {
83
- method: req.method,
84
- headers: headers,
85
- agent,
86
- };
87
- const nodeReq = requestFn(fullUrl, reqOptions, (nodeRes) => {
88
- nodeResInstance = nodeRes;
89
- nodeRes.on("end", () => {
90
- isDone = true;
91
- cleanupSignal();
92
- });
93
- nodeRes.on("close", () => {
94
- isDone = true;
95
- cleanupSignal();
96
- });
97
- const webStream = Readable.toWeb(nodeRes);
98
- const dump = async () => {
99
- isDone = true;
100
- cleanupSignal();
101
- nodeRes.destroy();
102
- };
103
- const bodyPayload = Object.assign(webStream, {
104
- dump,
105
- });
106
- resolve({
107
- status: nodeRes.statusCode ?? 200,
108
- headers: nodeRes.headers,
109
- url: fullUrl.toString(),
110
- body: bodyPayload,
111
- });
112
- });
113
- let onAbort;
114
- if (req.signal) {
115
- onAbort = () => {
116
- if (isDone)
117
- return;
118
- isDone = true;
119
- nodeReq.destroy();
120
- if (nodeResInstance) {
121
- nodeResInstance.destroy();
122
- }
123
- const err = req.signal?.reason || new Error("The operation was aborted.");
124
- if (err instanceof Error && !err.name)
125
- err.name = "AbortError";
126
- reject(err);
127
- };
128
- req.signal.addEventListener("abort", onAbort, { once: true });
129
- }
130
- function cleanupSignal() {
131
- if (req.signal && onAbort) {
132
- req.signal.removeEventListener("abort", onAbort);
133
- }
134
- }
135
- nodeReq.on("error", (err) => {
136
- if (isDone)
137
- return;
138
- isDone = true;
139
- cleanupSignal();
140
- reject(err);
141
- });
142
- if (finalBody !== undefined && finalBody !== null) {
143
- if (Buffer.isBuffer(finalBody) || typeof finalBody === "string") {
144
- nodeReq.end(finalBody);
145
- }
146
- else if (finalBody instanceof ReadableStream) {
147
- Readable.fromWeb(finalBody).pipe(nodeReq);
148
- }
149
- }
150
- else {
151
- nodeReq.end();
152
- }
53
+ const res = await globalThis.fetch(fullUrl, {
54
+ method: req.method,
55
+ headers: req.headers,
56
+ body: finalBody,
57
+ signal: req.signal,
153
58
  });
154
- }
155
- /**
156
- * @ru Закрывает все активные соединения (graceful shutdown). Уничтожает HTTP/HTTPS агенты.
157
- * @en Closes all active connections (graceful shutdown). Destroys HTTP/HTTPS agents.
158
- * @returns Promise that resolves after closing.
159
- */
160
- async close() {
161
- this.httpAgent.destroy();
162
- this.httpsAgent.destroy();
163
- }
164
- /**
165
- * @ru Принудительно уничтожает транспорт (аналог close).
166
- * @en Forcefully destroys the transport (alias for close).
167
- * @returns Promise that resolves after destruction.
168
- */
169
- async destroy() {
170
- await this.close();
59
+ const webStream = res.body || new ReadableStream();
60
+ webStream.dump = async function () {
61
+ if (res.body && !res.body.locked) {
62
+ await res.body.cancel();
63
+ }
64
+ };
65
+ const resHeaders = {};
66
+ for (const [key, value] of res.headers) {
67
+ resHeaders[key] = value;
68
+ }
69
+ return {
70
+ status: res.status,
71
+ headers: resHeaders,
72
+ url: res.url,
73
+ body: webStream,
74
+ };
171
75
  }
172
76
  }
173
77
  //# sourceMappingURL=node.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/transports/node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/transports/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;GAGG;AACH,MAAM,OAAO,aAAa;IACxB;;;OAGG;IACI,MAAM,CAAsB;IAElB,YAAY,CAAU;IACtB,YAAY,CAAS;IAEtC;;;;OAIG;IACH,YAAY,MAA2B;QACrC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;QAE1D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,uBAAuB,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,OAAO,CAAC,GAAqB;QACxC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC;QAEvB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YACvC,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE;gBAC3B,CAAC,CAAC,IAAI,CAAC,YAAY,GAAG,MAAM;gBAC5B,CAAC,CAAC,IAAI,CAAC,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC;QAEvC,IAAI,IAAI,CAAC,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,SAAS,GAAQ,GAAG,CAAC,IAAI,CAAC;QAC9B,IACE,SAAS,KAAK,SAAS;YACvB,SAAS,KAAK,IAAI;YAClB,OAAO,SAAS,KAAK,QAAQ;YAC7B,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC3B,CAAC,CAAC,SAAS,YAAY,cAAc,CAAC,EACtC,CAAC;YACD,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE;YAC1C,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,OAAO,EAAE,GAAG,CAAC,OAAiC;YAC9C,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,cAAc,EAAE,CAAC;QAElD,SAAiB,CAAC,IAAI,GAAG,KAAK;YAC7B,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,UAAU,GAA2B,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACvC,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC1B,CAAC;QAED,OAAO;YACL,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,OAAO,EAAE,UAAU;YACnB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,IAAI,EAAE,SAAgD;SACvD,CAAC;IACJ,CAAC;CACF"}
@@ -1,28 +1,26 @@
1
1
  import type { TransportStreamExtensions } from "@hyperttp/types";
2
- /**
3
- * @ru Тип потока с расширениями транспорта (метод dump).
4
- * @en Stream type with transport extensions (dump method).
5
- */
6
2
  type StreamPayload = ReadableStream<Uint8Array> & TransportStreamExtensions;
7
- /**
8
- * @ru Тип буфера с расширениями транспорта (метод dump).
9
- * @en Buffer type with transport extensions (dump method).
10
- */
11
3
  type BufferPayload = Uint8Array & TransportStreamExtensions;
12
4
  /**
13
- * @ru Декомпрессирует буфер, последовательно применяя все кодировки из Content-Encoding.
14
- * @en Decompresses a buffer by sequentially applying all encodings from Content-Encoding.
15
- * @param body - Compressed buffer (Uint8Array).
16
- * @param encoding - Content-Encoding header value (e.g., 'gzip, deflate').
17
- * @returns Decompressed buffer with a no-op dump method.
5
+ * @ru Полностью декомпрессирует буфер, последовательно применяя цепочку алгоритмов из Content-Encoding.
6
+ * @en Fully decompresses a buffer by sequentially applying the Content-Encoding chain.
7
+ * @param body - Compressed Uint8Array body.
8
+ * @param encoding - Raw Content-Encoding header (may contain multiple comma-separated values).
9
+ * @returns Decompressed buffer with a noop dump method.
18
10
  */
19
11
  export declare function decompressBuffer(body: Uint8Array, encoding: string): BufferPayload;
20
12
  /**
21
- * @ru Создаёт декомпрессирующий поток для тела ответа, поддерживая цепочку кодировок. Использует DecompressionStream (если доступен) или Node.js zlib.
22
- * @en Creates a decompressing stream for the response body, supporting chained encodings. Uses DecompressionStream (if available) or Node.js zlib.
23
- * @param body - Source compressed stream.
24
- * @param encoding - Content-Encoding header value.
25
- * @returns Transformed stream with a dump method that cancels the stream.
13
+ * @ru Создаёт декомпрессирующий поток, минимизируя конвертацию между Web и Node.js стримами.
14
+ * @en Creates a decompressing stream while minimising conversion between Web and Node.js streams.
15
+ *
16
+ * @ru Алгоритм: для gzip/deflate при наличии глобального DecompressionStream использует Web Streams API,
17
+ * иначе переключается на Node.js zlib. Brotli всегда идёт через Node.js (до появления стандартного Web-потока).
18
+ * @en Algorithm: for gzip/deflate, if global DecompressionStream is available, uses Web Streams API;
19
+ * otherwise falls back to Node.js zlib. Brotli always goes through Node.js (until standard Web stream is available).
20
+ *
21
+ * @param body - Original compressed ReadableStream.
22
+ * @param encoding - Raw Content-Encoding header (may be a chain).
23
+ * @returns Decompressed ReadableStream with a dump method that cancels the stream.
26
24
  */
27
25
  export declare function createDecompressStream(body: ReadableStream<Uint8Array>, encoding: string): StreamPayload;
28
26
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"decompress.d.ts","sourceRoot":"","sources":["../../src/utils/decompress.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAEjE;;;GAGG;AACH,KAAK,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,yBAAyB,CAAC;AAE5E;;;GAGG;AACH,KAAK,aAAa,GAAG,UAAU,GAAG,yBAAyB,CAAC;AAuE5D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,GACf,aAAa,CAQf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,QAAQ,EAAE,MAAM,GACf,aAAa,CA0Df"}
1
+ {"version":3,"file":"decompress.d.ts","sourceRoot":"","sources":["../../src/utils/decompress.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAEjE,KAAK,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,yBAAyB,CAAC;AAC5E,KAAK,aAAa,GAAG,UAAU,GAAG,yBAAyB,CAAC;AAqE5D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,GACf,aAAa,CASf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,QAAQ,EAAE,MAAM,GACf,aAAa,CA+Cf"}
@@ -1,9 +1,9 @@
1
1
  import { brotliDecompressSync, createBrotliDecompress, createGunzip, createInflate, gunzipSync, inflateSync, } from "node:zlib";
2
2
  import { Readable } from "node:stream";
3
3
  /**
4
- * @ru Разбирает строку Content-Encoding на массив кодировок (без identity).
5
- * @en Parses a Content-Encoding string into an array of encodings (excluding identity).
6
- * @param encoding - The Content-Encoding header value.
4
+ * @ru Разбирает заголовок Content-Encoding в массив алгоритмов, исключая "identity".
5
+ * @en Parses Content-Encoding header into an array of algorithms, excluding "identity".
6
+ * @param encoding - Raw Content-Encoding header value (e.g., "gzip, deflate").
7
7
  * @returns Array of normalized encoding names.
8
8
  */
9
9
  function parseEncodings(encoding) {
@@ -13,10 +13,10 @@ function parseEncodings(encoding) {
13
13
  .filter((part) => part.length > 0 && part !== "identity");
14
14
  }
15
15
  /**
16
- * @ru Прикрепляет пустой метод dump к буферу для соответствия интерфейсу TransportStreamExtensions.
17
- * @en Attaches a no-op dump method to a buffer to conform to TransportStreamExtensions.
18
- * @param buffer - The buffer to decorate.
19
- * @returns The same buffer with a dump method.
16
+ * @ru Добавляет noop-метод dump к буферу, сохраняя совместимость с TransportStreamExtensions.
17
+ * @en Attaches a noop dump method to a buffer, maintaining TransportStreamExtensions compatibility.
18
+ * @param buffer - The Uint8Array buffer.
19
+ * @returns Buffer with dump method attached.
20
20
  */
21
21
  function attachNoopDump(buffer) {
22
22
  const payload = buffer;
@@ -24,10 +24,10 @@ function attachNoopDump(buffer) {
24
24
  return payload;
25
25
  }
26
26
  /**
27
- * @ru Прикрепляет метод dump, который отменяет поток, к ReadableStream.
28
- * @en Attaches a dump method that cancels the stream to a ReadableStream.
29
- * @param stream - The stream to decorate.
30
- * @returns The same stream with a dump method.
27
+ * @ru Добавляет метод dump к стриму, который отменяет (cancel) поток.
28
+ * @en Attaches a dump method to a stream that cancels the stream.
29
+ * @param stream - ReadableStream to enhance.
30
+ * @returns Stream with dump method attached.
31
31
  */
32
32
  function attachStreamDump(stream) {
33
33
  const payload = stream;
@@ -42,14 +42,15 @@ function attachStreamDump(stream) {
42
42
  return payload;
43
43
  }
44
44
  /**
45
- * @ru Применяет декомпрессию один раз для заданной кодировки. Поддерживает gzip, deflate, br.
46
- * @en Applies decompression once for a given encoding. Supports gzip, deflate, br.
47
- * @param input - The compressed input buffer.
48
- * @param encoding - Single encoding name (e.g., 'gzip', 'deflate', 'br').
49
- * @returns Decompressed buffer, or original if encoding is not recognized.
45
+ * @ru Применяет один этап декомпрессии к буферу синхронно (для одиночных чанков).
46
+ * @en Applies a single decompression step to a buffer synchronously (for single chunks).
47
+ * @param input - Compressed buffer.
48
+ * @param encoding - Single encoding name ("gzip", "deflate", "br").
49
+ * @returns Decompressed buffer, or original if encoding is unknown.
50
+ * @throws If decompression fails due to corrupt data.
50
51
  */
51
52
  function decompressOnce(input, encoding) {
52
- const buffer = Buffer.from(input);
53
+ const buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
53
54
  switch (encoding) {
54
55
  case "gzip":
55
56
  case "x-gzip":
@@ -63,55 +64,65 @@ function decompressOnce(input, encoding) {
63
64
  }
64
65
  }
65
66
  /**
66
- * @ru Декомпрессирует буфер, последовательно применяя все кодировки из Content-Encoding.
67
- * @en Decompresses a buffer by sequentially applying all encodings from Content-Encoding.
68
- * @param body - Compressed buffer (Uint8Array).
69
- * @param encoding - Content-Encoding header value (e.g., 'gzip, deflate').
70
- * @returns Decompressed buffer with a no-op dump method.
67
+ * @ru Полностью декомпрессирует буфер, последовательно применяя цепочку алгоритмов из Content-Encoding.
68
+ * @en Fully decompresses a buffer by sequentially applying the Content-Encoding chain.
69
+ * @param body - Compressed Uint8Array body.
70
+ * @param encoding - Raw Content-Encoding header (may contain multiple comma-separated values).
71
+ * @returns Decompressed buffer with a noop dump method.
71
72
  */
72
73
  export function decompressBuffer(body, encoding) {
73
74
  let current = body;
74
- for (const enc of parseEncodings(encoding)) {
75
- current = decompressOnce(current, enc);
75
+ const encodings = parseEncodings(encoding);
76
+ for (let i = 0; i < encodings.length; i++) {
77
+ current = decompressOnce(current, encodings[i]);
76
78
  }
77
79
  return attachNoopDump(current);
78
80
  }
79
81
  /**
80
- * @ru Создаёт декомпрессирующий поток для тела ответа, поддерживая цепочку кодировок. Использует DecompressionStream (если доступен) или Node.js zlib.
81
- * @en Creates a decompressing stream for the response body, supporting chained encodings. Uses DecompressionStream (if available) or Node.js zlib.
82
- * @param body - Source compressed stream.
83
- * @param encoding - Content-Encoding header value.
84
- * @returns Transformed stream with a dump method that cancels the stream.
82
+ * @ru Создаёт декомпрессирующий поток, минимизируя конвертацию между Web и Node.js стримами.
83
+ * @en Creates a decompressing stream while minimising conversion between Web and Node.js streams.
84
+ *
85
+ * @ru Алгоритм: для gzip/deflate при наличии глобального DecompressionStream использует Web Streams API,
86
+ * иначе переключается на Node.js zlib. Brotli всегда идёт через Node.js (до появления стандартного Web-потока).
87
+ * @en Algorithm: for gzip/deflate, if global DecompressionStream is available, uses Web Streams API;
88
+ * otherwise falls back to Node.js zlib. Brotli always goes through Node.js (until standard Web stream is available).
89
+ *
90
+ * @param body - Original compressed ReadableStream.
91
+ * @param encoding - Raw Content-Encoding header (may be a chain).
92
+ * @returns Decompressed ReadableStream with a dump method that cancels the stream.
85
93
  */
86
94
  export function createDecompressStream(body, encoding) {
95
+ const encodings = parseEncodings(encoding);
96
+ if (encodings.length === 0)
97
+ return attachStreamDump(body);
87
98
  let current = body;
88
- for (const enc of parseEncodings(encoding)) {
89
- if (enc === "gzip" || enc === "x-gzip") {
90
- if (typeof DecompressionStream !== "undefined") {
91
- current = current.pipeThrough(new DecompressionStream("gzip"));
92
- continue;
99
+ let nodeReadableStream = null;
100
+ for (let i = 0; i < encodings.length; i++) {
101
+ const enc = encodings[i];
102
+ const isGzip = enc === "gzip" || enc === "x-gzip";
103
+ const isDeflate = enc === "deflate";
104
+ if ((isGzip || isDeflate) && typeof DecompressionStream !== "undefined") {
105
+ if (nodeReadableStream) {
106
+ current = Readable.toWeb(nodeReadableStream);
107
+ nodeReadableStream = null;
93
108
  }
94
- const nodeReadable = Readable.fromWeb(current);
95
- const decompressed = nodeReadable.pipe(createGunzip());
96
- current = Readable.toWeb(decompressed);
97
- continue;
109
+ current = current.pipeThrough(new DecompressionStream(isGzip ? "gzip" : "deflate"));
98
110
  }
99
- if (enc === "deflate") {
100
- if (typeof DecompressionStream !== "undefined") {
101
- current = current.pipeThrough(new DecompressionStream("deflate"));
102
- continue;
111
+ else {
112
+ if (!nodeReadableStream) {
113
+ nodeReadableStream = Readable.fromWeb(current);
103
114
  }
104
- const nodeReadable = Readable.fromWeb(current);
105
- const decompressed = nodeReadable.pipe(createInflate());
106
- current = Readable.toWeb(decompressed);
107
- continue;
108
- }
109
- if (enc === "br") {
110
- const nodeReadable = Readable.fromWeb(current);
111
- const decompressed = nodeReadable.pipe(createBrotliDecompress());
112
- current = Readable.toWeb(decompressed);
115
+ if (isGzip)
116
+ nodeReadableStream = nodeReadableStream.pipe(createGunzip());
117
+ else if (isDeflate)
118
+ nodeReadableStream = nodeReadableStream.pipe(createInflate());
119
+ else if (enc === "br")
120
+ nodeReadableStream = nodeReadableStream.pipe(createBrotliDecompress());
113
121
  }
114
122
  }
123
+ if (nodeReadableStream) {
124
+ current = Readable.toWeb(nodeReadableStream);
125
+ }
115
126
  return attachStreamDump(current);
116
127
  }
117
128
  //# sourceMappingURL=decompress.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decompress.js","sourceRoot":"","sources":["../../src/utils/decompress.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAevC;;;;;GAKG;AACH,SAAS,cAAc,CAAC,QAAgB;IACtC,OAAO,QAAQ;SACZ,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SACxC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,KAAK,UAAU,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,MAAkB;IACxC,MAAM,OAAO,GAAG,MAAuB,CAAC;IACxC,OAAO,CAAC,IAAI,GAAG,KAAK,IAAmB,EAAE,GAAE,CAAC,CAAC;IAC7C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,MAAkC;IAC1D,MAAM,OAAO,GAAG,MAAuB,CAAC;IACxC,OAAO,CAAC,IAAI,GAAG,KAAK,IAAmB,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,EAAE;QACJ,CAAC;IACH,CAAC,CAAC;IACF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,KAAiB,EAAE,QAAgB;IACzD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAElC,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;QAE5B,KAAK,SAAS;YACZ,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;QAE7B,KAAK,IAAI;YACP,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAEtC;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAgB,EAChB,QAAgB;IAEhB,IAAI,OAAO,GAAe,IAAI,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAgC,EAChC,QAAgB;IAEhB,IAAI,OAAO,GAA+B,IAAI,CAAC;IAE/C,KAAK,MAAM,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,OAAO,mBAAmB,KAAK,WAAW,EAAE,CAAC;gBAC/C,OAAO,GAAG,OAAO,CAAC,WAAW,CAC3B,IAAI,mBAAmB,CAAC,MAAM,CAG7B,CACF,CAAC;gBACF,SAAS;YACX,CAAC;YAED,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CACnC,OAA0E,CAC3E,CAAC;YACF,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;YACvD,OAAO,GAAG,QAAQ,CAAC,KAAK,CACtB,YAAY,CAC4B,CAAC;YAC3C,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,IAAI,OAAO,mBAAmB,KAAK,WAAW,EAAE,CAAC;gBAC/C,OAAO,GAAG,OAAO,CAAC,WAAW,CAC3B,IAAI,mBAAmB,CAAC,SAAS,CAGhC,CACF,CAAC;gBACF,SAAS;YACX,CAAC;YAED,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CACnC,OAA0E,CAC3E,CAAC;YACF,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;YACxD,OAAO,GAAG,QAAQ,CAAC,KAAK,CACtB,YAAY,CAC4B,CAAC;YAC3C,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CACnC,OAA0E,CAC3E,CAAC;YACF,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC;YACjE,OAAO,GAAG,QAAQ,CAAC,KAAK,CACtB,YAAY,CAC4B,CAAC;QAC7C,CAAC;IACH,CAAC;IAED,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"decompress.js","sourceRoot":"","sources":["../../src/utils/decompress.ts"],"names":[],"mappings":"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"}
package/utils/merge.js CHANGED
@@ -7,23 +7,17 @@
7
7
  */
8
8
  export function deepMerge(target, source) {
9
9
  const output = { ...target };
10
- if (isObject(target) && isObject(source)) {
11
- for (const key in source) {
12
- if (Object.prototype.hasOwnProperty.call(source, key)) {
13
- const sourceValue = source[key];
14
- const targetValue = output[key];
15
- if (isObject(sourceValue)) {
16
- if (key in output && isObject(targetValue)) {
17
- output[key] = deepMerge(targetValue, sourceValue);
18
- }
19
- else {
20
- output[key] = deepMerge({}, sourceValue);
21
- }
22
- }
23
- else if (sourceValue !== undefined) {
24
- output[key] = sourceValue;
25
- }
26
- }
10
+ for (const key of Object.keys(source)) {
11
+ const sourceValue = source[key];
12
+ const targetValue = output[key];
13
+ if (isObject(sourceValue) && isObject(targetValue)) {
14
+ output[key] = deepMerge(targetValue, sourceValue);
15
+ continue;
16
+ }
17
+ if (sourceValue !== undefined) {
18
+ output[key] = isObject(sourceValue)
19
+ ? deepMerge({}, sourceValue)
20
+ : sourceValue;
27
21
  }
28
22
  }
29
23
  return output;
@@ -34,6 +28,6 @@ export function deepMerge(target, source) {
34
28
  * @param item - Evaluated runtime variable value context.
35
29
  */
36
30
  function isObject(item) {
37
- return typeof item === "object" && item !== null && !Array.isArray(item);
31
+ return Object.prototype.toString.call(item) === "[object Object]";
38
32
  }
39
33
  //# sourceMappingURL=merge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/utils/merge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAGvB,MAAS,EAAE,MAAS;IACpB,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,EAA6B,CAAC;IAExD,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;gBACtD,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;gBAChC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;gBAEhC,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC1B,IAAI,GAAG,IAAI,MAAM,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;wBAC3C,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;oBACpD,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;oBAC3C,CAAC;gBACH,CAAC;qBAAM,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;oBACrC,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;gBAC5B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAe,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,IAAa;IAC7B,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC3E,CAAC"}
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/utils/merge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAGvB,MAAS,EAAE,MAAS;IACpB,MAAM,MAAM,GAA4B,EAAE,GAAG,MAAM,EAAE,CAAC;IAEtD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAEhC,IAAI,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CACrB,WAAsC,EACtC,WAAsC,CACvC,CAAC;YACF,SAAS;QACX,CAAC;QAED,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC;gBACjC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,WAAsC,CAAC;gBACvD,CAAC,CAAC,WAAW,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,MAAe,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,IAAa;IAC7B,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,iBAAiB,CAAC;AACpE,CAAC"}
@@ -1,33 +1,32 @@
1
1
  import type { Method, RequestBodyData } from "@hyperttp/types";
2
- export type NormalizedBody = string | Uint8Array | ArrayBuffer | FormData | URLSearchParams | ReadableStream | Blob | null | undefined;
3
2
  /**
4
- * @ru Нормализует метод HTTP: приводит к верхнему регистру, использует кэш для часто встречающихся методов.
5
- * @en Normalizes an HTTP method: converts to uppercase, uses cache for common methods.
6
- * @param method - Raw method string (e.g., 'get', 'POST').
7
- * @returns Normalized uppercase method.
3
+ * @ru Нормализует строку HTTP-метода в стандартную заглавную форму (например, "get" → "GET").
4
+ * @en Normalizes an HTTP method string to its standard uppercase form (e.g., "get" → "GET").
5
+ * @param method - Raw method string.
6
+ * @returns Normalized method (e.g., "GET", "POST", etc.).
8
7
  */
9
8
  export declare function normalizeMethod(method: string): Method;
10
9
  /**
11
- * @ru Извлекает URL из объекта запроса или строки. Поддерживает поля url, _url, или комбинацию scheme/host/path.
12
- * @en Extracts a URL from a request object or string. Supports url, _url fields, or scheme/host/path combination.
13
- * @param req - Request object or URL string.
10
+ * @ru Извлекает и нормализует URL из строки или объекта запроса (поддерживает поля url, _url, или комбинацию scheme/host/path).
11
+ * @en Extracts and normalizes a URL from a string or request object (supports url, _url fields, or scheme/host/path combination).
12
+ * @param req - Request object or plain URL string.
14
13
  * @returns Normalized URL string.
15
- * @throws If URL cannot be resolved.
14
+ * @throws If URL cannot be determined.
16
15
  */
17
16
  export declare function normalizeUrl(req: any): string;
18
17
  /**
19
- * @ru Нормализует заголовки из различных форматов (массив, объект) в единый объект с правильной обработкой множественных значений (cookie, set-cookie).
20
- * @en Normalizes headers from various formats (array, object) into a single object with proper handling of multi-value headers (cookie, set-cookie).
21
- * @param headers - Raw headers in array or object format.
22
- * @returns Normalized headers object where values are strings or arrays of strings (for set-cookie).
18
+ * @ru Нормализует входные заголовки (объект, массив пар) в стандартный объект с ключами в нижнем регистре и правильным объединением значений.
19
+ * @en Normalizes input headers (object, array of pairs) into a standard object with lower‑case keys and proper value merging.
20
+ * @param headers - Raw headers (plain object, array of [key, value] pairs, or null/undefined).
21
+ * @returns Normalized headers object (keys are lower‑case, values are strings or arrays for set-cookie).
23
22
  */
24
23
  export declare function normalizeHeaders(headers: unknown): Record<string, string | string[]>;
25
24
  /**
26
- * @ru Нормализует тело запроса: для методов GET и HEAD тело всегда undefined, для остальных возвращает переданное тело.
27
- * @en Normalizes request body: for GET and HEAD methods body is always undefined, for others returns the provided body.
25
+ * @ru Возвращает тело запроса только для методов, поддерживающих тело (не GET/HEAD), иначе undefined.
26
+ * @en Returns the request body only for methods that support a body (not GET/HEAD), otherwise undefined.
28
27
  * @param method - HTTP method.
29
- * @param body - Raw request body.
30
- * @returns Normalized body or undefined.
28
+ * @param body - Original request body (if any).
29
+ * @returns The body for allowed methods, or undefined for GET/HEAD.
31
30
  */
32
31
  export declare function normalizeBody(method: Method, body: RequestBodyData | undefined): RequestBodyData | undefined;
33
32
  //# sourceMappingURL=normalize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/utils/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE/D,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,UAAU,GACV,WAAW,GACX,QAAQ,GACR,eAAe,GACf,cAAc,GACd,IAAI,GACJ,IAAI,GACJ,SAAS,CAAC;AA+Cd;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAItD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAY7C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,GACf,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CA+HnC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,eAAe,GAAG,SAAS,GAChC,eAAe,GAAG,SAAS,CAK7B"}
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/utils/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAuC/D;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAU7C;AA0CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,GACf,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CA+CnC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,eAAe,GAAG,SAAS,GAChC,eAAe,GAAG,SAAS,CAE7B"}