recker 1.0.2-0 → 1.0.3

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 (287) hide show
  1. package/LICENSE +0 -2
  2. package/README.md +121 -72
  3. package/dist/cache/memory-storage.d.ts.map +1 -1
  4. package/dist/cache/memory-storage.js +7 -1
  5. package/dist/constants/http-status.d.ts +74 -0
  6. package/dist/constants/http-status.d.ts.map +1 -0
  7. package/dist/constants/http-status.js +156 -0
  8. package/dist/constants.d.ts.map +1 -1
  9. package/dist/constants.js +6 -6
  10. package/dist/cookies/memory-cookie-jar.d.ts +31 -0
  11. package/dist/cookies/memory-cookie-jar.d.ts.map +1 -0
  12. package/dist/cookies/memory-cookie-jar.js +210 -0
  13. package/dist/core/client.d.ts +9 -0
  14. package/dist/core/client.d.ts.map +1 -1
  15. package/dist/core/client.js +252 -53
  16. package/dist/core/errors.d.ts +18 -2
  17. package/dist/core/errors.d.ts.map +1 -1
  18. package/dist/core/errors.js +66 -5
  19. package/dist/core/index.d.ts +6 -0
  20. package/dist/core/index.d.ts.map +1 -0
  21. package/dist/core/index.js +5 -0
  22. package/dist/core/request-promise.d.ts.map +1 -1
  23. package/dist/core/request-promise.js +8 -2
  24. package/dist/core/request.d.ts +7 -1
  25. package/dist/core/request.d.ts.map +1 -1
  26. package/dist/core/request.js +32 -0
  27. package/dist/core/response.d.ts +2 -0
  28. package/dist/core/response.d.ts.map +1 -1
  29. package/dist/core/response.js +44 -19
  30. package/dist/events/request-events.d.ts +48 -0
  31. package/dist/events/request-events.d.ts.map +1 -0
  32. package/dist/events/request-events.js +85 -0
  33. package/dist/index.d.ts +28 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +28 -2
  36. package/dist/mcp/client.d.ts.map +1 -1
  37. package/dist/mcp/client.js +16 -5
  38. package/dist/mcp/contract.d.ts +77 -0
  39. package/dist/mcp/contract.d.ts.map +1 -0
  40. package/dist/mcp/contract.js +278 -0
  41. package/dist/mcp/types.d.ts +1 -0
  42. package/dist/mcp/types.d.ts.map +1 -1
  43. package/dist/plugins/auth.d.ts +45 -0
  44. package/dist/plugins/auth.d.ts.map +1 -0
  45. package/dist/plugins/auth.js +268 -0
  46. package/dist/plugins/cache.d.ts +7 -1
  47. package/dist/plugins/cache.d.ts.map +1 -1
  48. package/dist/plugins/cache.js +470 -49
  49. package/dist/plugins/circuit-breaker.js +1 -1
  50. package/dist/plugins/compression.d.ts.map +1 -1
  51. package/dist/plugins/compression.js +3 -3
  52. package/dist/plugins/dedup.d.ts.map +1 -1
  53. package/dist/plugins/dedup.js +2 -1
  54. package/dist/plugins/graphql.d.ts +4 -3
  55. package/dist/plugins/graphql.d.ts.map +1 -1
  56. package/dist/plugins/graphql.js +24 -5
  57. package/dist/plugins/grpc-web.d.ts +80 -0
  58. package/dist/plugins/grpc-web.d.ts.map +1 -0
  59. package/dist/plugins/grpc-web.js +261 -0
  60. package/dist/plugins/har-player.d.ts.map +1 -1
  61. package/dist/plugins/har-player.js +11 -2
  62. package/dist/plugins/hls.d.ts +33 -0
  63. package/dist/plugins/hls.d.ts.map +1 -0
  64. package/dist/plugins/hls.js +225 -0
  65. package/dist/plugins/http2-push.d.ts +64 -0
  66. package/dist/plugins/http2-push.d.ts.map +1 -0
  67. package/dist/plugins/http2-push.js +274 -0
  68. package/dist/plugins/http3.d.ts +76 -0
  69. package/dist/plugins/http3.d.ts.map +1 -0
  70. package/dist/plugins/http3.js +231 -0
  71. package/dist/plugins/interface-rotator.d.ts +10 -0
  72. package/dist/plugins/interface-rotator.d.ts.map +1 -0
  73. package/dist/plugins/interface-rotator.js +57 -0
  74. package/dist/plugins/jsonrpc.d.ts +76 -0
  75. package/dist/plugins/jsonrpc.d.ts.map +1 -0
  76. package/dist/plugins/jsonrpc.js +143 -0
  77. package/dist/plugins/logger.d.ts +8 -5
  78. package/dist/plugins/logger.d.ts.map +1 -1
  79. package/dist/plugins/logger.js +66 -30
  80. package/dist/plugins/odata.d.ts +182 -0
  81. package/dist/plugins/odata.d.ts.map +1 -0
  82. package/dist/plugins/odata.js +561 -0
  83. package/dist/plugins/retry.d.ts +1 -0
  84. package/dist/plugins/retry.d.ts.map +1 -1
  85. package/dist/plugins/retry.js +26 -2
  86. package/dist/plugins/scrape.d.ts +22 -0
  87. package/dist/plugins/scrape.d.ts.map +1 -0
  88. package/dist/plugins/scrape.js +87 -0
  89. package/dist/plugins/soap.d.ts +73 -0
  90. package/dist/plugins/soap.d.ts.map +1 -0
  91. package/dist/plugins/soap.js +347 -0
  92. package/dist/plugins/user-agent.d.ts +8 -0
  93. package/dist/plugins/user-agent.d.ts.map +1 -0
  94. package/dist/plugins/user-agent.js +46 -0
  95. package/dist/plugins/xml.d.ts +10 -0
  96. package/dist/plugins/xml.d.ts.map +1 -0
  97. package/dist/plugins/xml.js +194 -0
  98. package/dist/presets/anthropic.d.ts +7 -0
  99. package/dist/presets/anthropic.d.ts.map +1 -0
  100. package/dist/presets/anthropic.js +17 -0
  101. package/dist/presets/azure-openai.d.ts +9 -0
  102. package/dist/presets/azure-openai.d.ts.map +1 -0
  103. package/dist/presets/azure-openai.js +25 -0
  104. package/dist/presets/cloudflare.d.ts +13 -0
  105. package/dist/presets/cloudflare.d.ts.map +1 -0
  106. package/dist/presets/cloudflare.js +39 -0
  107. package/dist/presets/cohere.d.ts +6 -0
  108. package/dist/presets/cohere.d.ts.map +1 -0
  109. package/dist/presets/cohere.js +16 -0
  110. package/dist/presets/deepseek.d.ts +6 -0
  111. package/dist/presets/deepseek.d.ts.map +1 -0
  112. package/dist/presets/deepseek.js +16 -0
  113. package/dist/presets/digitalocean.d.ts +6 -0
  114. package/dist/presets/digitalocean.d.ts.map +1 -0
  115. package/dist/presets/digitalocean.js +16 -0
  116. package/dist/presets/discord.d.ts +7 -0
  117. package/dist/presets/discord.d.ts.map +1 -0
  118. package/dist/presets/discord.js +17 -0
  119. package/dist/presets/fireworks.d.ts +6 -0
  120. package/dist/presets/fireworks.d.ts.map +1 -0
  121. package/dist/presets/fireworks.js +16 -0
  122. package/dist/presets/gemini.d.ts +6 -0
  123. package/dist/presets/gemini.d.ts.map +1 -0
  124. package/dist/presets/gemini.js +16 -0
  125. package/dist/presets/github.d.ts +7 -0
  126. package/dist/presets/github.d.ts.map +1 -0
  127. package/dist/presets/github.js +17 -0
  128. package/dist/presets/gitlab.d.ts +7 -0
  129. package/dist/presets/gitlab.d.ts.map +1 -0
  130. package/dist/presets/gitlab.js +16 -0
  131. package/dist/presets/groq.d.ts +6 -0
  132. package/dist/presets/groq.d.ts.map +1 -0
  133. package/dist/presets/groq.js +16 -0
  134. package/dist/presets/huggingface.d.ts +6 -0
  135. package/dist/presets/huggingface.d.ts.map +1 -0
  136. package/dist/presets/huggingface.js +16 -0
  137. package/dist/presets/index.d.ts +28 -0
  138. package/dist/presets/index.d.ts.map +1 -0
  139. package/dist/presets/index.js +27 -0
  140. package/dist/presets/linear.d.ts +6 -0
  141. package/dist/presets/linear.d.ts.map +1 -0
  142. package/dist/presets/linear.js +16 -0
  143. package/dist/presets/mistral.d.ts +6 -0
  144. package/dist/presets/mistral.d.ts.map +1 -0
  145. package/dist/presets/mistral.js +16 -0
  146. package/dist/presets/notion.d.ts +7 -0
  147. package/dist/presets/notion.d.ts.map +1 -0
  148. package/dist/presets/notion.js +17 -0
  149. package/dist/presets/openai.d.ts +8 -0
  150. package/dist/presets/openai.d.ts.map +1 -0
  151. package/dist/presets/openai.js +23 -0
  152. package/dist/presets/perplexity.d.ts +6 -0
  153. package/dist/presets/perplexity.d.ts.map +1 -0
  154. package/dist/presets/perplexity.js +16 -0
  155. package/dist/presets/registry.d.ts +20 -0
  156. package/dist/presets/registry.d.ts.map +1 -0
  157. package/dist/presets/registry.js +311 -0
  158. package/dist/presets/replicate.d.ts +6 -0
  159. package/dist/presets/replicate.d.ts.map +1 -0
  160. package/dist/presets/replicate.js +16 -0
  161. package/dist/presets/slack.d.ts +6 -0
  162. package/dist/presets/slack.d.ts.map +1 -0
  163. package/dist/presets/slack.js +16 -0
  164. package/dist/presets/stripe.d.ts +8 -0
  165. package/dist/presets/stripe.d.ts.map +1 -0
  166. package/dist/presets/stripe.js +23 -0
  167. package/dist/presets/supabase.d.ts +7 -0
  168. package/dist/presets/supabase.d.ts.map +1 -0
  169. package/dist/presets/supabase.js +18 -0
  170. package/dist/presets/together.d.ts +6 -0
  171. package/dist/presets/together.d.ts.map +1 -0
  172. package/dist/presets/together.js +16 -0
  173. package/dist/presets/twilio.d.ts +7 -0
  174. package/dist/presets/twilio.d.ts.map +1 -0
  175. package/dist/presets/twilio.js +17 -0
  176. package/dist/presets/vercel.d.ts +7 -0
  177. package/dist/presets/vercel.d.ts.map +1 -0
  178. package/dist/presets/vercel.js +23 -0
  179. package/dist/presets/xai.d.ts +7 -0
  180. package/dist/presets/xai.d.ts.map +1 -0
  181. package/dist/presets/xai.js +17 -0
  182. package/dist/protocols/ftp.d.ts +63 -0
  183. package/dist/protocols/ftp.d.ts.map +1 -0
  184. package/dist/protocols/ftp.js +388 -0
  185. package/dist/protocols/index.d.ts +4 -0
  186. package/dist/protocols/index.d.ts.map +1 -0
  187. package/dist/protocols/index.js +3 -0
  188. package/dist/protocols/sftp.d.ts +65 -0
  189. package/dist/protocols/sftp.d.ts.map +1 -0
  190. package/dist/protocols/sftp.js +346 -0
  191. package/dist/protocols/telnet.d.ts +50 -0
  192. package/dist/protocols/telnet.d.ts.map +1 -0
  193. package/dist/protocols/telnet.js +139 -0
  194. package/dist/runner/request-runner.d.ts.map +1 -1
  195. package/dist/runner/request-runner.js +1 -0
  196. package/dist/scrape/document.d.ts +44 -0
  197. package/dist/scrape/document.d.ts.map +1 -0
  198. package/dist/scrape/document.js +198 -0
  199. package/dist/scrape/element.d.ts +50 -0
  200. package/dist/scrape/element.d.ts.map +1 -0
  201. package/dist/scrape/element.js +176 -0
  202. package/dist/scrape/extractors.d.ts +17 -0
  203. package/dist/scrape/extractors.d.ts.map +1 -0
  204. package/dist/scrape/extractors.js +356 -0
  205. package/dist/scrape/index.d.ts +5 -0
  206. package/dist/scrape/index.d.ts.map +1 -0
  207. package/dist/scrape/index.js +3 -0
  208. package/dist/scrape/types.d.ts +108 -0
  209. package/dist/scrape/types.d.ts.map +1 -0
  210. package/dist/scrape/types.js +1 -0
  211. package/dist/testing/index.d.ts +3 -0
  212. package/dist/testing/index.d.ts.map +1 -0
  213. package/dist/testing/index.js +1 -0
  214. package/dist/testing/mock.d.ts +58 -0
  215. package/dist/testing/mock.d.ts.map +1 -0
  216. package/dist/testing/mock.js +252 -0
  217. package/dist/transport/fetch.d.ts.map +1 -1
  218. package/dist/transport/fetch.js +12 -4
  219. package/dist/transport/undici.d.ts +17 -1
  220. package/dist/transport/undici.d.ts.map +1 -1
  221. package/dist/transport/undici.js +708 -47
  222. package/dist/types/index.d.ts +111 -10
  223. package/dist/types/index.d.ts.map +1 -1
  224. package/dist/types/index.js +1 -1
  225. package/dist/types/logger.d.ts +17 -0
  226. package/dist/types/logger.d.ts.map +1 -0
  227. package/dist/types/logger.js +66 -0
  228. package/dist/utils/agent-manager.d.ts.map +1 -1
  229. package/dist/utils/agent-manager.js +20 -4
  230. package/dist/utils/body.d.ts.map +1 -1
  231. package/dist/utils/body.js +14 -2
  232. package/dist/utils/charset.d.ts +16 -0
  233. package/dist/utils/charset.d.ts.map +1 -0
  234. package/dist/utils/charset.js +169 -0
  235. package/dist/utils/client-pool.d.ts +21 -0
  236. package/dist/utils/client-pool.d.ts.map +1 -0
  237. package/dist/utils/client-pool.js +49 -0
  238. package/dist/utils/concurrency.d.ts.map +1 -1
  239. package/dist/utils/concurrency.js +8 -4
  240. package/dist/utils/dns-toolkit.d.ts +13 -0
  241. package/dist/utils/dns-toolkit.d.ts.map +1 -0
  242. package/dist/utils/dns-toolkit.js +48 -0
  243. package/dist/utils/doh.d.ts.map +1 -1
  244. package/dist/utils/doh.js +16 -3
  245. package/dist/utils/download.d.ts +15 -0
  246. package/dist/utils/download.d.ts.map +1 -0
  247. package/dist/utils/download.js +44 -0
  248. package/dist/utils/env-proxy.d.ts +13 -0
  249. package/dist/utils/env-proxy.d.ts.map +1 -0
  250. package/dist/utils/env-proxy.js +105 -0
  251. package/dist/utils/header-parser.d.ts +15 -1
  252. package/dist/utils/header-parser.d.ts.map +1 -1
  253. package/dist/utils/header-parser.js +161 -1
  254. package/dist/utils/link-header.d.ts +70 -0
  255. package/dist/utils/link-header.d.ts.map +1 -0
  256. package/dist/utils/link-header.js +190 -0
  257. package/dist/utils/progress.d.ts +7 -2
  258. package/dist/utils/progress.d.ts.map +1 -1
  259. package/dist/utils/progress.js +48 -15
  260. package/dist/utils/rdap.d.ts +17 -0
  261. package/dist/utils/rdap.d.ts.map +1 -0
  262. package/dist/utils/rdap.js +32 -0
  263. package/dist/utils/request-pool.d.ts.map +1 -1
  264. package/dist/utils/request-pool.js +4 -3
  265. package/dist/utils/sse.d.ts.map +1 -1
  266. package/dist/utils/sse.js +8 -2
  267. package/dist/utils/status-codes.d.ts +84 -0
  268. package/dist/utils/status-codes.d.ts.map +1 -0
  269. package/dist/utils/status-codes.js +204 -0
  270. package/dist/utils/streaming.d.ts.map +1 -1
  271. package/dist/utils/streaming.js +1 -0
  272. package/dist/utils/tls-inspector.d.ts +21 -0
  273. package/dist/utils/tls-inspector.d.ts.map +1 -0
  274. package/dist/utils/tls-inspector.js +39 -0
  275. package/dist/utils/try-fn.d.ts.map +1 -1
  276. package/dist/utils/try-fn.js +11 -5
  277. package/dist/utils/upload.d.ts +1 -0
  278. package/dist/utils/upload.d.ts.map +1 -1
  279. package/dist/utils/upload.js +20 -3
  280. package/dist/utils/user-agent.d.ts +9 -9
  281. package/dist/utils/user-agent.js +9 -9
  282. package/dist/utils/whois.d.ts.map +1 -1
  283. package/dist/utils/whois.js +11 -2
  284. package/dist/websocket/client.d.ts +29 -1
  285. package/dist/websocket/client.d.ts.map +1 -1
  286. package/dist/websocket/client.js +145 -13
  287. package/package.json +45 -8
@@ -1,10 +1,13 @@
1
- import { request as undiciRequest, errors as undiciErrors, ProxyAgent, Agent } from 'undici';
1
+ import { request as undiciRequest, errors as undiciErrors, ProxyAgent, Agent, Client } from 'undici';
2
2
  import { HttpResponse } from '../core/response.js';
3
- import { NetworkError, TimeoutError } from '../core/errors.js';
3
+ import { NetworkError, TimeoutError, ReckerError } from '../core/errors.js';
4
4
  import { performance } from 'perf_hooks';
5
5
  import { AsyncLocalStorage } from 'async_hooks';
6
6
  import { channel } from 'node:diagnostics_channel';
7
7
  import { createLookupFunction } from '../utils/dns.js';
8
+ import { AgentManager } from '../utils/agent-manager.js';
9
+ import { createProgressStream } from '../utils/progress.js';
10
+ import { nodeToWebStream } from '../utils/streaming.js';
8
11
  const undiciRequestChannel = channel('undici:request:create');
9
12
  const undiciBodySentChannel = channel('undici:request:bodySent');
10
13
  const undiciHeadersChannel = channel('undici:request:headers');
@@ -116,24 +119,68 @@ undiciConnectChannel.subscribe((message) => {
116
119
  store.connection.reused = Boolean(socket.reused);
117
120
  }
118
121
  });
122
+ function mapTimeoutOptions(requestTimeout, transportDefaults) {
123
+ return {
124
+ connectTimeout: requestTimeout?.connect ??
125
+ requestTimeout?.secureConnect ??
126
+ transportDefaults?.connectTimeout,
127
+ headersTimeout: requestTimeout?.response ??
128
+ transportDefaults?.headersTimeout,
129
+ bodyTimeout: requestTimeout?.send ??
130
+ transportDefaults?.bodyTimeout,
131
+ totalTimeout: requestTimeout?.request
132
+ };
133
+ }
119
134
  export class UndiciTransport {
135
+ static requestCounter = 0;
120
136
  baseUrl;
121
137
  options;
122
138
  proxyAgent;
123
139
  dnsAgent;
124
140
  agentManager;
141
+ proxyBypassList;
142
+ tlsOptions;
143
+ socketClient;
144
+ observability;
125
145
  constructor(baseUrl, options = {}) {
126
146
  this.baseUrl = baseUrl;
127
147
  this.options = options;
148
+ this.tlsOptions = options.tls;
149
+ this.observability = options.observability !== false;
128
150
  if (options.proxy) {
129
- const proxyUrl = typeof options.proxy === 'string' ? options.proxy : options.proxy.url;
130
- const proxyAuth = typeof options.proxy === 'object' && options.proxy.auth
131
- ? `${options.proxy.auth.username}:${options.proxy.auth.password}`
151
+ const proxyConfig = typeof options.proxy === 'string'
152
+ ? { url: options.proxy }
153
+ : options.proxy;
154
+ const proxyUrl = new URL(proxyConfig.url);
155
+ const proxyType = proxyConfig.type || detectProxyType(proxyUrl.protocol);
156
+ if (proxyType?.startsWith('socks')) {
157
+ throw new NetworkError(`SOCKS proxy (${proxyType}) is not supported. Use an HTTP/HTTPS proxy or a SOCKS-to-HTTP bridge.`, 'ERR_UNSUPPORTED_PROXY_TYPE');
158
+ }
159
+ const proxyAuth = proxyConfig.auth
160
+ ? `${proxyConfig.auth.username}:${proxyConfig.auth.password}`
132
161
  : undefined;
133
162
  const finalProxyUrl = proxyAuth
134
- ? proxyUrl.replace('://', `://${proxyAuth}@`)
135
- : proxyUrl;
136
- this.proxyAgent = new ProxyAgent(finalProxyUrl);
163
+ ? proxyConfig.url.replace('://', `://${proxyAuth}@`)
164
+ : proxyConfig.url;
165
+ this.proxyBypassList = proxyConfig.bypass;
166
+ const proxyAgentOptions = {
167
+ uri: finalProxyUrl,
168
+ headers: proxyConfig.headers,
169
+ token: proxyConfig.token,
170
+ proxyTunnel: proxyConfig.tunnel,
171
+ requestTls: mapTlsOptions(proxyConfig.requestTls ?? options.tls),
172
+ proxyTls: mapTlsOptions(proxyConfig.proxyTls),
173
+ };
174
+ if (proxyConfig.connectTimeout) {
175
+ proxyAgentOptions.connectTimeout = proxyConfig.connectTimeout;
176
+ }
177
+ if (proxyConfig.http2) {
178
+ if (!proxyAgentOptions.requestTls) {
179
+ proxyAgentOptions.requestTls = {};
180
+ }
181
+ proxyAgentOptions.requestTls.ALPNProtocols = ['h2', 'http/1.1'];
182
+ }
183
+ this.proxyAgent = new ProxyAgent(proxyAgentOptions);
137
184
  }
138
185
  this.agentManager = options.agent;
139
186
  if (options.dns && !this.agentManager) {
@@ -144,19 +191,48 @@ export class UndiciTransport {
144
191
  },
145
192
  });
146
193
  }
194
+ if (!this.agentManager) {
195
+ this.agentManager = new AgentManager({
196
+ connections: options.connections,
197
+ pipelining: options.pipelining,
198
+ keepAlive: options.keepAlive,
199
+ keepAliveTimeout: options.keepAliveTimeout,
200
+ keepAliveMaxTimeout: options.keepAliveMaxTimeout,
201
+ connectTimeout: options.connectTimeout,
202
+ perDomainPooling: options.perDomainPooling,
203
+ localAddress: options.localAddress,
204
+ });
205
+ }
206
+ if (options.socketPath) {
207
+ this.socketClient = new Client(baseUrl, {
208
+ socketPath: options.socketPath
209
+ });
210
+ }
147
211
  }
148
212
  async dispatch(req) {
149
- const headers = {};
150
- req.headers.forEach((value, key) => {
151
- headers[key] = value;
152
- });
213
+ const headers = Object.fromEntries(req.headers);
214
+ const contentLengthHeader = headers['content-length'];
215
+ const uploadTotal = contentLengthHeader ? parseInt(contentLengthHeader, 10) : undefined;
153
216
  const path = req.url.startsWith(this.baseUrl) ? req.url.substring(this.baseUrl.length) : req.url;
154
- const fullUrl = new URL(path, this.baseUrl).toString();
217
+ let currentUrl = new URL(path, this.baseUrl).toString();
218
+ const handleRedirectsManually = Boolean(req.beforeRedirect);
219
+ const maxRedirects = req.maxRedirects ?? 20;
220
+ const followRedirects = req.followRedirects !== false;
221
+ const timeouts = mapTimeoutOptions(req.timeout, {
222
+ connectTimeout: this.options.connectTimeout,
223
+ headersTimeout: this.options.headersTimeout,
224
+ bodyTimeout: this.options.bodyTimeout
225
+ });
226
+ let timeoutController;
227
+ let timeoutId;
228
+ if (!this.observability) {
229
+ return this.dispatchFast(req, headers, currentUrl, timeouts, handleRedirectsManually, maxRedirects, followRedirects, uploadTotal);
230
+ }
155
231
  const requestContext = {
156
232
  timings: {},
157
233
  connection: {},
158
234
  requestStartTime: 0,
159
- requestCorrelationId: Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
235
+ requestCorrelationId: `r${++UndiciTransport.requestCounter}`,
160
236
  hooks: req._hooks
161
237
  };
162
238
  return requestStorage.run(requestContext, async () => {
@@ -165,22 +241,304 @@ export class UndiciTransport {
165
241
  if (requestContext.requestStartTime === 0) {
166
242
  requestContext.requestStartTime = startTime;
167
243
  }
168
- const dispatcher = req._dispatcher || this.proxyAgent || this.dnsAgent;
244
+ let redirectCount = 0;
245
+ let currentMethod = req.method;
246
+ let currentBody = req.body;
247
+ let currentHeaders = { ...headers };
248
+ let effectiveSignal = req.signal;
249
+ if (timeouts.totalTimeout) {
250
+ timeoutController = new AbortController();
251
+ if (req.signal) {
252
+ const originalSignal = req.signal;
253
+ effectiveSignal = timeoutController.signal;
254
+ const onOriginalAbort = () => {
255
+ timeoutController.abort();
256
+ };
257
+ if (originalSignal.aborted) {
258
+ timeoutController.abort();
259
+ }
260
+ else {
261
+ originalSignal.addEventListener('abort', onOriginalAbort, { once: true });
262
+ }
263
+ }
264
+ else {
265
+ effectiveSignal = timeoutController.signal;
266
+ }
267
+ timeoutId = setTimeout(() => {
268
+ timeoutController.abort();
269
+ }, timeouts.totalTimeout);
270
+ }
271
+ while (true) {
272
+ const dispatcher = this.socketClient || determineDispatcher({
273
+ explicit: req._dispatcher,
274
+ proxyAgent: this.proxyAgent,
275
+ agentManager: this.agentManager,
276
+ dnsAgent: this.dnsAgent,
277
+ url: currentUrl,
278
+ bypass: this.proxyBypassList
279
+ });
280
+ const bodyWithProgress = redirectCount === 0
281
+ ? wrapUploadBody(currentBody, req.onUploadProgress, uploadTotal)
282
+ : currentBody;
283
+ let finalBody = bodyWithProgress;
284
+ if (finalBody instanceof FormData) {
285
+ const tempResponse = new Response(finalBody);
286
+ finalBody = tempResponse.body;
287
+ tempResponse.headers.forEach((value, key) => {
288
+ if (key.toLowerCase() === 'content-type') {
289
+ currentHeaders[key] = value;
290
+ delete currentHeaders['Content-Type'];
291
+ }
292
+ else if (!currentHeaders[key]) {
293
+ currentHeaders[key] = value;
294
+ }
295
+ });
296
+ }
297
+ const undiciOptions = {
298
+ method: currentMethod,
299
+ headers: currentHeaders,
300
+ body: finalBody,
301
+ signal: effectiveSignal,
302
+ dispatcher: dispatcher,
303
+ connectTimeout: timeouts.connectTimeout,
304
+ headersTimeout: timeouts.headersTimeout,
305
+ bodyTimeout: timeouts.bodyTimeout,
306
+ maxRedirections: handleRedirectsManually ? 0 : this.options.maxRedirections,
307
+ };
308
+ if (finalBody && (finalBody instanceof ReadableStream ||
309
+ (typeof finalBody.pipe === 'function') ||
310
+ finalBody[Symbol.asyncIterator])) {
311
+ undiciOptions.duplex = 'half';
312
+ }
313
+ const tlsOptions = mapTlsOptions(this.tlsOptions);
314
+ if (tlsOptions) {
315
+ undiciOptions.tls = tlsOptions;
316
+ }
317
+ const http2Enabled = req.http2 !== undefined
318
+ ? req.http2
319
+ : this.options.http2?.enabled;
320
+ if (http2Enabled) {
321
+ undiciOptions.allowH2 = true;
322
+ }
323
+ else if (req.http2 === false) {
324
+ undiciOptions.allowH2 = false;
325
+ }
326
+ if (http2Enabled && this.options.http2) {
327
+ if (this.options.http2.maxConcurrentStreams !== undefined) {
328
+ undiciOptions.maxConcurrentStreams = this.options.http2.maxConcurrentStreams;
329
+ }
330
+ if (this.options.http2.pipelining !== undefined) {
331
+ undiciOptions.pipelining = this.options.http2.pipelining;
332
+ }
333
+ }
334
+ let undiciResponse;
335
+ if (this.socketClient) {
336
+ const urlPath = new URL(currentUrl).pathname + new URL(currentUrl).search;
337
+ undiciResponse = await this.socketClient.request({
338
+ path: urlPath || '/',
339
+ method: currentMethod,
340
+ headers: currentHeaders,
341
+ body: bodyWithProgress,
342
+ signal: req.signal,
343
+ });
344
+ }
345
+ else {
346
+ undiciResponse = await undiciRequest(currentUrl, undiciOptions);
347
+ }
348
+ const statusCode = undiciResponse.statusCode;
349
+ const isRedirect = statusCode >= 300 && statusCode < 400;
350
+ if (handleRedirectsManually && isRedirect && followRedirects && redirectCount < maxRedirects) {
351
+ const locationHeader = undiciResponse.headers['location'];
352
+ const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
353
+ if (location) {
354
+ const nextUrl = new URL(location, currentUrl).toString();
355
+ const responseHeaders = new Headers();
356
+ for (const [key, value] of Object.entries(undiciResponse.headers)) {
357
+ if (value !== undefined) {
358
+ if (Array.isArray(value)) {
359
+ value.forEach(v => responseHeaders.append(key, v));
360
+ }
361
+ else {
362
+ responseHeaders.set(key, value);
363
+ }
364
+ }
365
+ }
366
+ const redirectInfo = {
367
+ from: currentUrl,
368
+ to: nextUrl,
369
+ status: statusCode,
370
+ headers: responseHeaders,
371
+ };
372
+ const hookResult = await req.beforeRedirect(redirectInfo);
373
+ if (hookResult === false) {
374
+ const finalResponse = req.onDownloadProgress
375
+ ? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
376
+ : undiciResponse;
377
+ return new HttpResponse(finalResponse, {
378
+ timings: requestContext.timings,
379
+ connection: requestContext.connection
380
+ });
381
+ }
382
+ currentUrl = typeof hookResult === 'string' ? hookResult : nextUrl;
383
+ if (statusCode === 303 || ((statusCode === 301 || statusCode === 302) && currentMethod !== 'GET' && currentMethod !== 'HEAD')) {
384
+ currentMethod = 'GET';
385
+ currentBody = null;
386
+ delete currentHeaders['content-type'];
387
+ delete currentHeaders['content-length'];
388
+ delete currentHeaders['Content-Type'];
389
+ delete currentHeaders['Content-Length'];
390
+ }
391
+ await undiciResponse.body.arrayBuffer().catch(() => { });
392
+ redirectCount++;
393
+ continue;
394
+ }
395
+ }
396
+ const finalResponse = req.onDownloadProgress
397
+ ? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
398
+ : undiciResponse;
399
+ const ttfb = performance.now() - startTime;
400
+ const totalTime = performance.now() - requestContext.requestStartTime;
401
+ if (!requestContext.timings.firstByte) {
402
+ requestContext.timings.firstByte = ttfb;
403
+ }
404
+ if (!requestContext.timings.total) {
405
+ requestContext.timings.total = totalTime;
406
+ }
407
+ return new HttpResponse(finalResponse, {
408
+ timings: requestContext.timings,
409
+ connection: requestContext.connection
410
+ });
411
+ }
412
+ }
413
+ catch (error) {
414
+ if (error instanceof undiciErrors.ConnectTimeoutError || error.code === 'UND_ERR_CONNECT_TIMEOUT') {
415
+ throw new TimeoutError(req, {
416
+ phase: 'connect',
417
+ timeout: timeouts.connectTimeout
418
+ });
419
+ }
420
+ if (error instanceof undiciErrors.HeadersTimeoutError || error.code === 'UND_ERR_HEADERS_TIMEOUT') {
421
+ throw new TimeoutError(req, {
422
+ phase: 'response',
423
+ timeout: timeouts.headersTimeout
424
+ });
425
+ }
426
+ if (error instanceof undiciErrors.BodyTimeoutError || error.code === 'UND_ERR_BODY_TIMEOUT') {
427
+ throw new TimeoutError(req, {
428
+ phase: 'send',
429
+ timeout: timeouts.bodyTimeout
430
+ });
431
+ }
432
+ if (error.name === 'AbortError' || error.code === 'ABORT_ERR') {
433
+ throw new TimeoutError(req, {
434
+ phase: 'request',
435
+ timeout: timeouts.totalTimeout
436
+ });
437
+ }
438
+ const code = error.code || error?.cause?.code;
439
+ if (code === 'UND_ERR_HEADERS_OVERFLOW') {
440
+ throw new ReckerError('Response headers exceeded the maximum allowed size', req, undefined, [
441
+ 'Reduce response header size or adjust maxHeaderSize in agent configuration.',
442
+ 'Check for runaway set-cookie or debug headers.',
443
+ 'Verify upstream is not sending excessive headers.'
444
+ ], false);
445
+ }
446
+ throw new NetworkError(error.message, code, req);
447
+ }
448
+ finally {
449
+ if (timeoutId) {
450
+ clearTimeout(timeoutId);
451
+ }
452
+ }
453
+ });
454
+ }
455
+ async dispatchFast(req, headers, currentUrl, timeouts, handleRedirectsManually, maxRedirects, followRedirects, uploadTotal) {
456
+ let timeoutController;
457
+ let timeoutId;
458
+ try {
459
+ let redirectCount = 0;
460
+ let currentMethod = req.method;
461
+ let currentBody = req.body;
462
+ let currentHeaders = { ...headers };
463
+ let effectiveSignal = req.signal;
464
+ if (timeouts.totalTimeout) {
465
+ timeoutController = new AbortController();
466
+ if (req.signal) {
467
+ const originalSignal = req.signal;
468
+ effectiveSignal = timeoutController.signal;
469
+ const onOriginalAbort = () => {
470
+ timeoutController.abort();
471
+ };
472
+ if (originalSignal.aborted) {
473
+ timeoutController.abort();
474
+ }
475
+ else {
476
+ originalSignal.addEventListener('abort', onOriginalAbort, { once: true });
477
+ }
478
+ }
479
+ else {
480
+ effectiveSignal = timeoutController.signal;
481
+ }
482
+ timeoutId = setTimeout(() => {
483
+ timeoutController.abort();
484
+ }, timeouts.totalTimeout);
485
+ }
486
+ while (true) {
487
+ const dispatcher = this.socketClient || determineDispatcher({
488
+ explicit: req._dispatcher,
489
+ proxyAgent: this.proxyAgent,
490
+ agentManager: this.agentManager,
491
+ dnsAgent: this.dnsAgent,
492
+ url: currentUrl,
493
+ bypass: this.proxyBypassList
494
+ });
495
+ const bodyWithProgress = redirectCount === 0
496
+ ? wrapUploadBody(currentBody, req.onUploadProgress, uploadTotal)
497
+ : currentBody;
498
+ let finalBody = bodyWithProgress;
499
+ if (finalBody instanceof FormData) {
500
+ const tempResponse = new Response(finalBody);
501
+ finalBody = tempResponse.body;
502
+ tempResponse.headers.forEach((value, key) => {
503
+ if (key.toLowerCase() === 'content-type') {
504
+ currentHeaders[key] = value;
505
+ delete currentHeaders['Content-Type'];
506
+ }
507
+ else if (!currentHeaders[key]) {
508
+ currentHeaders[key] = value;
509
+ }
510
+ });
511
+ }
169
512
  const undiciOptions = {
170
- method: req.method,
171
- headers: headers,
172
- body: req.body,
173
- signal: req.signal,
513
+ method: currentMethod,
514
+ headers: currentHeaders,
515
+ body: finalBody,
516
+ signal: effectiveSignal,
174
517
  dispatcher: dispatcher,
175
- connectTimeout: this.options.connectTimeout,
176
- headersTimeout: this.options.headersTimeout,
177
- bodyTimeout: this.options.bodyTimeout,
178
- maxRedirections: this.options.maxRedirections,
518
+ connectTimeout: timeouts.connectTimeout,
519
+ headersTimeout: timeouts.headersTimeout,
520
+ bodyTimeout: timeouts.bodyTimeout,
521
+ maxRedirections: handleRedirectsManually ? 0 : this.options.maxRedirections,
179
522
  };
180
- if (this.options.http2) {
181
- if (this.options.http2.enabled) {
182
- undiciOptions.allowH2 = true;
183
- }
523
+ if (finalBody && (finalBody instanceof ReadableStream ||
524
+ (typeof finalBody.pipe === 'function') ||
525
+ finalBody[Symbol.asyncIterator])) {
526
+ undiciOptions.duplex = 'half';
527
+ }
528
+ const tlsOptions = mapTlsOptions(this.tlsOptions);
529
+ if (tlsOptions) {
530
+ undiciOptions.tls = tlsOptions;
531
+ }
532
+ const http2Enabled = req.http2 !== undefined
533
+ ? req.http2
534
+ : this.options.http2?.enabled;
535
+ if (http2Enabled) {
536
+ undiciOptions.allowH2 = true;
537
+ }
538
+ else if (req.http2 === false) {
539
+ undiciOptions.allowH2 = false;
540
+ }
541
+ if (http2Enabled && this.options.http2) {
184
542
  if (this.options.http2.maxConcurrentStreams !== undefined) {
185
543
  undiciOptions.maxConcurrentStreams = this.options.http2.maxConcurrentStreams;
186
544
  }
@@ -188,31 +546,334 @@ export class UndiciTransport {
188
546
  undiciOptions.pipelining = this.options.http2.pipelining;
189
547
  }
190
548
  }
191
- const undiciResponse = await undiciRequest(fullUrl, undiciOptions);
192
- const ttfb = performance.now() - startTime;
193
- const totalTime = performance.now() - requestContext.requestStartTime;
194
- if (!requestContext.timings.firstByte) {
195
- requestContext.timings.firstByte = ttfb;
549
+ let undiciResponse;
550
+ if (this.socketClient) {
551
+ const urlPath = new URL(currentUrl).pathname + new URL(currentUrl).search;
552
+ undiciResponse = await this.socketClient.request({
553
+ path: urlPath || '/',
554
+ method: currentMethod,
555
+ headers: currentHeaders,
556
+ body: bodyWithProgress,
557
+ signal: req.signal,
558
+ });
196
559
  }
197
- if (!requestContext.timings.total) {
198
- requestContext.timings.total = totalTime;
560
+ else {
561
+ undiciResponse = await undiciRequest(currentUrl, undiciOptions);
199
562
  }
200
- return new HttpResponse(undiciResponse, {
201
- timings: requestContext.timings,
202
- connection: requestContext.connection
563
+ const statusCode = undiciResponse.statusCode;
564
+ const isRedirect = statusCode >= 300 && statusCode < 400;
565
+ if (handleRedirectsManually && isRedirect && followRedirects && redirectCount < maxRedirects) {
566
+ const locationHeader = undiciResponse.headers['location'];
567
+ const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
568
+ if (location) {
569
+ const nextUrl = new URL(location, currentUrl).toString();
570
+ const responseHeaders = new Headers();
571
+ for (const [key, value] of Object.entries(undiciResponse.headers)) {
572
+ if (value !== undefined) {
573
+ if (Array.isArray(value)) {
574
+ value.forEach(v => responseHeaders.append(key, v));
575
+ }
576
+ else {
577
+ responseHeaders.set(key, value);
578
+ }
579
+ }
580
+ }
581
+ const redirectInfo = {
582
+ from: currentUrl,
583
+ to: nextUrl,
584
+ status: statusCode,
585
+ headers: responseHeaders,
586
+ };
587
+ const hookResult = await req.beforeRedirect(redirectInfo);
588
+ if (hookResult === false) {
589
+ const finalResponse = req.onDownloadProgress
590
+ ? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
591
+ : undiciResponse;
592
+ return new HttpResponse(finalResponse, {
593
+ timings: {},
594
+ connection: {}
595
+ });
596
+ }
597
+ currentUrl = typeof hookResult === 'string' ? hookResult : nextUrl;
598
+ if (statusCode === 303 || ((statusCode === 301 || statusCode === 302) && currentMethod !== 'GET' && currentMethod !== 'HEAD')) {
599
+ currentMethod = 'GET';
600
+ currentBody = null;
601
+ delete currentHeaders['content-type'];
602
+ delete currentHeaders['content-length'];
603
+ delete currentHeaders['Content-Type'];
604
+ delete currentHeaders['Content-Length'];
605
+ }
606
+ await undiciResponse.body.arrayBuffer().catch(() => { });
607
+ redirectCount++;
608
+ continue;
609
+ }
610
+ }
611
+ const finalResponse = req.onDownloadProgress
612
+ ? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
613
+ : undiciResponse;
614
+ return new HttpResponse(finalResponse, {
615
+ timings: {},
616
+ connection: {}
203
617
  });
204
618
  }
205
- catch (error) {
206
- if (error instanceof undiciErrors.ConnectTimeoutError ||
207
- error instanceof undiciErrors.HeadersTimeoutError ||
208
- error instanceof undiciErrors.BodyTimeoutError) {
209
- throw new TimeoutError(req);
210
- }
211
- if (error.code === 'UND_ERR_CONNECT_TIMEOUT' || error.code === 'UND_ERR_HEADERS_TIMEOUT') {
212
- throw new TimeoutError(req);
213
- }
214
- throw new NetworkError(error.message, error.code, req);
619
+ }
620
+ catch (error) {
621
+ if (error instanceof undiciErrors.ConnectTimeoutError || error.code === 'UND_ERR_CONNECT_TIMEOUT') {
622
+ throw new TimeoutError(req, {
623
+ phase: 'connect',
624
+ timeout: timeouts.connectTimeout
625
+ });
626
+ }
627
+ if (error instanceof undiciErrors.HeadersTimeoutError || error.code === 'UND_ERR_HEADERS_TIMEOUT') {
628
+ throw new TimeoutError(req, {
629
+ phase: 'response',
630
+ timeout: timeouts.headersTimeout
631
+ });
632
+ }
633
+ if (error instanceof undiciErrors.BodyTimeoutError || error.code === 'UND_ERR_BODY_TIMEOUT') {
634
+ throw new TimeoutError(req, {
635
+ phase: 'send',
636
+ timeout: timeouts.bodyTimeout
637
+ });
638
+ }
639
+ if (error.name === 'AbortError' || error.code === 'ABORT_ERR') {
640
+ throw new TimeoutError(req, {
641
+ phase: 'request',
642
+ timeout: timeouts.totalTimeout
643
+ });
644
+ }
645
+ const code = error.code || error?.cause?.code;
646
+ if (code === 'UND_ERR_HEADERS_OVERFLOW') {
647
+ throw new ReckerError('Response headers exceeded the maximum allowed size', req, undefined, [
648
+ 'Reduce response header size or adjust maxHeaderSize in agent configuration.',
649
+ 'Check for runaway set-cookie or debug headers.',
650
+ 'Verify upstream is not sending excessive headers.'
651
+ ], false);
215
652
  }
653
+ throw new NetworkError(error.message, code, req);
654
+ }
655
+ finally {
656
+ if (timeoutId) {
657
+ clearTimeout(timeoutId);
658
+ }
659
+ }
660
+ }
661
+ }
662
+ function mapTlsOptions(options) {
663
+ if (!options)
664
+ return undefined;
665
+ const tls = {};
666
+ if (options.minVersion)
667
+ tls.minVersion = options.minVersion;
668
+ if (options.maxVersion)
669
+ tls.maxVersion = options.maxVersion;
670
+ if (options.ciphers)
671
+ tls.ciphers = options.ciphers;
672
+ if (options.honorCipherOrder !== undefined)
673
+ tls.honorCipherOrder = options.honorCipherOrder;
674
+ if (options.ca)
675
+ tls.ca = options.ca;
676
+ if (options.cert)
677
+ tls.cert = options.cert;
678
+ if (options.key)
679
+ tls.key = options.key;
680
+ if (options.passphrase)
681
+ tls.passphrase = options.passphrase;
682
+ if (options.rejectUnauthorized !== undefined)
683
+ tls.rejectUnauthorized = options.rejectUnauthorized;
684
+ if (options.alpnProtocols)
685
+ tls.ALPNProtocols = options.alpnProtocols;
686
+ if (options.sessionTimeout !== undefined)
687
+ tls.sessionTimeout = options.sessionTimeout;
688
+ if (options.sessionIdContext)
689
+ tls.sessionIdContext = options.sessionIdContext;
690
+ if (options.servername !== undefined) {
691
+ tls.servername = options.servername === false ? '' : options.servername;
692
+ }
693
+ return tls;
694
+ }
695
+ function determineDispatcher(params) {
696
+ if (params.explicit)
697
+ return params.explicit;
698
+ const { proxyAgent, agentManager, dnsAgent, url, bypass } = params;
699
+ const bypassProxy = shouldBypassProxy(url, bypass);
700
+ if (proxyAgent && !bypassProxy)
701
+ return proxyAgent;
702
+ if (agentManager)
703
+ return agentManager.getAgentForUrl(url);
704
+ if (dnsAgent)
705
+ return dnsAgent;
706
+ return undefined;
707
+ }
708
+ function detectProxyType(protocol) {
709
+ const p = protocol.toLowerCase().replace(':', '');
710
+ switch (p) {
711
+ case 'http':
712
+ return 'http';
713
+ case 'https':
714
+ return 'https';
715
+ case 'socks4':
716
+ return 'socks4';
717
+ case 'socks4a':
718
+ return 'socks4a';
719
+ case 'socks5':
720
+ case 'socks':
721
+ return 'socks5';
722
+ default:
723
+ return undefined;
724
+ }
725
+ }
726
+ function matchesCIDR(ip, cidr) {
727
+ const [range, bits] = cidr.split('/');
728
+ if (!bits)
729
+ return ip === range;
730
+ const mask = parseInt(bits, 10);
731
+ if (isNaN(mask))
732
+ return false;
733
+ const ipParts = ip.split('.').map(Number);
734
+ const rangeParts = range.split('.').map(Number);
735
+ if (ipParts.length !== 4 || rangeParts.length !== 4)
736
+ return false;
737
+ if (ipParts.some(isNaN) || rangeParts.some(isNaN))
738
+ return false;
739
+ const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];
740
+ const rangeNum = (rangeParts[0] << 24) | (rangeParts[1] << 16) | (rangeParts[2] << 8) | rangeParts[3];
741
+ const maskNum = ~((1 << (32 - mask)) - 1);
742
+ return (ipNum & maskNum) === (rangeNum & maskNum);
743
+ }
744
+ function shouldBypassProxy(url, bypass) {
745
+ if (!bypass || bypass.length === 0)
746
+ return false;
747
+ let hostname = '';
748
+ let port = '';
749
+ try {
750
+ const parsed = new URL(url);
751
+ hostname = parsed.hostname;
752
+ port = parsed.port;
753
+ }
754
+ catch {
755
+ return false;
756
+ }
757
+ for (const rule of bypass) {
758
+ if (rule === '*')
759
+ return true;
760
+ if (rule.includes('/')) {
761
+ if (matchesCIDR(hostname, rule))
762
+ return true;
763
+ continue;
764
+ }
765
+ if (rule.includes(':') && !rule.includes('/')) {
766
+ const [hostRule, portRule] = rule.split(':');
767
+ if (hostname === hostRule && (!portRule || port === portRule))
768
+ return true;
769
+ continue;
770
+ }
771
+ if (rule.startsWith('*.')) {
772
+ const suffix = rule.slice(1);
773
+ if (hostname.endsWith(suffix))
774
+ return true;
775
+ continue;
776
+ }
777
+ if (rule.startsWith('.')) {
778
+ if (hostname.endsWith(rule))
779
+ return true;
780
+ continue;
781
+ }
782
+ if (hostname === rule) {
783
+ return true;
784
+ }
785
+ }
786
+ return false;
787
+ }
788
+ function parseContentLength(headers) {
789
+ if (!headers)
790
+ return undefined;
791
+ if (typeof headers.get === 'function') {
792
+ const raw = headers.get('content-length');
793
+ return raw ? parseInt(raw, 10) : undefined;
794
+ }
795
+ const raw = headers['content-length'] ?? headers['Content-Length'];
796
+ if (raw === undefined)
797
+ return undefined;
798
+ const parsed = parseInt(Array.isArray(raw) ? raw[0] : raw, 10);
799
+ return Number.isFinite(parsed) ? parsed : undefined;
800
+ }
801
+ function wrapDownloadResponse(response, onProgress) {
802
+ if (!onProgress)
803
+ return response;
804
+ if (typeof Response !== 'undefined' && response instanceof Response) {
805
+ if (!response.body)
806
+ return response;
807
+ const total = parseContentLength(response.headers);
808
+ const body = createProgressStream(response.body, onProgress, {
809
+ total,
810
+ direction: 'download'
216
811
  });
812
+ return new Response(body, {
813
+ status: response.status,
814
+ statusText: response.statusText,
815
+ headers: response.headers
816
+ });
817
+ }
818
+ const total = parseContentLength(response.headers);
819
+ const nodeBody = response.body;
820
+ if (!nodeBody)
821
+ return response;
822
+ const webBody = createProgressStream(nodeToWebStream(nodeBody), onProgress, {
823
+ total,
824
+ direction: 'download'
825
+ });
826
+ return new Response(webBody, {
827
+ status: response.statusCode,
828
+ statusText: String(response.statusCode),
829
+ headers: response.headers
830
+ });
831
+ }
832
+ function wrapUploadBody(body, onProgress, total) {
833
+ if (body instanceof FormData) {
834
+ return body;
835
+ }
836
+ if (!onProgress || !body)
837
+ return body;
838
+ if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {
839
+ return createProgressStream(body, onProgress, { total, direction: 'upload' });
840
+ }
841
+ if (typeof Blob !== 'undefined' && body instanceof Blob) {
842
+ return createProgressStream(body.stream(), onProgress, { total: body.size, direction: 'upload' });
843
+ }
844
+ if (isNodeReadable(body)) {
845
+ const webStream = nodeToWebStream(body);
846
+ return createProgressStream(webStream, onProgress, { total, direction: 'upload' });
217
847
  }
848
+ if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
849
+ const view = body instanceof ArrayBuffer
850
+ ? new Uint8Array(body)
851
+ : new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
852
+ return bufferToProgressStream(view, onProgress);
853
+ }
854
+ if (typeof body === 'string') {
855
+ const encoder = new TextEncoder();
856
+ const view = encoder.encode(body);
857
+ return bufferToProgressStream(view, onProgress);
858
+ }
859
+ return body;
860
+ }
861
+ function bufferToProgressStream(buffer, onProgress, chunkSize = 64 * 1024) {
862
+ let offset = 0;
863
+ const total = buffer.byteLength;
864
+ const stream = new ReadableStream({
865
+ pull(controller) {
866
+ if (offset >= total) {
867
+ controller.close();
868
+ return;
869
+ }
870
+ const end = Math.min(offset + chunkSize, total);
871
+ controller.enqueue(buffer.slice(offset, end));
872
+ offset = end;
873
+ }
874
+ });
875
+ return createProgressStream(stream, onProgress, { total, direction: 'upload' });
876
+ }
877
+ function isNodeReadable(obj) {
878
+ return obj && typeof obj.pipe === 'function' && typeof obj.on === 'function';
218
879
  }