@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protobufjs",
3
- "version": "7.5.8",
3
+ "version": "7.5.9",
4
4
  "versionScheme": "~",
5
5
  "description": "Protocol Buffers for JavaScript (& TypeScript).",
6
6
  "author": "Daniel Wirtz <dcode+protobufjs@dcode.io>",
@@ -27,6 +27,9 @@
27
27
  ],
28
28
  "main": "index.js",
29
29
  "types": "index.d.ts",
30
+ "browser": {
31
+ "fs": false
32
+ },
30
33
  "publishConfig": {
31
34
  "tag": "latest-7"
32
35
  },
@@ -58,9 +61,9 @@
58
61
  "@protobufjs/base64": "^1.1.2",
59
62
  "@protobufjs/codegen": "^2.0.5",
60
63
  "@protobufjs/eventemitter": "^1.1.0",
61
- "@protobufjs/fetch": "^1.1.0",
64
+ "@protobufjs/fetch": "^1.1.1",
62
65
  "@protobufjs/float": "^1.0.2",
63
- "@protobufjs/inquire": "^1.1.1",
66
+ "@protobufjs/inquire": "^1.1.2",
64
67
  "@protobufjs/path": "^1.1.2",
65
68
  "@protobufjs/pool": "^1.1.0",
66
69
  "@protobufjs/utf8": "^1.1.1",
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+
3
+ var fs = null;
4
+ try {
5
+ fs = require(/* webpackIgnore: true */ "fs");
6
+ if (!fs || !fs.readFile || !fs.readFileSync)
7
+ fs = null;
8
+ } catch (e) {
9
+ // `fs` is unavailable in browsers and browser-like bundles.
10
+ }
11
+ module.exports = fs;
@@ -125,7 +125,7 @@ util.isSet = function isSet(obj, prop) {
125
125
  */
126
126
  util.Buffer = (function() {
127
127
  try {
128
- var Buffer = util.inquire("buffer").Buffer;
128
+ var Buffer = util.global.Buffer;
129
129
  // refuse to use non-node buffers if not explicitly assigned (perf reasons):
130
130
  return Buffer.prototype.utf8Write ? Buffer : /* istanbul ignore next */ null;
131
131
  } catch (e) {
@@ -179,7 +179,15 @@ util.Array = typeof Uint8Array !== "undefined" ? Uint8Array /* istanbul ignore n
179
179
  */
180
180
  util.Long = /* istanbul ignore next */ util.global.dcodeIO && /* istanbul ignore next */ util.global.dcodeIO.Long
181
181
  || /* istanbul ignore next */ util.global.Long
182
- || util.inquire("long");
182
+ || (function() {
183
+ try {
184
+ var Long = require("long");
185
+ return Long && Long.isLong ? Long : null;
186
+ } catch (e) {
187
+ /* istanbul ignore next */
188
+ return null;
189
+ }
190
+ })();
183
191
 
184
192
  /**
185
193
  * Regular expression used to verify 2 bit (`bool`) map keys.
@@ -23,7 +23,7 @@ var reservedRe = util.patterns.reservedRe,
23
23
  * Node's fs module if available.
24
24
  * @type {Object.<string,*>}
25
25
  */
26
- util.fs = util.inquire("fs");
26
+ util.fs = require("./util/fs");
27
27
 
28
28
  /**
29
29
  * Checks a recursion depth.
@@ -200,7 +200,9 @@ await fetch('https://example.com', {
200
200
  ```
201
201
 
202
202
  `install()` replaces the global `fetch`, `Headers`, `Response`, `Request`, and
203
- `FormData` implementations with undici's versions, so they all match.
203
+ `FormData` implementations with undici's versions, so they all match. It also
204
+ installs undici's `WebSocket`, `CloseEvent`, `ErrorEvent`, `MessageEvent`, and
205
+ `EventSource` globals.
204
206
 
205
207
  Avoid mixing a global `FormData` with `undici.fetch()`, or `undici.FormData`
206
208
  with the built-in global `fetch()`.
@@ -283,12 +285,12 @@ const data2 = await getData();
283
285
 
284
286
  ## Global Installation
285
287
 
286
- Undici provides an `install()` function to add all WHATWG fetch classes to `globalThis`, making them available globally:
288
+ Undici provides an `install()` function to add fetch-related and other web API classes to `globalThis`, making them available globally:
287
289
 
288
290
  ```js
289
291
  import { install } from 'undici'
290
292
 
291
- // Install all WHATWG fetch classes globally
293
+ // Install undici's global web APIs
292
294
  install()
293
295
 
294
296
  // Now you can use fetch classes globally without importing
@@ -316,8 +318,9 @@ The `install()` function adds the following classes to `globalThis`:
316
318
 
317
319
  When you call `install()`, these globals come from the same undici
318
320
  implementation. For example, global `fetch` and global `FormData` will both be
319
- undici's versions, which is the recommended setup if you want to use undici
320
- through globals.
321
+ undici's versions, and `WebSocket` and `EventSource` will also come from
322
+ undici, which is the recommended setup if you want to use undici through
323
+ globals.
321
324
 
322
325
  This is useful for:
323
326
  - Polyfilling environments that don't have fetch
@@ -619,6 +622,12 @@ See [Dispatcher.upgrade](./docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-
619
622
  Sets the global dispatcher used by Common API Methods. Global dispatcher is shared among compatible undici modules,
620
623
  including undici that is bundled internally with node.js.
621
624
 
625
+ Undici stores this dispatcher under `Symbol.for('undici.globalDispatcher.2')`.
626
+
627
+ `setGlobalDispatcher()` also mirrors the configured dispatcher to
628
+ `Symbol.for('undici.globalDispatcher.1')` using `Dispatcher1Wrapper`, so Node.js built-in `fetch`
629
+ can keep using the legacy handler contract while Undici uses the new handler API.
630
+
622
631
  ### `undici.getGlobalDispatcher()`
623
632
 
624
633
  Gets the global dispatcher used by Common API Methods.
@@ -24,12 +24,14 @@ Returns: `Client`
24
24
  * **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds.
25
25
  * **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
26
26
  * **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
27
+ * **webSocket** `WebSocketOptions` (optional) - WebSocket-specific configuration options.
28
+ * **maxPayloadSize** `number` (optional) - Default: `134217728` (128 MB) - Maximum allowed payload size in bytes for WebSocket messages. Applied to uncompressed messages, compressed frame payloads, and decompressed (permessage-deflate) messages. Set to 0 to disable the limit.
27
29
  * **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.
28
30
  * **connect** `ConnectOptions | Function | null` (optional) - Default: `null`.
29
31
  * **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. **Security Warning:** Disabling this option can expose your application to HTTP Request Smuggling attacks, where mismatched content-length headers cause servers and proxies to interpret request boundaries differently. This can lead to cache poisoning, credential hijacking, and bypassing security controls. Only disable this in controlled environments where you fully trust the request source.
30
32
  * **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version.
31
33
  * **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details.
32
- * **allowH2**: `boolean` - Default: `false`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
34
+ * **allowH2**: `boolean` - Default: `true`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
33
35
  * **useH2c**: `boolean` - Default: `false`. Enforces h2c for non-https connections.
34
36
  * **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.
35
37
  * **initialWindowSize**: `number` (optional) - Default: `262144` (256KB). Sets the HTTP/2 stream-level flow-control window size (SETTINGS_INITIAL_WINDOW_SIZE). Must be a positive integer greater than 0. This default is higher than Node.js core's default (65535 bytes) to improve throughput, Node's choice is very conservative for current high-bandwith networks. See [RFC 7540 Section 6.9.2](https://datatracker.ietf.org/doc/html/rfc7540#section-6.9.2) for more details.
@@ -282,4 +284,4 @@ console.log('requests completed')
282
284
 
283
285
  ### Event: `'error'`
284
286
 
285
- Invoked for users errors such as throwing in the `onError` handler.
287
+ Invoked for user errors such as throwing in the `onResponseError` handler.
@@ -212,6 +212,41 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
212
212
  * **onResponseEnd** `(controller: DispatchController, trailers: Record<string, string | string[]>) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
213
213
  * **onResponseError** `(controller: DispatchController, error: Error) => void` - Invoked when an error has occurred. May not throw.
214
214
 
215
+ #### Migration from legacy handler API
216
+
217
+ If you were previously using `onConnect/onHeaders/onData/onComplete/onError`, switch to the new callbacks:
218
+
219
+ - `onConnect(abort)` → `onRequestStart(controller)` and call `controller.abort(reason)`
220
+ - `onHeaders(status, rawHeaders, resume, statusText)` → `onResponseStart(controller, status, headers, statusText)`
221
+ - `onData(chunk)` → `onResponseData(controller, chunk)`
222
+ - `onComplete(trailers)` → `onResponseEnd(controller, trailers)`
223
+ - `onError(err)` → `onResponseError(controller, err)`
224
+ - `onUpgrade(status, rawHeaders, socket)` → `onRequestUpgrade(controller, status, headers, socket)`
225
+
226
+ To access raw header arrays (for preserving duplicates/casing), read them from the controller:
227
+
228
+ - `controller.rawHeaders` for response headers
229
+ - `controller.rawTrailers` for trailers
230
+
231
+ Pause/resume now uses the controller:
232
+
233
+ - Call `controller.pause()` and `controller.resume()` instead of returning `false` from handlers.
234
+
235
+ #### Compatibility notes
236
+
237
+ Undici now stores the global dispatcher under `Symbol.for('undici.globalDispatcher.2')`.
238
+ This avoids conflicts with runtimes (such as Node.js built-in `fetch`) that still rely on the legacy dispatcher handler interface.
239
+
240
+ `setGlobalDispatcher()` also mirrors the configured dispatcher to `Symbol.for('undici.globalDispatcher.1')` using a `Dispatcher1Wrapper`, so Node's built-in `fetch` can keep using the legacy handler contract.
241
+
242
+ If you need to expose a new dispatcher/agent to legacy v1 handler consumers (`onConnect/onHeaders/onData/onComplete/onError/onUpgrade`), use `Dispatcher1Wrapper`:
243
+
244
+ ```js
245
+ import { Agent, Dispatcher1Wrapper } from 'undici'
246
+
247
+ const legacyCompatibleDispatcher = new Dispatcher1Wrapper(new Agent())
248
+ ```
249
+
215
250
  #### Example 1 - Dispatch GET request
216
251
 
217
252
  ```js
@@ -236,21 +271,21 @@ client.dispatch({
236
271
  'x-foo': 'bar'
237
272
  }
238
273
  }, {
239
- onConnect: () => {
274
+ onRequestStart: () => {
240
275
  console.log('Connected!')
241
276
  },
242
- onError: (error) => {
277
+ onResponseError: (_controller, error) => {
243
278
  console.error(error)
244
279
  },
245
- onHeaders: (statusCode, headers) => {
246
- console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
280
+ onResponseStart: (_controller, statusCode, headers) => {
281
+ console.log(`onResponseStart | statusCode: ${statusCode} | headers: ${JSON.stringify(headers)}`)
247
282
  },
248
- onData: (chunk) => {
249
- console.log('onData: chunk received')
283
+ onResponseData: (_controller, chunk) => {
284
+ console.log('onResponseData: chunk received')
250
285
  data.push(chunk)
251
286
  },
252
- onComplete: (trailers) => {
253
- console.log(`onComplete | trailers: ${trailers}`)
287
+ onResponseEnd: (_controller, trailers) => {
288
+ console.log(`onResponseEnd | trailers: ${JSON.stringify(trailers)}`)
254
289
  const res = Buffer.concat(data).toString('utf8')
255
290
  console.log(`Data: ${res}`)
256
291
  client.close()
@@ -288,15 +323,15 @@ client.dispatch({
288
323
  method: 'GET',
289
324
  upgrade: 'websocket'
290
325
  }, {
291
- onConnect: () => {
292
- console.log('Undici Client - onConnect')
326
+ onRequestStart: () => {
327
+ console.log('Undici Client - onRequestStart')
293
328
  },
294
- onError: (error) => {
295
- console.log('onError') // shouldn't print
329
+ onResponseError: () => {
330
+ console.log('onResponseError') // shouldn't print
296
331
  },
297
- onUpgrade: (statusCode, headers, socket) => {
298
- console.log('Undici Client - onUpgrade')
299
- console.log(`onUpgrade Headers: ${headers}`)
332
+ onRequestUpgrade: (_controller, statusCode, headers, socket) => {
333
+ console.log('Undici Client - onRequestUpgrade')
334
+ console.log(`onRequestUpgrade Headers: ${JSON.stringify(headers)}`)
300
335
  socket.on('data', buffer => {
301
336
  console.log(buffer.toString('utf8'))
302
337
  })
@@ -339,21 +374,21 @@ client.dispatch({
339
374
  },
340
375
  body: JSON.stringify({ message: 'Hello' })
341
376
  }, {
342
- onConnect: () => {
377
+ onRequestStart: () => {
343
378
  console.log('Connected!')
344
379
  },
345
- onError: (error) => {
380
+ onResponseError: (_controller, error) => {
346
381
  console.error(error)
347
382
  },
348
- onHeaders: (statusCode, headers) => {
349
- console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
383
+ onResponseStart: (_controller, statusCode, headers) => {
384
+ console.log(`onResponseStart | statusCode: ${statusCode} | headers: ${JSON.stringify(headers)}`)
350
385
  },
351
- onData: (chunk) => {
352
- console.log('onData: chunk received')
386
+ onResponseData: (_controller, chunk) => {
387
+ console.log('onResponseData: chunk received')
353
388
  data.push(chunk)
354
389
  },
355
- onComplete: (trailers) => {
356
- console.log(`onComplete | trailers: ${trailers}`)
390
+ onResponseEnd: (_controller, trailers) => {
391
+ console.log(`onResponseEnd | trailers: ${JSON.stringify(trailers)}`)
357
392
  const res = Buffer.concat(data).toString('utf8')
358
393
  console.log(`Response Data: ${res}`)
359
394
  client.close()
@@ -498,7 +533,7 @@ The `RequestOptions.method` property should not be value `'CONNECT'`.
498
533
 
499
534
  `body` contains the following additional extensions:
500
535
 
501
- - `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.
536
+ - `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 131072.
502
537
 
503
538
  Note that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`.
504
539
 
@@ -996,7 +1031,7 @@ const client = new Client("http://service.example").compose(
996
1031
  The `dump` interceptor enables you to dump the response body from a request upon a given limit.
997
1032
 
998
1033
  **Options**
999
- - `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the request's body exceeds this value then the connection will be closed. Default: `1048576`.
1034
+ - `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the response's body exceeds this value then the connection will be closed. Default: `1048576`.
1000
1035
 
1001
1036
  > The `Dispatcher#options` also gets extended with the options `dumpMaxSize`, `abortOnDumped`, and `waitForTrailers` which can be used to configure the interceptor at a request-per-request basis.
1002
1037
 
@@ -1319,10 +1354,10 @@ Emitted when dispatcher is no longer busy.
1319
1354
 
1320
1355
  ## Parameter: `UndiciHeaders`
1321
1356
 
1322
- * `Record<string, string | string[] | undefined> | string[] | Iterable<[string, string | string[] | undefined]> | null`
1357
+ * `Record<string, number | string | string[] | undefined> | string[] | Iterable<[string, string | string[] | undefined]> | null`
1323
1358
 
1324
1359
  Header arguments such as `options.headers` in [`Client.dispatch`](/docs/docs/api/Client.md#clientdispatchoptions-handlers) can be specified in three forms:
1325
- * As an object specified by the `Record<string, string | string[] | undefined>` (`IncomingHttpHeaders`) type.
1360
+ * As an object specified by the `Record<string, number | string | string[] | undefined>` (`OutgoingHttpHeaders`) type.
1326
1361
  * As an array of strings. An array representation of a header list must have an even length, or an `InvalidArgumentError` will be thrown.
1327
1362
  * As an iterable that can encompass `Headers`, `Map`, or a custom iterator returning key-value pairs.
1328
1363
  Keys are lowercase and values are not modified.
@@ -1,17 +1,17 @@
1
1
  # Global Installation
2
2
 
3
- Undici provides an `install()` function to add all WHATWG fetch classes to `globalThis`, making them available globally without requiring imports.
3
+ Undici provides an `install()` function to add fetch-related and other web API classes to `globalThis`, making them available globally without requiring imports.
4
4
 
5
5
  ## `install()`
6
6
 
7
- Install all WHATWG fetch classes globally on `globalThis`.
7
+ Install undici's global web APIs on `globalThis`.
8
8
 
9
9
  **Example:**
10
10
 
11
11
  ```js
12
12
  import { install } from 'undici'
13
13
 
14
- // Install all WHATWG fetch classes globally
14
+ // Install undici's global web APIs
15
15
  install()
16
16
 
17
17
  // Now you can use fetch classes globally without importing
@@ -74,6 +74,8 @@ await fetch('https://example.com', {
74
74
 
75
75
  After `install()`, `fetch`, `Headers`, `Response`, `Request`, and `FormData`
76
76
  all come from the installed `undici` package, so they work as a matching set.
77
+ `WebSocket`, `CloseEvent`, `ErrorEvent`, `MessageEvent`, and `EventSource`
78
+ also come from the installed `undici` package.
77
79
 
78
80
  If you do not want to install globals, import both from `undici` instead:
79
81
 
@@ -135,5 +137,5 @@ test('fetch API test', async () => {
135
137
 
136
138
  - The `install()` function overwrites any existing global implementations
137
139
  - Classes installed are undici's implementations, not Node.js built-ins
138
- - This provides access to undici's latest features and performance improvements
139
- - The global installation persists for the lifetime of the process
140
+ - This provides access to undici's latest fetch, WebSocket, and EventSource features and performance improvements
141
+ - The global installation persists for the lifetime of the process
@@ -260,4 +260,4 @@ console.log("requests completed");
260
260
 
261
261
  ### Event: `'error'`
262
262
 
263
- Invoked for users errors such as throwing in the `onError` handler.
263
+ Invoked for user errors such as throwing in the `onResponseError` handler.
@@ -31,57 +31,62 @@ Returns: `RedirectHandler`
31
31
 
32
32
  ### Methods
33
33
 
34
- #### `onConnect(abort)`
34
+ #### `onRequestStart(controller, context)`
35
35
 
36
- Called when the connection is established.
36
+ Called when the request starts.
37
37
 
38
38
  Parameters:
39
39
 
40
- - **abort** `function` - The abort function.
40
+ - **controller** `DispatchController` - The request controller.
41
+ - **context** `object` - The dispatch context.
41
42
 
42
- #### `onUpgrade(statusCode, headers, socket)`
43
+ #### `onRequestUpgrade(controller, statusCode, headers, socket)`
43
44
 
44
45
  Called when an upgrade is requested.
45
46
 
46
47
  Parameters:
47
48
 
49
+ - **controller** `DispatchController` - The request controller.
48
50
  - **statusCode** `number` - The HTTP status code.
49
51
  - **headers** `object` - The headers received in the response.
50
52
  - **socket** `object` - The socket object.
51
53
 
52
- #### `onError(error)`
54
+ #### `onResponseError(controller, error)`
53
55
 
54
56
  Called when an error occurs.
55
57
 
56
58
  Parameters:
57
59
 
60
+ - **controller** `DispatchController` - The request controller.
58
61
  - **error** `Error` - The error that occurred.
59
62
 
60
- #### `onHeaders(statusCode, headers, resume, statusText)`
63
+ #### `onResponseStart(controller, statusCode, headers, statusText)`
61
64
 
62
65
  Called when headers are received.
63
66
 
64
67
  Parameters:
65
68
 
69
+ - **controller** `DispatchController` - The request controller.
66
70
  - **statusCode** `number` - The HTTP status code.
67
71
  - **headers** `object` - The headers received in the response.
68
- - **resume** `function` - The resume function.
69
72
  - **statusText** `string` - The status text.
70
73
 
71
- #### `onData(chunk)`
74
+ #### `onResponseData(controller, chunk)`
72
75
 
73
76
  Called when data is received.
74
77
 
75
78
  Parameters:
76
79
 
80
+ - **controller** `DispatchController` - The request controller.
77
81
  - **chunk** `Buffer` - The data chunk received.
78
82
 
79
- #### `onComplete(trailers)`
83
+ #### `onResponseEnd(controller, trailers)`
80
84
 
81
85
  Called when the request is complete.
82
86
 
83
87
  Parameters:
84
88
 
89
+ - **controller** `DispatchController` - The request controller.
85
90
  - **trailers** `object` - The trailers received.
86
91
 
87
92
  #### `onBodySent(chunk)`
@@ -23,7 +23,6 @@ Returns: `ProxyAgent`
23
23
  - **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
24
24
  - **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
25
25
  - **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
26
- -
27
26
  - **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']`
28
27
  - **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
29
28
  - **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`
@@ -26,7 +26,6 @@ Extends: [`Dispatch.DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dis
26
26
  - **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
27
27
  - **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
28
28
  - **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
29
- -
30
29
  - **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']`
31
30
  - **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
32
31
  - **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']`
@@ -82,17 +81,16 @@ const handler = new RetryHandler(
82
81
  return client.dispatch(...args);
83
82
  },
84
83
  handler: {
85
- onConnect() {},
86
- onBodySent() {},
87
- onHeaders(status, _rawHeaders, resume, _statusMessage) {
84
+ onRequestStart() {},
85
+ onBodySent(chunk) {},
86
+ onResponseStart(_controller, status, headers) {
88
87
  // do something with headers
89
88
  },
90
- onData(chunk) {
89
+ onResponseData(_controller, chunk) {
91
90
  chunks.push(chunk);
92
- return true;
93
91
  },
94
- onComplete() {},
95
- onError() {
92
+ onResponseEnd() {},
93
+ onResponseError(_controller, err) {
96
94
  // handle error properly
97
95
  },
98
96
  },
@@ -107,12 +105,12 @@ const client = new Client(`http://localhost:${server.address().port}`);
107
105
  const handler = new RetryHandler(dispatchOptions, {
108
106
  dispatch: client.dispatch.bind(client),
109
107
  handler: {
110
- onConnect() {},
111
- onBodySent() {},
112
- onHeaders(status, _rawHeaders, resume, _statusMessage) {},
113
- onData(chunk) {},
114
- onComplete() {},
115
- onError(err) {},
108
+ onRequestStart() {},
109
+ onBodySent(chunk) {},
110
+ onResponseStart(_controller, status, headers) {},
111
+ onResponseData(_controller, chunk) {},
112
+ onResponseEnd() {},
113
+ onResponseError(_controller, err) {},
116
114
  },
117
115
  });
118
116
  ```
@@ -27,7 +27,9 @@ new SnapshotAgent([options])
27
27
  - **ignoreHeaders** `Array<String>` - Headers to ignore during request matching
28
28
  - **excludeHeaders** `Array<String>` - Headers to exclude from snapshots (for security)
29
29
  - **matchBody** `Boolean` - Whether to include request body in matching. Default: `true`
30
+ - **normalizeBody** `Function` - Optional function `(body) => string` to normalize the request body before matching (e.g. strip volatile fields like timestamps). Only used when `matchBody` is `true`.
30
31
  - **matchQuery** `Boolean` - Whether to include query parameters in matching. Default: `true`
32
+ - **normalizeQuery** `Function` - Optional function `(query: URLSearchParams) => string` to normalize query parameters before matching (e.g. strip volatile params like cache-busters). Only used when `matchQuery` is `true`.
31
33
  - **caseSensitive** `Boolean` - Whether header matching is case-sensitive. Default: `false`
32
34
  - **shouldRecord** `Function` - Callback to determine if a request should be recorded
33
35
  - **shouldPlayback** `Function` - Callback to determine if a request should be played back
@@ -108,6 +110,27 @@ await agent.saveSnapshots('./custom-snapshots.json')
108
110
 
109
111
  ## Advanced Configuration
110
112
 
113
+ ### Body Matching
114
+
115
+ By default (`matchBody: true`) the full request body string is included in the snapshot key. Set it to `false` to ignore the body entirely, or use `normalizeBody` to strip volatile fields (like timestamps) before matching:
116
+
117
+ ```javascript
118
+ const agent = new SnapshotAgent({
119
+ mode: 'playback',
120
+ snapshotPath: './snapshots.json',
121
+
122
+ // Match on everything except the timestamp field
123
+ normalizeBody: (body) => {
124
+ if (!body) return ''
125
+ const parsed = JSON.parse(String(body))
126
+ delete parsed.timestamp
127
+ return JSON.stringify(parsed)
128
+ }
129
+ })
130
+ ```
131
+
132
+ `normalizeBody` receives the raw body (`string | Buffer | null | undefined`) and must return a `string`. It runs at both record and playback time so the hash is consistent. Two requests match the same snapshot whenever their normalized strings are identical.
133
+
111
134
  ### Header Filtering
112
135
 
113
136
  Control which headers are used for request matching and what gets stored in snapshots: