@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.
Files changed (45) hide show
  1. package/Core/HyperCore.d.ts +142 -152
  2. package/Core/HyperCore.d.ts.map +1 -1
  3. package/Core/HyperCore.js +285 -286
  4. package/Core/HyperCore.js.map +1 -1
  5. package/package.json +14 -8
  6. package/transports/browser.d.ts +228 -0
  7. package/transports/browser.d.ts.map +1 -0
  8. package/transports/browser.js +451 -0
  9. package/transports/browser.js.map +1 -0
  10. package/transports/manager.d.ts +7 -67
  11. package/transports/manager.d.ts.map +1 -1
  12. package/transports/manager.js +138 -145
  13. package/transports/manager.js.map +1 -1
  14. package/transports/node.d.ts +0 -20
  15. package/transports/node.d.ts.map +1 -1
  16. package/transports/node.js +104 -48
  17. package/transports/node.js.map +1 -1
  18. package/utils/decompress.d.ts +16 -18
  19. package/utils/decompress.d.ts.map +1 -1
  20. package/utils/decompress.js +143 -63
  21. package/utils/decompress.js.map +1 -1
  22. package/utils/errors.d.ts +0 -10
  23. package/utils/errors.d.ts.map +1 -1
  24. package/utils/errors.js +1 -11
  25. package/utils/errors.js.map +1 -1
  26. package/utils/merge.d.ts +0 -3
  27. package/utils/merge.d.ts.map +1 -1
  28. package/utils/merge.js +0 -4
  29. package/utils/merge.js.map +1 -1
  30. package/utils/normalize.d.ts +40 -20
  31. package/utils/normalize.d.ts.map +1 -1
  32. package/utils/normalize.js +197 -63
  33. package/utils/normalize.js.map +1 -1
  34. package/utils/pipeline.d.ts +10 -30
  35. package/utils/pipeline.d.ts.map +1 -1
  36. package/utils/pipeline.js +44 -44
  37. package/utils/pipeline.js.map +1 -1
  38. package/utils/response.d.ts +95 -31
  39. package/utils/response.d.ts.map +1 -1
  40. package/utils/response.js +288 -58
  41. package/utils/response.js.map +1 -1
  42. package/utils/retryUtils.d.ts +2 -10
  43. package/utils/retryUtils.d.ts.map +1 -1
  44. package/utils/retryUtils.js +17 -21
  45. 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, } from "../utils/response.js";
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, } from "../utils/normalize.js";
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 Основной класс HTTP-клиента с поддержкой плагинов, потоковой передачи, кэширования URL и гибкой настройки.
10
- * @en Core HTTP client class with plugin support, streaming, URL caching and flexible configuration.
11
- * @implements {IHyperCore}
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 Создаёт экземпляр HyperCore.
25
- * @en Creates a HyperCore instance.
26
- * @param config - Client configuration (overrides defaults).
27
- * @param transport - Optional transport layer (auto‑selected if omitted).
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 Возвращает текущий транспортный экземпляр (или null, если он ещё не инициализирован).
46
- * @en Returns the current transport instance (or null if not yet initialized).
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.ensureTransport();
68
+ const t = this.transportManager.transport ?? (await this.transportManager.get());
53
69
  return t.constructor.name;
54
70
  }
55
71
  /**
56
- * @ru Гарантирует наличие готового к работе транспорта (асинхронная инициализация при необходимости).
57
- * @en Ensures a ready‑to‑use transport (async initialization if needed).
58
- * @returns Promise resolving to a transport instance.
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
- ensureTransport() {
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.pipelines.request.length > 0) {
72
- const shortCircuit = await executeRequestPipeline(this.pipelines.request, req, this.pluginCtx);
73
- if (shortCircuit) {
74
- await this.runResponsePipeline(shortCircuit, req);
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.transport || (await this.ensureTransport());
89
+ const transport = this.transportManager.getSync() ?? (await this.transportReady);
79
90
  let rawResponse = await transport.execute(req);
80
- if (this.pipelines.responseData.length > 0) {
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
- this.applyDecompression(rawResponse);
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
- await this.runResponsePipeline(response, req);
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 Отправляет HTTP-запрос и возвращает обычный (нестриминговый) ответ.
97
- * @en Sends an HTTP request and returns a regular (non‑streaming) response.
98
- * @param req - Internal request object.
99
- * @returns Promise resolving to a typed HTTP response.
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
- name: plugin.name,
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
- name: plugin.name,
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
- name: plugin.name,
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
- name: plugin.name,
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
- * @param req - Request URL or configuration object.
178
- * @param signal - Optional AbortSignal to cancel the request.
179
- * @returns Promise resolving to an HttpResponse.
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.buildInternalRequest("GET", req, undefined, signal));
157
+ return this.dispatch(this.acquireReq("GET", req, undefined, signal));
183
158
  }
184
159
  /**
185
- * @ru Выполняет POST-запрос.
186
- * @en Performs a POST request.
187
- * @param req - Request URL or configuration object.
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 AbortSignal to cancel the request.
190
- * @returns Promise resolving to an HttpResponse.
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.buildInternalRequest("POST", req, body, signal));
169
+ return this.dispatch(this.acquireReq("POST", req, body, signal));
194
170
  }
195
171
  /**
196
- * @ru Выполняет PUT-запрос.
197
- * @en Performs a PUT request.
198
- * @param req - Request URL or configuration object.
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 AbortSignal to cancel the request.
201
- * @returns Promise resolving to an HttpResponse.
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.buildInternalRequest("PUT", req, body, signal));
181
+ return this.dispatch(this.acquireReq("PUT", req, body, signal));
205
182
  }
206
183
  /**
207
- * @ru Выполняет PATCH-запрос.
208
- * @en Performs a PATCH request.
209
- * @param req - Request URL or configuration object.
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 AbortSignal to cancel the request.
212
- * @returns Promise resolving to an HttpResponse.
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.buildInternalRequest("PATCH", req, body, signal));
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
- * @param req - Request URL or configuration object.
221
- * @param signal - Optional AbortSignal to cancel the request.
222
- * @returns Promise resolving to an HttpResponse.
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.buildInternalRequest("DELETE", req, undefined, signal));
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
- * @param req - Request URL or configuration object.
231
- * @param body - Request body data.
232
- * @param signal - Optional AbortSignal to cancel the request.
233
- * @returns Promise resolving to an HttpResponse.
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.buildInternalRequest("OPTIONS", req, body, signal));
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 configuration object.
242
- * @param signal - Optional AbortSignal to cancel the request.
243
- * @returns Promise resolving to an HttpResponse with null body.
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.buildInternalRequest("HEAD", req, undefined, signal));
226
+ return this.dispatch(this.acquireReq("HEAD", req, undefined, signal));
247
227
  }
248
228
  /**
249
- * @ru Создаёт новый экземпляр HyperCore путём расширения текущей конфигурации.
250
- * @en Creates a new HyperCore instance by extending the current configuration.
251
- * @param options - Partial configuration overrides.
252
- * @returns New HyperCore instance.
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 Алиас для {@link extend}. Создаёт новый экземпляр с переопределёнными опциями.
263
- * @en Alias for {@link extend}. Creates a new instance with overridden options.
264
- * @param options - Partial configuration overrides.
265
- * @returns New HyperCore instance.
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: closes transport connections.
273
- * @param graceful - If true, attempts graceful shutdown (waiting for pending requests).
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
- async destroy(graceful = true) {
277
- await this.transportManager.destroy(graceful);
278
+ destroy(graceful = true) {
279
+ return this.transportManager.destroy(graceful);
278
280
  }
279
281
  /**
280
- * @ru Выполняет запрос и возвращает распарсенный JSON-ответ.
281
- * @en Performs a request and returns parsed JSON response.
282
- * @param req - Request URL or configuration object.
283
- * @param signal - Optional AbortSignal to cancel the request.
284
- * @returns Promise resolving to the parsed JSON value.
285
- * @throws If response body cannot be parsed as JSON.
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
- const res = await this.dispatchShortcut(req, signal);
289
- return res.json?.() ?? Promise.reject(new Error("json() not supported"));
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 configuration object.
295
- * @param signal - Optional AbortSignal to cancel the request.
296
- * @returns Promise resolving to the response body as string.
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
- const res = await this.dispatchShortcut(req, signal);
301
- return res.text?.() ?? Promise.reject(new Error("text() not supported"));
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 fully consumes the response body (no processing).
306
- * @param req - Request URL or configuration object.
307
- * @param signal - Optional AbortSignal to cancel the request.
308
- * @returns Promise that resolves when response body is drained.
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
- const res = await this.dispatchShortcut(req, signal);
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 occurring during request execution, considering the error pipeline.
329
- * @param error - The caught error.
330
- * @param req - Internal request that caused the error.
331
- * @returns If recovered, returns a response; otherwise throws the error.
332
- * @throws The original error if no plugin recovers it.
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.pipelines.error.length) {
323
+ if (this.hasErrorPlugins) {
336
324
  const recovered = await executeErrorPipeline(this.pipelines.error, error, req, this.pluginCtx);
337
- if (recovered) {
338
- await this.runResponsePipeline(recovered, req);
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 Применяет декомпрессию к телу ответа в зависимости от заголовка Content-Encoding.
346
- * @en Applies decompression to the response body based on the Content-Encoding header.
347
- * @param rawResponse - Raw transport response object (mutated in place).
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
- applyDecompression(rawResponse) {
350
- const encoding = rawResponse.headers?.["content-encoding"];
351
- if (!encoding || !rawResponse.body)
352
- return;
353
- const enc = Array.isArray(encoding) ? encoding[0] : encoding;
354
- if (rawResponse.body instanceof Uint8Array) {
355
- rawResponse.body = decompressBuffer(rawResponse.body, enc);
356
- }
357
- else {
358
- rawResponse.body = createDecompressStream(rawResponse.body, enc);
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 (typeof req === "string") {
377
- let cached = urlCache.get(rawUrl);
378
- if (!cached) {
379
- const urlObj = new URL(rawUrl);
380
- cached = {
381
- href: urlObj.href,
382
- origin: urlObj.origin,
383
- path: urlObj.pathname + urlObj.search,
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
- const urlObj = new URL(rawUrl);
401
- if (req.query) {
402
- this.appendQueryParams(urlObj, req.query);
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 Creates a request object preserving the prototype of the source object (if any).
419
- * @param source - Original request object (may have a custom prototype).
420
- * @param overrides - Properties to override or add.
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
- createInternalRequestObject(source, overrides) {
424
- const proto = Object.getPrototypeOf(source);
425
- const target = proto && proto !== Object.prototype ? Object.create(proto) : {};
426
- return Object.assign(target, source, overrides);
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 Добавляет параметры запроса к URL-объекту.
430
- * @en Appends query parameters to a URL object.
431
- * @param url - URL object to mutate.
432
- * @param query - Record of query parameters (supports arrays).
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
- const v = query[k];
439
- if (v == null)
440
- continue;
441
- if (Array.isArray(v)) {
442
- for (const i of v)
443
- url.searchParams.append(k, String(i));
444
- }
445
- else {
446
- url.searchParams.set(k, String(v));
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 Упрощённый метод для текстовых/JSON-запросов, определяющий метод из объекта запроса.
452
- * @en Shortcut method for text/JSON requests, determining method from the request object.
453
- * @param req - Request URL or configuration object.
454
- * @param signal - Optional AbortSignal.
455
- * @returns Promise resolving to HttpResponse.
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
- async dispatchShortcut(req, signal) {
456
+ shortcut(req, signal) {
458
457
  const method = typeof req === "string" ? "GET" : (req.method ?? "GET");
459
- return this.dispatch(this.buildInternalRequest(method, req, undefined, signal));
458
+ return this.dispatch(this.acquireReq(method, req, undefined, signal));
460
459
  }
461
460
  }
462
461
  //# sourceMappingURL=HyperCore.js.map