@hyperttp/core 1.2.6 → 1.3.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.js CHANGED
@@ -1,124 +1,88 @@
1
1
  import { defaultConfig } from "../defaultConfig.js";
2
2
  import { mapResponseFast, mapStreamFast, mergeHeadersFast, } from "../utils/response.js";
3
- import { createRequire } from "node:module";
4
- import { createPipelines, executeErrorPipeline, executeRequestPipeline, executeResponsePipeline, insertHookSorted, } from "../utils/pipeline.js";
5
- import { normalizeBody, normalizeHeaders, normalizeMethod, } from "../utils/normalize.js";
6
- export function getRuntime() {
7
- if (typeof Bun !== "undefined")
8
- return "bun";
9
- return "node";
10
- }
11
- export const TRANSPORTS = [
12
- {
13
- name: "Bun",
14
- runtime: ["bun"],
15
- pkg: "@hyperttp/transport-bun",
16
- export: "BunTransport",
17
- priority: 100,
18
- },
19
- {
20
- name: "Undici",
21
- runtime: ["node"],
22
- pkg: "@hyperttp/transport-undici",
23
- export: "UndiciTransport",
24
- priority: 90,
25
- },
26
- {
27
- name: "Node",
28
- runtime: ["node", "bun"],
29
- pkg: "../transports/node.js",
30
- export: "NodeTransport",
31
- priority: 10,
32
- },
33
- ];
34
- export async function resolveTransport(config) {
35
- if (config.customTransport) {
36
- config.logger?.("debug", "Using user-provided custom transport.");
37
- return config.customTransport;
38
- }
39
- const runtime = getRuntime();
40
- const candidates = TRANSPORTS.filter((t) => t.runtime.includes(runtime)).sort((a, b) => b.priority - a.priority);
41
- const localRequire = createRequire(process.cwd() + "/package.json");
42
- for (const t of candidates) {
43
- config.logger?.("debug", `Loading transport: ${t.name}`);
44
- try {
45
- const path = t.pkg.startsWith(".")
46
- ? new URL(t.pkg, import.meta.url).href
47
- : localRequire.resolve(t.pkg);
48
- const mod = await import(path);
49
- const Transport = mod[t.export] || mod.default?.[t.export] || mod.default;
50
- if (!Transport)
51
- continue;
52
- config.logger?.("info", `Selected transport: ${t.name}`);
53
- return new Transport(config);
54
- }
55
- catch (e) {
56
- config.logger?.("debug", `Skip ${t.name}: ${e}`);
57
- }
58
- }
59
- throw new Error(`No compatible transport implementation available for runtime: ${runtime}`);
60
- }
3
+ import { TransportManager } from "../transports/manager.js";
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
+ /**
8
+ * @ru Основной класс HTTP-клиента Hyperttp. Управляет транспортами, плагинами, перехватчиками, повторными попытками и сжатием.
9
+ * @en Core HTTP client class for Hyperttp. Manages transports, plugins, interceptors, retries, and compression.
10
+ */
61
11
  export class HyperCore {
12
+ /** @ru Конфигурация клиента (базовый URL, таймауты, заголовки и т.д.). @en Client configuration (base URL, timeouts, headers, etc.). */
62
13
  config;
63
- transport = null;
64
- transportPromise = null;
14
+ transportManager;
65
15
  defaultHeaders;
66
16
  pluginCtx;
67
17
  pipelines = createPipelines();
18
+ /**
19
+ * @ru Создаёт экземпляр HyperCore.
20
+ * @en Creates an instance of HyperCore.
21
+ * @param config - Client configuration options.
22
+ * @param transport - Optional custom transport implementation.
23
+ */
68
24
  constructor(config = defaultConfig, transport) {
69
25
  this.config = {
70
26
  ...defaultConfig,
71
27
  ...config,
72
28
  network: { ...defaultConfig.network, ...config.network },
73
29
  };
74
- if (transport) {
75
- this.transport = transport;
76
- this.transportPromise = Promise.resolve(transport);
77
- if ("config" in transport) {
78
- transport.config = this.config;
79
- }
80
- }
81
- this.defaultHeaders = {
30
+ this.transportManager = new TransportManager(this.config, transport);
31
+ this.defaultHeaders = normalizeHeaders({
82
32
  Accept: "application/json, text/plain, */*",
83
33
  "Accept-Encoding": "gzip, deflate, br",
84
34
  "User-Agent": this.config.network?.userAgent ?? "Hyperttp/2.0",
85
35
  ...this.config.network?.headers,
86
- };
36
+ });
87
37
  this.pluginCtx = { config: this.config, core: this };
88
38
  }
89
- async createTransport() {
90
- return resolveTransport(this.config);
39
+ get transport() {
40
+ return this.transportManager.instance;
91
41
  }
92
42
  ensureTransport() {
93
- return (this.transportPromise ||
94
- (this.transportPromise = this.createTransport().then((t) => {
95
- this.transport = t;
96
- return t;
97
- })));
43
+ return this.transportManager.get();
98
44
  }
99
- async dispatch(req) {
45
+ async dispatchInternal(req) {
100
46
  try {
101
- const shortCircuit = await executeRequestPipeline(this.pipelines.request, req, this.pluginCtx);
102
- if (shortCircuit) {
103
- await executeResponsePipeline(this.pipelines.responseMutators, this.pipelines.responseSideEffects, shortCircuit, req, this.pluginCtx, this.config.logger);
104
- return shortCircuit;
47
+ if (this.pipelines.request.length > 0) {
48
+ const shortCircuit = await executeRequestPipeline(this.pipelines.request, req, this.pluginCtx);
49
+ if (shortCircuit) {
50
+ await this.runResponsePipeline(shortCircuit, req);
51
+ return shortCircuit;
52
+ }
105
53
  }
106
54
  const transport = this.transport || (await this.ensureTransport());
107
- const rawResponse = await transport.execute(req);
108
- const response = mapResponseFast(rawResponse);
109
- await executeResponsePipeline(this.pipelines.responseMutators, this.pipelines.responseSideEffects, response, req, this.pluginCtx, this.config.logger);
55
+ let rawResponse = await transport.execute(req);
56
+ if (this.pipelines.responseData.length > 0) {
57
+ rawResponse = await executeResponseDataPipeline(this.pipelines.responseData, rawResponse, this.pluginCtx);
58
+ }
59
+ this.applyDecompression(rawResponse);
60
+ const isStream = req.meta?.responseType === "stream";
61
+ const response = isStream
62
+ ? mapStreamFast(rawResponse)
63
+ : mapResponseFast(rawResponse);
64
+ await this.runResponsePipeline(response, req);
110
65
  return response;
111
66
  }
112
67
  catch (error) {
113
- const httpError = error;
114
- const recovered = await executeErrorPipeline(this.pipelines.error, httpError, req, this.pluginCtx);
115
- if (recovered) {
116
- await executeResponsePipeline(this.pipelines.responseMutators, this.pipelines.responseSideEffects, recovered, req, this.pluginCtx, this.config.logger);
117
- return recovered;
118
- }
119
- throw error;
68
+ return this.handleDispatchError(error, req);
120
69
  }
121
70
  }
71
+ /**
72
+ * @ru Выполняет HTTP-запрос с полным контролем (через объект InternalRequest).
73
+ * @en Performs an HTTP request with full control (via InternalRequest object).
74
+ * @param req - Internal request object.
75
+ * @returns Promise with the HTTP response.
76
+ */
77
+ async dispatch(req) {
78
+ return this.dispatchInternal(req);
79
+ }
80
+ /**
81
+ * @ru Регистрирует плагин для расширения функциональности клиента.
82
+ * @en Registers a plugin to extend client functionality.
83
+ * @param plugin - Plugin instance.
84
+ * @returns This instance for chaining.
85
+ */
122
86
  use(plugin) {
123
87
  const isEnabled = plugin.enabled ? plugin.enabled(this.config) : true;
124
88
  if (!isEnabled)
@@ -134,20 +98,21 @@ export class HyperCore {
134
98
  });
135
99
  }
136
100
  if (plugin.onResponse) {
137
- if (plugin.mode === "background") {
138
- insertHookSorted(this.pipelines.responseSideEffects, {
139
- name: plugin.name,
140
- priority,
141
- run: plugin.onResponse,
142
- });
143
- }
144
- else {
145
- insertHookSorted(this.pipelines.responseMutators, {
146
- name: plugin.name,
147
- priority,
148
- run: plugin.onResponse,
149
- });
150
- }
101
+ const targetPipeline = plugin.mode === "background"
102
+ ? this.pipelines.responseSideEffects
103
+ : this.pipelines.responseMutators;
104
+ insertHookSorted(targetPipeline, {
105
+ name: plugin.name,
106
+ priority,
107
+ run: plugin.onResponse,
108
+ });
109
+ }
110
+ if (plugin.onResponseData) {
111
+ insertHookSorted(this.pipelines.responseData, {
112
+ name: plugin.name,
113
+ priority,
114
+ run: plugin.onResponseData,
115
+ });
151
116
  }
152
117
  if (plugin.onError) {
153
118
  insertHookSorted(this.pipelines.error, {
@@ -158,176 +123,305 @@ export class HyperCore {
158
123
  }
159
124
  return this;
160
125
  }
126
+ /**
127
+ * @ru Выполняет GET-запрос и возвращает ответ в виде потока (StreamResponse).
128
+ * @en Performs a GET request and returns the response as a stream (StreamResponse).
129
+ * @param req - Request URL or RequestInterface object.
130
+ * @param signal - Optional abort signal.
131
+ * @returns Promise with the stream response.
132
+ */
161
133
  async stream(req, signal) {
162
- const isStr = typeof req === "string";
163
- const url = isStr ? req : req.url;
164
- const reqHeaders = isStr ? undefined : req.headers;
165
- const finalSignal = isStr ? signal : (req.signal ?? signal);
166
- const transportArgs = {
167
- method: normalizeMethod("GET"),
168
- url,
169
- headers: normalizeHeaders(mergeHeadersFast(this.defaultHeaders, reqHeaders)),
170
- signal: finalSignal,
171
- };
172
- const transport = this.transport || (await this.ensureTransport());
173
- const rawResponse = await transport.execute(transportArgs);
174
- return mapStreamFast(rawResponse);
134
+ const internalReq = this.buildInternalRequest("GET", req, undefined, signal);
135
+ internalReq.meta = { ...internalReq.meta, responseType: "stream" };
136
+ return this.dispatchInternal(internalReq);
175
137
  }
138
+ /**
139
+ * @ru Выполняет POST-запрос и возвращает ответ в виде потока (StreamResponse).
140
+ * @en Performs a POST request and returns the response as a stream (StreamResponse).
141
+ * @param req - Request URL or RequestInterface object.
142
+ * @param body - Request body data.
143
+ * @param signal - Optional abort signal.
144
+ * @returns Promise with the stream response.
145
+ */
146
+ async postStream(req, body, signal) {
147
+ const internalReq = this.buildInternalRequest("POST", req, body, signal);
148
+ internalReq.meta = { ...internalReq.meta, responseType: "stream" };
149
+ return this.dispatchInternal(internalReq);
150
+ }
151
+ /**
152
+ * @ru Выполняет GET-запрос.
153
+ * @en Performs a GET request.
154
+ * @param req - Request URL or RequestInterface object.
155
+ * @param signal - Optional abort signal.
156
+ * @returns Promise with the HTTP response.
157
+ */
176
158
  get(req, signal) {
177
159
  return this.dispatch(this.buildInternalRequest("GET", req, undefined, signal));
178
160
  }
161
+ /**
162
+ * @ru Выполняет POST-запрос.
163
+ * @en Performs a POST request.
164
+ * @param req - Request URL or RequestInterface object.
165
+ * @param body - Request body data.
166
+ * @param signal - Optional abort signal.
167
+ * @returns Promise with the HTTP response.
168
+ */
179
169
  post(req, body, signal) {
180
170
  return this.dispatch(this.buildInternalRequest("POST", req, body, signal));
181
171
  }
182
- async postStream(req, body, signal) {
183
- const isStr = typeof req === "string";
184
- const url = isStr ? req : req.url;
185
- const reqHeaders = isStr ? undefined : req.headers;
186
- const finalSignal = isStr ? signal : (req.signal ?? signal);
187
- const transportArgs = {
188
- method: normalizeMethod("POST"),
189
- url,
190
- headers: normalizeHeaders(mergeHeadersFast(this.defaultHeaders, reqHeaders)),
191
- body: normalizeBody("POST", isStr ? body : (req.body ?? body)),
192
- signal: finalSignal,
193
- };
194
- const transport = this.transport || (await this.ensureTransport());
195
- const rawResponse = await transport.execute(transportArgs);
196
- return mapStreamFast(rawResponse);
197
- }
172
+ /**
173
+ * @ru Выполняет PUT-запрос.
174
+ * @en Performs a PUT request.
175
+ * @param req - Request URL or RequestInterface object.
176
+ * @param body - Request body data.
177
+ * @param signal - Optional abort signal.
178
+ * @returns Promise with the HTTP response.
179
+ */
198
180
  put(req, body, signal) {
199
181
  return this.dispatch(this.buildInternalRequest("PUT", req, body, signal));
200
182
  }
183
+ /**
184
+ * @ru Выполняет PATCH-запрос.
185
+ * @en Performs a PATCH request.
186
+ * @param req - Request URL or RequestInterface object.
187
+ * @param body - Request body data.
188
+ * @param signal - Optional abort signal.
189
+ * @returns Promise with the HTTP response.
190
+ */
201
191
  patch(req, body, signal) {
202
192
  return this.dispatch(this.buildInternalRequest("PATCH", req, body, signal));
203
193
  }
194
+ /**
195
+ * @ru Выполняет DELETE-запрос.
196
+ * @en Performs a DELETE request.
197
+ * @param req - Request URL or RequestInterface object.
198
+ * @param signal - Optional abort signal.
199
+ * @returns Promise with the HTTP response.
200
+ */
204
201
  delete(req, signal) {
205
202
  return this.dispatch(this.buildInternalRequest("DELETE", req, undefined, signal));
206
203
  }
204
+ /**
205
+ * @ru Выполняет OPTIONS-запрос.
206
+ * @en Performs an OPTIONS request.
207
+ * @param req - Request URL or RequestInterface object.
208
+ * @param body - Optional request body data.
209
+ * @param signal - Optional abort signal.
210
+ * @returns Promise with the HTTP response.
211
+ */
207
212
  options(req, body, signal) {
208
213
  return this.dispatch(this.buildInternalRequest("OPTIONS", req, body, signal));
209
214
  }
215
+ /**
216
+ * @ru Выполняет HEAD-запрос.
217
+ * @en Performs a HEAD request.
218
+ * @param req - Request URL or RequestInterface object.
219
+ * @param signal - Optional abort signal.
220
+ * @returns Promise with the HTTP response (body is always null).
221
+ */
210
222
  head(req, signal) {
211
223
  return this.dispatch(this.buildInternalRequest("HEAD", req, undefined, signal));
212
224
  }
213
- buildInternalRequest(method, req, body, signal) {
214
- const isStr = typeof req === "string";
215
- let rawUrl = isStr ? req : req?.url;
216
- if (!rawUrl && !isStr && req && "scheme" in req && "host" in req) {
217
- const casted = req;
218
- try {
219
- const cleanHost = String(casted.host).replace(/^https?:\/\//i, "");
220
- const urlObj = new URL(`${casted.scheme}://${cleanHost}`);
221
- if (casted.port)
222
- urlObj.port = String(casted.port);
223
- if (casted.path)
224
- urlObj.pathname = casted.path;
225
- if (casted.query) {
226
- for (const [key, value] of Object.entries(casted.query)) {
227
- if (value == null)
228
- continue;
229
- if (Array.isArray(value)) {
230
- value.forEach((v) => urlObj.searchParams.append(key, String(v)));
231
- }
232
- else {
233
- urlObj.searchParams.set(key, String(value));
234
- }
235
- }
236
- }
237
- rawUrl = urlObj.toString();
238
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
239
- }
240
- catch (_e) {
241
- //
225
+ /**
226
+ * @ru Создаёт новый экземпляр HyperCore с расширенной конфигурацией (поверх текущей).
227
+ * @en Creates a new HyperCore instance with extended configuration (on top of current).
228
+ * @param options - Additional configuration options.
229
+ * @returns New HyperCore instance.
230
+ */
231
+ extend(options) {
232
+ return new HyperCore({
233
+ ...this.config,
234
+ ...options,
235
+ network: { ...this.config.network, ...options.network },
236
+ }, this.transport ?? undefined);
237
+ }
238
+ /**
239
+ * @ru Алиас для extend(). Создаёт новый экземпляр HyperCore.
240
+ * @en Alias for extend(). Creates a new HyperCore instance.
241
+ * @param options - Configuration options.
242
+ * @returns New HyperCore instance.
243
+ */
244
+ create(options) {
245
+ return this.extend(options);
246
+ }
247
+ /**
248
+ * @ru Уничтожает клиент, закрывая соединения и очищая ресурсы.
249
+ * @en Destroys the client, closing connections and cleaning up resources.
250
+ * @param graceful - If true, attempts graceful shutdown (default: true).
251
+ * @returns Promise that resolves when destruction is complete.
252
+ */
253
+ async destroy(graceful = true) {
254
+ await this.transportManager.destroy(graceful);
255
+ }
256
+ /**
257
+ * @ru Выполняет запрос и сразу возвращает распарсенный JSON (сокращённый метод).
258
+ * @en Performs a request and immediately returns parsed JSON (shortcut method).
259
+ * @param req - Request URL or RequestInterface object.
260
+ * @param signal - Optional abort signal.
261
+ * @returns Promise with the parsed JSON value.
262
+ * @throws If the response does not contain a json() method.
263
+ */
264
+ async json(req, signal) {
265
+ const res = await this.dispatchShortcut(req, signal);
266
+ if (!res.json)
267
+ throw new Error("[HyperCore] Method 'json()' is missing on response object.");
268
+ return res.json();
269
+ }
270
+ /**
271
+ * @ru Выполняет запрос и возвращает тело как строку (сокращённый метод).
272
+ * @en Performs a request and returns the body as a string (shortcut method).
273
+ * @param req - Request URL or RequestInterface object.
274
+ * @param signal - Optional abort signal.
275
+ * @returns Promise with the response text.
276
+ * @throws If the response does not contain a text() method.
277
+ */
278
+ async text(req, signal) {
279
+ const res = await this.dispatchShortcut(req, signal);
280
+ if (!res.text)
281
+ throw new Error("[HyperCore] Method 'text()' is missing on response object.");
282
+ return res.text();
283
+ }
284
+ /**
285
+ * @ru Выполняет запрос и немедленно отменяет (сбрасывает) тело ответа без чтения.
286
+ * @en Performs a request and immediately discards the response body without reading.
287
+ * @param req - Request URL or RequestInterface object.
288
+ * @param signal - Optional abort signal.
289
+ * @returns Promise that resolves after dumping the response.
290
+ * @throws If the response does not contain a dump() method.
291
+ */
292
+ async dump(req, signal) {
293
+ const res = await this.dispatchShortcut(req, signal);
294
+ if (!res.dump)
295
+ throw new Error("[HyperCore] Method 'dump()' is missing on response object.");
296
+ await res.dump();
297
+ }
298
+ async runResponsePipeline(response, req) {
299
+ const hasMutators = this.pipelines.responseMutators.length > 0;
300
+ const hasSideEffects = this.pipelines.responseSideEffects.length > 0;
301
+ if (hasMutators || hasSideEffects) {
302
+ await executeResponsePipeline(this.pipelines.responseMutators, this.pipelines.responseSideEffects, response, req, this.pluginCtx, this.config.logger);
303
+ }
304
+ }
305
+ async handleDispatchError(error, req) {
306
+ if (this.pipelines.error.length > 0) {
307
+ const recovered = await executeErrorPipeline(this.pipelines.error, error, req, this.pluginCtx);
308
+ if (recovered) {
309
+ await this.runResponsePipeline(recovered, req);
310
+ return recovered;
242
311
  }
243
312
  }
244
- if (this.config.baseURL && rawUrl && !/^https?:\/\//i.test(rawUrl)) {
245
- rawUrl = new URL(rawUrl, this.config.baseURL).toString();
313
+ throw error;
314
+ }
315
+ applyDecompression(rawResponse) {
316
+ if (!rawResponse.body)
317
+ return;
318
+ const encodingHeader = rawResponse.headers?.["content-encoding"] ??
319
+ rawResponse.headers?.["Content-Encoding"];
320
+ if (!encodingHeader)
321
+ return;
322
+ const contentEncoding = typeof encodingHeader === "string"
323
+ ? encodingHeader
324
+ : Array.isArray(encodingHeader)
325
+ ? encodingHeader[0]
326
+ : undefined;
327
+ if (!contentEncoding)
328
+ return;
329
+ if (rawResponse.body instanceof Uint8Array) {
330
+ rawResponse.body = decompressBuffer(rawResponse.body, contentEncoding);
246
331
  }
332
+ else {
333
+ rawResponse.body = createDecompressStream(rawResponse.body, contentEncoding);
334
+ }
335
+ }
336
+ buildInternalRequest(method, req, body, signal) {
337
+ const isStr = typeof req === "string";
338
+ const rawUrl = normalizeUrl(req);
247
339
  if (!rawUrl) {
248
- throw new Error(`[HyperttpCore] Critical execution failure during ${method} method invocation: 'url' resolved to undefined. ` +
249
- `Verify incoming arguments or environment configurations.`);
340
+ throw new Error(`[HyperCore] Critical failure: 'url' resolved to undefined for ${method}.`);
341
+ }
342
+ const urlObj = new URL(rawUrl);
343
+ const castedReq = isStr
344
+ ? undefined
345
+ : req;
346
+ if (castedReq?.query) {
347
+ this.appendQueryParams(urlObj, castedReq.query);
250
348
  }
349
+ const context = {
350
+ method,
351
+ url: urlObj.href,
352
+ origin: urlObj.origin,
353
+ path: urlObj.pathname + urlObj.search,
354
+ };
251
355
  if (isStr) {
252
356
  return {
253
- method: normalizeMethod(method),
254
- url: rawUrl,
255
- headers: normalizeHeaders(this.defaultHeaders),
357
+ ...context,
358
+ headers: { ...this.defaultHeaders },
256
359
  body,
257
360
  signal,
258
- meta: undefined,
361
+ meta: {},
259
362
  };
260
363
  }
261
- const castedReq = req;
262
- const rawHeaders = req.headers ?? castedReq._headers;
364
+ const rawHeaders = (req.headers ?? castedReq._headers);
263
365
  const headers = rawHeaders
264
- ? mergeHeadersFast(this.defaultHeaders, rawHeaders)
366
+ ? normalizeHeaders(mergeHeadersFast(this.defaultHeaders, rawHeaders))
265
367
  : { ...this.defaultHeaders };
266
- const internalRequest = {
267
- method: normalizeMethod(method),
268
- url: rawUrl,
269
- headers: normalizeHeaders(headers),
270
- body: normalizeBody(method, castedReq.body ?? castedReq._bodyData ?? body),
271
- signal: req.signal ?? castedReq._signal ?? signal,
272
- meta: (req.meta ?? castedReq._meta),
273
- };
274
- this.config.logger?.("debug", `[HyperttpCore] Internal request dispatched: { ` +
275
- `method: "${internalRequest.method}", ` +
276
- `url: "${internalRequest.url}", ` +
277
- `headersCount: ${Object.keys(internalRequest.headers).length}, ` +
278
- `bodyType: "${typeof internalRequest.body}", ` +
279
- `bodyLength: ${typeof internalRequest.body === "string" ? internalRequest.body.length : 0} ` +
280
- `}`);
281
- return internalRequest;
282
- }
283
- extend(options) {
284
- this.config = {
285
- ...this.config,
286
- ...options,
287
- network: { ...this.config.network, ...options.network },
288
- };
289
- this.pluginCtx.config = this.config;
290
- return this;
291
- }
292
- create(options) {
293
- return new HyperCore({
294
- ...this.config,
295
- ...options,
296
- network: { ...this.config.network, ...options.network },
297
- }, this.transport ?? undefined);
368
+ const rawBody = castedReq.body ?? castedReq.bodyData ?? castedReq._bodyData ?? body;
369
+ const computedBody = normalizeBody(method, rawBody);
370
+ const computedSignal = req.signal ?? castedReq._signal ?? signal;
371
+ const meta = (req.meta ??
372
+ castedReq._meta ??
373
+ {});
374
+ return this.createInternalRequestObject(castedReq, {
375
+ ...context,
376
+ headers,
377
+ body: computedBody,
378
+ signal: computedSignal,
379
+ meta,
380
+ });
298
381
  }
299
- async destroy(graceful = true) {
300
- this.config.logger?.("debug", "Destroying transport...");
301
- const transport = this.transport;
302
- if (!transport)
303
- return;
304
- if (graceful && typeof transport.close === "function") {
305
- await transport.close();
382
+ createInternalRequestObject(sourceReq, overrides) {
383
+ const proto = Object.getPrototypeOf(sourceReq);
384
+ const target = proto && proto !== Object.prototype ? Object.create(proto) : {};
385
+ for (const key in sourceReq) {
386
+ if (Object.prototype.hasOwnProperty.call(sourceReq, key)) {
387
+ target[key] = sourceReq[key];
388
+ }
306
389
  }
307
- else if (typeof transport.destroy === "function") {
308
- await transport.destroy();
390
+ for (const key in overrides) {
391
+ if (Object.prototype.hasOwnProperty.call(overrides, key)) {
392
+ target[key] = overrides[key];
393
+ }
309
394
  }
395
+ return target;
310
396
  }
311
- async json(req, signal) {
312
- const method = typeof req === "string"
313
- ? "GET"
314
- : (req.method ?? "GET");
315
- const res = await this.dispatch(this.buildInternalRequest(method, req, undefined, signal));
316
- return res.json();
317
- }
318
- async text(req, signal) {
319
- const method = typeof req === "string"
320
- ? "GET"
321
- : (req.method ?? "GET");
322
- const res = await this.dispatch(this.buildInternalRequest(method, req, undefined, signal));
323
- return res.text();
397
+ appendQueryParams(url, query) {
398
+ if (!query)
399
+ return;
400
+ for (const key in query) {
401
+ if (Object.prototype.hasOwnProperty.call(query, key)) {
402
+ const value = query[key];
403
+ if (value === undefined || value === null)
404
+ continue;
405
+ if (Array.isArray(value)) {
406
+ const len = value.length;
407
+ for (let i = 0; i < len; i++) {
408
+ const item = value[i];
409
+ if (item === undefined || item === null)
410
+ continue;
411
+ url.searchParams.append(key, String(item));
412
+ }
413
+ }
414
+ else {
415
+ url.searchParams.set(key, String(value));
416
+ }
417
+ }
418
+ }
324
419
  }
325
- async dump(req, signal) {
420
+ async dispatchShortcut(req, signal) {
326
421
  const method = typeof req === "string"
327
422
  ? "GET"
328
423
  : (req.method ?? "GET");
329
- const res = await this.dispatch(this.buildInternalRequest(method, req, undefined, signal));
330
- await res.dump();
424
+ return this.dispatch(this.buildInternalRequest(method, req, undefined, signal));
331
425
  }
332
426
  }
333
427
  //# sourceMappingURL=HyperCore.js.map