@mswjs/interceptors 0.41.9 → 0.42.1
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-Bk5YX1AM.js +720 -0
- package/lib/browser/create-request-id-Bk5YX1AM.js.map +1 -0
- package/lib/browser/{getRawRequest-B1BqgWG6.mjs → fetch-utils-CUOrwEQf.js} +123 -52
- package/lib/browser/fetch-utils-CUOrwEQf.js.map +1 -0
- package/lib/browser/handle-request-CqVvBhcw.js +167 -0
- package/lib/browser/handle-request-CqVvBhcw.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} +39 -30
- 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/{XMLHttpRequest-DrZAf3w6.mjs → web-BcKSQghf.js} +327 -255
- package/lib/browser/web-BcKSQghf.js.map +1 -0
- package/lib/browser/web-CdcYFjgm.js +248 -0
- package/lib/browser/web-CdcYFjgm.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 +64 -179
- 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 +33 -50
- 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} +61 -41
- 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-DrZAf3w6.mjs.map +0 -1
- package/lib/browser/XMLHttpRequest-gXuzzX1g.cjs +0 -751
- package/lib/browser/XMLHttpRequest-gXuzzX1g.cjs.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-5IMPqr9e.mjs +0 -236
- package/lib/browser/fetch-5IMPqr9e.mjs.map +0 -1
- package/lib/browser/fetch-Bt9rMY9c.cjs +0 -241
- package/lib/browser/fetch-Bt9rMY9c.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-CJim54xG.cjs +0 -191
- package/lib/browser/handleRequest-CJim54xG.cjs.map +0 -1
- package/lib/browser/handleRequest-CPPgsG7s.mjs +0 -180
- package/lib/browser/handleRequest-CPPgsG7s.mjs.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-BGhH8z1z.cjs +0 -49
- package/lib/node/BatchInterceptor-BGhH8z1z.cjs.map +0 -1
- package/lib/node/BatchInterceptor-C4-RX5rg.mjs +0 -44
- package/lib/node/BatchInterceptor-C4-RX5rg.mjs.map +0 -1
- package/lib/node/BatchInterceptor-KKF8Q47I.d.cts +0 -26
- package/lib/node/BatchInterceptor-rSr-NH_H.d.mts +0 -26
- package/lib/node/ClientRequest-2loAH7xM.cjs +0 -1058
- package/lib/node/ClientRequest-2loAH7xM.cjs.map +0 -1
- package/lib/node/ClientRequest-DGhBX21V.mjs +0 -1049
- package/lib/node/ClientRequest-DGhBX21V.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-BtDNZipW.cjs +0 -752
- package/lib/node/XMLHttpRequest-BtDNZipW.cjs.map +0 -1
- package/lib/node/XMLHttpRequest-Dw6Wm-UU.mjs +0 -746
- package/lib/node/XMLHttpRequest-Dw6Wm-UU.mjs.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-CaC3rGEu.mjs +0 -252
- package/lib/node/fetch-CaC3rGEu.mjs.map +0 -1
- package/lib/node/fetch-D2ClLNN-.cjs +0 -259
- package/lib/node/fetch-D2ClLNN-.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-C-cKYjIh.cjs +0 -222
- package/lib/node/handleRequest-C-cKYjIh.cjs.map +0 -1
- package/lib/node/handleRequest-FIQv5pwH.mjs +0 -192
- package/lib/node/handleRequest-FIQv5pwH.mjs.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 -96
- 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/src/interceptors/XMLHttpRequest/{XMLHttpRequestProxy.ts → xml-http-request-proxy.ts}
RENAMED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import { RequestController } from '../../
|
|
4
|
-
import { XMLHttpRequestController } from './
|
|
5
|
-
import { handleRequest } from '../../utils/
|
|
6
|
-
import {
|
|
1
|
+
import type { Emitter } from 'rettime'
|
|
2
|
+
import { HttpResponseEvent, type HttpRequestEventMap } from '../../events/http'
|
|
3
|
+
import { RequestController } from '../../request-controller'
|
|
4
|
+
import { XMLHttpRequestController } from './xml-http-request-controller'
|
|
5
|
+
import { handleRequest } from '../../utils/handle-request'
|
|
6
|
+
import type { Logger } from '../../utils/logger'
|
|
7
7
|
|
|
8
8
|
export interface XMLHttpRequestProxyOptions {
|
|
9
|
-
emitter:
|
|
9
|
+
emitter: Emitter<HttpRequestEventMap>
|
|
10
10
|
logger: Logger
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -21,7 +21,7 @@ export function createXMLHttpRequestProxy({
|
|
|
21
21
|
}: XMLHttpRequestProxyOptions) {
|
|
22
22
|
const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {
|
|
23
23
|
construct(target, args, newTarget) {
|
|
24
|
-
logger.
|
|
24
|
+
logger.verbose('constructed new XMLHttpRequest')
|
|
25
25
|
|
|
26
26
|
const originalRequest = Reflect.construct(
|
|
27
27
|
target,
|
|
@@ -55,59 +55,62 @@ export function createXMLHttpRequestProxy({
|
|
|
55
55
|
xhrRequestController.onRequest = async function ({ request, requestId }) {
|
|
56
56
|
const controller = new RequestController(request, {
|
|
57
57
|
passthrough: () => {
|
|
58
|
-
this.logger.
|
|
58
|
+
this.logger.verbose(
|
|
59
59
|
'no mocked response received, performing request as-is...'
|
|
60
60
|
)
|
|
61
61
|
},
|
|
62
62
|
respondWith: async (response) => {
|
|
63
|
-
if (isResponseError(response)) {
|
|
64
|
-
this.errorWith(new TypeError('Network error'))
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
|
|
68
63
|
await this.respondWith(response)
|
|
69
64
|
},
|
|
70
65
|
errorWith: (reason) => {
|
|
71
|
-
this.logger.
|
|
66
|
+
this.logger.verbose('request errored %o', { error: reason })
|
|
72
67
|
|
|
73
68
|
if (reason instanceof Error) {
|
|
74
69
|
this.errorWith(reason)
|
|
75
70
|
}
|
|
76
71
|
},
|
|
72
|
+
}, {
|
|
73
|
+
logger,
|
|
74
|
+
requestId,
|
|
77
75
|
})
|
|
78
76
|
|
|
79
|
-
this.logger.
|
|
77
|
+
this.logger.verbose('awaiting mocked response')
|
|
80
78
|
|
|
81
|
-
this.logger.
|
|
79
|
+
this.logger.verbose(
|
|
82
80
|
'emitting the "request" event for %s listener(s)...',
|
|
83
81
|
emitter.listenerCount('request')
|
|
84
82
|
)
|
|
85
83
|
|
|
86
84
|
await handleRequest({
|
|
85
|
+
initiator: this.request,
|
|
87
86
|
request,
|
|
88
87
|
requestId,
|
|
89
88
|
controller,
|
|
90
89
|
emitter,
|
|
90
|
+
logger,
|
|
91
91
|
})
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
xhrRequestController.onResponse = async function ({
|
|
95
95
|
response,
|
|
96
|
-
|
|
96
|
+
responseType,
|
|
97
97
|
request,
|
|
98
98
|
requestId,
|
|
99
99
|
}) {
|
|
100
|
-
this.logger.
|
|
100
|
+
this.logger.verbose(
|
|
101
101
|
'emitting the "response" event for %s listener(s)...',
|
|
102
102
|
emitter.listenerCount('response')
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
-
emitter.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
await emitter.emitAsPromise(
|
|
106
|
+
new HttpResponseEvent({
|
|
107
|
+
initiator: this.request,
|
|
108
|
+
response,
|
|
109
|
+
responseType,
|
|
110
|
+
request,
|
|
111
|
+
requestId,
|
|
112
|
+
})
|
|
113
|
+
)
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
// Return the proxied request from the controller
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { hasConfigurableGlobal } from '#/src/utils/has-configurable-global'
|
|
2
|
+
import { getErrorResponse } from '#/src/utils/response-utils'
|
|
3
|
+
import { requestContext } from '#/src/request-context'
|
|
4
|
+
import { patchesRegistry } from '#/src/utils/patches-registry'
|
|
5
|
+
import { forwardHttpEvents } from '#/src/interceptors/http/forward-events'
|
|
6
|
+
import { NodeHttpRequestSource } from '#/src/interceptors/http/source'
|
|
7
|
+
import { HttpRequestEventMap } from '#/src/events/http'
|
|
8
|
+
import { Interceptor } from '../../interceptor'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Interceptor for `fetch` requests in Node.js.
|
|
12
|
+
* @note This interceptor only affects requests performed via
|
|
13
|
+
* the global `fetch` function. To intercept fetch requests performed
|
|
14
|
+
* by other means (e.g. direct `request()` from Undici) use the
|
|
15
|
+
* `HttpRequestInterceptor` instead.
|
|
16
|
+
*/
|
|
17
|
+
export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
18
|
+
static symbol = Symbol.for('fetch-interceptor')
|
|
19
|
+
|
|
20
|
+
protected predicate() {
|
|
21
|
+
return hasConfigurableGlobal('fetch')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected setup(): void {
|
|
25
|
+
const requestSource = Interceptor.singleton(NodeHttpRequestSource)
|
|
26
|
+
requestSource.apply(this)
|
|
27
|
+
|
|
28
|
+
this.subscriptions.push(() => {
|
|
29
|
+
requestSource.dispose(this)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
this.subscriptions.push(
|
|
33
|
+
forwardHttpEvents({
|
|
34
|
+
source: requestSource,
|
|
35
|
+
emitter: this.emitter,
|
|
36
|
+
predicate: (initiator) => {
|
|
37
|
+
return initiator instanceof Request
|
|
38
|
+
},
|
|
39
|
+
responsePredicate: (event) => {
|
|
40
|
+
/**
|
|
41
|
+
* @note Fetch clients never observe informational responses (1xx).
|
|
42
|
+
* Undici treats them as a network error, failing the request,
|
|
43
|
+
* so do not forward their "response" events to fetch consumers.
|
|
44
|
+
*/
|
|
45
|
+
return event.response.status >= 200
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
this.subscriptions.push(
|
|
51
|
+
patchesRegistry.applyPatch(globalThis, 'fetch', (realFetch) => {
|
|
52
|
+
return (input, init) => {
|
|
53
|
+
/**
|
|
54
|
+
* Resolve potentially relative request URL against the present `location`.
|
|
55
|
+
* This is mainly for native `fetch` in browser-like environments.
|
|
56
|
+
* @see https://github.com/mswjs/msw/issues/1625
|
|
57
|
+
*/
|
|
58
|
+
const resolvedInput =
|
|
59
|
+
typeof input === 'string' &&
|
|
60
|
+
typeof location !== 'undefined' &&
|
|
61
|
+
!URL.canParse(input)
|
|
62
|
+
? new URL(input, location.href)
|
|
63
|
+
: input
|
|
64
|
+
|
|
65
|
+
const request = new Request(resolvedInput, init)
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @note Scope the request context to the `fetch` call itself.
|
|
69
|
+
* Using `enterWith` here would rebind the store for the caller's
|
|
70
|
+
* entire asynchronous scope, attributing unrelated requests
|
|
71
|
+
* performed after this `fetch` to it.
|
|
72
|
+
*/
|
|
73
|
+
return requestContext.run(
|
|
74
|
+
{
|
|
75
|
+
initiator: request,
|
|
76
|
+
logger: this.logger,
|
|
77
|
+
},
|
|
78
|
+
() => {
|
|
79
|
+
return realFetch(request).catch((error: unknown) => {
|
|
80
|
+
/**
|
|
81
|
+
* @note A mocked `Response.error()` destroys the socket with
|
|
82
|
+
* an internal error, which Undici reports as the cause of its
|
|
83
|
+
* "fetch failed" rejection. Surface the error response as the
|
|
84
|
+
* rejection cause instead, so the consumer can tell a mocked
|
|
85
|
+
* network error apart from an actual connectivity issue.
|
|
86
|
+
*/
|
|
87
|
+
if (error instanceof TypeError) {
|
|
88
|
+
const errorResponse = getErrorResponse(error.cause)
|
|
89
|
+
|
|
90
|
+
if (errorResponse) {
|
|
91
|
+
error.cause = errorResponse
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
throw error
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { until } from '@open-draft/until'
|
|
2
|
+
import { HttpResponseEvent, type HttpRequestEventMap } from '../../events/http'
|
|
3
|
+
import { RequestController } from '../../request-controller'
|
|
4
|
+
import { handleRequest } from '../../utils/handle-request'
|
|
5
|
+
import { createRequestId } from '../../create-request-id'
|
|
6
|
+
import { createNetworkError } from './utils/create-network-error'
|
|
7
|
+
import { followFetchRedirect } from './utils/follow-redirect'
|
|
8
|
+
import { decompressResponse } from './utils/decompression'
|
|
9
|
+
import { hasConfigurableGlobal } from '../../utils/has-configurable-global'
|
|
10
|
+
import { FetchResponse } from '../../utils/fetch-utils'
|
|
11
|
+
import { isResponseError } from '../../utils/response-utils'
|
|
12
|
+
import { patchesRegistry } from '../../utils/patches-registry'
|
|
13
|
+
import { copyRawHeaders } from '../ClientRequest/utils/record-raw-headers'
|
|
14
|
+
import { Interceptor } from '../../interceptor'
|
|
15
|
+
import { createLogger } from '../../utils/logger'
|
|
16
|
+
|
|
17
|
+
const logger = createLogger('fetch')
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Interceptor for `fetch` requests in the browser.
|
|
21
|
+
*/
|
|
22
|
+
export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
23
|
+
static symbol = Symbol.for('fetch-interceptor')
|
|
24
|
+
|
|
25
|
+
protected predicate() {
|
|
26
|
+
return hasConfigurableGlobal('fetch')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected async setup() {
|
|
30
|
+
logger.verbose('patching global fetch...')
|
|
31
|
+
|
|
32
|
+
this.subscriptions.push(
|
|
33
|
+
patchesRegistry.applyPatch(globalThis, 'fetch', (realFetch) => {
|
|
34
|
+
return async (input, init) => {
|
|
35
|
+
const requestId = createRequestId()
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @note Resolve potentially relative request URL
|
|
39
|
+
* against the present `location`. This is mainly
|
|
40
|
+
* for native `fetch` in JSDOM.
|
|
41
|
+
* @see https://github.com/mswjs/msw/issues/1625
|
|
42
|
+
*/
|
|
43
|
+
const resolvedInput =
|
|
44
|
+
typeof input === 'string' &&
|
|
45
|
+
typeof location !== 'undefined' &&
|
|
46
|
+
!URL.canParse(input)
|
|
47
|
+
? new URL(input, location.href)
|
|
48
|
+
: input
|
|
49
|
+
|
|
50
|
+
const request = new Request(resolvedInput, init)
|
|
51
|
+
|
|
52
|
+
const responsePromise = Promise.withResolvers<Response>()
|
|
53
|
+
|
|
54
|
+
const controller = new RequestController(
|
|
55
|
+
request,
|
|
56
|
+
{
|
|
57
|
+
passthrough: async () => {
|
|
58
|
+
logger.verbose('performing request as-is')
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @note Clone the request instance right before performing it.
|
|
62
|
+
* This preserves any modifications made to the intercepted request
|
|
63
|
+
* in the "request" listener. This also allows the user to read the
|
|
64
|
+
* request body in the "response" listener (otherwise "unusable").
|
|
65
|
+
*/
|
|
66
|
+
const requestCloneForResponseEvent = request.clone()
|
|
67
|
+
|
|
68
|
+
// Perform the intercepted request as-is.
|
|
69
|
+
const [responseError, originalResponse] = await until(() =>
|
|
70
|
+
realFetch(request)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if (responseError) {
|
|
74
|
+
return responsePromise.reject(responseError)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
logger.verbose('original fetch performed %o', originalResponse)
|
|
78
|
+
|
|
79
|
+
if (this.emitter.listenerCount('response') > 0) {
|
|
80
|
+
logger.verbose('emitting the "response" event')
|
|
81
|
+
|
|
82
|
+
const responseClone = FetchResponse.clone(originalResponse)
|
|
83
|
+
await this.emitter.emitAsPromise(
|
|
84
|
+
new HttpResponseEvent({
|
|
85
|
+
initiator: requestCloneForResponseEvent,
|
|
86
|
+
request: requestCloneForResponseEvent,
|
|
87
|
+
requestId,
|
|
88
|
+
response: responseClone,
|
|
89
|
+
responseType: 'original',
|
|
90
|
+
})
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Resolve the response promise with the original response
|
|
95
|
+
// since the `fetch()` return this internal promise.
|
|
96
|
+
responsePromise.resolve(originalResponse)
|
|
97
|
+
},
|
|
98
|
+
respondWith: async (rawResponse) => {
|
|
99
|
+
// Handle mocked `Response.error()` (i.e. request errors).
|
|
100
|
+
if (isResponseError(rawResponse)) {
|
|
101
|
+
logger.verbose('request errored %o', {
|
|
102
|
+
response: rawResponse,
|
|
103
|
+
})
|
|
104
|
+
responsePromise.reject(createNetworkError(rawResponse))
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Decompress the mocked response body, if applicable.
|
|
109
|
+
const decompressedStream = decompressResponse(rawResponse)
|
|
110
|
+
const response = new FetchResponse(
|
|
111
|
+
decompressedStream || rawResponse.body,
|
|
112
|
+
{
|
|
113
|
+
url: request.url,
|
|
114
|
+
status: rawResponse.status,
|
|
115
|
+
statusText: rawResponse.statusText,
|
|
116
|
+
headers: rawResponse.headers,
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
copyRawHeaders(rawResponse.headers, response.headers)
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Undici's handling of following redirect responses.
|
|
124
|
+
* Treat the "manual" redirect mode as a regular mocked response.
|
|
125
|
+
* This way, the client can manually follow the redirect it receives.
|
|
126
|
+
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
|
|
127
|
+
*/
|
|
128
|
+
if (FetchResponse.isRedirectResponse(response.status)) {
|
|
129
|
+
// Reject the request promise if its `redirect` is set to `error`
|
|
130
|
+
// and it receives a mocked redirect response.
|
|
131
|
+
if (request.redirect === 'error') {
|
|
132
|
+
responsePromise.reject(
|
|
133
|
+
createNetworkError('unexpected redirect')
|
|
134
|
+
)
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (request.redirect === 'follow') {
|
|
139
|
+
followFetchRedirect(request, response).then(
|
|
140
|
+
(response) => {
|
|
141
|
+
responsePromise.resolve(response)
|
|
142
|
+
},
|
|
143
|
+
(reason) => {
|
|
144
|
+
responsePromise.reject(reason)
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.emitter.listenerCount('response') > 0) {
|
|
152
|
+
logger.verbose('emitting the "response" event')
|
|
153
|
+
|
|
154
|
+
// Await the response listeners to finish before resolving
|
|
155
|
+
// the response promise. This ensures all your logic finishes
|
|
156
|
+
// before the interceptor resolves the pending response.
|
|
157
|
+
await this.emitter.emitAsPromise(
|
|
158
|
+
new HttpResponseEvent({
|
|
159
|
+
initiator: request,
|
|
160
|
+
// Clone the mocked response for the "response" event listener.
|
|
161
|
+
// This way, the listener can read the response and not lock its body
|
|
162
|
+
// for the actual fetch consumer.
|
|
163
|
+
response: FetchResponse.clone(response),
|
|
164
|
+
responseType: 'mock',
|
|
165
|
+
request,
|
|
166
|
+
requestId,
|
|
167
|
+
})
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
responsePromise.resolve(response)
|
|
172
|
+
},
|
|
173
|
+
errorWith: (reason) => {
|
|
174
|
+
logger.verbose('request aborted %o', { reason })
|
|
175
|
+
responsePromise.reject(reason)
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
logger,
|
|
180
|
+
requestId,
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
logger.verbose('awaiting request resolution')
|
|
185
|
+
|
|
186
|
+
logger.verbose(
|
|
187
|
+
'emitting the "request" event for %s listener(s)...',
|
|
188
|
+
this.emitter.listenerCount('request')
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @note Give the consumer a chance to abort the request before
|
|
193
|
+
* it is dispatched. Fetch queues the request processing as a
|
|
194
|
+
* task, so a signal aborted synchronously after `fetch()` must
|
|
195
|
+
* prevent the request from ever reaching the "request" listeners.
|
|
196
|
+
* Without this, the first listener is invoked synchronously
|
|
197
|
+
* within the `fetch()` call itself.
|
|
198
|
+
*/
|
|
199
|
+
await Promise.resolve()
|
|
200
|
+
|
|
201
|
+
await handleRequest({
|
|
202
|
+
initiator: request,
|
|
203
|
+
request,
|
|
204
|
+
requestId,
|
|
205
|
+
emitter: this.emitter,
|
|
206
|
+
controller,
|
|
207
|
+
logger,
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
return responsePromise.promise
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
logger.verbose('global fetch patched: %s', globalThis.fetch.name)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Emitter } from 'rettime'
|
|
2
|
+
import {
|
|
3
|
+
HttpResponseEvent,
|
|
4
|
+
type HttpRequestEventMap,
|
|
5
|
+
} from '#/src/events/http'
|
|
6
|
+
import { Interceptor } from '#/src/interceptor'
|
|
7
|
+
import { forwardHttpEvents } from './forward-events'
|
|
8
|
+
|
|
9
|
+
class HttpRequestSource extends Interceptor<HttpRequestEventMap> {
|
|
10
|
+
protected predicate(): boolean {
|
|
11
|
+
return true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
protected setup(): void {}
|
|
15
|
+
|
|
16
|
+
public emitResponse(event: HttpResponseEvent) {
|
|
17
|
+
return this.emitter.emitAsPromise(event)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
it('forwards the canonical event instance to every interceptor', async () => {
|
|
22
|
+
const source = new HttpRequestSource()
|
|
23
|
+
const firstEmitter = new Emitter<HttpRequestEventMap>()
|
|
24
|
+
const secondEmitter = new Emitter<HttpRequestEventMap>()
|
|
25
|
+
const firstListener = vi.fn()
|
|
26
|
+
const secondListener = vi.fn()
|
|
27
|
+
|
|
28
|
+
firstEmitter.on('response', firstListener)
|
|
29
|
+
secondEmitter.on('response', secondListener)
|
|
30
|
+
|
|
31
|
+
const disposeFirstForwarding = forwardHttpEvents({
|
|
32
|
+
source,
|
|
33
|
+
emitter: firstEmitter,
|
|
34
|
+
predicate: () => {
|
|
35
|
+
return true
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
const disposeSecondForwarding = forwardHttpEvents({
|
|
39
|
+
source,
|
|
40
|
+
emitter: secondEmitter,
|
|
41
|
+
predicate: () => {
|
|
42
|
+
return true
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
const responseEvent = new HttpResponseEvent({
|
|
46
|
+
request: new Request('https://example.com'),
|
|
47
|
+
requestId: 'request-id',
|
|
48
|
+
initiator: null,
|
|
49
|
+
response: new Response(),
|
|
50
|
+
responseType: 'mock',
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
await source.emitResponse(responseEvent)
|
|
54
|
+
|
|
55
|
+
expect(firstListener).toHaveBeenCalledExactlyOnceWith(responseEvent)
|
|
56
|
+
expect(secondListener).toHaveBeenCalledExactlyOnceWith(responseEvent)
|
|
57
|
+
|
|
58
|
+
disposeFirstForwarding()
|
|
59
|
+
disposeSecondForwarding()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('removes lazy source listeners with the consumer listener', () => {
|
|
63
|
+
const source = new HttpRequestSource()
|
|
64
|
+
const emitter = new Emitter<HttpRequestEventMap>()
|
|
65
|
+
const listener = vi.fn()
|
|
66
|
+
|
|
67
|
+
emitter.on('response', listener)
|
|
68
|
+
const disposeForwarding = forwardHttpEvents({
|
|
69
|
+
source,
|
|
70
|
+
emitter,
|
|
71
|
+
predicate: () => {
|
|
72
|
+
return true
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
expect(source.listenerCount('request')).toBe(1)
|
|
77
|
+
expect(source.listenerCount('response')).toBe(1)
|
|
78
|
+
|
|
79
|
+
emitter.removeListener('response', listener)
|
|
80
|
+
|
|
81
|
+
expect(source.listenerCount('response')).toBe(0)
|
|
82
|
+
|
|
83
|
+
disposeForwarding()
|
|
84
|
+
|
|
85
|
+
expect(source.listenerCount('request')).toBe(0)
|
|
86
|
+
})
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type Emitter } from 'rettime'
|
|
2
|
+
import {
|
|
3
|
+
type HttpRequestEventMap,
|
|
4
|
+
type HttpResponseEvent,
|
|
5
|
+
} from '#/src/events/http'
|
|
6
|
+
import { type Interceptor } from '#/src/interceptor'
|
|
7
|
+
import { type DisposableSubscription } from '#/src/disposable'
|
|
8
|
+
import { createLogger } from '#/src/utils/logger'
|
|
9
|
+
|
|
10
|
+
const logger = createLogger('http-request')
|
|
11
|
+
|
|
12
|
+
interface ForwardHttpEventsOptions {
|
|
13
|
+
source: Interceptor<HttpRequestEventMap>
|
|
14
|
+
emitter: Emitter<HttpRequestEventMap>
|
|
15
|
+
predicate: (initiator: unknown) => boolean
|
|
16
|
+
responsePredicate?: (event: HttpResponseEvent) => boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function forwardHttpEvents(
|
|
20
|
+
options: ForwardHttpEventsOptions
|
|
21
|
+
): DisposableSubscription {
|
|
22
|
+
const controller = new AbortController()
|
|
23
|
+
const { source, emitter, predicate, responsePredicate } = options
|
|
24
|
+
|
|
25
|
+
source.on(
|
|
26
|
+
'request',
|
|
27
|
+
async (event) => {
|
|
28
|
+
if (predicate(event.initiator)) {
|
|
29
|
+
logger.verbose('forwarding "request" event %o', {
|
|
30
|
+
requestId: event.requestId,
|
|
31
|
+
})
|
|
32
|
+
await emitter.emitAsPromise(event)
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
signal: controller.signal,
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const responseListener: Emitter.Listener<
|
|
41
|
+
Emitter<HttpRequestEventMap>,
|
|
42
|
+
'response'
|
|
43
|
+
> = async (event) => {
|
|
44
|
+
if (
|
|
45
|
+
predicate(event.initiator) &&
|
|
46
|
+
(responsePredicate == null || responsePredicate(event))
|
|
47
|
+
) {
|
|
48
|
+
logger.verbose('forwarding "response" event %o', {
|
|
49
|
+
requestId: event.requestId,
|
|
50
|
+
responseType: event.responseType,
|
|
51
|
+
})
|
|
52
|
+
await emitter.emitAsPromise(event)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const unhandledExceptionListener: Emitter.Listener<
|
|
57
|
+
Emitter<HttpRequestEventMap>,
|
|
58
|
+
'unhandledException'
|
|
59
|
+
> = async (event) => {
|
|
60
|
+
if (predicate(event.initiator)) {
|
|
61
|
+
logger.verbose('forwarding "unhandledException" event %o', {
|
|
62
|
+
requestId: event.requestId,
|
|
63
|
+
})
|
|
64
|
+
await emitter.emitAsPromise(event)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const addResponseListener = (): void => {
|
|
69
|
+
if (!source.listeners('response').includes(responseListener)) {
|
|
70
|
+
source.on('response', responseListener, {
|
|
71
|
+
signal: controller.signal,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const addUnhandledExceptionListener = (): void => {
|
|
77
|
+
if (
|
|
78
|
+
!source
|
|
79
|
+
.listeners('unhandledException')
|
|
80
|
+
.includes(unhandledExceptionListener)
|
|
81
|
+
) {
|
|
82
|
+
source.on('unhandledException', unhandledExceptionListener, {
|
|
83
|
+
signal: controller.signal,
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (emitter.listenerCount('response') > 0) {
|
|
89
|
+
addResponseListener()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (emitter.listenerCount('unhandledException') > 0) {
|
|
93
|
+
addUnhandledExceptionListener()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
emitter.hooks.on(
|
|
97
|
+
'newListener',
|
|
98
|
+
(type) => {
|
|
99
|
+
if (type === 'response') {
|
|
100
|
+
addResponseListener()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (type === 'unhandledException') {
|
|
104
|
+
addUnhandledExceptionListener()
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
signal: controller.signal,
|
|
109
|
+
persist: true,
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
emitter.hooks.on(
|
|
114
|
+
'removeListener',
|
|
115
|
+
(type) => {
|
|
116
|
+
if (type === 'response' && emitter.listenerCount('response') === 0) {
|
|
117
|
+
source.removeListener('response', responseListener)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (
|
|
121
|
+
type === 'unhandledException' &&
|
|
122
|
+
emitter.listenerCount('unhandledException') === 0
|
|
123
|
+
) {
|
|
124
|
+
source.removeListener(
|
|
125
|
+
'unhandledException',
|
|
126
|
+
unhandledExceptionListener
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
signal: controller.signal,
|
|
132
|
+
persist: true,
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return () => {
|
|
137
|
+
controller.abort()
|
|
138
|
+
}
|
|
139
|
+
}
|