@mswjs/interceptors 0.41.8 → 0.42.0

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 (335) hide show
  1. package/README.md +274 -228
  2. package/lib/browser/{bufferUtils-BiiO6HZv.mjs → buffer-utils-DJj7YzLG.js} +3 -3
  3. package/lib/browser/buffer-utils-DJj7YzLG.js.map +1 -0
  4. package/lib/browser/create-request-id-DlEd4GOA.js +184 -0
  5. package/lib/browser/create-request-id-DlEd4GOA.js.map +1 -0
  6. package/lib/browser/{getRawRequest-B1BqgWG6.mjs → fetch-utils-zxA_SD66.js} +123 -52
  7. package/lib/browser/fetch-utils-zxA_SD66.js.map +1 -0
  8. package/lib/browser/handle-request-CIOa9O-N.js +167 -0
  9. package/lib/browser/handle-request-CIOa9O-N.js.map +1 -0
  10. package/lib/{node/hasConfigurableGlobal-BiTmog1u.mjs → browser/has-configurable-global-Cew-dYqk.js} +5 -7
  11. package/lib/browser/has-configurable-global-Cew-dYqk.js.map +1 -0
  12. package/lib/browser/http-CB9rQ7i_.d.ts +105 -0
  13. package/lib/browser/{index.d.cts → index.d.ts} +33 -38
  14. package/lib/browser/index.js +130 -0
  15. package/lib/browser/index.js.map +1 -0
  16. package/lib/browser/interceptor-dtJZ_9QT.d.ts +50 -0
  17. package/lib/browser/interceptors/WebSocket/index.d.ts +2 -0
  18. package/lib/browser/interceptors/WebSocket/{index.mjs → index.js} +40 -31
  19. package/lib/browser/interceptors/WebSocket/index.js.map +1 -0
  20. package/lib/browser/interceptors/XMLHttpRequest/web.d.ts +11 -0
  21. package/lib/browser/interceptors/XMLHttpRequest/web.js +2 -0
  22. package/lib/browser/interceptors/fetch/web.d.ts +14 -0
  23. package/lib/browser/interceptors/fetch/web.js +2 -0
  24. package/lib/browser/presets/browser.d.ts +11 -0
  25. package/lib/browser/presets/browser.js +12 -0
  26. package/lib/browser/presets/browser.js.map +1 -0
  27. package/lib/browser/{resolveWebSocketUrl-C83-x9iE.mjs → resolve-web-socket-url-CSvNPLGi.js} +3 -3
  28. package/lib/browser/resolve-web-socket-url-CSvNPLGi.js.map +1 -0
  29. package/lib/browser/web-BjVDkiX0.js +248 -0
  30. package/lib/browser/web-BjVDkiX0.js.map +1 -0
  31. package/lib/{node/XMLHttpRequest-DhupxLHX.mjs → browser/web-C2oKsT3Q.js} +328 -256
  32. package/lib/browser/web-C2oKsT3Q.js.map +1 -0
  33. package/lib/browser/{interceptors/WebSocket/index.d.cts → websocket-CC0nB0md.d.ts} +41 -33
  34. package/lib/node/batch-interceptor-ByEZVih3.js +119 -0
  35. package/lib/node/batch-interceptor-ByEZVih3.js.map +1 -0
  36. package/lib/node/batch-interceptor-CPjFK2td.d.ts +28 -0
  37. package/lib/node/buffer-utils-BvPY1Tc-.js +187 -0
  38. package/lib/node/buffer-utils-BvPY1Tc-.js.map +1 -0
  39. package/lib/node/fetch-utils-Dw1PsmtX.js +568 -0
  40. package/lib/node/fetch-utils-Dw1PsmtX.js.map +1 -0
  41. package/lib/node/has-configurable-global-CJCh_g1I.js +22 -0
  42. package/lib/node/has-configurable-global-CJCh_g1I.js.map +1 -0
  43. package/lib/node/http-CB9rQ7i_.d.ts +105 -0
  44. package/lib/{browser/interceptors/WebSocket/index.d.mts → node/index.d.ts} +120 -60
  45. package/lib/node/{index.mjs → index.js} +8 -11
  46. package/lib/node/index.js.map +1 -0
  47. package/lib/node/interceptor-dtJZ_9QT.d.ts +50 -0
  48. package/lib/node/interceptors/ClientRequest/index.d.ts +14 -0
  49. package/lib/node/interceptors/ClientRequest/index.js +67 -0
  50. package/lib/node/interceptors/ClientRequest/index.js.map +1 -0
  51. package/lib/node/interceptors/XMLHttpRequest/node.d.ts +12 -0
  52. package/lib/node/interceptors/XMLHttpRequest/node.js +74 -0
  53. package/lib/node/interceptors/XMLHttpRequest/node.js.map +1 -0
  54. package/lib/node/interceptors/fetch/node.d.ts +18 -0
  55. package/lib/node/interceptors/fetch/node.js +83 -0
  56. package/lib/node/interceptors/fetch/node.js.map +1 -0
  57. package/lib/node/interceptors/http/index.d.ts +14 -0
  58. package/lib/node/interceptors/http/index.js +32 -0
  59. package/lib/node/interceptors/http/index.js.map +1 -0
  60. package/lib/node/interceptors/net/index.d.ts +203 -0
  61. package/lib/node/interceptors/net/index.js +2 -0
  62. package/lib/node/llhttp/constants.cjs +485 -0
  63. package/lib/node/llhttp/constants.d.cts +957 -0
  64. package/lib/node/llhttp/llhttp.wasm +0 -0
  65. package/lib/node/net-DtMnyEeg.js +1388 -0
  66. package/lib/node/net-DtMnyEeg.js.map +1 -0
  67. package/lib/node/presets/node.d.ts +12 -0
  68. package/lib/node/presets/node.js +17 -0
  69. package/lib/node/presets/node.js.map +1 -0
  70. package/lib/node/{RemoteHttpInterceptor.d.cts → remote-http-interceptor.d.ts} +12 -11
  71. package/lib/node/{RemoteHttpInterceptor.mjs → remote-http-interceptor.js} +38 -33
  72. package/lib/node/remote-http-interceptor.js.map +1 -0
  73. package/lib/node/source-lP0yyEtA.js +1990 -0
  74. package/lib/node/source-lP0yyEtA.js.map +1 -0
  75. package/package.json +79 -118
  76. package/src/batch-interceptor.test.ts +475 -0
  77. package/src/batch-interceptor.ts +244 -0
  78. package/src/{createRequestId.test.ts → create-request-id.test.ts} +1 -2
  79. package/src/disposable.ts +13 -0
  80. package/src/events/http.ts +92 -0
  81. package/src/events/websocket.ts +47 -0
  82. package/src/glossary.ts +1 -27
  83. package/src/index.ts +18 -10
  84. package/src/interceptor.test.ts +279 -0
  85. package/src/interceptor.ts +139 -0
  86. package/src/interceptors/ClientRequest/index.test.ts +21 -39
  87. package/src/interceptors/ClientRequest/index.ts +65 -180
  88. package/src/interceptors/ClientRequest/utils/{recordRawHeaders.test.ts → record-raw-headers.test.ts} +1 -2
  89. package/src/interceptors/ClientRequest/utils/{recordRawHeaders.ts → record-raw-headers.ts} +29 -2
  90. package/src/interceptors/WebSocket/index.ts +34 -51
  91. package/src/interceptors/WebSocket/utils/{bindEvent.test.ts → bind-event.test.ts} +1 -2
  92. package/src/interceptors/WebSocket/utils/events.test.ts +0 -1
  93. package/src/interceptors/WebSocket/{WebSocketClassTransport.ts → web-socket-class-transport.ts} +3 -3
  94. package/src/interceptors/WebSocket/{WebSocketClientConnection.ts → web-socket-client-connection.ts} +4 -4
  95. package/src/interceptors/WebSocket/{WebSocketOverride.ts → web-socket-override.ts} +6 -7
  96. package/src/interceptors/WebSocket/{WebSocketServerConnection.ts → web-socket-server-connection.ts} +4 -4
  97. package/src/interceptors/WebSocket/{WebSocketTransport.ts → web-socket-transport.ts} +1 -1
  98. package/src/interceptors/XMLHttpRequest/node.ts +104 -0
  99. package/src/interceptors/XMLHttpRequest/polyfills/{ProgressEventPolyfill.ts → progress-event-polyfill.ts} +1 -1
  100. package/src/interceptors/XMLHttpRequest/utils/{concateArrayBuffer.test.ts → concat-array-buffer.test.ts} +1 -2
  101. package/src/interceptors/XMLHttpRequest/utils/{concatArrayBuffer.ts → concat-array-buffer.ts} +1 -1
  102. package/src/interceptors/XMLHttpRequest/utils/{createEvent.test.ts → create-event.test.ts} +3 -4
  103. package/src/interceptors/XMLHttpRequest/utils/{createEvent.ts → create-event.ts} +2 -2
  104. package/src/interceptors/XMLHttpRequest/utils/{createResponse.ts → create-response.ts} +1 -1
  105. package/src/interceptors/XMLHttpRequest/utils/{getBodyByteLength.test.ts → get-body-byte-length.test.ts} +1 -2
  106. package/src/interceptors/XMLHttpRequest/web.ts +34 -0
  107. package/src/interceptors/XMLHttpRequest/{XMLHttpRequestController.ts → xml-http-request-controller.ts} +363 -204
  108. package/src/interceptors/XMLHttpRequest/{XMLHttpRequestProxy.ts → xml-http-request-proxy.ts} +28 -25
  109. package/src/interceptors/fetch/node.ts +103 -0
  110. package/src/interceptors/fetch/utils/{followRedirect.ts → follow-redirect.ts} +1 -1
  111. package/src/interceptors/fetch/web.ts +217 -0
  112. package/src/interceptors/http/forward-events.test.ts +86 -0
  113. package/src/interceptors/http/forward-events.ts +139 -0
  114. package/src/interceptors/http/http-parser/index.ts +310 -0
  115. package/src/interceptors/http/http-parser/llhttp/constants.cjs +485 -0
  116. package/src/interceptors/http/http-parser/llhttp/constants.d.cts +957 -0
  117. package/src/interceptors/http/http-parser/llhttp/llhttp.wasm +0 -0
  118. package/src/interceptors/http/http-parser.ts +138 -0
  119. package/src/interceptors/http/index.ts +34 -0
  120. package/src/interceptors/http/source.ts +688 -0
  121. package/src/interceptors/net/index.ts +368 -0
  122. package/src/interceptors/net/socket-controller.ts +1476 -0
  123. package/src/interceptors/net/utils/address-info.ts +54 -0
  124. package/src/interceptors/net/utils/connection-options-to-url.ts +56 -0
  125. package/src/interceptors/net/utils/flush-writes.ts +57 -0
  126. package/src/interceptors/net/utils/get-tls-connect-options.ts +23 -0
  127. package/src/interceptors/net/utils/normalize-net-connect-args.test.ts +233 -0
  128. package/src/interceptors/net/utils/normalize-net-connect-args.ts +121 -0
  129. package/src/interceptors/net/utils/normalize-tls-connect-args.ts +50 -0
  130. package/src/presets/browser.ts +4 -4
  131. package/src/presets/node.ts +4 -4
  132. package/src/{RemoteHttpInterceptor.ts → remote-http-interceptor.ts} +55 -42
  133. package/src/request-context.ts +40 -0
  134. package/src/{RequestController.test.ts → request-controller.test.ts} +37 -3
  135. package/src/{RequestController.ts → request-controller.ts} +33 -11
  136. package/src/utils/{bufferUtils.test.ts → buffer-utils.test.ts} +1 -2
  137. package/src/utils/buffer-utils.ts +37 -0
  138. package/src/utils/{cloneObject.test.ts → clone-object.test.ts} +1 -2
  139. package/src/utils/{cloneObject.ts → clone-object.ts} +8 -8
  140. package/src/utils/{createProxy.test.ts → create-proxy.test.ts} +6 -4
  141. package/src/utils/{createProxy.ts → create-proxy.ts} +1 -1
  142. package/src/utils/{fetchUtils.test.ts → fetch-utils.test.ts} +1 -1
  143. package/src/utils/{fetchUtils.ts → fetch-utils.ts} +28 -3
  144. package/src/utils/{findPropertySource.test.ts → find-property-source.test.ts} +1 -2
  145. package/src/utils/{getCleanUrl.test.ts → get-clean-url.test.ts} +1 -2
  146. package/src/utils/{getUrlByRequestOptions.test.ts → get-url-by-request-options.test.ts} +4 -8
  147. package/src/utils/{getUrlByRequestOptions.ts → get-url-by-request-options.ts} +16 -16
  148. package/src/utils/{getValueBySymbol.test.ts → get-value-by-symbol.test.ts} +1 -2
  149. package/src/utils/{handleRequest.ts → handle-request.ts} +66 -47
  150. package/src/utils/{hasConfigurableGlobal.test.ts → has-configurable-global.test.ts} +1 -2
  151. package/src/utils/{hasConfigurableGlobal.ts → has-configurable-global.ts} +2 -4
  152. package/src/utils/{isObject.test.ts → is-object.test.ts} +1 -2
  153. package/src/utils/logger.test.ts +154 -0
  154. package/src/utils/logger.ts +154 -0
  155. package/src/utils/{parseJson.test.ts → parse-json.test.ts} +1 -2
  156. package/src/utils/{patchesRegistry.test.ts → patches-registry.test.ts} +1 -1
  157. package/src/utils/{patchesRegistry.ts → patches-registry.ts} +1 -1
  158. package/src/utils/{responseUtils.ts → response-utils.ts} +25 -2
  159. package/ClientRequest/package.json +0 -11
  160. package/RemoteHttpInterceptor/package.json +0 -11
  161. package/WebSocket/package.json +0 -12
  162. package/XMLHttpRequest/package.json +0 -12
  163. package/fetch/package.json +0 -12
  164. package/lib/browser/Interceptor-Deczogc8.d.cts +0 -65
  165. package/lib/browser/Interceptor-gqKgs-aF.d.mts +0 -65
  166. package/lib/browser/XMLHttpRequest-B11I9TBx.cjs +0 -751
  167. package/lib/browser/XMLHttpRequest-B11I9TBx.cjs.map +0 -1
  168. package/lib/browser/XMLHttpRequest-DHEkSoRN.mjs +0 -746
  169. package/lib/browser/XMLHttpRequest-DHEkSoRN.mjs.map +0 -1
  170. package/lib/browser/bufferUtils-BiiO6HZv.mjs.map +0 -1
  171. package/lib/browser/bufferUtils-Uc0eRItL.cjs +0 -38
  172. package/lib/browser/bufferUtils-Uc0eRItL.cjs.map +0 -1
  173. package/lib/browser/createRequestId-DOf8Ktjs.cjs +0 -205
  174. package/lib/browser/createRequestId-DOf8Ktjs.cjs.map +0 -1
  175. package/lib/browser/createRequestId-DYCsFHOi.mjs +0 -170
  176. package/lib/browser/createRequestId-DYCsFHOi.mjs.map +0 -1
  177. package/lib/browser/fetch-BJwfdses.mjs +0 -236
  178. package/lib/browser/fetch-BJwfdses.mjs.map +0 -1
  179. package/lib/browser/fetch-M8iXkZ3L.cjs +0 -241
  180. package/lib/browser/fetch-M8iXkZ3L.cjs.map +0 -1
  181. package/lib/browser/getRawRequest-B1BqgWG6.mjs.map +0 -1
  182. package/lib/browser/getRawRequest-DdfaiPVH.cjs +0 -376
  183. package/lib/browser/getRawRequest-DdfaiPVH.cjs.map +0 -1
  184. package/lib/browser/glossary-1lhZx6Aw.d.cts +0 -69
  185. package/lib/browser/glossary-nkHWcwvY.d.mts +0 -69
  186. package/lib/browser/handleRequest-1Yqu_ho_.mjs +0 -178
  187. package/lib/browser/handleRequest-1Yqu_ho_.mjs.map +0 -1
  188. package/lib/browser/handleRequest-Cz4_wmQ9.cjs +0 -189
  189. package/lib/browser/handleRequest-Cz4_wmQ9.cjs.map +0 -1
  190. package/lib/browser/hasConfigurableGlobal-C8zq1MCg.mjs +0 -109
  191. package/lib/browser/hasConfigurableGlobal-C8zq1MCg.mjs.map +0 -1
  192. package/lib/browser/hasConfigurableGlobal-CS7adEvV.cjs +0 -126
  193. package/lib/browser/hasConfigurableGlobal-CS7adEvV.cjs.map +0 -1
  194. package/lib/browser/index.cjs +0 -70
  195. package/lib/browser/index.cjs.map +0 -1
  196. package/lib/browser/index.d.mts +0 -125
  197. package/lib/browser/index.mjs +0 -56
  198. package/lib/browser/index.mjs.map +0 -1
  199. package/lib/browser/interceptors/WebSocket/index.cjs +0 -618
  200. package/lib/browser/interceptors/WebSocket/index.cjs.map +0 -1
  201. package/lib/browser/interceptors/WebSocket/index.mjs.map +0 -1
  202. package/lib/browser/interceptors/XMLHttpRequest/index.cjs +0 -8
  203. package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +0 -15
  204. package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +0 -15
  205. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +0 -8
  206. package/lib/browser/interceptors/fetch/index.cjs +0 -7
  207. package/lib/browser/interceptors/fetch/index.d.cts +0 -13
  208. package/lib/browser/interceptors/fetch/index.d.mts +0 -13
  209. package/lib/browser/interceptors/fetch/index.mjs +0 -7
  210. package/lib/browser/presets/browser.cjs +0 -18
  211. package/lib/browser/presets/browser.cjs.map +0 -1
  212. package/lib/browser/presets/browser.d.cts +0 -12
  213. package/lib/browser/presets/browser.d.mts +0 -14
  214. package/lib/browser/presets/browser.mjs +0 -18
  215. package/lib/browser/presets/browser.mjs.map +0 -1
  216. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs +0 -31
  217. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs.map +0 -1
  218. package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs.map +0 -1
  219. package/lib/node/BatchInterceptor-DdMNfSUY.mjs +0 -44
  220. package/lib/node/BatchInterceptor-DdMNfSUY.mjs.map +0 -1
  221. package/lib/node/BatchInterceptor-KKF8Q47I.d.cts +0 -26
  222. package/lib/node/BatchInterceptor-Z1lwK23r.cjs +0 -49
  223. package/lib/node/BatchInterceptor-Z1lwK23r.cjs.map +0 -1
  224. package/lib/node/BatchInterceptor-rSr-NH_H.d.mts +0 -26
  225. package/lib/node/ClientRequest-DFs7FPNi.cjs +0 -1058
  226. package/lib/node/ClientRequest-DFs7FPNi.cjs.map +0 -1
  227. package/lib/node/ClientRequest-dRpI9v1o.mjs +0 -1049
  228. package/lib/node/ClientRequest-dRpI9v1o.mjs.map +0 -1
  229. package/lib/node/Interceptor-BsZ21ue0.d.cts +0 -132
  230. package/lib/node/Interceptor-soD-WpQO.d.mts +0 -132
  231. package/lib/node/RemoteHttpInterceptor.cjs +0 -155
  232. package/lib/node/RemoteHttpInterceptor.cjs.map +0 -1
  233. package/lib/node/RemoteHttpInterceptor.d.mts +0 -39
  234. package/lib/node/RemoteHttpInterceptor.mjs.map +0 -1
  235. package/lib/node/XMLHttpRequest-DhupxLHX.mjs.map +0 -1
  236. package/lib/node/XMLHttpRequest-Dyi6ahGz.cjs +0 -752
  237. package/lib/node/XMLHttpRequest-Dyi6ahGz.cjs.map +0 -1
  238. package/lib/node/bufferUtils-DxPxwff_.mjs +0 -20
  239. package/lib/node/bufferUtils-DxPxwff_.mjs.map +0 -1
  240. package/lib/node/bufferUtils-S5_-2eN4.cjs +0 -38
  241. package/lib/node/bufferUtils-S5_-2eN4.cjs.map +0 -1
  242. package/lib/node/chunk-CbDLau6x.cjs +0 -34
  243. package/lib/node/fetch-BqMcbuYR.mjs +0 -252
  244. package/lib/node/fetch-BqMcbuYR.mjs.map +0 -1
  245. package/lib/node/fetch-aQJu1Gf_.cjs +0 -259
  246. package/lib/node/fetch-aQJu1Gf_.cjs.map +0 -1
  247. package/lib/node/fetchUtils-BKJ1XmiO.mjs +0 -480
  248. package/lib/node/fetchUtils-BKJ1XmiO.mjs.map +0 -1
  249. package/lib/node/fetchUtils-umV5xXBy.cjs +0 -546
  250. package/lib/node/fetchUtils-umV5xXBy.cjs.map +0 -1
  251. package/lib/node/getRawRequest-B6znU6il.cjs +0 -36
  252. package/lib/node/getRawRequest-B6znU6il.cjs.map +0 -1
  253. package/lib/node/getRawRequest-C2-1urzA.mjs +0 -24
  254. package/lib/node/getRawRequest-C2-1urzA.mjs.map +0 -1
  255. package/lib/node/handleRequest-DCLzePtS.mjs +0 -190
  256. package/lib/node/handleRequest-DCLzePtS.mjs.map +0 -1
  257. package/lib/node/handleRequest-DVOthWJo.cjs +0 -220
  258. package/lib/node/handleRequest-DVOthWJo.cjs.map +0 -1
  259. package/lib/node/hasConfigurableGlobal-BiTmog1u.mjs.map +0 -1
  260. package/lib/node/hasConfigurableGlobal-MjY06_Ok.cjs +0 -108
  261. package/lib/node/hasConfigurableGlobal-MjY06_Ok.cjs.map +0 -1
  262. package/lib/node/index-BJ5E5aW-.d.cts +0 -113
  263. package/lib/node/index-DfoF45ih.d.mts +0 -113
  264. package/lib/node/index.cjs +0 -53
  265. package/lib/node/index.cjs.map +0 -1
  266. package/lib/node/index.d.cts +0 -104
  267. package/lib/node/index.d.mts +0 -104
  268. package/lib/node/index.mjs.map +0 -1
  269. package/lib/node/interceptors/ClientRequest/index.cjs +0 -6
  270. package/lib/node/interceptors/ClientRequest/index.d.cts +0 -2
  271. package/lib/node/interceptors/ClientRequest/index.d.mts +0 -3
  272. package/lib/node/interceptors/ClientRequest/index.mjs +0 -6
  273. package/lib/node/interceptors/XMLHttpRequest/index.cjs +0 -7
  274. package/lib/node/interceptors/XMLHttpRequest/index.d.cts +0 -14
  275. package/lib/node/interceptors/XMLHttpRequest/index.d.mts +0 -14
  276. package/lib/node/interceptors/XMLHttpRequest/index.mjs +0 -7
  277. package/lib/node/interceptors/fetch/index.cjs +0 -6
  278. package/lib/node/interceptors/fetch/index.d.cts +0 -12
  279. package/lib/node/interceptors/fetch/index.d.mts +0 -12
  280. package/lib/node/interceptors/fetch/index.mjs +0 -6
  281. package/lib/node/node-DIKcnzhK.cjs +0 -39
  282. package/lib/node/node-DIKcnzhK.cjs.map +0 -1
  283. package/lib/node/node-lsdNwZEW.mjs +0 -27
  284. package/lib/node/node-lsdNwZEW.mjs.map +0 -1
  285. package/lib/node/presets/node.cjs +0 -23
  286. package/lib/node/presets/node.cjs.map +0 -1
  287. package/lib/node/presets/node.d.cts +0 -13
  288. package/lib/node/presets/node.d.mts +0 -15
  289. package/lib/node/presets/node.mjs +0 -23
  290. package/lib/node/presets/node.mjs.map +0 -1
  291. package/lib/node/utils/node/index.cjs +0 -4
  292. package/lib/node/utils/node/index.d.cts +0 -16
  293. package/lib/node/utils/node/index.d.mts +0 -16
  294. package/lib/node/utils/node/index.mjs +0 -3
  295. package/presets/browser/package.json +0 -5
  296. package/presets/node/package.json +0 -11
  297. package/src/BatchInterceptor.test.ts +0 -255
  298. package/src/BatchInterceptor.ts +0 -95
  299. package/src/Interceptor.test.ts +0 -205
  300. package/src/Interceptor.ts +0 -249
  301. package/src/getRawRequest.ts +0 -21
  302. package/src/interceptors/ClientRequest/MockHttpSocket.ts +0 -724
  303. package/src/interceptors/ClientRequest/agents.ts +0 -110
  304. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +0 -54
  305. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +0 -45
  306. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +0 -427
  307. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +0 -268
  308. package/src/interceptors/ClientRequest/utils/parserUtils.ts +0 -48
  309. package/src/interceptors/Socket/MockSocket.test.ts +0 -264
  310. package/src/interceptors/Socket/MockSocket.ts +0 -58
  311. package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +0 -26
  312. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +0 -52
  313. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +0 -33
  314. package/src/interceptors/XMLHttpRequest/index.ts +0 -40
  315. package/src/interceptors/fetch/index.ts +0 -203
  316. package/src/utils/bufferUtils.ts +0 -22
  317. package/src/utils/canParseUrl.ts +0 -13
  318. package/src/utils/emitAsync.ts +0 -25
  319. package/src/utils/nextTick.ts +0 -11
  320. package/src/utils/node/index.ts +0 -39
  321. /package/src/{createRequestId.ts → create-request-id.ts} +0 -0
  322. /package/src/{InterceptorError.ts → interceptor-error.ts} +0 -0
  323. /package/src/interceptors/WebSocket/utils/{bindEvent.ts → bind-event.ts} +0 -0
  324. /package/src/interceptors/XMLHttpRequest/polyfills/{EventPolyfill.ts → event-polyfill.ts} +0 -0
  325. /package/src/interceptors/XMLHttpRequest/utils/{getBodyByteLength.ts → get-body-byte-length.ts} +0 -0
  326. /package/src/interceptors/XMLHttpRequest/utils/{isDomParserSupportedType.ts → is-dom-parser-supported-type.ts} +0 -0
  327. /package/src/interceptors/fetch/utils/{createNetworkError.ts → create-network-error.ts} +0 -0
  328. /package/src/utils/{findPropertySource.ts → find-property-source.ts} +0 -0
  329. /package/src/utils/{getCleanUrl.ts → get-clean-url.ts} +0 -0
  330. /package/src/utils/{getValueBySymbol.ts → get-value-by-symbol.ts} +0 -0
  331. /package/src/utils/{isNodeLikeError.ts → is-node-like-error.ts} +0 -0
  332. /package/src/utils/{isObject.ts → is-object.ts} +0 -0
  333. /package/src/utils/{isPropertyAccessible.ts → is-property-accessible.ts} +0 -0
  334. /package/src/utils/{parseJson.ts → parse-json.ts} +0 -0
  335. /package/src/utils/{resolveWebSocketUrl.ts → resolve-web-socket-url.ts} +0 -0
package/README.md CHANGED
@@ -1,270 +1,201 @@
1
- [![Latest version](https://img.shields.io/npm/v/@mswjs/interceptors.svg)](https://www.npmjs.com/package/@mswjs/interceptors)
2
-
3
1
  # `@mswjs/interceptors`
4
2
 
5
- Low-level network interception library.
3
+ Low-level network interception library for Node.js.
6
4
 
7
- This library supports intercepting the following protocols:
5
+ Use this library if you wish to intercept any of the below:
8
6
 
9
- - HTTP (via the `http` module, `XMLHttpRequest`, or `globalThis.fetch`);
10
- - [WebSocket](#websocket-interception) (the `WebSocket` class in Undici and in the browser).
7
+ - Raw TCP and TLS socket connections (`net.connect()`, `tls.connect()`);
8
+ - HTTP requests regardless of the request client (e.g. `http.request()`, `axios()`, etc);
9
+ - Fetch requests (both global `fetch()` and custom fetch implementations like `undici()`);
10
+ - WebSocket connections (global `WebSocket` constructor).
11
11
 
12
12
  ## Motivation
13
13
 
14
- While there are a lot of network mocking libraries, they tend to use request interception as an implementation detail, giving you a high-level API that includes request matching, timeouts, recording, and so forth.
15
-
16
- This library is a barebones implementation that provides as little abstraction as possible to execute arbitrary logic upon any request. It's primarily designed as an underlying component for high-level API mocking solutions such as [Mock Service Worker](https://github.com/mswjs/msw).
17
-
18
- ### How is this library different?
19
-
20
- A traditional API mocking implementation in Node.js looks roughly like this:
21
-
22
- ```js
23
- import http from 'node:http'
14
+ There has been a few attempts at the network interception in Node.js throughout its existence. Around 2018, those efforts have settled on patching `http.request()` and `http.ClientRequest`, if not resorting to far worse practices like patching request clients directly. These algorithms turned network requests into black boxes that, effectively, short-circuited the network code at the interception point.
24
15
 
25
- // Store the original request function.
26
- const originalHttpRequest = http.request
16
+ Consider how Node.js orchestrates an average HTTP request:
27
17
 
28
- // Override the request function entirely.
29
- http.request = function (...args) {
30
- // Decide if the outgoing request matches a predicate.
31
- if (predicate(args)) {
32
- // If it does, never create a request, respond to it
33
- // using the mocked response from this blackbox.
34
- return coerceToResponse.bind(this, mock)
35
- }
36
-
37
- // Otherwise, construct the original request
38
- // and perform it as-is.
39
- return originalHttpRequest(...args)
40
- }
18
+ ```
19
+ 1. Third-party request client (axios/got/etc);
20
+ ---- node:http / node:https ----
21
+ 2. http.request() (node:http/node:https);
22
+ 3. new http.ClientRequest();
23
+ ---- node:net / node:tls ----
24
+ 4. net.connect() / tls.connect();
25
+ 5. new net.Socket();
26
+ 6. socket.connect();
27
+ ---- native bindings ----
28
+ 7. TCPWrap / TLSWrap;
29
+ ---- C++ network code ---
30
+ 8. [TOO_COMPLEX_TO_FATHOM];
41
31
  ```
42
32
 
43
- The core philosophy of Interceptors is to _run as much of the underlying network code as possible_. Strange for a network mocking library, isn't it? Turns out, respecting the system's integrity and executing more of the network code leads to more resilient tests and also helps to uncover bugs in the code that would otherwise go unnoticed.
33
+ You can see how intercepting requests at the `http.request()` level (2) is rather limiting as, typically, nothing executes past the interception point. As a result, whenever such interception is introduced, it significantly deviates your system from how it normally behaves otherwise.
44
34
 
45
- Interceptors heavily rely on _class extension_ instead of function and module overrides. By extending the native network code, it can surgically insert the interception and mocking pieces only where necessary, leaving the rest of the system intact.
35
+ So I decided to build a network interception algorithm that would have no such limitations, would execute as much of the Node.js network code as possible, and actually establish network connections (yes, even when mocking requests to non-existing hosts). On top of that, I want that algorithm to be fully available for anybody who wishes to build their own API mocking library.
46
36
 
47
- ```js
48
- class XMLHttpRequestProxy extends XMLHttpRequest {
49
- async send() {
50
- // Call the request listeners and see if any of them
51
- // returns a mocked response for this request.
52
- const mockedResponse = await waitForRequestListeners({ request })
53
-
54
- // If there is a mocked response, use it. This actually
55
- // transitions the XMLHttpRequest instance into the correct
56
- // response state (below is a simplified illustration).
57
- if (mockedResponse) {
58
- // Handle the response headers.
59
- this.request.status = mockedResponse.status
60
- this.request.statusText = mockedResponse.statusText
61
- this.request.responseUrl = mockedResponse.url
62
- this.readyState = 2
63
- this.trigger('readystatechange')
64
-
65
- // Start streaming the response body.
66
- this.trigger('loadstart')
67
- this.readyState = 3
68
- this.trigger('readystatechange')
69
- await streamResponseBody(mockedResponse)
70
-
71
- // Finish the response.
72
- this.trigger('load')
73
- this.trigger('loadend')
74
- this.readyState = 4
75
- return
76
- }
77
-
78
- // Otherwise, perform the original "XMLHttpRequest.prototype.send" call.
79
- return super.send(...args)
80
- }
81
- }
82
- ```
37
+ ### What makes Interceptors different?
83
38
 
84
- > The request interception algorithms differ dramatically based on the request API. Interceptors accommodate for them all, bringing the intercepted requests to a common ground—the Fetch API `Request` instance. The same applies for responses, where a Fetch API `Response` instance is translated to the appropriate response format.
39
+ Interceptors (the library you're reading about) implements the network interception on the TCP/TLS handle level (point 7 on the graph above). In the simplest of terms, it's the lowest possible level to spy on outgoing traffic without having to recompile Node.js on your machine.
85
40
 
86
- This library aims to provide _full specification compliance_ with the APIs and protocols it extends.
41
+ In more technical terms, the algorithm combines multiple entry points along the network graph, each playing its role in the interception:
87
42
 
88
- ## What this library does
43
+ - Spies on the network on the socket level by intercepting `Socket.prototype.connect`, `net.connect()`, and `tls.connect()`;
44
+ - Stubs `TCPWrap`/`TLSWrap` until the connection is either claimed or passed through;
45
+ - Wraps socket-level interception in higher-level interceptors, like `HttpRequestInterceptor`, which pipe outgoing and incoming socket packets through respective parsers;
46
+ - Wraps higher-level interceptors in request client interceptors that leverage `AsyncLocalStorage` to annotate request initiators without intercepting any traffic themselves (a socket connection are unaware of any protocols, let alone request clients that triggered the connection);
89
47
 
90
- This library extends the following native modules:
48
+ Intercepting the network this low on the network graph means executing as much of Node.js network code as physically possible even when mocking requests. This minimizes the deviations introduced by the said interception and yields a more compliant mocking experience.
91
49
 
92
- - `http.get`/`http.request`
93
- - `https.get`/`https.request`
94
- - `XMLHttpRequest`
95
- - `fetch`
96
- - `WebSocket`
50
+ ## When to use Interceptors?
97
51
 
98
- Once extended, it intercepts and normalizes all requests to the Fetch API `Request` instances. This way, no matter the request source (`http.ClientRequest`, `XMLHttpRequest`, `window.Request`, etc), you always get a specification-compliant request instance to work with.
52
+ Interceptors is **not** an API mocking library. It's a low-level network interception library. Mocking the network is just a subset of what you can do with it.
99
53
 
100
- You can respond to the intercepted HTTP request by constructing a Fetch API Response instance. Instead of designing custom abstractions, this library respects the Fetch API specification and takes the responsibility to coerce a single response declaration to the appropriate response formats based on the request-issuing modules (like `http.OutgoingMessage` to respond to `http.ClientRequest`, or updating `XMLHttpRequest` response-related properties).
54
+ As a rule of thumb, if you're uncertain whether you need Interceptors, you likely don't. Interceptors exist primarily to help other developers implement their own higher-level API mocking libraries, like [Nock](https://github.com/nock/nock) or [Mock Service Worker](https://mswjs.io), with the goal of unifying the network interception algorithm for richer features and better runtime compliance.
101
55
 
102
- ## What this library doesn't do
56
+ ## Getting started
103
57
 
104
- - Does **not** provide any request matching logic;
105
- - Does **not** handle requests by default.
58
+ ```bash
59
+ npm i @mswjs/interceptors
60
+ ```
106
61
 
107
- ## Limitations
62
+ ### Debugging
108
63
 
109
- - Interceptors will hang indefinitely if you call `req.end()` in the `connect` event listener of the respective `socket`:
64
+ Enable default interceptor logs with `debug` namespaces:
110
65
 
111
- ```ts
112
- req.on('socket', (socket) => {
113
- socket.on('connect', () => {
114
- // ❌ While this is allowed in Node.js, this cannot be handled in Interceptors.
115
- req.end()
116
- })
117
- })
66
+ ```bash
67
+ DEBUG='interceptors:*' node app.js
118
68
  ```
119
69
 
120
- > This limitation is intrinsic to the interception algorithm used by the library. In order for it to emit the `connect` event on the socket, the library must know if you've handled the request in any way (e.g. responded with a mocked response or errored it). For that, it emits the `request` event on the interceptor where you can handle the request. Since you can consume the request stream in the `request` event, it waits until the request body stream is complete (i.e. until `req.end()` is called). This creates a catch 22 that causes this limitation.
121
-
122
- ## Getting started
70
+ Default logs cover interceptor lifecycle, requests, and request
71
+ resolution. Add verbose logs for socket packets, event forwarding, and other
72
+ internals:
123
73
 
124
74
  ```bash
125
- npm install @mswjs/interceptors
75
+ DEBUG='interceptors:*' DEBUG_LEVEL=verbose node app.js
126
76
  ```
127
77
 
78
+ Scope either level to an interceptor using its lowercase kebab-case name, such
79
+ as `interceptors:fetch`, `interceptors:xhr`, `interceptors:client-request`, or
80
+ `interceptors:websocket`. In browsers, assign the same value to
81
+ `localStorage.debug` and set `localStorage.debugLevel` to `verbose` for verbose
82
+ logs. Each namespace has a stable color.
83
+
128
84
  ## Interceptors
129
85
 
130
86
  To use this library you need to choose one or multiple interceptors to apply. There are different interceptors exported by this library to spy on respective request-issuing modules:
131
87
 
132
- - `ClientRequestInterceptor` to spy on `http.ClientRequest` (`http.get`/`http.request`);
133
- - `XMLHttpRequestInterceptor` to spy on `XMLHttpRequest`;
134
- - `FetchInterceptor` to spy on `fetch`.
88
+ - [`SocketInterceptor`](#socketinterceptor) to spy on any socket connections in Node.js;
89
+ - [`HttpRequestInterceptor`](#httprequestinterceptor) to spy on any HTTP requests in Node.js;
90
+ - [`ClientRequestInterceptor`](#clientrequestinterceptor) to spy on `http.ClientRequest` (`http.get`/`http.request`);
91
+ - [`XMLHttpRequestInterceptor`](#xmlhttprequestinterceptor) to spy on `XMLHttpRequest`;
92
+ - [`FetchInterceptor`](#fetchinterceptor) to spy on the global `fetch`;
93
+ - [`WebSocketInterceptor`](#websocketinterceptor) to spy on WebSocket connections.
94
+
95
+ You can combine multiple interceptors using [`BatchInterceptor`](#batchinterceptor).
96
+
97
+ ### `SocketInterceptor`
135
98
 
136
- Use an interceptor by constructing it and attaching request/response listeners:
99
+ The lowest-level interceptor in this library. It intercepts _every outgoing TCP and TLS connection_ in Node.js at the `net.Socket` level, no matter which module or third-party package creates it. It is the foundation the HTTP interceptors below are built upon.
137
100
 
138
101
  ```js
139
- import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest'
102
+ import { SocketInterceptor } from '@mswjs/interceptors/net'
140
103
 
141
- const interceptor = new ClientRequestInterceptor()
104
+ const interceptor = new SocketInterceptor()
142
105
 
143
- // Enable the interception of requests.
144
- interceptor.apply()
106
+ interceptor.on('connection', ({ socket, connectionOptions, controller }) => {
107
+ if (connectionOptions.host === 'example.com') {
108
+ controller.claim()
145
109
 
146
- // Listen to any "http.ClientRequest" being dispatched,
147
- // and log its method and full URL.
148
- interceptor.on('request', ({ request, requestId }) => {
149
- console.log(request.method, request.url)
110
+ socket.on('data', (chunk) => {
111
+ socket.write(anotherChunk)
112
+ })
113
+ }
150
114
  })
151
115
 
152
- // Listen to any responses sent to "http.ClientRequest".
153
- // Note that this listener is read-only and cannot affect responses.
154
- interceptor.on(
155
- 'response',
156
- ({ response, isMockedResponse, request, requestId }) => {
157
- console.log('response to %s %s was:', request.method, request.url, response)
158
- }
159
- )
116
+ interceptor.apply()
160
117
  ```
161
118
 
162
- All HTTP request interceptors implement the same events:
119
+ > The exposed `socket` instance is _mirrored_ so you can think of the connection listener as a server-side handler. It emits `data` when the client _writes_ to it and writing to it will emit `data` events on the intercepted socket.
163
120
 
164
- - `request`, emitted whenever a request has been dispatched;
165
- - `response`, emitted whenever any request receives a response.
121
+ The `connection` event is emitted whenever a socket connection is open in this process. Use its listener to inspect and it decide whether you want to claim it for manual management (`controller.claim()`) or let it pass through (`controller.passthrough()`). Until you decide either, the connection will remain in the pending state.
166
122
 
167
- ### Using multiple interceptors
123
+ ### `HttpRequestInterceptor`
168
124
 
169
- You can combine multiple interceptors to capture requests from different request-issuing modules at once.
125
+ Intercepts **all HTTP requests in Node.js, regardless of the client** that issued them. Because the interception happens at the socket level, this includes `http`/`https` modules, the global `fetch`, direct Undici usage (`fetch`, `request`, pools, agents), and any third-party HTTP client built on top of them (Axios, Got, node-fetch, superagent, etc).
170
126
 
171
127
  ```js
172
- import { BatchInterceptor } from '@mswjs/interceptors'
173
- import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest'
174
- import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest'
175
-
176
- const interceptor = new BatchInterceptor({
177
- name: 'my-interceptor',
178
- interceptors: [
179
- new ClientRequestInterceptor(),
180
- new XMLHttpRequestInterceptor(),
181
- ],
182
- })
128
+ import { HttpRequestInterceptor } from '@mswjs/interceptors/http'
183
129
 
130
+ const interceptor = new HttpRequestInterceptor()
184
131
  interceptor.apply()
185
-
186
- // This "request" listener will be called on both
187
- // "http.ClientRequest" and "XMLHttpRequest" being dispatched.
188
- interceptor.on('request', listener)
189
132
  ```
190
133
 
191
- > Note that you can use [pre-defined presets](#presets) that cover all the request sources for a given environment type.
192
-
193
- ## Presets
194
-
195
- When using [`BatchInterceptor`](#batchinterceptor), you can provide a pre-defined preset to its "interceptors" option to capture all request for that environment.
134
+ #### Observing requests
196
135
 
197
- ### Node.js preset
136
+ Add a listener to the `request` event to observe outgoing HTTP requests. The listener exposes the intercepted request as a [Fetch API `Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) instance.
198
137
 
199
- This preset combines `ClientRequestInterceptor`, `XMLHttpRequestInterceptor` and is meant to be used in Node.js.
138
+ > There are many ways to describe a request in Node.js but this library coerces different request definitions to a single specification-compliant `Request` instance to make the handling consistent.
200
139
 
201
140
  ```js
202
- import { BatchInterceptor } from '@mswjs/interceptors'
203
- import nodeInterceptors from '@mswjs/interceptors/presets/node'
204
-
205
- const interceptor = new BatchInterceptor({
206
- name: 'my-interceptor',
207
- interceptors: nodeInterceptors,
141
+ interceptor.on('request', ({ request, requestId }) => {
142
+ console.log(request.method, request.url)
208
143
  })
209
-
210
- interceptor.apply()
211
-
212
- interceptor.on('request', listener)
213
144
  ```
214
145
 
215
- ### Browser preset
216
-
217
- This preset combines `XMLHttpRequestInterceptor` and `FetchInterceptor` and is meant to be used in a browser.
146
+ Since the exposed `request` instance implements the Fetch API specification, you can operate with it just as you do with the regular browser request. For example, this is how you would read the request body as JSON:
218
147
 
219
148
  ```js
220
- import { BatchInterceptor } from '@mswjs/interceptors'
221
- import browserInterceptors from '@mswjs/interceptors/presets/browser'
222
-
223
- const interceptor = new BatchInterceptor({
224
- name: 'my-interceptor',
225
- interceptors: browserInterceptors,
149
+ interceptor.on('request', async ({ request, requestId }) => {
150
+ const json = await request.clone().json()
226
151
  })
227
-
228
- interceptor.on('request', listener)
229
152
  ```
230
153
 
231
- ## Introspecting requests
154
+ > Make sure to clone the request before reading its body.
232
155
 
233
- All HTTP request interceptors emit a "request" event. In the listener to this event, they expose a `request` reference, which is a [Fetch API Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) instance.
156
+ ##### Request initiator
234
157
 
235
- > There are many ways to describe a request in Node.js but this library coerces different request definitions to a single specification-compliant `Request` instance to make the handling consistent.
158
+ The `request` event exposes an `initiator` property that references the object that issued the intercepted request:
236
159
 
237
- ```js
238
- interceptor.on('request', ({ request, requestId, controller }) => {
239
- console.log(request.method, request.url)
240
- })
241
- ```
160
+ - an `http.ClientRequest` instance for requests made via the `http`/`https` modules;
161
+ - a Fetch API `Request` instance for requests made via the global `fetch`;
162
+ - an `XMLHttpRequest` instance for requests made via `XMLHttpRequest`;
163
+ - a `net.Socket` instance for requests that cannot be attributed to a known client (e.g. raw socket connections or direct Undici usage).
242
164
 
243
- Since the exposed `request` instance implements the Fetch API specification, you can operate with it just as you do with the regular browser request. For example, this is how you would read the request body as JSON:
165
+ > Attributing a request to its client requires the corresponding client-level interceptor ([`ClientRequestInterceptor`](#clientrequestinterceptor), [`FetchInterceptor`](#fetchinterceptor), or [`XMLHttpRequestInterceptor`](#xmlhttprequestinterceptor)) to be applied alongside `HttpRequestInterceptor`. With `HttpRequestInterceptor` alone, the initiator is the underlying `net.Socket`.
166
+
167
+ The initiator is typed as `unknown`. Narrow it down with `instanceof` to access the client-specific state, e.g. to tell the requests from different clients apart:
244
168
 
245
169
  ```js
246
- interceptor.on('request', async ({ request, requestId }) => {
247
- const json = await request.clone().json()
170
+ import http from 'node:http'
171
+
172
+ interceptor.on('request', ({ request, initiator }) => {
173
+ if (initiator instanceof http.ClientRequest) {
174
+ // This request was made via "http.request()"/"http.get()".
175
+ console.log(initiator.getHeaders())
176
+ }
177
+
178
+ if (initiator instanceof Request) {
179
+ // This request was made via the global "fetch".
180
+ }
248
181
  })
249
182
  ```
250
183
 
251
- > **Do not forget to clone the request before reading its body!**
184
+ #### Modifying outgoing requests
252
185
 
253
- ## Modifying requests
254
-
255
- Request representations are readonly. You can, however, mutate the intercepted request's headers in the "request" listener:
186
+ Request representations are readonly. You can, however, mutate the intercepted request's headers in the `request` listener. The modified headers are sent to the actual server if the request is performed as-is:
256
187
 
257
188
  ```js
258
189
  interceptor.on('request', ({ request }) => {
259
- request.headers.set('X-My-Header', 'true')
190
+ request.headers.set('x-my-header', 'true')
260
191
  })
261
192
  ```
262
193
 
263
194
  > This restriction is done so that the library wouldn't have to unnecessarily synchronize the actual request instance and its Fetch API request representation. As of now, this library is not meant to be used as a full-scale proxy.
264
195
 
265
- ## Mocking responses
196
+ #### Mocking responses
266
197
 
267
- Although this library can be used purely for request introspection purposes, you can also affect request resolution by responding to any intercepted request within the "request" event.
198
+ Although this library can be used purely for observing the network, you can also affect request resolution by responding to any intercepted request within the `request` event.
268
199
 
269
200
  Access the `controller` object from the request event listener arguments and call its `controller.respondWith()` method, providing it with a mocked `Response` instance:
270
201
 
@@ -290,22 +221,22 @@ interceptor.on('request', ({ request, controller }) => {
290
221
 
291
222
  > We use Fetch API `Response` class as the middle-ground for mocked response definition. This library then coerces the response instance to the appropriate response format (e.g. to `http.OutgoingMessage` in the case of `http.ClientRequest`).
292
223
 
293
- **The `Response` class is built-in in since Node.js 18. Use a Fetch API-compatible polyfill, like `node-fetch`, for older versions of Node.js.`**
294
-
295
224
  Note that a single request _can only be handled once_. You may want to introduce conditional logic, like routing, in your request listener but it's generally advised to use a higher-level library like [Mock Service Worker](https://github.com/mswjs/msw) that does request matching for you.
296
225
 
297
226
  Requests must be responded to within the same tick as the request listener. This means you cannot respond to a request using `setTimeout`, as this will delegate the callback to the next tick. If you wish to introduce asynchronous side-effects in the listener, consider making it an `async` function, awaiting any side-effects you need.
298
227
 
299
228
  ```js
229
+ import { setTimeout } from 'node:timers/promises'
230
+
300
231
  // Respond to all requests with a 500 response
301
232
  // delayed by 500ms.
302
233
  interceptor.on('request', async ({ controller }) => {
303
- await sleep(500)
234
+ await setTimeout(500)
304
235
  controller.respondWith(new Response(null, { status: 500 }))
305
236
  })
306
237
  ```
307
238
 
308
- ### Mocking response errors
239
+ ##### Mocking response errors
309
240
 
310
241
  You can provide an instance of `Response.error()` to error the pending request.
311
242
 
@@ -319,7 +250,7 @@ This will automatically translate to the appropriate request error based on the
319
250
 
320
251
  > Note that the standard `Response.error()` API does not accept an error message.
321
252
 
322
- ## Mocking errors
253
+ ##### Mocking errors
323
254
 
324
255
  Use the `controller.errorWith()` method to error the request.
325
256
 
@@ -333,22 +264,7 @@ Unlike responding with `Response.error()`, you can provide an exact error reason
333
264
 
334
265
  > Note that it is up to the request client to respect your custom error. Some clients, like `ClientRequest` will use the provided error message, while others, like `fetch`, will produce a generic `TypeError: failed to fetch` responses. Interceptors will try to preserve the original error in the `cause` property of such generic errors.
335
266
 
336
- ## Observing responses
337
-
338
- You can use the "response" event to transparently observe any incoming responses in your Node.js process.
339
-
340
- ```js
341
- interceptor.on(
342
- 'response',
343
- ({ response, isMockedResponse, request, requestId }) => {
344
- // react to the incoming response...
345
- }
346
- )
347
- ```
348
-
349
- > Note that the `isMockedResponse` property will only be set to `true` if you resolved this request in the "request" event listener using the `controller.respondWith()` method and providing a mocked `Response` instance.
350
-
351
- ## Error handling
267
+ ##### Handling exceptions
352
268
 
353
269
  By default, all unhandled exceptions thrown within the `request` listener are coerced to 500 error responses, emulating those exceptions occurring on the actual server. You can listen to the exceptions by adding the `unhandledException` listener to the interceptor:
354
270
 
@@ -376,34 +292,120 @@ interceptor.on('unhandledException', ({ error }) => {
376
292
  })
377
293
  ```
378
294
 
379
- ## WebSocket interception
295
+ #### Observing responses
296
+
297
+ You can use the `response` event to transparently observe any incoming responses in your Node.js process.
298
+
299
+ ```js
300
+ interceptor.on(
301
+ 'response',
302
+ ({ response, responseType, request, requestId }) => {
303
+ // react to the incoming response...
304
+ }
305
+ )
306
+ ```
307
+
308
+ > Note that the `responseType` property equals `"mock"` if you resolved this request in the `request` event listener using the `controller.respondWith()` method, and `"original"` for the responses received from the actual server.
309
+
310
+ ### `ClientRequestInterceptor`
311
+
312
+ Intercepts HTTP requests made via `http.ClientRequest`—that is, `http.get()`/`http.request()` and their `https` counterparts (this also covers third-party clients built on top of them, like Axios or Got).
313
+
314
+ ```js
315
+ import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest'
316
+
317
+ const interceptor = new ClientRequestInterceptor()
318
+ interceptor.apply()
319
+
320
+ interceptor.on('request', ({ request, controller }) => {
321
+ console.log(request.method, request.url)
322
+ controller.respondWith(new Response('Hello world'))
323
+ })
324
+ ```
325
+
326
+ This interceptor implements the same events as [`HttpRequestInterceptor`](#httprequestinterceptor)—`request`, `response`, and `unhandledException`—and you subscribe to them in the same way. See the sections above for observing, modifying, and mocking requests.
380
327
 
381
- You can intercept a WebSocket communication using the `WebSocketInterceptor` class.
328
+ ### `XMLHttpRequestInterceptor`
329
+
330
+ Intercepts HTTP requests made via `XMLHttpRequest`, both in the browser and in Node.js (e.g. in test environments polyfilling `XMLHttpRequest`, like JSDOM).
331
+
332
+ ```js
333
+ import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest'
334
+
335
+ const interceptor = new XMLHttpRequestInterceptor()
336
+ interceptor.apply()
337
+
338
+ interceptor.on('request', ({ request, controller }) => {
339
+ console.log(request.method, request.url)
340
+ controller.respondWith(new Response('Hello world'))
341
+ })
342
+ ```
343
+
344
+ This interceptor implements the same events as [`HttpRequestInterceptor`](#httprequestinterceptor)—`request`, `response`, and `unhandledException`—and you subscribe to them in the same way.
345
+
346
+ This interceptor has two versions: `/node` and `/web`. The `@mswjs/interceptors/XMLHttpRequest` import automatically loads the correct one based on your environment. If you wish, you can import the exact version manually:
347
+
348
+ ```js
349
+ import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest/node'
350
+ import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest/web'
351
+ ```
352
+
353
+ ### `FetchInterceptor`
354
+
355
+ Intercepts HTTP requests made via the global `fetch` function. In Node.js, the global `fetch` is powered by Undici; in the browser, it is the native `window.fetch`.
356
+
357
+ > To intercept the requests made via _direct_ Undici imports (e.g. `fetch` or `request` from the `undici` package), use the [`HttpRequestInterceptor`](#httprequestinterceptor) instead—those requests do not go through the global `fetch` but are still intercepted at the socket level.
358
+
359
+ ```js
360
+ import { FetchInterceptor } from '@mswjs/interceptors/fetch'
361
+
362
+ const interceptor = new FetchInterceptor()
363
+ interceptor.apply()
364
+
365
+ interceptor.on('request', ({ request, controller }) => {
366
+ console.log(request.method, request.url)
367
+ controller.respondWith(new Response('Hello world'))
368
+ })
369
+ ```
370
+
371
+ This interceptor implements the same events as [`HttpRequestInterceptor`](#httprequestinterceptor)—`request`, `response`, and `unhandledException`—and you subscribe to them in the same way.
372
+
373
+ This interceptor has two versions: `/node` and `/web`. The `@mswjs/interceptors/fetch` import automatically loads the correct one based on your environment. If you wish, you can import the exact version manually:
374
+
375
+ ```js
376
+ import { FetchInterceptor } from '@mswjs/interceptors/fetch/node'
377
+ import { FetchInterceptor } from '@mswjs/interceptors/fetch/web'
378
+ ```
379
+
380
+ ### `WebSocketInterceptor`
381
+
382
+ Intercepts WebSocket connections created using the global WHATWG `WebSocket` class.
382
383
 
383
384
  > [!IMPORTANT]
384
- > This library only supports intercepting WebSocket connections created using the global WHATWG `WebSocket` class. Third-party transports, such as HTTP/XHR polling, are not supported by design due to their contrived nature.
385
+ > The `WebSocketInterceptor` provides its connection-level API only for the global WHATWG `WebSocket` class. In Node.js, WebSocket handshakes issued by other clients (e.g. the `ws` package or direct Undici usage) are additionally interceptable at the HTTP layer as `Upgrade` requests via the [`HttpRequestInterceptor`](#httprequestinterceptor). Polling transports (HTTP/XHR long-polling) surface as regular HTTP requests, not as WebSocket connections.
385
386
 
386
387
  ```js
387
388
  import { WebSocketInterceptor } from '@mswjs/interceptors/WebSocket'
388
389
 
389
390
  const interceptor = new WebSocketInterceptor()
391
+ interceptor.apply()
390
392
  ```
391
393
 
392
- Unlike the HTTP-based interceptors that share the same `request`/`response` events, the WebSocket interceptor only emits the `connection` event and let's you handle the incoming/outgoing events in its listener.
394
+ Unlike the HTTP-based interceptors that share the same `request`/`response` events, the WebSocket interceptor only emits the `connection` event and lets you handle the incoming/outgoing events in its listener.
393
395
 
394
- ### Important defaults
396
+ #### Important defaults
395
397
 
396
398
  1. Intercepted WebSocket connections are _not opened_. To open the actual WebSocket connection, call [`server.connect()`](#connect) in the interceptor.
397
399
  1. Once connected to the actual server, the outgoing client events are _forwarded to that server by default_. If you wish to prevent a client message from reaching the server, call `event.preventDefault()` for that client message event.
398
400
  1. Once connected to the actual server, the incoming server events are _forwarded to the client by default_. If you wish to prevent a server message from reaching the client, call `event.preventDefault()` for the server message event.
399
401
  1. Once connected to the actual server, the `close` event received from that server is _forwarded to the client by default_. If you wish to prevent that, call `event.preventDefault()` for that close event of the server.
400
402
 
401
- ### WebSocket connection
403
+ #### Observing connections
402
404
 
403
405
  Whenever a WebSocket instance is constructed, the `connection` event is emitted on the WebSocket interceptor.
404
406
 
405
407
  ```js
406
- intereceptor.on('connection', ({ client }) => {
408
+ interceptor.on('connection', ({ client }) => {
407
409
  console.log(client.url)
408
410
  })
409
411
  ```
@@ -416,9 +418,9 @@ The `connection` event exposes the following arguments:
416
418
  | `server` | [`WebSocketServerConnection`](#websocketserverconnection) | An object representing the original WebSocket server connection. |
417
419
  | `info` | `object` | Additional WebSocket connection information (like the original client `protocols`). |
418
420
 
419
- ### `WebSocketClientConnection`
421
+ #### `WebSocketClientConnection`
420
422
 
421
- #### `.addEventListener(type, listener)`
423
+ ##### `.addEventListener(type, listener)`
422
424
 
423
425
  - `type`, `string`
424
426
  - `listener`, `EventListener`
@@ -441,14 +443,14 @@ client.addEventListener('message', (event) => {
441
443
  })
442
444
  ```
443
445
 
444
- #### `.removeEventListener(type, listener)`
446
+ ##### `.removeEventListener(type, listener)`
445
447
 
446
448
  - `type`, `string`
447
449
  - `listener`, `EventListener`
448
450
 
449
451
  Removes the listener for the given event type.
450
452
 
451
- #### `.send(data)`
453
+ ##### `.send(data)`
452
454
 
453
455
  - `data`, `string | Blob | ArrayBuffer`
454
456
 
@@ -460,7 +462,7 @@ client.send(new Blob(['blob']))
460
462
  client.send(new TextEncoder().encode('array buffer'))
461
463
  ```
462
464
 
463
- #### `.close(code, reason)`
465
+ ##### `.close(code, reason)`
464
466
 
465
467
  - `code`, close [status code](https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1).
466
468
  - `reason`, [close reason](https://www.rfc-editor.org/rfc/rfc6455#section-7.1.6).
@@ -479,13 +481,13 @@ client.close()
479
481
  client.close(1003)
480
482
  ```
481
483
 
482
- ### `WebSocketServerConnection`
484
+ #### `WebSocketServerConnection`
483
485
 
484
- #### `.connect()`
486
+ ##### `.connect()`
485
487
 
486
488
  Establishes the connection to the original WebSocket server. Connection cannot be awaited. Any data sent via `server.send()` while connecting is buffered and flushed once the connection is open.
487
489
 
488
- #### `.addEventListener(type, listener)`
490
+ ##### `.addEventListener(type, listener)`
489
491
 
490
492
  - `type`, `string`
491
493
  - `listener`, `EventListener`
@@ -511,14 +513,14 @@ server.addEventListener('message', (event) => {
511
513
  })
512
514
  ```
513
515
 
514
- #### `.removeEventListener(type, listener)`
516
+ ##### `.removeEventListener(type, listener)`
515
517
 
516
518
  - `type`, `string`
517
519
  - `listener`, `EventListener`
518
520
 
519
521
  Removes the listener for the given event type.
520
522
 
521
- #### `.send(data)`
523
+ ##### `.send(data)`
522
524
 
523
525
  - `data`, `string | Blob | ArrayBuffer`
524
526
 
@@ -530,7 +532,7 @@ client.addEventListener('message', (event) => {
530
532
  })
531
533
  ```
532
534
 
533
- #### `.close()`
535
+ ##### `.close()`
534
536
 
535
537
  Closes the connection with the original WebSocket server. Unlike `client.close()`, closing the server connection does not accept any arguments and always assumes a graceful closure. Sending data via `server.send()` after the connection has been closed will have no effect.
536
538
 
@@ -560,7 +562,36 @@ class Interceptor {
560
562
 
561
563
  ### `BatchInterceptor`
562
564
 
563
- Applies multiple request interceptors at the same time.
565
+ Applies multiple request interceptors at the same time. Use it to combine interceptors to capture requests from different request-issuing modules at once.
566
+
567
+ ```js
568
+ import { BatchInterceptor } from '@mswjs/interceptors'
569
+ import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest'
570
+ import { FetchInterceptor } from '@mswjs/interceptors/fetch'
571
+
572
+ const interceptor = new BatchInterceptor({
573
+ name: 'my-interceptor',
574
+ interceptors: [
575
+ new XMLHttpRequestInterceptor(),
576
+ new FetchInterceptor(),
577
+ ],
578
+ })
579
+
580
+ interceptor.apply()
581
+
582
+ // Spy on both XMLHttpRequest and fetch requests in this process.
583
+ interceptor.on('request', listener)
584
+ ```
585
+
586
+ Instead of listing the interceptors manually, you can provide one of the pre-defined presets to the `interceptors` option to capture all requests for that environment:
587
+
588
+ #### Node.js preset
589
+
590
+ This preset combines the following interceptors:
591
+
592
+ - `ClientRequestInterceptor`
593
+ - `XMLHttpRequestInterceptor`
594
+ - `FetchInterceptor`
564
595
 
565
596
  ```js
566
597
  import { BatchInterceptor } from '@mswjs/interceptors'
@@ -571,15 +602,31 @@ const interceptor = new BatchInterceptor({
571
602
  interceptors: nodeInterceptors,
572
603
  })
573
604
 
605
+ interceptor.on('request', listener)
606
+
574
607
  interceptor.apply()
608
+ ```
575
609
 
576
- interceptor.on('request', ({ request, requestId }) => {
577
- // Inspect the intercepted "request".
578
- // Optionally, return a mocked response.
610
+ #### Browser preset
611
+
612
+ This preset combines the following interceptors:
613
+
614
+ - `XMLHttpRequestInterceptor`
615
+ - `FetchInterceptor`
616
+
617
+ ```js
618
+ import { BatchInterceptor } from '@mswjs/interceptors'
619
+ import browserInterceptors from '@mswjs/interceptors/presets/browser'
620
+
621
+ const interceptor = new BatchInterceptor({
622
+ name: 'my-interceptor',
623
+ interceptors: browserInterceptors,
579
624
  })
580
- ```
581
625
 
582
- > Using the `/presets/node` interceptors preset is the recommended way to ensure all requests get intercepted, regardless of their origin.
626
+ interceptor.on('request', listener)
627
+
628
+ interceptor.apply()
629
+ ```
583
630
 
584
631
  ### `RemoteHttpInterceptor`
585
632
 
@@ -591,7 +638,6 @@ import { RemoteHttpInterceptor } from '@mswjs/interceptors/RemoteHttpInterceptor
591
638
  import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest'
592
639
 
593
640
  const interceptor = new RemoteHttpInterceptor({
594
- // Alternatively, you can use presets.
595
641
  interceptors: [new ClientRequestInterceptor()],
596
642
  })
597
643