🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@mswjs/interceptors

Package Overview
Dependencies
Maintainers
1
Versions
197
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mswjs/interceptors - npm Package Compare versions

Comparing version
0.41.7
to
0.41.8
+236
lib/browser/fetch-BJwfdses.mjs
import { r as Interceptor, t as createRequestId } from "./createRequestId-DYCsFHOi.mjs";
import { a as canParseUrl, i as FetchResponse, n as setRawRequest, o as RequestController, r as FetchRequest } from "./getRawRequest-B1BqgWG6.mjs";
import { n as patchesRegistry, r as emitAsync, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-C8zq1MCg.mjs";
import { n as isResponseError, t as handleRequest } from "./handleRequest-1Yqu_ho_.mjs";
import { DeferredPromise } from "@open-draft/deferred-promise";
import { until } from "@open-draft/until";
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.browser.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
console.warn("[Interceptors]: Brotli decompression of response streams is not supported in the browser");
super({ transform(chunk, controller) {
controller.enqueue(chunk);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = createRequestId();
const request = new FetchRequest(typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) setRawRequest(request, input);
const responsePromise = new DeferredPromise();
const controller = new RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await until(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = FetchResponse.clone(originalResponse);
await emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await emitAsync(this.emitter, "response", {
response: FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
export { FetchInterceptor as t };
//# sourceMappingURL=fetch-BJwfdses.mjs.map
{"version":3,"file":"fetch-BJwfdses.mjs","names":["locationUrl: URL","requestInit: RequestInit","readable","fetchProxy: typeof fetch","response"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","export class BrotliDecompressionStream extends TransformStream {\n constructor() {\n console.warn(\n '[Interceptors]: Brotli decompression of response streams is not supported in the browser'\n )\n\n super({\n transform(chunk, controller) {\n // Keep the stream as passthrough, it does nothing.\n controller.enqueue(chunk)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
const require_createRequestId = require('./createRequestId-DOf8Ktjs.cjs');
const require_getRawRequest = require('./getRawRequest-DdfaiPVH.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-CS7adEvV.cjs');
const require_handleRequest = require('./handleRequest-Cz4_wmQ9.cjs');
let _open_draft_deferred_promise = require("@open-draft/deferred-promise");
let _open_draft_until = require("@open-draft/until");
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.browser.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
console.warn("[Interceptors]: Brotli decompression of response streams is not supported in the browser");
super({ transform(chunk, controller) {
controller.enqueue(chunk);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends require_createRequestId.Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = require_createRequestId.createRequestId();
const request = new require_getRawRequest.FetchRequest(typeof input === "string" && typeof location !== "undefined" && !require_getRawRequest.canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) require_getRawRequest.setRawRequest(request, input);
const responsePromise = new _open_draft_deferred_promise.DeferredPromise();
const controller = new require_getRawRequest.RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await (0, _open_draft_until.until)(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = require_getRawRequest.FetchResponse.clone(originalResponse);
await require_hasConfigurableGlobal.emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (require_handleRequest.isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new require_getRawRequest.FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (require_getRawRequest.FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await require_hasConfigurableGlobal.emitAsync(this.emitter, "response", {
response: require_getRawRequest.FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
Object.defineProperty(exports, 'FetchInterceptor', {
enumerable: true,
get: function () {
return FetchInterceptor;
}
});
//# sourceMappingURL=fetch-M8iXkZ3L.cjs.map
{"version":3,"file":"fetch-M8iXkZ3L.cjs","names":["locationUrl: URL","requestInit: RequestInit","readable","Interceptor","hasConfigurableGlobal","fetchProxy: typeof fetch","createRequestId","FetchRequest","canParseUrl","DeferredPromise","RequestController","FetchResponse","emitAsync","isResponseError","response","handleRequest","patchesRegistry"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","export class BrotliDecompressionStream extends TransformStream {\n constructor() {\n console.warn(\n '[Interceptors]: Brotli decompression of response streams is not supported in the browser'\n )\n\n super({\n transform(chunk, controller) {\n // Keep the stream as passthrough, it does nothing.\n controller.enqueue(chunk)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":";;;;;;;;AAAA,SAAgB,mBAAmB,OAAiB;AAClD,QAAO,OAAO,uBAAO,IAAI,UAAU,kBAAkB,EAAE,EACrD,OACD,CAAC;;;;;ACDJ,MAAM,uBAAuB;CAC3B;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,iBAAiB,OAAO,iBAAiB;;;;AAK/C,eAAsB,oBACpB,SACA,UACmB;AACnB,KAAI,SAAS,WAAW,OAAO,QAAQ,QAAQ,KAC7C,QAAO,QAAQ,OAAO,oBAAoB,CAAC;CAG7C,MAAM,aAAa,IAAI,IAAI,QAAQ,IAAI;CAEvC,IAAIA;AACJ,KAAI;AAEF,gBAAc,IAAI,IAAI,SAAS,QAAQ,IAAI,WAAW,EAAG,QAAQ,IAAI;UAC9D,OAAO;AACd,SAAO,QAAQ,OAAO,mBAAmB,MAAM,CAAC;;AAGlD,KACE,EAAE,YAAY,aAAa,WAAW,YAAY,aAAa,UAE/D,QAAO,QAAQ,OACb,mBAAmB,sCAAsC,CAC1D;AAGH,KAAI,QAAQ,IAAI,SAAS,eAAe,GAAG,GACzC,QAAO,QAAQ,OAAO,mBAAmB,0BAA0B,CAAC;AAGtE,QAAO,eAAe,SAAS,gBAAgB,EAC7C,QAAQ,QAAQ,IAAI,SAAS,eAAe,IAAI,KAAK,GACtD,CAAC;AAEF,KACE,QAAQ,SAAS,WAChB,YAAY,YAAY,YAAY,aACrC,CAAC,WAAW,YAAY,YAAY,CAEpC,QAAO,QAAQ,OACb,mBAAmB,qDAAmD,CACvE;CAGH,MAAMC,cAA2B,EAAE;AAEnC,KACG,CAAC,KAAK,IAAI,CAAC,SAAS,SAAS,OAAO,IAAI,QAAQ,WAAW,UAC3D,SAAS,WAAW,OAAO,CAAC,CAAC,QAAQ,MAAM,CAAC,SAAS,QAAQ,OAAO,EACrE;AACA,cAAY,SAAS;AACrB,cAAY,OAAO;AAEnB,uBAAqB,SAAS,eAAe;AAC3C,WAAQ,QAAQ,OAAO,WAAW;IAClC;;AAGJ,KAAI,CAAC,WAAW,YAAY,YAAY,EAAE;AACxC,UAAQ,QAAQ,OAAO,gBAAgB;AACvC,UAAQ,QAAQ,OAAO,sBAAsB;AAC7C,UAAQ,QAAQ,OAAO,SAAS;AAChC,UAAQ,QAAQ,OAAO,OAAO;;;;;;;AAShC,aAAY,UAAU,QAAQ;CAC9B,MAAM,gBAAgB,MAAM,MAAM,IAAI,QAAQ,aAAa,YAAY,CAAC;AACxE,QAAO,eAAe,eAAe,cAAc;EACjD,OAAO;EACP,cAAc;EACf,CAAC;AAEF,QAAO;;;;;AAMT,SAAS,WAAW,MAAW,OAAqB;AAClD,KAAI,KAAK,WAAW,MAAM,UAAU,KAAK,WAAW,OAClD,QAAO;AAGT,KACE,KAAK,aAAa,MAAM,YACxB,KAAK,aAAa,MAAM,YACxB,KAAK,SAAS,MAAM,KAEpB,QAAO;AAGT,QAAO;;;;;AChHT,IAAa,4BAAb,cAA+C,gBAAgB;CAC7D,cAAc;AACZ,UAAQ,KACN,2FACD;AAED,QAAM,EACJ,UAAU,OAAO,YAAY;AAE3B,cAAW,QAAQ,MAAM;KAE5B,CAAC;;;;;;ACNN,IAAM,iBAAN,cAA6B,gBAAgB;CAC3C,YACE,kBACA,GAAG,YACH;AACA,QAAM,EAAE,EAAE,GAAG,WAAW;EAExB,MAAM,WAAW,CAAC,MAAM,UAAiB,GAAG,iBAAiB,CAAC,QAC3D,YAAU,cAAcC,WAAS,YAAY,UAAU,CACzD;AAED,SAAO,eAAe,MAAM,YAAY,EACtC,MAAM;AACJ,UAAO;KAEV,CAAC;;;AAIN,SAAgB,qBAAqB,iBAAwC;AAC3E,QAAO,gBACJ,aAAa,CACb,MAAM,IAAI,CACV,KAAK,WAAW,OAAO,MAAM,CAAC;;AAGnC,SAAS,0BACP,iBACwB;AACxB,KAAI,oBAAoB,GACtB,QAAO;CAGT,MAAM,UAAU,qBAAqB,gBAAgB;AAErD,KAAI,QAAQ,WAAW,EACrB,QAAO;AAoBT,QAAO,IAAI,eAjBU,QAAQ,aAC1B,cAAc,WAAW;AACxB,MAAI,WAAW,UAAU,WAAW,SAClC,QAAO,aAAa,OAAO,IAAI,oBAAoB,OAAO,CAAC;WAClD,WAAW,UACpB,QAAO,aAAa,OAAO,IAAI,oBAAoB,UAAU,CAAC;WACrD,WAAW,KACpB,QAAO,aAAa,OAAO,IAAI,2BAA2B,CAAC;MAE3D,cAAa,SAAS;AAGxB,SAAO;IAET,EAAE,CACH,CAEsC;;AAGzC,SAAgB,mBACd,UAC4B;AAC5B,KAAI,SAAS,SAAS,KACpB,QAAO;CAGT,MAAM,sBAAsB,0BAC1B,SAAS,QAAQ,IAAI,mBAAmB,IAAI,GAC7C;AAED,KAAI,CAAC,oBACH,QAAO;AAMT,UAAS,KAAK,OAAO,oBAAoB,SAAS;AAClD,QAAO,oBAAoB;;;;;ACjE7B,IAAa,mBAAb,MAAa,yBAAyBC,oCAAiC;;gBACrD,OAAO,QAAQ;;CAE/B,cAAc;AACZ,QAAM,iBAAiB,OAAO;;CAGhC,AAAU,mBAAmB;AAC3B,SAAOC,oDAAsB,QAAQ;;CAGvC,MAAgB,QAAQ;EACtB,MAAM,SAAS,KAAK,OAAO,OAAO,QAAQ;EAE1C,MAAM,YAAY,WAAW;EAE7B,MAAMC,aAA2B,OAAO,OAAO,SAAS;GACtD,MAAM,YAAYC,yCAAiB;GAenC,MAAM,UAAU,IAAIC,mCANlB,OAAO,UAAU,YACjB,OAAO,aAAa,eACpB,CAACC,kCAAY,MAAM,GACf,IAAI,IAAI,OAAO,SAAS,KAAK,GAC7B,OAE0C,KAAK;;;;AAKrD,OAAI,iBAAiB,QACnB,qCAAc,SAAS,MAAM;GAG/B,MAAM,kBAAkB,IAAIC,8CAA2B;GAEvD,MAAM,aAAa,IAAIC,wCAAkB,SAAS;IAChD,aAAa,YAAY;AACvB,UAAK,OAAO,KAAK,+CAA+C;;;;;;;KAQhE,MAAM,+BAA+B,QAAQ,OAAO;KAGpD,MAAM,EAAE,OAAO,eAAe,MAAM,qBAAqB,yCACjD,UAAU,QAAQ,CACzB;AAED,SAAI,cACF,QAAO,gBAAgB,OAAO,cAAc;AAG9C,UAAK,OAAO,KAAK,4BAA4B,iBAAiB;AAE9D,SAAI,KAAK,QAAQ,cAAc,WAAW,GAAG,GAAG;AAC9C,WAAK,OAAO,KAAK,qCAAmC;MAEpD,MAAM,gBAAgBC,oCAAc,MAAM,iBAAiB;AAC3D,YAAMC,wCAAU,KAAK,SAAS,YAAY;OACxC,UAAU;OACV,kBAAkB;OAClB,SAAS;OACT;OACD,CAAC;;AAKJ,qBAAgB,QAAQ,iBAAiB;;IAE3C,aAAa,OAAO,gBAAgB;AAElC,SAAIC,sCAAgB,YAAY,EAAE;AAChC,WAAK,OAAO,KAAK,wBAAwB,EAAE,UAAU,aAAa,CAAC;AACnE,sBAAgB,OAAO,mBAAmB,YAAY,CAAC;AACvD;;AAGF,UAAK,OAAO,KAAK,6BAA6B,EAC5C,aACD,CAAC;KAIF,MAAM,WAAW,IAAIF,oCADM,mBAAmB,YAAY,IAElC,YAAY,MAClC;MACE,KAAK,QAAQ;MACb,QAAQ,YAAY;MACpB,YAAY,YAAY;MACxB,SAAS,YAAY;MACtB,CACF;;;;;;;AAQD,SAAIA,oCAAc,mBAAmB,SAAS,OAAO,EAAE;AAGrD,UAAI,QAAQ,aAAa,SAAS;AAChC,uBAAgB,OAAO,mBAAmB,sBAAsB,CAAC;AACjE;;AAGF,UAAI,QAAQ,aAAa,UAAU;AACjC,2BAAoB,SAAS,SAAS,CAAC,MACpC,eAAa;AACZ,wBAAgB,QAAQG,WAAS;WAElC,WAAW;AACV,wBAAgB,OAAO,OAAO;SAEjC;AACD;;;AAIJ,SAAI,KAAK,QAAQ,cAAc,WAAW,GAAG,GAAG;AAC9C,WAAK,OAAO,KAAK,qCAAmC;AAKpD,YAAMF,wCAAU,KAAK,SAAS,YAAY;OAIxC,UAAUD,oCAAc,MAAM,SAAS;OACvC,kBAAkB;OAClB;OACA;OACD,CAAC;;AAGJ,qBAAgB,QAAQ,SAAS;;IAEnC,YAAY,WAAW;AACrB,UAAK,OAAO,KAAK,6BAA6B,EAAE,QAAQ,CAAC;AACzD,qBAAgB,OAAO,OAAO;;IAEjC,CAAC;AAEF,QAAK,OAAO,KAAK,WAAW,QAAQ,QAAQ,QAAQ,IAAI;AACxD,QAAK,OAAO,KAAK,sCAAsC;AAEvD,QAAK,OAAO,KACV,wDACA,KAAK,QAAQ,cAAc,UAAU,CACtC;AAED,SAAMI,oCAAc;IAClB;IACA;IACA,SAAS,KAAK;IACd;IACD,CAAC;AAEF,UAAO;;AAGT,SAAO,KAAK,2BAA2B;AAEvC,OAAK,cAAc,KACjBC,8CAAgB,WAAW,YAAY,eAAe,WAAW,CAClE;AAED,SAAO,KAAK,yBAAyB,WAAW,MAAM,KAAK"}
import { o as RequestController, s as InterceptorError } from "./getRawRequest-B1BqgWG6.mjs";
import { r as emitAsync } from "./hasConfigurableGlobal-C8zq1MCg.mjs";
import { DeferredPromise } from "@open-draft/deferred-promise";
import { until } from "@open-draft/until";
//#region src/utils/isObject.ts
/**
* Determines if a given value is an instance of object.
*/
function isObject(value, loose = false) {
return loose ? Object.prototype.toString.call(value).startsWith("[object ") : Object.prototype.toString.call(value) === "[object Object]";
}
//#endregion
//#region src/utils/isPropertyAccessible.ts
/**
* A function that validates if property access is possible on an object
* without throwing. It returns `true` if the property access is possible
* and `false` otherwise.
*
* Environments like miniflare will throw on property access on certain objects
* like Request and Response, for unimplemented properties.
*/
function isPropertyAccessible(obj, key) {
try {
obj[key];
return true;
} catch {
return false;
}
}
//#endregion
//#region src/utils/responseUtils.ts
/**
* Creates a generic 500 Unhandled Exception response.
*/
function createServerErrorResponse(body) {
return new Response(JSON.stringify(body instanceof Error ? {
name: body.name,
message: body.message,
stack: body.stack
} : body), {
status: 500,
statusText: "Unhandled Exception",
headers: { "Content-Type": "application/json" }
});
}
/**
* Check if the given response is a `Response.error()`.
*
* @note Some environments, like Miniflare (Cloudflare) do not
* implement the "Response.type" property and throw on its access.
* Safely check if we can access "type" on "Response" before continuing.
* @see https://github.com/mswjs/msw/issues/1834
*/
function isResponseError(response) {
return response != null && response instanceof Response && isPropertyAccessible(response, "type") && response.type === "error";
}
/**
* Check if the given value is a `Response` or a Response-like object.
* This is different from `value instanceof Response` because it supports
* custom `Response` constructors, like the one when using Undici directly.
*/
function isResponseLike(value) {
return isObject(value, true) && isPropertyAccessible(value, "status") && isPropertyAccessible(value, "statusText") && isPropertyAccessible(value, "bodyUsed");
}
//#endregion
//#region src/utils/isNodeLikeError.ts
function isNodeLikeError(error) {
if (error == null) return false;
if (!(error instanceof Error)) return false;
return "code" in error && "errno" in error;
}
//#endregion
//#region src/utils/handleRequest.ts
async function handleRequest(options) {
const handleResponse = async (response) => {
if (response instanceof Error) {
await options.controller.errorWith(response);
return true;
}
if (isResponseError(response)) {
await options.controller.respondWith(response);
return true;
}
/**
* Handle normal responses or response-like objects.
* @note This must come before the arbitrary object check
* since Response instances are, in fact, objects.
*/
if (isResponseLike(response)) {
await options.controller.respondWith(response);
return true;
}
if (isObject(response)) {
await options.controller.errorWith(response);
return true;
}
return false;
};
const handleResponseError = async (error) => {
if (error instanceof InterceptorError) throw result.error;
if (isNodeLikeError(error)) {
await options.controller.errorWith(error);
return true;
}
if (error instanceof Response) return await handleResponse(error);
return false;
};
const requestAbortPromise = new DeferredPromise();
/**
* @note `signal` is not always defined in React Native.
*/
if (options.request.signal) {
if (options.request.signal.aborted) {
await options.controller.errorWith(options.request.signal.reason);
return;
}
options.request.signal.addEventListener("abort", () => {
requestAbortPromise.reject(options.request.signal.reason);
}, { once: true });
}
const result = await until(async () => {
const requestListenersPromise = emitAsync(options.emitter, "request", {
requestId: options.requestId,
request: options.request,
controller: options.controller
});
await Promise.race([
requestAbortPromise,
requestListenersPromise,
options.controller.handled
]);
});
if (requestAbortPromise.state === "rejected") {
await options.controller.errorWith(requestAbortPromise.rejectionReason);
return;
}
if (result.error) {
if (await handleResponseError(result.error)) return;
if (options.emitter.listenerCount("unhandledException") > 0) {
const unhandledExceptionController = new RequestController(options.request, {
passthrough() {},
async respondWith(response) {
await handleResponse(response);
},
async errorWith(reason) {
/**
* @note Handle the result of the unhandled controller
* in the same way as the original request controller.
* The exception here is that thrown errors within the
* "unhandledException" event do NOT result in another
* emit of the same event. They are forwarded as-is.
*/
await options.controller.errorWith(reason);
}
});
await emitAsync(options.emitter, "unhandledException", {
error: result.error,
request: options.request,
requestId: options.requestId,
controller: unhandledExceptionController
});
if (unhandledExceptionController.readyState !== RequestController.PENDING) return;
}
await options.controller.respondWith(createServerErrorResponse(result.error));
return;
}
if (options.controller.readyState === RequestController.PENDING) return await options.controller.passthrough();
return options.controller.handled;
}
//#endregion
export { isResponseError as n, handleRequest as t };
//# sourceMappingURL=handleRequest-1Yqu_ho_.mjs.map
{"version":3,"file":"handleRequest-1Yqu_ho_.mjs","names":[],"sources":["../../src/utils/isObject.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/responseUtils.ts","../../src/utils/isNodeLikeError.ts","../../src/utils/handleRequest.ts"],"sourcesContent":["/**\n * Determines if a given value is an instance of object.\n */\nexport function isObject<T>(value: any, loose = false): value is T {\n return loose\n ? Object.prototype.toString.call(value).startsWith('[object ')\n : Object.prototype.toString.call(value) === '[object Object]'\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","import { isObject } from './isObject'\nimport { isPropertyAccessible } from './isPropertyAccessible'\n\n/**\n * Creates a generic 500 Unhandled Exception response.\n */\nexport function createServerErrorResponse(body: unknown): Response {\n return new Response(\n JSON.stringify(\n body instanceof Error\n ? {\n name: body.name,\n message: body.message,\n stack: body.stack,\n }\n : body\n ),\n {\n status: 500,\n statusText: 'Unhandled Exception',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n}\n\nexport type ResponseError = Response & { type: 'error' }\n\n/**\n * Check if the given response is a `Response.error()`.\n *\n * @note Some environments, like Miniflare (Cloudflare) do not\n * implement the \"Response.type\" property and throw on its access.\n * Safely check if we can access \"type\" on \"Response\" before continuing.\n * @see https://github.com/mswjs/msw/issues/1834\n */\nexport function isResponseError(response: unknown): response is ResponseError {\n return (\n response != null &&\n response instanceof Response &&\n isPropertyAccessible(response, 'type') &&\n response.type === 'error'\n )\n}\n\n/**\n * Check if the given value is a `Response` or a Response-like object.\n * This is different from `value instanceof Response` because it supports\n * custom `Response` constructors, like the one when using Undici directly.\n */\nexport function isResponseLike(value: unknown): value is Response {\n return (\n isObject<Record<string, any>>(value, true) &&\n isPropertyAccessible(value, 'status') &&\n isPropertyAccessible(value, 'statusText') &&\n isPropertyAccessible(value, 'bodyUsed')\n )\n}\n","export function isNodeLikeError(\n error: unknown\n): error is NodeJS.ErrnoException {\n if (error == null) {\n return false\n }\n\n if (!(error instanceof Error)) {\n return false\n }\n\n return 'code' in error && 'errno' in error\n}\n","import type { Emitter } from 'strict-event-emitter'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport type { HttpRequestEventMap } from '../glossary'\nimport { emitAsync } from './emitAsync'\nimport { RequestController } from '../RequestController'\nimport {\n createServerErrorResponse,\n isResponseError,\n isResponseLike,\n} from './responseUtils'\nimport { InterceptorError } from '../InterceptorError'\nimport { isNodeLikeError } from './isNodeLikeError'\nimport { isObject } from './isObject'\n\ninterface HandleRequestOptions {\n requestId: string\n request: Request\n emitter: Emitter<HttpRequestEventMap>\n controller: RequestController\n}\n\nexport async function handleRequest(\n options: HandleRequestOptions\n): Promise<void> {\n const handleResponse = async (\n response: Response | Error | Record<string, any>\n ) => {\n if (response instanceof Error) {\n await options.controller.errorWith(response)\n return true\n }\n\n // Handle \"Response.error()\" instances.\n if (isResponseError(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n /**\n * Handle normal responses or response-like objects.\n * @note This must come before the arbitrary object check\n * since Response instances are, in fact, objects.\n */\n if (isResponseLike(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n // Handle arbitrary objects provided to `.errorWith(reason)`.\n if (isObject(response)) {\n await options.controller.errorWith(response)\n return true\n }\n\n return false\n }\n\n const handleResponseError = async (error: unknown): Promise<boolean> => {\n // Forward the special interceptor error instances\n // to the developer. These must not be handled in any way.\n if (error instanceof InterceptorError) {\n throw result.error\n }\n\n // Support mocking Node.js-like errors.\n if (isNodeLikeError(error)) {\n await options.controller.errorWith(error)\n return true\n }\n\n // Handle thrown responses.\n if (error instanceof Response) {\n return await handleResponse(error)\n }\n\n return false\n }\n\n // Add the last \"request\" listener to check if the request\n // has been handled in any way. If it hasn't, resolve the\n // response promise with undefined.\n // options.emitter.once('request', async ({ requestId: pendingRequestId }) => {\n // if (\n // pendingRequestId === options.requestId &&\n // options.controller.readyState === RequestController.PENDING\n // ) {\n // await options.controller.passthrough()\n // }\n // })\n\n const requestAbortPromise = new DeferredPromise<void, unknown>()\n\n /**\n * @note `signal` is not always defined in React Native.\n */\n if (options.request.signal) {\n if (options.request.signal.aborted) {\n await options.controller.errorWith(options.request.signal.reason)\n return\n }\n\n options.request.signal.addEventListener(\n 'abort',\n () => {\n requestAbortPromise.reject(options.request.signal.reason)\n },\n { once: true }\n )\n }\n\n const result = await until(async () => {\n // Emit the \"request\" event and wait until all the listeners\n // for that event are finished (e.g. async listeners awaited).\n // By the end of this promise, the developer cannot affect the\n // request anymore.\n const requestListenersPromise = emitAsync(options.emitter, 'request', {\n requestId: options.requestId,\n request: options.request,\n controller: options.controller,\n })\n\n await Promise.race([\n // Short-circuit the request handling promise if the request gets aborted.\n requestAbortPromise,\n requestListenersPromise,\n options.controller.handled,\n ])\n })\n\n // Handle the request being aborted while waiting for the request listeners.\n if (requestAbortPromise.state === 'rejected') {\n await options.controller.errorWith(requestAbortPromise.rejectionReason)\n return\n }\n\n if (result.error) {\n // Handle the error during the request listener execution.\n // These can be thrown responses or request errors.\n if (await handleResponseError(result.error)) {\n return\n }\n\n // If the developer has added \"unhandledException\" listeners,\n // allow them to handle the error. They can translate it to a\n // mocked response, network error, or forward it as-is.\n if (options.emitter.listenerCount('unhandledException') > 0) {\n // Create a new request controller just for the unhandled exception case.\n // This is needed because the original controller might have been already\n // interacted with (e.g. \"respondWith\" or \"errorWith\" called on it).\n const unhandledExceptionController = new RequestController(\n options.request,\n {\n /**\n * @note Intentionally empty passthrough handle.\n * This controller is created within another controller and we only need\n * to know if `unhandledException` listeners handled the request.\n */\n passthrough() {},\n async respondWith(response) {\n await handleResponse(response)\n },\n async errorWith(reason) {\n /**\n * @note Handle the result of the unhandled controller\n * in the same way as the original request controller.\n * The exception here is that thrown errors within the\n * \"unhandledException\" event do NOT result in another\n * emit of the same event. They are forwarded as-is.\n */\n await options.controller.errorWith(reason)\n },\n }\n )\n\n await emitAsync(options.emitter, 'unhandledException', {\n error: result.error,\n request: options.request,\n requestId: options.requestId,\n controller: unhandledExceptionController,\n })\n\n // If all the \"unhandledException\" listeners have finished\n // but have not handled the request in any way, passthrough.\n if (\n unhandledExceptionController.readyState !== RequestController.PENDING\n ) {\n return\n }\n }\n\n // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.\n await options.controller.respondWith(\n createServerErrorResponse(result.error)\n )\n return\n }\n\n // If the request hasn't been handled by this point, passthrough.\n if (options.controller.readyState === RequestController.PENDING) {\n return await options.controller.passthrough()\n }\n\n return options.controller.handled\n}\n"],"mappings":"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"}
const require_getRawRequest = require('./getRawRequest-DdfaiPVH.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-CS7adEvV.cjs');
let _open_draft_deferred_promise = require("@open-draft/deferred-promise");
let _open_draft_until = require("@open-draft/until");
//#region src/utils/isObject.ts
/**
* Determines if a given value is an instance of object.
*/
function isObject(value, loose = false) {
return loose ? Object.prototype.toString.call(value).startsWith("[object ") : Object.prototype.toString.call(value) === "[object Object]";
}
//#endregion
//#region src/utils/isPropertyAccessible.ts
/**
* A function that validates if property access is possible on an object
* without throwing. It returns `true` if the property access is possible
* and `false` otherwise.
*
* Environments like miniflare will throw on property access on certain objects
* like Request and Response, for unimplemented properties.
*/
function isPropertyAccessible(obj, key) {
try {
obj[key];
return true;
} catch {
return false;
}
}
//#endregion
//#region src/utils/responseUtils.ts
/**
* Creates a generic 500 Unhandled Exception response.
*/
function createServerErrorResponse(body) {
return new Response(JSON.stringify(body instanceof Error ? {
name: body.name,
message: body.message,
stack: body.stack
} : body), {
status: 500,
statusText: "Unhandled Exception",
headers: { "Content-Type": "application/json" }
});
}
/**
* Check if the given response is a `Response.error()`.
*
* @note Some environments, like Miniflare (Cloudflare) do not
* implement the "Response.type" property and throw on its access.
* Safely check if we can access "type" on "Response" before continuing.
* @see https://github.com/mswjs/msw/issues/1834
*/
function isResponseError(response) {
return response != null && response instanceof Response && isPropertyAccessible(response, "type") && response.type === "error";
}
/**
* Check if the given value is a `Response` or a Response-like object.
* This is different from `value instanceof Response` because it supports
* custom `Response` constructors, like the one when using Undici directly.
*/
function isResponseLike(value) {
return isObject(value, true) && isPropertyAccessible(value, "status") && isPropertyAccessible(value, "statusText") && isPropertyAccessible(value, "bodyUsed");
}
//#endregion
//#region src/utils/isNodeLikeError.ts
function isNodeLikeError(error) {
if (error == null) return false;
if (!(error instanceof Error)) return false;
return "code" in error && "errno" in error;
}
//#endregion
//#region src/utils/handleRequest.ts
async function handleRequest(options) {
const handleResponse = async (response) => {
if (response instanceof Error) {
await options.controller.errorWith(response);
return true;
}
if (isResponseError(response)) {
await options.controller.respondWith(response);
return true;
}
/**
* Handle normal responses or response-like objects.
* @note This must come before the arbitrary object check
* since Response instances are, in fact, objects.
*/
if (isResponseLike(response)) {
await options.controller.respondWith(response);
return true;
}
if (isObject(response)) {
await options.controller.errorWith(response);
return true;
}
return false;
};
const handleResponseError = async (error) => {
if (error instanceof require_getRawRequest.InterceptorError) throw result.error;
if (isNodeLikeError(error)) {
await options.controller.errorWith(error);
return true;
}
if (error instanceof Response) return await handleResponse(error);
return false;
};
const requestAbortPromise = new _open_draft_deferred_promise.DeferredPromise();
/**
* @note `signal` is not always defined in React Native.
*/
if (options.request.signal) {
if (options.request.signal.aborted) {
await options.controller.errorWith(options.request.signal.reason);
return;
}
options.request.signal.addEventListener("abort", () => {
requestAbortPromise.reject(options.request.signal.reason);
}, { once: true });
}
const result = await (0, _open_draft_until.until)(async () => {
const requestListenersPromise = require_hasConfigurableGlobal.emitAsync(options.emitter, "request", {
requestId: options.requestId,
request: options.request,
controller: options.controller
});
await Promise.race([
requestAbortPromise,
requestListenersPromise,
options.controller.handled
]);
});
if (requestAbortPromise.state === "rejected") {
await options.controller.errorWith(requestAbortPromise.rejectionReason);
return;
}
if (result.error) {
if (await handleResponseError(result.error)) return;
if (options.emitter.listenerCount("unhandledException") > 0) {
const unhandledExceptionController = new require_getRawRequest.RequestController(options.request, {
passthrough() {},
async respondWith(response) {
await handleResponse(response);
},
async errorWith(reason) {
/**
* @note Handle the result of the unhandled controller
* in the same way as the original request controller.
* The exception here is that thrown errors within the
* "unhandledException" event do NOT result in another
* emit of the same event. They are forwarded as-is.
*/
await options.controller.errorWith(reason);
}
});
await require_hasConfigurableGlobal.emitAsync(options.emitter, "unhandledException", {
error: result.error,
request: options.request,
requestId: options.requestId,
controller: unhandledExceptionController
});
if (unhandledExceptionController.readyState !== require_getRawRequest.RequestController.PENDING) return;
}
await options.controller.respondWith(createServerErrorResponse(result.error));
return;
}
if (options.controller.readyState === require_getRawRequest.RequestController.PENDING) return await options.controller.passthrough();
return options.controller.handled;
}
//#endregion
Object.defineProperty(exports, 'handleRequest', {
enumerable: true,
get: function () {
return handleRequest;
}
});
Object.defineProperty(exports, 'isResponseError', {
enumerable: true,
get: function () {
return isResponseError;
}
});
//# sourceMappingURL=handleRequest-Cz4_wmQ9.cjs.map
{"version":3,"file":"handleRequest-Cz4_wmQ9.cjs","names":["InterceptorError","DeferredPromise","emitAsync","RequestController"],"sources":["../../src/utils/isObject.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/responseUtils.ts","../../src/utils/isNodeLikeError.ts","../../src/utils/handleRequest.ts"],"sourcesContent":["/**\n * Determines if a given value is an instance of object.\n */\nexport function isObject<T>(value: any, loose = false): value is T {\n return loose\n ? Object.prototype.toString.call(value).startsWith('[object ')\n : Object.prototype.toString.call(value) === '[object Object]'\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","import { isObject } from './isObject'\nimport { isPropertyAccessible } from './isPropertyAccessible'\n\n/**\n * Creates a generic 500 Unhandled Exception response.\n */\nexport function createServerErrorResponse(body: unknown): Response {\n return new Response(\n JSON.stringify(\n body instanceof Error\n ? {\n name: body.name,\n message: body.message,\n stack: body.stack,\n }\n : body\n ),\n {\n status: 500,\n statusText: 'Unhandled Exception',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n}\n\nexport type ResponseError = Response & { type: 'error' }\n\n/**\n * Check if the given response is a `Response.error()`.\n *\n * @note Some environments, like Miniflare (Cloudflare) do not\n * implement the \"Response.type\" property and throw on its access.\n * Safely check if we can access \"type\" on \"Response\" before continuing.\n * @see https://github.com/mswjs/msw/issues/1834\n */\nexport function isResponseError(response: unknown): response is ResponseError {\n return (\n response != null &&\n response instanceof Response &&\n isPropertyAccessible(response, 'type') &&\n response.type === 'error'\n )\n}\n\n/**\n * Check if the given value is a `Response` or a Response-like object.\n * This is different from `value instanceof Response` because it supports\n * custom `Response` constructors, like the one when using Undici directly.\n */\nexport function isResponseLike(value: unknown): value is Response {\n return (\n isObject<Record<string, any>>(value, true) &&\n isPropertyAccessible(value, 'status') &&\n isPropertyAccessible(value, 'statusText') &&\n isPropertyAccessible(value, 'bodyUsed')\n )\n}\n","export function isNodeLikeError(\n error: unknown\n): error is NodeJS.ErrnoException {\n if (error == null) {\n return false\n }\n\n if (!(error instanceof Error)) {\n return false\n }\n\n return 'code' in error && 'errno' in error\n}\n","import type { Emitter } from 'strict-event-emitter'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport type { HttpRequestEventMap } from '../glossary'\nimport { emitAsync } from './emitAsync'\nimport { RequestController } from '../RequestController'\nimport {\n createServerErrorResponse,\n isResponseError,\n isResponseLike,\n} from './responseUtils'\nimport { InterceptorError } from '../InterceptorError'\nimport { isNodeLikeError } from './isNodeLikeError'\nimport { isObject } from './isObject'\n\ninterface HandleRequestOptions {\n requestId: string\n request: Request\n emitter: Emitter<HttpRequestEventMap>\n controller: RequestController\n}\n\nexport async function handleRequest(\n options: HandleRequestOptions\n): Promise<void> {\n const handleResponse = async (\n response: Response | Error | Record<string, any>\n ) => {\n if (response instanceof Error) {\n await options.controller.errorWith(response)\n return true\n }\n\n // Handle \"Response.error()\" instances.\n if (isResponseError(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n /**\n * Handle normal responses or response-like objects.\n * @note This must come before the arbitrary object check\n * since Response instances are, in fact, objects.\n */\n if (isResponseLike(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n // Handle arbitrary objects provided to `.errorWith(reason)`.\n if (isObject(response)) {\n await options.controller.errorWith(response)\n return true\n }\n\n return false\n }\n\n const handleResponseError = async (error: unknown): Promise<boolean> => {\n // Forward the special interceptor error instances\n // to the developer. These must not be handled in any way.\n if (error instanceof InterceptorError) {\n throw result.error\n }\n\n // Support mocking Node.js-like errors.\n if (isNodeLikeError(error)) {\n await options.controller.errorWith(error)\n return true\n }\n\n // Handle thrown responses.\n if (error instanceof Response) {\n return await handleResponse(error)\n }\n\n return false\n }\n\n // Add the last \"request\" listener to check if the request\n // has been handled in any way. If it hasn't, resolve the\n // response promise with undefined.\n // options.emitter.once('request', async ({ requestId: pendingRequestId }) => {\n // if (\n // pendingRequestId === options.requestId &&\n // options.controller.readyState === RequestController.PENDING\n // ) {\n // await options.controller.passthrough()\n // }\n // })\n\n const requestAbortPromise = new DeferredPromise<void, unknown>()\n\n /**\n * @note `signal` is not always defined in React Native.\n */\n if (options.request.signal) {\n if (options.request.signal.aborted) {\n await options.controller.errorWith(options.request.signal.reason)\n return\n }\n\n options.request.signal.addEventListener(\n 'abort',\n () => {\n requestAbortPromise.reject(options.request.signal.reason)\n },\n { once: true }\n )\n }\n\n const result = await until(async () => {\n // Emit the \"request\" event and wait until all the listeners\n // for that event are finished (e.g. async listeners awaited).\n // By the end of this promise, the developer cannot affect the\n // request anymore.\n const requestListenersPromise = emitAsync(options.emitter, 'request', {\n requestId: options.requestId,\n request: options.request,\n controller: options.controller,\n })\n\n await Promise.race([\n // Short-circuit the request handling promise if the request gets aborted.\n requestAbortPromise,\n requestListenersPromise,\n options.controller.handled,\n ])\n })\n\n // Handle the request being aborted while waiting for the request listeners.\n if (requestAbortPromise.state === 'rejected') {\n await options.controller.errorWith(requestAbortPromise.rejectionReason)\n return\n }\n\n if (result.error) {\n // Handle the error during the request listener execution.\n // These can be thrown responses or request errors.\n if (await handleResponseError(result.error)) {\n return\n }\n\n // If the developer has added \"unhandledException\" listeners,\n // allow them to handle the error. They can translate it to a\n // mocked response, network error, or forward it as-is.\n if (options.emitter.listenerCount('unhandledException') > 0) {\n // Create a new request controller just for the unhandled exception case.\n // This is needed because the original controller might have been already\n // interacted with (e.g. \"respondWith\" or \"errorWith\" called on it).\n const unhandledExceptionController = new RequestController(\n options.request,\n {\n /**\n * @note Intentionally empty passthrough handle.\n * This controller is created within another controller and we only need\n * to know if `unhandledException` listeners handled the request.\n */\n passthrough() {},\n async respondWith(response) {\n await handleResponse(response)\n },\n async errorWith(reason) {\n /**\n * @note Handle the result of the unhandled controller\n * in the same way as the original request controller.\n * The exception here is that thrown errors within the\n * \"unhandledException\" event do NOT result in another\n * emit of the same event. They are forwarded as-is.\n */\n await options.controller.errorWith(reason)\n },\n }\n )\n\n await emitAsync(options.emitter, 'unhandledException', {\n error: result.error,\n request: options.request,\n requestId: options.requestId,\n controller: unhandledExceptionController,\n })\n\n // If all the \"unhandledException\" listeners have finished\n // but have not handled the request in any way, passthrough.\n if (\n unhandledExceptionController.readyState !== RequestController.PENDING\n ) {\n return\n }\n }\n\n // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.\n await options.controller.respondWith(\n createServerErrorResponse(result.error)\n )\n return\n }\n\n // If the request hasn't been handled by this point, passthrough.\n if (options.controller.readyState === RequestController.PENDING) {\n return await options.controller.passthrough()\n }\n\n return options.controller.handled\n}\n"],"mappings":";;;;;;;;;AAGA,SAAgB,SAAY,OAAY,QAAQ,OAAmB;AACjE,QAAO,QACH,OAAO,UAAU,SAAS,KAAK,MAAM,CAAC,WAAW,WAAW,GAC5D,OAAO,UAAU,SAAS,KAAK,MAAM,KAAK;;;;;;;;;;;;;ACEhD,SAAgB,qBACd,KACA,KACA;AACA,KAAI;AACF,MAAI;AACJ,SAAO;SACD;AACN,SAAO;;;;;;;;;ACVX,SAAgB,0BAA0B,MAAyB;AACjE,QAAO,IAAI,SACT,KAAK,UACH,gBAAgB,QACZ;EACE,MAAM,KAAK;EACX,SAAS,KAAK;EACd,OAAO,KAAK;EACb,GACD,KACL,EACD;EACE,QAAQ;EACR,YAAY;EACZ,SAAS,EACP,gBAAgB,oBACjB;EACF,CACF;;;;;;;;;;AAaH,SAAgB,gBAAgB,UAA8C;AAC5E,QACE,YAAY,QACZ,oBAAoB,YACpB,qBAAqB,UAAU,OAAO,IACtC,SAAS,SAAS;;;;;;;AAStB,SAAgB,eAAe,OAAmC;AAChE,QACE,SAA8B,OAAO,KAAK,IAC1C,qBAAqB,OAAO,SAAS,IACrC,qBAAqB,OAAO,aAAa,IACzC,qBAAqB,OAAO,WAAW;;;;;ACxD3C,SAAgB,gBACd,OACgC;AAChC,KAAI,SAAS,KACX,QAAO;AAGT,KAAI,EAAE,iBAAiB,OACrB,QAAO;AAGT,QAAO,UAAU,SAAS,WAAW;;;;;ACWvC,eAAsB,cACpB,SACe;CACf,MAAM,iBAAiB,OACrB,aACG;AACH,MAAI,oBAAoB,OAAO;AAC7B,SAAM,QAAQ,WAAW,UAAU,SAAS;AAC5C,UAAO;;AAIT,MAAI,gBAAgB,SAAS,EAAE;AAC7B,SAAM,QAAQ,WAAW,YAAY,SAAS;AAC9C,UAAO;;;;;;;AAQT,MAAI,eAAe,SAAS,EAAE;AAC5B,SAAM,QAAQ,WAAW,YAAY,SAAS;AAC9C,UAAO;;AAIT,MAAI,SAAS,SAAS,EAAE;AACtB,SAAM,QAAQ,WAAW,UAAU,SAAS;AAC5C,UAAO;;AAGT,SAAO;;CAGT,MAAM,sBAAsB,OAAO,UAAqC;AAGtE,MAAI,iBAAiBA,uCACnB,OAAM,OAAO;AAIf,MAAI,gBAAgB,MAAM,EAAE;AAC1B,SAAM,QAAQ,WAAW,UAAU,MAAM;AACzC,UAAO;;AAIT,MAAI,iBAAiB,SACnB,QAAO,MAAM,eAAe,MAAM;AAGpC,SAAO;;CAeT,MAAM,sBAAsB,IAAIC,8CAAgC;;;;AAKhE,KAAI,QAAQ,QAAQ,QAAQ;AAC1B,MAAI,QAAQ,QAAQ,OAAO,SAAS;AAClC,SAAM,QAAQ,WAAW,UAAU,QAAQ,QAAQ,OAAO,OAAO;AACjE;;AAGF,UAAQ,QAAQ,OAAO,iBACrB,eACM;AACJ,uBAAoB,OAAO,QAAQ,QAAQ,OAAO,OAAO;KAE3D,EAAE,MAAM,MAAM,CACf;;CAGH,MAAM,SAAS,mCAAY,YAAY;EAKrC,MAAM,0BAA0BC,wCAAU,QAAQ,SAAS,WAAW;GACpE,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,YAAY,QAAQ;GACrB,CAAC;AAEF,QAAM,QAAQ,KAAK;GAEjB;GACA;GACA,QAAQ,WAAW;GACpB,CAAC;GACF;AAGF,KAAI,oBAAoB,UAAU,YAAY;AAC5C,QAAM,QAAQ,WAAW,UAAU,oBAAoB,gBAAgB;AACvE;;AAGF,KAAI,OAAO,OAAO;AAGhB,MAAI,MAAM,oBAAoB,OAAO,MAAM,CACzC;AAMF,MAAI,QAAQ,QAAQ,cAAc,qBAAqB,GAAG,GAAG;GAI3D,MAAM,+BAA+B,IAAIC,wCACvC,QAAQ,SACR;IAME,cAAc;IACd,MAAM,YAAY,UAAU;AAC1B,WAAM,eAAe,SAAS;;IAEhC,MAAM,UAAU,QAAQ;;;;;;;;AAQtB,WAAM,QAAQ,WAAW,UAAU,OAAO;;IAE7C,CACF;AAED,SAAMD,wCAAU,QAAQ,SAAS,sBAAsB;IACrD,OAAO,OAAO;IACd,SAAS,QAAQ;IACjB,WAAW,QAAQ;IACnB,YAAY;IACb,CAAC;AAIF,OACE,6BAA6B,eAAeC,wCAAkB,QAE9D;;AAKJ,QAAM,QAAQ,WAAW,YACvB,0BAA0B,OAAO,MAAM,CACxC;AACD;;AAIF,KAAI,QAAQ,WAAW,eAAeA,wCAAkB,QACtD,QAAO,MAAM,QAAQ,WAAW,aAAa;AAG/C,QAAO,QAAQ,WAAW"}
import { invariant } from "outvariant";
//#region src/utils/emitAsync.ts
/**
* Emits an event on the given emitter but executes
* the listeners sequentially. This accounts for asynchronous
* listeners (e.g. those having "sleep" and handling the request).
*/
async function emitAsync(emitter, eventName, ...data) {
const listeners = emitter.listeners(eventName);
if (listeners.length === 0) return;
for (const listener of listeners) await listener.apply(emitter, data);
}
//#endregion
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
invariant(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
if (match.descriptor.configurable) Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
else if (match.descriptor.writable) owner[key] = getNextValue(owner[key]);
else throw new Error(`Failed to patch a non-configurable non-writable property "${key.toString()}"`);
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner)
/**
* @note Restoring non-configurable properties works as long as "writable: true"
* and none of the other descriptor properties except for "value" have changed.
*/
Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
export { patchesRegistry as n, emitAsync as r, hasConfigurableGlobal as t };
//# sourceMappingURL=hasConfigurableGlobal-C8zq1MCg.mjs.map
{"version":3,"file":"hasConfigurableGlobal-C8zq1MCg.mjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/emitAsync.ts","../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { Emitter, EventMap } from 'strict-event-emitter'\n\n/**\n * Emits an event on the given emitter but executes\n * the listeners sequentially. This accounts for asynchronous\n * listeners (e.g. those having \"sleep\" and handling the request).\n */\nexport async function emitAsync<\n Events extends EventMap,\n EventName extends keyof Events\n>(\n emitter: Emitter<Events>,\n eventName: EventName,\n ...data: Events[EventName]\n): Promise<void> {\n const listeners = emitter.listeners(eventName)\n\n if (listeners.length === 0) {\n return\n }\n\n for (const listener of listeners) {\n await listener.apply(emitter, data)\n }\n}\n","import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n if (match.descriptor.configurable) {\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n } else if (match.descriptor.writable) {\n owner[key] = getNextValue(owner[key])\n } else {\n throw new Error(\n `Failed to patch a non-configurable non-writable property \"${key.toString()}\"`\n )\n }\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n /**\n * @note Restoring non-configurable properties works as long as \"writable: true\"\n * and none of the other descriptor properties except for \"value\" have changed.\n */\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,UAIpB,SACA,WACA,GAAG,MACY;CACf,MAAM,YAAY,QAAQ,UAAU,UAAU;AAE9C,KAAI,UAAU,WAAW,EACvB;AAGF,MAAK,MAAM,YAAY,UACrB,OAAM,SAAS,MAAM,SAAS,KAAK;;;;;ACpBvC,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,YACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,MAAI,MAAM,WAAW,aACnB,QAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;WACO,MAAM,WAAW,SAC1B,OAAM,OAAO,aAAa,MAAM,KAAK;MAErC,OAAM,IAAI,MACR,6DAA6D,IAAI,UAAU,CAAC,GAC7E;EAGH,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU;;;;;AAKlB,UAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;AC7HtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
let outvariant = require("outvariant");
//#region src/utils/emitAsync.ts
/**
* Emits an event on the given emitter but executes
* the listeners sequentially. This accounts for asynchronous
* listeners (e.g. those having "sleep" and handling the request).
*/
async function emitAsync(emitter, eventName, ...data) {
const listeners = emitter.listeners(eventName);
if (listeners.length === 0) return;
for (const listener of listeners) await listener.apply(emitter, data);
}
//#endregion
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
(0, outvariant.invariant)(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
if (match.descriptor.configurable) Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
else if (match.descriptor.writable) owner[key] = getNextValue(owner[key]);
else throw new Error(`Failed to patch a non-configurable non-writable property "${key.toString()}"`);
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner)
/**
* @note Restoring non-configurable properties works as long as "writable: true"
* and none of the other descriptor properties except for "value" have changed.
*/
Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
Object.defineProperty(exports, 'emitAsync', {
enumerable: true,
get: function () {
return emitAsync;
}
});
Object.defineProperty(exports, 'hasConfigurableGlobal', {
enumerable: true,
get: function () {
return hasConfigurableGlobal;
}
});
Object.defineProperty(exports, 'patchesRegistry', {
enumerable: true,
get: function () {
return patchesRegistry;
}
});
//# sourceMappingURL=hasConfigurableGlobal-CS7adEvV.cjs.map
{"version":3,"file":"hasConfigurableGlobal-CS7adEvV.cjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/emitAsync.ts","../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { Emitter, EventMap } from 'strict-event-emitter'\n\n/**\n * Emits an event on the given emitter but executes\n * the listeners sequentially. This accounts for asynchronous\n * listeners (e.g. those having \"sleep\" and handling the request).\n */\nexport async function emitAsync<\n Events extends EventMap,\n EventName extends keyof Events\n>(\n emitter: Emitter<Events>,\n eventName: EventName,\n ...data: Events[EventName]\n): Promise<void> {\n const listeners = emitter.listeners(eventName)\n\n if (listeners.length === 0) {\n return\n }\n\n for (const listener of listeners) {\n await listener.apply(emitter, data)\n }\n}\n","import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n if (match.descriptor.configurable) {\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n } else if (match.descriptor.writable) {\n owner[key] = getNextValue(owner[key])\n } else {\n throw new Error(\n `Failed to patch a non-configurable non-writable property \"${key.toString()}\"`\n )\n }\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n /**\n * @note Restoring non-configurable properties works as long as \"writable: true\"\n * and none of the other descriptor properties except for \"value\" have changed.\n */\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,UAIpB,SACA,WACA,GAAG,MACY;CACf,MAAM,YAAY,QAAQ,UAAU,UAAU;AAE9C,KAAI,UAAU,WAAW,EACvB;AAGF,MAAK,MAAM,YAAY,UACrB,OAAM,SAAS,MAAM,SAAS,KAAK;;;;;ACpBvC,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,4BACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,MAAI,MAAM,WAAW,aACnB,QAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;WACO,MAAM,WAAW,SAC1B,OAAM,OAAO,aAAa,MAAM,KAAK;MAErC,OAAM,IAAI,MACR,6DAA6D,IAAI,UAAU,CAAC,GAC7E;EAGH,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU;;;;;AAKlB,UAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;AC7HtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
const require_createRequestId = require('./createRequestId-DOf8Ktjs.cjs');
const require_getRawRequest = require('./getRawRequest-DdfaiPVH.cjs');
const require_bufferUtils = require('./bufferUtils-Uc0eRItL.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-CS7adEvV.cjs');
const require_handleRequest = require('./handleRequest-Cz4_wmQ9.cjs');
let outvariant = require("outvariant");
let is_node_process = require("is-node-process");
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new require_getRawRequest.FetchResponse(require_getRawRequest.FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = (0, is_node_process.isNodeProcess)();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = require_createRequestId.createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? require_bufferUtils.encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(require_createRequestId.INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return require_bufferUtils.decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = require_bufferUtils.toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new require_getRawRequest.FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
require_getRawRequest.setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new require_getRawRequest.RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (require_handleRequest.isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends require_createRequestId.Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
Object.defineProperty(exports, 'XMLHttpRequestInterceptor', {
enumerable: true,
get: function () {
return XMLHttpRequestInterceptor;
}
});
//# sourceMappingURL=XMLHttpRequest-B11I9TBx.cjs.map
{"version":3,"file":"XMLHttpRequest-B11I9TBx.cjs","names":["handler: ProxyHandler<T>","next","FetchResponse","initialRequest: XMLHttpRequest","logger: Logger","createRequestId","encodeBuffer","INTERNAL_REQUEST_ID_HEADER_NAME","decodeBuffer","toArrayBuffer","FetchRequest","RequestController","isResponseError","handleRequest","Interceptor","hasConfigurableGlobal","patchesRegistry"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
import { n as INTERNAL_REQUEST_ID_HEADER_NAME, r as Interceptor, t as createRequestId } from "./createRequestId-DYCsFHOi.mjs";
import { i as FetchResponse, n as setRawRequest, o as RequestController, r as FetchRequest } from "./getRawRequest-B1BqgWG6.mjs";
import { n as encodeBuffer, r as toArrayBuffer, t as decodeBuffer } from "./bufferUtils-BiiO6HZv.mjs";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-C8zq1MCg.mjs";
import { n as isResponseError, t as handleRequest } from "./handleRequest-1Yqu_ho_.mjs";
import { invariant } from "outvariant";
import { isNodeProcess } from "is-node-process";
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new FetchResponse(FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = isNodeProcess();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
invariant(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
invariant(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
export { XMLHttpRequestInterceptor as t };
//# sourceMappingURL=XMLHttpRequest-DHEkSoRN.mjs.map
{"version":3,"file":"XMLHttpRequest-DHEkSoRN.mjs","names":["handler: ProxyHandler<T>","next","initialRequest: XMLHttpRequest","logger: Logger"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
const require_chunk = require('./chunk-CbDLau6x.cjs');
const require_fetchUtils = require('./fetchUtils-umV5xXBy.cjs');
const require_getRawRequest = require('./getRawRequest-B6znU6il.cjs');
const require_handleRequest = require('./handleRequest-DVOthWJo.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-MjY06_Ok.cjs');
let _open_draft_deferred_promise = require("@open-draft/deferred-promise");
let _open_draft_until = require("@open-draft/until");
let node_zlib = require("node:zlib");
node_zlib = require_chunk.__toESM(node_zlib);
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
const decompress = node_zlib.default.createBrotliDecompress({
flush: node_zlib.default.constants.BROTLI_OPERATION_FLUSH,
finishFlush: node_zlib.default.constants.BROTLI_OPERATION_FLUSH
});
super({ async transform(chunk, controller) {
const buffer = Buffer.from(chunk);
const decompressed = await new Promise((resolve, reject) => {
decompress.write(buffer, (error) => {
if (error) reject(error);
});
decompress.flush();
decompress.once("data", (data) => resolve(data));
decompress.once("error", (error) => reject(error));
decompress.once("end", () => controller.terminate());
}).catch((error) => {
controller.error(error);
});
controller.enqueue(decompressed);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends require_fetchUtils.Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = require_fetchUtils.createRequestId();
const request = new require_fetchUtils.FetchRequest(typeof input === "string" && typeof location !== "undefined" && !require_fetchUtils.canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) require_getRawRequest.setRawRequest(request, input);
const responsePromise = new _open_draft_deferred_promise.DeferredPromise();
const controller = new require_fetchUtils.RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await (0, _open_draft_until.until)(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = require_fetchUtils.FetchResponse.clone(originalResponse);
await require_handleRequest.emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (require_handleRequest.isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new require_fetchUtils.FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (require_fetchUtils.FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await require_handleRequest.emitAsync(this.emitter, "response", {
response: require_fetchUtils.FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
Object.defineProperty(exports, 'FetchInterceptor', {
enumerable: true,
get: function () {
return FetchInterceptor;
}
});
//# sourceMappingURL=fetch-aQJu1Gf_.cjs.map
{"version":3,"file":"fetch-aQJu1Gf_.cjs","names":["locationUrl: URL","requestInit: RequestInit","zlib","readable","Interceptor","hasConfigurableGlobal","fetchProxy: typeof fetch","createRequestId","FetchRequest","canParseUrl","DeferredPromise","RequestController","FetchResponse","emitAsync","isResponseError","response","handleRequest","patchesRegistry"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","import zlib from 'node:zlib'\n\nexport class BrotliDecompressionStream extends TransformStream {\n constructor() {\n const decompress = zlib.createBrotliDecompress({\n flush: zlib.constants.BROTLI_OPERATION_FLUSH,\n finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,\n })\n\n super({\n async transform(chunk, controller) {\n const buffer = Buffer.from(chunk)\n\n const decompressed = await new Promise<Buffer>((resolve, reject) => {\n decompress.write(buffer, (error) => {\n if (error) reject(error)\n })\n\n decompress.flush()\n decompress.once('data', (data) => resolve(data))\n decompress.once('error', (error) => reject(error))\n decompress.once('end', () => controller.terminate())\n }).catch((error) => {\n controller.error(error)\n })\n\n controller.enqueue(decompressed)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
import { a as RequestController, c as Interceptor, i as createRequestId, n as FetchResponse, r as canParseUrl, t as FetchRequest } from "./fetchUtils-BKJ1XmiO.mjs";
import { n as setRawRequest } from "./getRawRequest-C2-1urzA.mjs";
import { i as emitAsync, n as isResponseError, t as handleRequest } from "./handleRequest-DCLzePtS.mjs";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-BiTmog1u.mjs";
import { DeferredPromise } from "@open-draft/deferred-promise";
import { until } from "@open-draft/until";
import zlib from "node:zlib";
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
const decompress = zlib.createBrotliDecompress({
flush: zlib.constants.BROTLI_OPERATION_FLUSH,
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
});
super({ async transform(chunk, controller) {
const buffer = Buffer.from(chunk);
const decompressed = await new Promise((resolve, reject) => {
decompress.write(buffer, (error) => {
if (error) reject(error);
});
decompress.flush();
decompress.once("data", (data) => resolve(data));
decompress.once("error", (error) => reject(error));
decompress.once("end", () => controller.terminate());
}).catch((error) => {
controller.error(error);
});
controller.enqueue(decompressed);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = createRequestId();
const request = new FetchRequest(typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) setRawRequest(request, input);
const responsePromise = new DeferredPromise();
const controller = new RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await until(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = FetchResponse.clone(originalResponse);
await emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await emitAsync(this.emitter, "response", {
response: FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
export { FetchInterceptor as t };
//# sourceMappingURL=fetch-BqMcbuYR.mjs.map
{"version":3,"file":"fetch-BqMcbuYR.mjs","names":["locationUrl: URL","requestInit: RequestInit","readable","fetchProxy: typeof fetch","response"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","import zlib from 'node:zlib'\n\nexport class BrotliDecompressionStream extends TransformStream {\n constructor() {\n const decompress = zlib.createBrotliDecompress({\n flush: zlib.constants.BROTLI_OPERATION_FLUSH,\n finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,\n })\n\n super({\n async transform(chunk, controller) {\n const buffer = Buffer.from(chunk)\n\n const decompressed = await new Promise<Buffer>((resolve, reject) => {\n decompress.write(buffer, (error) => {\n if (error) reject(error)\n })\n\n decompress.flush()\n decompress.once('data', (data) => resolve(data))\n decompress.once('error', (error) => reject(error))\n decompress.once('end', () => controller.terminate())\n }).catch((error) => {\n controller.error(error)\n })\n\n controller.enqueue(decompressed)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
import { invariant } from "outvariant";
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
invariant(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
if (match.descriptor.configurable) Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
else if (match.descriptor.writable) owner[key] = getNextValue(owner[key]);
else throw new Error(`Failed to patch a non-configurable non-writable property "${key.toString()}"`);
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner)
/**
* @note Restoring non-configurable properties works as long as "writable: true"
* and none of the other descriptor properties except for "value" have changed.
*/
Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
export { patchesRegistry as n, hasConfigurableGlobal as t };
//# sourceMappingURL=hasConfigurableGlobal-BiTmog1u.mjs.map
{"version":3,"file":"hasConfigurableGlobal-BiTmog1u.mjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n if (match.descriptor.configurable) {\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n } else if (match.descriptor.writable) {\n owner[key] = getNextValue(owner[key])\n } else {\n throw new Error(\n `Failed to patch a non-configurable non-writable property \"${key.toString()}\"`\n )\n }\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n /**\n * @note Restoring non-configurable properties works as long as \"writable: true\"\n * and none of the other descriptor properties except for \"value\" have changed.\n */\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;AAEA,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,YACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,MAAI,MAAM,WAAW,aACnB,QAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;WACO,MAAM,WAAW,SAC1B,OAAM,OAAO,aAAa,MAAM,KAAK;MAErC,OAAM,IAAI,MACR,6DAA6D,IAAI,UAAU,CAAC,GAC7E;EAGH,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU;;;;;AAKlB,UAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;AC7HtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
const require_chunk = require('./chunk-CbDLau6x.cjs');
let outvariant = require("outvariant");
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
(0, outvariant.invariant)(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
if (match.descriptor.configurable) Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
else if (match.descriptor.writable) owner[key] = getNextValue(owner[key]);
else throw new Error(`Failed to patch a non-configurable non-writable property "${key.toString()}"`);
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner)
/**
* @note Restoring non-configurable properties works as long as "writable: true"
* and none of the other descriptor properties except for "value" have changed.
*/
Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
Object.defineProperty(exports, 'hasConfigurableGlobal', {
enumerable: true,
get: function () {
return hasConfigurableGlobal;
}
});
Object.defineProperty(exports, 'patchesRegistry', {
enumerable: true,
get: function () {
return patchesRegistry;
}
});
//# sourceMappingURL=hasConfigurableGlobal-MjY06_Ok.cjs.map
{"version":3,"file":"hasConfigurableGlobal-MjY06_Ok.cjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n if (match.descriptor.configurable) {\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n } else if (match.descriptor.writable) {\n owner[key] = getNextValue(owner[key])\n } else {\n throw new Error(\n `Failed to patch a non-configurable non-writable property \"${key.toString()}\"`\n )\n }\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n /**\n * @note Restoring non-configurable properties works as long as \"writable: true\"\n * and none of the other descriptor properties except for \"value\" have changed.\n */\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;;AAEA,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,4BACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,MAAI,MAAM,WAAW,aACnB,QAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;WACO,MAAM,WAAW,SAC1B,OAAM,OAAO,aAAa,MAAM,KAAK;MAErC,OAAM,IAAI,MACR,6DAA6D,IAAI,UAAU,CAAC,GAC7E;EAGH,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU;;;;;AAKlB,UAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;AC7HtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
import { a as RequestController, c as Interceptor, i as createRequestId, n as FetchResponse, s as INTERNAL_REQUEST_ID_HEADER_NAME, t as FetchRequest } from "./fetchUtils-BKJ1XmiO.mjs";
import { n as encodeBuffer, r as toArrayBuffer, t as decodeBuffer } from "./bufferUtils-DxPxwff_.mjs";
import { n as setRawRequest } from "./getRawRequest-C2-1urzA.mjs";
import { n as isResponseError, t as handleRequest } from "./handleRequest-DCLzePtS.mjs";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-BiTmog1u.mjs";
import { invariant } from "outvariant";
import { isNodeProcess } from "is-node-process";
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new FetchResponse(FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = isNodeProcess();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
invariant(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
invariant(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
export { XMLHttpRequestInterceptor as t };
//# sourceMappingURL=XMLHttpRequest-DhupxLHX.mjs.map
{"version":3,"file":"XMLHttpRequest-DhupxLHX.mjs","names":["handler: ProxyHandler<T>","next","initialRequest: XMLHttpRequest","logger: Logger"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
const require_chunk = require('./chunk-CbDLau6x.cjs');
const require_fetchUtils = require('./fetchUtils-umV5xXBy.cjs');
const require_bufferUtils = require('./bufferUtils-S5_-2eN4.cjs');
const require_getRawRequest = require('./getRawRequest-B6znU6il.cjs');
const require_handleRequest = require('./handleRequest-DVOthWJo.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-MjY06_Ok.cjs');
let outvariant = require("outvariant");
let is_node_process = require("is-node-process");
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new require_fetchUtils.FetchResponse(require_fetchUtils.FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = (0, is_node_process.isNodeProcess)();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = require_fetchUtils.createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? require_bufferUtils.encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(require_fetchUtils.INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return require_bufferUtils.decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = require_bufferUtils.toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new require_fetchUtils.FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
require_getRawRequest.setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new require_fetchUtils.RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (require_handleRequest.isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends require_fetchUtils.Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
Object.defineProperty(exports, 'XMLHttpRequestInterceptor', {
enumerable: true,
get: function () {
return XMLHttpRequestInterceptor;
}
});
//# sourceMappingURL=XMLHttpRequest-Dyi6ahGz.cjs.map
{"version":3,"file":"XMLHttpRequest-Dyi6ahGz.cjs","names":["handler: ProxyHandler<T>","next","FetchResponse","initialRequest: XMLHttpRequest","logger: Logger","createRequestId","encodeBuffer","INTERNAL_REQUEST_ID_HEADER_NAME","decodeBuffer","toArrayBuffer","FetchRequest","RequestController","isResponseError","handleRequest","Interceptor","hasConfigurableGlobal","patchesRegistry"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
+3
-3
require('../../createRequestId-DOf8Ktjs.cjs');
require('../../getRawRequest-DdfaiPVH.cjs');
require('../../hasConfigurableGlobal-BS75Oulv.cjs');
require('../../handleRequest-tUNU616J.cjs');
const require_fetch = require('../../fetch-CtkfZi4Z.cjs');
require('../../hasConfigurableGlobal-CS7adEvV.cjs');
require('../../handleRequest-Cz4_wmQ9.cjs');
const require_fetch = require('../../fetch-M8iXkZ3L.cjs');
exports.FetchInterceptor = require_fetch.FetchInterceptor;
import "../../createRequestId-DYCsFHOi.mjs";
import "../../getRawRequest-B1BqgWG6.mjs";
import "../../hasConfigurableGlobal-FTYwno1G.mjs";
import "../../handleRequest-BHrC8Flw.mjs";
import { t as FetchInterceptor } from "../../fetch-Ddj5TV04.mjs";
import "../../hasConfigurableGlobal-C8zq1MCg.mjs";
import "../../handleRequest-1Yqu_ho_.mjs";
import { t as FetchInterceptor } from "../../fetch-BJwfdses.mjs";
export { FetchInterceptor };
const require_createRequestId = require('../../createRequestId-DOf8Ktjs.cjs');
const require_resolveWebSocketUrl = require('../../resolveWebSocketUrl-6K6EgqsA.cjs');
const require_hasConfigurableGlobal = require('../../hasConfigurableGlobal-BS75Oulv.cjs');
const require_hasConfigurableGlobal = require('../../hasConfigurableGlobal-CS7adEvV.cjs');
let _open_draft_deferred_promise = require("@open-draft/deferred-promise");

@@ -5,0 +5,0 @@ let outvariant = require("outvariant");

import { r as Interceptor, t as createRequestId } from "../../createRequestId-DYCsFHOi.mjs";
import { t as resolveWebSocketUrl } from "../../resolveWebSocketUrl-C83-x9iE.mjs";
import { n as patchesRegistry, r as emitAsync, t as hasConfigurableGlobal } from "../../hasConfigurableGlobal-FTYwno1G.mjs";
import { n as patchesRegistry, r as emitAsync, t as hasConfigurableGlobal } from "../../hasConfigurableGlobal-C8zq1MCg.mjs";
import { DeferredPromise } from "@open-draft/deferred-promise";

@@ -5,0 +5,0 @@ import { invariant } from "outvariant";

require('../../createRequestId-DOf8Ktjs.cjs');
require('../../getRawRequest-DdfaiPVH.cjs');
require('../../bufferUtils-Uc0eRItL.cjs');
require('../../hasConfigurableGlobal-BS75Oulv.cjs');
require('../../handleRequest-tUNU616J.cjs');
const require_XMLHttpRequest = require('../../XMLHttpRequest-CHqOQWxg.cjs');
require('../../hasConfigurableGlobal-CS7adEvV.cjs');
require('../../handleRequest-Cz4_wmQ9.cjs');
const require_XMLHttpRequest = require('../../XMLHttpRequest-B11I9TBx.cjs');
exports.XMLHttpRequestInterceptor = require_XMLHttpRequest.XMLHttpRequestInterceptor;
import "../../createRequestId-DYCsFHOi.mjs";
import "../../getRawRequest-B1BqgWG6.mjs";
import "../../bufferUtils-BiiO6HZv.mjs";
import "../../hasConfigurableGlobal-FTYwno1G.mjs";
import "../../handleRequest-BHrC8Flw.mjs";
import { t as XMLHttpRequestInterceptor } from "../../XMLHttpRequest-DP9ps_69.mjs";
import "../../hasConfigurableGlobal-C8zq1MCg.mjs";
import "../../handleRequest-1Yqu_ho_.mjs";
import { t as XMLHttpRequestInterceptor } from "../../XMLHttpRequest-DHEkSoRN.mjs";
export { XMLHttpRequestInterceptor };
require('../createRequestId-DOf8Ktjs.cjs');
require('../getRawRequest-DdfaiPVH.cjs');
require('../bufferUtils-Uc0eRItL.cjs');
require('../hasConfigurableGlobal-BS75Oulv.cjs');
require('../handleRequest-tUNU616J.cjs');
const require_fetch = require('../fetch-CtkfZi4Z.cjs');
const require_XMLHttpRequest = require('../XMLHttpRequest-CHqOQWxg.cjs');
require('../hasConfigurableGlobal-CS7adEvV.cjs');
require('../handleRequest-Cz4_wmQ9.cjs');
const require_fetch = require('../fetch-M8iXkZ3L.cjs');
const require_XMLHttpRequest = require('../XMLHttpRequest-B11I9TBx.cjs');

@@ -9,0 +9,0 @@ //#region src/presets/browser.ts

import "../createRequestId-DYCsFHOi.mjs";
import "../getRawRequest-B1BqgWG6.mjs";
import "../bufferUtils-BiiO6HZv.mjs";
import "../hasConfigurableGlobal-FTYwno1G.mjs";
import "../handleRequest-BHrC8Flw.mjs";
import { t as FetchInterceptor } from "../fetch-Ddj5TV04.mjs";
import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-DP9ps_69.mjs";
import "../hasConfigurableGlobal-C8zq1MCg.mjs";
import "../handleRequest-1Yqu_ho_.mjs";
import { t as FetchInterceptor } from "../fetch-BJwfdses.mjs";
import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-DHEkSoRN.mjs";

@@ -9,0 +9,0 @@ //#region src/presets/browser.ts

require('../../fetchUtils-umV5xXBy.cjs');
require('../../handleRequest-DVOthWJo.cjs');
require('../../hasConfigurableGlobal-BtHi5OlL.cjs');
const require_fetch = require('../../fetch-znfXVEPe.cjs');
require('../../hasConfigurableGlobal-MjY06_Ok.cjs');
const require_fetch = require('../../fetch-aQJu1Gf_.cjs');
exports.FetchInterceptor = require_fetch.FetchInterceptor;
import "../../fetchUtils-BKJ1XmiO.mjs";
import "../../handleRequest-DCLzePtS.mjs";
import "../../hasConfigurableGlobal-Cwrs35Xo.mjs";
import { t as FetchInterceptor } from "../../fetch-BciBtVqm.mjs";
import "../../hasConfigurableGlobal-BiTmog1u.mjs";
import { t as FetchInterceptor } from "../../fetch-BqMcbuYR.mjs";
export { FetchInterceptor };
require('../../fetchUtils-umV5xXBy.cjs');
require('../../bufferUtils-S5_-2eN4.cjs');
require('../../handleRequest-DVOthWJo.cjs');
const require_XMLHttpRequest = require('../../XMLHttpRequest-CUbLHiq6.cjs');
require('../../hasConfigurableGlobal-BtHi5OlL.cjs');
const require_XMLHttpRequest = require('../../XMLHttpRequest-Dyi6ahGz.cjs');
require('../../hasConfigurableGlobal-MjY06_Ok.cjs');
exports.XMLHttpRequestInterceptor = require_XMLHttpRequest.XMLHttpRequestInterceptor;
import "../../fetchUtils-BKJ1XmiO.mjs";
import "../../bufferUtils-DxPxwff_.mjs";
import "../../handleRequest-DCLzePtS.mjs";
import { t as XMLHttpRequestInterceptor } from "../../XMLHttpRequest-BgkpwNOu.mjs";
import "../../hasConfigurableGlobal-Cwrs35Xo.mjs";
import { t as XMLHttpRequestInterceptor } from "../../XMLHttpRequest-DhupxLHX.mjs";
import "../../hasConfigurableGlobal-BiTmog1u.mjs";
export { XMLHttpRequestInterceptor };

@@ -6,5 +6,5 @@ require('../fetchUtils-umV5xXBy.cjs');

require('../node-DIKcnzhK.cjs');
const require_XMLHttpRequest = require('../XMLHttpRequest-CUbLHiq6.cjs');
require('../hasConfigurableGlobal-BtHi5OlL.cjs');
const require_fetch = require('../fetch-znfXVEPe.cjs');
const require_XMLHttpRequest = require('../XMLHttpRequest-Dyi6ahGz.cjs');
require('../hasConfigurableGlobal-MjY06_Ok.cjs');
const require_fetch = require('../fetch-aQJu1Gf_.cjs');

@@ -11,0 +11,0 @@ //#region src/presets/node.ts

@@ -6,5 +6,5 @@ import "../fetchUtils-BKJ1XmiO.mjs";

import "../node-lsdNwZEW.mjs";
import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-BgkpwNOu.mjs";
import "../hasConfigurableGlobal-Cwrs35Xo.mjs";
import { t as FetchInterceptor } from "../fetch-BciBtVqm.mjs";
import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-DhupxLHX.mjs";
import "../hasConfigurableGlobal-BiTmog1u.mjs";
import { t as FetchInterceptor } from "../fetch-BqMcbuYR.mjs";

@@ -11,0 +11,0 @@ //#region src/presets/node.ts

@@ -7,5 +7,5 @@ const require_fetchUtils = require('./fetchUtils-umV5xXBy.cjs');

require('./node-DIKcnzhK.cjs');
const require_XMLHttpRequest = require('./XMLHttpRequest-CUbLHiq6.cjs');
require('./hasConfigurableGlobal-BtHi5OlL.cjs');
const require_fetch = require('./fetch-znfXVEPe.cjs');
const require_XMLHttpRequest = require('./XMLHttpRequest-Dyi6ahGz.cjs');
require('./hasConfigurableGlobal-MjY06_Ok.cjs');
const require_fetch = require('./fetch-aQJu1Gf_.cjs');

@@ -12,0 +12,0 @@ //#region src/RemoteHttpInterceptor.ts

@@ -7,5 +7,5 @@ import { a as RequestController, c as Interceptor, n as FetchResponse, t as FetchRequest } from "./fetchUtils-BKJ1XmiO.mjs";

import "./node-lsdNwZEW.mjs";
import { t as XMLHttpRequestInterceptor } from "./XMLHttpRequest-BgkpwNOu.mjs";
import "./hasConfigurableGlobal-Cwrs35Xo.mjs";
import { t as FetchInterceptor } from "./fetch-BciBtVqm.mjs";
import { t as XMLHttpRequestInterceptor } from "./XMLHttpRequest-DhupxLHX.mjs";
import "./hasConfigurableGlobal-BiTmog1u.mjs";
import { t as FetchInterceptor } from "./fetch-BqMcbuYR.mjs";

@@ -12,0 +12,0 @@ //#region src/RemoteHttpInterceptor.ts

@@ -5,3 +5,3 @@ {

"description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
"version": "0.41.7",
"version": "0.41.8",
"main": "./lib/node/index.cjs",

@@ -8,0 +8,0 @@ "module": "./lib/node/index.mjs",

@@ -11,3 +11,8 @@ import { it, beforeEach, afterEach, expect, vi } from 'vitest'

beforeEach(() => {
global.foo = { original: true }
Object.defineProperty(global, 'foo', {
value: { original: true },
writable: true,
enumerable: true,
configurable: true,
})
})

@@ -186,1 +191,52 @@

})
it('replaces a non-configurable writable property', () => {
const owner = {} as { foo: { original: boolean } }
const descriptor: PropertyDescriptor = {
value: { original: true },
enumerable: true,
writable: true,
configurable: false,
}
Object.defineProperty(owner, 'foo', descriptor)
const restoreGlobal = patchesRegistry.applyPatch(owner, 'foo', () => ({
original: false,
}))
expect(owner.foo).toEqual({ original: false })
expect(
Object.getOwnPropertyDescriptor(owner, 'foo'),
'Preserves the original descriptor'
).toEqual({
value: { original: false },
enumerable: true,
configurable: false,
writable: true,
})
restoreGlobal()
expect(owner.foo).toEqual({ original: true })
expect(
Object.getOwnPropertyDescriptor(owner, 'foo'),
'Restores the original descriptor'
).toEqual(descriptor)
})
it('throws when replacing a non-configurable non-writable property', () => {
let owner = {} as { foo: { original: boolean } }
const descriptor: PropertyDescriptor = {
value: { original: true },
enumerable: true,
writable: false,
configurable: false,
}
Object.defineProperty(owner, 'foo', descriptor)
expect(() =>
patchesRegistry.applyPatch(owner, 'foo', () => ({
original: false,
}))
).toThrow('Failed to patch a non-configurable non-writable property "foo"')
})

@@ -27,7 +27,15 @@ import { invariant } from 'outvariant'

Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true,
})
if (match.descriptor.configurable) {
Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true,
})
} else if (match.descriptor.writable) {
owner[key] = getNextValue(owner[key])
} else {
throw new Error(
`Failed to patch a non-configurable non-writable property "${key.toString()}"`
)
}

@@ -42,2 +50,6 @@ const restorePatch = () => {

if (match.owner === owner) {
/**
* @note Restoring non-configurable properties works as long as "writable: true"
* and none of the other descriptor properties except for "value" have changed.
*/
Object.defineProperty(match.owner, key, match.descriptor)

@@ -44,0 +56,0 @@ } else {

const require_createRequestId = require('./createRequestId-DOf8Ktjs.cjs');
const require_getRawRequest = require('./getRawRequest-DdfaiPVH.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-BS75Oulv.cjs');
const require_handleRequest = require('./handleRequest-tUNU616J.cjs');
let _open_draft_deferred_promise = require("@open-draft/deferred-promise");
let _open_draft_until = require("@open-draft/until");
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.browser.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
console.warn("[Interceptors]: Brotli decompression of response streams is not supported in the browser");
super({ transform(chunk, controller) {
controller.enqueue(chunk);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends require_createRequestId.Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = require_createRequestId.createRequestId();
const request = new require_getRawRequest.FetchRequest(typeof input === "string" && typeof location !== "undefined" && !require_getRawRequest.canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) require_getRawRequest.setRawRequest(request, input);
const responsePromise = new _open_draft_deferred_promise.DeferredPromise();
const controller = new require_getRawRequest.RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await (0, _open_draft_until.until)(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = require_getRawRequest.FetchResponse.clone(originalResponse);
await require_hasConfigurableGlobal.emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (require_handleRequest.isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new require_getRawRequest.FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (require_getRawRequest.FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await require_hasConfigurableGlobal.emitAsync(this.emitter, "response", {
response: require_getRawRequest.FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
Object.defineProperty(exports, 'FetchInterceptor', {
enumerable: true,
get: function () {
return FetchInterceptor;
}
});
//# sourceMappingURL=fetch-CtkfZi4Z.cjs.map
{"version":3,"file":"fetch-CtkfZi4Z.cjs","names":["locationUrl: URL","requestInit: RequestInit","readable","Interceptor","hasConfigurableGlobal","fetchProxy: typeof fetch","createRequestId","FetchRequest","canParseUrl","DeferredPromise","RequestController","FetchResponse","emitAsync","isResponseError","response","handleRequest","patchesRegistry"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","export class BrotliDecompressionStream extends TransformStream {\n constructor() {\n console.warn(\n '[Interceptors]: Brotli decompression of response streams is not supported in the browser'\n )\n\n super({\n transform(chunk, controller) {\n // Keep the stream as passthrough, it does nothing.\n controller.enqueue(chunk)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
import { r as Interceptor, t as createRequestId } from "./createRequestId-DYCsFHOi.mjs";
import { a as canParseUrl, i as FetchResponse, n as setRawRequest, o as RequestController, r as FetchRequest } from "./getRawRequest-B1BqgWG6.mjs";
import { n as patchesRegistry, r as emitAsync, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-FTYwno1G.mjs";
import { n as isResponseError, t as handleRequest } from "./handleRequest-BHrC8Flw.mjs";
import { DeferredPromise } from "@open-draft/deferred-promise";
import { until } from "@open-draft/until";
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.browser.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
console.warn("[Interceptors]: Brotli decompression of response streams is not supported in the browser");
super({ transform(chunk, controller) {
controller.enqueue(chunk);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = createRequestId();
const request = new FetchRequest(typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) setRawRequest(request, input);
const responsePromise = new DeferredPromise();
const controller = new RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await until(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = FetchResponse.clone(originalResponse);
await emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await emitAsync(this.emitter, "response", {
response: FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
export { FetchInterceptor as t };
//# sourceMappingURL=fetch-Ddj5TV04.mjs.map
{"version":3,"file":"fetch-Ddj5TV04.mjs","names":["locationUrl: URL","requestInit: RequestInit","readable","fetchProxy: typeof fetch","response"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","export class BrotliDecompressionStream extends TransformStream {\n constructor() {\n console.warn(\n '[Interceptors]: Brotli decompression of response streams is not supported in the browser'\n )\n\n super({\n transform(chunk, controller) {\n // Keep the stream as passthrough, it does nothing.\n controller.enqueue(chunk)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
import { o as RequestController, s as InterceptorError } from "./getRawRequest-B1BqgWG6.mjs";
import { r as emitAsync } from "./hasConfigurableGlobal-FTYwno1G.mjs";
import { DeferredPromise } from "@open-draft/deferred-promise";
import { until } from "@open-draft/until";
//#region src/utils/isObject.ts
/**
* Determines if a given value is an instance of object.
*/
function isObject(value, loose = false) {
return loose ? Object.prototype.toString.call(value).startsWith("[object ") : Object.prototype.toString.call(value) === "[object Object]";
}
//#endregion
//#region src/utils/isPropertyAccessible.ts
/**
* A function that validates if property access is possible on an object
* without throwing. It returns `true` if the property access is possible
* and `false` otherwise.
*
* Environments like miniflare will throw on property access on certain objects
* like Request and Response, for unimplemented properties.
*/
function isPropertyAccessible(obj, key) {
try {
obj[key];
return true;
} catch {
return false;
}
}
//#endregion
//#region src/utils/responseUtils.ts
/**
* Creates a generic 500 Unhandled Exception response.
*/
function createServerErrorResponse(body) {
return new Response(JSON.stringify(body instanceof Error ? {
name: body.name,
message: body.message,
stack: body.stack
} : body), {
status: 500,
statusText: "Unhandled Exception",
headers: { "Content-Type": "application/json" }
});
}
/**
* Check if the given response is a `Response.error()`.
*
* @note Some environments, like Miniflare (Cloudflare) do not
* implement the "Response.type" property and throw on its access.
* Safely check if we can access "type" on "Response" before continuing.
* @see https://github.com/mswjs/msw/issues/1834
*/
function isResponseError(response) {
return response != null && response instanceof Response && isPropertyAccessible(response, "type") && response.type === "error";
}
/**
* Check if the given value is a `Response` or a Response-like object.
* This is different from `value instanceof Response` because it supports
* custom `Response` constructors, like the one when using Undici directly.
*/
function isResponseLike(value) {
return isObject(value, true) && isPropertyAccessible(value, "status") && isPropertyAccessible(value, "statusText") && isPropertyAccessible(value, "bodyUsed");
}
//#endregion
//#region src/utils/isNodeLikeError.ts
function isNodeLikeError(error) {
if (error == null) return false;
if (!(error instanceof Error)) return false;
return "code" in error && "errno" in error;
}
//#endregion
//#region src/utils/handleRequest.ts
async function handleRequest(options) {
const handleResponse = async (response) => {
if (response instanceof Error) {
await options.controller.errorWith(response);
return true;
}
if (isResponseError(response)) {
await options.controller.respondWith(response);
return true;
}
/**
* Handle normal responses or response-like objects.
* @note This must come before the arbitrary object check
* since Response instances are, in fact, objects.
*/
if (isResponseLike(response)) {
await options.controller.respondWith(response);
return true;
}
if (isObject(response)) {
await options.controller.errorWith(response);
return true;
}
return false;
};
const handleResponseError = async (error) => {
if (error instanceof InterceptorError) throw result.error;
if (isNodeLikeError(error)) {
await options.controller.errorWith(error);
return true;
}
if (error instanceof Response) return await handleResponse(error);
return false;
};
const requestAbortPromise = new DeferredPromise();
/**
* @note `signal` is not always defined in React Native.
*/
if (options.request.signal) {
if (options.request.signal.aborted) {
await options.controller.errorWith(options.request.signal.reason);
return;
}
options.request.signal.addEventListener("abort", () => {
requestAbortPromise.reject(options.request.signal.reason);
}, { once: true });
}
const result = await until(async () => {
const requestListenersPromise = emitAsync(options.emitter, "request", {
requestId: options.requestId,
request: options.request,
controller: options.controller
});
await Promise.race([
requestAbortPromise,
requestListenersPromise,
options.controller.handled
]);
});
if (requestAbortPromise.state === "rejected") {
await options.controller.errorWith(requestAbortPromise.rejectionReason);
return;
}
if (result.error) {
if (await handleResponseError(result.error)) return;
if (options.emitter.listenerCount("unhandledException") > 0) {
const unhandledExceptionController = new RequestController(options.request, {
passthrough() {},
async respondWith(response) {
await handleResponse(response);
},
async errorWith(reason) {
/**
* @note Handle the result of the unhandled controller
* in the same way as the original request controller.
* The exception here is that thrown errors within the
* "unhandledException" event do NOT result in another
* emit of the same event. They are forwarded as-is.
*/
await options.controller.errorWith(reason);
}
});
await emitAsync(options.emitter, "unhandledException", {
error: result.error,
request: options.request,
requestId: options.requestId,
controller: unhandledExceptionController
});
if (unhandledExceptionController.readyState !== RequestController.PENDING) return;
}
await options.controller.respondWith(createServerErrorResponse(result.error));
return;
}
if (options.controller.readyState === RequestController.PENDING) return await options.controller.passthrough();
return options.controller.handled;
}
//#endregion
export { isResponseError as n, handleRequest as t };
//# sourceMappingURL=handleRequest-BHrC8Flw.mjs.map
{"version":3,"file":"handleRequest-BHrC8Flw.mjs","names":[],"sources":["../../src/utils/isObject.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/responseUtils.ts","../../src/utils/isNodeLikeError.ts","../../src/utils/handleRequest.ts"],"sourcesContent":["/**\n * Determines if a given value is an instance of object.\n */\nexport function isObject<T>(value: any, loose = false): value is T {\n return loose\n ? Object.prototype.toString.call(value).startsWith('[object ')\n : Object.prototype.toString.call(value) === '[object Object]'\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","import { isObject } from './isObject'\nimport { isPropertyAccessible } from './isPropertyAccessible'\n\n/**\n * Creates a generic 500 Unhandled Exception response.\n */\nexport function createServerErrorResponse(body: unknown): Response {\n return new Response(\n JSON.stringify(\n body instanceof Error\n ? {\n name: body.name,\n message: body.message,\n stack: body.stack,\n }\n : body\n ),\n {\n status: 500,\n statusText: 'Unhandled Exception',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n}\n\nexport type ResponseError = Response & { type: 'error' }\n\n/**\n * Check if the given response is a `Response.error()`.\n *\n * @note Some environments, like Miniflare (Cloudflare) do not\n * implement the \"Response.type\" property and throw on its access.\n * Safely check if we can access \"type\" on \"Response\" before continuing.\n * @see https://github.com/mswjs/msw/issues/1834\n */\nexport function isResponseError(response: unknown): response is ResponseError {\n return (\n response != null &&\n response instanceof Response &&\n isPropertyAccessible(response, 'type') &&\n response.type === 'error'\n )\n}\n\n/**\n * Check if the given value is a `Response` or a Response-like object.\n * This is different from `value instanceof Response` because it supports\n * custom `Response` constructors, like the one when using Undici directly.\n */\nexport function isResponseLike(value: unknown): value is Response {\n return (\n isObject<Record<string, any>>(value, true) &&\n isPropertyAccessible(value, 'status') &&\n isPropertyAccessible(value, 'statusText') &&\n isPropertyAccessible(value, 'bodyUsed')\n )\n}\n","export function isNodeLikeError(\n error: unknown\n): error is NodeJS.ErrnoException {\n if (error == null) {\n return false\n }\n\n if (!(error instanceof Error)) {\n return false\n }\n\n return 'code' in error && 'errno' in error\n}\n","import type { Emitter } from 'strict-event-emitter'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport type { HttpRequestEventMap } from '../glossary'\nimport { emitAsync } from './emitAsync'\nimport { RequestController } from '../RequestController'\nimport {\n createServerErrorResponse,\n isResponseError,\n isResponseLike,\n} from './responseUtils'\nimport { InterceptorError } from '../InterceptorError'\nimport { isNodeLikeError } from './isNodeLikeError'\nimport { isObject } from './isObject'\n\ninterface HandleRequestOptions {\n requestId: string\n request: Request\n emitter: Emitter<HttpRequestEventMap>\n controller: RequestController\n}\n\nexport async function handleRequest(\n options: HandleRequestOptions\n): Promise<void> {\n const handleResponse = async (\n response: Response | Error | Record<string, any>\n ) => {\n if (response instanceof Error) {\n await options.controller.errorWith(response)\n return true\n }\n\n // Handle \"Response.error()\" instances.\n if (isResponseError(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n /**\n * Handle normal responses or response-like objects.\n * @note This must come before the arbitrary object check\n * since Response instances are, in fact, objects.\n */\n if (isResponseLike(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n // Handle arbitrary objects provided to `.errorWith(reason)`.\n if (isObject(response)) {\n await options.controller.errorWith(response)\n return true\n }\n\n return false\n }\n\n const handleResponseError = async (error: unknown): Promise<boolean> => {\n // Forward the special interceptor error instances\n // to the developer. These must not be handled in any way.\n if (error instanceof InterceptorError) {\n throw result.error\n }\n\n // Support mocking Node.js-like errors.\n if (isNodeLikeError(error)) {\n await options.controller.errorWith(error)\n return true\n }\n\n // Handle thrown responses.\n if (error instanceof Response) {\n return await handleResponse(error)\n }\n\n return false\n }\n\n // Add the last \"request\" listener to check if the request\n // has been handled in any way. If it hasn't, resolve the\n // response promise with undefined.\n // options.emitter.once('request', async ({ requestId: pendingRequestId }) => {\n // if (\n // pendingRequestId === options.requestId &&\n // options.controller.readyState === RequestController.PENDING\n // ) {\n // await options.controller.passthrough()\n // }\n // })\n\n const requestAbortPromise = new DeferredPromise<void, unknown>()\n\n /**\n * @note `signal` is not always defined in React Native.\n */\n if (options.request.signal) {\n if (options.request.signal.aborted) {\n await options.controller.errorWith(options.request.signal.reason)\n return\n }\n\n options.request.signal.addEventListener(\n 'abort',\n () => {\n requestAbortPromise.reject(options.request.signal.reason)\n },\n { once: true }\n )\n }\n\n const result = await until(async () => {\n // Emit the \"request\" event and wait until all the listeners\n // for that event are finished (e.g. async listeners awaited).\n // By the end of this promise, the developer cannot affect the\n // request anymore.\n const requestListenersPromise = emitAsync(options.emitter, 'request', {\n requestId: options.requestId,\n request: options.request,\n controller: options.controller,\n })\n\n await Promise.race([\n // Short-circuit the request handling promise if the request gets aborted.\n requestAbortPromise,\n requestListenersPromise,\n options.controller.handled,\n ])\n })\n\n // Handle the request being aborted while waiting for the request listeners.\n if (requestAbortPromise.state === 'rejected') {\n await options.controller.errorWith(requestAbortPromise.rejectionReason)\n return\n }\n\n if (result.error) {\n // Handle the error during the request listener execution.\n // These can be thrown responses or request errors.\n if (await handleResponseError(result.error)) {\n return\n }\n\n // If the developer has added \"unhandledException\" listeners,\n // allow them to handle the error. They can translate it to a\n // mocked response, network error, or forward it as-is.\n if (options.emitter.listenerCount('unhandledException') > 0) {\n // Create a new request controller just for the unhandled exception case.\n // This is needed because the original controller might have been already\n // interacted with (e.g. \"respondWith\" or \"errorWith\" called on it).\n const unhandledExceptionController = new RequestController(\n options.request,\n {\n /**\n * @note Intentionally empty passthrough handle.\n * This controller is created within another controller and we only need\n * to know if `unhandledException` listeners handled the request.\n */\n passthrough() {},\n async respondWith(response) {\n await handleResponse(response)\n },\n async errorWith(reason) {\n /**\n * @note Handle the result of the unhandled controller\n * in the same way as the original request controller.\n * The exception here is that thrown errors within the\n * \"unhandledException\" event do NOT result in another\n * emit of the same event. They are forwarded as-is.\n */\n await options.controller.errorWith(reason)\n },\n }\n )\n\n await emitAsync(options.emitter, 'unhandledException', {\n error: result.error,\n request: options.request,\n requestId: options.requestId,\n controller: unhandledExceptionController,\n })\n\n // If all the \"unhandledException\" listeners have finished\n // but have not handled the request in any way, passthrough.\n if (\n unhandledExceptionController.readyState !== RequestController.PENDING\n ) {\n return\n }\n }\n\n // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.\n await options.controller.respondWith(\n createServerErrorResponse(result.error)\n )\n return\n }\n\n // If the request hasn't been handled by this point, passthrough.\n if (options.controller.readyState === RequestController.PENDING) {\n return await options.controller.passthrough()\n }\n\n return options.controller.handled\n}\n"],"mappings":"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"}
const require_getRawRequest = require('./getRawRequest-DdfaiPVH.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-BS75Oulv.cjs');
let _open_draft_deferred_promise = require("@open-draft/deferred-promise");
let _open_draft_until = require("@open-draft/until");
//#region src/utils/isObject.ts
/**
* Determines if a given value is an instance of object.
*/
function isObject(value, loose = false) {
return loose ? Object.prototype.toString.call(value).startsWith("[object ") : Object.prototype.toString.call(value) === "[object Object]";
}
//#endregion
//#region src/utils/isPropertyAccessible.ts
/**
* A function that validates if property access is possible on an object
* without throwing. It returns `true` if the property access is possible
* and `false` otherwise.
*
* Environments like miniflare will throw on property access on certain objects
* like Request and Response, for unimplemented properties.
*/
function isPropertyAccessible(obj, key) {
try {
obj[key];
return true;
} catch {
return false;
}
}
//#endregion
//#region src/utils/responseUtils.ts
/**
* Creates a generic 500 Unhandled Exception response.
*/
function createServerErrorResponse(body) {
return new Response(JSON.stringify(body instanceof Error ? {
name: body.name,
message: body.message,
stack: body.stack
} : body), {
status: 500,
statusText: "Unhandled Exception",
headers: { "Content-Type": "application/json" }
});
}
/**
* Check if the given response is a `Response.error()`.
*
* @note Some environments, like Miniflare (Cloudflare) do not
* implement the "Response.type" property and throw on its access.
* Safely check if we can access "type" on "Response" before continuing.
* @see https://github.com/mswjs/msw/issues/1834
*/
function isResponseError(response) {
return response != null && response instanceof Response && isPropertyAccessible(response, "type") && response.type === "error";
}
/**
* Check if the given value is a `Response` or a Response-like object.
* This is different from `value instanceof Response` because it supports
* custom `Response` constructors, like the one when using Undici directly.
*/
function isResponseLike(value) {
return isObject(value, true) && isPropertyAccessible(value, "status") && isPropertyAccessible(value, "statusText") && isPropertyAccessible(value, "bodyUsed");
}
//#endregion
//#region src/utils/isNodeLikeError.ts
function isNodeLikeError(error) {
if (error == null) return false;
if (!(error instanceof Error)) return false;
return "code" in error && "errno" in error;
}
//#endregion
//#region src/utils/handleRequest.ts
async function handleRequest(options) {
const handleResponse = async (response) => {
if (response instanceof Error) {
await options.controller.errorWith(response);
return true;
}
if (isResponseError(response)) {
await options.controller.respondWith(response);
return true;
}
/**
* Handle normal responses or response-like objects.
* @note This must come before the arbitrary object check
* since Response instances are, in fact, objects.
*/
if (isResponseLike(response)) {
await options.controller.respondWith(response);
return true;
}
if (isObject(response)) {
await options.controller.errorWith(response);
return true;
}
return false;
};
const handleResponseError = async (error) => {
if (error instanceof require_getRawRequest.InterceptorError) throw result.error;
if (isNodeLikeError(error)) {
await options.controller.errorWith(error);
return true;
}
if (error instanceof Response) return await handleResponse(error);
return false;
};
const requestAbortPromise = new _open_draft_deferred_promise.DeferredPromise();
/**
* @note `signal` is not always defined in React Native.
*/
if (options.request.signal) {
if (options.request.signal.aborted) {
await options.controller.errorWith(options.request.signal.reason);
return;
}
options.request.signal.addEventListener("abort", () => {
requestAbortPromise.reject(options.request.signal.reason);
}, { once: true });
}
const result = await (0, _open_draft_until.until)(async () => {
const requestListenersPromise = require_hasConfigurableGlobal.emitAsync(options.emitter, "request", {
requestId: options.requestId,
request: options.request,
controller: options.controller
});
await Promise.race([
requestAbortPromise,
requestListenersPromise,
options.controller.handled
]);
});
if (requestAbortPromise.state === "rejected") {
await options.controller.errorWith(requestAbortPromise.rejectionReason);
return;
}
if (result.error) {
if (await handleResponseError(result.error)) return;
if (options.emitter.listenerCount("unhandledException") > 0) {
const unhandledExceptionController = new require_getRawRequest.RequestController(options.request, {
passthrough() {},
async respondWith(response) {
await handleResponse(response);
},
async errorWith(reason) {
/**
* @note Handle the result of the unhandled controller
* in the same way as the original request controller.
* The exception here is that thrown errors within the
* "unhandledException" event do NOT result in another
* emit of the same event. They are forwarded as-is.
*/
await options.controller.errorWith(reason);
}
});
await require_hasConfigurableGlobal.emitAsync(options.emitter, "unhandledException", {
error: result.error,
request: options.request,
requestId: options.requestId,
controller: unhandledExceptionController
});
if (unhandledExceptionController.readyState !== require_getRawRequest.RequestController.PENDING) return;
}
await options.controller.respondWith(createServerErrorResponse(result.error));
return;
}
if (options.controller.readyState === require_getRawRequest.RequestController.PENDING) return await options.controller.passthrough();
return options.controller.handled;
}
//#endregion
Object.defineProperty(exports, 'handleRequest', {
enumerable: true,
get: function () {
return handleRequest;
}
});
Object.defineProperty(exports, 'isResponseError', {
enumerable: true,
get: function () {
return isResponseError;
}
});
//# sourceMappingURL=handleRequest-tUNU616J.cjs.map
{"version":3,"file":"handleRequest-tUNU616J.cjs","names":["InterceptorError","DeferredPromise","emitAsync","RequestController"],"sources":["../../src/utils/isObject.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/responseUtils.ts","../../src/utils/isNodeLikeError.ts","../../src/utils/handleRequest.ts"],"sourcesContent":["/**\n * Determines if a given value is an instance of object.\n */\nexport function isObject<T>(value: any, loose = false): value is T {\n return loose\n ? Object.prototype.toString.call(value).startsWith('[object ')\n : Object.prototype.toString.call(value) === '[object Object]'\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","import { isObject } from './isObject'\nimport { isPropertyAccessible } from './isPropertyAccessible'\n\n/**\n * Creates a generic 500 Unhandled Exception response.\n */\nexport function createServerErrorResponse(body: unknown): Response {\n return new Response(\n JSON.stringify(\n body instanceof Error\n ? {\n name: body.name,\n message: body.message,\n stack: body.stack,\n }\n : body\n ),\n {\n status: 500,\n statusText: 'Unhandled Exception',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n}\n\nexport type ResponseError = Response & { type: 'error' }\n\n/**\n * Check if the given response is a `Response.error()`.\n *\n * @note Some environments, like Miniflare (Cloudflare) do not\n * implement the \"Response.type\" property and throw on its access.\n * Safely check if we can access \"type\" on \"Response\" before continuing.\n * @see https://github.com/mswjs/msw/issues/1834\n */\nexport function isResponseError(response: unknown): response is ResponseError {\n return (\n response != null &&\n response instanceof Response &&\n isPropertyAccessible(response, 'type') &&\n response.type === 'error'\n )\n}\n\n/**\n * Check if the given value is a `Response` or a Response-like object.\n * This is different from `value instanceof Response` because it supports\n * custom `Response` constructors, like the one when using Undici directly.\n */\nexport function isResponseLike(value: unknown): value is Response {\n return (\n isObject<Record<string, any>>(value, true) &&\n isPropertyAccessible(value, 'status') &&\n isPropertyAccessible(value, 'statusText') &&\n isPropertyAccessible(value, 'bodyUsed')\n )\n}\n","export function isNodeLikeError(\n error: unknown\n): error is NodeJS.ErrnoException {\n if (error == null) {\n return false\n }\n\n if (!(error instanceof Error)) {\n return false\n }\n\n return 'code' in error && 'errno' in error\n}\n","import type { Emitter } from 'strict-event-emitter'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport type { HttpRequestEventMap } from '../glossary'\nimport { emitAsync } from './emitAsync'\nimport { RequestController } from '../RequestController'\nimport {\n createServerErrorResponse,\n isResponseError,\n isResponseLike,\n} from './responseUtils'\nimport { InterceptorError } from '../InterceptorError'\nimport { isNodeLikeError } from './isNodeLikeError'\nimport { isObject } from './isObject'\n\ninterface HandleRequestOptions {\n requestId: string\n request: Request\n emitter: Emitter<HttpRequestEventMap>\n controller: RequestController\n}\n\nexport async function handleRequest(\n options: HandleRequestOptions\n): Promise<void> {\n const handleResponse = async (\n response: Response | Error | Record<string, any>\n ) => {\n if (response instanceof Error) {\n await options.controller.errorWith(response)\n return true\n }\n\n // Handle \"Response.error()\" instances.\n if (isResponseError(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n /**\n * Handle normal responses or response-like objects.\n * @note This must come before the arbitrary object check\n * since Response instances are, in fact, objects.\n */\n if (isResponseLike(response)) {\n await options.controller.respondWith(response)\n return true\n }\n\n // Handle arbitrary objects provided to `.errorWith(reason)`.\n if (isObject(response)) {\n await options.controller.errorWith(response)\n return true\n }\n\n return false\n }\n\n const handleResponseError = async (error: unknown): Promise<boolean> => {\n // Forward the special interceptor error instances\n // to the developer. These must not be handled in any way.\n if (error instanceof InterceptorError) {\n throw result.error\n }\n\n // Support mocking Node.js-like errors.\n if (isNodeLikeError(error)) {\n await options.controller.errorWith(error)\n return true\n }\n\n // Handle thrown responses.\n if (error instanceof Response) {\n return await handleResponse(error)\n }\n\n return false\n }\n\n // Add the last \"request\" listener to check if the request\n // has been handled in any way. If it hasn't, resolve the\n // response promise with undefined.\n // options.emitter.once('request', async ({ requestId: pendingRequestId }) => {\n // if (\n // pendingRequestId === options.requestId &&\n // options.controller.readyState === RequestController.PENDING\n // ) {\n // await options.controller.passthrough()\n // }\n // })\n\n const requestAbortPromise = new DeferredPromise<void, unknown>()\n\n /**\n * @note `signal` is not always defined in React Native.\n */\n if (options.request.signal) {\n if (options.request.signal.aborted) {\n await options.controller.errorWith(options.request.signal.reason)\n return\n }\n\n options.request.signal.addEventListener(\n 'abort',\n () => {\n requestAbortPromise.reject(options.request.signal.reason)\n },\n { once: true }\n )\n }\n\n const result = await until(async () => {\n // Emit the \"request\" event and wait until all the listeners\n // for that event are finished (e.g. async listeners awaited).\n // By the end of this promise, the developer cannot affect the\n // request anymore.\n const requestListenersPromise = emitAsync(options.emitter, 'request', {\n requestId: options.requestId,\n request: options.request,\n controller: options.controller,\n })\n\n await Promise.race([\n // Short-circuit the request handling promise if the request gets aborted.\n requestAbortPromise,\n requestListenersPromise,\n options.controller.handled,\n ])\n })\n\n // Handle the request being aborted while waiting for the request listeners.\n if (requestAbortPromise.state === 'rejected') {\n await options.controller.errorWith(requestAbortPromise.rejectionReason)\n return\n }\n\n if (result.error) {\n // Handle the error during the request listener execution.\n // These can be thrown responses or request errors.\n if (await handleResponseError(result.error)) {\n return\n }\n\n // If the developer has added \"unhandledException\" listeners,\n // allow them to handle the error. They can translate it to a\n // mocked response, network error, or forward it as-is.\n if (options.emitter.listenerCount('unhandledException') > 0) {\n // Create a new request controller just for the unhandled exception case.\n // This is needed because the original controller might have been already\n // interacted with (e.g. \"respondWith\" or \"errorWith\" called on it).\n const unhandledExceptionController = new RequestController(\n options.request,\n {\n /**\n * @note Intentionally empty passthrough handle.\n * This controller is created within another controller and we only need\n * to know if `unhandledException` listeners handled the request.\n */\n passthrough() {},\n async respondWith(response) {\n await handleResponse(response)\n },\n async errorWith(reason) {\n /**\n * @note Handle the result of the unhandled controller\n * in the same way as the original request controller.\n * The exception here is that thrown errors within the\n * \"unhandledException\" event do NOT result in another\n * emit of the same event. They are forwarded as-is.\n */\n await options.controller.errorWith(reason)\n },\n }\n )\n\n await emitAsync(options.emitter, 'unhandledException', {\n error: result.error,\n request: options.request,\n requestId: options.requestId,\n controller: unhandledExceptionController,\n })\n\n // If all the \"unhandledException\" listeners have finished\n // but have not handled the request in any way, passthrough.\n if (\n unhandledExceptionController.readyState !== RequestController.PENDING\n ) {\n return\n }\n }\n\n // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.\n await options.controller.respondWith(\n createServerErrorResponse(result.error)\n )\n return\n }\n\n // If the request hasn't been handled by this point, passthrough.\n if (options.controller.readyState === RequestController.PENDING) {\n return await options.controller.passthrough()\n }\n\n return options.controller.handled\n}\n"],"mappings":"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"}
let outvariant = require("outvariant");
//#region src/utils/emitAsync.ts
/**
* Emits an event on the given emitter but executes
* the listeners sequentially. This accounts for asynchronous
* listeners (e.g. those having "sleep" and handling the request).
*/
async function emitAsync(emitter, eventName, ...data) {
const listeners = emitter.listeners(eventName);
if (listeners.length === 0) return;
for (const listener of listeners) await listener.apply(emitter, data);
}
//#endregion
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
(0, outvariant.invariant)(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner) Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
Object.defineProperty(exports, 'emitAsync', {
enumerable: true,
get: function () {
return emitAsync;
}
});
Object.defineProperty(exports, 'hasConfigurableGlobal', {
enumerable: true,
get: function () {
return hasConfigurableGlobal;
}
});
Object.defineProperty(exports, 'patchesRegistry', {
enumerable: true,
get: function () {
return patchesRegistry;
}
});
//# sourceMappingURL=hasConfigurableGlobal-BS75Oulv.cjs.map
{"version":3,"file":"hasConfigurableGlobal-BS75Oulv.cjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/emitAsync.ts","../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { Emitter, EventMap } from 'strict-event-emitter'\n\n/**\n * Emits an event on the given emitter but executes\n * the listeners sequentially. This accounts for asynchronous\n * listeners (e.g. those having \"sleep\" and handling the request).\n */\nexport async function emitAsync<\n Events extends EventMap,\n EventName extends keyof Events\n>(\n emitter: Emitter<Events>,\n eventName: EventName,\n ...data: Events[EventName]\n): Promise<void> {\n const listeners = emitter.listeners(eventName)\n\n if (listeners.length === 0) {\n return\n }\n\n for (const listener of listeners) {\n await listener.apply(emitter, data)\n }\n}\n","import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,UAIpB,SACA,WACA,GAAG,MACY;CACf,MAAM,YAAY,QAAQ,UAAU,UAAU;AAE9C,KAAI,UAAU,WAAW,EACvB;AAGF,MAAK,MAAM,YAAY,UACrB,OAAM,SAAS,MAAM,SAAS,KAAK;;;;;ACpBvC,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,4BACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,SAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;EAEF,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU,MAClB,QAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;ACjHtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
import { invariant } from "outvariant";
//#region src/utils/emitAsync.ts
/**
* Emits an event on the given emitter but executes
* the listeners sequentially. This accounts for asynchronous
* listeners (e.g. those having "sleep" and handling the request).
*/
async function emitAsync(emitter, eventName, ...data) {
const listeners = emitter.listeners(eventName);
if (listeners.length === 0) return;
for (const listener of listeners) await listener.apply(emitter, data);
}
//#endregion
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
invariant(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner) Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
export { patchesRegistry as n, emitAsync as r, hasConfigurableGlobal as t };
//# sourceMappingURL=hasConfigurableGlobal-FTYwno1G.mjs.map
{"version":3,"file":"hasConfigurableGlobal-FTYwno1G.mjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/emitAsync.ts","../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { Emitter, EventMap } from 'strict-event-emitter'\n\n/**\n * Emits an event on the given emitter but executes\n * the listeners sequentially. This accounts for asynchronous\n * listeners (e.g. those having \"sleep\" and handling the request).\n */\nexport async function emitAsync<\n Events extends EventMap,\n EventName extends keyof Events\n>(\n emitter: Emitter<Events>,\n eventName: EventName,\n ...data: Events[EventName]\n): Promise<void> {\n const listeners = emitter.listeners(eventName)\n\n if (listeners.length === 0) {\n return\n }\n\n for (const listener of listeners) {\n await listener.apply(emitter, data)\n }\n}\n","import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,UAIpB,SACA,WACA,GAAG,MACY;CACf,MAAM,YAAY,QAAQ,UAAU,UAAU;AAE9C,KAAI,UAAU,WAAW,EACvB;AAGF,MAAK,MAAM,YAAY,UACrB,OAAM,SAAS,MAAM,SAAS,KAAK;;;;;ACpBvC,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,YACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,SAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;EAEF,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU,MAClB,QAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;ACjHtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
const require_createRequestId = require('./createRequestId-DOf8Ktjs.cjs');
const require_getRawRequest = require('./getRawRequest-DdfaiPVH.cjs');
const require_bufferUtils = require('./bufferUtils-Uc0eRItL.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-BS75Oulv.cjs');
const require_handleRequest = require('./handleRequest-tUNU616J.cjs');
let outvariant = require("outvariant");
let is_node_process = require("is-node-process");
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new require_getRawRequest.FetchResponse(require_getRawRequest.FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = (0, is_node_process.isNodeProcess)();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = require_createRequestId.createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? require_bufferUtils.encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(require_createRequestId.INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return require_bufferUtils.decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = require_bufferUtils.toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new require_getRawRequest.FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
require_getRawRequest.setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new require_getRawRequest.RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (require_handleRequest.isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends require_createRequestId.Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
Object.defineProperty(exports, 'XMLHttpRequestInterceptor', {
enumerable: true,
get: function () {
return XMLHttpRequestInterceptor;
}
});
//# sourceMappingURL=XMLHttpRequest-CHqOQWxg.cjs.map
{"version":3,"file":"XMLHttpRequest-CHqOQWxg.cjs","names":["handler: ProxyHandler<T>","next","FetchResponse","initialRequest: XMLHttpRequest","logger: Logger","createRequestId","encodeBuffer","INTERNAL_REQUEST_ID_HEADER_NAME","decodeBuffer","toArrayBuffer","FetchRequest","RequestController","isResponseError","handleRequest","Interceptor","hasConfigurableGlobal","patchesRegistry"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
import { n as INTERNAL_REQUEST_ID_HEADER_NAME, r as Interceptor, t as createRequestId } from "./createRequestId-DYCsFHOi.mjs";
import { i as FetchResponse, n as setRawRequest, o as RequestController, r as FetchRequest } from "./getRawRequest-B1BqgWG6.mjs";
import { n as encodeBuffer, r as toArrayBuffer, t as decodeBuffer } from "./bufferUtils-BiiO6HZv.mjs";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-FTYwno1G.mjs";
import { n as isResponseError, t as handleRequest } from "./handleRequest-BHrC8Flw.mjs";
import { invariant } from "outvariant";
import { isNodeProcess } from "is-node-process";
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new FetchResponse(FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = isNodeProcess();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
invariant(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
invariant(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
export { XMLHttpRequestInterceptor as t };
//# sourceMappingURL=XMLHttpRequest-DP9ps_69.mjs.map
{"version":3,"file":"XMLHttpRequest-DP9ps_69.mjs","names":["handler: ProxyHandler<T>","next","initialRequest: XMLHttpRequest","logger: Logger"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
import { a as RequestController, c as Interceptor, i as createRequestId, n as FetchResponse, r as canParseUrl, t as FetchRequest } from "./fetchUtils-BKJ1XmiO.mjs";
import { n as setRawRequest } from "./getRawRequest-C2-1urzA.mjs";
import { i as emitAsync, n as isResponseError, t as handleRequest } from "./handleRequest-DCLzePtS.mjs";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-Cwrs35Xo.mjs";
import { DeferredPromise } from "@open-draft/deferred-promise";
import { until } from "@open-draft/until";
import zlib from "node:zlib";
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
const decompress = zlib.createBrotliDecompress({
flush: zlib.constants.BROTLI_OPERATION_FLUSH,
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
});
super({ async transform(chunk, controller) {
const buffer = Buffer.from(chunk);
const decompressed = await new Promise((resolve, reject) => {
decompress.write(buffer, (error) => {
if (error) reject(error);
});
decompress.flush();
decompress.once("data", (data) => resolve(data));
decompress.once("error", (error) => reject(error));
decompress.once("end", () => controller.terminate());
}).catch((error) => {
controller.error(error);
});
controller.enqueue(decompressed);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = createRequestId();
const request = new FetchRequest(typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) setRawRequest(request, input);
const responsePromise = new DeferredPromise();
const controller = new RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await until(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = FetchResponse.clone(originalResponse);
await emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await emitAsync(this.emitter, "response", {
response: FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
export { FetchInterceptor as t };
//# sourceMappingURL=fetch-BciBtVqm.mjs.map
{"version":3,"file":"fetch-BciBtVqm.mjs","names":["locationUrl: URL","requestInit: RequestInit","readable","fetchProxy: typeof fetch","response"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","import zlib from 'node:zlib'\n\nexport class BrotliDecompressionStream extends TransformStream {\n constructor() {\n const decompress = zlib.createBrotliDecompress({\n flush: zlib.constants.BROTLI_OPERATION_FLUSH,\n finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,\n })\n\n super({\n async transform(chunk, controller) {\n const buffer = Buffer.from(chunk)\n\n const decompressed = await new Promise<Buffer>((resolve, reject) => {\n decompress.write(buffer, (error) => {\n if (error) reject(error)\n })\n\n decompress.flush()\n decompress.once('data', (data) => resolve(data))\n decompress.once('error', (error) => reject(error))\n decompress.once('end', () => controller.terminate())\n }).catch((error) => {\n controller.error(error)\n })\n\n controller.enqueue(decompressed)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
const require_chunk = require('./chunk-CbDLau6x.cjs');
const require_fetchUtils = require('./fetchUtils-umV5xXBy.cjs');
const require_getRawRequest = require('./getRawRequest-B6znU6il.cjs');
const require_handleRequest = require('./handleRequest-DVOthWJo.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-BtHi5OlL.cjs');
let _open_draft_deferred_promise = require("@open-draft/deferred-promise");
let _open_draft_until = require("@open-draft/until");
let node_zlib = require("node:zlib");
node_zlib = require_chunk.__toESM(node_zlib);
//#region src/interceptors/fetch/utils/createNetworkError.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/followRedirect.ts
const REQUEST_BODY_HEADERS = [
"content-encoding",
"content-language",
"content-location",
"content-type",
"content-length"
];
const kRedirectCount = Symbol("kRedirectCount");
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210
*/
async function followFetchRedirect(request, response) {
if (response.status !== 303 && request.body != null) return Promise.reject(createNetworkError());
const requestUrl = new URL(request.url);
let locationUrl;
try {
locationUrl = new URL(response.headers.get("location"), request.url);
} catch (error) {
return Promise.reject(createNetworkError(error));
}
if (!(locationUrl.protocol === "http:" || locationUrl.protocol === "https:")) return Promise.reject(createNetworkError("URL scheme must be a HTTP(S) scheme"));
if (Reflect.get(request, kRedirectCount) > 20) return Promise.reject(createNetworkError("redirect count exceeded"));
Object.defineProperty(request, kRedirectCount, { value: (Reflect.get(request, kRedirectCount) || 0) + 1 });
if (request.mode === "cors" && (locationUrl.username || locationUrl.password) && !sameOrigin(requestUrl, locationUrl)) return Promise.reject(createNetworkError("cross origin not allowed for request mode \"cors\""));
const requestInit = {};
if ([301, 302].includes(response.status) && request.method === "POST" || response.status === 303 && !["HEAD", "GET"].includes(request.method)) {
requestInit.method = "GET";
requestInit.body = null;
REQUEST_BODY_HEADERS.forEach((headerName) => {
request.headers.delete(headerName);
});
}
if (!sameOrigin(requestUrl, locationUrl)) {
request.headers.delete("authorization");
request.headers.delete("proxy-authorization");
request.headers.delete("cookie");
request.headers.delete("host");
}
/**
* @note Undici "safely" extracts the request body.
* I suspect we cannot dispatch this request again
* since its body has been read and the stream is locked.
*/
requestInit.headers = request.headers;
const finalResponse = await fetch(new Request(locationUrl, requestInit));
Object.defineProperty(finalResponse, "redirected", {
value: true,
configurable: true
});
return finalResponse;
}
/**
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761
*/
function sameOrigin(left, right) {
if (left.origin === right.origin && left.origin === "null") return true;
if (left.protocol === right.protocol && left.hostname === right.hostname && left.port === right.port) return true;
return false;
}
//#endregion
//#region src/interceptors/fetch/utils/brotli-decompress.ts
var BrotliDecompressionStream = class extends TransformStream {
constructor() {
const decompress = node_zlib.default.createBrotliDecompress({
flush: node_zlib.default.constants.BROTLI_OPERATION_FLUSH,
finishFlush: node_zlib.default.constants.BROTLI_OPERATION_FLUSH
});
super({ async transform(chunk, controller) {
const buffer = Buffer.from(chunk);
const decompressed = await new Promise((resolve, reject) => {
decompress.write(buffer, (error) => {
if (error) reject(error);
});
decompress.flush();
decompress.once("data", (data) => resolve(data));
decompress.once("error", (error) => reject(error));
decompress.once("end", () => controller.terminate());
}).catch((error) => {
controller.error(error);
});
controller.enqueue(decompressed);
} });
}
};
//#endregion
//#region src/interceptors/fetch/utils/decompression.ts
var PipelineStream = class extends TransformStream {
constructor(transformStreams, ...strategies) {
super({}, ...strategies);
const readable = [super.readable, ...transformStreams].reduce((readable$1, transform) => readable$1.pipeThrough(transform));
Object.defineProperty(this, "readable", { get() {
return readable;
} });
}
};
function parseContentEncoding(contentEncoding) {
return contentEncoding.toLowerCase().split(",").map((coding) => coding.trim());
}
function createDecompressionStream(contentEncoding) {
if (contentEncoding === "") return null;
const codings = parseContentEncoding(contentEncoding);
if (codings.length === 0) return null;
return new PipelineStream(codings.reduceRight((transformers, coding) => {
if (coding === "gzip" || coding === "x-gzip") return transformers.concat(new DecompressionStream("gzip"));
else if (coding === "deflate") return transformers.concat(new DecompressionStream("deflate"));
else if (coding === "br") return transformers.concat(new BrotliDecompressionStream());
else transformers.length = 0;
return transformers;
}, []));
}
function decompressResponse(response) {
if (response.body === null) return null;
const decompressionStream = createDecompressionStream(response.headers.get("content-encoding") || "");
if (!decompressionStream) return null;
response.body.pipeTo(decompressionStream.writable);
return decompressionStream.readable;
}
//#endregion
//#region src/interceptors/fetch/index.ts
var FetchInterceptor = class FetchInterceptor extends require_fetchUtils.Interceptor {
static {
this.symbol = Symbol("fetch");
}
constructor() {
super(FetchInterceptor.symbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("fetch");
}
async setup() {
const logger = this.logger.extend("setup");
const pureFetch = globalThis.fetch;
const fetchProxy = async (input, init) => {
const requestId = require_fetchUtils.createRequestId();
const request = new require_fetchUtils.FetchRequest(typeof input === "string" && typeof location !== "undefined" && !require_fetchUtils.canParseUrl(input) ? new URL(input, location.href) : input, init);
/**
* @note Set the raw request only if a Request instance was provided to fetch.
*/
if (input instanceof Request) require_getRawRequest.setRawRequest(request, input);
const responsePromise = new _open_draft_deferred_promise.DeferredPromise();
const controller = new require_fetchUtils.RequestController(request, {
passthrough: async () => {
this.logger.info("request has not been handled, passthrough...");
/**
* @note Clone the request instance right before performing it.
* This preserves any modifications made to the intercepted request
* in the "request" listener. This also allows the user to read the
* request body in the "response" listener (otherwise "unusable").
*/
const requestCloneForResponseEvent = request.clone();
const { error: responseError, data: originalResponse } = await (0, _open_draft_until.until)(() => pureFetch(request));
if (responseError) return responsePromise.reject(responseError);
this.logger.info("original fetch performed", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
const responseClone = require_fetchUtils.FetchResponse.clone(originalResponse);
await require_handleRequest.emitAsync(this.emitter, "response", {
response: responseClone,
isMockedResponse: false,
request: requestCloneForResponseEvent,
requestId
});
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (require_handleRequest.isResponseError(rawResponse)) {
this.logger.info("request has errored!", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
this.logger.info("received mocked response!", { rawResponse });
const response = new require_fetchUtils.FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
/**
* Undici's handling of following redirect responses.
* Treat the "manual" redirect mode as a regular mocked response.
* This way, the client can manually follow the redirect it receives.
* @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173
*/
if (require_fetchUtils.FetchResponse.isRedirectResponse(response.status)) {
if (request.redirect === "error") {
responsePromise.reject(createNetworkError("unexpected redirect"));
return;
}
if (request.redirect === "follow") {
followFetchRedirect(request, response).then((response$1) => {
responsePromise.resolve(response$1);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
this.logger.info("emitting the \"response\" event...");
await require_handleRequest.emitAsync(this.emitter, "response", {
response: require_fetchUtils.FetchResponse.clone(response),
isMockedResponse: true,
request,
requestId
});
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
this.logger.info("request has been aborted!", { reason });
responsePromise.reject(reason);
}
});
this.logger.info("[%s] %s", request.method, request.url);
this.logger.info("awaiting for the mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
emitter: this.emitter,
controller
});
return responsePromise;
};
logger.info("patching global fetch...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "fetch", () => fetchProxy));
logger.info("global fetch patched!", globalThis.fetch.name);
}
};
//#endregion
Object.defineProperty(exports, 'FetchInterceptor', {
enumerable: true,
get: function () {
return FetchInterceptor;
}
});
//# sourceMappingURL=fetch-znfXVEPe.cjs.map
{"version":3,"file":"fetch-znfXVEPe.cjs","names":["locationUrl: URL","requestInit: RequestInit","zlib","readable","Interceptor","hasConfigurableGlobal","fetchProxy: typeof fetch","createRequestId","FetchRequest","canParseUrl","DeferredPromise","RequestController","FetchResponse","emitAsync","isResponseError","response","handleRequest","patchesRegistry"],"sources":["../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/index.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","import zlib from 'node:zlib'\n\nexport class BrotliDecompressionStream extends TransformStream {\n constructor() {\n const decompress = zlib.createBrotliDecompress({\n flush: zlib.constants.BROTLI_OPERATION_FLUSH,\n finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,\n })\n\n super({\n async transform(chunk, controller) {\n const buffer = Buffer.from(chunk)\n\n const decompressed = await new Promise<Buffer>((resolve, reject) => {\n decompress.write(buffer, (error) => {\n if (error) reject(error)\n })\n\n decompress.flush()\n decompress.once('data', (data) => resolve(data))\n decompress.once('error', (error) => reject(error))\n decompress.once('end', () => controller.terminate())\n }).catch((error) => {\n controller.error(error)\n })\n\n controller.enqueue(decompressed)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n","import { until } from '@open-draft/until'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchRequest, FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\nimport { isResponseError } from '../../utils/responseUtils'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\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 hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const logger = this.logger.extend('setup')\n\n const pureFetch = globalThis.fetch\n\n const fetchProxy: typeof fetch = async (input, init) => {\n const requestId = createRequestId()\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.href)\n : input\n\n const request = new FetchRequest(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n\n const controller = new RequestController(request, {\n passthrough: async () => {\n this.logger.info('request has not been handled, passthrough...')\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n // Perform the intercepted request as-is.\n const { error: responseError, data: originalResponse } = await until(\n () => pureFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n this.logger.info('original fetch performed', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n // Resolve the response promise with the original response\n // since the `fetch()` return this internal promise.\n responsePromise.resolve(originalResponse)\n },\n respondWith: async (rawResponse) => {\n // Handle mocked `Response.error()` (i.e. request errors).\n if (isResponseError(rawResponse)) {\n this.logger.info('request has errored!', { response: rawResponse })\n responsePromise.reject(createNetworkError(rawResponse))\n return\n }\n\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response = new FetchResponse(\n decompressedStream || rawResponse.body,\n {\n url: request.url,\n status: rawResponse.status,\n statusText: rawResponse.statusText,\n headers: rawResponse.headers,\n }\n )\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\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 response: FetchResponse.clone(response),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n this.logger.info('request has been aborted!', { reason })\n responsePromise.reject(reason)\n },\n })\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n })\n\n return responsePromise\n }\n\n logger.info('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', () => fetchProxy)\n )\n\n logger.info('global fetch patched!', globalThis.fetch.name)\n }\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAgB,mBAAmB,OAAiB;AAClD,QAAO,OAAO,uBAAO,IAAI,UAAU,kBAAkB,EAAE,EACrD,OACD,CAAC;;;;;ACDJ,MAAM,uBAAuB;CAC3B;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,iBAAiB,OAAO,iBAAiB;;;;AAK/C,eAAsB,oBACpB,SACA,UACmB;AACnB,KAAI,SAAS,WAAW,OAAO,QAAQ,QAAQ,KAC7C,QAAO,QAAQ,OAAO,oBAAoB,CAAC;CAG7C,MAAM,aAAa,IAAI,IAAI,QAAQ,IAAI;CAEvC,IAAIA;AACJ,KAAI;AAEF,gBAAc,IAAI,IAAI,SAAS,QAAQ,IAAI,WAAW,EAAG,QAAQ,IAAI;UAC9D,OAAO;AACd,SAAO,QAAQ,OAAO,mBAAmB,MAAM,CAAC;;AAGlD,KACE,EAAE,YAAY,aAAa,WAAW,YAAY,aAAa,UAE/D,QAAO,QAAQ,OACb,mBAAmB,sCAAsC,CAC1D;AAGH,KAAI,QAAQ,IAAI,SAAS,eAAe,GAAG,GACzC,QAAO,QAAQ,OAAO,mBAAmB,0BAA0B,CAAC;AAGtE,QAAO,eAAe,SAAS,gBAAgB,EAC7C,QAAQ,QAAQ,IAAI,SAAS,eAAe,IAAI,KAAK,GACtD,CAAC;AAEF,KACE,QAAQ,SAAS,WAChB,YAAY,YAAY,YAAY,aACrC,CAAC,WAAW,YAAY,YAAY,CAEpC,QAAO,QAAQ,OACb,mBAAmB,qDAAmD,CACvE;CAGH,MAAMC,cAA2B,EAAE;AAEnC,KACG,CAAC,KAAK,IAAI,CAAC,SAAS,SAAS,OAAO,IAAI,QAAQ,WAAW,UAC3D,SAAS,WAAW,OAAO,CAAC,CAAC,QAAQ,MAAM,CAAC,SAAS,QAAQ,OAAO,EACrE;AACA,cAAY,SAAS;AACrB,cAAY,OAAO;AAEnB,uBAAqB,SAAS,eAAe;AAC3C,WAAQ,QAAQ,OAAO,WAAW;IAClC;;AAGJ,KAAI,CAAC,WAAW,YAAY,YAAY,EAAE;AACxC,UAAQ,QAAQ,OAAO,gBAAgB;AACvC,UAAQ,QAAQ,OAAO,sBAAsB;AAC7C,UAAQ,QAAQ,OAAO,SAAS;AAChC,UAAQ,QAAQ,OAAO,OAAO;;;;;;;AAShC,aAAY,UAAU,QAAQ;CAC9B,MAAM,gBAAgB,MAAM,MAAM,IAAI,QAAQ,aAAa,YAAY,CAAC;AACxE,QAAO,eAAe,eAAe,cAAc;EACjD,OAAO;EACP,cAAc;EACf,CAAC;AAEF,QAAO;;;;;AAMT,SAAS,WAAW,MAAW,OAAqB;AAClD,KAAI,KAAK,WAAW,MAAM,UAAU,KAAK,WAAW,OAClD,QAAO;AAGT,KACE,KAAK,aAAa,MAAM,YACxB,KAAK,aAAa,MAAM,YACxB,KAAK,SAAS,MAAM,KAEpB,QAAO;AAGT,QAAO;;;;;AC9GT,IAAa,4BAAb,cAA+C,gBAAgB;CAC7D,cAAc;EACZ,MAAM,aAAaC,kBAAK,uBAAuB;GAC7C,OAAOA,kBAAK,UAAU;GACtB,aAAaA,kBAAK,UAAU;GAC7B,CAAC;AAEF,QAAM,EACJ,MAAM,UAAU,OAAO,YAAY;GACjC,MAAM,SAAS,OAAO,KAAK,MAAM;GAEjC,MAAM,eAAe,MAAM,IAAI,SAAiB,SAAS,WAAW;AAClE,eAAW,MAAM,SAAS,UAAU;AAClC,SAAI,MAAO,QAAO,MAAM;MACxB;AAEF,eAAW,OAAO;AAClB,eAAW,KAAK,SAAS,SAAS,QAAQ,KAAK,CAAC;AAChD,eAAW,KAAK,UAAU,UAAU,OAAO,MAAM,CAAC;AAClD,eAAW,KAAK,aAAa,WAAW,WAAW,CAAC;KACpD,CAAC,OAAO,UAAU;AAClB,eAAW,MAAM,MAAM;KACvB;AAEF,cAAW,QAAQ,aAAa;KAEnC,CAAC;;;;;;ACvBN,IAAM,iBAAN,cAA6B,gBAAgB;CAC3C,YACE,kBACA,GAAG,YACH;AACA,QAAM,EAAE,EAAE,GAAG,WAAW;EAExB,MAAM,WAAW,CAAC,MAAM,UAAiB,GAAG,iBAAiB,CAAC,QAC3D,YAAU,cAAcC,WAAS,YAAY,UAAU,CACzD;AAED,SAAO,eAAe,MAAM,YAAY,EACtC,MAAM;AACJ,UAAO;KAEV,CAAC;;;AAIN,SAAgB,qBAAqB,iBAAwC;AAC3E,QAAO,gBACJ,aAAa,CACb,MAAM,IAAI,CACV,KAAK,WAAW,OAAO,MAAM,CAAC;;AAGnC,SAAS,0BACP,iBACwB;AACxB,KAAI,oBAAoB,GACtB,QAAO;CAGT,MAAM,UAAU,qBAAqB,gBAAgB;AAErD,KAAI,QAAQ,WAAW,EACrB,QAAO;AAoBT,QAAO,IAAI,eAjBU,QAAQ,aAC1B,cAAc,WAAW;AACxB,MAAI,WAAW,UAAU,WAAW,SAClC,QAAO,aAAa,OAAO,IAAI,oBAAoB,OAAO,CAAC;WAClD,WAAW,UACpB,QAAO,aAAa,OAAO,IAAI,oBAAoB,UAAU,CAAC;WACrD,WAAW,KACpB,QAAO,aAAa,OAAO,IAAI,2BAA2B,CAAC;MAE3D,cAAa,SAAS;AAGxB,SAAO;IAET,EAAE,CACH,CAEsC;;AAGzC,SAAgB,mBACd,UAC4B;AAC5B,KAAI,SAAS,SAAS,KACpB,QAAO;CAGT,MAAM,sBAAsB,0BAC1B,SAAS,QAAQ,IAAI,mBAAmB,IAAI,GAC7C;AAED,KAAI,CAAC,oBACH,QAAO;AAMT,UAAS,KAAK,OAAO,oBAAoB,SAAS;AAClD,QAAO,oBAAoB;;;;;ACjE7B,IAAa,mBAAb,MAAa,yBAAyBC,+BAAiC;;gBACrD,OAAO,QAAQ;;CAE/B,cAAc;AACZ,QAAM,iBAAiB,OAAO;;CAGhC,AAAU,mBAAmB;AAC3B,SAAOC,oDAAsB,QAAQ;;CAGvC,MAAgB,QAAQ;EACtB,MAAM,SAAS,KAAK,OAAO,OAAO,QAAQ;EAE1C,MAAM,YAAY,WAAW;EAE7B,MAAMC,aAA2B,OAAO,OAAO,SAAS;GACtD,MAAM,YAAYC,oCAAiB;GAenC,MAAM,UAAU,IAAIC,gCANlB,OAAO,UAAU,YACjB,OAAO,aAAa,eACpB,CAACC,+BAAY,MAAM,GACf,IAAI,IAAI,OAAO,SAAS,KAAK,GAC7B,OAE0C,KAAK;;;;AAKrD,OAAI,iBAAiB,QACnB,qCAAc,SAAS,MAAM;GAG/B,MAAM,kBAAkB,IAAIC,8CAA2B;GAEvD,MAAM,aAAa,IAAIC,qCAAkB,SAAS;IAChD,aAAa,YAAY;AACvB,UAAK,OAAO,KAAK,+CAA+C;;;;;;;KAQhE,MAAM,+BAA+B,QAAQ,OAAO;KAGpD,MAAM,EAAE,OAAO,eAAe,MAAM,qBAAqB,yCACjD,UAAU,QAAQ,CACzB;AAED,SAAI,cACF,QAAO,gBAAgB,OAAO,cAAc;AAG9C,UAAK,OAAO,KAAK,4BAA4B,iBAAiB;AAE9D,SAAI,KAAK,QAAQ,cAAc,WAAW,GAAG,GAAG;AAC9C,WAAK,OAAO,KAAK,qCAAmC;MAEpD,MAAM,gBAAgBC,iCAAc,MAAM,iBAAiB;AAC3D,YAAMC,gCAAU,KAAK,SAAS,YAAY;OACxC,UAAU;OACV,kBAAkB;OAClB,SAAS;OACT;OACD,CAAC;;AAKJ,qBAAgB,QAAQ,iBAAiB;;IAE3C,aAAa,OAAO,gBAAgB;AAElC,SAAIC,sCAAgB,YAAY,EAAE;AAChC,WAAK,OAAO,KAAK,wBAAwB,EAAE,UAAU,aAAa,CAAC;AACnE,sBAAgB,OAAO,mBAAmB,YAAY,CAAC;AACvD;;AAGF,UAAK,OAAO,KAAK,6BAA6B,EAC5C,aACD,CAAC;KAIF,MAAM,WAAW,IAAIF,iCADM,mBAAmB,YAAY,IAElC,YAAY,MAClC;MACE,KAAK,QAAQ;MACb,QAAQ,YAAY;MACpB,YAAY,YAAY;MACxB,SAAS,YAAY;MACtB,CACF;;;;;;;AAQD,SAAIA,iCAAc,mBAAmB,SAAS,OAAO,EAAE;AAGrD,UAAI,QAAQ,aAAa,SAAS;AAChC,uBAAgB,OAAO,mBAAmB,sBAAsB,CAAC;AACjE;;AAGF,UAAI,QAAQ,aAAa,UAAU;AACjC,2BAAoB,SAAS,SAAS,CAAC,MACpC,eAAa;AACZ,wBAAgB,QAAQG,WAAS;WAElC,WAAW;AACV,wBAAgB,OAAO,OAAO;SAEjC;AACD;;;AAIJ,SAAI,KAAK,QAAQ,cAAc,WAAW,GAAG,GAAG;AAC9C,WAAK,OAAO,KAAK,qCAAmC;AAKpD,YAAMF,gCAAU,KAAK,SAAS,YAAY;OAIxC,UAAUD,iCAAc,MAAM,SAAS;OACvC,kBAAkB;OAClB;OACA;OACD,CAAC;;AAGJ,qBAAgB,QAAQ,SAAS;;IAEnC,YAAY,WAAW;AACrB,UAAK,OAAO,KAAK,6BAA6B,EAAE,QAAQ,CAAC;AACzD,qBAAgB,OAAO,OAAO;;IAEjC,CAAC;AAEF,QAAK,OAAO,KAAK,WAAW,QAAQ,QAAQ,QAAQ,IAAI;AACxD,QAAK,OAAO,KAAK,sCAAsC;AAEvD,QAAK,OAAO,KACV,wDACA,KAAK,QAAQ,cAAc,UAAU,CACtC;AAED,SAAMI,oCAAc;IAClB;IACA;IACA,SAAS,KAAK;IACd;IACD,CAAC;AAEF,UAAO;;AAGT,SAAO,KAAK,2BAA2B;AAEvC,OAAK,cAAc,KACjBC,8CAAgB,WAAW,YAAY,eAAe,WAAW,CAClE;AAED,SAAO,KAAK,yBAAyB,WAAW,MAAM,KAAK"}
const require_chunk = require('./chunk-CbDLau6x.cjs');
let outvariant = require("outvariant");
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
(0, outvariant.invariant)(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner) Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
Object.defineProperty(exports, 'hasConfigurableGlobal', {
enumerable: true,
get: function () {
return hasConfigurableGlobal;
}
});
Object.defineProperty(exports, 'patchesRegistry', {
enumerable: true,
get: function () {
return patchesRegistry;
}
});
//# sourceMappingURL=hasConfigurableGlobal-BtHi5OlL.cjs.map
{"version":3,"file":"hasConfigurableGlobal-BtHi5OlL.cjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;;AAEA,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,4BACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,SAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;EAEF,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU,MAClB,QAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;ACjHtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
import { invariant } from "outvariant";
//#region src/utils/patchesRegistry.ts
var PatchesRegistry = class {
#replacements = /* @__PURE__ */ new Map();
applyPatch(owner, key, getNextValue) {
const ownerReplacements = this.#replacements.get(owner);
invariant(!ownerReplacements?.has(key), `Failed to replace a global value at "${String(key)}": already replaced.`);
const match = getDeepPropertyDescriptor(owner, key);
if (typeof match === "undefined") {
console.warn(`Failed to replace a global value at "${String(key)}": not a global value.`);
return () => {};
}
Object.defineProperty(owner, key, {
value: getNextValue(owner[key]),
enumerable: true,
configurable: true
});
const restorePatch = () => {
const currentReplacements = this.#replacements.get(owner);
if (!currentReplacements?.has(key)) return;
if (match.owner === owner) Object.defineProperty(match.owner, key, match.descriptor);
else
/**
* @todo Delete the proxy property set by the registry.
* If the match's owner isn't the original owner, the property is likely nested in the prototype.
* The registry does not meddle with those, they are left intact.
*/
Reflect.deleteProperty(owner, key);
currentReplacements.delete(key);
if (currentReplacements.size === 0) this.#replacements.delete(owner);
};
if (ownerReplacements) ownerReplacements.set(key, restorePatch);
else this.#replacements.set(owner, new Map([[key, restorePatch]]));
return restorePatch;
}
restoreAllPatches() {
const errors = [];
for (const [, ownerReplacements] of this.#replacements) for (const [, restorePatch] of ownerReplacements) try {
restorePatch();
} catch (error) {
if (error instanceof Error) errors.push(error);
else throw error;
}
if (errors.length > 0) throw new AggregateError(errors, "FOO!");
}
};
const patchesRegistry = new PatchesRegistry();
/**
* Returns a property descriptor for the given property on the owner.
* Walks down the prototype chain if the property does not exist on the owner.
* Handy for getting a global property descriptor where `globalThis` is
* replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
*/
function getDeepPropertyDescriptor(owner, key) {
let currentOwner = owner;
let descriptor;
while (currentOwner) {
descriptor = Object.getOwnPropertyDescriptor(currentOwner, key);
if (descriptor) return {
owner: currentOwner,
descriptor
};
currentOwner = Object.getPrototypeOf(currentOwner);
}
}
//#endregion
//#region src/utils/hasConfigurableGlobal.ts
/**
* Returns a boolean indicating whether the given global property
* is defined and is configurable.
*/
function hasConfigurableGlobal(propertyName) {
const match = getDeepPropertyDescriptor(globalThis, propertyName);
if (typeof match === "undefined") return false;
const { descriptor } = match;
if (typeof descriptor.get === "function" && typeof descriptor.get() === "undefined") return false;
if (typeof descriptor.get === "undefined" && descriptor.value == null) return false;
if (typeof descriptor.set === "undefined" && !descriptor.configurable) {
console.error(`[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`);
return false;
}
return true;
}
//#endregion
export { patchesRegistry as n, hasConfigurableGlobal as t };
//# sourceMappingURL=hasConfigurableGlobal-Cwrs35Xo.mjs.map
{"version":3,"file":"hasConfigurableGlobal-Cwrs35Xo.mjs","names":["#replacements","errors: Array<Error>","currentOwner: Owner | null","descriptor: PropertyDescriptor | undefined"],"sources":["../../src/utils/patchesRegistry.ts","../../src/utils/hasConfigurableGlobal.ts"],"sourcesContent":["import { invariant } from 'outvariant'\n\nclass PatchesRegistry {\n #replacements = new Map<object, Map<PropertyKey, () => void>>()\n\n public applyPatch<Owner extends object, K extends keyof Owner>(\n owner: Owner,\n key: K,\n getNextValue: (realValue: Owner[K]) => Owner[K]\n ): () => void {\n const ownerReplacements = this.#replacements.get(owner)\n\n invariant(\n !ownerReplacements?.has(key),\n `Failed to replace a global value at \"${String(key)}\": already replaced.`\n )\n\n const match = getDeepPropertyDescriptor(owner, key)\n\n if (typeof match === 'undefined') {\n console.warn(\n `Failed to replace a global value at \"${String(key)}\": not a global value.`\n )\n return () => {}\n }\n\n Object.defineProperty(owner, key, {\n value: getNextValue(owner[key]),\n enumerable: true,\n configurable: true,\n })\n\n const restorePatch = () => {\n const currentReplacements = this.#replacements.get(owner)\n\n if (!currentReplacements?.has(key)) {\n return\n }\n\n if (match.owner === owner) {\n Object.defineProperty(match.owner, key, match.descriptor)\n } else {\n /**\n * @todo Delete the proxy property set by the registry.\n * If the match's owner isn't the original owner, the property is likely nested in the prototype.\n * The registry does not meddle with those, they are left intact.\n */\n Reflect.deleteProperty(owner, key)\n }\n\n currentReplacements.delete(key)\n\n if (currentReplacements.size === 0) {\n this.#replacements.delete(owner)\n }\n }\n\n if (ownerReplacements) {\n ownerReplacements.set(key, restorePatch)\n } else {\n this.#replacements.set(owner, new Map([[key, restorePatch]]))\n }\n\n return restorePatch\n }\n\n public restoreAllPatches(): void {\n const errors: Array<Error> = []\n\n for (const [, ownerReplacements] of this.#replacements) {\n for (const [, restorePatch] of ownerReplacements) {\n try {\n restorePatch()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n } else {\n throw error\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'FOO!')\n }\n }\n}\n\nexport const patchesRegistry = new PatchesRegistry()\n\ninterface DeepDescriptorMatch {\n owner: object\n descriptor: PropertyDescriptor\n}\n\n/**\n * Returns a property descriptor for the given property on the owner.\n * Walks down the prototype chain if the property does not exist on the owner.\n * Handy for getting a global property descriptor where `globalThis` is\n * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).\n */\nexport function getDeepPropertyDescriptor<Owner extends object>(\n owner: Owner,\n key: keyof Owner\n): DeepDescriptorMatch | undefined {\n let currentOwner: Owner | null = owner\n let descriptor: PropertyDescriptor | undefined\n\n while (currentOwner) {\n descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)\n\n if (descriptor) {\n return {\n owner: currentOwner,\n descriptor,\n }\n }\n\n currentOwner = Object.getPrototypeOf(currentOwner)\n }\n}\n","import { getDeepPropertyDescriptor } from './patchesRegistry'\n\n/**\n * Returns a boolean indicating whether the given global property\n * is defined and is configurable.\n */\nexport function hasConfigurableGlobal(\n propertyName: keyof typeof globalThis\n): boolean {\n const match = getDeepPropertyDescriptor(globalThis, propertyName)\n\n // The property is not set at all.\n if (typeof match === 'undefined') {\n return false\n }\n\n const { descriptor } = match\n\n // The property is set to a getter that returns undefined.\n if (\n typeof descriptor.get === 'function' &&\n typeof descriptor.get() === 'undefined'\n ) {\n return false\n }\n\n // The property is set to a value equal to undefined.\n if (typeof descriptor.get === 'undefined' && descriptor.value == null) {\n return false\n }\n\n if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {\n console.error(\n `[MSW] Failed to apply interceptor: the global \\`${propertyName}\\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`\n )\n return false\n }\n\n return true\n}\n"],"mappings":";;;AAEA,IAAM,kBAAN,MAAsB;CACpB,gCAAgB,IAAI,KAA2C;CAE/D,AAAO,WACL,OACA,KACA,cACY;EACZ,MAAM,oBAAoB,MAAKA,aAAc,IAAI,MAAM;AAEvD,YACE,CAAC,mBAAmB,IAAI,IAAI,EAC5B,wCAAwC,OAAO,IAAI,CAAC,sBACrD;EAED,MAAM,QAAQ,0BAA0B,OAAO,IAAI;AAEnD,MAAI,OAAO,UAAU,aAAa;AAChC,WAAQ,KACN,wCAAwC,OAAO,IAAI,CAAC,wBACrD;AACD,gBAAa;;AAGf,SAAO,eAAe,OAAO,KAAK;GAChC,OAAO,aAAa,MAAM,KAAK;GAC/B,YAAY;GACZ,cAAc;GACf,CAAC;EAEF,MAAM,qBAAqB;GACzB,MAAM,sBAAsB,MAAKA,aAAc,IAAI,MAAM;AAEzD,OAAI,CAAC,qBAAqB,IAAI,IAAI,CAChC;AAGF,OAAI,MAAM,UAAU,MAClB,QAAO,eAAe,MAAM,OAAO,KAAK,MAAM,WAAW;;;;;;;AAOzD,WAAQ,eAAe,OAAO,IAAI;AAGpC,uBAAoB,OAAO,IAAI;AAE/B,OAAI,oBAAoB,SAAS,EAC/B,OAAKA,aAAc,OAAO,MAAM;;AAIpC,MAAI,kBACF,mBAAkB,IAAI,KAAK,aAAa;MAExC,OAAKA,aAAc,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC;AAG/D,SAAO;;CAGT,AAAO,oBAA0B;EAC/B,MAAMC,SAAuB,EAAE;AAE/B,OAAK,MAAM,GAAG,sBAAsB,MAAKD,aACvC,MAAK,MAAM,GAAG,iBAAiB,kBAC7B,KAAI;AACF,iBAAc;WACP,OAAO;AACd,OAAI,iBAAiB,MACnB,QAAO,KAAK,MAAM;OAElB,OAAM;;AAMd,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,QAAQ,OAAO;;;AAK9C,MAAa,kBAAkB,IAAI,iBAAiB;;;;;;;AAapD,SAAgB,0BACd,OACA,KACiC;CACjC,IAAIE,eAA6B;CACjC,IAAIC;AAEJ,QAAO,cAAc;AACnB,eAAa,OAAO,yBAAyB,cAAc,IAAI;AAE/D,MAAI,WACF,QAAO;GACL,OAAO;GACP;GACD;AAGH,iBAAe,OAAO,eAAe,aAAa;;;;;;;;;;ACjHtD,SAAgB,sBACd,cACS;CACT,MAAM,QAAQ,0BAA0B,YAAY,aAAa;AAGjE,KAAI,OAAO,UAAU,YACnB,QAAO;CAGT,MAAM,EAAE,eAAe;AAGvB,KACE,OAAO,WAAW,QAAQ,cAC1B,OAAO,WAAW,KAAK,KAAK,YAE5B,QAAO;AAIT,KAAI,OAAO,WAAW,QAAQ,eAAe,WAAW,SAAS,KAC/D,QAAO;AAGT,KAAI,OAAO,WAAW,QAAQ,eAAe,CAAC,WAAW,cAAc;AACrE,UAAQ,MACN,mDAAmD,aAAa,oKACjE;AACD,SAAO;;AAGT,QAAO"}
import { a as RequestController, c as Interceptor, i as createRequestId, n as FetchResponse, s as INTERNAL_REQUEST_ID_HEADER_NAME, t as FetchRequest } from "./fetchUtils-BKJ1XmiO.mjs";
import { n as encodeBuffer, r as toArrayBuffer, t as decodeBuffer } from "./bufferUtils-DxPxwff_.mjs";
import { n as setRawRequest } from "./getRawRequest-C2-1urzA.mjs";
import { n as isResponseError, t as handleRequest } from "./handleRequest-DCLzePtS.mjs";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./hasConfigurableGlobal-Cwrs35Xo.mjs";
import { invariant } from "outvariant";
import { isNodeProcess } from "is-node-process";
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new FetchResponse(FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = isNodeProcess();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
invariant(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
invariant(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
export { XMLHttpRequestInterceptor as t };
//# sourceMappingURL=XMLHttpRequest-BgkpwNOu.mjs.map
{"version":3,"file":"XMLHttpRequest-BgkpwNOu.mjs","names":["handler: ProxyHandler<T>","next","initialRequest: XMLHttpRequest","logger: Logger"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
const require_chunk = require('./chunk-CbDLau6x.cjs');
const require_fetchUtils = require('./fetchUtils-umV5xXBy.cjs');
const require_bufferUtils = require('./bufferUtils-S5_-2eN4.cjs');
const require_getRawRequest = require('./getRawRequest-B6znU6il.cjs');
const require_handleRequest = require('./handleRequest-DVOthWJo.cjs');
const require_hasConfigurableGlobal = require('./hasConfigurableGlobal-BtHi5OlL.cjs');
let outvariant = require("outvariant");
let is_node_process = require("is-node-process");
//#region src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
/**
* Concatenate two `Uint8Array` buffers.
*/
function concatArrayBuffer(left, right) {
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(left, 0);
result.set(right, left.byteLength);
return result;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts
var EventPolyfill = class {
constructor(type, options) {
this.NONE = 0;
this.CAPTURING_PHASE = 1;
this.AT_TARGET = 2;
this.BUBBLING_PHASE = 3;
this.type = "";
this.srcElement = null;
this.currentTarget = null;
this.eventPhase = 0;
this.isTrusted = true;
this.composed = false;
this.cancelable = true;
this.defaultPrevented = false;
this.bubbles = true;
this.lengthComputable = true;
this.loaded = 0;
this.total = 0;
this.cancelBubble = false;
this.returnValue = true;
this.type = type;
this.target = options?.target || null;
this.currentTarget = options?.currentTarget || null;
this.timeStamp = Date.now();
}
composedPath() {
return [];
}
initEvent(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
}
preventDefault() {
this.defaultPrevented = true;
}
stopPropagation() {}
stopImmediatePropagation() {}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts
var ProgressEventPolyfill = class extends EventPolyfill {
constructor(type, init) {
super(type);
this.lengthComputable = init?.lengthComputable || false;
this.composed = init?.composed || false;
this.loaded = init?.loaded || 0;
this.total = init?.total || 0;
}
};
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createEvent.ts
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== "undefined";
function createEvent(target, type, init) {
const progressEvents = [
"error",
"progress",
"loadstart",
"loadend",
"load",
"timeout",
"abort"
];
/**
* `ProgressEvent` is not supported in React Native.
* @see https://github.com/mswjs/interceptors/issues/40
*/
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill;
return progressEvents.includes(type) ? new ProgressEventClass(type, {
lengthComputable: true,
loaded: init?.loaded || 0,
total: init?.total || 0
}) : new EventPolyfill(type, {
target,
currentTarget: target
});
}
//#endregion
//#region src/utils/findPropertySource.ts
/**
* Returns the source object of the given property on the target object
* (the target itself, any parent in its prototype, or null).
*/
function findPropertySource(target, propertyName) {
if (!(propertyName in target)) return null;
if (Object.prototype.hasOwnProperty.call(target, propertyName)) return target;
const prototype = Reflect.getPrototypeOf(target);
return prototype ? findPropertySource(prototype, propertyName) : null;
}
//#endregion
//#region src/utils/createProxy.ts
function createProxy(target, options) {
return new Proxy(target, optionsToProxyHandler(options));
}
function optionsToProxyHandler(options) {
const { constructorCall, methodCall, getProperty, setProperty } = options;
const handler = {};
if (typeof constructorCall !== "undefined") handler.construct = function(target, args, newTarget) {
const next = Reflect.construct.bind(null, target, args, newTarget);
return constructorCall.call(newTarget, args, next);
};
handler.set = function(target, propertyName, nextValue) {
const next = () => {
const propertySource = findPropertySource(target, propertyName) || target;
const ownDescriptors = Reflect.getOwnPropertyDescriptor(propertySource, propertyName);
if (typeof ownDescriptors?.set !== "undefined") {
ownDescriptors.set.apply(target, [nextValue]);
return true;
}
return Reflect.defineProperty(propertySource, propertyName, {
writable: true,
enumerable: true,
configurable: true,
value: nextValue
});
};
if (typeof setProperty !== "undefined") return setProperty.call(target, [propertyName, nextValue], next);
return next();
};
handler.get = function(target, propertyName, receiver) {
/**
* @note Using `Reflect.get()` here causes "TypeError: Illegal invocation".
*/
const next = () => target[propertyName];
const value = typeof getProperty !== "undefined" ? getProperty.call(target, [propertyName, receiver], next) : next();
if (typeof value === "function") return (...args) => {
const next$1 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next$1);
return next$1();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts
function isDomParserSupportedType(type) {
return [
"application/xhtml+xml",
"application/xml",
"image/svg+xml",
"text/html",
"text/xml"
].some((supportedType) => {
return type.startsWith(supportedType);
});
}
//#endregion
//#region src/utils/parseJson.ts
/**
* Parses a given string into JSON.
* Gracefully handles invalid JSON by returning `null`.
*/
function parseJson(data) {
try {
return JSON.parse(data);
} catch (_) {
return null;
}
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/createResponse.ts
/**
* Creates a Fetch API `Response` instance from the given
* `XMLHttpRequest` instance and a response body.
*/
function createResponse(request, body) {
return new require_fetchUtils.FetchResponse(require_fetchUtils.FetchResponse.isResponseWithBody(request.status) ? body : null, {
url: request.responseURL,
status: request.status,
statusText: request.statusText,
headers: createHeadersFromXMLHttpRequestHeaders(request.getAllResponseHeaders())
});
}
function createHeadersFromXMLHttpRequestHeaders(headersString) {
const headers = new Headers();
const lines = headersString.split(/[\r\n]+/);
for (const line of lines) {
if (line.trim() === "") continue;
const [name, ...parts] = line.split(": ");
const value = parts.join(": ");
headers.append(name, value);
}
return headers;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts
/**
* Return a total byte length of the given request/response body.
* If the `Content-Length` header is present, it will be used as the byte length.
*/
async function getBodyByteLength(input) {
const explicitContentLength = input.headers.get("content-length");
if (explicitContentLength != null && explicitContentLength !== "") return Number(explicitContentLength);
return (await input.arrayBuffer()).byteLength;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const IS_NODE = (0, is_node_process.isNodeProcess)();
const kFetchRequest = Symbol("kFetchRequest");
/**
* An `XMLHttpRequest` instance controller that allows us
* to handle any given request instance (e.g. responding to it).
*/
var XMLHttpRequestController = class {
constructor(initialRequest, logger) {
this.initialRequest = initialRequest;
this.logger = logger;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = require_fetchUtils.createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = new Uint8Array();
this.request = createProxy(initialRequest, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "ontimeout": {
const eventName = propertyName.slice(2);
/**
* @note Proxy callbacks to event listeners because JSDOM has trouble
* translating these properties to callbacks. It seemed to be operating
* on events exclusively.
*/
this.request.addEventListener(eventName, nextValue);
return invoke();
}
default: return invoke();
}
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url] = args;
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger = this.logger.extend(`${this.method} ${this.url.href}`);
this.logger.info("open", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.info("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.info("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
this.request.addEventListener("load", () => {
if (typeof this.onResponse !== "undefined") {
const fetchResponse = createResponse(
this.request,
/**
* The `response` property is the right way to read
* the ambiguous response body, as the request's "responseType" may differ.
* @see https://xhr.spec.whatwg.org/#the-response-attribute
*/
this.request.response
);
this.onResponse.call(this, {
response: fetchResponse,
isMockedResponse: this[kIsRequestHandled],
request: fetchRequest,
requestId: this.requestId
});
}
});
const requestBody = typeof body === "string" ? require_bufferUtils.encodeBuffer(body) : body;
const fetchRequest = this.toFetchApiRequest(requestBody);
this[kFetchRequest] = fetchRequest.clone();
/**
* @note Start request handling on the next tick so that the user
* could add event listeners for "loadend" before the interceptor fires it.
*/
queueMicrotask(() => {
(this.onRequest?.call(this, {
request: fetchRequest,
requestId: this.requestId
}) || Promise.resolve()).finally(() => {
if (!this[kIsRequestHandled]) {
this.logger.info("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
/**
* @note Set the intercepted request ID on the original request in Node.js
* so that if it triggers any other interceptors, they don't attempt
* to process it once again.
*
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
* and we don't want for both XHR and ClientRequest interceptors to
* handle the same request at the same time (e.g. emit the "response" event twice).
*/
if (IS_NODE) this.request.setRequestHeader(require_fetchUtils.INTERNAL_REQUEST_ID_HEADER_NAME, this.requestId);
return invoke();
}
});
});
break;
}
default: return invoke();
}
}
});
/**
* Proxy the `.upload` property to gather the event listeners/callbacks.
*/
define(this.request, "upload", createProxy(this.request.upload, {
setProperty: ([propertyName, nextValue], invoke) => {
switch (propertyName) {
case "onloadstart":
case "onprogress":
case "onaboart":
case "onerror":
case "onload":
case "ontimeout":
case "onloadend": {
const eventName = propertyName.slice(2);
this.registerUploadEvent(eventName, nextValue);
}
}
return invoke();
},
methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "addEventListener": {
const [eventName, listener] = args;
this.registerUploadEvent(eventName, listener);
this.logger.info("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.info("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.info("registered upload event \"%s\"", eventName, listener);
}
/**
* Responds to the current request with the given
* Fetch API `Response` instance.
*/
async respondWith(response) {
/**
* @note Since `XMLHttpRequestController` delegates the handling of the responses
* to the "load" event listener that doesn't distinguish between the mocked and original
* responses, mark the request that had a mocked response with a corresponding symbol.
*
* Mark this request as having a mocked response immediately since
* calculating request/response total body length is asynchronous.
*/
this[kIsRequestHandled] = true;
/**
* Dispatch request upload events for requests with a body.
* @see https://github.com/mswjs/interceptors/issues/573
*/
if (this[kFetchRequest]) {
const totalRequestBodyLength = await getBodyByteLength(this[kFetchRequest]);
this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: totalRequestBodyLength
});
this.trigger("progress", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: totalRequestBodyLength,
total: totalRequestBodyLength
});
}
this.logger.info("responding with a mocked response: %d %s", response.status, response.statusText);
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
define(this.request, "responseURL", this.url.href);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.info("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning null");
return null;
}
const headerValue = response.headers.get(args[0]);
this.logger.info("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.info("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.info("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(response.headers.entries()).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.info("resolved all response headers to", allHeaders);
return allHeaders;
} });
Object.defineProperties(this.request, {
response: {
enumerable: true,
configurable: false,
get: () => this.response
},
responseText: {
enumerable: true,
configurable: false,
get: () => this.responseText
},
responseXML: {
enumerable: true,
configurable: false,
get: () => this.responseXML
}
});
const totalResponseBodyLength = await getBodyByteLength(response.clone());
this.logger.info("calculated response body length", totalResponseBodyLength);
this.trigger("loadstart", this.request, {
loaded: 0,
total: totalResponseBodyLength
});
this.setReadyState(this.request.HEADERS_RECEIVED);
this.setReadyState(this.request.LOADING);
const finalizeResponse = () => {
this.logger.info("finalizing the mocked response...");
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
this.trigger("loadend", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
};
if (response.body) {
this.logger.info("mocked response has body, streaming...");
const reader = response.body.getReader();
const readNextResponseBodyChunk = async () => {
const { value, done } = await reader.read();
if (done) {
this.logger.info("response body stream done!");
finalizeResponse();
return;
}
if (value) {
this.logger.info("read response body chunk:", value);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
this.trigger("progress", this.request, {
loaded: this.responseBuffer.byteLength,
total: totalResponseBodyLength
});
}
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
} else finalizeResponse();
}
responseBufferToText() {
return require_bufferUtils.decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.info("getResponse (responseType: %s)", this.request.responseType);
if (this.request.readyState !== this.request.DONE) return null;
switch (this.request.responseType) {
case "json": {
const responseJson = parseJson(this.responseBufferToText());
this.logger.info("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = require_bufferUtils.toArrayBuffer(this.responseBuffer);
this.logger.info("resolved response ArrayBuffer", arrayBuffer);
return arrayBuffer;
}
case "blob": {
const mimeType = this.request.getResponseHeader("Content-Type") || "text/plain";
const responseBlob = new Blob([this.responseBufferToText()], { type: mimeType });
this.logger.info("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.info("resolving \"%s\" response type as text", this.request.responseType, responseText);
return responseText;
}
}
}
get responseText() {
/**
* Throw when trying to read the response body as text when the
* "responseType" doesn't expect text. This just respects the spec better.
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
*/
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "text", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.LOADING && this.request.readyState !== this.request.DONE) return "";
const responseText = this.responseBufferToText();
this.logger.info("getResponseText: \"%s\"", responseText);
return responseText;
}
get responseXML() {
(0, outvariant.invariant)(this.request.responseType === "" || this.request.responseType === "document", "InvalidStateError: The object is in invalid state.");
if (this.request.readyState !== this.request.DONE) return null;
const contentType = this.request.getResponseHeader("Content-Type") || "";
if (typeof DOMParser === "undefined") {
console.warn("Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.");
return null;
}
if (isDomParserSupportedType(contentType)) return new DOMParser().parseFromString(this.responseBufferToText(), contentType);
return null;
}
errorWith(error) {
/**
* @note Mark this request as handled even if it received a mock error.
* This prevents the controller from trying to perform this request as-is.
*/
this[kIsRequestHandled] = true;
this.logger.info("responding with an error");
this.setReadyState(this.request.DONE);
this.trigger("error", this.request);
this.trigger("loadend", this.request);
}
/**
* Transitions this request's `readyState` to the given one.
*/
setReadyState(nextReadyState) {
this.logger.info("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.info("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.info("set readyState to: %d", nextReadyState);
if (nextReadyState !== this.request.UNSENT) {
this.logger.info("triggering \"readystatechange\" event...");
this.trigger("readystatechange", this.request);
}
}
/**
* Triggers given event on the `XMLHttpRequest` instance.
*/
trigger(eventName, target, options) {
const callback = target[`on${eventName}`];
const event = createEvent(target, eventName, options);
this.logger.info("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.info("found a direct \"%s\" callback, calling...", eventName);
callback.call(target, event);
}
const events = target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events;
for (const [registeredEventName, listeners] of events) if (registeredEventName === eventName) {
this.logger.info("found %d listener(s) for \"%s\" event, calling...", listeners.length, eventName);
listeners.forEach((listener) => listener.call(target, event));
}
}
/**
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
*/
toFetchApiRequest(body) {
this.logger.info("converting request to a Fetch API Request...");
const resolvedBody = body instanceof Document ? body.documentElement.innerText : body;
const fetchRequest = new require_fetchUtils.FetchRequest(this.url.href, {
method: this.method,
headers: this.requestHeaders,
credentials: this.request.withCredentials ? "include" : "same-origin",
body: ["GET", "HEAD"].includes(this.method.toUpperCase()) ? null : resolvedBody
});
define(fetchRequest, "headers", createProxy(fetchRequest.headers, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "append":
case "set": {
const [headerName, headerValue] = args;
this.request.setRequestHeader(headerName, headerValue);
break;
}
case "delete": {
const [headerName] = args;
console.warn(`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`);
break;
}
}
return invoke();
} }));
require_getRawRequest.setRawRequest(fetchRequest, this.request);
this.logger.info("converted request to a Fetch API Request!", fetchRequest);
return fetchRequest;
}
};
function toAbsoluteUrl(url) {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === "undefined") return new URL(url);
return new URL(url.toString(), location.href);
}
function define(target, property, value) {
Reflect.defineProperty(target, property, {
writable: true,
enumerable: true,
value
});
}
//#endregion
//#region src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts
/**
* Create a proxied `XMLHttpRequest` class.
* The proxied class establishes spies on certain methods,
* allowing us to intercept requests and respond to them.
*/
function createXMLHttpRequestProxy({ emitter, logger }) {
return new Proxy(globalThis.XMLHttpRequest, { construct(target, args, newTarget) {
logger.info("constructed new XMLHttpRequest");
const originalRequest = Reflect.construct(target, args, newTarget);
/**
* @note Forward prototype descriptors onto the proxied object.
* XMLHttpRequest is implemented in JSDOM in a way that assigns
* a bunch of descriptors, like "set responseType()" on the prototype.
* With this propagation, we make sure that those descriptors trigger
* when the user operates with the proxied request instance.
*/
const prototypeDescriptors = Object.getOwnPropertyDescriptors(target.prototype);
for (const propertyName in prototypeDescriptors) Reflect.defineProperty(originalRequest, propertyName, prototypeDescriptors[propertyName]);
const xhrRequestController = new XMLHttpRequestController(originalRequest, logger);
xhrRequestController.onRequest = async function({ request, requestId }) {
const controller = new require_fetchUtils.RequestController(request, {
passthrough: () => {
this.logger.info("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
if (require_handleRequest.isResponseError(response)) {
this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
return;
}
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.info("request errored!", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
});
this.logger.info("awaiting mocked response...");
this.logger.info("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await require_handleRequest.handleRequest({
request,
requestId,
controller,
emitter
});
};
xhrRequestController.onResponse = async function({ response, isMockedResponse, request, requestId }) {
this.logger.info("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
emitter.emit("response", {
response,
isMockedResponse,
request,
requestId
});
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/index.ts
var XMLHttpRequestInterceptor = class XMLHttpRequestInterceptor extends require_fetchUtils.Interceptor {
static {
this.interceptorSymbol = Symbol("xhr");
}
constructor() {
super(XMLHttpRequestInterceptor.interceptorSymbol);
}
checkEnvironment() {
return require_hasConfigurableGlobal.hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
const logger = this.logger.extend("setup");
logger.info("patching global XMLHttpRequest...");
this.subscriptions.push(require_hasConfigurableGlobal.patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger: this.logger
});
}));
logger.info("global XMLHttpRequest patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
Object.defineProperty(exports, 'XMLHttpRequestInterceptor', {
enumerable: true,
get: function () {
return XMLHttpRequestInterceptor;
}
});
//# sourceMappingURL=XMLHttpRequest-CUbLHiq6.cjs.map
{"version":3,"file":"XMLHttpRequest-CUbLHiq6.cjs","names":["handler: ProxyHandler<T>","next","FetchResponse","initialRequest: XMLHttpRequest","logger: Logger","createRequestId","encodeBuffer","INTERNAL_REQUEST_ID_HEADER_NAME","decodeBuffer","toArrayBuffer","FetchRequest","RequestController","isResponseError","handleRequest","Interceptor","hasConfigurableGlobal","patchesRegistry"],"sources":["../../src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts","../../src/interceptors/XMLHttpRequest/utils/createEvent.ts","../../src/utils/findPropertySource.ts","../../src/utils/createProxy.ts","../../src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts","../../src/utils/parseJson.ts","../../src/interceptors/XMLHttpRequest/utils/createResponse.ts","../../src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts","../../src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts","../../src/interceptors/XMLHttpRequest/index.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n): Uint8Array {\n const result = new Uint8Array(left.byteLength + right.byteLength)\n result.set(left, 0)\n result.set(right, left.byteLength)\n return result\n}\n","export class EventPolyfill implements Event {\n readonly NONE = 0\n readonly CAPTURING_PHASE = 1\n readonly AT_TARGET = 2\n readonly BUBBLING_PHASE = 3\n\n public type: string = ''\n public srcElement: EventTarget | null = null\n public target: EventTarget | null\n public currentTarget: EventTarget | null = null\n public eventPhase: number = 0\n public timeStamp: number\n public isTrusted: boolean = true\n public composed: boolean = false\n public cancelable: boolean = true\n public defaultPrevented: boolean = false\n public bubbles: boolean = true\n public lengthComputable: boolean = true\n public loaded: number = 0\n public total: number = 0\n\n cancelBubble: boolean = false\n returnValue: boolean = true\n\n constructor(\n type: string,\n options?: { target: EventTarget; currentTarget: EventTarget }\n ) {\n this.type = type\n this.target = options?.target || null\n this.currentTarget = options?.currentTarget || null\n this.timeStamp = Date.now()\n }\n\n public composedPath(): EventTarget[] {\n return []\n }\n\n public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {\n this.type = type\n this.bubbles = !!bubbles\n this.cancelable = !!cancelable\n }\n\n public preventDefault() {\n this.defaultPrevented = true\n }\n\n public stopPropagation() {}\n public stopImmediatePropagation() {}\n}\n","import { EventPolyfill } from './EventPolyfill'\n\nexport class ProgressEventPolyfill extends EventPolyfill {\n readonly lengthComputable: boolean\n readonly composed: boolean\n readonly loaded: number\n readonly total: number\n\n constructor(type: string, init?: ProgressEventInit) {\n super(type)\n\n this.lengthComputable = init?.lengthComputable || false\n this.composed = init?.composed || false\n this.loaded = init?.loaded || 0\n this.total = init?.total || 0\n }\n}\n","import { EventPolyfill } from '../polyfills/EventPolyfill'\nimport { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'\n\nconst SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'\n\nexport function createEvent(\n target: XMLHttpRequest | XMLHttpRequestUpload,\n type: string,\n init?: ProgressEventInit\n): EventPolyfill | ProgressEvent {\n const progressEvents = [\n 'error',\n 'progress',\n 'loadstart',\n 'loadend',\n 'load',\n 'timeout',\n 'abort',\n ]\n\n /**\n * `ProgressEvent` is not supported in React Native.\n * @see https://github.com/mswjs/interceptors/issues/40\n */\n const ProgressEventClass = SUPPORTS_PROGRESS_EVENT\n ? ProgressEvent\n : ProgressEventPolyfill\n\n const event = progressEvents.includes(type)\n ? new ProgressEventClass(type, {\n lengthComputable: true,\n loaded: init?.loaded || 0,\n total: init?.total || 0,\n })\n : new EventPolyfill(type, {\n target,\n currentTarget: target,\n })\n\n return event\n}\n","/**\n * Returns the source object of the given property on the target object\n * (the target itself, any parent in its prototype, or null).\n */\nexport function findPropertySource(\n target: object,\n propertyName: string | symbol\n): object | null {\n if (!(propertyName in target)) {\n return null\n }\n\n const hasProperty = Object.prototype.hasOwnProperty.call(target, propertyName)\n if (hasProperty) {\n return target\n }\n\n const prototype = Reflect.getPrototypeOf(target)\n return prototype ? findPropertySource(prototype, propertyName) : null\n}\n","import { findPropertySource } from './findPropertySource'\n\nexport interface ProxyOptions<Target extends Record<string, any>> {\n constructorCall?(args: Array<unknown>, next: NextFunction<Target>): Target\n\n methodCall?<F extends keyof Target>(\n this: Target,\n data: [methodName: F, args: Array<unknown>],\n next: NextFunction<void>\n ): void\n\n setProperty?(\n data: [propertyName: string | symbol, nextValue: unknown],\n next: NextFunction<boolean>\n ): boolean\n\n getProperty?(\n data: [propertyName: string | symbol, receiver: Target],\n next: NextFunction<void>\n ): void\n}\n\nexport type NextFunction<ReturnType> = () => ReturnType\n\nexport function createProxy<Target extends object>(\n target: Target,\n options: ProxyOptions<Target>\n): Target {\n const proxy = new Proxy(target, optionsToProxyHandler(options))\n\n return proxy\n}\n\nfunction optionsToProxyHandler<T extends Record<string, any>>(\n options: ProxyOptions<T>\n): ProxyHandler<T> {\n const { constructorCall, methodCall, getProperty, setProperty } = options\n const handler: ProxyHandler<T> = {}\n\n if (typeof constructorCall !== 'undefined') {\n handler.construct = function (target, args, newTarget) {\n const next = Reflect.construct.bind(null, target as any, args, newTarget)\n return constructorCall.call(newTarget, args, next)\n }\n }\n\n handler.set = function (target, propertyName, nextValue) {\n const next = () => {\n const propertySource = findPropertySource(target, propertyName) || target\n const ownDescriptors = Reflect.getOwnPropertyDescriptor(\n propertySource,\n propertyName\n )\n\n // Respect any custom setters present for this property.\n if (typeof ownDescriptors?.set !== 'undefined') {\n ownDescriptors.set.apply(target, [nextValue])\n return true\n }\n\n // Otherwise, set the property on the source.\n return Reflect.defineProperty(propertySource, propertyName, {\n writable: true,\n enumerable: true,\n configurable: true,\n value: nextValue,\n })\n }\n\n if (typeof setProperty !== 'undefined') {\n return setProperty.call(target, [propertyName, nextValue], next)\n }\n\n return next()\n }\n\n handler.get = function (target, propertyName, receiver) {\n /**\n * @note Using `Reflect.get()` here causes \"TypeError: Illegal invocation\".\n */\n const next = () => target[propertyName as any]\n\n const value =\n typeof getProperty !== 'undefined'\n ? getProperty.call(target, [propertyName, receiver], next)\n : next()\n\n if (typeof value === 'function') {\n return (...args: Array<any>) => {\n const next = value.bind(target, ...args)\n\n if (typeof methodCall !== 'undefined') {\n return methodCall.call(target, [propertyName as any, args], next)\n }\n\n return next()\n }\n }\n\n return value\n }\n\n return handler\n}\n","export function isDomParserSupportedType(\n type: string\n): type is DOMParserSupportedType {\n const supportedTypes: Array<DOMParserSupportedType> = [\n 'application/xhtml+xml',\n 'application/xml',\n 'image/svg+xml',\n 'text/html',\n 'text/xml',\n ]\n return supportedTypes.some((supportedType) => {\n return type.startsWith(supportedType)\n })\n}\n","/**\n * Parses a given string into JSON.\n * Gracefully handles invalid JSON by returning `null`.\n */\nexport function parseJson(data: string): Record<string, unknown> | null {\n try {\n const json = JSON.parse(data)\n return json\n } catch (_) {\n return null\n }\n}\n","import { FetchResponse } from '../../../utils/fetchUtils'\n\n/**\n * Creates a Fetch API `Response` instance from the given\n * `XMLHttpRequest` instance and a response body.\n */\nexport function createResponse(\n request: XMLHttpRequest,\n body: BodyInit | null\n): Response {\n /**\n * Handle XMLHttpRequest responses that must have null as the\n * response body when represented using Fetch API Response.\n * XMLHttpRequest response will always have an empty string\n * as the \"request.response\" in those cases, resulting in an error\n * when constructing a Response instance.\n * @see https://github.com/mswjs/interceptors/issues/379\n */\n const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)\n ? body\n : null\n\n return new FetchResponse(responseBodyOrNull, {\n url: request.responseURL,\n status: request.status,\n statusText: request.statusText,\n headers: createHeadersFromXMLHttpRequestHeaders(\n request.getAllResponseHeaders()\n ),\n })\n}\n\nfunction createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {\n const headers = new Headers()\n\n const lines = headersString.split(/[\\r\\n]+/)\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n const [name, ...parts] = line.split(': ')\n const value = parts.join(': ')\n\n headers.append(name, value)\n }\n\n return headers\n}\n","/**\n * Return a total byte length of the given request/response body.\n * If the `Content-Length` header is present, it will be used as the byte length.\n */\nexport async function getBodyByteLength(\n input: Request | Response\n): Promise<number> {\n const explicitContentLength = input.headers.get('content-length')\n\n if (explicitContentLength != null && explicitContentLength !== '') {\n return Number(explicitContentLength)\n }\n\n const buffer = await input.arrayBuffer()\n return buffer.byteLength\n}\n","import { invariant } from 'outvariant'\nimport { isNodeProcess } from 'is-node-process'\nimport type { Logger } from '@open-draft/logger'\nimport { concatArrayBuffer } from './utils/concatArrayBuffer'\nimport { createEvent } from './utils/createEvent'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/bufferUtils'\nimport { createProxy } from '../../utils/createProxy'\nimport { isDomParserSupportedType } from './utils/isDomParserSupportedType'\nimport { parseJson } from '../../utils/parseJson'\nimport { createResponse } from './utils/createResponse'\nimport { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'\nimport { createRequestId } from '../../createRequestId'\nimport { getBodyByteLength } from './utils/getBodyByteLength'\nimport { setRawRequest } from '../../getRawRequest'\nimport { FetchRequest } from '../../utils/fetchUtils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst IS_NODE = isNodeProcess()\nconst kFetchRequest = Symbol('kFetchRequest')\n\n/**\n * An `XMLHttpRequest` instance controller that allows us\n * to handle any given request instance (e.g. responding to it).\n */\nexport class XMLHttpRequestController {\n public request: XMLHttpRequest\n public requestId: string\n public onRequest?: (\n this: XMLHttpRequestController,\n args: {\n request: Request\n requestId: string\n }\n ) => Promise<void>\n public onResponse?: (\n this: XMLHttpRequestController,\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array\n private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>\n private uploadEvents: Map<\n keyof XMLHttpRequestEventTargetEventMap,\n Array<Function>\n >\n\n constructor(\n readonly initialRequest: XMLHttpRequest,\n public logger: Logger\n ) {\n this[kIsRequestHandled] = false\n\n this.events = new Map()\n this.uploadEvents = new Map()\n this.requestId = createRequestId()\n this.requestHeaders = new Headers()\n this.responseBuffer = new Uint8Array()\n\n this.request = createProxy(initialRequest, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'ontimeout': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n /**\n * @note Proxy callbacks to event listeners because JSDOM has trouble\n * translating these properties to callbacks. It seemed to be operating\n * on events exclusively.\n */\n this.request.addEventListener(eventName, nextValue as any)\n\n return invoke()\n }\n\n default: {\n return invoke()\n }\n }\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url] = args as [string, string | undefined]\n\n if (typeof url === 'undefined') {\n this.method = 'GET'\n this.url = toAbsoluteUrl(method)\n } else {\n this.method = method\n this.url = toAbsoluteUrl(url)\n }\n\n this.logger = this.logger.extend(`${this.method} ${this.url.href}`)\n this.logger.info('open', this.method, this.url.href)\n\n return invoke()\n }\n\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n\n this.registerEvent(eventName, listener)\n this.logger.info('addEventListener', eventName, listener)\n\n return invoke()\n }\n\n case 'setRequestHeader': {\n const [name, value] = args as [string, string]\n this.requestHeaders.set(name, value)\n\n this.logger.info('setRequestHeader', name, value)\n\n return invoke()\n }\n\n case 'send': {\n const [body] = args as [\n body?: XMLHttpRequestBodyInit | Document | null,\n ]\n\n this.request.addEventListener('load', () => {\n if (typeof this.onResponse !== 'undefined') {\n // Create a Fetch API Response representation of whichever\n // response this XMLHttpRequest received. Note those may\n // be either a mocked and the original response.\n const fetchResponse = createResponse(\n this.request,\n /**\n * The `response` property is the right way to read\n * the ambiguous response body, as the request's \"responseType\" may differ.\n * @see https://xhr.spec.whatwg.org/#the-response-attribute\n */\n this.request.response\n )\n\n // Notify the consumer about the response.\n this.onResponse.call(this, {\n response: fetchResponse,\n isMockedResponse: this[kIsRequestHandled],\n request: fetchRequest,\n requestId: this.requestId!,\n })\n }\n })\n\n const requestBody =\n typeof body === 'string' ? encodeBuffer(body) : body\n\n // Delegate request handling to the consumer.\n const fetchRequest = this.toFetchApiRequest(requestBody)\n this[kFetchRequest] = fetchRequest.clone()\n\n /**\n * @note Start request handling on the next tick so that the user\n * could add event listeners for \"loadend\" before the interceptor fires it.\n */\n queueMicrotask(() => {\n const onceRequestSettled =\n this.onRequest?.call(this, {\n request: fetchRequest,\n requestId: this.requestId!,\n }) || Promise.resolve()\n\n onceRequestSettled.finally(() => {\n // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.\n if (!this[kIsRequestHandled]) {\n this.logger.info(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\n )\n\n /**\n * @note Set the intercepted request ID on the original request in Node.js\n * so that if it triggers any other interceptors, they don't attempt\n * to process it once again.\n *\n * For instance, XMLHttpRequest is often implemented via \"http.ClientRequest\"\n * and we don't want for both XHR and ClientRequest interceptors to\n * handle the same request at the same time (e.g. emit the \"response\" event twice).\n */\n if (IS_NODE) {\n this.request.setRequestHeader(\n INTERNAL_REQUEST_ID_HEADER_NAME,\n this.requestId!\n )\n }\n\n return invoke()\n }\n })\n })\n\n break\n }\n\n default: {\n return invoke()\n }\n }\n },\n })\n\n /**\n * Proxy the `.upload` property to gather the event listeners/callbacks.\n */\n define(\n this.request,\n 'upload',\n createProxy(this.request.upload, {\n setProperty: ([propertyName, nextValue], invoke) => {\n switch (propertyName) {\n case 'onloadstart':\n case 'onprogress':\n case 'onaboart':\n case 'onerror':\n case 'onload':\n case 'ontimeout':\n case 'onloadend': {\n const eventName = propertyName.slice(\n 2\n ) as keyof XMLHttpRequestEventTargetEventMap\n\n this.registerUploadEvent(eventName, nextValue as Function)\n }\n }\n\n return invoke()\n },\n methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'addEventListener': {\n const [eventName, listener] = args as [\n keyof XMLHttpRequestEventTargetEventMap,\n Function,\n ]\n this.registerUploadEvent(eventName, listener)\n this.logger.info('upload.addEventListener', eventName, listener)\n\n return invoke()\n }\n }\n },\n })\n )\n }\n\n private registerEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.events.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.events.set(eventName, nextEvents)\n\n this.logger.info('registered event \"%s\"', eventName, listener)\n }\n\n private registerUploadEvent(\n eventName: keyof XMLHttpRequestEventTargetEventMap,\n listener: Function\n ): void {\n const prevEvents = this.uploadEvents.get(eventName) || []\n const nextEvents = prevEvents.concat(listener)\n this.uploadEvents.set(eventName, nextEvents)\n\n this.logger.info('registered upload event \"%s\"', eventName, listener)\n }\n\n /**\n * Responds to the current request with the given\n * Fetch API `Response` instance.\n */\n public async respondWith(response: Response): Promise<void> {\n /**\n * @note Since `XMLHttpRequestController` delegates the handling of the responses\n * to the \"load\" event listener that doesn't distinguish between the mocked and original\n * responses, mark the request that had a mocked response with a corresponding symbol.\n *\n * Mark this request as having a mocked response immediately since\n * calculating request/response total body length is asynchronous.\n */\n this[kIsRequestHandled] = true\n\n /**\n * Dispatch request upload events for requests with a body.\n * @see https://github.com/mswjs/interceptors/issues/573\n */\n if (this[kFetchRequest]) {\n const totalRequestBodyLength = await getBodyByteLength(\n this[kFetchRequest]\n )\n\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: totalRequestBodyLength,\n })\n this.trigger('progress', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n\n this.trigger('loadend', this.request.upload, {\n loaded: totalRequestBodyLength,\n total: totalRequestBodyLength,\n })\n }\n\n this.logger.info(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n define(this.request, 'responseURL', this.url.href)\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.info('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning null')\n\n // Headers not received yet, nothing to return.\n return null\n }\n\n const headerValue = response.headers.get(args[0])\n this.logger.info(\n 'resolved response header \"%s\" to',\n args[0],\n headerValue\n )\n\n return headerValue\n },\n })\n\n this.request.getAllResponseHeaders = new Proxy(\n this.request.getAllResponseHeaders,\n {\n apply: () => {\n this.logger.info('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.info('headers not received yet, returning empty string')\n\n // Headers not received yet, nothing to return.\n return ''\n }\n\n const headersList = Array.from(response.headers.entries())\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.info('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n\n // Update the response getters to resolve against the mocked response.\n Object.defineProperties(this.request, {\n response: {\n enumerable: true,\n configurable: false,\n get: () => this.response,\n },\n responseText: {\n enumerable: true,\n configurable: false,\n get: () => this.responseText,\n },\n responseXML: {\n enumerable: true,\n configurable: false,\n get: () => this.responseXML,\n },\n })\n\n const totalResponseBodyLength = await getBodyByteLength(response.clone())\n\n this.logger.info('calculated response body length', totalResponseBodyLength)\n\n this.trigger('loadstart', this.request, {\n loaded: 0,\n total: totalResponseBodyLength,\n })\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n this.setReadyState(this.request.LOADING)\n\n const finalizeResponse = () => {\n this.logger.info('finalizing the mocked response...')\n\n this.setReadyState(this.request.DONE)\n\n this.trigger('load', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n\n this.trigger('loadend', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n if (response.body) {\n this.logger.info('mocked response has body, streaming...')\n\n const reader = response.body.getReader()\n\n const readNextResponseBodyChunk = async () => {\n const { value, done } = await reader.read()\n\n if (done) {\n this.logger.info('response body stream done!')\n finalizeResponse()\n return\n }\n\n if (value) {\n this.logger.info('read response body chunk:', value)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n\n this.trigger('progress', this.request, {\n loaded: this.responseBuffer.byteLength,\n total: totalResponseBodyLength,\n })\n }\n\n readNextResponseBodyChunk()\n }\n\n readNextResponseBodyChunk()\n } else {\n finalizeResponse()\n }\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.info(\n 'getResponse (responseType: %s)',\n this.request.responseType\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n switch (this.request.responseType) {\n case 'json': {\n const responseJson = parseJson(this.responseBufferToText())\n this.logger.info('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.info('resolved response ArrayBuffer', arrayBuffer)\n\n return arrayBuffer\n }\n\n case 'blob': {\n const mimeType =\n this.request.getResponseHeader('Content-Type') || 'text/plain'\n const responseBlob = new Blob([this.responseBufferToText()], {\n type: mimeType,\n })\n\n this.logger.info(\n 'resolved response Blob (mime type: %s)',\n responseBlob,\n mimeType\n )\n\n return responseBlob\n }\n\n default: {\n const responseText = this.responseBufferToText()\n this.logger.info(\n 'resolving \"%s\" response type as text',\n this.request.responseType,\n responseText\n )\n\n return responseText\n }\n }\n }\n\n get responseText(): string {\n /**\n * Throw when trying to read the response body as text when the\n * \"responseType\" doesn't expect text. This just respects the spec better.\n * @see https://xhr.spec.whatwg.org/#the-responsetext-attribute\n */\n invariant(\n this.request.responseType === '' || this.request.responseType === 'text',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (\n this.request.readyState !== this.request.LOADING &&\n this.request.readyState !== this.request.DONE\n ) {\n return ''\n }\n\n const responseText = this.responseBufferToText()\n this.logger.info('getResponseText: \"%s\"', responseText)\n\n return responseText\n }\n\n get responseXML(): Document | null {\n invariant(\n this.request.responseType === '' ||\n this.request.responseType === 'document',\n 'InvalidStateError: The object is in invalid state.'\n )\n\n if (this.request.readyState !== this.request.DONE) {\n return null\n }\n\n const contentType = this.request.getResponseHeader('Content-Type') || ''\n\n if (typeof DOMParser === 'undefined') {\n console.warn(\n 'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'\n )\n return null\n }\n\n if (isDomParserSupportedType(contentType)) {\n return new DOMParser().parseFromString(\n this.responseBufferToText(),\n contentType\n )\n }\n\n return null\n }\n\n public errorWith(error?: Error): void {\n /**\n * @note Mark this request as handled even if it received a mock error.\n * This prevents the controller from trying to perform this request as-is.\n */\n this[kIsRequestHandled] = true\n this.logger.info('responding with an error')\n\n this.setReadyState(this.request.DONE)\n this.trigger('error', this.request)\n this.trigger('loadend', this.request)\n }\n\n /**\n * Transitions this request's `readyState` to the given one.\n */\n private setReadyState(nextReadyState: number): void {\n this.logger.info(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.info('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.info('set readyState to: %d', nextReadyState)\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.info('triggering \"readystatechange\" event...')\n\n this.trigger('readystatechange', this.request)\n }\n }\n\n /**\n * Triggers given event on the `XMLHttpRequest` instance.\n */\n private trigger<\n EventName extends keyof (XMLHttpRequestEventTargetEventMap & {\n readystatechange: ProgressEvent<XMLHttpRequestEventTarget>\n }),\n >(\n eventName: EventName,\n target: XMLHttpRequest | XMLHttpRequestUpload,\n options?: ProgressEventInit\n ): void {\n const callback = (target as XMLHttpRequest)[`on${eventName}`]\n const event = createEvent(target, eventName, options)\n\n this.logger.info('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.info('found a direct \"%s\" callback, calling...', eventName)\n callback.call(target as XMLHttpRequest, event)\n }\n\n // Invoke event listeners.\n const events =\n target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events\n\n for (const [registeredEventName, listeners] of events) {\n if (registeredEventName === eventName) {\n this.logger.info(\n 'found %d listener(s) for \"%s\" event, calling...',\n listeners.length,\n eventName\n )\n\n listeners.forEach((listener) => listener.call(target, event))\n }\n }\n }\n\n /**\n * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.\n */\n private toFetchApiRequest(\n body: XMLHttpRequestBodyInit | Document | null | undefined\n ): Request {\n this.logger.info('converting request to a Fetch API Request...')\n\n // If the `Document` is used as the body of this XMLHttpRequest,\n // set its inner text as the Fetch API Request body.\n const resolvedBody =\n body instanceof Document ? body.documentElement.innerText : body\n\n const fetchRequest = new FetchRequest(this.url.href, {\n method: this.method,\n headers: this.requestHeaders,\n /**\n * @see https://xhr.spec.whatwg.org/#cross-origin-credentials\n */\n credentials: this.request.withCredentials ? 'include' : 'same-origin',\n body: ['GET', 'HEAD'].includes(this.method.toUpperCase())\n ? null\n : resolvedBody,\n })\n\n const proxyHeaders = createProxy(fetchRequest.headers, {\n methodCall: ([methodName, args], invoke) => {\n // Forward the latest state of the internal request headers\n // because the interceptor might have modified them\n // without responding to the request.\n switch (methodName) {\n case 'append':\n case 'set': {\n const [headerName, headerValue] = args as [string, string]\n this.request.setRequestHeader(headerName, headerValue)\n break\n }\n\n case 'delete': {\n const [headerName] = args as [string]\n console.warn(\n `XMLHttpRequest: Cannot remove a \"${headerName}\" header from the Fetch API representation of the \"${fetchRequest.method} ${fetchRequest.url}\" request. XMLHttpRequest headers cannot be removed.`\n )\n break\n }\n }\n\n return invoke()\n },\n })\n define(fetchRequest, 'headers', proxyHeaders)\n setRawRequest(fetchRequest, this.request)\n\n this.logger.info('converted request to a Fetch API Request!', fetchRequest)\n\n return fetchRequest\n }\n}\n\nfunction toAbsoluteUrl(url: string | URL): URL {\n /**\n * @note XMLHttpRequest interceptor may run in environments\n * that implement XMLHttpRequest but don't implement \"location\"\n * (for example, React Native). If that's the case, return the\n * input URL as-is (nothing to be relative to).\n * @see https://github.com/mswjs/msw/issues/1777\n */\n if (typeof location === 'undefined') {\n return new URL(url)\n }\n\n return new URL(url.toString(), location.href)\n}\n\nfunction define(\n target: object,\n property: string | symbol,\n value: unknown\n): void {\n Reflect.defineProperty(target, property, {\n // Ensure writable properties to allow redefining readonly properties.\n writable: true,\n enumerable: true,\n value,\n })\n}\n","import type { Logger } from '@open-draft/logger'\nimport { XMLHttpRequestEmitter } from '.'\nimport { RequestController } from '../../RequestController'\nimport { XMLHttpRequestController } from './XMLHttpRequestController'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { isResponseError } from '../../utils/responseUtils'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: XMLHttpRequestEmitter\n logger: Logger\n}\n\n/**\n * Create a proxied `XMLHttpRequest` class.\n * The proxied class establishes spies on certain methods,\n * allowing us to intercept requests and respond to them.\n */\nexport function createXMLHttpRequestProxy({\n emitter,\n logger,\n}: XMLHttpRequestProxyOptions) {\n const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {\n construct(target, args, newTarget) {\n logger.info('constructed new XMLHttpRequest')\n\n const originalRequest = Reflect.construct(\n target,\n args,\n newTarget\n ) as XMLHttpRequest\n\n /**\n * @note Forward prototype descriptors onto the proxied object.\n * XMLHttpRequest is implemented in JSDOM in a way that assigns\n * a bunch of descriptors, like \"set responseType()\" on the prototype.\n * With this propagation, we make sure that those descriptors trigger\n * when the user operates with the proxied request instance.\n */\n const prototypeDescriptors = Object.getOwnPropertyDescriptors(\n target.prototype\n )\n for (const propertyName in prototypeDescriptors) {\n Reflect.defineProperty(\n originalRequest,\n propertyName,\n prototypeDescriptors[propertyName]\n )\n }\n\n const xhrRequestController = new XMLHttpRequestController(\n originalRequest,\n logger\n )\n\n xhrRequestController.onRequest = async function ({ request, requestId }) {\n const controller = new RequestController(request, {\n passthrough: () => {\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n if (isResponseError(response)) {\n this.errorWith(new TypeError('Network error'))\n return\n }\n\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.info('request errored!', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n })\n\n this.logger.info('awaiting mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n request,\n requestId,\n controller,\n emitter,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n isMockedResponse,\n request,\n requestId,\n }) {\n this.logger.info(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n emitter.emit('response', {\n response,\n isMockedResponse,\n request,\n requestId,\n })\n }\n\n // Return the proxied request from the controller\n // so that the controller can react to the consumer's interactions\n // with this request (opening/sending/etc).\n return xhrRequestController.request\n },\n })\n\n return XMLHttpRequestProxy\n}\n","import { Emitter } from 'strict-event-emitter'\nimport { HttpRequestEventMap } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { patchesRegistry } from '../../utils/patchesRegistry'\n\nexport type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static interceptorSymbol = Symbol('xhr')\n\n constructor() {\n super(XMLHttpRequestInterceptor.interceptorSymbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n const logger = this.logger.extend('setup')\n\n logger.info('patching global XMLHttpRequest...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger: this.logger,\n })\n })\n )\n\n logger.info(\n 'global XMLHttpRequest patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}