@hyperttp/core 1.3.1 → 1.4.0
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 +135 -77
- package/Core/HyperCore.d.ts.map +1 -1
- package/Core/HyperCore.js +280 -246
- package/Core/HyperCore.js.map +1 -1
- package/README.md +169 -96
- package/package.json +14 -8
- package/transports/browser.d.ts +228 -0
- package/transports/browser.d.ts.map +1 -0
- package/transports/browser.js +451 -0
- package/transports/browser.js.map +1 -0
- package/transports/manager.d.ts +9 -85
- package/transports/manager.d.ts.map +1 -1
- package/transports/manager.js +152 -222
- package/transports/manager.js.map +1 -1
- package/transports/node.d.ts +4 -36
- package/transports/node.d.ts.map +1 -1
- package/transports/node.js +119 -159
- package/transports/node.js.map +1 -1
- package/utils/decompress.d.ts +16 -20
- package/utils/decompress.d.ts.map +1 -1
- package/utils/decompress.js +151 -60
- package/utils/decompress.js.map +1 -1
- package/utils/errors.d.ts +0 -10
- package/utils/errors.d.ts.map +1 -1
- package/utils/errors.js +1 -11
- package/utils/errors.js.map +1 -1
- package/utils/merge.d.ts +0 -3
- package/utils/merge.d.ts.map +1 -1
- package/utils/merge.js +12 -22
- package/utils/merge.js.map +1 -1
- package/utils/normalize.d.ts +40 -21
- package/utils/normalize.d.ts.map +1 -1
- package/utils/normalize.js +217 -138
- package/utils/normalize.js.map +1 -1
- package/utils/pipeline.d.ts +10 -30
- package/utils/pipeline.d.ts.map +1 -1
- package/utils/pipeline.js +44 -44
- package/utils/pipeline.js.map +1 -1
- package/utils/response.d.ts +105 -23
- package/utils/response.d.ts.map +1 -1
- package/utils/response.js +308 -38
- package/utils/response.js.map +1 -1
- package/utils/retryUtils.d.ts +2 -10
- package/utils/retryUtils.d.ts.map +1 -1
- package/utils/retryUtils.js +17 -21
- package/utils/retryUtils.js.map +1 -1
package/Core/HyperCore.js
CHANGED
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
import { defaultConfig } from "../defaultConfig.js";
|
|
2
|
-
import { mapResponseFast, mapStreamFast, mergeHeadersFast
|
|
2
|
+
import { mapResponseFast, mapStreamFast, mergeHeadersFast } from "../utils/response.js";
|
|
3
3
|
import { TransportManager } from "../transports/manager.js";
|
|
4
4
|
import { createPipelines, executeErrorPipeline, executeRequestPipeline, executeResponsePipeline, executeResponseDataPipeline, insertHookSorted, } from "../utils/pipeline.js";
|
|
5
|
-
import { normalizeBody, normalizeHeaders, normalizeUrl
|
|
6
|
-
import { decompressBuffer, createDecompressStream, } from "../utils/decompress.js";
|
|
5
|
+
import { normalizeBody, normalizeHeaders, normalizeUrl } from "../utils/normalize.js";
|
|
7
6
|
/**
|
|
8
|
-
* @ru
|
|
9
|
-
*
|
|
7
|
+
* @ru Глобальный кэш URL для избежания повторного парсинга через `new URL()`.
|
|
8
|
+
* Использует `Object.create(null)` для быстрого доступа без прототипа.
|
|
9
|
+
* @en Global URL cache to avoid repeated parsing via `new URL()`.
|
|
10
|
+
* Uses `Object.create(null)` for fast prototype-less access.
|
|
11
|
+
*/
|
|
12
|
+
const urlCache = Object.create(null);
|
|
13
|
+
let urlCacheCount = 0;
|
|
14
|
+
const MAX_CACHE_SIZE = 512;
|
|
15
|
+
/**
|
|
16
|
+
* @ru Пул переиспользуемых объектов InternalRequest для zero-allocation в горячем пути.
|
|
17
|
+
* @en Pool of reusable InternalRequest objects for zero-allocation in the hot path.
|
|
18
|
+
*/
|
|
19
|
+
const requestPool = [];
|
|
20
|
+
const MAX_POOL_SIZE = 64;
|
|
21
|
+
/**
|
|
22
|
+
* @ru Ядро HTTP-клиента Hyperttp. Обеспечивает диспетчеризацию запросов через
|
|
23
|
+
* конвейер плагинов, пулинг объектов запросов и кэширование URL.
|
|
24
|
+
* @en Hyperttp HTTP client core. Provides request dispatching through the
|
|
25
|
+
* plugin pipeline, request object pooling, and URL caching.
|
|
10
26
|
*/
|
|
11
27
|
export class HyperCore {
|
|
12
|
-
/** @ru Конфигурация клиента (базовый URL, таймауты, заголовки и т.д.). @en Client configuration (base URL, timeouts, headers, etc.). */
|
|
13
28
|
config;
|
|
14
29
|
transportManager;
|
|
30
|
+
transportReady;
|
|
15
31
|
defaultHeaders;
|
|
16
32
|
pluginCtx;
|
|
17
33
|
pipelines = createPipelines();
|
|
34
|
+
hasRequestPlugins = false;
|
|
35
|
+
hasResponseDataPlugins = false;
|
|
36
|
+
hasResponsePlugins = false;
|
|
37
|
+
hasErrorPlugins = false;
|
|
18
38
|
/**
|
|
19
|
-
* @ru Создаёт экземпляр
|
|
20
|
-
* @en Creates
|
|
39
|
+
* @ru Создаёт новый экземпляр ядра HTTP-клиента.
|
|
40
|
+
* @en Creates a new HTTP client core instance.
|
|
21
41
|
* @param config - Client configuration options.
|
|
22
42
|
* @param transport - Optional custom transport implementation.
|
|
23
43
|
*/
|
|
@@ -28,6 +48,9 @@ export class HyperCore {
|
|
|
28
48
|
network: { ...defaultConfig.network, ...config.network },
|
|
29
49
|
};
|
|
30
50
|
this.transportManager = new TransportManager(this.config, transport);
|
|
51
|
+
this.transportReady = this.transportManager.getSync()
|
|
52
|
+
? Promise.resolve(this.transportManager.getSync())
|
|
53
|
+
: this.transportManager.ensure();
|
|
31
54
|
this.defaultHeaders = normalizeHeaders({
|
|
32
55
|
Accept: "application/json, text/plain, */*",
|
|
33
56
|
"Accept-Encoding": "gzip, deflate, br",
|
|
@@ -36,392 +59,403 @@ export class HyperCore {
|
|
|
36
59
|
});
|
|
37
60
|
this.pluginCtx = { config: this.config, core: this };
|
|
38
61
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
/**
|
|
63
|
+
* @ru Возвращает имя класса текущего активного транспорта.
|
|
64
|
+
* @en Returns the class name of the currently active transport.
|
|
65
|
+
* @returns Promise resolving to the transport class name.
|
|
66
|
+
*/
|
|
67
|
+
async getTransportName() {
|
|
68
|
+
const t = this.transportManager.transport ?? (await this.transportManager.get());
|
|
69
|
+
return t.constructor.name;
|
|
44
70
|
}
|
|
45
|
-
|
|
71
|
+
/**
|
|
72
|
+
* @ru Отправляет внутренний запрос через полный конвейер обработки:
|
|
73
|
+
* плагины запроса → транспорт → плагины данных ответа → маппинг → плагины ответа.
|
|
74
|
+
* @en Dispatches an internal request through the full processing pipeline:
|
|
75
|
+
* request plugins → transport → response data plugins → mapping → response plugins.
|
|
76
|
+
* @template T - Expected response body type.
|
|
77
|
+
* @param req - The normalized internal request object.
|
|
78
|
+
* @returns Promise resolving to the HTTP response.
|
|
79
|
+
*/
|
|
80
|
+
async dispatch(req) {
|
|
46
81
|
try {
|
|
47
|
-
if (this.
|
|
48
|
-
const shortCircuit = await executeRequestPipeline(this.pipelines.request, req, this.pluginCtx);
|
|
49
|
-
if (shortCircuit) {
|
|
50
|
-
|
|
82
|
+
if (this.hasRequestPlugins) {
|
|
83
|
+
const shortCircuit = await Promise.resolve(executeRequestPipeline(this.pipelines.request, req, this.pluginCtx));
|
|
84
|
+
if (shortCircuit != null) {
|
|
85
|
+
this.recycleRequest(req);
|
|
51
86
|
return shortCircuit;
|
|
52
87
|
}
|
|
53
88
|
}
|
|
54
|
-
const transport = this.
|
|
89
|
+
const transport = this.transportManager.getSync() ?? (await this.transportReady);
|
|
55
90
|
let rawResponse = await transport.execute(req);
|
|
56
|
-
if (this.
|
|
57
|
-
rawResponse = await executeResponseDataPipeline(this.pipelines.responseData, rawResponse, this.pluginCtx);
|
|
91
|
+
if (this.hasResponseDataPlugins) {
|
|
92
|
+
rawResponse = await Promise.resolve(executeResponseDataPipeline(this.pipelines.responseData, rawResponse, this.pluginCtx));
|
|
58
93
|
}
|
|
59
|
-
|
|
60
|
-
const isStream = req.meta?.responseType === "stream";
|
|
61
|
-
const response = isStream
|
|
94
|
+
const response = req.meta?.responseType === "stream"
|
|
62
95
|
? mapStreamFast(rawResponse)
|
|
63
96
|
: mapResponseFast(rawResponse);
|
|
64
|
-
|
|
97
|
+
if (this.hasResponsePlugins) {
|
|
98
|
+
await Promise.resolve(executeResponsePipeline(this.pipelines.responseMutators, this.pipelines.responseSideEffects, response, req, this.pluginCtx, this.config.logger));
|
|
99
|
+
}
|
|
100
|
+
this.recycleRequest(req);
|
|
65
101
|
return response;
|
|
66
102
|
}
|
|
67
103
|
catch (error) {
|
|
104
|
+
this.recycleRequest(req);
|
|
68
105
|
return this.handleDispatchError(error, req);
|
|
69
106
|
}
|
|
70
107
|
}
|
|
71
108
|
/**
|
|
72
|
-
* @ru
|
|
73
|
-
* @en
|
|
74
|
-
* @param
|
|
75
|
-
* @returns
|
|
76
|
-
*/
|
|
77
|
-
async dispatch(req) {
|
|
78
|
-
return this.dispatchInternal(req);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* @ru Регистрирует плагин для расширения функциональности клиента.
|
|
82
|
-
* @en Registers a plugin to extend client functionality.
|
|
83
|
-
* @param plugin - Plugin instance.
|
|
84
|
-
* @returns This instance for chaining.
|
|
109
|
+
* @ru Регистрирует плагин в экземпляре клиента. Плагины выполняются в порядке приоритета.
|
|
110
|
+
* @en Registers a plugin into the client instance. Plugins are executed in priority order.
|
|
111
|
+
* @param plugin - The plugin instance to register.
|
|
112
|
+
* @returns The current instance for chaining.
|
|
85
113
|
*/
|
|
86
114
|
use(plugin) {
|
|
87
115
|
const isEnabled = plugin.enabled ? plugin.enabled(this.config) : true;
|
|
88
116
|
if (!isEnabled)
|
|
89
117
|
return this;
|
|
90
|
-
|
|
91
|
-
plugin.setup(this.pluginCtx);
|
|
118
|
+
plugin.setup?.(this.pluginCtx);
|
|
92
119
|
const priority = plugin.priority ?? 0;
|
|
120
|
+
const hook = { name: plugin.name, priority };
|
|
93
121
|
if (plugin.onRequest) {
|
|
94
122
|
insertHookSorted(this.pipelines.request, {
|
|
95
|
-
|
|
96
|
-
priority,
|
|
123
|
+
...hook,
|
|
97
124
|
run: plugin.onRequest,
|
|
98
125
|
});
|
|
126
|
+
this.hasRequestPlugins = true;
|
|
99
127
|
}
|
|
100
128
|
if (plugin.onResponse) {
|
|
101
|
-
const
|
|
129
|
+
const target = plugin.mode === "background"
|
|
102
130
|
? this.pipelines.responseSideEffects
|
|
103
131
|
: this.pipelines.responseMutators;
|
|
104
|
-
insertHookSorted(
|
|
105
|
-
|
|
106
|
-
priority,
|
|
107
|
-
run: plugin.onResponse,
|
|
108
|
-
});
|
|
132
|
+
insertHookSorted(target, { ...hook, run: plugin.onResponse });
|
|
133
|
+
this.hasResponsePlugins = true;
|
|
109
134
|
}
|
|
110
135
|
if (plugin.onResponseData) {
|
|
111
136
|
insertHookSorted(this.pipelines.responseData, {
|
|
112
|
-
|
|
113
|
-
priority,
|
|
137
|
+
...hook,
|
|
114
138
|
run: plugin.onResponseData,
|
|
115
139
|
});
|
|
140
|
+
this.hasResponseDataPlugins = true;
|
|
116
141
|
}
|
|
117
142
|
if (plugin.onError) {
|
|
118
|
-
insertHookSorted(this.pipelines.error, {
|
|
119
|
-
|
|
120
|
-
priority,
|
|
121
|
-
run: plugin.onError,
|
|
122
|
-
});
|
|
143
|
+
insertHookSorted(this.pipelines.error, { ...hook, run: plugin.onError });
|
|
144
|
+
this.hasErrorPlugins = true;
|
|
123
145
|
}
|
|
124
146
|
return this;
|
|
125
147
|
}
|
|
126
|
-
/**
|
|
127
|
-
* @ru Выполняет GET-запрос и возвращает ответ в виде потока (StreamResponse).
|
|
128
|
-
* @en Performs a GET request and returns the response as a stream (StreamResponse).
|
|
129
|
-
* @param req - Request URL or RequestInterface object.
|
|
130
|
-
* @param signal - Optional abort signal.
|
|
131
|
-
* @returns Promise with the stream response.
|
|
132
|
-
*/
|
|
133
|
-
async stream(req, signal) {
|
|
134
|
-
const internalReq = this.buildInternalRequest("GET", req, undefined, signal);
|
|
135
|
-
internalReq.meta = { ...internalReq.meta, responseType: "stream" };
|
|
136
|
-
return this.dispatchInternal(internalReq);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* @ru Выполняет POST-запрос и возвращает ответ в виде потока (StreamResponse).
|
|
140
|
-
* @en Performs a POST request and returns the response as a stream (StreamResponse).
|
|
141
|
-
* @param req - Request URL or RequestInterface object.
|
|
142
|
-
* @param body - Request body data.
|
|
143
|
-
* @param signal - Optional abort signal.
|
|
144
|
-
* @returns Promise with the stream response.
|
|
145
|
-
*/
|
|
146
|
-
async postStream(req, body, signal) {
|
|
147
|
-
const internalReq = this.buildInternalRequest("POST", req, body, signal);
|
|
148
|
-
internalReq.meta = { ...internalReq.meta, responseType: "stream" };
|
|
149
|
-
return this.dispatchInternal(internalReq);
|
|
150
|
-
}
|
|
151
148
|
/**
|
|
152
149
|
* @ru Выполняет GET-запрос.
|
|
153
150
|
* @en Performs a GET request.
|
|
151
|
+
* @template T - Expected response body type.
|
|
154
152
|
* @param req - Request URL or RequestInterface object.
|
|
155
153
|
* @param signal - Optional abort signal.
|
|
156
|
-
* @returns Promise
|
|
154
|
+
* @returns Promise resolving to the HTTP response.
|
|
157
155
|
*/
|
|
158
156
|
get(req, signal) {
|
|
159
|
-
return this.dispatch(this.
|
|
157
|
+
return this.dispatch(this.acquireReq("GET", req, undefined, signal));
|
|
160
158
|
}
|
|
161
159
|
/**
|
|
162
|
-
* @ru Выполняет POST
|
|
163
|
-
* @en Performs a POST request.
|
|
160
|
+
* @ru Выполняет POST-запрос с телом.
|
|
161
|
+
* @en Performs a POST request with a body.
|
|
162
|
+
* @template T - Expected response body type.
|
|
164
163
|
* @param req - Request URL or RequestInterface object.
|
|
165
164
|
* @param body - Request body data.
|
|
166
165
|
* @param signal - Optional abort signal.
|
|
167
|
-
* @returns Promise
|
|
166
|
+
* @returns Promise resolving to the HTTP response.
|
|
168
167
|
*/
|
|
169
168
|
post(req, body, signal) {
|
|
170
|
-
return this.dispatch(this.
|
|
169
|
+
return this.dispatch(this.acquireReq("POST", req, body, signal));
|
|
171
170
|
}
|
|
172
171
|
/**
|
|
173
|
-
* @ru Выполняет PUT
|
|
174
|
-
* @en Performs a PUT request.
|
|
172
|
+
* @ru Выполняет PUT-запрос с телом.
|
|
173
|
+
* @en Performs a PUT request with a body.
|
|
174
|
+
* @template T - Expected response body type.
|
|
175
175
|
* @param req - Request URL or RequestInterface object.
|
|
176
176
|
* @param body - Request body data.
|
|
177
177
|
* @param signal - Optional abort signal.
|
|
178
|
-
* @returns Promise
|
|
178
|
+
* @returns Promise resolving to the HTTP response.
|
|
179
179
|
*/
|
|
180
180
|
put(req, body, signal) {
|
|
181
|
-
return this.dispatch(this.
|
|
181
|
+
return this.dispatch(this.acquireReq("PUT", req, body, signal));
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
184
|
-
* @ru Выполняет PATCH
|
|
185
|
-
* @en Performs a PATCH request.
|
|
184
|
+
* @ru Выполняет PATCH-запрос с телом.
|
|
185
|
+
* @en Performs a PATCH request with a body.
|
|
186
|
+
* @template T - Expected response body type.
|
|
186
187
|
* @param req - Request URL or RequestInterface object.
|
|
187
188
|
* @param body - Request body data.
|
|
188
189
|
* @param signal - Optional abort signal.
|
|
189
|
-
* @returns Promise
|
|
190
|
+
* @returns Promise resolving to the HTTP response.
|
|
190
191
|
*/
|
|
191
192
|
patch(req, body, signal) {
|
|
192
|
-
return this.dispatch(this.
|
|
193
|
+
return this.dispatch(this.acquireReq("PATCH", req, body, signal));
|
|
193
194
|
}
|
|
194
195
|
/**
|
|
195
196
|
* @ru Выполняет DELETE-запрос.
|
|
196
197
|
* @en Performs a DELETE request.
|
|
198
|
+
* @template T - Expected response body type.
|
|
197
199
|
* @param req - Request URL or RequestInterface object.
|
|
198
200
|
* @param signal - Optional abort signal.
|
|
199
|
-
* @returns Promise
|
|
201
|
+
* @returns Promise resolving to the HTTP response.
|
|
200
202
|
*/
|
|
201
203
|
delete(req, signal) {
|
|
202
|
-
return this.dispatch(this.
|
|
204
|
+
return this.dispatch(this.acquireReq("DELETE", req, undefined, signal));
|
|
203
205
|
}
|
|
204
206
|
/**
|
|
205
207
|
* @ru Выполняет OPTIONS-запрос.
|
|
206
208
|
* @en Performs an OPTIONS request.
|
|
209
|
+
* @template T - Expected response body type.
|
|
207
210
|
* @param req - Request URL or RequestInterface object.
|
|
208
211
|
* @param body - Optional request body data.
|
|
209
212
|
* @param signal - Optional abort signal.
|
|
210
|
-
* @returns Promise
|
|
213
|
+
* @returns Promise resolving to the HTTP response.
|
|
211
214
|
*/
|
|
212
215
|
options(req, body, signal) {
|
|
213
|
-
return this.dispatch(this.
|
|
216
|
+
return this.dispatch(this.acquireReq("OPTIONS", req, body, signal));
|
|
214
217
|
}
|
|
215
218
|
/**
|
|
216
|
-
* @ru Выполняет HEAD
|
|
217
|
-
* @en Performs a HEAD request.
|
|
219
|
+
* @ru Выполняет HEAD-запрос (без тела ответа).
|
|
220
|
+
* @en Performs a HEAD request (no response body).
|
|
218
221
|
* @param req - Request URL or RequestInterface object.
|
|
219
222
|
* @param signal - Optional abort signal.
|
|
220
|
-
* @returns Promise
|
|
223
|
+
* @returns Promise resolving to the HTTP response with null body.
|
|
221
224
|
*/
|
|
222
225
|
head(req, signal) {
|
|
223
|
-
return this.dispatch(this.
|
|
226
|
+
return this.dispatch(this.acquireReq("HEAD", req, undefined, signal));
|
|
224
227
|
}
|
|
225
228
|
/**
|
|
226
|
-
* @ru
|
|
227
|
-
* @en
|
|
228
|
-
* @param
|
|
229
|
-
* @
|
|
229
|
+
* @ru Инициирует потоковый GET-запрос. Тело ответа возвращается как ReadableStream.
|
|
230
|
+
* @en Initiates a streaming GET request. Response body is returned as a ReadableStream.
|
|
231
|
+
* @param req - Request URL or RequestInterface object.
|
|
232
|
+
* @param signal - Optional abort signal.
|
|
233
|
+
* @returns Promise resolving to the stream response.
|
|
234
|
+
*/
|
|
235
|
+
stream(req, signal) {
|
|
236
|
+
return this.dispatch(this.acquireReq("GET", req, undefined, signal, "stream"));
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* @ru Инициирует потоковый POST-запрос с телом. Тело ответа возвращается как ReadableStream.
|
|
240
|
+
* @en Initiates a streaming POST request with a body. Response body is returned as a ReadableStream.
|
|
241
|
+
* @template T - Expected response body type.
|
|
242
|
+
* @param req - Request URL or RequestInterface object.
|
|
243
|
+
* @param body - Request body data.
|
|
244
|
+
* @param signal - Optional abort signal.
|
|
245
|
+
* @returns Promise resolving to the stream response.
|
|
246
|
+
*/
|
|
247
|
+
postStream(req, body, signal) {
|
|
248
|
+
return this.dispatch(this.acquireReq("POST", req, body, signal, "stream"));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* @ru Создаёт новый экземпляр клиента, объединяя текущую конфигурацию с переданными опциями.
|
|
252
|
+
* @en Creates a new client instance by merging the current configuration with provided options.
|
|
253
|
+
* @param options - Partial configuration options to extend.
|
|
254
|
+
* @returns A new HyperCore instance.
|
|
230
255
|
*/
|
|
231
256
|
extend(options) {
|
|
232
257
|
return new HyperCore({
|
|
233
258
|
...this.config,
|
|
234
259
|
...options,
|
|
235
260
|
network: { ...this.config.network, ...options.network },
|
|
236
|
-
}, this.transport ?? undefined);
|
|
261
|
+
}, this.transportManager.transport ?? undefined);
|
|
237
262
|
}
|
|
238
263
|
/**
|
|
239
|
-
* @ru
|
|
240
|
-
* @en
|
|
241
|
-
* @param options -
|
|
242
|
-
* @returns
|
|
264
|
+
* @ru Создаёт полностью новый экземпляр клиента на основе переданных опций.
|
|
265
|
+
* @en Creates a completely new client instance based on provided options.
|
|
266
|
+
* @param options - Partial configuration options for the new instance.
|
|
267
|
+
* @returns A new HyperCore instance.
|
|
243
268
|
*/
|
|
244
269
|
create(options) {
|
|
245
270
|
return this.extend(options);
|
|
246
271
|
}
|
|
247
272
|
/**
|
|
248
|
-
* @ru
|
|
249
|
-
* @en
|
|
250
|
-
* @param graceful - If true,
|
|
251
|
-
* @returns Promise that resolves when
|
|
273
|
+
* @ru Завершает работу клиента и освобождает ресурсы (соединения, пулы).
|
|
274
|
+
* @en Shuts down the client and releases resources (connections, pools).
|
|
275
|
+
* @param graceful - If true, waits for active requests to complete before closing.
|
|
276
|
+
* @returns Promise that resolves when shutdown is complete.
|
|
252
277
|
*/
|
|
253
|
-
|
|
254
|
-
|
|
278
|
+
destroy(graceful = true) {
|
|
279
|
+
return this.transportManager.destroy(graceful);
|
|
255
280
|
}
|
|
256
281
|
/**
|
|
257
|
-
* @ru Выполняет
|
|
258
|
-
* @en Performs a request and
|
|
282
|
+
* @ru Выполняет GET-запрос и возвращает распарсенное JSON-тело ответа.
|
|
283
|
+
* @en Performs a GET request and returns the parsed JSON response body.
|
|
284
|
+
* @template T - Expected type of the parsed JSON.
|
|
259
285
|
* @param req - Request URL or RequestInterface object.
|
|
260
286
|
* @param signal - Optional abort signal.
|
|
261
|
-
* @returns Promise
|
|
262
|
-
* @throws If the response does not contain a json() method.
|
|
287
|
+
* @returns Promise resolving to the parsed JSON data.
|
|
263
288
|
*/
|
|
264
289
|
async json(req, signal) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
throw new Error("[HyperCore] Method 'json()' is missing on response object.");
|
|
268
|
-
return res.json();
|
|
290
|
+
return ((await this.shortcut(req, signal)).json?.() ??
|
|
291
|
+
Promise.reject(new Error("json() not supported")));
|
|
269
292
|
}
|
|
270
293
|
/**
|
|
271
|
-
* @ru Выполняет
|
|
272
|
-
* @en Performs a request and returns the body as
|
|
294
|
+
* @ru Выполняет GET-запрос и возвращает тело ответа как текст.
|
|
295
|
+
* @en Performs a GET request and returns the response body as text.
|
|
273
296
|
* @param req - Request URL or RequestInterface object.
|
|
274
297
|
* @param signal - Optional abort signal.
|
|
275
|
-
* @returns Promise
|
|
276
|
-
* @throws If the response does not contain a text() method.
|
|
298
|
+
* @returns Promise resolving to the response text.
|
|
277
299
|
*/
|
|
278
300
|
async text(req, signal) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
throw new Error("[HyperCore] Method 'text()' is missing on response object.");
|
|
282
|
-
return res.text();
|
|
301
|
+
return ((await this.shortcut(req, signal)).text?.() ??
|
|
302
|
+
Promise.reject(new Error("text() not supported")));
|
|
283
303
|
}
|
|
284
304
|
/**
|
|
285
|
-
* @ru Выполняет
|
|
286
|
-
* @en Performs a request and immediately discards the response body
|
|
305
|
+
* @ru Выполняет GET-запрос и немедленно отбрасывает тело ответа для освобождения ресурсов.
|
|
306
|
+
* @en Performs a GET request and immediately discards the response body to free resources.
|
|
287
307
|
* @param req - Request URL or RequestInterface object.
|
|
288
308
|
* @param signal - Optional abort signal.
|
|
289
|
-
* @returns Promise that resolves
|
|
290
|
-
* @throws If the response does not contain a dump() method.
|
|
309
|
+
* @returns Promise that resolves when the stream is drained.
|
|
291
310
|
*/
|
|
292
311
|
async dump(req, signal) {
|
|
293
|
-
|
|
294
|
-
if (!res.dump)
|
|
295
|
-
throw new Error("[HyperCore] Method 'dump()' is missing on response object.");
|
|
296
|
-
await res.dump();
|
|
297
|
-
}
|
|
298
|
-
async runResponsePipeline(response, req) {
|
|
299
|
-
const hasMutators = this.pipelines.responseMutators.length > 0;
|
|
300
|
-
const hasSideEffects = this.pipelines.responseSideEffects.length > 0;
|
|
301
|
-
if (hasMutators || hasSideEffects) {
|
|
302
|
-
await executeResponsePipeline(this.pipelines.responseMutators, this.pipelines.responseSideEffects, response, req, this.pluginCtx, this.config.logger);
|
|
303
|
-
}
|
|
312
|
+
await (await this.shortcut(req, signal)).dump?.();
|
|
304
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* @ru Обрабатывает ошибку диспетчеризации через конвейер плагинов обработки ошибок.
|
|
316
|
+
* @en Handles dispatch errors through the error handling plugin pipeline.
|
|
317
|
+
* @template T - Expected response body type.
|
|
318
|
+
* @param error - The error that occurred.
|
|
319
|
+
* @param req - The original internal request.
|
|
320
|
+
* @returns Promise resolving to a recovered HTTP response, or throws if unrecoverable.
|
|
321
|
+
*/
|
|
305
322
|
async handleDispatchError(error, req) {
|
|
306
|
-
if (this.
|
|
323
|
+
if (this.hasErrorPlugins) {
|
|
307
324
|
const recovered = await executeErrorPipeline(this.pipelines.error, error, req, this.pluginCtx);
|
|
308
|
-
if (recovered) {
|
|
309
|
-
|
|
325
|
+
if (recovered != null) {
|
|
326
|
+
if (this.hasResponsePlugins) {
|
|
327
|
+
await Promise.resolve(executeResponsePipeline(this.pipelines.responseMutators, this.pipelines.responseSideEffects, recovered, req, this.pluginCtx, this.config.logger));
|
|
328
|
+
}
|
|
310
329
|
return recovered;
|
|
311
330
|
}
|
|
312
331
|
}
|
|
313
332
|
throw error;
|
|
314
333
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
+
/**
|
|
335
|
+
* @ru Создаёт или переиспользует объект InternalRequest из пула, заполняя его поля.
|
|
336
|
+
* @en Creates or reuses an InternalRequest object from the pool, populating its fields.
|
|
337
|
+
* @param method - HTTP method (GET, POST, etc.).
|
|
338
|
+
* @param req - Request URL or RequestInterface object.
|
|
339
|
+
* @param body - Optional request body data.
|
|
340
|
+
* @param signal - Optional abort signal.
|
|
341
|
+
* @param responseType - Optional response type hint ('stream').
|
|
342
|
+
* @returns The populated internal request object.
|
|
343
|
+
*/
|
|
344
|
+
acquireReq(method, req, body, signal, responseType) {
|
|
345
|
+
const pooled = requestPool.pop();
|
|
346
|
+
const internalReq = pooled ?? {
|
|
347
|
+
method: "GET",
|
|
348
|
+
url: "",
|
|
349
|
+
headers: this.defaultHeaders,
|
|
350
|
+
body: undefined,
|
|
351
|
+
signal: undefined,
|
|
352
|
+
meta: undefined,
|
|
353
|
+
};
|
|
354
|
+
if (typeof req === "string") {
|
|
355
|
+
internalReq.method = method;
|
|
356
|
+
internalReq.url = this.resolveUrl(req);
|
|
357
|
+
internalReq.headers = this.defaultHeaders;
|
|
358
|
+
internalReq.body = body !== undefined ? normalizeBody(method, body) : undefined;
|
|
359
|
+
internalReq.signal = signal;
|
|
360
|
+
internalReq.meta = responseType ? { responseType } : undefined;
|
|
361
|
+
return internalReq;
|
|
334
362
|
}
|
|
335
|
-
}
|
|
336
|
-
buildInternalRequest(method, req, body, signal) {
|
|
337
|
-
const isStr = typeof req === "string";
|
|
338
363
|
const rawUrl = normalizeUrl(req);
|
|
339
|
-
if (!rawUrl)
|
|
340
|
-
throw new Error(`[HyperCore]
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
url: urlObj.href,
|
|
352
|
-
origin: urlObj.origin,
|
|
353
|
-
path: urlObj.pathname + urlObj.search,
|
|
354
|
-
};
|
|
355
|
-
if (isStr) {
|
|
356
|
-
return {
|
|
357
|
-
...context,
|
|
358
|
-
headers: { ...this.defaultHeaders },
|
|
359
|
-
body,
|
|
360
|
-
signal,
|
|
361
|
-
meta: {},
|
|
362
|
-
};
|
|
364
|
+
if (!rawUrl)
|
|
365
|
+
throw new Error(`[HyperCore] URL is undefined for ${method}`);
|
|
366
|
+
let finalUrl = urlCache[rawUrl];
|
|
367
|
+
if (!finalUrl) {
|
|
368
|
+
const urlObj = new URL(rawUrl);
|
|
369
|
+
if (req.query)
|
|
370
|
+
this.appendQueryParams(urlObj, req.query);
|
|
371
|
+
finalUrl = urlObj.href;
|
|
372
|
+
if (urlCacheCount < MAX_CACHE_SIZE) {
|
|
373
|
+
urlCache[rawUrl] = finalUrl;
|
|
374
|
+
urlCacheCount++;
|
|
375
|
+
}
|
|
363
376
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
meta,
|
|
380
|
-
});
|
|
377
|
+
internalReq.method = method;
|
|
378
|
+
internalReq.url = finalUrl;
|
|
379
|
+
internalReq.headers = req.headers
|
|
380
|
+
? mergeHeadersFast(this.defaultHeaders, req.headers)
|
|
381
|
+
: this.defaultHeaders;
|
|
382
|
+
internalReq.body = normalizeBody(method, req.body ?? body);
|
|
383
|
+
internalReq.signal = req.signal ?? signal;
|
|
384
|
+
internalReq.meta = responseType
|
|
385
|
+
? { responseType }
|
|
386
|
+
: req.meta
|
|
387
|
+
? {
|
|
388
|
+
responseType: req.meta.responseType,
|
|
389
|
+
}
|
|
390
|
+
: undefined;
|
|
391
|
+
return internalReq;
|
|
381
392
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
393
|
+
/**
|
|
394
|
+
* @ru Возвращает объект запроса в пул для переиспользования, очищая ссылки для GC.
|
|
395
|
+
* @en Returns the request object to the pool for reuse, clearing references for GC.
|
|
396
|
+
* @param req - The internal request object to recycle.
|
|
397
|
+
*/
|
|
398
|
+
recycleRequest(req) {
|
|
399
|
+
if (requestPool.length < MAX_POOL_SIZE) {
|
|
400
|
+
req.body = undefined;
|
|
401
|
+
req.signal = undefined;
|
|
402
|
+
req.meta = undefined;
|
|
403
|
+
requestPool.push(req);
|
|
389
404
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* @ru Разрешает и кэширует URL, нормализуя его через URL API при необходимости.
|
|
408
|
+
* @en Resolves and caches the URL, normalizing it via the URL API when necessary.
|
|
409
|
+
* @param url - The raw URL string.
|
|
410
|
+
* @returns The resolved and cached URL string.
|
|
411
|
+
*/
|
|
412
|
+
resolveUrl(url) {
|
|
413
|
+
if (!url)
|
|
414
|
+
throw new Error("[HyperCore] URL is undefined");
|
|
415
|
+
let finalUrl = urlCache[url];
|
|
416
|
+
if (finalUrl)
|
|
417
|
+
return finalUrl;
|
|
418
|
+
const hasQuery = url.includes("?") || url.endsWith("?");
|
|
419
|
+
const isAbsolute = url.startsWith("http://") || url.startsWith("https://");
|
|
420
|
+
finalUrl = !hasQuery || !isAbsolute ? new URL(url).href : url;
|
|
421
|
+
if (urlCacheCount < MAX_CACHE_SIZE) {
|
|
422
|
+
urlCache[url] = finalUrl;
|
|
423
|
+
urlCacheCount++;
|
|
394
424
|
}
|
|
395
|
-
return
|
|
425
|
+
return finalUrl;
|
|
396
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* @ru Добавляет query-параметры к объекту URL, поддерживая массивы значений.
|
|
429
|
+
* @en Appends query parameters to the URL object, supporting arrays of values.
|
|
430
|
+
* @param url - The URL object to modify.
|
|
431
|
+
* @param query - Record of query parameter key-value pairs.
|
|
432
|
+
*/
|
|
397
433
|
appendQueryParams(url, query) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const value = query[key];
|
|
403
|
-
if (value === undefined || value === null)
|
|
434
|
+
for (const k in query) {
|
|
435
|
+
if (Object.prototype.hasOwnProperty.call(query, k)) {
|
|
436
|
+
const v = query[k];
|
|
437
|
+
if (v == null)
|
|
404
438
|
continue;
|
|
405
|
-
if (Array.isArray(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const item = value[i];
|
|
409
|
-
if (item === undefined || item === null)
|
|
410
|
-
continue;
|
|
411
|
-
url.searchParams.append(key, String(item));
|
|
412
|
-
}
|
|
439
|
+
if (Array.isArray(v)) {
|
|
440
|
+
for (let j = 0; j < v.length; j++)
|
|
441
|
+
url.searchParams.append(k, String(v[j]));
|
|
413
442
|
}
|
|
414
443
|
else {
|
|
415
|
-
url.searchParams.set(
|
|
444
|
+
url.searchParams.set(k, String(v));
|
|
416
445
|
}
|
|
417
446
|
}
|
|
418
447
|
}
|
|
419
448
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
449
|
+
/**
|
|
450
|
+
* @ru Выполняет быстрый запрос с автоматическим определением HTTP-метода.
|
|
451
|
+
* @en Performs a shortcut request with automatic HTTP method detection.
|
|
452
|
+
* @param req - Request URL or RequestInterface object.
|
|
453
|
+
* @param signal - Optional abort signal.
|
|
454
|
+
* @returns Promise resolving to the HTTP response.
|
|
455
|
+
*/
|
|
456
|
+
shortcut(req, signal) {
|
|
457
|
+
const method = typeof req === "string" ? "GET" : (req.method ?? "GET");
|
|
458
|
+
return this.dispatch(this.acquireReq(method, req, undefined, signal));
|
|
425
459
|
}
|
|
426
460
|
}
|
|
427
461
|
//# sourceMappingURL=HyperCore.js.map
|