@entelligentsia/forgecli 0.7.10 → 0.8.4

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 (227) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/CHANGELOG-forge-plugin.md +70 -0
  3. package/dist/CHANGELOG-pi.md +63 -0
  4. package/dist/bin/argv.d.ts +2 -2
  5. package/dist/bin/argv.js +10 -0
  6. package/dist/bin/argv.js.map +1 -1
  7. package/dist/bin/env-defaults.d.ts +1 -0
  8. package/dist/bin/env-defaults.js +13 -0
  9. package/dist/bin/env-defaults.js.map +1 -0
  10. package/dist/bin/forge.js +9 -0
  11. package/dist/bin/forge.js.map +1 -1
  12. package/dist/bin/update-cli.d.ts +9 -0
  13. package/dist/bin/update-cli.js +120 -0
  14. package/dist/bin/update-cli.js.map +1 -0
  15. package/dist/extensions/forgecli/index.js +3 -3
  16. package/dist/extensions/forgecli/index.js.map +1 -1
  17. package/dist/extensions/forgecli/update-check.js +1 -1
  18. package/dist/extensions/forgecli/update-check.js.map +1 -1
  19. package/dist/extensions/forgecli/whats-new-widget.d.ts +5 -5
  20. package/dist/extensions/forgecli/whats-new-widget.js +11 -11
  21. package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
  22. package/dist/extensions/forgecli/whats-new.js +6 -5
  23. package/dist/extensions/forgecli/whats-new.js.map +1 -1
  24. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  25. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +27 -98
  26. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  27. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +62 -132
  28. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  29. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
  30. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +25 -15
  31. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  32. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  33. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +1 -0
  34. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
  35. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  36. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +17 -1
  37. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  38. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  39. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +8 -2
  40. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
  41. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  42. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +17 -1
  43. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
  44. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  45. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +8 -1
  46. package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
  47. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  48. package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +63 -0
  49. package/node_modules/@earendil-works/pi-coding-agent/README.md +1 -1
  50. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.d.ts.map +1 -1
  51. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js +1 -1
  52. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js.map +1 -1
  53. package/node_modules/@earendil-works/pi-coding-agent/dist/cli.d.ts.map +1 -1
  54. package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js +6 -10
  55. package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js.map +1 -1
  56. package/node_modules/@earendil-works/pi-coding-agent/dist/config.d.ts.map +1 -1
  57. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +12 -3
  58. package/node_modules/@earendil-works/pi-coding-agent/dist/config.js.map +1 -1
  59. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +1 -0
  60. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  61. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +30 -15
  62. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  63. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts +3 -3
  64. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  65. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +23 -13
  66. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  67. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts +4 -0
  68. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  69. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +58 -38
  70. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  71. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  72. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +0 -1
  73. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  74. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  75. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +3 -2
  76. package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  77. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts +2 -2
  78. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  79. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +7 -4
  80. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js.map +1 -1
  81. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  82. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +6 -2
  83. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  84. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts.map +1 -1
  85. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +3 -4
  86. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js.map +1 -1
  87. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.d.ts.map +1 -1
  88. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js +2 -2
  89. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js.map +1 -1
  90. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts +7 -1
  91. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts.map +1 -1
  92. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js +60 -7
  93. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js.map +1 -1
  94. package/node_modules/@earendil-works/pi-coding-agent/docs/packages.md +2 -2
  95. package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +1 -3
  96. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
  97. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  98. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
  99. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
  100. package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -6
  101. package/node_modules/@earendil-works/pi-tui/package.json +2 -2
  102. package/node_modules/@protobufjs/fetch/CHANGELOG.md +8 -0
  103. package/node_modules/@protobufjs/fetch/index.d.ts +7 -7
  104. package/node_modules/@protobufjs/fetch/index.js +4 -7
  105. package/node_modules/@protobufjs/fetch/package.json +7 -5
  106. package/node_modules/@protobufjs/fetch/tests/data/file.txt +1 -0
  107. package/node_modules/@protobufjs/fetch/tests/index.js +150 -8
  108. package/node_modules/@protobufjs/fetch/util/fs.js +11 -0
  109. package/node_modules/@protobufjs/inquire/CHANGELOG.md +8 -0
  110. package/node_modules/@protobufjs/inquire/index.d.ts +1 -0
  111. package/node_modules/@protobufjs/inquire/index.js +1 -0
  112. package/node_modules/@protobufjs/inquire/package.json +1 -1
  113. package/node_modules/protobufjs/dist/light/protobuf.js +187 -153
  114. package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
  115. package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
  116. package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
  117. package/node_modules/protobufjs/dist/minimal/protobuf.js +14 -5
  118. package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
  119. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
  120. package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
  121. package/node_modules/protobufjs/dist/protobuf.js +207 -173
  122. package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  123. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  124. package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  125. package/node_modules/protobufjs/package.json +6 -3
  126. package/node_modules/protobufjs/src/util/fs.js +11 -0
  127. package/node_modules/protobufjs/src/util/minimal.js +10 -2
  128. package/node_modules/protobufjs/src/util.js +1 -1
  129. package/node_modules/undici/README.md +14 -5
  130. package/node_modules/undici/docs/docs/api/Client.md +4 -2
  131. package/node_modules/undici/docs/docs/api/Dispatcher.md +62 -27
  132. package/node_modules/undici/docs/docs/api/GlobalInstallation.md +7 -5
  133. package/node_modules/undici/docs/docs/api/H2CClient.md +1 -1
  134. package/node_modules/undici/docs/docs/api/RedirectHandler.md +14 -9
  135. package/node_modules/undici/docs/docs/api/RetryAgent.md +0 -1
  136. package/node_modules/undici/docs/docs/api/RetryHandler.md +12 -14
  137. package/node_modules/undici/docs/docs/api/SnapshotAgent.md +23 -0
  138. package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +231 -0
  139. package/node_modules/undici/index.js +4 -2
  140. package/node_modules/undici/lib/api/api-connect.js +13 -11
  141. package/node_modules/undici/lib/api/api-pipeline.js +26 -13
  142. package/node_modules/undici/lib/api/api-request.js +45 -21
  143. package/node_modules/undici/lib/api/api-stream.js +81 -20
  144. package/node_modules/undici/lib/api/api-upgrade.js +21 -11
  145. package/node_modules/undici/lib/api/readable.js +3 -2
  146. package/node_modules/undici/lib/cache/memory-cache-store.js +1 -1
  147. package/node_modules/undici/lib/cache/sqlite-cache-store.js +6 -4
  148. package/node_modules/undici/lib/core/connect.js +17 -1
  149. package/node_modules/undici/lib/core/constants.js +1 -24
  150. package/node_modules/undici/lib/core/errors.js +2 -2
  151. package/node_modules/undici/lib/core/request.js +115 -18
  152. package/node_modules/undici/lib/core/socks5-client.js +24 -9
  153. package/node_modules/undici/lib/core/socks5-utils.js +32 -23
  154. package/node_modules/undici/lib/core/symbols.js +1 -0
  155. package/node_modules/undici/lib/core/util.js +70 -43
  156. package/node_modules/undici/lib/dispatcher/agent.js +47 -33
  157. package/node_modules/undici/lib/dispatcher/balanced-pool.js +21 -26
  158. package/node_modules/undici/lib/dispatcher/client-h1.js +98 -39
  159. package/node_modules/undici/lib/dispatcher/client-h2.js +603 -272
  160. package/node_modules/undici/lib/dispatcher/client.js +12 -5
  161. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +24 -5
  162. package/node_modules/undici/lib/dispatcher/dispatcher.js +0 -4
  163. package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +107 -0
  164. package/node_modules/undici/lib/dispatcher/h2c-client.js +5 -5
  165. package/node_modules/undici/lib/dispatcher/pool-base.js +28 -10
  166. package/node_modules/undici/lib/dispatcher/pool.js +31 -6
  167. package/node_modules/undici/lib/dispatcher/proxy-agent.js +38 -13
  168. package/node_modules/undici/lib/dispatcher/round-robin-pool.js +31 -9
  169. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +95 -80
  170. package/node_modules/undici/lib/global.js +13 -1
  171. package/node_modules/undici/lib/handler/cache-handler.js +16 -8
  172. package/node_modules/undici/lib/handler/decorator-handler.js +1 -2
  173. package/node_modules/undici/lib/handler/redirect-handler.js +5 -51
  174. package/node_modules/undici/lib/handler/retry-handler.js +15 -2
  175. package/node_modules/undici/lib/interceptor/cache.js +30 -17
  176. package/node_modules/undici/lib/interceptor/decompress.js +28 -2
  177. package/node_modules/undici/lib/interceptor/dns.js +1 -1
  178. package/node_modules/undici/lib/interceptor/redirect.js +3 -3
  179. package/node_modules/undici/lib/llhttp/llhttp-wasm.js +1 -1
  180. package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +1 -1
  181. package/node_modules/undici/lib/mock/mock-agent.js +8 -8
  182. package/node_modules/undici/lib/mock/mock-call-history.js +15 -15
  183. package/node_modules/undici/lib/mock/mock-utils.js +37 -22
  184. package/node_modules/undici/lib/mock/snapshot-agent.js +16 -6
  185. package/node_modules/undici/lib/mock/snapshot-recorder.js +38 -3
  186. package/node_modules/undici/lib/util/cache.js +8 -7
  187. package/node_modules/undici/lib/util/runtime-features.js +3 -34
  188. package/node_modules/undici/lib/web/cache/cache.js +6 -8
  189. package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +245 -150
  190. package/node_modules/undici/lib/web/fetch/body.js +3 -9
  191. package/node_modules/undici/lib/web/fetch/formdata-parser.js +17 -6
  192. package/node_modules/undici/lib/web/fetch/formdata.js +21 -2
  193. package/node_modules/undici/lib/web/fetch/index.js +214 -221
  194. package/node_modules/undici/lib/web/webidl/index.js +7 -9
  195. package/node_modules/undici/lib/web/websocket/frame.js +1 -7
  196. package/node_modules/undici/lib/web/websocket/permessage-deflate.js +13 -31
  197. package/node_modules/undici/lib/web/websocket/receiver.js +62 -22
  198. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +11 -17
  199. package/node_modules/undici/lib/web/websocket/websocket.js +6 -1
  200. package/node_modules/undici/package.json +9 -9
  201. package/node_modules/undici/types/agent.d.ts +0 -2
  202. package/node_modules/undici/types/client.d.ts +25 -19
  203. package/node_modules/undici/types/dispatcher.d.ts +7 -27
  204. package/node_modules/undici/types/dispatcher1-wrapper.d.ts +7 -0
  205. package/node_modules/undici/types/formdata.d.ts +0 -6
  206. package/node_modules/undici/types/h2c-client.d.ts +6 -6
  207. package/node_modules/undici/types/header.d.ts +5 -0
  208. package/node_modules/undici/types/index.d.ts +3 -1
  209. package/node_modules/undici/types/interceptors.d.ts +1 -1
  210. package/node_modules/undici/types/pool.d.ts +0 -2
  211. package/node_modules/undici/types/proxy-agent.d.ts +2 -2
  212. package/node_modules/undici/types/round-robin-pool.d.ts +0 -2
  213. package/node_modules/undici/types/snapshot-agent.d.ts +4 -0
  214. package/node_modules/undici/types/socks5-proxy-agent.d.ts +2 -2
  215. package/node_modules/undici/types/webidl.d.ts +0 -1
  216. package/package.json +7 -8
  217. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
  218. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package-lock.json +0 -92
  219. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package-lock.json +0 -31
  220. package/node_modules/undici/lib/handler/unwrap-handler.js +0 -100
  221. package/node_modules/undici/lib/handler/wrap-handler.js +0 -105
  222. package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
  223. package/node_modules/undici/lib/util/promise.js +0 -28
  224. package/skills/refresh-kb-links/SKILL.md +0 -217
  225. package/skills/store-custodian/SKILL.md +0 -163
  226. package/skills/store-query-grammar/SKILL.md +0 -145
  227. package/skills/store-query-nlp/SKILL.md +0 -110
@@ -0,0 +1,231 @@
1
+ # Migrating from Undici 7 to 8
2
+
3
+ This guide covers the changes you are most likely to hit when upgrading an
4
+ application or library from Undici v7 to v8.
5
+
6
+ ## Before you upgrade
7
+
8
+ - Make sure your runtime is Node.js `>= 22.19.0`.
9
+ - If you have custom dispatchers, interceptors, or handlers, review the
10
+ handler API changes before updating.
11
+ - If you rely on HTTP/1.1-only behavior, plan to set `allowH2: false`
12
+ explicitly.
13
+
14
+ ## 1. Update your Node.js version
15
+
16
+ Undici v8 requires Node.js `>= 22.19.0`.
17
+
18
+ If you are still on Node.js 20 or an older Node.js 22 release, upgrade Node.js
19
+ first:
20
+
21
+ ```bash
22
+ node -v
23
+ ```
24
+
25
+ If that command prints a version lower than `v22.19.0`, upgrade Node.js before
26
+ installing Undici v8.
27
+
28
+ ## 2. Migrate custom dispatcher handlers to the v2 API
29
+
30
+ Undici v8 uses the newer dispatcher handler API consistently.
31
+
32
+ If you implemented custom dispatchers, interceptors, or wrappers around
33
+ `dispatch()`, update legacy callbacks such as `onConnect`, `onHeaders`, and
34
+ `onComplete` to the newer callback names.
35
+
36
+ ### Old handler callbacks vs. v8 callbacks
37
+
38
+ | Undici 7 style | Undici 8 style |
39
+ |---|---|
40
+ | `onConnect(abort, context)` | `onRequestStart(controller, context)` |
41
+ | `onHeaders(statusCode, rawHeaders, resume, statusText)` | `onResponseStart(controller, statusCode, headers, statusText)` |
42
+ | `onData(chunk)` | `onResponseData(controller, chunk)` |
43
+ | `onComplete(trailers)` | `onResponseEnd(controller, trailers)` |
44
+ | `onError(err)` | `onResponseError(controller, err)` |
45
+ | `onUpgrade(statusCode, rawHeaders, socket)` | `onRequestUpgrade(controller, statusCode, headers, socket)` |
46
+
47
+ ### Example
48
+
49
+ Before:
50
+
51
+ ```js
52
+ client.dispatch(options, {
53
+ onConnect (abort) {
54
+ this.abort = abort
55
+ },
56
+ onHeaders (statusCode, headers, resume) {
57
+ this.resume = resume
58
+ return true
59
+ },
60
+ onData (chunk) {
61
+ chunks.push(chunk)
62
+ return true
63
+ },
64
+ onComplete (trailers) {
65
+ console.log(trailers)
66
+ },
67
+ onError (err) {
68
+ console.error(err)
69
+ }
70
+ })
71
+ ```
72
+
73
+ After:
74
+
75
+ ```js
76
+ client.dispatch(options, {
77
+ onRequestStart (controller) {
78
+ this.controller = controller
79
+ },
80
+ onResponseStart (controller, statusCode, headers, statusText) {
81
+ console.log(statusCode, statusText, headers)
82
+ },
83
+ onResponseData (controller, chunk) {
84
+ chunks.push(chunk)
85
+ },
86
+ onResponseEnd (controller, trailers) {
87
+ console.log(trailers)
88
+ },
89
+ onResponseError (controller, err) {
90
+ console.error(err)
91
+ }
92
+ })
93
+ ```
94
+
95
+ ### Pause, resume, and abort now go through the controller
96
+
97
+ In Undici v7, legacy handlers could return `false` or keep references to
98
+ `abort()` and `resume()` callbacks. In Undici v8, use the controller instead:
99
+
100
+ ```js
101
+ onRequestStart (controller) {
102
+ this.controller = controller
103
+ }
104
+
105
+ onResponseData (controller, chunk) {
106
+ controller.pause()
107
+ setImmediate(() => controller.resume())
108
+ }
109
+
110
+ onResponseError (controller, err) {
111
+ controller.abort(err)
112
+ }
113
+ ```
114
+
115
+ ### Raw headers and trailers moved to the controller
116
+
117
+ If you need the raw header arrays, read them from the controller:
118
+
119
+ - `controller.rawHeaders`
120
+ - `controller.rawTrailers`
121
+
122
+ ## 3. Update `onBodySent()` handlers
123
+
124
+ If you implemented `onBodySent()`, note that its signature changed.
125
+
126
+ Before, handlers received counters:
127
+
128
+ ```js
129
+ onBodySent (chunkSize, totalBytesSent) {}
130
+ ```
131
+
132
+ In Undici v8, handlers receive the actual chunk:
133
+
134
+ ```js
135
+ onBodySent (chunk) {}
136
+ ```
137
+
138
+ If you need a notification that the whole body has been sent, use
139
+ `onRequestSent()`:
140
+
141
+ ```js
142
+ onRequestSent () {
143
+ console.log('request body fully sent')
144
+ }
145
+ ```
146
+
147
+ ## 4. If you need HTTP/1.1 only, disable HTTP/2 explicitly
148
+
149
+ Undici v8 enables HTTP/2 by default when a TLS server negotiates it via ALPN.
150
+
151
+ If your application depends on HTTP/1.1-specific behavior, set `allowH2: false`
152
+ explicitly.
153
+
154
+ Before:
155
+
156
+ ```js
157
+ const client = new Client('https://example.com')
158
+ ```
159
+
160
+ After, to keep HTTP/1.1 only:
161
+
162
+ ```js
163
+ const client = new Client('https://example.com', {
164
+ allowH2: false
165
+ })
166
+ ```
167
+
168
+ The same applies when you configure an `Agent`:
169
+
170
+ ```js
171
+ const agent = new Agent({
172
+ allowH2: false
173
+ })
174
+ ```
175
+
176
+ ## 5. Use real `Blob` and `File` instances
177
+
178
+ Undici v8 no longer accepts fake Blob-like values that only imitate `Blob` or
179
+ `File` via properties such as `Symbol.toStringTag`.
180
+
181
+ If you were passing custom objects that looked like `Blob`s, replace them with
182
+ actual `Blob` or `File` instances:
183
+
184
+ ```js
185
+ const body = new Blob(['hello'])
186
+ ```
187
+
188
+ ## 6. Avoid depending on the internal global dispatcher symbol
189
+
190
+ `setGlobalDispatcher()` and `getGlobalDispatcher()` remain the public APIs and
191
+ should continue to be used.
192
+
193
+ Internally, Undici v8 stores its dispatcher under
194
+ `Symbol.for('undici.globalDispatcher.2')` and mirrors a v1-compatible wrapper
195
+ for legacy consumers such as Node.js built-in `fetch`.
196
+
197
+ If your code was reading or writing `Symbol.for('undici.globalDispatcher.1')`
198
+ directly, migrate to the public APIs instead:
199
+
200
+ ```js
201
+ import { setGlobalDispatcher, getGlobalDispatcher, Agent } from 'undici'
202
+
203
+ setGlobalDispatcher(new Agent())
204
+ const dispatcher = getGlobalDispatcher()
205
+ ```
206
+
207
+ If you must expose a dispatcher to legacy v1 handler consumers, wrap it with
208
+ `Dispatcher1Wrapper`:
209
+
210
+ ```js
211
+ import { Agent, Dispatcher1Wrapper } from 'undici'
212
+
213
+ const legacyCompatibleDispatcher = new Dispatcher1Wrapper(new Agent())
214
+ ```
215
+
216
+ ## 7. Verify the upgrade
217
+
218
+ After moving to Undici v8, it is worth checking these paths in your test suite:
219
+
220
+ - requests that use a custom `dispatcher`
221
+ - `setGlobalDispatcher()` behavior
222
+ - any custom interceptor or retry handler
223
+ - uploads that use `Blob`, `File`, or `FormData`
224
+ - integrations that depend on HTTP/1.1-only behavior
225
+
226
+ ## Related documentation
227
+
228
+ - [Dispatcher](/docs/api/Dispatcher.md)
229
+ - [Client](/docs/api/Client.md)
230
+ - [Global Installation](/docs/api/GlobalInstallation.md)
231
+ - [Undici Module vs. Node.js Built-in Fetch](/docs/best-practices/undici-vs-builtin-fetch.md)
@@ -6,6 +6,7 @@ const Pool = require('./lib/dispatcher/pool')
6
6
  const BalancedPool = require('./lib/dispatcher/balanced-pool')
7
7
  const RoundRobinPool = require('./lib/dispatcher/round-robin-pool')
8
8
  const Agent = require('./lib/dispatcher/agent')
9
+ const Dispatcher1Wrapper = require('./lib/dispatcher/dispatcher1-wrapper')
9
10
  const ProxyAgent = require('./lib/dispatcher/proxy-agent')
10
11
  const Socks5ProxyAgent = require('./lib/dispatcher/socks5-proxy-agent')
11
12
  const EnvHttpProxyAgent = require('./lib/dispatcher/env-http-proxy-agent')
@@ -35,6 +36,7 @@ module.exports.Pool = Pool
35
36
  module.exports.BalancedPool = BalancedPool
36
37
  module.exports.RoundRobinPool = RoundRobinPool
37
38
  module.exports.Agent = Agent
39
+ module.exports.Dispatcher1Wrapper = Dispatcher1Wrapper
38
40
  module.exports.ProxyAgent = ProxyAgent
39
41
  module.exports.Socks5ProxyAgent = Socks5ProxyAgent
40
42
  module.exports.EnvHttpProxyAgent = EnvHttpProxyAgent
@@ -103,14 +105,14 @@ function makeDispatcher (fn) {
103
105
  url = util.parseURL(url)
104
106
  }
105
107
 
106
- const { agent, dispatcher = getGlobalDispatcher() } = opts
108
+ const { agent, dispatcher = getGlobalDispatcher(), ...restOpts } = opts
107
109
 
108
110
  if (agent) {
109
111
  throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?')
110
112
  }
111
113
 
112
114
  return fn.call(dispatcher, {
113
- ...opts,
115
+ ...restOpts,
114
116
  origin: url.origin,
115
117
  path: url.search ? `${url.pathname}${url.search}` : url.pathname,
116
118
  method: opts.method || (opts.body ? 'PUT' : 'GET')
@@ -32,45 +32,48 @@ class ConnectHandler extends AsyncResource {
32
32
  addSignal(this, signal)
33
33
  }
34
34
 
35
- onConnect (abort, context) {
35
+ onRequestStart (controller, context) {
36
36
  if (this.reason) {
37
- abort(this.reason)
37
+ controller.abort(this.reason)
38
38
  return
39
39
  }
40
40
 
41
41
  assert(this.callback)
42
42
 
43
- this.abort = abort
43
+ this.abort = (reason) => controller.abort(reason)
44
44
  this.context = context
45
45
  }
46
46
 
47
- onHeaders () {
47
+ onResponseStart () {
48
48
  throw new SocketError('bad connect', null)
49
49
  }
50
50
 
51
- onUpgrade (statusCode, rawHeaders, socket) {
51
+ onRequestUpgrade (controller, statusCode, headers, socket) {
52
52
  const { callback, opaque, context } = this
53
53
 
54
54
  removeSignal(this)
55
55
 
56
56
  this.callback = null
57
57
 
58
- let headers = rawHeaders
58
+ let responseHeaders = headers
59
+ const rawHeaders = controller?.rawHeaders
59
60
  // Indicates is an HTTP2Session
60
- if (headers != null) {
61
- headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
61
+ if (responseHeaders != null) {
62
+ responseHeaders = this.responseHeaders === 'raw'
63
+ ? util.parseRawHeaders(rawHeaders)
64
+ : headers
62
65
  }
63
66
 
64
67
  this.runInAsyncScope(callback, null, null, {
65
68
  statusCode,
66
- headers,
69
+ headers: responseHeaders,
67
70
  socket,
68
71
  opaque,
69
72
  context
70
73
  })
71
74
  }
72
75
 
73
- onError (err) {
76
+ onResponseError (_controller, err) {
74
77
  const { callback, opaque } = this
75
78
 
76
79
  removeSignal(this)
@@ -96,7 +99,6 @@ function connect (opts, callback) {
96
99
  try {
97
100
  const connectHandler = new ConnectHandler(opts, callback)
98
101
  const connectOptions = { ...opts, method: 'CONNECT' }
99
-
100
102
  this.dispatch(connectOptions, connectHandler)
101
103
  } catch (err) {
102
104
  if (typeof callback !== 'function') {
@@ -13,6 +13,7 @@ const {
13
13
  RequestAbortedError
14
14
  } = require('../core/errors')
15
15
  const util = require('../core/util')
16
+ const { kBodyUsed } = require('../core/symbols')
16
17
  const { addSignal, removeSignal } = require('./abort-signal')
17
18
 
18
19
  function noop () {}
@@ -24,6 +25,9 @@ class PipelineRequest extends Readable {
24
25
  super({ autoDestroy: true })
25
26
 
26
27
  this[kResume] = null
28
+ // Pipeline request bodies come from a live writable side and cannot be
29
+ // replayed across redirects or retries, even before any bytes are read.
30
+ this[kBodyUsed] = true
27
31
  }
28
32
 
29
33
  _read () {
@@ -146,40 +150,46 @@ class PipelineHandler extends AsyncResource {
146
150
  addSignal(this, signal)
147
151
  }
148
152
 
149
- onConnect (abort, context) {
153
+ onRequestStart (controller, context) {
150
154
  const { res } = this
151
155
 
152
156
  if (this.reason) {
153
- abort(this.reason)
157
+ controller.abort(this.reason)
154
158
  return
155
159
  }
156
160
 
157
161
  assert(!res, 'pipeline cannot be retried')
158
162
 
159
- this.abort = abort
163
+ this.abort = (reason) => controller.abort(reason)
160
164
  this.context = context
161
165
  }
162
166
 
163
- onHeaders (statusCode, rawHeaders, resume) {
167
+ onResponseStart (controller, statusCode, headers, _statusMessage) {
164
168
  const { opaque, handler, context } = this
165
169
 
166
170
  if (statusCode < 200) {
167
171
  if (this.onInfo) {
168
- const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
169
- this.onInfo({ statusCode, headers })
172
+ const rawHeaders = controller?.rawHeaders
173
+ const responseHeaders = this.responseHeaders === 'raw'
174
+ ? util.parseRawHeaders(rawHeaders)
175
+ : headers
176
+ this.onInfo({ statusCode, headers: responseHeaders })
170
177
  }
171
178
  return
172
179
  }
173
180
 
174
- this.res = new PipelineResponse(resume)
181
+ this.res = new PipelineResponse(() => controller.resume())
175
182
 
176
183
  let body
177
184
  try {
178
185
  this.handler = null
179
- const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
186
+ const rawHeaders = controller?.rawHeaders
187
+ const responseHeaders = this.responseHeaders === 'raw'
188
+ ? util.parseRawHeaders(rawHeaders)
189
+ : headers
180
190
  body = this.runInAsyncScope(handler, null, {
181
191
  statusCode,
182
- headers,
192
+ headers: responseHeaders,
183
193
  opaque,
184
194
  body: this.res,
185
195
  context
@@ -222,17 +232,20 @@ class PipelineHandler extends AsyncResource {
222
232
  this.body = body
223
233
  }
224
234
 
225
- onData (chunk) {
235
+ onResponseData (controller, chunk) {
226
236
  const { res } = this
227
- return res.push(chunk)
237
+
238
+ if (res.push(chunk) === false) {
239
+ controller.pause()
240
+ }
228
241
  }
229
242
 
230
- onComplete (trailers) {
243
+ onResponseEnd (_controller, _trailers) {
231
244
  const { res } = this
232
245
  res.push(null)
233
246
  }
234
247
 
235
- onError (err) {
248
+ onResponseError (_controller, err) {
236
249
  const { ret } = this
237
250
  this.handler = null
238
251
  util.destroy(ret, err)
@@ -21,7 +21,7 @@ class RequestHandler extends AsyncResource {
21
21
  throw new InvalidArgumentError('invalid callback')
22
22
  }
23
23
 
24
- if (highWaterMark && (typeof highWaterMark !== 'number' || highWaterMark < 0)) {
24
+ if (highWaterMark != null && (!Number.isFinite(highWaterMark) || highWaterMark < 0)) {
25
25
  throw new InvalidArgumentError('invalid highWaterMark')
26
26
  }
27
27
 
@@ -54,6 +54,7 @@ class RequestHandler extends AsyncResource {
54
54
  this.body = body
55
55
  this.trailers = {}
56
56
  this.context = null
57
+ this.controller = null
57
58
  this.onInfo = onInfo || null
58
59
  this.highWaterMark = highWaterMark
59
60
  this.reason = null
@@ -73,36 +74,40 @@ class RequestHandler extends AsyncResource {
73
74
  }
74
75
  }
75
76
 
76
- onConnect (abort, context) {
77
+ onRequestStart (controller, context) {
77
78
  if (this.reason) {
78
- abort(this.reason)
79
+ controller.abort(this.reason)
79
80
  return
80
81
  }
81
82
 
82
83
  assert(this.callback)
83
84
 
84
- this.abort = abort
85
+ this.controller = controller
86
+ this.abort = (reason) => controller.abort(reason)
85
87
  this.context = context
86
88
  }
87
89
 
88
- onHeaders (statusCode, rawHeaders, resume, statusMessage) {
89
- const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this
90
+ onResponseStart (controller, statusCode, headers, statusText) {
91
+ const { callback, opaque, context, responseHeaders, highWaterMark } = this
90
92
 
91
- const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)
93
+ const rawHeaders = controller?.rawHeaders
94
+ const responseHeaderData = responseHeaders === 'raw'
95
+ ? util.parseRawHeaders(rawHeaders)
96
+ : headers
92
97
 
93
98
  if (statusCode < 200) {
94
99
  if (this.onInfo) {
95
- this.onInfo({ statusCode, headers })
100
+ this.onInfo({ statusCode, headers: responseHeaderData })
96
101
  }
97
102
  return
98
103
  }
99
104
 
100
- const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers
101
- const contentType = parsedHeaders['content-type']
102
- const contentLength = parsedHeaders['content-length']
105
+ const parsedHeaders = headers
106
+ const contentType = parsedHeaders?.['content-type']
107
+ const contentLength = parsedHeaders?.['content-length']
103
108
  const res = new Readable({
104
- resume,
105
- abort,
109
+ resume: () => controller.resume(),
110
+ abort: (reason) => controller.abort(reason),
106
111
  contentType,
107
112
  contentLength: this.method !== 'HEAD' && contentLength
108
113
  ? Number(contentLength)
@@ -121,8 +126,8 @@ class RequestHandler extends AsyncResource {
121
126
  try {
122
127
  this.runInAsyncScope(callback, null, null, {
123
128
  statusCode,
124
- statusText: statusMessage,
125
- headers,
129
+ statusText,
130
+ headers: responseHeaderData,
126
131
  trailers: this.trailers,
127
132
  opaque,
128
133
  body: res,
@@ -144,16 +149,35 @@ class RequestHandler extends AsyncResource {
144
149
  }
145
150
  }
146
151
 
147
- onData (chunk) {
148
- return this.res.push(chunk)
152
+ onResponseData (controller, chunk) {
153
+ if (!this.res) {
154
+ return
155
+ }
156
+
157
+ if (this.res.push(chunk) === false) {
158
+ controller.pause()
159
+ }
149
160
  }
150
161
 
151
- onComplete (trailers) {
152
- util.parseHeaders(trailers, this.trailers)
153
- this.res.push(null)
162
+ onResponseEnd (_controller, trailers) {
163
+ if (trailers && typeof trailers === 'object') {
164
+ for (const key of Object.keys(trailers)) {
165
+ if (key === '__proto__') {
166
+ Object.defineProperty(this.trailers, key, {
167
+ value: trailers[key],
168
+ enumerable: true,
169
+ configurable: true,
170
+ writable: true
171
+ })
172
+ } else {
173
+ this.trailers[key] = trailers[key]
174
+ }
175
+ }
176
+ }
177
+ this.res?.push(null)
154
178
  }
155
179
 
156
- onError (err) {
180
+ onResponseError (_controller, err) {
157
181
  const { res, callback, body, opaque } = this
158
182
 
159
183
  if (callback) {