@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.
- package/Core/HyperCore.d.ts +140 -72
- package/Core/HyperCore.d.ts.map +1 -1
- package/Core/HyperCore.js +204 -169
- package/Core/HyperCore.js.map +1 -1
- package/README.md +169 -96
- package/package.json +2 -2
- package/transports/manager.d.ts +39 -55
- package/transports/manager.d.ts.map +1 -1
- package/transports/manager.js +104 -167
- package/transports/manager.js.map +1 -1
- package/transports/node.d.ts +18 -30
- package/transports/node.d.ts.map +1 -1
- package/transports/node.js +55 -151
- package/transports/node.js.map +1 -1
- package/utils/decompress.d.ts +16 -18
- package/utils/decompress.d.ts.map +1 -1
- package/utils/decompress.js +62 -51
- package/utils/decompress.js.map +1 -1
- package/utils/merge.js +12 -18
- package/utils/merge.js.map +1 -1
- package/utils/normalize.d.ts +16 -17
- package/utils/normalize.d.ts.map +1 -1
- package/utils/normalize.js +58 -113
- package/utils/normalize.js.map +1 -1
- package/utils/response.d.ts +41 -23
- package/utils/response.d.ts.map +1 -1
- package/utils/response.js +76 -36
- package/utils/response.js.map +1 -1
package/transports/node.js
CHANGED
|
@@ -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
|
|
7
|
-
* @en
|
|
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
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* @ru Конфигурация клиента.
|
|
12
|
+
* @en Client configuration.
|
|
13
|
+
*/
|
|
11
14
|
config;
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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 -
|
|
38
|
-
* @returns Promise
|
|
39
|
-
* @throws If
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
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 &&
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
package/transports/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/transports/node.ts"],"names":[],"mappings":"AAAA
|
|
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"}
|
package/utils/decompress.d.ts
CHANGED
|
@@ -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
|
|
14
|
-
* @en
|
|
15
|
-
* @param body - Compressed
|
|
16
|
-
* @param encoding - Content-Encoding header
|
|
17
|
-
* @returns Decompressed buffer with a
|
|
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 Создаёт декомпрессирующий
|
|
22
|
-
* @en Creates a decompressing stream
|
|
23
|
-
*
|
|
24
|
-
* @
|
|
25
|
-
*
|
|
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
|
|
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"}
|
package/utils/decompress.js
CHANGED
|
@@ -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 Разбирает
|
|
5
|
-
* @en Parses
|
|
6
|
-
* @param encoding -
|
|
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
|
|
17
|
-
* @en Attaches a
|
|
18
|
-
* @param buffer - The buffer
|
|
19
|
-
* @returns
|
|
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
|
|
28
|
-
* @en Attaches a dump method that cancels the stream
|
|
29
|
-
* @param stream -
|
|
30
|
-
* @returns
|
|
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 Применяет
|
|
46
|
-
* @en Applies decompression
|
|
47
|
-
* @param input -
|
|
48
|
-
* @param encoding - Single encoding name (
|
|
49
|
-
* @returns Decompressed buffer, or original if encoding is
|
|
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
|
|
67
|
-
* @en
|
|
68
|
-
* @param body - Compressed
|
|
69
|
-
* @param encoding - Content-Encoding header
|
|
70
|
-
* @returns Decompressed buffer with a
|
|
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
|
-
|
|
75
|
-
|
|
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 Создаёт декомпрессирующий
|
|
81
|
-
* @en Creates a decompressing stream
|
|
82
|
-
*
|
|
83
|
-
* @
|
|
84
|
-
*
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
continue;
|
|
111
|
+
else {
|
|
112
|
+
if (!nodeReadableStream) {
|
|
113
|
+
nodeReadableStream = Readable.fromWeb(current);
|
|
103
114
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
package/utils/decompress.js.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
31
|
+
return Object.prototype.toString.call(item) === "[object Object]";
|
|
38
32
|
}
|
|
39
33
|
//# sourceMappingURL=merge.js.map
|
package/utils/merge.js.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/utils/normalize.d.ts
CHANGED
|
@@ -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 Нормализует
|
|
5
|
-
* @en Normalizes an HTTP method
|
|
6
|
-
* @param method - Raw method string
|
|
7
|
-
* @returns Normalized
|
|
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 из объекта запроса
|
|
12
|
-
* @en Extracts a URL from a request object
|
|
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
|
|
14
|
+
* @throws If URL cannot be determined.
|
|
16
15
|
*/
|
|
17
16
|
export declare function normalizeUrl(req: any): string;
|
|
18
17
|
/**
|
|
19
|
-
* @ru Нормализует заголовки
|
|
20
|
-
* @en Normalizes headers
|
|
21
|
-
* @param headers - Raw headers
|
|
22
|
-
* @returns Normalized headers object
|
|
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
|
|
27
|
-
* @en
|
|
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 -
|
|
30
|
-
* @returns
|
|
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
|
package/utils/normalize.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|