@mswjs/interceptors 0.26.10 → 0.26.12
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 +74 -74
- package/lib/browser/{Interceptor-b7c08a9f.d.ts → Interceptor-af98b768.d.ts} +9 -1
- package/lib/{node/chunk-EM6NYHQV.js → browser/chunk-3PGRU2BR.js} +4 -2
- package/lib/browser/chunk-3PGRU2BR.js.map +1 -0
- package/lib/browser/{chunk-65OQZE6W.mjs → chunk-5E3BR6QC.mjs} +4 -7
- package/lib/browser/chunk-5E3BR6QC.mjs.map +1 -0
- package/lib/browser/{chunk-P2GC6ZRG.js → chunk-5HSEVANC.js} +8 -7
- package/lib/browser/chunk-5HSEVANC.js.map +1 -0
- package/lib/{node/chunk-SGO3INLV.mjs → browser/chunk-KHZ3VYHS.mjs} +3 -1
- package/lib/browser/chunk-KHZ3VYHS.mjs.map +1 -0
- package/lib/browser/{chunk-AMRVY5JV.js → chunk-N6P4WNMD.js} +5 -8
- package/lib/browser/chunk-N6P4WNMD.js.map +1 -0
- package/lib/browser/{chunk-6ZIL5FW4.mjs → chunk-WVYFUFZR.mjs} +8 -7
- package/lib/browser/chunk-WVYFUFZR.mjs.map +1 -0
- package/lib/browser/index.d.ts +2 -2
- package/lib/browser/index.js +5 -3
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/index.mjs +3 -1
- package/lib/browser/index.mjs.map +1 -1
- package/lib/browser/interceptors/WebSocket/index.d.ts +9 -2
- package/lib/browser/interceptors/WebSocket/index.js +164 -132
- package/lib/browser/interceptors/WebSocket/index.js.map +1 -1
- package/lib/browser/interceptors/WebSocket/index.mjs +167 -135
- package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -1
- package/lib/browser/interceptors/XMLHttpRequest/index.d.ts +1 -1
- package/lib/browser/interceptors/XMLHttpRequest/index.js +3 -4
- package/lib/browser/interceptors/XMLHttpRequest/index.mjs +2 -3
- package/lib/browser/interceptors/fetch/index.d.ts +2 -2
- package/lib/browser/interceptors/fetch/index.js +3 -4
- package/lib/browser/interceptors/fetch/index.mjs +2 -3
- package/lib/browser/presets/browser.d.ts +1 -1
- package/lib/browser/presets/browser.js +5 -6
- package/lib/browser/presets/browser.js.map +1 -1
- package/lib/browser/presets/browser.mjs +3 -4
- package/lib/browser/presets/browser.mjs.map +1 -1
- package/lib/node/{BatchInterceptor-9785c567.d.ts → BatchInterceptor-cb145daa.d.ts} +1 -1
- package/lib/node/{Interceptor-7a701c1f.d.ts → Interceptor-6696a18d.d.ts} +9 -1
- package/lib/node/RemoteHttpInterceptor.d.ts +2 -2
- package/lib/node/RemoteHttpInterceptor.js +15 -15
- package/lib/node/RemoteHttpInterceptor.mjs +9 -9
- package/lib/node/{chunk-7UF4SATD.mjs → chunk-5JPIH6H2.mjs} +15 -11
- package/lib/node/chunk-5JPIH6H2.mjs.map +1 -0
- package/lib/node/{chunk-6ZZBHB3P.mjs → chunk-66HWDCQ2.mjs} +2 -2
- package/lib/node/{chunk-5IBJT662.mjs → chunk-7GOGV57P.mjs} +1 -11
- package/lib/node/chunk-7GOGV57P.mjs.map +1 -0
- package/lib/node/{chunk-4NTPASNT.js → chunk-AQD7SRYT.js} +2 -12
- package/lib/node/chunk-AQD7SRYT.js.map +1 -0
- package/lib/node/{chunk-DERTLGL3.mjs → chunk-CDO7LUCB.mjs} +1 -1
- package/lib/node/{chunk-LK6DILFK.js → chunk-DWNXSX4R.js} +1 -1
- package/lib/node/{chunk-FZJKKO5H.js → chunk-HOHLBCZO.js} +1 -1
- package/lib/node/{chunk-IBYBTTYK.mjs → chunk-LVMKW3BV.mjs} +1 -1
- package/lib/node/{chunk-YXLNUCS2.mjs → chunk-OIJM4NGH.mjs} +12 -11
- package/lib/node/chunk-OIJM4NGH.mjs.map +1 -0
- package/lib/node/{chunk-Y6GRL6UD.js → chunk-OQFUVENL.js} +1 -1
- package/lib/node/{chunk-6HYIRFX2.mjs → chunk-ORI3LFXR.mjs} +1 -1
- package/lib/{browser/chunk-EM6NYHQV.js → node/chunk-Q2ZAXW2V.js} +9 -3
- package/lib/node/chunk-Q2ZAXW2V.js.map +1 -0
- package/lib/{browser/chunk-SGO3INLV.mjs → node/chunk-TNNJTZLG.mjs} +7 -1
- package/lib/node/chunk-TNNJTZLG.mjs.map +1 -0
- package/lib/node/{chunk-NWYLCOX6.js → chunk-WJBJLZSK.js} +22 -18
- package/lib/node/chunk-WJBJLZSK.js.map +1 -0
- package/lib/node/{chunk-JSSEHRRB.js → chunk-XRANZXDY.js} +1 -1
- package/lib/node/{chunk-HAGW22AN.mjs → chunk-YQUDKP67.mjs} +1 -1
- package/lib/node/{chunk-ZW4SKXEP.js → chunk-Z2GCDAWN.js} +3 -3
- package/lib/node/{chunk-UJRRZPRV.js → chunk-ZOS6ZFFL.js} +14 -13
- package/lib/node/chunk-ZOS6ZFFL.js.map +1 -0
- package/lib/node/index.d.ts +2 -2
- package/lib/node/index.js +8 -6
- package/lib/node/index.js.map +1 -1
- package/lib/node/index.mjs +7 -5
- package/lib/node/index.mjs.map +1 -1
- package/lib/node/interceptors/ClientRequest/index.d.ts +1 -1
- package/lib/node/interceptors/ClientRequest/index.js +6 -6
- package/lib/node/interceptors/ClientRequest/index.mjs +5 -5
- package/lib/node/interceptors/XMLHttpRequest/index.d.ts +1 -1
- package/lib/node/interceptors/XMLHttpRequest/index.js +7 -7
- package/lib/node/interceptors/XMLHttpRequest/index.mjs +6 -6
- package/lib/node/interceptors/fetch/index.d.ts +2 -2
- package/lib/node/interceptors/fetch/index.js +13 -13
- package/lib/node/interceptors/fetch/index.js.map +1 -1
- package/lib/node/interceptors/fetch/index.mjs +9 -9
- package/lib/node/interceptors/fetch/index.mjs.map +1 -1
- package/lib/node/presets/node.d.ts +1 -1
- package/lib/node/presets/node.js +10 -10
- package/lib/node/presets/node.mjs +8 -8
- package/package.json +2 -2
- package/src/Interceptor.ts +11 -1
- package/src/crypto-shim.ts +3 -0
- package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +0 -5
- package/src/interceptors/ClientRequest/NodeClientRequest.ts +5 -5
- package/src/interceptors/WebSocket/WebSocketClientConnection.ts +1 -2
- package/src/interceptors/WebSocket/WebSocketServerConnection.ts +87 -21
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +13 -7
- package/src/interceptors/fetch/index.ts +2 -3
- package/lib/browser/chunk-65OQZE6W.mjs.map +0 -1
- package/lib/browser/chunk-6ZIL5FW4.mjs.map +0 -1
- package/lib/browser/chunk-AMRVY5JV.js.map +0 -1
- package/lib/browser/chunk-CEKC26XE.mjs +0 -13
- package/lib/browser/chunk-CEKC26XE.mjs.map +0 -1
- package/lib/browser/chunk-EM6NYHQV.js.map +0 -1
- package/lib/browser/chunk-LQSFVQBO.js +0 -13
- package/lib/browser/chunk-LQSFVQBO.js.map +0 -1
- package/lib/browser/chunk-P2GC6ZRG.js.map +0 -1
- package/lib/browser/chunk-SGO3INLV.mjs.map +0 -1
- package/lib/node/chunk-4NTPASNT.js.map +0 -1
- package/lib/node/chunk-5IBJT662.mjs.map +0 -1
- package/lib/node/chunk-7UF4SATD.mjs.map +0 -1
- package/lib/node/chunk-EM6NYHQV.js.map +0 -1
- package/lib/node/chunk-NWYLCOX6.js.map +0 -1
- package/lib/node/chunk-SGO3INLV.mjs.map +0 -1
- package/lib/node/chunk-UJRRZPRV.js.map +0 -1
- package/lib/node/chunk-YXLNUCS2.mjs.map +0 -1
- package/src/utils/uuid.ts +0 -7
- /package/lib/node/{chunk-6ZZBHB3P.mjs.map → chunk-66HWDCQ2.mjs.map} +0 -0
- /package/lib/node/{chunk-DERTLGL3.mjs.map → chunk-CDO7LUCB.mjs.map} +0 -0
- /package/lib/node/{chunk-LK6DILFK.js.map → chunk-DWNXSX4R.js.map} +0 -0
- /package/lib/node/{chunk-FZJKKO5H.js.map → chunk-HOHLBCZO.js.map} +0 -0
- /package/lib/node/{chunk-IBYBTTYK.mjs.map → chunk-LVMKW3BV.mjs.map} +0 -0
- /package/lib/node/{chunk-Y6GRL6UD.js.map → chunk-OQFUVENL.js.map} +0 -0
- /package/lib/node/{chunk-6HYIRFX2.mjs.map → chunk-ORI3LFXR.mjs.map} +0 -0
- /package/lib/node/{chunk-JSSEHRRB.js.map → chunk-XRANZXDY.js.map} +0 -0
- /package/lib/node/{chunk-HAGW22AN.mjs.map → chunk-YQUDKP67.mjs.map} +0 -0
- /package/lib/node/{chunk-ZW4SKXEP.js.map → chunk-Z2GCDAWN.js.map} +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isPropertyAccessible
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-CDO7LUCB.mjs";
|
|
4
4
|
import {
|
|
5
5
|
IS_PATCHED_MODULE
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-YQUDKP67.mjs";
|
|
7
7
|
import {
|
|
8
8
|
emitAsync,
|
|
9
|
-
toInteractiveRequest
|
|
10
|
-
|
|
11
|
-
} from "../../chunk-5IBJT662.mjs";
|
|
9
|
+
toInteractiveRequest
|
|
10
|
+
} from "../../chunk-7GOGV57P.mjs";
|
|
12
11
|
import {
|
|
13
|
-
Interceptor
|
|
14
|
-
|
|
12
|
+
Interceptor,
|
|
13
|
+
crypto
|
|
14
|
+
} from "../../chunk-TNNJTZLG.mjs";
|
|
15
15
|
|
|
16
16
|
// src/interceptors/fetch/index.ts
|
|
17
17
|
import { invariant } from "outvariant";
|
|
@@ -36,7 +36,7 @@ var _FetchInterceptor = class extends Interceptor {
|
|
|
36
36
|
checkEnvironment() {
|
|
37
37
|
return typeof globalThis !== "undefined" && typeof globalThis.fetch !== "undefined";
|
|
38
38
|
}
|
|
39
|
-
setup() {
|
|
39
|
+
async setup() {
|
|
40
40
|
const pureFetch = globalThis.fetch;
|
|
41
41
|
invariant(
|
|
42
42
|
!pureFetch[IS_PATCHED_MODULE],
|
|
@@ -44,7 +44,7 @@ var _FetchInterceptor = class extends Interceptor {
|
|
|
44
44
|
);
|
|
45
45
|
globalThis.fetch = async (input, init) => {
|
|
46
46
|
var _a;
|
|
47
|
-
const requestId =
|
|
47
|
+
const requestId = crypto.randomUUID();
|
|
48
48
|
const resolvedInput = typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.origin) : input;
|
|
49
49
|
const request = new Request(resolvedInput, init);
|
|
50
50
|
this.logger.info("[%s] %s", request.method, request.url);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/interceptors/fetch/index.ts","../../../../src/utils/canParseUrl.ts"],"sourcesContent":["import { invariant } from 'outvariant'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { uuidv4 } from '../../utils/uuid'\nimport { toInteractiveRequest } from '../../utils/toInteractiveRequest'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { isPropertyAccessible } from '../../utils/isPropertyAccessible'\nimport { canParseUrl } from '../../utils/canParseUrl'\n\nexport class FetchInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol('fetch')\n\n constructor() {\n super(FetchInterceptor.symbol)\n }\n\n protected checkEnvironment() {\n return (\n typeof globalThis !== 'undefined' &&\n typeof globalThis.fetch !== 'undefined'\n )\n }\n\n protected setup() {\n const pureFetch = globalThis.fetch\n\n invariant(\n !(pureFetch as any)[IS_PATCHED_MODULE],\n 'Failed to patch the \"fetch\" module: already patched.'\n )\n\n globalThis.fetch = async (input, init) => {\n const requestId = uuidv4()\n\n /**\n * @note Resolve potentially relative request URL\n * against the present `location`. This is mainly\n * for native `fetch` in JSDOM.\n * @see https://github.com/mswjs/msw/issues/1625\n */\n const resolvedInput =\n typeof input === 'string' &&\n typeof location !== 'undefined' &&\n !canParseUrl(input)\n ? new URL(input, location.origin)\n : input\n\n const request = new Request(resolvedInput, init)\n\n this.logger.info('[%s] %s', request.method, request.url)\n\n const { interactiveRequest, requestController } =\n toInteractiveRequest(request)\n\n this.logger.info(\n 'emitting the \"request\" event for %d listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n this.emitter.once('request', ({ requestId: pendingRequestId }) => {\n if (pendingRequestId !== requestId) {\n return\n }\n\n if (requestController.responsePromise.state === 'pending') {\n requestController.responsePromise.resolve(undefined)\n }\n })\n\n this.logger.info('awaiting for the mocked response...')\n\n const signal = interactiveRequest.signal\n const requestAborted = new DeferredPromise()\n\n // Signal isn't always defined in react-native.\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n requestAborted.reject(signal.reason)\n },\n { once: true }\n )\n }\n\n const resolverResult = await until(async () => {\n const listenersFinished = emitAsync(this.emitter, 'request', {\n request: interactiveRequest,\n requestId,\n })\n\n await Promise.race([\n requestAborted,\n // Put the listeners invocation Promise in the same race condition\n // with the request abort Promise because otherwise awaiting the listeners\n // would always yield some response (or undefined).\n listenersFinished,\n requestController.responsePromise,\n ])\n\n this.logger.info('all request listeners have been resolved!')\n\n const mockedResponse = await requestController.responsePromise\n this.logger.info('event.respondWith called with:', mockedResponse)\n\n return mockedResponse\n })\n\n if (requestAborted.state === 'rejected') {\n return Promise.reject(requestAborted.rejectionReason)\n }\n\n if (resolverResult.error) {\n return Promise.reject(createNetworkError(resolverResult.error))\n }\n\n const mockedResponse = resolverResult.data\n\n if (mockedResponse && !request.signal?.aborted) {\n this.logger.info('received mocked response:', mockedResponse)\n\n // Reject the request Promise on mocked \"Response.error\" responses.\n if (\n isPropertyAccessible(mockedResponse, 'type') &&\n mockedResponse.type === 'error'\n ) {\n this.logger.info(\n 'received a network error response, rejecting the request promise...'\n )\n\n /**\n * Set the cause of the request promise rejection to the\n * network error Response instance. This different from Undici.\n * Undici will forward the \"response.error\" custom property\n * as the rejection reason but for \"Response.error()\" static method\n * \"response.error\" will equal to undefined, making \"cause\" an empty Error.\n * @see https://github.com/nodejs/undici/blob/83cb522ae0157a19d149d72c7d03d46e34510d0a/lib/fetch/response.js#L344\n */\n return Promise.reject(createNetworkError(mockedResponse))\n }\n\n // Clone the mocked response for the \"response\" event listener.\n // This way, the listener can read the response and not lock its body\n // for the actual fetch consumer.\n const responseClone = mockedResponse.clone()\n\n this.emitter.emit('response', {\n response: responseClone,\n isMockedResponse: true,\n request: interactiveRequest,\n requestId,\n })\n\n // Set the \"response.url\" property to equal the intercepted request URL.\n Object.defineProperty(mockedResponse, 'url', {\n writable: false,\n enumerable: true,\n configurable: false,\n value: request.url,\n })\n\n return mockedResponse\n }\n\n this.logger.info('no mocked response received!')\n\n return pureFetch(request).then((response) => {\n const responseClone = response.clone()\n this.logger.info('original fetch performed', responseClone)\n\n this.emitter.emit('response', {\n response: responseClone,\n isMockedResponse: false,\n request: interactiveRequest,\n requestId,\n })\n\n return response\n })\n }\n\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n enumerable: true,\n configurable: true,\n value: true,\n })\n\n this.subscriptions.push(() => {\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n value: undefined,\n })\n\n globalThis.fetch = pureFetch\n\n this.logger.info(\n 'restored native \"globalThis.fetch\"!',\n globalThis.fetch.name\n )\n })\n }\n}\n\nfunction createNetworkError(cause: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","/**\n * Returns a boolean indicating whether the given URL string\n * can be parsed into a `URL` instance.\n * A substitute for `URL.canParse()` for Node.js 18.\n */\nexport function canParseUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch (_error) {\n return false\n }\n}\n"],"mappings":"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","names":["mockedResponse"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/interceptors/fetch/index.ts","../../../../src/utils/canParseUrl.ts"],"sourcesContent":["import { invariant } from 'outvariant'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { toInteractiveRequest } from '../../utils/toInteractiveRequest'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { isPropertyAccessible } from '../../utils/isPropertyAccessible'\nimport { canParseUrl } from '../../utils/canParseUrl'\n\nexport class FetchInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol('fetch')\n\n constructor() {\n super(FetchInterceptor.symbol)\n }\n\n protected checkEnvironment() {\n return (\n typeof globalThis !== 'undefined' &&\n typeof globalThis.fetch !== 'undefined'\n )\n }\n\n protected async setup() {\n const pureFetch = globalThis.fetch\n\n invariant(\n !(pureFetch as any)[IS_PATCHED_MODULE],\n 'Failed to patch the \"fetch\" module: already patched.'\n )\n\n globalThis.fetch = async (input, init) => {\n const requestId = crypto.randomUUID()\n\n /**\n * @note Resolve potentially relative request URL\n * against the present `location`. This is mainly\n * for native `fetch` in JSDOM.\n * @see https://github.com/mswjs/msw/issues/1625\n */\n const resolvedInput =\n typeof input === 'string' &&\n typeof location !== 'undefined' &&\n !canParseUrl(input)\n ? new URL(input, location.origin)\n : input\n\n const request = new Request(resolvedInput, init)\n\n this.logger.info('[%s] %s', request.method, request.url)\n\n const { interactiveRequest, requestController } =\n toInteractiveRequest(request)\n\n this.logger.info(\n 'emitting the \"request\" event for %d listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n this.emitter.once('request', ({ requestId: pendingRequestId }) => {\n if (pendingRequestId !== requestId) {\n return\n }\n\n if (requestController.responsePromise.state === 'pending') {\n requestController.responsePromise.resolve(undefined)\n }\n })\n\n this.logger.info('awaiting for the mocked response...')\n\n const signal = interactiveRequest.signal\n const requestAborted = new DeferredPromise()\n\n // Signal isn't always defined in react-native.\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n requestAborted.reject(signal.reason)\n },\n { once: true }\n )\n }\n\n const resolverResult = await until(async () => {\n const listenersFinished = emitAsync(this.emitter, 'request', {\n request: interactiveRequest,\n requestId,\n })\n\n await Promise.race([\n requestAborted,\n // Put the listeners invocation Promise in the same race condition\n // with the request abort Promise because otherwise awaiting the listeners\n // would always yield some response (or undefined).\n listenersFinished,\n requestController.responsePromise,\n ])\n\n this.logger.info('all request listeners have been resolved!')\n\n const mockedResponse = await requestController.responsePromise\n this.logger.info('event.respondWith called with:', mockedResponse)\n\n return mockedResponse\n })\n\n if (requestAborted.state === 'rejected') {\n return Promise.reject(requestAborted.rejectionReason)\n }\n\n if (resolverResult.error) {\n return Promise.reject(createNetworkError(resolverResult.error))\n }\n\n const mockedResponse = resolverResult.data\n\n if (mockedResponse && !request.signal?.aborted) {\n this.logger.info('received mocked response:', mockedResponse)\n\n // Reject the request Promise on mocked \"Response.error\" responses.\n if (\n isPropertyAccessible(mockedResponse, 'type') &&\n mockedResponse.type === 'error'\n ) {\n this.logger.info(\n 'received a network error response, rejecting the request promise...'\n )\n\n /**\n * Set the cause of the request promise rejection to the\n * network error Response instance. This different from Undici.\n * Undici will forward the \"response.error\" custom property\n * as the rejection reason but for \"Response.error()\" static method\n * \"response.error\" will equal to undefined, making \"cause\" an empty Error.\n * @see https://github.com/nodejs/undici/blob/83cb522ae0157a19d149d72c7d03d46e34510d0a/lib/fetch/response.js#L344\n */\n return Promise.reject(createNetworkError(mockedResponse))\n }\n\n // Clone the mocked response for the \"response\" event listener.\n // This way, the listener can read the response and not lock its body\n // for the actual fetch consumer.\n const responseClone = mockedResponse.clone()\n\n this.emitter.emit('response', {\n response: responseClone,\n isMockedResponse: true,\n request: interactiveRequest,\n requestId,\n })\n\n // Set the \"response.url\" property to equal the intercepted request URL.\n Object.defineProperty(mockedResponse, 'url', {\n writable: false,\n enumerable: true,\n configurable: false,\n value: request.url,\n })\n\n return mockedResponse\n }\n\n this.logger.info('no mocked response received!')\n\n return pureFetch(request).then((response) => {\n const responseClone = response.clone()\n this.logger.info('original fetch performed', responseClone)\n\n this.emitter.emit('response', {\n response: responseClone,\n isMockedResponse: false,\n request: interactiveRequest,\n requestId,\n })\n\n return response\n })\n }\n\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n enumerable: true,\n configurable: true,\n value: true,\n })\n\n this.subscriptions.push(() => {\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n value: undefined,\n })\n\n globalThis.fetch = pureFetch\n\n this.logger.info(\n 'restored native \"globalThis.fetch\"!',\n globalThis.fetch.name\n )\n })\n }\n}\n\nfunction createNetworkError(cause: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","/**\n * Returns a boolean indicating whether the given URL string\n * can be parsed into a `URL` instance.\n * A substitute for `URL.canParse()` for Node.js 18.\n */\nexport function canParseUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch (_error) {\n return false\n }\n}\n"],"mappings":"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","names":["mockedResponse"]}
|
|
@@ -3,7 +3,7 @@ import { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest/index.
|
|
|
3
3
|
import 'http';
|
|
4
4
|
import 'https';
|
|
5
5
|
import 'strict-event-emitter';
|
|
6
|
-
import '../Interceptor-
|
|
6
|
+
import '../Interceptor-6696a18d.js';
|
|
7
7
|
import '@open-draft/deferred-promise';
|
|
8
8
|
import '@open-draft/logger';
|
|
9
9
|
|
package/lib/node/presets/node.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkZOS6ZFFLjs = require('../chunk-ZOS6ZFFL.js');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
7
|
-
require('../chunk-
|
|
8
|
-
require('../chunk-
|
|
9
|
-
require('../chunk-
|
|
10
|
-
require('../chunk-
|
|
11
|
-
require('../chunk-
|
|
12
|
-
require('../chunk-
|
|
6
|
+
var _chunkWJBJLZSKjs = require('../chunk-WJBJLZSK.js');
|
|
7
|
+
require('../chunk-DWNXSX4R.js');
|
|
8
|
+
require('../chunk-XRANZXDY.js');
|
|
9
|
+
require('../chunk-OQFUVENL.js');
|
|
10
|
+
require('../chunk-HOHLBCZO.js');
|
|
11
|
+
require('../chunk-AQD7SRYT.js');
|
|
12
|
+
require('../chunk-Q2ZAXW2V.js');
|
|
13
13
|
|
|
14
14
|
// src/presets/node.ts
|
|
15
15
|
var node_default = [
|
|
16
|
-
new (0,
|
|
17
|
-
new (0,
|
|
16
|
+
new (0, _chunkZOS6ZFFLjs.ClientRequestInterceptor)(),
|
|
17
|
+
new (0, _chunkWJBJLZSKjs.XMLHttpRequestInterceptor)()
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ClientRequestInterceptor
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-OIJM4NGH.mjs";
|
|
4
4
|
import {
|
|
5
5
|
XMLHttpRequestInterceptor
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-5JPIH6H2.mjs";
|
|
7
|
+
import "../chunk-ORI3LFXR.mjs";
|
|
8
|
+
import "../chunk-LVMKW3BV.mjs";
|
|
9
|
+
import "../chunk-CDO7LUCB.mjs";
|
|
10
|
+
import "../chunk-YQUDKP67.mjs";
|
|
11
|
+
import "../chunk-7GOGV57P.mjs";
|
|
12
|
+
import "../chunk-TNNJTZLG.mjs";
|
|
13
13
|
|
|
14
14
|
// src/presets/node.ts
|
|
15
15
|
var node_default = [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mswjs/interceptors",
|
|
3
3
|
"description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
|
|
4
|
-
"version": "0.26.
|
|
4
|
+
"version": "0.26.12",
|
|
5
5
|
"main": "./lib/node/index.js",
|
|
6
6
|
"module": "./lib/node/index.mjs",
|
|
7
7
|
"types": "./lib/node/index.d.ts",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"@commitlint/cli": "^16.0.2",
|
|
102
102
|
"@commitlint/config-conventional": "^16.0.0",
|
|
103
103
|
"@open-draft/test-server": "^0.5.1",
|
|
104
|
-
"@ossjs/release": "^0.
|
|
104
|
+
"@ossjs/release": "^0.8.1",
|
|
105
105
|
"@playwright/test": "^1.37.1",
|
|
106
106
|
"@types/cors": "^2.8.12",
|
|
107
107
|
"@types/express": "^4.17.13",
|
package/src/Interceptor.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { Logger } from '@open-draft/logger'
|
|
2
|
-
import { Emitter,
|
|
2
|
+
import { Emitter, Listener } from 'strict-event-emitter'
|
|
3
3
|
|
|
4
4
|
export type InterceptorEventMap = Record<string, any>
|
|
5
5
|
export type InterceptorSubscription = () => void
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Request header name to detect when a single request
|
|
9
|
+
* is being handled by nested interceptors (XHR -> ClientRequest).
|
|
10
|
+
* Obscure by design to prevent collisions with user-defined headers.
|
|
11
|
+
* Ideally, come up with the Interceptor-level mechanism for this.
|
|
12
|
+
* @see https://github.com/mswjs/interceptors/issues/378
|
|
13
|
+
*/
|
|
14
|
+
export const INTERNAL_REQUEST_ID_HEADER_NAME =
|
|
15
|
+
'x-interceptors-internal-request-id'
|
|
16
|
+
|
|
7
17
|
export function getGlobalSymbol<V>(symbol: Symbol): V | undefined {
|
|
8
18
|
return (
|
|
9
19
|
// @ts-ignore https://github.com/Microsoft/TypeScript/issues/24587
|
|
@@ -11,11 +11,6 @@ import { normalizeClientRequestArgs } from './utils/normalizeClientRequestArgs'
|
|
|
11
11
|
import { sleep } from '../../../test/helpers'
|
|
12
12
|
import { HttpRequestEventMap } from '../../glossary'
|
|
13
13
|
|
|
14
|
-
interface ErrorConnectionRefused extends NodeJS.ErrnoException {
|
|
15
|
-
address: string
|
|
16
|
-
port: number
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
const httpServer = new HttpServer((app) => {
|
|
20
15
|
app.post('/comment', (_req, res) => {
|
|
21
16
|
res.status(200).send('original-response')
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ClientRequest, IncomingMessage } from 'http'
|
|
1
|
+
import { ClientRequest, IncomingMessage } from 'node:http'
|
|
2
2
|
import type { Logger } from '@open-draft/logger'
|
|
3
3
|
import { until } from '@open-draft/until'
|
|
4
4
|
import { DeferredPromise } from '@open-draft/deferred-promise'
|
|
@@ -17,10 +17,10 @@ import { cloneIncomingMessage } from './utils/cloneIncomingMessage'
|
|
|
17
17
|
import { createResponse } from './utils/createResponse'
|
|
18
18
|
import { createRequest } from './utils/createRequest'
|
|
19
19
|
import { toInteractiveRequest } from '../../utils/toInteractiveRequest'
|
|
20
|
-
import { uuidv4 } from '../../utils/uuid'
|
|
21
20
|
import { emitAsync } from '../../utils/emitAsync'
|
|
22
21
|
import { getRawFetchHeaders } from '../../utils/getRawFetchHeaders'
|
|
23
22
|
import { isPropertyAccessible } from '../../utils/isPropertyAccessible'
|
|
23
|
+
import { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'
|
|
24
24
|
|
|
25
25
|
export type Protocol = 'http' | 'https'
|
|
26
26
|
|
|
@@ -151,7 +151,7 @@ export class NodeClientRequest extends ClientRequest {
|
|
|
151
151
|
end(...args: any): this {
|
|
152
152
|
this.logger.info('end', args)
|
|
153
153
|
|
|
154
|
-
const requestId =
|
|
154
|
+
const requestId = crypto.randomUUID()
|
|
155
155
|
|
|
156
156
|
const [chunk, encoding, callback] = normalizeClientRequestEndArgs(...args)
|
|
157
157
|
this.logger.info('normalized arguments:', { chunk, encoding, callback })
|
|
@@ -189,8 +189,8 @@ export class NodeClientRequest extends ClientRequest {
|
|
|
189
189
|
// in another (parent) interceptor (like XMLHttpRequest -> ClientRequest).
|
|
190
190
|
// That means some interceptor up the chain has concluded that
|
|
191
191
|
// this request must be performed as-is.
|
|
192
|
-
if (this.
|
|
193
|
-
this.removeHeader(
|
|
192
|
+
if (this.hasHeader(INTERNAL_REQUEST_ID_HEADER_NAME)) {
|
|
193
|
+
this.removeHeader(INTERNAL_REQUEST_ID_HEADER_NAME)
|
|
194
194
|
return this.passthrough(chunk, encoding, callback)
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -9,7 +9,6 @@ import type { WebSocketData, WebSocketTransport } from './WebSocketTransport'
|
|
|
9
9
|
import { WebSocketMessageListener } from './WebSocketOverride'
|
|
10
10
|
import { bindEvent } from './utils/bindEvent'
|
|
11
11
|
import { CloseEvent } from './utils/events'
|
|
12
|
-
import { uuidv4 } from '../../utils/uuid'
|
|
13
12
|
|
|
14
13
|
const kEmitter = Symbol('kEmitter')
|
|
15
14
|
|
|
@@ -37,7 +36,7 @@ export class WebSocketClientConnection
|
|
|
37
36
|
public readonly socket: WebSocket,
|
|
38
37
|
private readonly transport: WebSocketTransport
|
|
39
38
|
) {
|
|
40
|
-
this.id =
|
|
39
|
+
this.id = crypto.randomUUID()
|
|
41
40
|
this.url = new URL(socket.url)
|
|
42
41
|
this[kEmitter] = new EventTarget()
|
|
43
42
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { invariant } from 'outvariant'
|
|
2
|
-
import
|
|
2
|
+
import { kClose, WebSocketOverride } from './WebSocketOverride'
|
|
3
3
|
import type { WebSocketData } from './WebSocketTransport'
|
|
4
4
|
import type { WebSocketClassTransport } from './WebSocketClassTransport'
|
|
5
5
|
import { bindEvent } from './utils/bindEvent'
|
|
6
|
-
import { CancelableMessageEvent } from './utils/events'
|
|
6
|
+
import { CancelableMessageEvent, CloseEvent } from './utils/events'
|
|
7
7
|
|
|
8
8
|
const kEmitter = Symbol('kEmitter')
|
|
9
9
|
|
|
@@ -17,6 +17,7 @@ export class WebSocketServerConnection {
|
|
|
17
17
|
* A WebSocket instance connected to the original server.
|
|
18
18
|
*/
|
|
19
19
|
private realWebSocket?: WebSocket
|
|
20
|
+
private mockCloseController: AbortController
|
|
20
21
|
private [kEmitter]: EventTarget
|
|
21
22
|
|
|
22
23
|
constructor(
|
|
@@ -25,6 +26,7 @@ export class WebSocketServerConnection {
|
|
|
25
26
|
private readonly createConnection: () => WebSocket
|
|
26
27
|
) {
|
|
27
28
|
this[kEmitter] = new EventTarget()
|
|
29
|
+
this.mockCloseController = new AbortController()
|
|
28
30
|
|
|
29
31
|
// Handle incoming events from the actual server.
|
|
30
32
|
// The (mock) WebSocket instance will call this
|
|
@@ -97,36 +99,36 @@ export class WebSocketServerConnection {
|
|
|
97
99
|
*/
|
|
98
100
|
public connect(): void {
|
|
99
101
|
invariant(
|
|
100
|
-
this.readyState
|
|
102
|
+
!this.realWebSocket || this.realWebSocket.readyState !== WebSocket.OPEN,
|
|
101
103
|
'Failed to call "connect()" on the original WebSocket instance: the connection already open'
|
|
102
104
|
)
|
|
103
105
|
|
|
104
|
-
const
|
|
106
|
+
const realWebSocket = this.createConnection()
|
|
105
107
|
|
|
106
108
|
// Inherit the binary type from the mock WebSocket client.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// Close the original connection when the (mock)
|
|
110
|
-
// client closes, regardless of the reason.
|
|
111
|
-
this.socket.addEventListener(
|
|
112
|
-
'close',
|
|
113
|
-
(event) => {
|
|
114
|
-
ws.close(event.code, event.reason)
|
|
115
|
-
},
|
|
116
|
-
{ once: true }
|
|
117
|
-
)
|
|
109
|
+
realWebSocket.binaryType = this.socket.binaryType
|
|
118
110
|
|
|
119
|
-
|
|
111
|
+
realWebSocket.addEventListener('message', (event) => {
|
|
120
112
|
this.transport.onIncoming(event)
|
|
121
113
|
})
|
|
122
114
|
|
|
115
|
+
// Close the original connection when the mock client closes.
|
|
116
|
+
// E.g. "client.close()" was called.
|
|
117
|
+
this.socket.addEventListener('close', this.handleMockClose.bind(this), {
|
|
118
|
+
signal: this.mockCloseController.signal,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Forward the "close" event to let the interceptor handle
|
|
122
|
+
// closures initiated by the original server.
|
|
123
|
+
realWebSocket.addEventListener('close', this.handleRealClose.bind(this))
|
|
124
|
+
|
|
123
125
|
// Forward server errors to the WebSocket client as-is.
|
|
124
126
|
// We may consider exposing them to the interceptor in the future.
|
|
125
|
-
|
|
127
|
+
realWebSocket.addEventListener('error', () => {
|
|
126
128
|
this.socket.dispatchEvent(bindEvent(this.socket, new Event('error')))
|
|
127
129
|
})
|
|
128
130
|
|
|
129
|
-
this.realWebSocket =
|
|
131
|
+
this.realWebSocket = realWebSocket
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
/**
|
|
@@ -145,7 +147,7 @@ export class WebSocketServerConnection {
|
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
/**
|
|
148
|
-
*
|
|
150
|
+
* Remove the listener for the given event.
|
|
149
151
|
*/
|
|
150
152
|
public removeEventListener<K extends keyof WebSocketEventMap>(
|
|
151
153
|
event: K,
|
|
@@ -168,16 +170,25 @@ export class WebSocketServerConnection {
|
|
|
168
170
|
*/
|
|
169
171
|
public send(data: WebSocketData): void {
|
|
170
172
|
const { realWebSocket } = this
|
|
173
|
+
|
|
171
174
|
invariant(
|
|
172
175
|
realWebSocket,
|
|
173
|
-
'Failed to call "server.send()" for "%s": the connection is not open. Did you forget to call "
|
|
176
|
+
'Failed to call "server.send()" for "%s": the connection is not open. Did you forget to call "server.connect()"?',
|
|
174
177
|
this.socket.url
|
|
175
178
|
)
|
|
176
179
|
|
|
180
|
+
// Silently ignore writes on the closed original WebSocket.
|
|
181
|
+
if (
|
|
182
|
+
realWebSocket.readyState === WebSocket.CLOSING ||
|
|
183
|
+
realWebSocket.readyState === WebSocket.CLOSED
|
|
184
|
+
) {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
177
188
|
// Delegate the send to when the original connection is open.
|
|
178
189
|
// Unlike the mock, connecting to the original server may take time
|
|
179
190
|
// so we cannot call this on the next tick.
|
|
180
|
-
if (realWebSocket.readyState ===
|
|
191
|
+
if (realWebSocket.readyState === WebSocket.CONNECTING) {
|
|
181
192
|
realWebSocket.addEventListener(
|
|
182
193
|
'open',
|
|
183
194
|
() => {
|
|
@@ -191,4 +202,59 @@ export class WebSocketServerConnection {
|
|
|
191
202
|
// Send the data to the original WebSocket server.
|
|
192
203
|
realWebSocket.send(data)
|
|
193
204
|
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Close the actual server connection.
|
|
208
|
+
*/
|
|
209
|
+
public close(): void {
|
|
210
|
+
const { realWebSocket } = this
|
|
211
|
+
|
|
212
|
+
invariant(
|
|
213
|
+
realWebSocket,
|
|
214
|
+
'Failed to close server connection for "%s": the connection is not open. Did you forget to call "server.connect()"?',
|
|
215
|
+
this.socket.url
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
realWebSocket.removeEventListener('close', this.handleRealClose)
|
|
219
|
+
|
|
220
|
+
if (
|
|
221
|
+
realWebSocket.readyState === WebSocket.CLOSING ||
|
|
222
|
+
realWebSocket.readyState === WebSocket.CLOSED
|
|
223
|
+
) {
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
realWebSocket.close()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private handleMockClose(_event: Event): void {
|
|
231
|
+
// Close the original connection if the mock client closes.
|
|
232
|
+
if (this.realWebSocket) {
|
|
233
|
+
this.realWebSocket.close()
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private handleRealClose(event: CloseEvent): void {
|
|
238
|
+
// For closures originating from the original server,
|
|
239
|
+
// remove the "close" listener from the mock client.
|
|
240
|
+
// original close -> (?) client[kClose]() --X-> "close" (again).
|
|
241
|
+
this.mockCloseController.abort()
|
|
242
|
+
|
|
243
|
+
const closeEvent = bindEvent(
|
|
244
|
+
this.realWebSocket,
|
|
245
|
+
new CloseEvent('close', event)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
this[kEmitter].dispatchEvent(closeEvent)
|
|
249
|
+
|
|
250
|
+
// If the close event from the server hasn't been prevented,
|
|
251
|
+
// forward the closure to the mock client.
|
|
252
|
+
if (!closeEvent.defaultPrevented) {
|
|
253
|
+
// Close the intercepted client forcefully to
|
|
254
|
+
// allow non-configurable status codes from the server.
|
|
255
|
+
// If the socket has been closed by now, no harm calling
|
|
256
|
+
// this again—it will have no effect.
|
|
257
|
+
this.socket[kClose](event.code, event.reason)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
194
260
|
}
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
import { createProxy } from '../../utils/createProxy'
|
|
12
12
|
import { isDomParserSupportedType } from './utils/isDomParserSupportedType'
|
|
13
13
|
import { parseJson } from '../../utils/parseJson'
|
|
14
|
-
import { uuidv4 } from '../../utils/uuid'
|
|
15
14
|
import { createResponse } from './utils/createResponse'
|
|
15
|
+
import { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'
|
|
16
16
|
|
|
17
17
|
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
|
18
18
|
const IS_NODE = isNodeProcess()
|
|
@@ -48,9 +48,12 @@ export class XMLHttpRequestController {
|
|
|
48
48
|
private responseBuffer: Uint8Array
|
|
49
49
|
private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>
|
|
50
50
|
|
|
51
|
-
constructor(
|
|
51
|
+
constructor(
|
|
52
|
+
readonly initialRequest: XMLHttpRequest,
|
|
53
|
+
public logger: Logger
|
|
54
|
+
) {
|
|
52
55
|
this.events = new Map()
|
|
53
|
-
this.requestId =
|
|
56
|
+
this.requestId = crypto.randomUUID()
|
|
54
57
|
this.requestHeaders = new Headers()
|
|
55
58
|
this.responseBuffer = new Uint8Array()
|
|
56
59
|
|
|
@@ -99,7 +102,7 @@ export class XMLHttpRequestController {
|
|
|
99
102
|
case 'addEventListener': {
|
|
100
103
|
const [eventName, listener] = args as [
|
|
101
104
|
keyof XMLHttpRequestEventTargetEventMap,
|
|
102
|
-
Function
|
|
105
|
+
Function,
|
|
103
106
|
]
|
|
104
107
|
|
|
105
108
|
this.registerEvent(eventName, listener)
|
|
@@ -119,7 +122,7 @@ export class XMLHttpRequestController {
|
|
|
119
122
|
|
|
120
123
|
case 'send': {
|
|
121
124
|
const [body] = args as [
|
|
122
|
-
body?: XMLHttpRequestBodyInit | Document | null
|
|
125
|
+
body?: XMLHttpRequestBodyInit | Document | null,
|
|
123
126
|
]
|
|
124
127
|
|
|
125
128
|
if (body != null) {
|
|
@@ -180,7 +183,10 @@ export class XMLHttpRequestController {
|
|
|
180
183
|
* handle the same request at the same time (e.g. emit the "response" event twice).
|
|
181
184
|
*/
|
|
182
185
|
if (IS_NODE) {
|
|
183
|
-
this.request.setRequestHeader(
|
|
186
|
+
this.request.setRequestHeader(
|
|
187
|
+
INTERNAL_REQUEST_ID_HEADER_NAME,
|
|
188
|
+
this.requestId!
|
|
189
|
+
)
|
|
184
190
|
}
|
|
185
191
|
|
|
186
192
|
return invoke()
|
|
@@ -517,7 +523,7 @@ export class XMLHttpRequestController {
|
|
|
517
523
|
private trigger<
|
|
518
524
|
EventName extends keyof (XMLHttpRequestEventTargetEventMap & {
|
|
519
525
|
readystatechange: ProgressEvent<XMLHttpRequestEventTarget>
|
|
520
|
-
})
|
|
526
|
+
}),
|
|
521
527
|
>(eventName: EventName, options?: ProgressEventInit): void {
|
|
522
528
|
const callback = this.request[`on${eventName}`]
|
|
523
529
|
const event = createEvent(this.request, eventName, options)
|
|
@@ -3,7 +3,6 @@ import { DeferredPromise } from '@open-draft/deferred-promise'
|
|
|
3
3
|
import { until } from '@open-draft/until'
|
|
4
4
|
import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
|
|
5
5
|
import { Interceptor } from '../../Interceptor'
|
|
6
|
-
import { uuidv4 } from '../../utils/uuid'
|
|
7
6
|
import { toInteractiveRequest } from '../../utils/toInteractiveRequest'
|
|
8
7
|
import { emitAsync } from '../../utils/emitAsync'
|
|
9
8
|
import { isPropertyAccessible } from '../../utils/isPropertyAccessible'
|
|
@@ -23,7 +22,7 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
|
23
22
|
)
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
protected setup() {
|
|
25
|
+
protected async setup() {
|
|
27
26
|
const pureFetch = globalThis.fetch
|
|
28
27
|
|
|
29
28
|
invariant(
|
|
@@ -32,7 +31,7 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
|
32
31
|
)
|
|
33
32
|
|
|
34
33
|
globalThis.fetch = async (input, init) => {
|
|
35
|
-
const requestId =
|
|
34
|
+
const requestId = crypto.randomUUID()
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
37
|
* @note Resolve potentially relative request URL
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/interceptors/fetch/index.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/canParseUrl.ts"],"sourcesContent":["import { invariant } from 'outvariant'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { uuidv4 } from '../../utils/uuid'\nimport { toInteractiveRequest } from '../../utils/toInteractiveRequest'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { isPropertyAccessible } from '../../utils/isPropertyAccessible'\nimport { canParseUrl } from '../../utils/canParseUrl'\n\nexport class FetchInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol('fetch')\n\n constructor() {\n super(FetchInterceptor.symbol)\n }\n\n protected checkEnvironment() {\n return (\n typeof globalThis !== 'undefined' &&\n typeof globalThis.fetch !== 'undefined'\n )\n }\n\n protected setup() {\n const pureFetch = globalThis.fetch\n\n invariant(\n !(pureFetch as any)[IS_PATCHED_MODULE],\n 'Failed to patch the \"fetch\" module: already patched.'\n )\n\n globalThis.fetch = async (input, init) => {\n const requestId = uuidv4()\n\n /**\n * @note Resolve potentially relative request URL\n * against the present `location`. This is mainly\n * for native `fetch` in JSDOM.\n * @see https://github.com/mswjs/msw/issues/1625\n */\n const resolvedInput =\n typeof input === 'string' &&\n typeof location !== 'undefined' &&\n !canParseUrl(input)\n ? new URL(input, location.origin)\n : input\n\n const request = new Request(resolvedInput, init)\n\n this.logger.info('[%s] %s', request.method, request.url)\n\n const { interactiveRequest, requestController } =\n toInteractiveRequest(request)\n\n this.logger.info(\n 'emitting the \"request\" event for %d listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n this.emitter.once('request', ({ requestId: pendingRequestId }) => {\n if (pendingRequestId !== requestId) {\n return\n }\n\n if (requestController.responsePromise.state === 'pending') {\n requestController.responsePromise.resolve(undefined)\n }\n })\n\n this.logger.info('awaiting for the mocked response...')\n\n const signal = interactiveRequest.signal\n const requestAborted = new DeferredPromise()\n\n // Signal isn't always defined in react-native.\n if (signal) {\n signal.addEventListener(\n 'abort',\n () => {\n requestAborted.reject(signal.reason)\n },\n { once: true }\n )\n }\n\n const resolverResult = await until(async () => {\n const listenersFinished = emitAsync(this.emitter, 'request', {\n request: interactiveRequest,\n requestId,\n })\n\n await Promise.race([\n requestAborted,\n // Put the listeners invocation Promise in the same race condition\n // with the request abort Promise because otherwise awaiting the listeners\n // would always yield some response (or undefined).\n listenersFinished,\n requestController.responsePromise,\n ])\n\n this.logger.info('all request listeners have been resolved!')\n\n const mockedResponse = await requestController.responsePromise\n this.logger.info('event.respondWith called with:', mockedResponse)\n\n return mockedResponse\n })\n\n if (requestAborted.state === 'rejected') {\n return Promise.reject(requestAborted.rejectionReason)\n }\n\n if (resolverResult.error) {\n return Promise.reject(createNetworkError(resolverResult.error))\n }\n\n const mockedResponse = resolverResult.data\n\n if (mockedResponse && !request.signal?.aborted) {\n this.logger.info('received mocked response:', mockedResponse)\n\n // Reject the request Promise on mocked \"Response.error\" responses.\n if (\n isPropertyAccessible(mockedResponse, 'type') &&\n mockedResponse.type === 'error'\n ) {\n this.logger.info(\n 'received a network error response, rejecting the request promise...'\n )\n\n /**\n * Set the cause of the request promise rejection to the\n * network error Response instance. This different from Undici.\n * Undici will forward the \"response.error\" custom property\n * as the rejection reason but for \"Response.error()\" static method\n * \"response.error\" will equal to undefined, making \"cause\" an empty Error.\n * @see https://github.com/nodejs/undici/blob/83cb522ae0157a19d149d72c7d03d46e34510d0a/lib/fetch/response.js#L344\n */\n return Promise.reject(createNetworkError(mockedResponse))\n }\n\n // Clone the mocked response for the \"response\" event listener.\n // This way, the listener can read the response and not lock its body\n // for the actual fetch consumer.\n const responseClone = mockedResponse.clone()\n\n this.emitter.emit('response', {\n response: responseClone,\n isMockedResponse: true,\n request: interactiveRequest,\n requestId,\n })\n\n // Set the \"response.url\" property to equal the intercepted request URL.\n Object.defineProperty(mockedResponse, 'url', {\n writable: false,\n enumerable: true,\n configurable: false,\n value: request.url,\n })\n\n return mockedResponse\n }\n\n this.logger.info('no mocked response received!')\n\n return pureFetch(request).then((response) => {\n const responseClone = response.clone()\n this.logger.info('original fetch performed', responseClone)\n\n this.emitter.emit('response', {\n response: responseClone,\n isMockedResponse: false,\n request: interactiveRequest,\n requestId,\n })\n\n return response\n })\n }\n\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n enumerable: true,\n configurable: true,\n value: true,\n })\n\n this.subscriptions.push(() => {\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n value: undefined,\n })\n\n globalThis.fetch = pureFetch\n\n this.logger.info(\n 'restored native \"globalThis.fetch\"!',\n globalThis.fetch.name\n )\n })\n }\n}\n\nfunction createNetworkError(cause: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","/**\n * A function that validates if property access is possible on an object\n * without throwing. It returns `true` if the property access is possible\n * and `false` otherwise.\n *\n * Environments like miniflare will throw on property access on certain objects\n * like Request and Response, for unimplemented properties.\n */\nexport function isPropertyAccessible<Obj extends Record<string, any>>(\n obj: Obj,\n key: keyof Obj\n) {\n try {\n obj[key]\n return true\n } catch {\n return false\n }\n}\n","/**\n * Returns a boolean indicating whether the given URL string\n * can be parsed into a `URL` instance.\n * A substitute for `URL.canParse()` for Node.js 18.\n */\nexport function canParseUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch (_error) {\n return false\n }\n}\n"],"mappings":"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","names":["mockedResponse"]}
|