@hyperttp/core 1.3.3 → 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 +142 -152
- package/Core/HyperCore.d.ts.map +1 -1
- package/Core/HyperCore.js +285 -286
- package/Core/HyperCore.js.map +1 -1
- 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 +95 -31
- package/utils/response.d.ts.map +1 -1
- package/utils/response.js +288 -58
- 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,56 @@ 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);
|
|
79
90
|
let rawResponse = await transport.execute(req);
|
|
80
|
-
if (this.
|
|
81
|
-
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));
|
|
82
93
|
}
|
|
83
|
-
|
|
84
|
-
const isStream = req.meta?.responseType === "stream";
|
|
85
|
-
const response = isStream
|
|
94
|
+
const response = req.meta?.responseType === "stream"
|
|
86
95
|
? mapStreamFast(rawResponse)
|
|
87
96
|
: mapResponseFast(rawResponse);
|
|
88
|
-
|
|
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);
|
|
89
101
|
return response;
|
|
90
102
|
}
|
|
91
103
|
catch (error) {
|
|
104
|
+
this.recycleRequest(req);
|
|
92
105
|
return this.handleDispatchError(error, req);
|
|
93
106
|
}
|
|
94
107
|
}
|
|
95
108
|
/**
|
|
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.
|
|
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.
|
|
109
113
|
*/
|
|
110
114
|
use(plugin) {
|
|
111
115
|
const isEnabled = plugin.enabled ? plugin.enabled(this.config) : true;
|
|
@@ -113,350 +117,345 @@ export class HyperCore {
|
|
|
113
117
|
return this;
|
|
114
118
|
plugin.setup?.(this.pluginCtx);
|
|
115
119
|
const priority = plugin.priority ?? 0;
|
|
120
|
+
const hook = { name: plugin.name, priority };
|
|
116
121
|
if (plugin.onRequest) {
|
|
117
122
|
insertHookSorted(this.pipelines.request, {
|
|
118
|
-
|
|
119
|
-
priority,
|
|
123
|
+
...hook,
|
|
120
124
|
run: plugin.onRequest,
|
|
121
125
|
});
|
|
126
|
+
this.hasRequestPlugins = true;
|
|
122
127
|
}
|
|
123
128
|
if (plugin.onResponse) {
|
|
124
129
|
const target = plugin.mode === "background"
|
|
125
130
|
? this.pipelines.responseSideEffects
|
|
126
131
|
: this.pipelines.responseMutators;
|
|
127
|
-
insertHookSorted(target, {
|
|
128
|
-
|
|
129
|
-
priority,
|
|
130
|
-
run: plugin.onResponse,
|
|
131
|
-
});
|
|
132
|
+
insertHookSorted(target, { ...hook, run: plugin.onResponse });
|
|
133
|
+
this.hasResponsePlugins = true;
|
|
132
134
|
}
|
|
133
135
|
if (plugin.onResponseData) {
|
|
134
136
|
insertHookSorted(this.pipelines.responseData, {
|
|
135
|
-
|
|
136
|
-
priority,
|
|
137
|
+
...hook,
|
|
137
138
|
run: plugin.onResponseData,
|
|
138
139
|
});
|
|
140
|
+
this.hasResponseDataPlugins = true;
|
|
139
141
|
}
|
|
140
142
|
if (plugin.onError) {
|
|
141
|
-
insertHookSorted(this.pipelines.error, {
|
|
142
|
-
|
|
143
|
-
priority,
|
|
144
|
-
run: plugin.onError,
|
|
145
|
-
});
|
|
143
|
+
insertHookSorted(this.pipelines.error, { ...hook, run: plugin.onError });
|
|
144
|
+
this.hasErrorPlugins = true;
|
|
146
145
|
}
|
|
147
146
|
return this;
|
|
148
147
|
}
|
|
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
148
|
/**
|
|
175
149
|
* @ru Выполняет GET-запрос.
|
|
176
150
|
* @en Performs a GET request.
|
|
177
|
-
* @
|
|
178
|
-
* @param
|
|
179
|
-
* @
|
|
151
|
+
* @template T - Expected response body type.
|
|
152
|
+
* @param req - Request URL or RequestInterface object.
|
|
153
|
+
* @param signal - Optional abort signal.
|
|
154
|
+
* @returns Promise resolving to the HTTP response.
|
|
180
155
|
*/
|
|
181
156
|
get(req, signal) {
|
|
182
|
-
return this.dispatch(this.
|
|
157
|
+
return this.dispatch(this.acquireReq("GET", req, undefined, signal));
|
|
183
158
|
}
|
|
184
159
|
/**
|
|
185
|
-
* @ru Выполняет POST
|
|
186
|
-
* @en Performs a POST request.
|
|
187
|
-
* @
|
|
160
|
+
* @ru Выполняет POST-запрос с телом.
|
|
161
|
+
* @en Performs a POST request with a body.
|
|
162
|
+
* @template T - Expected response body type.
|
|
163
|
+
* @param req - Request URL or RequestInterface object.
|
|
188
164
|
* @param body - Request body data.
|
|
189
|
-
* @param signal - Optional
|
|
190
|
-
* @returns Promise resolving to
|
|
165
|
+
* @param signal - Optional abort signal.
|
|
166
|
+
* @returns Promise resolving to the HTTP response.
|
|
191
167
|
*/
|
|
192
168
|
post(req, body, signal) {
|
|
193
|
-
return this.dispatch(this.
|
|
169
|
+
return this.dispatch(this.acquireReq("POST", req, body, signal));
|
|
194
170
|
}
|
|
195
171
|
/**
|
|
196
|
-
* @ru Выполняет PUT
|
|
197
|
-
* @en Performs a PUT request.
|
|
198
|
-
* @
|
|
172
|
+
* @ru Выполняет PUT-запрос с телом.
|
|
173
|
+
* @en Performs a PUT request with a body.
|
|
174
|
+
* @template T - Expected response body type.
|
|
175
|
+
* @param req - Request URL or RequestInterface object.
|
|
199
176
|
* @param body - Request body data.
|
|
200
|
-
* @param signal - Optional
|
|
201
|
-
* @returns Promise resolving to
|
|
177
|
+
* @param signal - Optional abort signal.
|
|
178
|
+
* @returns Promise resolving to the HTTP response.
|
|
202
179
|
*/
|
|
203
180
|
put(req, body, signal) {
|
|
204
|
-
return this.dispatch(this.
|
|
181
|
+
return this.dispatch(this.acquireReq("PUT", req, body, signal));
|
|
205
182
|
}
|
|
206
183
|
/**
|
|
207
|
-
* @ru Выполняет PATCH
|
|
208
|
-
* @en Performs a PATCH request.
|
|
209
|
-
* @
|
|
184
|
+
* @ru Выполняет PATCH-запрос с телом.
|
|
185
|
+
* @en Performs a PATCH request with a body.
|
|
186
|
+
* @template T - Expected response body type.
|
|
187
|
+
* @param req - Request URL or RequestInterface object.
|
|
210
188
|
* @param body - Request body data.
|
|
211
|
-
* @param signal - Optional
|
|
212
|
-
* @returns Promise resolving to
|
|
189
|
+
* @param signal - Optional abort signal.
|
|
190
|
+
* @returns Promise resolving to the HTTP response.
|
|
213
191
|
*/
|
|
214
192
|
patch(req, body, signal) {
|
|
215
|
-
return this.dispatch(this.
|
|
193
|
+
return this.dispatch(this.acquireReq("PATCH", req, body, signal));
|
|
216
194
|
}
|
|
217
195
|
/**
|
|
218
196
|
* @ru Выполняет DELETE-запрос.
|
|
219
197
|
* @en Performs a DELETE request.
|
|
220
|
-
* @
|
|
221
|
-
* @param
|
|
222
|
-
* @
|
|
198
|
+
* @template T - Expected response body type.
|
|
199
|
+
* @param req - Request URL or RequestInterface object.
|
|
200
|
+
* @param signal - Optional abort signal.
|
|
201
|
+
* @returns Promise resolving to the HTTP response.
|
|
223
202
|
*/
|
|
224
203
|
delete(req, signal) {
|
|
225
|
-
return this.dispatch(this.
|
|
204
|
+
return this.dispatch(this.acquireReq("DELETE", req, undefined, signal));
|
|
226
205
|
}
|
|
227
206
|
/**
|
|
228
207
|
* @ru Выполняет OPTIONS-запрос.
|
|
229
208
|
* @en Performs an OPTIONS request.
|
|
230
|
-
* @
|
|
231
|
-
* @param
|
|
232
|
-
* @param
|
|
233
|
-
* @
|
|
209
|
+
* @template T - Expected response body type.
|
|
210
|
+
* @param req - Request URL or RequestInterface object.
|
|
211
|
+
* @param body - Optional request body data.
|
|
212
|
+
* @param signal - Optional abort signal.
|
|
213
|
+
* @returns Promise resolving to the HTTP response.
|
|
234
214
|
*/
|
|
235
215
|
options(req, body, signal) {
|
|
236
|
-
return this.dispatch(this.
|
|
216
|
+
return this.dispatch(this.acquireReq("OPTIONS", req, body, signal));
|
|
237
217
|
}
|
|
238
218
|
/**
|
|
239
|
-
* @ru Выполняет HEAD
|
|
240
|
-
* @en Performs a HEAD request.
|
|
241
|
-
* @param req - Request URL or
|
|
242
|
-
* @param signal - Optional
|
|
243
|
-
* @returns Promise resolving to
|
|
219
|
+
* @ru Выполняет HEAD-запрос (без тела ответа).
|
|
220
|
+
* @en Performs a HEAD request (no response body).
|
|
221
|
+
* @param req - Request URL or RequestInterface object.
|
|
222
|
+
* @param signal - Optional abort signal.
|
|
223
|
+
* @returns Promise resolving to the HTTP response with null body.
|
|
244
224
|
*/
|
|
245
225
|
head(req, signal) {
|
|
246
|
-
return this.dispatch(this.
|
|
226
|
+
return this.dispatch(this.acquireReq("HEAD", req, undefined, signal));
|
|
247
227
|
}
|
|
248
228
|
/**
|
|
249
|
-
* @ru
|
|
250
|
-
* @en
|
|
251
|
-
* @param
|
|
252
|
-
* @
|
|
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.
|
|
253
255
|
*/
|
|
254
256
|
extend(options) {
|
|
255
257
|
return new HyperCore({
|
|
256
258
|
...this.config,
|
|
257
259
|
...options,
|
|
258
260
|
network: { ...this.config.network, ...options.network },
|
|
259
|
-
}, this.transport ?? undefined);
|
|
261
|
+
}, this.transportManager.transport ?? undefined);
|
|
260
262
|
}
|
|
261
263
|
/**
|
|
262
|
-
* @ru
|
|
263
|
-
* @en
|
|
264
|
-
* @param options - Partial configuration
|
|
265
|
-
* @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.
|
|
266
268
|
*/
|
|
267
269
|
create(options) {
|
|
268
270
|
return this.extend(options);
|
|
269
271
|
}
|
|
270
272
|
/**
|
|
271
|
-
* @ru Завершает работу
|
|
272
|
-
* @en Shuts down the client
|
|
273
|
-
* @param graceful - If true,
|
|
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.
|
|
274
276
|
* @returns Promise that resolves when shutdown is complete.
|
|
275
277
|
*/
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
destroy(graceful = true) {
|
|
279
|
+
return this.transportManager.destroy(graceful);
|
|
278
280
|
}
|
|
279
281
|
/**
|
|
280
|
-
* @ru Выполняет
|
|
281
|
-
* @en Performs a request and returns parsed JSON response.
|
|
282
|
-
* @
|
|
283
|
-
* @param
|
|
284
|
-
* @
|
|
285
|
-
* @
|
|
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.
|
|
285
|
+
* @param req - Request URL or RequestInterface object.
|
|
286
|
+
* @param signal - Optional abort signal.
|
|
287
|
+
* @returns Promise resolving to the parsed JSON data.
|
|
286
288
|
*/
|
|
287
289
|
async json(req, signal) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
+
return ((await this.shortcut(req, signal)).json?.() ??
|
|
291
|
+
Promise.reject(new Error("json() not supported")));
|
|
290
292
|
}
|
|
291
293
|
/**
|
|
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.
|
|
294
|
+
* @ru Выполняет GET-запрос и возвращает тело ответа как текст.
|
|
295
|
+
* @en Performs a GET request and returns the response body as text.
|
|
296
|
+
* @param req - Request URL or RequestInterface object.
|
|
297
|
+
* @param signal - Optional abort signal.
|
|
298
|
+
* @returns Promise resolving to the response text.
|
|
298
299
|
*/
|
|
299
300
|
async text(req, signal) {
|
|
300
|
-
|
|
301
|
-
|
|
301
|
+
return ((await this.shortcut(req, signal)).text?.() ??
|
|
302
|
+
Promise.reject(new Error("text() not supported")));
|
|
302
303
|
}
|
|
303
304
|
/**
|
|
304
|
-
* @ru Выполняет
|
|
305
|
-
* @en Performs a request and
|
|
306
|
-
* @param req - Request URL or
|
|
307
|
-
* @param signal - Optional
|
|
308
|
-
* @returns Promise that resolves when
|
|
305
|
+
* @ru Выполняет GET-запрос и немедленно отбрасывает тело ответа для освобождения ресурсов.
|
|
306
|
+
* @en Performs a GET request and immediately discards the response body to free resources.
|
|
307
|
+
* @param req - Request URL or RequestInterface object.
|
|
308
|
+
* @param signal - Optional abort signal.
|
|
309
|
+
* @returns Promise that resolves when the stream is drained.
|
|
309
310
|
*/
|
|
310
311
|
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
|
-
}
|
|
312
|
+
await (await this.shortcut(req, signal)).dump?.();
|
|
325
313
|
}
|
|
326
314
|
/**
|
|
327
|
-
* @ru Обрабатывает
|
|
328
|
-
* @en Handles errors
|
|
329
|
-
* @
|
|
330
|
-
* @param
|
|
331
|
-
* @
|
|
332
|
-
* @
|
|
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.
|
|
333
321
|
*/
|
|
334
322
|
async handleDispatchError(error, req) {
|
|
335
|
-
if (this.
|
|
323
|
+
if (this.hasErrorPlugins) {
|
|
336
324
|
const recovered = await executeErrorPipeline(this.pipelines.error, error, req, this.pluginCtx);
|
|
337
|
-
if (recovered) {
|
|
338
|
-
|
|
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
|
+
}
|
|
339
329
|
return recovered;
|
|
340
330
|
}
|
|
341
331
|
}
|
|
342
332
|
throw error;
|
|
343
333
|
}
|
|
344
334
|
/**
|
|
345
|
-
* @ru
|
|
346
|
-
* @en
|
|
347
|
-
* @param
|
|
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.
|
|
348
343
|
*/
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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;
|
|
359
362
|
}
|
|
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
363
|
const rawUrl = normalizeUrl(req);
|
|
373
|
-
if (!rawUrl)
|
|
364
|
+
if (!rawUrl)
|
|
374
365
|
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);
|
|
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++;
|
|
388
375
|
}
|
|
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
376
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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;
|
|
392
|
+
}
|
|
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);
|
|
403
404
|
}
|
|
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
405
|
}
|
|
416
406
|
/**
|
|
417
|
-
* @ru
|
|
418
|
-
* @en
|
|
419
|
-
* @param
|
|
420
|
-
* @
|
|
421
|
-
* @returns New object with merged properties and inherited prototype.
|
|
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.
|
|
422
411
|
*/
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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++;
|
|
424
|
+
}
|
|
425
|
+
return finalUrl;
|
|
427
426
|
}
|
|
428
427
|
/**
|
|
429
|
-
* @ru Добавляет
|
|
430
|
-
* @en Appends query parameters to
|
|
431
|
-
* @param url - URL object to
|
|
432
|
-
* @param query - Record of query
|
|
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.
|
|
433
432
|
*/
|
|
434
433
|
appendQueryParams(url, query) {
|
|
435
|
-
if (!query)
|
|
436
|
-
return;
|
|
437
434
|
for (const k in query) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
435
|
+
if (Object.prototype.hasOwnProperty.call(query, k)) {
|
|
436
|
+
const v = query[k];
|
|
437
|
+
if (v == null)
|
|
438
|
+
continue;
|
|
439
|
+
if (Array.isArray(v)) {
|
|
440
|
+
for (let j = 0; j < v.length; j++)
|
|
441
|
+
url.searchParams.append(k, String(v[j]));
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
url.searchParams.set(k, String(v));
|
|
445
|
+
}
|
|
447
446
|
}
|
|
448
447
|
}
|
|
449
448
|
}
|
|
450
449
|
/**
|
|
451
|
-
* @ru
|
|
452
|
-
* @en
|
|
453
|
-
* @param req - Request URL or
|
|
454
|
-
* @param signal - Optional
|
|
455
|
-
* @returns Promise resolving to
|
|
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.
|
|
456
455
|
*/
|
|
457
|
-
|
|
456
|
+
shortcut(req, signal) {
|
|
458
457
|
const method = typeof req === "string" ? "GET" : (req.method ?? "GET");
|
|
459
|
-
return this.dispatch(this.
|
|
458
|
+
return this.dispatch(this.acquireReq(method, req, undefined, signal));
|
|
460
459
|
}
|
|
461
460
|
}
|
|
462
461
|
//# sourceMappingURL=HyperCore.js.map
|