@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.
- package/README.md +274 -228
- package/lib/browser/{bufferUtils-BiiO6HZv.mjs → buffer-utils-DJj7YzLG.js} +3 -3
- package/lib/browser/buffer-utils-DJj7YzLG.js.map +1 -0
- package/lib/browser/create-request-id-DlEd4GOA.js +184 -0
- package/lib/browser/create-request-id-DlEd4GOA.js.map +1 -0
- package/lib/browser/{getRawRequest-B1BqgWG6.mjs → fetch-utils-zxA_SD66.js} +123 -52
- package/lib/browser/fetch-utils-zxA_SD66.js.map +1 -0
- package/lib/browser/handle-request-CIOa9O-N.js +167 -0
- package/lib/browser/handle-request-CIOa9O-N.js.map +1 -0
- package/lib/{node/hasConfigurableGlobal-BiTmog1u.mjs → browser/has-configurable-global-Cew-dYqk.js} +5 -7
- package/lib/browser/has-configurable-global-Cew-dYqk.js.map +1 -0
- package/lib/browser/http-CB9rQ7i_.d.ts +105 -0
- package/lib/browser/{index.d.cts → index.d.ts} +33 -38
- package/lib/browser/index.js +130 -0
- package/lib/browser/index.js.map +1 -0
- package/lib/browser/interceptor-dtJZ_9QT.d.ts +50 -0
- package/lib/browser/interceptors/WebSocket/index.d.ts +2 -0
- package/lib/browser/interceptors/WebSocket/{index.mjs → index.js} +40 -31
- package/lib/browser/interceptors/WebSocket/index.js.map +1 -0
- package/lib/browser/interceptors/XMLHttpRequest/web.d.ts +11 -0
- package/lib/browser/interceptors/XMLHttpRequest/web.js +2 -0
- package/lib/browser/interceptors/fetch/web.d.ts +14 -0
- package/lib/browser/interceptors/fetch/web.js +2 -0
- package/lib/browser/presets/browser.d.ts +11 -0
- package/lib/browser/presets/browser.js +12 -0
- package/lib/browser/presets/browser.js.map +1 -0
- package/lib/browser/{resolveWebSocketUrl-C83-x9iE.mjs → resolve-web-socket-url-CSvNPLGi.js} +3 -3
- package/lib/browser/resolve-web-socket-url-CSvNPLGi.js.map +1 -0
- package/lib/browser/web-BjVDkiX0.js +248 -0
- package/lib/browser/web-BjVDkiX0.js.map +1 -0
- package/lib/{node/XMLHttpRequest-DhupxLHX.mjs → browser/web-C2oKsT3Q.js} +328 -256
- package/lib/browser/web-C2oKsT3Q.js.map +1 -0
- package/lib/browser/{interceptors/WebSocket/index.d.cts → websocket-CC0nB0md.d.ts} +41 -33
- package/lib/node/batch-interceptor-ByEZVih3.js +119 -0
- package/lib/node/batch-interceptor-ByEZVih3.js.map +1 -0
- package/lib/node/batch-interceptor-CPjFK2td.d.ts +28 -0
- package/lib/node/buffer-utils-BvPY1Tc-.js +187 -0
- package/lib/node/buffer-utils-BvPY1Tc-.js.map +1 -0
- package/lib/node/fetch-utils-Dw1PsmtX.js +568 -0
- package/lib/node/fetch-utils-Dw1PsmtX.js.map +1 -0
- package/lib/node/has-configurable-global-CJCh_g1I.js +22 -0
- package/lib/node/has-configurable-global-CJCh_g1I.js.map +1 -0
- package/lib/node/http-CB9rQ7i_.d.ts +105 -0
- package/lib/{browser/interceptors/WebSocket/index.d.mts → node/index.d.ts} +120 -60
- package/lib/node/{index.mjs → index.js} +8 -11
- package/lib/node/index.js.map +1 -0
- package/lib/node/interceptor-dtJZ_9QT.d.ts +50 -0
- package/lib/node/interceptors/ClientRequest/index.d.ts +14 -0
- package/lib/node/interceptors/ClientRequest/index.js +67 -0
- package/lib/node/interceptors/ClientRequest/index.js.map +1 -0
- package/lib/node/interceptors/XMLHttpRequest/node.d.ts +12 -0
- package/lib/node/interceptors/XMLHttpRequest/node.js +74 -0
- package/lib/node/interceptors/XMLHttpRequest/node.js.map +1 -0
- package/lib/node/interceptors/fetch/node.d.ts +18 -0
- package/lib/node/interceptors/fetch/node.js +83 -0
- package/lib/node/interceptors/fetch/node.js.map +1 -0
- package/lib/node/interceptors/http/index.d.ts +14 -0
- package/lib/node/interceptors/http/index.js +32 -0
- package/lib/node/interceptors/http/index.js.map +1 -0
- package/lib/node/interceptors/net/index.d.ts +203 -0
- package/lib/node/interceptors/net/index.js +2 -0
- package/lib/node/llhttp/constants.cjs +485 -0
- package/lib/node/llhttp/constants.d.cts +957 -0
- package/lib/node/llhttp/llhttp.wasm +0 -0
- package/lib/node/net-DtMnyEeg.js +1388 -0
- package/lib/node/net-DtMnyEeg.js.map +1 -0
- package/lib/node/presets/node.d.ts +12 -0
- package/lib/node/presets/node.js +17 -0
- package/lib/node/presets/node.js.map +1 -0
- package/lib/node/{RemoteHttpInterceptor.d.cts → remote-http-interceptor.d.ts} +12 -11
- package/lib/node/{RemoteHttpInterceptor.mjs → remote-http-interceptor.js} +38 -33
- package/lib/node/remote-http-interceptor.js.map +1 -0
- package/lib/node/source-lP0yyEtA.js +1990 -0
- package/lib/node/source-lP0yyEtA.js.map +1 -0
- package/package.json +79 -118
- package/src/batch-interceptor.test.ts +475 -0
- package/src/batch-interceptor.ts +244 -0
- package/src/{createRequestId.test.ts → create-request-id.test.ts} +1 -2
- package/src/disposable.ts +13 -0
- package/src/events/http.ts +92 -0
- package/src/events/websocket.ts +47 -0
- package/src/glossary.ts +1 -27
- package/src/index.ts +18 -10
- package/src/interceptor.test.ts +279 -0
- package/src/interceptor.ts +139 -0
- package/src/interceptors/ClientRequest/index.test.ts +21 -39
- package/src/interceptors/ClientRequest/index.ts +65 -180
- package/src/interceptors/ClientRequest/utils/{recordRawHeaders.test.ts → record-raw-headers.test.ts} +1 -2
- package/src/interceptors/ClientRequest/utils/{recordRawHeaders.ts → record-raw-headers.ts} +29 -2
- package/src/interceptors/WebSocket/index.ts +34 -51
- package/src/interceptors/WebSocket/utils/{bindEvent.test.ts → bind-event.test.ts} +1 -2
- package/src/interceptors/WebSocket/utils/events.test.ts +0 -1
- package/src/interceptors/WebSocket/{WebSocketClassTransport.ts → web-socket-class-transport.ts} +3 -3
- package/src/interceptors/WebSocket/{WebSocketClientConnection.ts → web-socket-client-connection.ts} +4 -4
- package/src/interceptors/WebSocket/{WebSocketOverride.ts → web-socket-override.ts} +6 -7
- package/src/interceptors/WebSocket/{WebSocketServerConnection.ts → web-socket-server-connection.ts} +4 -4
- package/src/interceptors/WebSocket/{WebSocketTransport.ts → web-socket-transport.ts} +1 -1
- package/src/interceptors/XMLHttpRequest/node.ts +104 -0
- package/src/interceptors/XMLHttpRequest/polyfills/{ProgressEventPolyfill.ts → progress-event-polyfill.ts} +1 -1
- package/src/interceptors/XMLHttpRequest/utils/{concateArrayBuffer.test.ts → concat-array-buffer.test.ts} +1 -2
- package/src/interceptors/XMLHttpRequest/utils/{concatArrayBuffer.ts → concat-array-buffer.ts} +1 -1
- package/src/interceptors/XMLHttpRequest/utils/{createEvent.test.ts → create-event.test.ts} +3 -4
- package/src/interceptors/XMLHttpRequest/utils/{createEvent.ts → create-event.ts} +2 -2
- package/src/interceptors/XMLHttpRequest/utils/{createResponse.ts → create-response.ts} +1 -1
- package/src/interceptors/XMLHttpRequest/utils/{getBodyByteLength.test.ts → get-body-byte-length.test.ts} +1 -2
- package/src/interceptors/XMLHttpRequest/web.ts +34 -0
- package/src/interceptors/XMLHttpRequest/{XMLHttpRequestController.ts → xml-http-request-controller.ts} +363 -204
- package/src/interceptors/XMLHttpRequest/{XMLHttpRequestProxy.ts → xml-http-request-proxy.ts} +28 -25
- package/src/interceptors/fetch/node.ts +103 -0
- package/src/interceptors/fetch/utils/{followRedirect.ts → follow-redirect.ts} +1 -1
- package/src/interceptors/fetch/web.ts +217 -0
- package/src/interceptors/http/forward-events.test.ts +86 -0
- package/src/interceptors/http/forward-events.ts +139 -0
- package/src/interceptors/http/http-parser/index.ts +310 -0
- package/src/interceptors/http/http-parser/llhttp/constants.cjs +485 -0
- package/src/interceptors/http/http-parser/llhttp/constants.d.cts +957 -0
- package/src/interceptors/http/http-parser/llhttp/llhttp.wasm +0 -0
- package/src/interceptors/http/http-parser.ts +138 -0
- package/src/interceptors/http/index.ts +34 -0
- package/src/interceptors/http/source.ts +688 -0
- package/src/interceptors/net/index.ts +368 -0
- package/src/interceptors/net/socket-controller.ts +1476 -0
- package/src/interceptors/net/utils/address-info.ts +54 -0
- package/src/interceptors/net/utils/connection-options-to-url.ts +56 -0
- package/src/interceptors/net/utils/flush-writes.ts +57 -0
- package/src/interceptors/net/utils/get-tls-connect-options.ts +23 -0
- package/src/interceptors/net/utils/normalize-net-connect-args.test.ts +233 -0
- package/src/interceptors/net/utils/normalize-net-connect-args.ts +121 -0
- package/src/interceptors/net/utils/normalize-tls-connect-args.ts +50 -0
- package/src/presets/browser.ts +4 -4
- package/src/presets/node.ts +4 -4
- package/src/{RemoteHttpInterceptor.ts → remote-http-interceptor.ts} +55 -42
- package/src/request-context.ts +40 -0
- package/src/{RequestController.test.ts → request-controller.test.ts} +37 -3
- package/src/{RequestController.ts → request-controller.ts} +33 -11
- package/src/utils/{bufferUtils.test.ts → buffer-utils.test.ts} +1 -2
- package/src/utils/buffer-utils.ts +37 -0
- package/src/utils/{cloneObject.test.ts → clone-object.test.ts} +1 -2
- package/src/utils/{cloneObject.ts → clone-object.ts} +8 -8
- package/src/utils/{createProxy.test.ts → create-proxy.test.ts} +6 -4
- package/src/utils/{createProxy.ts → create-proxy.ts} +1 -1
- package/src/utils/{fetchUtils.test.ts → fetch-utils.test.ts} +1 -1
- package/src/utils/{fetchUtils.ts → fetch-utils.ts} +28 -3
- package/src/utils/{findPropertySource.test.ts → find-property-source.test.ts} +1 -2
- package/src/utils/{getCleanUrl.test.ts → get-clean-url.test.ts} +1 -2
- package/src/utils/{getUrlByRequestOptions.test.ts → get-url-by-request-options.test.ts} +4 -8
- package/src/utils/{getUrlByRequestOptions.ts → get-url-by-request-options.ts} +16 -16
- package/src/utils/{getValueBySymbol.test.ts → get-value-by-symbol.test.ts} +1 -2
- package/src/utils/{handleRequest.ts → handle-request.ts} +66 -47
- package/src/utils/{hasConfigurableGlobal.test.ts → has-configurable-global.test.ts} +1 -2
- package/src/utils/{hasConfigurableGlobal.ts → has-configurable-global.ts} +2 -4
- package/src/utils/{isObject.test.ts → is-object.test.ts} +1 -2
- package/src/utils/logger.test.ts +154 -0
- package/src/utils/logger.ts +154 -0
- package/src/utils/{parseJson.test.ts → parse-json.test.ts} +1 -2
- package/src/utils/{patchesRegistry.test.ts → patches-registry.test.ts} +1 -1
- package/src/utils/{patchesRegistry.ts → patches-registry.ts} +1 -1
- package/src/utils/{responseUtils.ts → response-utils.ts} +25 -2
- package/ClientRequest/package.json +0 -11
- package/RemoteHttpInterceptor/package.json +0 -11
- package/WebSocket/package.json +0 -12
- package/XMLHttpRequest/package.json +0 -12
- package/fetch/package.json +0 -12
- package/lib/browser/Interceptor-Deczogc8.d.cts +0 -65
- package/lib/browser/Interceptor-gqKgs-aF.d.mts +0 -65
- package/lib/browser/XMLHttpRequest-B11I9TBx.cjs +0 -751
- package/lib/browser/XMLHttpRequest-B11I9TBx.cjs.map +0 -1
- package/lib/browser/XMLHttpRequest-DHEkSoRN.mjs +0 -746
- package/lib/browser/XMLHttpRequest-DHEkSoRN.mjs.map +0 -1
- package/lib/browser/bufferUtils-BiiO6HZv.mjs.map +0 -1
- package/lib/browser/bufferUtils-Uc0eRItL.cjs +0 -38
- package/lib/browser/bufferUtils-Uc0eRItL.cjs.map +0 -1
- package/lib/browser/createRequestId-DOf8Ktjs.cjs +0 -205
- package/lib/browser/createRequestId-DOf8Ktjs.cjs.map +0 -1
- package/lib/browser/createRequestId-DYCsFHOi.mjs +0 -170
- package/lib/browser/createRequestId-DYCsFHOi.mjs.map +0 -1
- package/lib/browser/fetch-BJwfdses.mjs +0 -236
- package/lib/browser/fetch-BJwfdses.mjs.map +0 -1
- package/lib/browser/fetch-M8iXkZ3L.cjs +0 -241
- package/lib/browser/fetch-M8iXkZ3L.cjs.map +0 -1
- package/lib/browser/getRawRequest-B1BqgWG6.mjs.map +0 -1
- package/lib/browser/getRawRequest-DdfaiPVH.cjs +0 -376
- package/lib/browser/getRawRequest-DdfaiPVH.cjs.map +0 -1
- package/lib/browser/glossary-1lhZx6Aw.d.cts +0 -69
- package/lib/browser/glossary-nkHWcwvY.d.mts +0 -69
- package/lib/browser/handleRequest-1Yqu_ho_.mjs +0 -178
- package/lib/browser/handleRequest-1Yqu_ho_.mjs.map +0 -1
- package/lib/browser/handleRequest-Cz4_wmQ9.cjs +0 -189
- package/lib/browser/handleRequest-Cz4_wmQ9.cjs.map +0 -1
- package/lib/browser/hasConfigurableGlobal-C8zq1MCg.mjs +0 -109
- package/lib/browser/hasConfigurableGlobal-C8zq1MCg.mjs.map +0 -1
- package/lib/browser/hasConfigurableGlobal-CS7adEvV.cjs +0 -126
- package/lib/browser/hasConfigurableGlobal-CS7adEvV.cjs.map +0 -1
- package/lib/browser/index.cjs +0 -70
- package/lib/browser/index.cjs.map +0 -1
- package/lib/browser/index.d.mts +0 -125
- package/lib/browser/index.mjs +0 -56
- package/lib/browser/index.mjs.map +0 -1
- package/lib/browser/interceptors/WebSocket/index.cjs +0 -618
- package/lib/browser/interceptors/WebSocket/index.cjs.map +0 -1
- package/lib/browser/interceptors/WebSocket/index.mjs.map +0 -1
- package/lib/browser/interceptors/XMLHttpRequest/index.cjs +0 -8
- package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +0 -15
- package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +0 -15
- package/lib/browser/interceptors/XMLHttpRequest/index.mjs +0 -8
- package/lib/browser/interceptors/fetch/index.cjs +0 -7
- package/lib/browser/interceptors/fetch/index.d.cts +0 -13
- package/lib/browser/interceptors/fetch/index.d.mts +0 -13
- package/lib/browser/interceptors/fetch/index.mjs +0 -7
- package/lib/browser/presets/browser.cjs +0 -18
- package/lib/browser/presets/browser.cjs.map +0 -1
- package/lib/browser/presets/browser.d.cts +0 -12
- package/lib/browser/presets/browser.d.mts +0 -14
- package/lib/browser/presets/browser.mjs +0 -18
- package/lib/browser/presets/browser.mjs.map +0 -1
- package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs +0 -31
- package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs.map +0 -1
- package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs.map +0 -1
- package/lib/node/BatchInterceptor-DdMNfSUY.mjs +0 -44
- package/lib/node/BatchInterceptor-DdMNfSUY.mjs.map +0 -1
- package/lib/node/BatchInterceptor-KKF8Q47I.d.cts +0 -26
- package/lib/node/BatchInterceptor-Z1lwK23r.cjs +0 -49
- package/lib/node/BatchInterceptor-Z1lwK23r.cjs.map +0 -1
- package/lib/node/BatchInterceptor-rSr-NH_H.d.mts +0 -26
- package/lib/node/ClientRequest-DFs7FPNi.cjs +0 -1058
- package/lib/node/ClientRequest-DFs7FPNi.cjs.map +0 -1
- package/lib/node/ClientRequest-dRpI9v1o.mjs +0 -1049
- package/lib/node/ClientRequest-dRpI9v1o.mjs.map +0 -1
- package/lib/node/Interceptor-BsZ21ue0.d.cts +0 -132
- package/lib/node/Interceptor-soD-WpQO.d.mts +0 -132
- package/lib/node/RemoteHttpInterceptor.cjs +0 -155
- package/lib/node/RemoteHttpInterceptor.cjs.map +0 -1
- package/lib/node/RemoteHttpInterceptor.d.mts +0 -39
- package/lib/node/RemoteHttpInterceptor.mjs.map +0 -1
- package/lib/node/XMLHttpRequest-DhupxLHX.mjs.map +0 -1
- package/lib/node/XMLHttpRequest-Dyi6ahGz.cjs +0 -752
- package/lib/node/XMLHttpRequest-Dyi6ahGz.cjs.map +0 -1
- package/lib/node/bufferUtils-DxPxwff_.mjs +0 -20
- package/lib/node/bufferUtils-DxPxwff_.mjs.map +0 -1
- package/lib/node/bufferUtils-S5_-2eN4.cjs +0 -38
- package/lib/node/bufferUtils-S5_-2eN4.cjs.map +0 -1
- package/lib/node/chunk-CbDLau6x.cjs +0 -34
- package/lib/node/fetch-BqMcbuYR.mjs +0 -252
- package/lib/node/fetch-BqMcbuYR.mjs.map +0 -1
- package/lib/node/fetch-aQJu1Gf_.cjs +0 -259
- package/lib/node/fetch-aQJu1Gf_.cjs.map +0 -1
- package/lib/node/fetchUtils-BKJ1XmiO.mjs +0 -480
- package/lib/node/fetchUtils-BKJ1XmiO.mjs.map +0 -1
- package/lib/node/fetchUtils-umV5xXBy.cjs +0 -546
- package/lib/node/fetchUtils-umV5xXBy.cjs.map +0 -1
- package/lib/node/getRawRequest-B6znU6il.cjs +0 -36
- package/lib/node/getRawRequest-B6znU6il.cjs.map +0 -1
- package/lib/node/getRawRequest-C2-1urzA.mjs +0 -24
- package/lib/node/getRawRequest-C2-1urzA.mjs.map +0 -1
- package/lib/node/handleRequest-DCLzePtS.mjs +0 -190
- package/lib/node/handleRequest-DCLzePtS.mjs.map +0 -1
- package/lib/node/handleRequest-DVOthWJo.cjs +0 -220
- package/lib/node/handleRequest-DVOthWJo.cjs.map +0 -1
- package/lib/node/hasConfigurableGlobal-BiTmog1u.mjs.map +0 -1
- package/lib/node/hasConfigurableGlobal-MjY06_Ok.cjs +0 -108
- package/lib/node/hasConfigurableGlobal-MjY06_Ok.cjs.map +0 -1
- package/lib/node/index-BJ5E5aW-.d.cts +0 -113
- package/lib/node/index-DfoF45ih.d.mts +0 -113
- package/lib/node/index.cjs +0 -53
- package/lib/node/index.cjs.map +0 -1
- package/lib/node/index.d.cts +0 -104
- package/lib/node/index.d.mts +0 -104
- package/lib/node/index.mjs.map +0 -1
- package/lib/node/interceptors/ClientRequest/index.cjs +0 -6
- package/lib/node/interceptors/ClientRequest/index.d.cts +0 -2
- package/lib/node/interceptors/ClientRequest/index.d.mts +0 -3
- package/lib/node/interceptors/ClientRequest/index.mjs +0 -6
- package/lib/node/interceptors/XMLHttpRequest/index.cjs +0 -7
- package/lib/node/interceptors/XMLHttpRequest/index.d.cts +0 -14
- package/lib/node/interceptors/XMLHttpRequest/index.d.mts +0 -14
- package/lib/node/interceptors/XMLHttpRequest/index.mjs +0 -7
- package/lib/node/interceptors/fetch/index.cjs +0 -6
- package/lib/node/interceptors/fetch/index.d.cts +0 -12
- package/lib/node/interceptors/fetch/index.d.mts +0 -12
- package/lib/node/interceptors/fetch/index.mjs +0 -6
- package/lib/node/node-DIKcnzhK.cjs +0 -39
- package/lib/node/node-DIKcnzhK.cjs.map +0 -1
- package/lib/node/node-lsdNwZEW.mjs +0 -27
- package/lib/node/node-lsdNwZEW.mjs.map +0 -1
- package/lib/node/presets/node.cjs +0 -23
- package/lib/node/presets/node.cjs.map +0 -1
- package/lib/node/presets/node.d.cts +0 -13
- package/lib/node/presets/node.d.mts +0 -15
- package/lib/node/presets/node.mjs +0 -23
- package/lib/node/presets/node.mjs.map +0 -1
- package/lib/node/utils/node/index.cjs +0 -4
- package/lib/node/utils/node/index.d.cts +0 -16
- package/lib/node/utils/node/index.d.mts +0 -16
- package/lib/node/utils/node/index.mjs +0 -3
- package/presets/browser/package.json +0 -5
- package/presets/node/package.json +0 -11
- package/src/BatchInterceptor.test.ts +0 -255
- package/src/BatchInterceptor.ts +0 -95
- package/src/Interceptor.test.ts +0 -205
- package/src/Interceptor.ts +0 -249
- package/src/getRawRequest.ts +0 -21
- package/src/interceptors/ClientRequest/MockHttpSocket.ts +0 -724
- package/src/interceptors/ClientRequest/agents.ts +0 -110
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +0 -54
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +0 -45
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +0 -427
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +0 -268
- package/src/interceptors/ClientRequest/utils/parserUtils.ts +0 -48
- package/src/interceptors/Socket/MockSocket.test.ts +0 -264
- package/src/interceptors/Socket/MockSocket.ts +0 -58
- package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +0 -26
- package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +0 -52
- package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +0 -33
- package/src/interceptors/XMLHttpRequest/index.ts +0 -40
- package/src/interceptors/fetch/index.ts +0 -203
- package/src/utils/bufferUtils.ts +0 -22
- package/src/utils/canParseUrl.ts +0 -13
- package/src/utils/emitAsync.ts +0 -25
- package/src/utils/nextTick.ts +0 -11
- package/src/utils/node/index.ts +0 -39
- /package/src/{createRequestId.ts → create-request-id.ts} +0 -0
- /package/src/{InterceptorError.ts → interceptor-error.ts} +0 -0
- /package/src/interceptors/WebSocket/utils/{bindEvent.ts → bind-event.ts} +0 -0
- /package/src/interceptors/XMLHttpRequest/polyfills/{EventPolyfill.ts → event-polyfill.ts} +0 -0
- /package/src/interceptors/XMLHttpRequest/utils/{getBodyByteLength.ts → get-body-byte-length.ts} +0 -0
- /package/src/interceptors/XMLHttpRequest/utils/{isDomParserSupportedType.ts → is-dom-parser-supported-type.ts} +0 -0
- /package/src/interceptors/fetch/utils/{createNetworkError.ts → create-network-error.ts} +0 -0
- /package/src/utils/{findPropertySource.ts → find-property-source.ts} +0 -0
- /package/src/utils/{getCleanUrl.ts → get-clean-url.ts} +0 -0
- /package/src/utils/{getValueBySymbol.ts → get-value-by-symbol.ts} +0 -0
- /package/src/utils/{isNodeLikeError.ts → is-node-like-error.ts} +0 -0
- /package/src/utils/{isObject.ts → is-object.ts} +0 -0
- /package/src/utils/{isPropertyAccessible.ts → is-property-accessible.ts} +0 -0
- /package/src/utils/{parseJson.ts → parse-json.ts} +0 -0
- /package/src/utils/{resolveWebSocketUrl.ts → resolve-web-socket-url.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,270 +1,201 @@
|
|
|
1
|
-
[](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
|
-
|
|
5
|
+
Use this library if you wish to intercept any of the below:
|
|
8
6
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
const originalHttpRequest = http.request
|
|
16
|
+
Consider how Node.js orchestrates an average HTTP request:
|
|
27
17
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
93
|
-
- `https.get`/`https.request`
|
|
94
|
-
- `XMLHttpRequest`
|
|
95
|
-
- `fetch`
|
|
96
|
-
- `WebSocket`
|
|
50
|
+
## When to use Interceptors?
|
|
97
51
|
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
56
|
+
## Getting started
|
|
103
57
|
|
|
104
|
-
|
|
105
|
-
|
|
58
|
+
```bash
|
|
59
|
+
npm i @mswjs/interceptors
|
|
60
|
+
```
|
|
106
61
|
|
|
107
|
-
|
|
62
|
+
### Debugging
|
|
108
63
|
|
|
109
|
-
|
|
64
|
+
Enable default interceptor logs with `debug` namespaces:
|
|
110
65
|
|
|
111
|
-
```
|
|
112
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
- `
|
|
133
|
-
- `
|
|
134
|
-
- `
|
|
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
|
-
|
|
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 {
|
|
102
|
+
import { SocketInterceptor } from '@mswjs/interceptors/net'
|
|
140
103
|
|
|
141
|
-
const interceptor = new
|
|
104
|
+
const interceptor = new SocketInterceptor()
|
|
142
105
|
|
|
143
|
-
|
|
144
|
-
|
|
106
|
+
interceptor.on('connection', ({ socket, connectionOptions, controller }) => {
|
|
107
|
+
if (connectionOptions.host === 'example.com') {
|
|
108
|
+
controller.claim()
|
|
145
109
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
110
|
+
socket.on('data', (chunk) => {
|
|
111
|
+
socket.write(anotherChunk)
|
|
112
|
+
})
|
|
113
|
+
}
|
|
150
114
|
})
|
|
151
115
|
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
123
|
+
### `HttpRequestInterceptor`
|
|
168
124
|
|
|
169
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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
|
-
|
|
221
|
-
|
|
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
|
-
|
|
154
|
+
> Make sure to clone the request before reading its body.
|
|
232
155
|
|
|
233
|
-
|
|
156
|
+
##### Request initiator
|
|
234
157
|
|
|
235
|
-
|
|
158
|
+
The `request` event exposes an `initiator` property that references the object that issued the intercepted request:
|
|
236
159
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
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
|
-
|
|
247
|
-
|
|
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
|
-
|
|
184
|
+
#### Modifying outgoing requests
|
|
252
185
|
|
|
253
|
-
|
|
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('
|
|
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
|
-
|
|
196
|
+
#### Mocking responses
|
|
266
197
|
|
|
267
|
-
Although this library can be used purely for
|
|
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
|
|
234
|
+
await setTimeout(500)
|
|
304
235
|
controller.respondWith(new Response(null, { status: 500 }))
|
|
305
236
|
})
|
|
306
237
|
```
|
|
307
238
|
|
|
308
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
>
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
421
|
+
#### `WebSocketClientConnection`
|
|
420
422
|
|
|
421
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
484
|
+
#### `WebSocketServerConnection`
|
|
483
485
|
|
|
484
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
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
|
|