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