deepv-code 1.0.182

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 (223) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +3 -0
  3. package/bundle/assets/help/README.md +113 -0
  4. package/bundle/assets/help/cli-help-knowledge.md +1382 -0
  5. package/bundle/assets/sounds/README.md +74 -0
  6. package/bundle/assets/sounds/confirmation-required.wav +0 -0
  7. package/bundle/assets/sounds/response-complete.wav +0 -0
  8. package/bundle/assets/sounds/selection-made.wav +0 -0
  9. package/bundle/dvcode.js +4442 -0
  10. package/bundle/fix-binary-permissions.js +215 -0
  11. package/bundle/login/templates/authSelectPage.html +870 -0
  12. package/bundle/login/templates/deepv.ico +0 -0
  13. package/bundle/login/templates/feishu.ico +0 -0
  14. package/bundle/node_modules/@vscode/ripgrep/bin/darwin-arm64-rg +0 -0
  15. package/bundle/node_modules/@vscode/ripgrep/bin/darwin-x64-rg +0 -0
  16. package/bundle/node_modules/@vscode/ripgrep/bin/linux-arm-rg +0 -0
  17. package/bundle/node_modules/@vscode/ripgrep/bin/linux-arm64-rg +0 -0
  18. package/bundle/node_modules/@vscode/ripgrep/bin/linux-x64-rg +0 -0
  19. package/bundle/node_modules/@vscode/ripgrep/bin/rg.exe +0 -0
  20. package/bundle/node_modules/@vscode/ripgrep/bin/win32-arm64-rg.exe +0 -0
  21. package/bundle/node_modules/@vscode/ripgrep/bin/win32-ia32-rg.exe +0 -0
  22. package/bundle/node_modules/@vscode/ripgrep/bin/win32-x64-rg.exe +0 -0
  23. package/bundle/node_modules/@vscode/ripgrep/lib/download.js +357 -0
  24. package/bundle/node_modules/@vscode/ripgrep/lib/index.d.ts +1 -0
  25. package/bundle/node_modules/@vscode/ripgrep/lib/index.js +42 -0
  26. package/bundle/node_modules/@vscode/ripgrep/lib/postinstall.js +121 -0
  27. package/bundle/node_modules/@vscode/ripgrep/package.json +24 -0
  28. package/bundle/node_modules/undici/LICENSE +21 -0
  29. package/bundle/node_modules/undici/README.md +472 -0
  30. package/bundle/node_modules/undici/docs/docs/api/Agent.md +83 -0
  31. package/bundle/node_modules/undici/docs/docs/api/BalancedPool.md +99 -0
  32. package/bundle/node_modules/undici/docs/docs/api/CacheStorage.md +30 -0
  33. package/bundle/node_modules/undici/docs/docs/api/CacheStore.md +151 -0
  34. package/bundle/node_modules/undici/docs/docs/api/Client.md +281 -0
  35. package/bundle/node_modules/undici/docs/docs/api/ClientStats.md +27 -0
  36. package/bundle/node_modules/undici/docs/docs/api/Connector.md +115 -0
  37. package/bundle/node_modules/undici/docs/docs/api/ContentType.md +57 -0
  38. package/bundle/node_modules/undici/docs/docs/api/Cookies.md +101 -0
  39. package/bundle/node_modules/undici/docs/docs/api/Debug.md +62 -0
  40. package/bundle/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +204 -0
  41. package/bundle/node_modules/undici/docs/docs/api/Dispatcher.md +1200 -0
  42. package/bundle/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +159 -0
  43. package/bundle/node_modules/undici/docs/docs/api/Errors.md +49 -0
  44. package/bundle/node_modules/undici/docs/docs/api/EventSource.md +45 -0
  45. package/bundle/node_modules/undici/docs/docs/api/Fetch.md +52 -0
  46. package/bundle/node_modules/undici/docs/docs/api/H2CClient.md +262 -0
  47. package/bundle/node_modules/undici/docs/docs/api/MockAgent.md +603 -0
  48. package/bundle/node_modules/undici/docs/docs/api/MockCallHistory.md +197 -0
  49. package/bundle/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +43 -0
  50. package/bundle/node_modules/undici/docs/docs/api/MockClient.md +77 -0
  51. package/bundle/node_modules/undici/docs/docs/api/MockErrors.md +12 -0
  52. package/bundle/node_modules/undici/docs/docs/api/MockPool.md +548 -0
  53. package/bundle/node_modules/undici/docs/docs/api/Pool.md +84 -0
  54. package/bundle/node_modules/undici/docs/docs/api/PoolStats.md +35 -0
  55. package/bundle/node_modules/undici/docs/docs/api/ProxyAgent.md +227 -0
  56. package/bundle/node_modules/undici/docs/docs/api/RedirectHandler.md +96 -0
  57. package/bundle/node_modules/undici/docs/docs/api/RetryAgent.md +45 -0
  58. package/bundle/node_modules/undici/docs/docs/api/RetryHandler.md +117 -0
  59. package/bundle/node_modules/undici/docs/docs/api/Util.md +25 -0
  60. package/bundle/node_modules/undici/docs/docs/api/WebSocket.md +85 -0
  61. package/bundle/node_modules/undici/docs/docs/api/api-lifecycle.md +91 -0
  62. package/bundle/node_modules/undici/docs/docs/best-practices/client-certificate.md +64 -0
  63. package/bundle/node_modules/undici/docs/docs/best-practices/mocking-request.md +190 -0
  64. package/bundle/node_modules/undici/docs/docs/best-practices/proxy.md +127 -0
  65. package/bundle/node_modules/undici/docs/docs/best-practices/writing-tests.md +20 -0
  66. package/bundle/node_modules/undici/index-fetch.js +35 -0
  67. package/bundle/node_modules/undici/index.d.ts +3 -0
  68. package/bundle/node_modules/undici/index.js +183 -0
  69. package/bundle/node_modules/undici/lib/api/abort-signal.js +59 -0
  70. package/bundle/node_modules/undici/lib/api/api-connect.js +110 -0
  71. package/bundle/node_modules/undici/lib/api/api-pipeline.js +252 -0
  72. package/bundle/node_modules/undici/lib/api/api-request.js +199 -0
  73. package/bundle/node_modules/undici/lib/api/api-stream.js +209 -0
  74. package/bundle/node_modules/undici/lib/api/api-upgrade.js +110 -0
  75. package/bundle/node_modules/undici/lib/api/index.js +7 -0
  76. package/bundle/node_modules/undici/lib/api/readable.js +558 -0
  77. package/bundle/node_modules/undici/lib/api/util.js +95 -0
  78. package/bundle/node_modules/undici/lib/cache/memory-cache-store.js +234 -0
  79. package/bundle/node_modules/undici/lib/cache/sqlite-cache-store.js +461 -0
  80. package/bundle/node_modules/undici/lib/core/connect.js +164 -0
  81. package/bundle/node_modules/undici/lib/core/constants.js +143 -0
  82. package/bundle/node_modules/undici/lib/core/diagnostics.js +196 -0
  83. package/bundle/node_modules/undici/lib/core/errors.js +244 -0
  84. package/bundle/node_modules/undici/lib/core/request.js +397 -0
  85. package/bundle/node_modules/undici/lib/core/symbols.js +68 -0
  86. package/bundle/node_modules/undici/lib/core/tree.js +160 -0
  87. package/bundle/node_modules/undici/lib/core/util.js +988 -0
  88. package/bundle/node_modules/undici/lib/dispatcher/agent.js +135 -0
  89. package/bundle/node_modules/undici/lib/dispatcher/balanced-pool.js +206 -0
  90. package/bundle/node_modules/undici/lib/dispatcher/client-h1.js +1615 -0
  91. package/bundle/node_modules/undici/lib/dispatcher/client-h2.js +798 -0
  92. package/bundle/node_modules/undici/lib/dispatcher/client.js +614 -0
  93. package/bundle/node_modules/undici/lib/dispatcher/dispatcher-base.js +161 -0
  94. package/bundle/node_modules/undici/lib/dispatcher/dispatcher.js +48 -0
  95. package/bundle/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +151 -0
  96. package/bundle/node_modules/undici/lib/dispatcher/fixed-queue.js +159 -0
  97. package/bundle/node_modules/undici/lib/dispatcher/h2c-client.js +122 -0
  98. package/bundle/node_modules/undici/lib/dispatcher/pool-base.js +191 -0
  99. package/bundle/node_modules/undici/lib/dispatcher/pool.js +118 -0
  100. package/bundle/node_modules/undici/lib/dispatcher/proxy-agent.js +275 -0
  101. package/bundle/node_modules/undici/lib/dispatcher/retry-agent.js +35 -0
  102. package/bundle/node_modules/undici/lib/global.js +32 -0
  103. package/bundle/node_modules/undici/lib/handler/cache-handler.js +448 -0
  104. package/bundle/node_modules/undici/lib/handler/cache-revalidation-handler.js +124 -0
  105. package/bundle/node_modules/undici/lib/handler/decorator-handler.js +67 -0
  106. package/bundle/node_modules/undici/lib/handler/redirect-handler.js +227 -0
  107. package/bundle/node_modules/undici/lib/handler/retry-handler.js +342 -0
  108. package/bundle/node_modules/undici/lib/handler/unwrap-handler.js +96 -0
  109. package/bundle/node_modules/undici/lib/handler/wrap-handler.js +95 -0
  110. package/bundle/node_modules/undici/lib/interceptor/cache.js +372 -0
  111. package/bundle/node_modules/undici/lib/interceptor/dns.js +432 -0
  112. package/bundle/node_modules/undici/lib/interceptor/dump.js +111 -0
  113. package/bundle/node_modules/undici/lib/interceptor/redirect.js +21 -0
  114. package/bundle/node_modules/undici/lib/interceptor/response-error.js +95 -0
  115. package/bundle/node_modules/undici/lib/interceptor/retry.js +19 -0
  116. package/bundle/node_modules/undici/lib/llhttp/.gitkeep +0 -0
  117. package/bundle/node_modules/undici/lib/llhttp/constants.d.ts +97 -0
  118. package/bundle/node_modules/undici/lib/llhttp/constants.js +498 -0
  119. package/bundle/node_modules/undici/lib/llhttp/constants.js.map +1 -0
  120. package/bundle/node_modules/undici/lib/llhttp/llhttp-wasm.js +15 -0
  121. package/bundle/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +15 -0
  122. package/bundle/node_modules/undici/lib/llhttp/utils.d.ts +2 -0
  123. package/bundle/node_modules/undici/lib/llhttp/utils.js +15 -0
  124. package/bundle/node_modules/undici/lib/llhttp/utils.js.map +1 -0
  125. package/bundle/node_modules/undici/lib/mock/mock-agent.js +224 -0
  126. package/bundle/node_modules/undici/lib/mock/mock-call-history.js +248 -0
  127. package/bundle/node_modules/undici/lib/mock/mock-client.js +64 -0
  128. package/bundle/node_modules/undici/lib/mock/mock-errors.js +19 -0
  129. package/bundle/node_modules/undici/lib/mock/mock-interceptor.js +209 -0
  130. package/bundle/node_modules/undici/lib/mock/mock-pool.js +64 -0
  131. package/bundle/node_modules/undici/lib/mock/mock-symbols.js +31 -0
  132. package/bundle/node_modules/undici/lib/mock/mock-utils.js +433 -0
  133. package/bundle/node_modules/undici/lib/mock/pending-interceptors-formatter.js +43 -0
  134. package/bundle/node_modules/undici/lib/util/cache.js +368 -0
  135. package/bundle/node_modules/undici/lib/util/date.js +259 -0
  136. package/bundle/node_modules/undici/lib/util/stats.js +32 -0
  137. package/bundle/node_modules/undici/lib/util/timers.js +423 -0
  138. package/bundle/node_modules/undici/lib/web/cache/cache.js +862 -0
  139. package/bundle/node_modules/undici/lib/web/cache/cachestorage.js +152 -0
  140. package/bundle/node_modules/undici/lib/web/cache/util.js +45 -0
  141. package/bundle/node_modules/undici/lib/web/cookies/constants.js +12 -0
  142. package/bundle/node_modules/undici/lib/web/cookies/index.js +199 -0
  143. package/bundle/node_modules/undici/lib/web/cookies/parse.js +322 -0
  144. package/bundle/node_modules/undici/lib/web/cookies/util.js +282 -0
  145. package/bundle/node_modules/undici/lib/web/eventsource/eventsource-stream.js +399 -0
  146. package/bundle/node_modules/undici/lib/web/eventsource/eventsource.js +484 -0
  147. package/bundle/node_modules/undici/lib/web/eventsource/util.js +37 -0
  148. package/bundle/node_modules/undici/lib/web/fetch/LICENSE +21 -0
  149. package/bundle/node_modules/undici/lib/web/fetch/body.js +532 -0
  150. package/bundle/node_modules/undici/lib/web/fetch/constants.js +131 -0
  151. package/bundle/node_modules/undici/lib/web/fetch/data-url.js +744 -0
  152. package/bundle/node_modules/undici/lib/web/fetch/dispatcher-weakref.js +46 -0
  153. package/bundle/node_modules/undici/lib/web/fetch/formdata-parser.js +501 -0
  154. package/bundle/node_modules/undici/lib/web/fetch/formdata.js +263 -0
  155. package/bundle/node_modules/undici/lib/web/fetch/global.js +40 -0
  156. package/bundle/node_modules/undici/lib/web/fetch/headers.js +719 -0
  157. package/bundle/node_modules/undici/lib/web/fetch/index.js +2258 -0
  158. package/bundle/node_modules/undici/lib/web/fetch/request.js +1099 -0
  159. package/bundle/node_modules/undici/lib/web/fetch/response.js +636 -0
  160. package/bundle/node_modules/undici/lib/web/fetch/util.js +1782 -0
  161. package/bundle/node_modules/undici/lib/web/fetch/webidl.js +740 -0
  162. package/bundle/node_modules/undici/lib/web/websocket/connection.js +325 -0
  163. package/bundle/node_modules/undici/lib/web/websocket/constants.js +126 -0
  164. package/bundle/node_modules/undici/lib/web/websocket/events.js +331 -0
  165. package/bundle/node_modules/undici/lib/web/websocket/frame.js +138 -0
  166. package/bundle/node_modules/undici/lib/web/websocket/permessage-deflate.js +70 -0
  167. package/bundle/node_modules/undici/lib/web/websocket/receiver.js +454 -0
  168. package/bundle/node_modules/undici/lib/web/websocket/sender.js +109 -0
  169. package/bundle/node_modules/undici/lib/web/websocket/stream/websocketerror.js +83 -0
  170. package/bundle/node_modules/undici/lib/web/websocket/stream/websocketstream.js +485 -0
  171. package/bundle/node_modules/undici/lib/web/websocket/util.js +338 -0
  172. package/bundle/node_modules/undici/lib/web/websocket/websocket.js +686 -0
  173. package/bundle/node_modules/undici/package.json +149 -0
  174. package/bundle/node_modules/undici/scripts/strip-comments.js +10 -0
  175. package/bundle/node_modules/undici/types/README.md +6 -0
  176. package/bundle/node_modules/undici/types/agent.d.ts +35 -0
  177. package/bundle/node_modules/undici/types/api.d.ts +43 -0
  178. package/bundle/node_modules/undici/types/balanced-pool.d.ts +29 -0
  179. package/bundle/node_modules/undici/types/cache-interceptor.d.ts +172 -0
  180. package/bundle/node_modules/undici/types/cache.d.ts +36 -0
  181. package/bundle/node_modules/undici/types/client-stats.d.ts +15 -0
  182. package/bundle/node_modules/undici/types/client.d.ts +110 -0
  183. package/bundle/node_modules/undici/types/connector.d.ts +34 -0
  184. package/bundle/node_modules/undici/types/content-type.d.ts +21 -0
  185. package/bundle/node_modules/undici/types/cookies.d.ts +30 -0
  186. package/bundle/node_modules/undici/types/diagnostics-channel.d.ts +66 -0
  187. package/bundle/node_modules/undici/types/dispatcher.d.ts +281 -0
  188. package/bundle/node_modules/undici/types/env-http-proxy-agent.d.ts +21 -0
  189. package/bundle/node_modules/undici/types/errors.d.ts +171 -0
  190. package/bundle/node_modules/undici/types/eventsource.d.ts +61 -0
  191. package/bundle/node_modules/undici/types/fetch.d.ts +210 -0
  192. package/bundle/node_modules/undici/types/formdata.d.ts +108 -0
  193. package/bundle/node_modules/undici/types/global-dispatcher.d.ts +9 -0
  194. package/bundle/node_modules/undici/types/global-origin.d.ts +7 -0
  195. package/bundle/node_modules/undici/types/h2c-client.d.ts +75 -0
  196. package/bundle/node_modules/undici/types/handlers.d.ts +15 -0
  197. package/bundle/node_modules/undici/types/header.d.ts +160 -0
  198. package/bundle/node_modules/undici/types/index.d.ts +75 -0
  199. package/bundle/node_modules/undici/types/interceptors.d.ts +34 -0
  200. package/bundle/node_modules/undici/types/mock-agent.d.ts +68 -0
  201. package/bundle/node_modules/undici/types/mock-call-history.d.ts +111 -0
  202. package/bundle/node_modules/undici/types/mock-client.d.ts +25 -0
  203. package/bundle/node_modules/undici/types/mock-errors.d.ts +12 -0
  204. package/bundle/node_modules/undici/types/mock-interceptor.d.ts +93 -0
  205. package/bundle/node_modules/undici/types/mock-pool.d.ts +25 -0
  206. package/bundle/node_modules/undici/types/patch.d.ts +29 -0
  207. package/bundle/node_modules/undici/types/pool-stats.d.ts +19 -0
  208. package/bundle/node_modules/undici/types/pool.d.ts +41 -0
  209. package/bundle/node_modules/undici/types/proxy-agent.d.ts +29 -0
  210. package/bundle/node_modules/undici/types/readable.d.ts +68 -0
  211. package/bundle/node_modules/undici/types/retry-agent.d.ts +8 -0
  212. package/bundle/node_modules/undici/types/retry-handler.d.ts +116 -0
  213. package/bundle/node_modules/undici/types/util.d.ts +18 -0
  214. package/bundle/node_modules/undici/types/utility.d.ts +7 -0
  215. package/bundle/node_modules/undici/types/webidl.d.ts +266 -0
  216. package/bundle/node_modules/undici/types/websocket.d.ts +184 -0
  217. package/bundle/sandbox-macos-permissive-closed.sb +26 -0
  218. package/bundle/sandbox-macos-permissive-open.sb +19 -0
  219. package/bundle/sandbox-macos-permissive-proxied.sb +31 -0
  220. package/bundle/sandbox-macos-restrictive-closed.sb +87 -0
  221. package/bundle/sandbox-macos-restrictive-open.sb +90 -0
  222. package/bundle/sandbox-macos-restrictive-proxied.sb +92 -0
  223. package/package.json +137 -0
@@ -0,0 +1,227 @@
1
+ 'use strict'
2
+
3
+ const util = require('../core/util')
4
+ const { kBodyUsed } = require('../core/symbols')
5
+ const assert = require('node:assert')
6
+ const { InvalidArgumentError } = require('../core/errors')
7
+ const EE = require('node:events')
8
+
9
+ const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
10
+
11
+ const kBody = Symbol('body')
12
+
13
+ const noop = () => {}
14
+
15
+ class BodyAsyncIterable {
16
+ constructor (body) {
17
+ this[kBody] = body
18
+ this[kBodyUsed] = false
19
+ }
20
+
21
+ async * [Symbol.asyncIterator] () {
22
+ assert(!this[kBodyUsed], 'disturbed')
23
+ this[kBodyUsed] = true
24
+ yield * this[kBody]
25
+ }
26
+ }
27
+
28
+ class RedirectHandler {
29
+ static buildDispatch (dispatcher, maxRedirections) {
30
+ if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
31
+ throw new InvalidArgumentError('maxRedirections must be a positive number')
32
+ }
33
+
34
+ const dispatch = dispatcher.dispatch.bind(dispatcher)
35
+ return (opts, originalHandler) => dispatch(opts, new RedirectHandler(dispatch, maxRedirections, opts, originalHandler))
36
+ }
37
+
38
+ constructor (dispatch, maxRedirections, opts, handler) {
39
+ if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
40
+ throw new InvalidArgumentError('maxRedirections must be a positive number')
41
+ }
42
+
43
+ this.dispatch = dispatch
44
+ this.location = null
45
+ this.opts = { ...opts, maxRedirections: 0 } // opts must be a copy
46
+ this.maxRedirections = maxRedirections
47
+ this.handler = handler
48
+ this.history = []
49
+
50
+ if (util.isStream(this.opts.body)) {
51
+ // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
52
+ // so that it can be dispatched again?
53
+ // TODO (fix): Do we need 100-expect support to provide a way to do this properly?
54
+ if (util.bodyLength(this.opts.body) === 0) {
55
+ this.opts.body
56
+ .on('data', function () {
57
+ assert(false)
58
+ })
59
+ }
60
+
61
+ if (typeof this.opts.body.readableDidRead !== 'boolean') {
62
+ this.opts.body[kBodyUsed] = false
63
+ EE.prototype.on.call(this.opts.body, 'data', function () {
64
+ this[kBodyUsed] = true
65
+ })
66
+ }
67
+ } else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') {
68
+ // TODO (fix): We can't access ReadableStream internal state
69
+ // to determine whether or not it has been disturbed. This is just
70
+ // a workaround.
71
+ this.opts.body = new BodyAsyncIterable(this.opts.body)
72
+ } else if (
73
+ this.opts.body &&
74
+ typeof this.opts.body !== 'string' &&
75
+ !ArrayBuffer.isView(this.opts.body) &&
76
+ util.isIterable(this.opts.body) &&
77
+ !util.isFormDataLike(this.opts.body)
78
+ ) {
79
+ // TODO: Should we allow re-using iterable if !this.opts.idempotent
80
+ // or through some other flag?
81
+ this.opts.body = new BodyAsyncIterable(this.opts.body)
82
+ }
83
+ }
84
+
85
+ onRequestStart (controller, context) {
86
+ this.handler.onRequestStart?.(controller, { ...context, history: this.history })
87
+ }
88
+
89
+ onRequestUpgrade (controller, statusCode, headers, socket) {
90
+ this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
91
+ }
92
+
93
+ onResponseStart (controller, statusCode, headers, statusMessage) {
94
+ if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) {
95
+ throw new Error('max redirects')
96
+ }
97
+
98
+ // https://tools.ietf.org/html/rfc7231#section-6.4.2
99
+ // https://fetch.spec.whatwg.org/#http-redirect-fetch
100
+ // In case of HTTP 301 or 302 with POST, change the method to GET
101
+ if ((statusCode === 301 || statusCode === 302) && this.opts.method === 'POST') {
102
+ this.opts.method = 'GET'
103
+ if (util.isStream(this.opts.body)) {
104
+ util.destroy(this.opts.body.on('error', noop))
105
+ }
106
+ this.opts.body = null
107
+ }
108
+
109
+ // https://tools.ietf.org/html/rfc7231#section-6.4.4
110
+ // In case of HTTP 303, always replace method to be either HEAD or GET
111
+ if (statusCode === 303 && this.opts.method !== 'HEAD') {
112
+ this.opts.method = 'GET'
113
+ if (util.isStream(this.opts.body)) {
114
+ util.destroy(this.opts.body.on('error', noop))
115
+ }
116
+ this.opts.body = null
117
+ }
118
+
119
+ this.location = this.history.length >= this.maxRedirections || util.isDisturbed(this.opts.body) || redirectableStatusCodes.indexOf(statusCode) === -1
120
+ ? null
121
+ : headers.location
122
+
123
+ if (this.opts.origin) {
124
+ this.history.push(new URL(this.opts.path, this.opts.origin))
125
+ }
126
+
127
+ if (!this.location) {
128
+ this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)
129
+ return
130
+ }
131
+
132
+ const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)))
133
+ const path = search ? `${pathname}${search}` : pathname
134
+
135
+ // Remove headers referring to the original URL.
136
+ // By default it is Host only, unless it's a 303 (see below), which removes also all Content-* headers.
137
+ // https://tools.ietf.org/html/rfc7231#section-6.4
138
+ this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin)
139
+ this.opts.path = path
140
+ this.opts.origin = origin
141
+ this.opts.maxRedirections = 0
142
+ this.opts.query = null
143
+ }
144
+
145
+ onResponseData (controller, chunk) {
146
+ if (this.location) {
147
+ /*
148
+ https://tools.ietf.org/html/rfc7231#section-6.4
149
+
150
+ TLDR: undici always ignores 3xx response bodies.
151
+
152
+ Redirection is used to serve the requested resource from another URL, so it assumes that
153
+ no body is generated (and thus can be ignored). Even though generating a body is not prohibited.
154
+
155
+ For status 301, 302, 303, 307 and 308 (the latter from RFC 7238), the specs mention that the body usually
156
+ (which means it's optional and not mandated) contain just an hyperlink to the value of
157
+ the Location response header, so the body can be ignored safely.
158
+
159
+ For status 300, which is "Multiple Choices", the spec mentions both generating a Location
160
+ response header AND a response body with the other possible location to follow.
161
+ Since the spec explicitly chooses not to specify a format for such body and leave it to
162
+ servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it.
163
+ */
164
+ } else {
165
+ this.handler.onResponseData?.(controller, chunk)
166
+ }
167
+ }
168
+
169
+ onResponseEnd (controller, trailers) {
170
+ if (this.location) {
171
+ /*
172
+ https://tools.ietf.org/html/rfc7231#section-6.4
173
+
174
+ TLDR: undici always ignores 3xx response trailers as they are not expected in case of redirections
175
+ and neither are useful if present.
176
+
177
+ See comment on onData method above for more detailed information.
178
+ */
179
+ this.dispatch(this.opts, this)
180
+ } else {
181
+ this.handler.onResponseEnd(controller, trailers)
182
+ }
183
+ }
184
+
185
+ onResponseError (controller, error) {
186
+ this.handler.onResponseError?.(controller, error)
187
+ }
188
+ }
189
+
190
+ // https://tools.ietf.org/html/rfc7231#section-6.4.4
191
+ function shouldRemoveHeader (header, removeContent, unknownOrigin) {
192
+ if (header.length === 4) {
193
+ return util.headerNameToString(header) === 'host'
194
+ }
195
+ if (removeContent && util.headerNameToString(header).startsWith('content-')) {
196
+ return true
197
+ }
198
+ if (unknownOrigin && (header.length === 13 || header.length === 6 || header.length === 19)) {
199
+ const name = util.headerNameToString(header)
200
+ return name === 'authorization' || name === 'cookie' || name === 'proxy-authorization'
201
+ }
202
+ return false
203
+ }
204
+
205
+ // https://tools.ietf.org/html/rfc7231#section-6.4
206
+ function cleanRequestHeaders (headers, removeContent, unknownOrigin) {
207
+ const ret = []
208
+ if (Array.isArray(headers)) {
209
+ for (let i = 0; i < headers.length; i += 2) {
210
+ if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin)) {
211
+ ret.push(headers[i], headers[i + 1])
212
+ }
213
+ }
214
+ } else if (headers && typeof headers === 'object') {
215
+ const entries = typeof headers[Symbol.iterator] === 'function' ? headers : Object.entries(headers)
216
+ for (const [key, value] of entries) {
217
+ if (!shouldRemoveHeader(key, removeContent, unknownOrigin)) {
218
+ ret.push(key, value)
219
+ }
220
+ }
221
+ } else {
222
+ assert(headers == null, 'headers must be an object or an array')
223
+ }
224
+ return ret
225
+ }
226
+
227
+ module.exports = RedirectHandler
@@ -0,0 +1,342 @@
1
+ 'use strict'
2
+ const assert = require('node:assert')
3
+
4
+ const { kRetryHandlerDefaultRetry } = require('../core/symbols')
5
+ const { RequestRetryError } = require('../core/errors')
6
+ const WrapHandler = require('./wrap-handler')
7
+ const {
8
+ isDisturbed,
9
+ parseRangeHeader,
10
+ wrapRequestBody
11
+ } = require('../core/util')
12
+
13
+ function calculateRetryAfterHeader (retryAfter) {
14
+ const retryTime = new Date(retryAfter).getTime()
15
+ return isNaN(retryTime) ? 0 : retryTime - Date.now()
16
+ }
17
+
18
+ class RetryHandler {
19
+ constructor (opts, { dispatch, handler }) {
20
+ const { retryOptions, ...dispatchOpts } = opts
21
+ const {
22
+ // Retry scoped
23
+ retry: retryFn,
24
+ maxRetries,
25
+ maxTimeout,
26
+ minTimeout,
27
+ timeoutFactor,
28
+ // Response scoped
29
+ methods,
30
+ errorCodes,
31
+ retryAfter,
32
+ statusCodes
33
+ } = retryOptions ?? {}
34
+
35
+ this.dispatch = dispatch
36
+ this.handler = WrapHandler.wrap(handler)
37
+ this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }
38
+ this.retryOpts = {
39
+ retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],
40
+ retryAfter: retryAfter ?? true,
41
+ maxTimeout: maxTimeout ?? 30 * 1000, // 30s,
42
+ minTimeout: minTimeout ?? 500, // .5s
43
+ timeoutFactor: timeoutFactor ?? 2,
44
+ maxRetries: maxRetries ?? 5,
45
+ // What errors we should retry
46
+ methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],
47
+ // Indicates which errors to retry
48
+ statusCodes: statusCodes ?? [500, 502, 503, 504, 429],
49
+ // List of errors to retry
50
+ errorCodes: errorCodes ?? [
51
+ 'ECONNRESET',
52
+ 'ECONNREFUSED',
53
+ 'ENOTFOUND',
54
+ 'ENETDOWN',
55
+ 'ENETUNREACH',
56
+ 'EHOSTDOWN',
57
+ 'EHOSTUNREACH',
58
+ 'EPIPE',
59
+ 'UND_ERR_SOCKET'
60
+ ]
61
+ }
62
+
63
+ this.retryCount = 0
64
+ this.retryCountCheckpoint = 0
65
+ this.headersSent = false
66
+ this.start = 0
67
+ this.end = null
68
+ this.etag = null
69
+ }
70
+
71
+ onRequestStart (controller, context) {
72
+ if (!this.headersSent) {
73
+ this.handler.onRequestStart?.(controller, context)
74
+ }
75
+ }
76
+
77
+ onRequestUpgrade (controller, statusCode, headers, socket) {
78
+ this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
79
+ }
80
+
81
+ static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) {
82
+ const { statusCode, code, headers } = err
83
+ const { method, retryOptions } = opts
84
+ const {
85
+ maxRetries,
86
+ minTimeout,
87
+ maxTimeout,
88
+ timeoutFactor,
89
+ statusCodes,
90
+ errorCodes,
91
+ methods
92
+ } = retryOptions
93
+ const { counter } = state
94
+
95
+ // Any code that is not a Undici's originated and allowed to retry
96
+ if (code && code !== 'UND_ERR_REQ_RETRY' && !errorCodes.includes(code)) {
97
+ cb(err)
98
+ return
99
+ }
100
+
101
+ // If a set of method are provided and the current method is not in the list
102
+ if (Array.isArray(methods) && !methods.includes(method)) {
103
+ cb(err)
104
+ return
105
+ }
106
+
107
+ // If a set of status code are provided and the current status code is not in the list
108
+ if (
109
+ statusCode != null &&
110
+ Array.isArray(statusCodes) &&
111
+ !statusCodes.includes(statusCode)
112
+ ) {
113
+ cb(err)
114
+ return
115
+ }
116
+
117
+ // If we reached the max number of retries
118
+ if (counter > maxRetries) {
119
+ cb(err)
120
+ return
121
+ }
122
+
123
+ let retryAfterHeader = headers?.['retry-after']
124
+ if (retryAfterHeader) {
125
+ retryAfterHeader = Number(retryAfterHeader)
126
+ retryAfterHeader = Number.isNaN(retryAfterHeader)
127
+ ? calculateRetryAfterHeader(headers['retry-after'])
128
+ : retryAfterHeader * 1e3 // Retry-After is in seconds
129
+ }
130
+
131
+ const retryTimeout =
132
+ retryAfterHeader > 0
133
+ ? Math.min(retryAfterHeader, maxTimeout)
134
+ : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)
135
+
136
+ setTimeout(() => cb(null), retryTimeout)
137
+ }
138
+
139
+ onResponseStart (controller, statusCode, headers, statusMessage) {
140
+ this.retryCount += 1
141
+
142
+ if (statusCode >= 300) {
143
+ if (this.retryOpts.statusCodes.includes(statusCode) === false) {
144
+ this.headersSent = true
145
+ this.handler.onResponseStart?.(
146
+ controller,
147
+ statusCode,
148
+ headers,
149
+ statusMessage
150
+ )
151
+ return
152
+ } else {
153
+ throw new RequestRetryError('Request failed', statusCode, {
154
+ headers,
155
+ data: {
156
+ count: this.retryCount
157
+ }
158
+ })
159
+ }
160
+ }
161
+
162
+ // Checkpoint for resume from where we left it
163
+ if (this.headersSent) {
164
+ // Only Partial Content 206 supposed to provide Content-Range,
165
+ // any other status code that partially consumed the payload
166
+ // should not be retried because it would result in downstream
167
+ // wrongly concatenate multiple responses.
168
+ if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {
169
+ throw new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
170
+ headers,
171
+ data: { count: this.retryCount }
172
+ })
173
+ }
174
+
175
+ const contentRange = parseRangeHeader(headers['content-range'])
176
+ // If no content range
177
+ if (!contentRange) {
178
+ throw new RequestRetryError('Content-Range mismatch', statusCode, {
179
+ headers,
180
+ data: { count: this.retryCount }
181
+ })
182
+ }
183
+
184
+ // Let's start with a weak etag check
185
+ if (this.etag != null && this.etag !== headers.etag) {
186
+ throw new RequestRetryError('ETag mismatch', statusCode, {
187
+ headers,
188
+ data: { count: this.retryCount }
189
+ })
190
+ }
191
+
192
+ const { start, size, end = size ? size - 1 : null } = contentRange
193
+
194
+ assert(this.start === start, 'content-range mismatch')
195
+ assert(this.end == null || this.end === end, 'content-range mismatch')
196
+
197
+ return
198
+ }
199
+
200
+ if (this.end == null) {
201
+ if (statusCode === 206) {
202
+ // First time we receive 206
203
+ const range = parseRangeHeader(headers['content-range'])
204
+
205
+ if (range == null) {
206
+ this.headersSent = true
207
+ this.handler.onResponseStart?.(
208
+ controller,
209
+ statusCode,
210
+ headers,
211
+ statusMessage
212
+ )
213
+ return
214
+ }
215
+
216
+ const { start, size, end = size ? size - 1 : null } = range
217
+ assert(
218
+ start != null && Number.isFinite(start),
219
+ 'content-range mismatch'
220
+ )
221
+ assert(end != null && Number.isFinite(end), 'invalid content-length')
222
+
223
+ this.start = start
224
+ this.end = end
225
+ }
226
+
227
+ // We make our best to checkpoint the body for further range headers
228
+ if (this.end == null) {
229
+ const contentLength = headers['content-length']
230
+ this.end = contentLength != null ? Number(contentLength) - 1 : null
231
+ }
232
+
233
+ assert(Number.isFinite(this.start))
234
+ assert(
235
+ this.end == null || Number.isFinite(this.end),
236
+ 'invalid content-length'
237
+ )
238
+
239
+ this.resume = true
240
+ this.etag = headers.etag != null ? headers.etag : null
241
+
242
+ // Weak etags are not useful for comparison nor cache
243
+ // for instance not safe to assume if the response is byte-per-byte
244
+ // equal
245
+ if (
246
+ this.etag != null &&
247
+ this.etag[0] === 'W' &&
248
+ this.etag[1] === '/'
249
+ ) {
250
+ this.etag = null
251
+ }
252
+
253
+ this.headersSent = true
254
+ this.handler.onResponseStart?.(
255
+ controller,
256
+ statusCode,
257
+ headers,
258
+ statusMessage
259
+ )
260
+ } else {
261
+ throw new RequestRetryError('Request failed', statusCode, {
262
+ headers,
263
+ data: { count: this.retryCount }
264
+ })
265
+ }
266
+ }
267
+
268
+ onResponseData (controller, chunk) {
269
+ this.start += chunk.length
270
+
271
+ this.handler.onResponseData?.(controller, chunk)
272
+ }
273
+
274
+ onResponseEnd (controller, trailers) {
275
+ this.retryCount = 0
276
+ return this.handler.onResponseEnd?.(controller, trailers)
277
+ }
278
+
279
+ onResponseError (controller, err) {
280
+ if (controller?.aborted || isDisturbed(this.opts.body)) {
281
+ this.handler.onResponseError?.(controller, err)
282
+ return
283
+ }
284
+
285
+ // We reconcile in case of a mix between network errors
286
+ // and server error response
287
+ if (this.retryCount - this.retryCountCheckpoint > 0) {
288
+ // We count the difference between the last checkpoint and the current retry count
289
+ this.retryCount =
290
+ this.retryCountCheckpoint +
291
+ (this.retryCount - this.retryCountCheckpoint)
292
+ } else {
293
+ this.retryCount += 1
294
+ }
295
+
296
+ this.retryOpts.retry(
297
+ err,
298
+ {
299
+ state: { counter: this.retryCount },
300
+ opts: { retryOptions: this.retryOpts, ...this.opts }
301
+ },
302
+ onRetry.bind(this)
303
+ )
304
+
305
+ /**
306
+ * @this {RetryHandler}
307
+ * @param {Error} [err]
308
+ * @returns
309
+ */
310
+ function onRetry (err) {
311
+ if (err != null || controller?.aborted || isDisturbed(this.opts.body)) {
312
+ return this.handler.onResponseError?.(controller, err)
313
+ }
314
+
315
+ if (this.start !== 0) {
316
+ const headers = { range: `bytes=${this.start}-${this.end ?? ''}` }
317
+
318
+ // Weak etag check - weak etags will make comparison algorithms never match
319
+ if (this.etag != null) {
320
+ headers['if-match'] = this.etag
321
+ }
322
+
323
+ this.opts = {
324
+ ...this.opts,
325
+ headers: {
326
+ ...this.opts.headers,
327
+ ...headers
328
+ }
329
+ }
330
+ }
331
+
332
+ try {
333
+ this.retryCountCheckpoint = this.retryCount
334
+ this.dispatch(this.opts, this)
335
+ } catch (err) {
336
+ this.handler.onResponseError?.(controller, err)
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ module.exports = RetryHandler
@@ -0,0 +1,96 @@
1
+ 'use strict'
2
+
3
+ const { parseHeaders } = require('../core/util')
4
+ const { InvalidArgumentError } = require('../core/errors')
5
+
6
+ const kResume = Symbol('resume')
7
+
8
+ class UnwrapController {
9
+ #paused = false
10
+ #reason = null
11
+ #aborted = false
12
+ #abort
13
+
14
+ [kResume] = null
15
+
16
+ constructor (abort) {
17
+ this.#abort = abort
18
+ }
19
+
20
+ pause () {
21
+ this.#paused = true
22
+ }
23
+
24
+ resume () {
25
+ if (this.#paused) {
26
+ this.#paused = false
27
+ this[kResume]?.()
28
+ }
29
+ }
30
+
31
+ abort (reason) {
32
+ if (!this.#aborted) {
33
+ this.#aborted = true
34
+ this.#reason = reason
35
+ this.#abort(reason)
36
+ }
37
+ }
38
+
39
+ get aborted () {
40
+ return this.#aborted
41
+ }
42
+
43
+ get reason () {
44
+ return this.#reason
45
+ }
46
+
47
+ get paused () {
48
+ return this.#paused
49
+ }
50
+ }
51
+
52
+ module.exports = class UnwrapHandler {
53
+ #handler
54
+ #controller
55
+
56
+ constructor (handler) {
57
+ this.#handler = handler
58
+ }
59
+
60
+ static unwrap (handler) {
61
+ // TODO (fix): More checks...
62
+ return !handler.onRequestStart ? handler : new UnwrapHandler(handler)
63
+ }
64
+
65
+ onConnect (abort, context) {
66
+ this.#controller = new UnwrapController(abort)
67
+ this.#handler.onRequestStart?.(this.#controller, context)
68
+ }
69
+
70
+ onUpgrade (statusCode, rawHeaders, socket) {
71
+ this.#handler.onRequestUpgrade?.(this.#controller, statusCode, parseHeaders(rawHeaders), socket)
72
+ }
73
+
74
+ onHeaders (statusCode, rawHeaders, resume, statusMessage) {
75
+ this.#controller[kResume] = resume
76
+ this.#handler.onResponseStart?.(this.#controller, statusCode, parseHeaders(rawHeaders), statusMessage)
77
+ return !this.#controller.paused
78
+ }
79
+
80
+ onData (data) {
81
+ this.#handler.onResponseData?.(this.#controller, data)
82
+ return !this.#controller.paused
83
+ }
84
+
85
+ onComplete (rawTrailers) {
86
+ this.#handler.onResponseEnd?.(this.#controller, parseHeaders(rawTrailers))
87
+ }
88
+
89
+ onError (err) {
90
+ if (!this.#handler.onResponseError) {
91
+ throw new InvalidArgumentError('invalid onError method')
92
+ }
93
+
94
+ this.#handler.onResponseError?.(this.#controller, err)
95
+ }
96
+ }