🎩 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.42.0
to
0.42.1
+720
lib/browser/create-request-id-Bk5YX1AM.js
import { Emitter } from "rettime";
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
//#region src/disposable.ts
var Disposable = class {
constructor() {
this.subscriptions = [];
}
dispose() {
let subscription;
while (subscription = this.subscriptions.pop()) subscription();
}
};
//#endregion
//#region node_modules/.pnpm/ms@2.1.3/node_modules/ms/index.js
var require_ms = /* @__PURE__ */ __commonJSMin(((exports, module) => {
/**
* Helpers.
*/
var s = 1e3;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var w = d * 7;
var y = d * 365.25;
/**
* Parse or format the given `val`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} [options]
* @throws {Error} throw an error if val is not a non-empty string or a number
* @return {String|Number}
* @api public
*/
module.exports = function(val, options) {
options = options || {};
var type = typeof val;
if (type === "string" && val.length > 0) return parse(val);
else if (type === "number" && isFinite(val)) return options.long ? fmtLong(val) : fmtShort(val);
throw new Error("val is not a non-empty string or a valid number. val=" + JSON.stringify(val));
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
str = String(str);
if (str.length > 100) return;
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str);
if (!match) return;
var n = parseFloat(match[1]);
switch ((match[2] || "ms").toLowerCase()) {
case "years":
case "year":
case "yrs":
case "yr":
case "y": return n * y;
case "weeks":
case "week":
case "w": return n * w;
case "days":
case "day":
case "d": return n * d;
case "hours":
case "hour":
case "hrs":
case "hr":
case "h": return n * h;
case "minutes":
case "minute":
case "mins":
case "min":
case "m": return n * m;
case "seconds":
case "second":
case "secs":
case "sec":
case "s": return n * s;
case "milliseconds":
case "millisecond":
case "msecs":
case "msec":
case "ms": return n;
default: return;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtShort(ms) {
var msAbs = Math.abs(ms);
if (msAbs >= d) return Math.round(ms / d) + "d";
if (msAbs >= h) return Math.round(ms / h) + "h";
if (msAbs >= m) return Math.round(ms / m) + "m";
if (msAbs >= s) return Math.round(ms / s) + "s";
return ms + "ms";
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtLong(ms) {
var msAbs = Math.abs(ms);
if (msAbs >= d) return plural(ms, msAbs, d, "day");
if (msAbs >= h) return plural(ms, msAbs, h, "hour");
if (msAbs >= m) return plural(ms, msAbs, m, "minute");
if (msAbs >= s) return plural(ms, msAbs, s, "second");
return ms + " ms";
}
/**
* Pluralization helper.
*/
function plural(ms, msAbs, n, name) {
var isPlural = msAbs >= n * 1.5;
return Math.round(ms / n) + " " + name + (isPlural ? "s" : "");
}
}));
//#endregion
//#region node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/common.js
var require_common = /* @__PURE__ */ __commonJSMin(((exports, module) => {
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*/
function setup(env) {
createDebug.debug = createDebug;
createDebug.default = createDebug;
createDebug.coerce = coerce;
createDebug.disable = disable;
createDebug.enable = enable;
createDebug.enabled = enabled;
createDebug.humanize = require_ms();
createDebug.destroy = destroy;
Object.keys(env).forEach((key) => {
createDebug[key] = env[key];
});
/**
* The currently active debug mode names, and names to skip.
*/
createDebug.names = [];
createDebug.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
*/
createDebug.formatters = {};
/**
* Selects a color for a debug namespace
* @param {String} namespace The namespace string for the debug instance to be colored
* @return {Number|String} An ANSI color code for the given namespace
* @api private
*/
function selectColor(namespace) {
let hash = 0;
for (let i = 0; i < namespace.length; i++) {
hash = (hash << 5) - hash + namespace.charCodeAt(i);
hash |= 0;
}
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
}
createDebug.selectColor = selectColor;
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function createDebug(namespace) {
let prevTime;
let enableOverride = null;
let namespacesCache;
let enabledCache;
function debug(...args) {
if (!debug.enabled) return;
const self = debug;
const curr = Number(/* @__PURE__ */ new Date());
self.diff = curr - (prevTime || curr);
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
args[0] = createDebug.coerce(args[0]);
if (typeof args[0] !== "string") args.unshift("%O");
let index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
if (match === "%%") return "%";
index++;
const formatter = createDebug.formatters[format];
if (typeof formatter === "function") {
const val = args[index];
match = formatter.call(self, val);
args.splice(index, 1);
index--;
}
return match;
});
createDebug.formatArgs.call(self, args);
(self.log || createDebug.log).apply(self, args);
}
debug.namespace = namespace;
debug.useColors = createDebug.useColors();
debug.color = createDebug.selectColor(namespace);
debug.extend = extend;
debug.destroy = createDebug.destroy;
Object.defineProperty(debug, "enabled", {
enumerable: true,
configurable: false,
get: () => {
if (enableOverride !== null) return enableOverride;
if (namespacesCache !== createDebug.namespaces) {
namespacesCache = createDebug.namespaces;
enabledCache = createDebug.enabled(namespace);
}
return enabledCache;
},
set: (v) => {
enableOverride = v;
}
});
if (typeof createDebug.init === "function") createDebug.init(debug);
return debug;
}
function extend(namespace, delimiter) {
const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
newDebug.log = this.log;
return newDebug;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
createDebug.save(namespaces);
createDebug.namespaces = namespaces;
createDebug.names = [];
createDebug.skips = [];
const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
for (const ns of split) if (ns[0] === "-") createDebug.skips.push(ns.slice(1));
else createDebug.names.push(ns);
}
/**
* Checks if the given string matches a namespace template, honoring
* asterisks as wildcards.
*
* @param {String} search
* @param {String} template
* @return {Boolean}
*/
function matchesTemplate(search, template) {
let searchIndex = 0;
let templateIndex = 0;
let starIndex = -1;
let matchIndex = 0;
while (searchIndex < search.length) if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) if (template[templateIndex] === "*") {
starIndex = templateIndex;
matchIndex = searchIndex;
templateIndex++;
} else {
searchIndex++;
templateIndex++;
}
else if (starIndex !== -1) {
templateIndex = starIndex + 1;
matchIndex++;
searchIndex = matchIndex;
} else return false;
while (templateIndex < template.length && template[templateIndex] === "*") templateIndex++;
return templateIndex === template.length;
}
/**
* Disable debug output.
*
* @return {String} namespaces
* @api public
*/
function disable() {
const namespaces = [...createDebug.names, ...createDebug.skips.map((namespace) => "-" + namespace)].join(",");
createDebug.enable("");
return namespaces;
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
for (const skip of createDebug.skips) if (matchesTemplate(name, skip)) return false;
for (const ns of createDebug.names) if (matchesTemplate(name, ns)) return true;
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) return val.stack || val.message;
return val;
}
/**
* XXX DO NOT USE. This is a temporary stub function.
* XXX It WILL be removed in the next major release.
*/
function destroy() {
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
}
createDebug.enable(createDebug.load());
return createDebug;
}
module.exports = setup;
}));
//#endregion
//#region src/utils/logger.ts
var import_browser = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
/**
* This is the web browser implementation of `debug()`.
*/
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = localstorage();
exports.destroy = (() => {
let warned = false;
return () => {
if (!warned) {
warned = true;
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
}
};
})();
/**
* Colors.
*/
exports.colors = [
"#0000CC",
"#0000FF",
"#0033CC",
"#0033FF",
"#0066CC",
"#0066FF",
"#0099CC",
"#0099FF",
"#00CC00",
"#00CC33",
"#00CC66",
"#00CC99",
"#00CCCC",
"#00CCFF",
"#3300CC",
"#3300FF",
"#3333CC",
"#3333FF",
"#3366CC",
"#3366FF",
"#3399CC",
"#3399FF",
"#33CC00",
"#33CC33",
"#33CC66",
"#33CC99",
"#33CCCC",
"#33CCFF",
"#6600CC",
"#6600FF",
"#6633CC",
"#6633FF",
"#66CC00",
"#66CC33",
"#9900CC",
"#9900FF",
"#9933CC",
"#9933FF",
"#99CC00",
"#99CC33",
"#CC0000",
"#CC0033",
"#CC0066",
"#CC0099",
"#CC00CC",
"#CC00FF",
"#CC3300",
"#CC3333",
"#CC3366",
"#CC3399",
"#CC33CC",
"#CC33FF",
"#CC6600",
"#CC6633",
"#CC9900",
"#CC9933",
"#CCCC00",
"#CCCC33",
"#FF0000",
"#FF0033",
"#FF0066",
"#FF0099",
"#FF00CC",
"#FF00FF",
"#FF3300",
"#FF3333",
"#FF3366",
"#FF3399",
"#FF33CC",
"#FF33FF",
"#FF6600",
"#FF6633",
"#FF9900",
"#FF9933",
"#FFCC00",
"#FFCC33"
];
/**
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
* and the Firebug extension (any Firefox version) are known
* to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
function useColors() {
if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) return true;
if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) return false;
let m;
return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
}
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs(args) {
args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff);
if (!this.useColors) return;
const c = "color: " + this.color;
args.splice(1, 0, c, "color: inherit");
let index = 0;
let lastC = 0;
args[0].replace(/%[a-zA-Z%]/g, (match) => {
if (match === "%%") return;
index++;
if (match === "%c") lastC = index;
});
args.splice(lastC, 0, c);
}
/**
* Invokes `console.debug()` when available.
* No-op when `console.debug` is not a "function".
* If `console.debug` is not available, falls back
* to `console.log`.
*
* @api public
*/
exports.log = console.debug || console.log || (() => {});
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (namespaces) exports.storage.setItem("debug", namespaces);
else exports.storage.removeItem("debug");
} catch (error) {}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
let r;
try {
r = exports.storage.getItem("debug") || exports.storage.getItem("DEBUG");
} catch (error) {}
if (!r && typeof process !== "undefined" && "env" in process) r = process.env.DEBUG;
return r;
}
/**
* Localstorage attempts to return the localstorage.
*
* This is necessary because safari throws
* when a user disables cookies/localstorage
* and you attempt to access it.
*
* @return {LocalStorage}
* @api private
*/
function localstorage() {
try {
return localStorage;
} catch (error) {}
}
module.exports = require_common()(exports);
const { formatters } = module.exports;
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
formatters.j = function(v) {
try {
return JSON.stringify(v);
} catch (error) {
return "[UnexpectedJSONParseError]: " + error.message;
}
};
})))(), 1);
const LOG_TIMESTAMP_REGEXP = /\d{2}:\d{2}:\d{2}\.\d{3}/;
function normalizeNamespace(namespace) {
return namespace.split(":").map((segment) => {
return segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase();
}).filter(Boolean).join(":");
}
function getTimestamp() {
return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
}
async function readBody(message) {
if (message.body == null) return null;
try {
return await message.clone().text();
} catch {
return null;
}
}
function formatHeaders(headers) {
return Array.from(headers.entries()).map(([name, value]) => {
return `${name}: ${value}`;
});
}
async function formatHttpMessage(startLine, message) {
const lines = [startLine, ...formatHeaders(message.headers)];
const body = await readBody(message);
lines.push("", body ?? "");
return lines.join("\n");
}
async function formatRequest(request) {
return formatHttpMessage(`${request.method} ${request.url}`, request);
}
async function formatResponse(response) {
const statusText = response.statusText ? ` ${response.statusText}` : "";
return formatHttpMessage(`HTTP ${response.status}${statusText}`, response);
}
function formatLogArguments(arguments_) {
const message = arguments_[0];
if (typeof message === "string") {
const messageWithoutDebugTimestamp = message.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z /, "");
const timestampMatch = messageWithoutDebugTimestamp.match(LOG_TIMESTAMP_REGEXP);
if (!timestampMatch || timestampMatch.index === void 0) {
arguments_[0] = messageWithoutDebugTimestamp;
return;
}
const messagePrefix = messageWithoutDebugTimestamp.slice(0, timestampMatch.index).trim();
const messageBody = messageWithoutDebugTimestamp.slice(timestampMatch.index + timestampMatch[0].length).trimStart();
arguments_[0] = `${timestampMatch[0]} ${messagePrefix} ${messageBody}`;
}
}
function useConciseTimestamp(logger) {
logger.log = (...arguments_) => {
formatLogArguments(arguments_);
import_browser.log(...arguments_);
};
}
function isVerboseLoggingEnabled() {
if (typeof process !== "undefined" && process.env.DEBUG_LEVEL === "verbose") return true;
/**
* @note Consult the localStorage only in browser-like environments.
* In Node.js 26+, reading "globalThis.localStorage" without the
* "--localstorage-file" flag set emits an experimental warning
* (a try/catch cannot suppress it). Node.js consumers control the
* log level via the "DEBUG_LEVEL" environment variable above.
*/
if (typeof document === "undefined") return false;
try {
return globalThis.localStorage?.getItem("debugLevel") === "verbose";
} catch {
return false;
}
}
function createLogger(namespace) {
const logger = (0, import_browser.default)(`interceptors:${normalizeNamespace(namespace)}`);
Reflect.set(logger, "useColors", true);
useConciseTimestamp(logger);
return {
info(message, ...positionals) {
logger(`${getTimestamp()} ${message}`, ...positionals);
},
verbose(message, ...positionals) {
if (!isVerboseLoggingEnabled()) return;
logger(`${getTimestamp()} ${message}`, ...positionals);
},
isEnabled(level) {
return logger.enabled && (level === "default" || isVerboseLoggingEnabled());
}
};
}
//#endregion
//#region src/interceptor.ts
const interceptorsRegistry = globalThis.__MSW_INTERCEPTORS_REGISTRY ??= /* @__PURE__ */ new Map();
var Interceptor = class extends Disposable {
#owners;
static singleton(InterceptorClass) {
const symbol = InterceptorClass.symbol;
const existing = interceptorsRegistry.get(symbol);
if (existing instanceof InterceptorClass) return existing;
const newInstance = new InterceptorClass();
interceptorsRegistry.set(symbol, newInstance);
return newInstance;
}
constructor() {
super();
this.on = (type, listener, options) => {
return this.emitter.on(type, listener, options);
};
this.once = (type, listener, options) => {
return this.emitter.once(type, listener, options);
};
this.listeners = (type) => {
return this.emitter.listeners(type);
};
this.listenerCount = (type) => {
return this.emitter.listenerCount(type);
};
this.removeListener = (type, listener) => {
return this.emitter.removeListener(type, listener);
};
this.removeAllListeners = (type) => {
this.logger.info("removeAllListeners %o", { eventType: type ?? "*" });
return this.emitter.removeAllListeners(type);
};
this.#owners = /* @__PURE__ */ new Set();
this.readyState = "INACTIVE";
this.emitter = new Emitter();
this.logger = createLogger(this.#getLoggerNamespace());
}
apply(owner = this) {
if (this.#owners.has(owner)) return;
if (this.readyState !== "ACTIVE" && !this.predicate()) return;
this.#owners.add(owner);
if (this.readyState === "ACTIVE") return;
try {
this.setup();
this.readyState = "ACTIVE";
this.logger.info("apply");
} catch (error) {
this.dispose(owner);
throw error;
}
}
dispose(owner = this) {
if (!this.#owners.delete(owner)) return;
if (this.#owners.size > 0) return;
super.dispose();
this.emitter.removeAllListeners();
this.readyState = "DISPOSED";
this.logger.info("disable");
}
#getLoggerNamespace() {
const symbolDescription = this.constructor.symbol?.description;
if (symbolDescription) return symbolDescription.replace(/-interceptor$/, "");
return this.constructor.name.replace(/Interceptor$/, "");
}
};
//#endregion
//#region src/create-request-id.ts
/**
* Generate a random ID string to represent a request.
* @example
* createRequestId()
* // "f774b6c9c600f"
*/
function createRequestId() {
return Math.random().toString(16).slice(2);
}
//#endregion
export { formatResponse as a, formatRequest as i, Interceptor as n, createLogger as r, createRequestId as t };
//# sourceMappingURL=create-request-id-Bk5YX1AM.js.map
{"version":3,"file":"create-request-id-Bk5YX1AM.js","names":["#owners","#getLoggerNamespace"],"sources":["../../src/disposable.ts","../../node_modules/.pnpm/ms@2.1.3/node_modules/ms/index.js","../../node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/common.js","../../node_modules/.pnpm/debug@4.4.3/node_modules/debug/src/browser.js","../../src/utils/logger.ts","../../src/interceptor.ts","../../src/create-request-id.ts"],"sourcesContent":["export type DisposableSubscription = () => void\n\nexport class Disposable {\n protected subscriptions: Array<DisposableSubscription> = []\n\n public dispose() {\n let subscription: DisposableSubscription | undefined\n\n while ((subscription = this.subscriptions.pop())) {\n subscription()\n }\n }\n}\n","/**\n * Helpers.\n */\n\nvar s = 1000;\nvar m = s * 60;\nvar h = m * 60;\nvar d = h * 24;\nvar w = d * 7;\nvar y = d * 365.25;\n\n/**\n * Parse or format the given `val`.\n *\n * Options:\n *\n * - `long` verbose formatting [false]\n *\n * @param {String|Number} val\n * @param {Object} [options]\n * @throws {Error} throw an error if val is not a non-empty string or a number\n * @return {String|Number}\n * @api public\n */\n\nmodule.exports = function (val, options) {\n options = options || {};\n var type = typeof val;\n if (type === 'string' && val.length > 0) {\n return parse(val);\n } else if (type === 'number' && isFinite(val)) {\n return options.long ? fmtLong(val) : fmtShort(val);\n }\n throw new Error(\n 'val is not a non-empty string or a valid number. val=' +\n JSON.stringify(val)\n );\n};\n\n/**\n * Parse the given `str` and return milliseconds.\n *\n * @param {String} str\n * @return {Number}\n * @api private\n */\n\nfunction parse(str) {\n str = String(str);\n if (str.length > 100) {\n return;\n }\n var match = /^(-?(?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(\n str\n );\n if (!match) {\n return;\n }\n var n = parseFloat(match[1]);\n var type = (match[2] || 'ms').toLowerCase();\n switch (type) {\n case 'years':\n case 'year':\n case 'yrs':\n case 'yr':\n case 'y':\n return n * y;\n case 'weeks':\n case 'week':\n case 'w':\n return n * w;\n case 'days':\n case 'day':\n case 'd':\n return n * d;\n case 'hours':\n case 'hour':\n case 'hrs':\n case 'hr':\n case 'h':\n return n * h;\n case 'minutes':\n case 'minute':\n case 'mins':\n case 'min':\n case 'm':\n return n * m;\n case 'seconds':\n case 'second':\n case 'secs':\n case 'sec':\n case 's':\n return n * s;\n case 'milliseconds':\n case 'millisecond':\n case 'msecs':\n case 'msec':\n case 'ms':\n return n;\n default:\n return undefined;\n }\n}\n\n/**\n * Short format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtShort(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return Math.round(ms / d) + 'd';\n }\n if (msAbs >= h) {\n return Math.round(ms / h) + 'h';\n }\n if (msAbs >= m) {\n return Math.round(ms / m) + 'm';\n }\n if (msAbs >= s) {\n return Math.round(ms / s) + 's';\n }\n return ms + 'ms';\n}\n\n/**\n * Long format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtLong(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return plural(ms, msAbs, d, 'day');\n }\n if (msAbs >= h) {\n return plural(ms, msAbs, h, 'hour');\n }\n if (msAbs >= m) {\n return plural(ms, msAbs, m, 'minute');\n }\n if (msAbs >= s) {\n return plural(ms, msAbs, s, 'second');\n }\n return ms + ' ms';\n}\n\n/**\n * Pluralization helper.\n */\n\nfunction plural(ms, msAbs, n, name) {\n var isPlural = msAbs >= n * 1.5;\n return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');\n}\n","\n/**\n * This is the common logic for both the Node.js and web browser\n * implementations of `debug()`.\n */\n\nfunction setup(env) {\n\tcreateDebug.debug = createDebug;\n\tcreateDebug.default = createDebug;\n\tcreateDebug.coerce = coerce;\n\tcreateDebug.disable = disable;\n\tcreateDebug.enable = enable;\n\tcreateDebug.enabled = enabled;\n\tcreateDebug.humanize = require('ms');\n\tcreateDebug.destroy = destroy;\n\n\tObject.keys(env).forEach(key => {\n\t\tcreateDebug[key] = env[key];\n\t});\n\n\t/**\n\t* The currently active debug mode names, and names to skip.\n\t*/\n\n\tcreateDebug.names = [];\n\tcreateDebug.skips = [];\n\n\t/**\n\t* Map of special \"%n\" handling functions, for the debug \"format\" argument.\n\t*\n\t* Valid key names are a single, lower or upper-case letter, i.e. \"n\" and \"N\".\n\t*/\n\tcreateDebug.formatters = {};\n\n\t/**\n\t* Selects a color for a debug namespace\n\t* @param {String} namespace The namespace string for the debug instance to be colored\n\t* @return {Number|String} An ANSI color code for the given namespace\n\t* @api private\n\t*/\n\tfunction selectColor(namespace) {\n\t\tlet hash = 0;\n\n\t\tfor (let i = 0; i < namespace.length; i++) {\n\t\t\thash = ((hash << 5) - hash) + namespace.charCodeAt(i);\n\t\t\thash |= 0; // Convert to 32bit integer\n\t\t}\n\n\t\treturn createDebug.colors[Math.abs(hash) % createDebug.colors.length];\n\t}\n\tcreateDebug.selectColor = selectColor;\n\n\t/**\n\t* Create a debugger with the given `namespace`.\n\t*\n\t* @param {String} namespace\n\t* @return {Function}\n\t* @api public\n\t*/\n\tfunction createDebug(namespace) {\n\t\tlet prevTime;\n\t\tlet enableOverride = null;\n\t\tlet namespacesCache;\n\t\tlet enabledCache;\n\n\t\tfunction debug(...args) {\n\t\t\t// Disabled?\n\t\t\tif (!debug.enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst self = debug;\n\n\t\t\t// Set `diff` timestamp\n\t\t\tconst curr = Number(new Date());\n\t\t\tconst ms = curr - (prevTime || curr);\n\t\t\tself.diff = ms;\n\t\t\tself.prev = prevTime;\n\t\t\tself.curr = curr;\n\t\t\tprevTime = curr;\n\n\t\t\targs[0] = createDebug.coerce(args[0]);\n\n\t\t\tif (typeof args[0] !== 'string') {\n\t\t\t\t// Anything else let's inspect with %O\n\t\t\t\targs.unshift('%O');\n\t\t\t}\n\n\t\t\t// Apply any `formatters` transformations\n\t\t\tlet index = 0;\n\t\t\targs[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {\n\t\t\t\t// If we encounter an escaped % then don't increase the array index\n\t\t\t\tif (match === '%%') {\n\t\t\t\t\treturn '%';\n\t\t\t\t}\n\t\t\t\tindex++;\n\t\t\t\tconst formatter = createDebug.formatters[format];\n\t\t\t\tif (typeof formatter === 'function') {\n\t\t\t\t\tconst val = args[index];\n\t\t\t\t\tmatch = formatter.call(self, val);\n\n\t\t\t\t\t// Now we need to remove `args[index]` since it's inlined in the `format`\n\t\t\t\t\targs.splice(index, 1);\n\t\t\t\t\tindex--;\n\t\t\t\t}\n\t\t\t\treturn match;\n\t\t\t});\n\n\t\t\t// Apply env-specific formatting (colors, etc.)\n\t\t\tcreateDebug.formatArgs.call(self, args);\n\n\t\t\tconst logFn = self.log || createDebug.log;\n\t\t\tlogFn.apply(self, args);\n\t\t}\n\n\t\tdebug.namespace = namespace;\n\t\tdebug.useColors = createDebug.useColors();\n\t\tdebug.color = createDebug.selectColor(namespace);\n\t\tdebug.extend = extend;\n\t\tdebug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.\n\n\t\tObject.defineProperty(debug, 'enabled', {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: false,\n\t\t\tget: () => {\n\t\t\t\tif (enableOverride !== null) {\n\t\t\t\t\treturn enableOverride;\n\t\t\t\t}\n\t\t\t\tif (namespacesCache !== createDebug.namespaces) {\n\t\t\t\t\tnamespacesCache = createDebug.namespaces;\n\t\t\t\t\tenabledCache = createDebug.enabled(namespace);\n\t\t\t\t}\n\n\t\t\t\treturn enabledCache;\n\t\t\t},\n\t\t\tset: v => {\n\t\t\t\tenableOverride = v;\n\t\t\t}\n\t\t});\n\n\t\t// Env-specific initialization logic for debug instances\n\t\tif (typeof createDebug.init === 'function') {\n\t\t\tcreateDebug.init(debug);\n\t\t}\n\n\t\treturn debug;\n\t}\n\n\tfunction extend(namespace, delimiter) {\n\t\tconst newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);\n\t\tnewDebug.log = this.log;\n\t\treturn newDebug;\n\t}\n\n\t/**\n\t* Enables a debug mode by namespaces. This can include modes\n\t* separated by a colon and wildcards.\n\t*\n\t* @param {String} namespaces\n\t* @api public\n\t*/\n\tfunction enable(namespaces) {\n\t\tcreateDebug.save(namespaces);\n\t\tcreateDebug.namespaces = namespaces;\n\n\t\tcreateDebug.names = [];\n\t\tcreateDebug.skips = [];\n\n\t\tconst split = (typeof namespaces === 'string' ? namespaces : '')\n\t\t\t.trim()\n\t\t\t.replace(/\\s+/g, ',')\n\t\t\t.split(',')\n\t\t\t.filter(Boolean);\n\n\t\tfor (const ns of split) {\n\t\t\tif (ns[0] === '-') {\n\t\t\t\tcreateDebug.skips.push(ns.slice(1));\n\t\t\t} else {\n\t\t\t\tcreateDebug.names.push(ns);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks if the given string matches a namespace template, honoring\n\t * asterisks as wildcards.\n\t *\n\t * @param {String} search\n\t * @param {String} template\n\t * @return {Boolean}\n\t */\n\tfunction matchesTemplate(search, template) {\n\t\tlet searchIndex = 0;\n\t\tlet templateIndex = 0;\n\t\tlet starIndex = -1;\n\t\tlet matchIndex = 0;\n\n\t\twhile (searchIndex < search.length) {\n\t\t\tif (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {\n\t\t\t\t// Match character or proceed with wildcard\n\t\t\t\tif (template[templateIndex] === '*') {\n\t\t\t\t\tstarIndex = templateIndex;\n\t\t\t\t\tmatchIndex = searchIndex;\n\t\t\t\t\ttemplateIndex++; // Skip the '*'\n\t\t\t\t} else {\n\t\t\t\t\tsearchIndex++;\n\t\t\t\t\ttemplateIndex++;\n\t\t\t\t}\n\t\t\t} else if (starIndex !== -1) { // eslint-disable-line no-negated-condition\n\t\t\t\t// Backtrack to the last '*' and try to match more characters\n\t\t\t\ttemplateIndex = starIndex + 1;\n\t\t\t\tmatchIndex++;\n\t\t\t\tsearchIndex = matchIndex;\n\t\t\t} else {\n\t\t\t\treturn false; // No match\n\t\t\t}\n\t\t}\n\n\t\t// Handle trailing '*' in template\n\t\twhile (templateIndex < template.length && template[templateIndex] === '*') {\n\t\t\ttemplateIndex++;\n\t\t}\n\n\t\treturn templateIndex === template.length;\n\t}\n\n\t/**\n\t* Disable debug output.\n\t*\n\t* @return {String} namespaces\n\t* @api public\n\t*/\n\tfunction disable() {\n\t\tconst namespaces = [\n\t\t\t...createDebug.names,\n\t\t\t...createDebug.skips.map(namespace => '-' + namespace)\n\t\t].join(',');\n\t\tcreateDebug.enable('');\n\t\treturn namespaces;\n\t}\n\n\t/**\n\t* Returns true if the given mode name is enabled, false otherwise.\n\t*\n\t* @param {String} name\n\t* @return {Boolean}\n\t* @api public\n\t*/\n\tfunction enabled(name) {\n\t\tfor (const skip of createDebug.skips) {\n\t\t\tif (matchesTemplate(name, skip)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tfor (const ns of createDebug.names) {\n\t\t\tif (matchesTemplate(name, ns)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t* Coerce `val`.\n\t*\n\t* @param {Mixed} val\n\t* @return {Mixed}\n\t* @api private\n\t*/\n\tfunction coerce(val) {\n\t\tif (val instanceof Error) {\n\t\t\treturn val.stack || val.message;\n\t\t}\n\t\treturn val;\n\t}\n\n\t/**\n\t* XXX DO NOT USE. This is a temporary stub function.\n\t* XXX It WILL be removed in the next major release.\n\t*/\n\tfunction destroy() {\n\t\tconsole.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');\n\t}\n\n\tcreateDebug.enable(createDebug.load());\n\n\treturn createDebug;\n}\n\nmodule.exports = setup;\n","/* eslint-env browser */\n\n/**\n * This is the web browser implementation of `debug()`.\n */\n\nexports.formatArgs = formatArgs;\nexports.save = save;\nexports.load = load;\nexports.useColors = useColors;\nexports.storage = localstorage();\nexports.destroy = (() => {\n\tlet warned = false;\n\n\treturn () => {\n\t\tif (!warned) {\n\t\t\twarned = true;\n\t\t\tconsole.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');\n\t\t}\n\t};\n})();\n\n/**\n * Colors.\n */\n\nexports.colors = [\n\t'#0000CC',\n\t'#0000FF',\n\t'#0033CC',\n\t'#0033FF',\n\t'#0066CC',\n\t'#0066FF',\n\t'#0099CC',\n\t'#0099FF',\n\t'#00CC00',\n\t'#00CC33',\n\t'#00CC66',\n\t'#00CC99',\n\t'#00CCCC',\n\t'#00CCFF',\n\t'#3300CC',\n\t'#3300FF',\n\t'#3333CC',\n\t'#3333FF',\n\t'#3366CC',\n\t'#3366FF',\n\t'#3399CC',\n\t'#3399FF',\n\t'#33CC00',\n\t'#33CC33',\n\t'#33CC66',\n\t'#33CC99',\n\t'#33CCCC',\n\t'#33CCFF',\n\t'#6600CC',\n\t'#6600FF',\n\t'#6633CC',\n\t'#6633FF',\n\t'#66CC00',\n\t'#66CC33',\n\t'#9900CC',\n\t'#9900FF',\n\t'#9933CC',\n\t'#9933FF',\n\t'#99CC00',\n\t'#99CC33',\n\t'#CC0000',\n\t'#CC0033',\n\t'#CC0066',\n\t'#CC0099',\n\t'#CC00CC',\n\t'#CC00FF',\n\t'#CC3300',\n\t'#CC3333',\n\t'#CC3366',\n\t'#CC3399',\n\t'#CC33CC',\n\t'#CC33FF',\n\t'#CC6600',\n\t'#CC6633',\n\t'#CC9900',\n\t'#CC9933',\n\t'#CCCC00',\n\t'#CCCC33',\n\t'#FF0000',\n\t'#FF0033',\n\t'#FF0066',\n\t'#FF0099',\n\t'#FF00CC',\n\t'#FF00FF',\n\t'#FF3300',\n\t'#FF3333',\n\t'#FF3366',\n\t'#FF3399',\n\t'#FF33CC',\n\t'#FF33FF',\n\t'#FF6600',\n\t'#FF6633',\n\t'#FF9900',\n\t'#FF9933',\n\t'#FFCC00',\n\t'#FFCC33'\n];\n\n/**\n * Currently only WebKit-based Web Inspectors, Firefox >= v31,\n * and the Firebug extension (any Firefox version) are known\n * to support \"%c\" CSS customizations.\n *\n * TODO: add a `localStorage` variable to explicitly enable/disable colors\n */\n\n// eslint-disable-next-line complexity\nfunction useColors() {\n\t// NB: In an Electron preload script, document will be defined but not fully\n\t// initialized. Since we know we're in Chrome, we'll just detect this case\n\t// explicitly\n\tif (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {\n\t\treturn true;\n\t}\n\n\t// Internet Explorer and Edge do not support colors.\n\tif (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\\/(\\d+)/)) {\n\t\treturn false;\n\t}\n\n\tlet m;\n\n\t// Is webkit? http://stackoverflow.com/a/16459606/376773\n\t// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632\n\t// eslint-disable-next-line no-return-assign\n\treturn (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||\n\t\t// Is firebug? http://stackoverflow.com/a/398120/376773\n\t\t(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||\n\t\t// Is firefox >= v31?\n\t\t// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages\n\t\t(typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/)) && parseInt(m[1], 10) >= 31) ||\n\t\t// Double check webkit in userAgent just in case we are in a worker\n\t\t(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\\/(\\d+)/));\n}\n\n/**\n * Colorize log arguments if enabled.\n *\n * @api public\n */\n\nfunction formatArgs(args) {\n\targs[0] = (this.useColors ? '%c' : '') +\n\t\tthis.namespace +\n\t\t(this.useColors ? ' %c' : ' ') +\n\t\targs[0] +\n\t\t(this.useColors ? '%c ' : ' ') +\n\t\t'+' + module.exports.humanize(this.diff);\n\n\tif (!this.useColors) {\n\t\treturn;\n\t}\n\n\tconst c = 'color: ' + this.color;\n\targs.splice(1, 0, c, 'color: inherit');\n\n\t// The final \"%c\" is somewhat tricky, because there could be other\n\t// arguments passed either before or after the %c, so we need to\n\t// figure out the correct index to insert the CSS into\n\tlet index = 0;\n\tlet lastC = 0;\n\targs[0].replace(/%[a-zA-Z%]/g, match => {\n\t\tif (match === '%%') {\n\t\t\treturn;\n\t\t}\n\t\tindex++;\n\t\tif (match === '%c') {\n\t\t\t// We only are interested in the *last* %c\n\t\t\t// (the user may have provided their own)\n\t\t\tlastC = index;\n\t\t}\n\t});\n\n\targs.splice(lastC, 0, c);\n}\n\n/**\n * Invokes `console.debug()` when available.\n * No-op when `console.debug` is not a \"function\".\n * If `console.debug` is not available, falls back\n * to `console.log`.\n *\n * @api public\n */\nexports.log = console.debug || console.log || (() => {});\n\n/**\n * Save `namespaces`.\n *\n * @param {String} namespaces\n * @api private\n */\nfunction save(namespaces) {\n\ttry {\n\t\tif (namespaces) {\n\t\t\texports.storage.setItem('debug', namespaces);\n\t\t} else {\n\t\t\texports.storage.removeItem('debug');\n\t\t}\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n}\n\n/**\n * Load `namespaces`.\n *\n * @return {String} returns the previously persisted debug modes\n * @api private\n */\nfunction load() {\n\tlet r;\n\ttry {\n\t\tr = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG') ;\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n\n\t// If debug isn't set in LS, and we're in Electron, try to load $DEBUG\n\tif (!r && typeof process !== 'undefined' && 'env' in process) {\n\t\tr = process.env.DEBUG;\n\t}\n\n\treturn r;\n}\n\n/**\n * Localstorage attempts to return the localstorage.\n *\n * This is necessary because safari throws\n * when a user disables cookies/localstorage\n * and you attempt to access it.\n *\n * @return {LocalStorage}\n * @api private\n */\n\nfunction localstorage() {\n\ttry {\n\t\t// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context\n\t\t// The Browser also has localStorage in the global context.\n\t\treturn localStorage;\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n}\n\nmodule.exports = require('./common')(exports);\n\nconst {formatters} = module.exports;\n\n/**\n * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.\n */\n\nformatters.j = function (v) {\n\ttry {\n\t\treturn JSON.stringify(v);\n\t} catch (error) {\n\t\treturn '[UnexpectedJSONParseError]: ' + error.message;\n\t}\n};\n","import debug from 'debug'\n\nexport type LogLevel = 'default' | 'verbose'\n\nexport interface Logger {\n info(message: string, ...positionals: Array<unknown>): void\n verbose(message: string, ...positionals: Array<unknown>): void\n isEnabled(level: LogLevel): boolean\n}\n\nconst LOG_TIMESTAMP_REGEXP = /\\d{2}:\\d{2}:\\d{2}\\.\\d{3}/\n\nfunction normalizeNamespace(namespace: string): string {\n return namespace\n .split(':')\n .map((segment) => {\n return segment\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/[^a-zA-Z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase()\n })\n .filter(Boolean)\n .join(':')\n}\n\nfunction getTimestamp(): string {\n return new Date().toISOString().slice(11, 23)\n}\n\nasync function readBody(message: Request | Response): Promise<string | null> {\n if (message.body == null) {\n return null\n }\n\n try {\n return await message.clone().text()\n } catch {\n return null\n }\n}\n\nfunction formatHeaders(headers: Headers): Array<string> {\n return Array.from(headers.entries()).map(([name, value]) => {\n return `${name}: ${value}`\n })\n}\n\nasync function formatHttpMessage(\n startLine: string,\n message: Request | Response\n): Promise<string> {\n const lines = [startLine, ...formatHeaders(message.headers)]\n const body = await readBody(message)\n\n lines.push('', body ?? '')\n\n return lines.join('\\n')\n}\n\nexport async function formatRequest(request: Request): Promise<string> {\n return formatHttpMessage(`${request.method} ${request.url}`, request)\n}\n\nexport async function formatResponse(response: Response): Promise<string> {\n const statusText = response.statusText ? ` ${response.statusText}` : ''\n return formatHttpMessage(\n `HTTP ${response.status}${statusText}`,\n response\n )\n}\n\nfunction formatLogArguments(arguments_: Array<unknown>): void {\n const message = arguments_[0]\n\n if (typeof message === 'string') {\n const messageWithoutDebugTimestamp = message.replace(\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z /,\n ''\n )\n const timestampMatch = messageWithoutDebugTimestamp.match(\n LOG_TIMESTAMP_REGEXP\n )\n\n if (!timestampMatch || timestampMatch.index === undefined) {\n arguments_[0] = messageWithoutDebugTimestamp\n return\n }\n\n const messagePrefix = messageWithoutDebugTimestamp\n .slice(0, timestampMatch.index)\n .trim()\n const messageBody = messageWithoutDebugTimestamp\n .slice(timestampMatch.index + timestampMatch[0].length)\n .trimStart()\n\n arguments_[0] = `${timestampMatch[0]} ${messagePrefix} ${messageBody}`\n }\n}\n\nfunction useConciseTimestamp(logger: debug.Debugger): void {\n logger.log = (...arguments_) => {\n formatLogArguments(arguments_)\n debug.log(...arguments_)\n }\n}\n\nfunction isVerboseLoggingEnabled(): boolean {\n if (typeof process !== 'undefined' && process.env.DEBUG_LEVEL === 'verbose') {\n return true\n }\n\n /**\n * @note Consult the localStorage only in browser-like environments.\n * In Node.js 26+, reading \"globalThis.localStorage\" without the\n * \"--localstorage-file\" flag set emits an experimental warning\n * (a try/catch cannot suppress it). Node.js consumers control the\n * log level via the \"DEBUG_LEVEL\" environment variable above.\n */\n if (typeof document === 'undefined') {\n return false\n }\n\n try {\n return globalThis.localStorage?.getItem('debugLevel') === 'verbose'\n } catch {\n return false\n }\n}\n\nexport function createLogger(namespace: string): Logger {\n const normalizedNamespace = normalizeNamespace(namespace)\n const logger = debug(`interceptors:${normalizedNamespace}`)\n Reflect.set(logger, 'useColors', true)\n useConciseTimestamp(logger)\n\n return {\n info(message, ...positionals) {\n logger(`${getTimestamp()} ${message}`, ...positionals)\n },\n verbose(message, ...positionals) {\n if (!isVerboseLoggingEnabled()) {\n return\n }\n\n logger(`${getTimestamp()} ${message}`, ...positionals)\n },\n isEnabled(level) {\n return (\n logger.enabled && (level === 'default' || isVerboseLoggingEnabled())\n )\n },\n }\n}\n","import { Emitter, type DefaultEventMap } from 'rettime'\nimport { Disposable } from './disposable'\nimport { createLogger, type Logger } from './utils/logger'\n\nexport enum InterceptorReadyState {\n INACTIVE = 'INACTIVE',\n ACTIVE = 'ACTIVE',\n DISPOSED = 'DISPOSED',\n}\n\ndeclare global {\n var __MSW_INTERCEPTORS_REGISTRY: Map<symbol, Interceptor<any>> | undefined\n}\n\nconst interceptorsRegistry = (globalThis.__MSW_INTERCEPTORS_REGISTRY ??=\n new Map<symbol, Interceptor<any>>())\n\nexport abstract class Interceptor<\n Events extends DefaultEventMap,\n> extends Disposable {\n declare ['constructor']: typeof Interceptor\n\n protected emitter: Emitter<Events>\n protected readonly logger: Logger\n\n public readyState: InterceptorReadyState\n\n static readonly symbol: symbol\n\n #owners: Set<object>\n\n static singleton<T extends Interceptor<any>>(\n InterceptorClass: (new () => T) & { symbol: symbol }\n ): T {\n const symbol = InterceptorClass.symbol\n const existing = interceptorsRegistry.get(symbol)\n\n if (existing instanceof InterceptorClass) {\n return existing\n }\n\n const newInstance = new InterceptorClass()\n interceptorsRegistry.set(symbol, newInstance)\n return newInstance\n }\n\n constructor() {\n super()\n\n this.#owners = new Set()\n this.readyState = InterceptorReadyState.INACTIVE\n this.emitter = new Emitter()\n this.logger = createLogger(this.#getLoggerNamespace())\n }\n\n protected abstract predicate(): boolean\n protected abstract setup(): void\n\n public apply(owner: object = this): void {\n if (this.#owners.has(owner)) {\n return\n }\n\n if (\n this.readyState !== InterceptorReadyState.ACTIVE &&\n !this.predicate()\n ) {\n return\n }\n\n this.#owners.add(owner)\n\n if (this.readyState === InterceptorReadyState.ACTIVE) {\n return\n }\n\n try {\n this.setup()\n this.readyState = InterceptorReadyState.ACTIVE\n this.logger.info('apply')\n } catch (error) {\n this.dispose(owner)\n throw error\n }\n }\n\n public dispose(owner: object = this): void {\n if (!this.#owners.delete(owner)) {\n return\n }\n\n if (this.#owners.size > 0) {\n return\n }\n\n super.dispose()\n this.emitter.removeAllListeners()\n this.readyState = InterceptorReadyState.DISPOSED\n this.logger.info('disable')\n }\n\n public on: Emitter<Events>['on'] = (type, listener, options) => {\n return this.emitter.on(type, listener, options)\n }\n\n public once: Emitter<Events>['once'] = (type, listener, options) => {\n return this.emitter.once(type, listener, options)\n }\n\n public listeners: Emitter<Events>['listeners'] = (type) => {\n return this.emitter.listeners(type)\n }\n\n public listenerCount: Emitter<Events>['listenerCount'] = (type) => {\n return this.emitter.listenerCount(type)\n }\n\n public removeListener: Emitter<Events>['removeListener'] = (\n type,\n listener\n ) => {\n return this.emitter.removeListener(type, listener)\n }\n\n public removeAllListeners: Emitter<Events>['removeAllListeners'] = (type) => {\n this.logger.info('removeAllListeners %o', { eventType: type ?? '*' })\n return this.emitter.removeAllListeners(type)\n }\n\n #getLoggerNamespace(): string {\n const symbolDescription = this.constructor.symbol?.description\n\n if (symbolDescription) {\n return symbolDescription.replace(/-interceptor$/, '')\n }\n\n return this.constructor.name.replace(/Interceptor$/, '')\n }\n}\n","/**\n * Generate a random ID string to represent a request.\n * @example\n * createRequestId()\n * // \"f774b6c9c600f\"\n */\nexport function createRequestId(): string {\n return Math.random().toString(16).slice(2)\n}\n"],"x_google_ignoreList":[1,2,3],"mappings":"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"}
import { a as formatResponse } from "./create-request-id-Bk5YX1AM.js";
import { invariant } from "outvariant";
//#region src/interceptor-error.ts
var InterceptorError = class InterceptorError extends Error {
constructor(message) {
super(message);
this.name = "InterceptorError";
Object.setPrototypeOf(this, InterceptorError.prototype);
}
};
//#endregion
//#region src/request-controller.ts
var RequestController = class RequestController {
static {
this.PENDING = 0;
}
static {
this.PASSTHROUGH = 1;
}
static {
this.RESPONSE = 2;
}
static {
this.ERROR = 3;
}
#handled;
constructor(request, source, options) {
this.request = request;
this.source = source;
this.options = options;
this.readyState = RequestController.PENDING;
this.#handled = Promise.withResolvers();
this.handled = this.#handled.promise;
}
/**
* Perform this request as-is.
*/
async passthrough() {
invariant.as(InterceptorError, this.readyState === RequestController.PENDING, "Failed to passthrough the \"%s %s\" request: the request has already been handled", this.request.method, this.request.url);
this.readyState = RequestController.PASSTHROUGH;
if (this.options) this.options.logger.info("[%s] passthrough", this.options.requestId);
await this.source.passthrough();
this.#handled.resolve();
}
/**
* Respond to this request with the given `Response` instance.
*
* @example
* controller.respondWith(new Response())
* controller.respondWith(Response.json({ id }))
* controller.respondWith(Response.error())
*/
respondWith(response) {
invariant.as(InterceptorError, this.readyState === RequestController.PENDING, "Failed to respond to the \"%s %s\" request with \"%d %s\": the request has already been handled (%d)", this.request.method, this.request.url, response.status, response.statusText || "OK", this.readyState);
this.readyState = RequestController.RESPONSE;
if (this.options?.logger.isEnabled("default")) {
const { logger, requestId } = this.options;
formatResponse(response).then((message) => {
logger.info("[%s] mocked %s", requestId, message);
});
}
this.#handled.resolve();
/**
* @note Although `source.respondWith()` is potentially asynchronous,
* do NOT await it for backward-compatibility. Awaiting it will short-circuit
* the request listener invocation as soon as a listener responds to a request.
* Ideally, that's what we want, but that's not what we promise the user.
*/
this.source.respondWith(response);
}
/**
* Error this request with the given reason.
*
* @example
* controller.errorWith()
* controller.errorWith(new Error('Oops!'))
* controller.errorWith({ message: 'Oops!'})
*/
errorWith(reason) {
invariant.as(InterceptorError, this.readyState === RequestController.PENDING, "Failed to error the \"%s %s\" request with \"%s\": the request has already been handled (%d)", this.request.method, this.request.url, reason?.toString(), this.readyState);
this.readyState = RequestController.ERROR;
if (this.options) this.options.logger.info("[%s] error %o", this.options.requestId, reason);
this.source.errorWith(reason);
this.#handled.resolve();
}
};
//#endregion
//#region src/interceptors/ClientRequest/utils/record-raw-headers.ts
const kRawHeaders = Symbol("kRawHeaders");
/**
* Define the raw headers symbol on the given `Headers` instance.
* If the symbol already exists, it gets overridden.
*/
function defineRawHeadersSymbol(headers, rawHeaders) {
Object.defineProperty(headers, kRawHeaders, {
value: rawHeaders,
enumerable: false,
configurable: true
});
}
function getRawFetchHeaders(headers) {
if (!Reflect.has(headers, kRawHeaders)) return Array.from(headers.entries());
const rawHeaders = Reflect.get(headers, kRawHeaders);
return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries());
}
function copyRawHeaders(source, destination) {
const rawHeaders = [...getRawFetchHeaders(source)];
if (rawHeaders.length === 0) return;
/**
* @note Add headers from trhe destination that raw headers from the source
* don't have. Undici automatically appends a "Content-Type" header for responses
* and, for some reason, that change is not recorded. This preserves it.
*/
for (const [name, value] of destination) if (rawHeaders.every((header) => header[0].toLowerCase() !== name.toLowerCase())) rawHeaders.push([name, value]);
defineRawHeadersSymbol(destination, rawHeaders);
}
//#endregion
//#region src/utils/get-value-by-symbol.ts
/**
* Returns the value behind the symbol with the given name.
*/
function getValueBySymbol(symbolName, source) {
const symbol = Object.getOwnPropertySymbols(source).find((symbol) => {
return symbol.description === symbolName;
});
if (symbol) return Reflect.get(source, symbol);
}
//#endregion
//#region src/utils/is-object.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/is-property-accessible.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/response-utils.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/fetch-utils.ts
var FetchRequest = class FetchRequest extends Request {
static #resolveProperty(input, init = {}, key) {
return init[key] ?? (input instanceof Request ? input[key] : void 0);
}
/**
* Check if the given request method is configurable.
* @see https://fetch.spec.whatwg.org/#methods
*/
static isConfigurableMethod(method) {
return method !== "CONNECT" && method !== "TRACE" && method !== "TRACK";
}
static isMethodWithBody(method) {
return method !== "HEAD" && method !== "GET" && FetchRequest.isConfigurableMethod(method);
}
/**
* Check if the given request `mode` is configurable.
* @see https://fetch.spec.whatwg.org/#concept-request-mode
*/
static isConfigurableMode(mode) {
return mode !== "navigate" && mode !== "websocket" && mode !== "webtransport";
}
constructor(input, init) {
const method = FetchRequest.#resolveProperty(input, init, "method") || "GET";
const safeMethod = FetchRequest.isConfigurableMethod(method) ? method : "GET";
const hasExplicitBody = init != null && "body" in init;
/**
* Only include `body` in the super init when it needs to be overridden.
* When `input` is a Request and no explicit body is in `init`, let the
* Request constructor handle body transfer naturally so it properly
* marks the original request's body as consumed (bodyUsed = true).
*/
const bodyInit = !FetchRequest.isMethodWithBody(method) ? { body: void 0 } : hasExplicitBody ? { body: init.body } : {};
const mode = FetchRequest.#resolveProperty(input, init, "mode") ?? void 0;
const safeMode = FetchRequest.isConfigurableMode(mode) ? mode : void 0;
super(input, {
...init || {},
method: safeMethod,
mode: safeMode,
duplex: init?.duplex ?? (FetchRequest.isMethodWithBody(method) ? "half" : void 0),
...bodyInit
});
if (method !== safeMethod) this.#setInternalProperty("method", method);
if (method === "CONNECT") {
const url = new URL(input instanceof Request ? input.url : input);
let authority;
/**
* @note Node.js has a bug parsing raw CONNECT requests URLs like
* "http://127.0.0.1:1337/localhost:80". It would treat "localhost:" as a protocol.
*/
if (url.protocol === "localhost:") authority = url.href;
else authority = url.pathname.replace(/^\/+/, "");
/**
* @note Define "url" as a getter because Undici uses their own
* logic to resolve the "request.url" property. Simply reassigning
* its value doesn't do anything. This is a destructive action
* but it's safe because "CONNECT" requests are forbidden per fetch.
*/
Object.defineProperty(this, "url", {
get: () => authority,
enumerable: true,
configurable: true
});
}
if (mode != null && mode !== safeMode) this.#setInternalProperty("mode", mode);
}
#setInternalProperty(key, value) {
const internalState = getValueBySymbol("state", this);
if (internalState) Reflect.set(internalState, key, value);
else Object.defineProperty(this, key, {
value,
enumerable: true,
configurable: true,
writable: false
});
}
};
const kStatus = Symbol("kStatus");
const kUrl = Symbol("kUrl");
var FetchResponse = class FetchResponse extends Response {
static from(response, init) {
if (response instanceof FetchResponse) return response;
if (isResponseError(response)) return response;
const fetchResponse = new FetchResponse(response.body, {
url: init?.url ?? response.url,
status: init?.status || response.status,
statusText: init?.statusText ?? response.statusText,
headers: init?.headers ?? response.headers
});
copyRawHeaders(response.headers, fetchResponse.headers);
return fetchResponse;
}
static {
this.STATUS_CODES_WITHOUT_BODY = [
101,
103,
204,
205,
304
];
}
static {
this.STATUS_CODES_WITH_REDIRECT = [
301,
302,
303,
307,
308
];
}
static isConfigurableStatusCode(status) {
return status >= 200 && status <= 599;
}
static isRedirectResponse(status) {
return FetchResponse.STATUS_CODES_WITH_REDIRECT.includes(status);
}
/**
* Returns a boolean indicating whether the given response status
* code represents a response that can have a body.
*/
static isResponseWithBody(status) {
return !FetchResponse.STATUS_CODES_WITHOUT_BODY.includes(status);
}
static setStatus(status, response) {
/**
* @note Undici keeps an internal "Symbol(state)" that holds
* the actual value of response status. Update that in Node.js.
*/
const internalState = getValueBySymbol("state", response);
if (internalState) internalState.status = status;
else Object.defineProperty(response, "status", {
value: status,
enumerable: true,
configurable: true,
writable: false
});
Object.defineProperty(response, kStatus, {
value: status,
enumerable: false
});
}
static setUrl(url, response) {
if (!url || url === "about:" || !URL.canParse(url)) return;
const state = getValueBySymbol("state", response);
if (state) state.urlList.push(new URL(url));
else Object.defineProperty(response, "url", {
value: url,
enumerable: true,
configurable: true,
writable: false
});
Object.defineProperty(response, kUrl, {
value: url,
enumerable: false
});
}
/**
* Parses the given raw HTTP headers into a Fetch API `Headers` instance.
*/
static parseRawHeaders(rawHeaders) {
const headers = new Headers();
for (let line = 0; line < rawHeaders.length; line += 2) headers.append(rawHeaders[line], rawHeaders[line + 1]);
return headers;
}
/**
* Safely clones the given `Response`.
* Coerces response clone exceptions into 500 mocked responses.
* Handy in the environments that introduce arbitrary response
* cloning restrictions, like "101 Switching Protocols" cloning
* in "miniflare".
*/
static clone(response) {
try {
return response.clone();
} catch (error) {
return Response.json(error instanceof Error ? {
name: error.name,
message: error.message,
stack: error.stack
} : {}, {
status: 500,
statusText: "Unclonable Response"
});
}
}
#status;
#url;
constructor(body, init = {}) {
const status = init.status ?? 200;
const safeStatus = FetchResponse.isConfigurableStatusCode(status) ? status : 200;
const finalBody = FetchResponse.isResponseWithBody(status) ? body : null;
super(finalBody, {
status: safeStatus,
statusText: init.statusText,
headers: init.headers
});
/**
* Since Node.js v24, Undici stores the Response state in an inaccessible field "#state".
* Forward the modified status/URL to the cloned response manually.
* @see https://github.com/nodejs/undici/blob/f734c87280e626c75f59aad55b65eb6a89cef392/lib/web/fetch/response.js#L242
*/
if (status !== safeStatus) FetchResponse.setStatus(status, this);
FetchResponse.setUrl(init.url, this);
}
clone() {
const clonedResponse = super.clone();
const customStatus = Reflect.get(this, kStatus);
if (customStatus) FetchResponse.setStatus(customStatus, clonedResponse);
const customUrl = Reflect.get(this, kUrl);
if (customUrl) FetchResponse.setUrl(customUrl, clonedResponse);
return clonedResponse;
}
};
//#endregion
export { isResponseLike as a, RequestController as c, isResponseError as i, InterceptorError as l, FetchResponse as n, isObject as o, createServerErrorResponse as r, copyRawHeaders as s, FetchRequest as t };
//# sourceMappingURL=fetch-utils-CUOrwEQf.js.map
{"version":3,"file":"fetch-utils-CUOrwEQf.js","names":["#handled","#resolveProperty","#setInternalProperty"],"sources":["../../src/interceptor-error.ts","../../src/request-controller.ts","../../src/interceptors/ClientRequest/utils/record-raw-headers.ts","../../src/utils/get-value-by-symbol.ts","../../src/utils/is-object.ts","../../src/utils/is-property-accessible.ts","../../src/utils/response-utils.ts","../../src/utils/fetch-utils.ts"],"sourcesContent":["export class InterceptorError extends Error {\n constructor(message?: string) {\n super(message)\n this.name = 'InterceptorError'\n Object.setPrototypeOf(this, InterceptorError.prototype)\n }\n}\n","import { invariant } from 'outvariant'\nimport { InterceptorError } from './interceptor-error'\nimport { formatResponse, type Logger } from './utils/logger'\n\nexport interface RequestControllerSource {\n passthrough(): void | Promise<void>\n respondWith(response: Response): void | Promise<void>\n errorWith(reason?: unknown): void | Promise<void>\n}\n\ninterface RequestControllerOptions {\n logger: Logger\n requestId: string\n}\n\nexport class RequestController {\n static PENDING = 0 as const\n static PASSTHROUGH = 1 as const\n static RESPONSE = 2 as const\n static ERROR = 3 as const\n\n public readyState: number\n\n /**\n * A Promise that resolves when this controller handles a request.\n * See `controller.readyState` for more information on the handling result.\n */\n public handled: Promise<void>\n\n readonly #handled: PromiseWithResolvers<void>\n\n constructor(\n protected readonly request: Request,\n protected readonly source: RequestControllerSource,\n protected readonly options?: RequestControllerOptions\n ) {\n this.readyState = RequestController.PENDING\n this.#handled = Promise.withResolvers<void>()\n this.handled = this.#handled.promise\n }\n\n /**\n * Perform this request as-is.\n */\n public async passthrough(): Promise<void> {\n invariant.as(\n InterceptorError,\n this.readyState === RequestController.PENDING,\n 'Failed to passthrough the \"%s %s\" request: the request has already been handled',\n this.request.method,\n this.request.url\n )\n\n this.readyState = RequestController.PASSTHROUGH\n if (this.options) {\n this.options.logger.info('[%s] passthrough', this.options.requestId)\n }\n await this.source.passthrough()\n this.#handled.resolve()\n }\n\n /**\n * Respond to this request with the given `Response` instance.\n *\n * @example\n * controller.respondWith(new Response())\n * controller.respondWith(Response.json({ id }))\n * controller.respondWith(Response.error())\n */\n public respondWith(response: Response): void {\n invariant.as(\n InterceptorError,\n this.readyState === RequestController.PENDING,\n 'Failed to respond to the \"%s %s\" request with \"%d %s\": the request has already been handled (%d)',\n this.request.method,\n this.request.url,\n response.status,\n response.statusText || 'OK',\n this.readyState\n )\n\n this.readyState = RequestController.RESPONSE\n if (this.options?.logger.isEnabled('default')) {\n const { logger, requestId } = this.options\n\n void formatResponse(response).then((message) => {\n logger.info('[%s] mocked %s', requestId, message)\n })\n }\n this.#handled.resolve()\n\n /**\n * @note Although `source.respondWith()` is potentially asynchronous,\n * do NOT await it for backward-compatibility. Awaiting it will short-circuit\n * the request listener invocation as soon as a listener responds to a request.\n * Ideally, that's what we want, but that's not what we promise the user.\n */\n this.source.respondWith(response)\n }\n\n /**\n * Error this request with the given reason.\n *\n * @example\n * controller.errorWith()\n * controller.errorWith(new Error('Oops!'))\n * controller.errorWith({ message: 'Oops!'})\n */\n public errorWith(reason?: unknown): void {\n invariant.as(\n InterceptorError,\n this.readyState === RequestController.PENDING,\n 'Failed to error the \"%s %s\" request with \"%s\": the request has already been handled (%d)',\n this.request.method,\n this.request.url,\n reason?.toString(),\n this.readyState\n )\n\n this.readyState = RequestController.ERROR\n if (this.options) {\n this.options.logger.info(\n '[%s] error %o',\n this.options.requestId,\n reason\n )\n }\n this.source.errorWith(reason)\n this.#handled.resolve()\n }\n}\n","import { FetchRequest, FetchResponse } from '../../../utils/fetch-utils'\n\ntype HeaderTuple = [string, string]\ntype RawHeaders = Array<HeaderTuple>\ntype SetHeaderBehavior = 'set' | 'append'\n\nconst kRawHeaders = Symbol('kRawHeaders')\nconst kRestorePatches = Symbol('kRestorePatches')\n\nfunction recordRawHeader(\n headers: Headers,\n args: HeaderTuple,\n behavior: SetHeaderBehavior\n) {\n ensureRawHeadersSymbol(headers, [])\n const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders\n\n if (behavior === 'set') {\n // When recording a set header, ensure we remove any matching existing headers.\n for (let index = rawHeaders.length - 1; index >= 0; index--) {\n if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) {\n rawHeaders.splice(index, 1)\n }\n }\n }\n\n rawHeaders.push(args)\n}\n\n/**\n * Define the raw headers symbol on the given `Headers` instance.\n * If the symbol already exists, this function does nothing.\n */\nfunction ensureRawHeadersSymbol(\n headers: Headers,\n rawHeaders: RawHeaders\n): void {\n if (Reflect.has(headers, kRawHeaders)) {\n return\n }\n\n defineRawHeadersSymbol(headers, rawHeaders)\n}\n\n/**\n * Define the raw headers symbol on the given `Headers` instance.\n * If the symbol already exists, it gets overridden.\n */\nfunction defineRawHeadersSymbol(headers: Headers, rawHeaders: RawHeaders) {\n Object.defineProperty(headers, kRawHeaders, {\n value: rawHeaders,\n enumerable: false,\n // Mark the symbol as configurable so its value can be overridden.\n // Overrides happen when merging raw headers from multiple sources.\n // E.g. new Request(new Request(url, { headers }), { headers })\n configurable: true,\n })\n}\n\n/**\n * Patch the global `Headers` class to store raw headers.\n * This is for compatibility with `IncomingMessage.prototype.rawHeaders`.\n *\n * @note Node.js has their own raw headers symbol but it\n * only records the first header name in case of multi-value headers.\n * Any other headers are normalized before comparing. This makes it\n * incompatible with the `rawHeaders` format.\n *\n * let h = new Headers()\n * h.append('X-Custom', 'one')\n * h.append('x-custom', 'two')\n * h[Symbol('headers map')] // Map { 'X-Custom' => 'one, two' }\n */\nexport function recordRawFetchHeaders(): () => void {\n // Prevent patching the Headers prototype multiple times.\n if (Reflect.get(Headers, kRestorePatches)) {\n return Reflect.get(Headers, kRestorePatches)\n }\n\n const {\n Headers: OriginalHeaders,\n Request: OriginalRequest,\n Response: OriginalResponse,\n } = globalThis\n const { set, append, delete: headersDeleteMethod } = Headers.prototype\n\n Object.defineProperty(Headers, kRestorePatches, {\n value: () => {\n Headers.prototype.set = set\n Headers.prototype.append = append\n Headers.prototype.delete = headersDeleteMethod\n globalThis.Headers = OriginalHeaders\n\n globalThis.Request = OriginalRequest\n globalThis.Response = OriginalResponse\n\n Object.setPrototypeOf(FetchRequest, OriginalRequest)\n Object.setPrototypeOf(FetchRequest.prototype, OriginalRequest.prototype)\n Object.setPrototypeOf(FetchResponse, OriginalResponse)\n Object.setPrototypeOf(FetchResponse.prototype, OriginalResponse.prototype)\n\n Reflect.deleteProperty(Headers, kRestorePatches)\n },\n enumerable: false,\n /**\n * @note Mark this property as configurable\n * so we can delete it using `Reflect.delete` during cleanup.\n */\n configurable: true,\n })\n\n Object.defineProperty(globalThis, 'Headers', {\n enumerable: true,\n writable: true,\n value: new Proxy(Headers, {\n construct(target, args, newTarget) {\n const headersInit = args[0] || []\n\n if (\n headersInit instanceof Headers &&\n Reflect.has(headersInit, kRawHeaders)\n ) {\n // Ensure each header tuple has exactly 2 elements (name, value).\n // Node.js 24+ may have stored tuples with extra internal arguments.\n const rawHeadersFromInit = Reflect.get(\n headersInit,\n kRawHeaders\n ) as RawHeaders\n const sanitizedHeaders = rawHeadersFromInit.map(\n (tuple): HeaderTuple => [tuple[0], tuple[1]]\n )\n const headers = Reflect.construct(\n target,\n [sanitizedHeaders],\n newTarget\n )\n ensureRawHeadersSymbol(headers, [\n /**\n * @note Spread the retrieved headers to clone them.\n * This prevents multiple Headers instances from pointing\n * at the same internal \"rawHeaders\" array.\n */\n ...sanitizedHeaders,\n ])\n return headers\n }\n\n const headers = Reflect.construct(target, args, newTarget)\n\n // Request/Response constructors will set the symbol\n // upon creating a new instance, using the raw developer\n // input as the raw headers. Skip the symbol altogether\n // in those cases because the input to Headers will be normalized.\n if (!Reflect.has(headers, kRawHeaders)) {\n const rawHeadersInit = Array.isArray(headersInit)\n ? headersInit\n : Object.entries(headersInit)\n ensureRawHeadersSymbol(headers, rawHeadersInit)\n }\n\n return headers\n },\n }),\n })\n\n Headers.prototype.set = new Proxy(Headers.prototype.set, {\n apply(target, thisArg, args: HeaderTuple) {\n // Use only the first two arguments (name, value) to record raw headers.\n // Node.js 24+ may pass additional internal arguments that should not\n // be included in the raw headers array.\n recordRawHeader(thisArg, [args[0], args[1]], 'set')\n return Reflect.apply(target, thisArg, args)\n },\n })\n\n Headers.prototype.append = new Proxy(Headers.prototype.append, {\n apply(target, thisArg, args: HeaderTuple) {\n // Use only the first two arguments (name, value) to record raw headers.\n // Node.js 24+ may pass additional internal arguments that should not\n // be included in the raw headers array.\n recordRawHeader(thisArg, [args[0], args[1]], 'append')\n return Reflect.apply(target, thisArg, args)\n },\n })\n\n Headers.prototype.delete = new Proxy(Headers.prototype.delete, {\n apply(target, thisArg, args: [string]) {\n const rawHeaders = Reflect.get(thisArg, kRawHeaders) as RawHeaders\n\n if (rawHeaders) {\n for (let index = rawHeaders.length - 1; index >= 0; index--) {\n if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) {\n rawHeaders.splice(index, 1)\n }\n }\n }\n\n return Reflect.apply(target, thisArg, args)\n },\n })\n\n Object.defineProperty(globalThis, 'Request', {\n enumerable: true,\n writable: true,\n value: new Proxy(Request, {\n construct(target, args, newTarget) {\n const request = Reflect.construct(target, args, newTarget)\n const inferredRawHeaders: RawHeaders = []\n\n // Infer raw headers from a `Request` instance used as init.\n if (typeof args[0] === 'object' && args[0].headers != null) {\n inferredRawHeaders.push(...inferRawHeaders(args[0].headers))\n }\n\n // Infer raw headers from the \"headers\" init argument.\n if (typeof args[1] === 'object' && args[1].headers != null) {\n inferredRawHeaders.push(...inferRawHeaders(args[1].headers))\n }\n\n if (inferredRawHeaders.length > 0) {\n ensureRawHeadersSymbol(request.headers, inferredRawHeaders)\n }\n\n return request\n },\n }),\n })\n\n Object.defineProperty(globalThis, 'Response', {\n enumerable: true,\n writable: true,\n value: new Proxy(Response, {\n construct(target, args, newTarget) {\n const response = Reflect.construct(target, args, newTarget)\n\n if (typeof args[1] === 'object' && args[1].headers != null) {\n ensureRawHeadersSymbol(\n response.headers,\n inferRawHeaders(args[1].headers)\n )\n }\n\n return response\n },\n }),\n })\n\n /**\n * Re-parent FetchRequest/FetchResponse so their `super()` calls go\n * through the proxied globalThis.Request/Response above. Without this,\n * FetchRequest extends the statically-captured (original) Request,\n * bypassing the construct proxy that records raw headers.\n */\n Object.setPrototypeOf(FetchRequest, globalThis.Request)\n Object.setPrototypeOf(FetchRequest.prototype, globalThis.Request.prototype)\n Object.setPrototypeOf(FetchResponse, globalThis.Response)\n Object.setPrototypeOf(FetchResponse.prototype, globalThis.Response.prototype)\n\n return restoreHeadersPrototype\n}\n\nexport function restoreHeadersPrototype() {\n if (!Reflect.get(Headers, kRestorePatches)) {\n return\n }\n\n Reflect.get(Headers, kRestorePatches)()\n}\n\nexport function getRawFetchHeaders(headers: Headers): RawHeaders {\n // If the raw headers recording failed for some reason,\n // use the normalized header entries instead.\n if (!Reflect.has(headers, kRawHeaders)) {\n return Array.from(headers.entries())\n }\n\n const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders\n return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries())\n}\n\n/**\n * Infers the raw headers from the given `HeadersInit` provided\n * to the Request/Response constructor.\n *\n * If the `init.headers` is a Headers instance, use it directly.\n * That means the headers were created standalone and already have\n * the raw headers stored.\n * If the `init.headers` is a HeadersInit, create a new Headers\n * instance out of it.\n */\nfunction inferRawHeaders(headers: HeadersInit): RawHeaders {\n if (headers instanceof Headers) {\n return Reflect.get(headers, kRawHeaders) || []\n }\n\n return Reflect.get(new Headers(headers), kRawHeaders)\n}\n\nexport function copyRawHeaders(source: Headers, destination: Headers): void {\n const rawHeaders = [...getRawFetchHeaders(source)]\n\n if (rawHeaders.length === 0) {\n return\n }\n\n /**\n * @note Add headers from trhe destination that raw headers from the source\n * don't have. Undici automatically appends a \"Content-Type\" header for responses\n * and, for some reason, that change is not recorded. This preserves it.\n */\n for (const [name, value] of destination) {\n if (\n rawHeaders.every(\n (header) => header[0].toLowerCase() !== name.toLowerCase()\n )\n ) {\n rawHeaders.push([name, value])\n }\n }\n\n defineRawHeadersSymbol(destination, rawHeaders)\n}\n","/**\n * Returns the value behind the symbol with the given name.\n */\nexport function getValueBySymbol<T>(\n symbolName: string,\n source: object\n): T | undefined {\n const ownSymbols = Object.getOwnPropertySymbols(source)\n\n const symbol = ownSymbols.find((symbol) => {\n return symbol.description === symbolName\n })\n\n if (symbol) {\n return Reflect.get(source, symbol)\n }\n\n return\n}\n","/**\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 './is-object'\nimport { isPropertyAccessible } from './is-property-accessible'\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 * A key on the error a mocked `Response.error()` destroys the socket\n * with, referencing that error response. Allows the client-side\n * interceptors (e.g. fetch) to surface the error response to the\n * consumer instead of the internal socket error.\n */\nexport const kErrorResponse = Symbol('kErrorResponse')\n\n/**\n * Get the mocked error response that caused the given error, if any.\n */\nexport function getErrorResponse(error: unknown): ResponseError | undefined {\n if (\n error instanceof Error &&\n kErrorResponse in error &&\n isResponseError(error[kErrorResponse])\n ) {\n return error[kErrorResponse]\n }\n\n return undefined\n}\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","import { copyRawHeaders } from '../interceptors/ClientRequest/utils/record-raw-headers'\nimport { getValueBySymbol } from './get-value-by-symbol'\nimport { isResponseError } from './response-utils'\n\ninterface UndiciRequestState extends RequestInit {}\n\ninterface FetchRequestInit extends Omit<RequestInit, 'mode'> {\n mode?: RequestMode | 'websocket' | 'webtransport'\n duplex?: 'half' | 'full'\n}\n\nexport class FetchRequest extends Request {\n static #resolveProperty<T extends keyof FetchRequestInit & keyof Request>(\n input: RequestInfo | URL,\n init: FetchRequestInit = {},\n key: T\n ): FetchRequestInit[T] {\n return init[key] ?? (input instanceof Request ? input[key] : undefined)\n }\n\n /**\n * Check if the given request method is configurable.\n * @see https://fetch.spec.whatwg.org/#methods\n */\n static isConfigurableMethod(method: string): boolean {\n return method !== 'CONNECT' && method !== 'TRACE' && method !== 'TRACK'\n }\n\n static isMethodWithBody(method: string): boolean {\n return (\n method !== 'HEAD' &&\n method !== 'GET' &&\n FetchRequest.isConfigurableMethod(method)\n )\n }\n\n /**\n * Check if the given request `mode` is configurable.\n * @see https://fetch.spec.whatwg.org/#concept-request-mode\n */\n static isConfigurableMode(mode: string): boolean {\n return (\n mode !== 'navigate' && mode !== 'websocket' && mode !== 'webtransport'\n )\n }\n\n constructor(input: URL | RequestInfo, init?: FetchRequestInit) {\n const method = FetchRequest.#resolveProperty(input, init, 'method') || 'GET'\n const safeMethod = FetchRequest.isConfigurableMethod(method)\n ? method\n : 'GET'\n\n const hasExplicitBody = init != null && 'body' in init\n\n /**\n * Only include `body` in the super init when it needs to be overridden.\n * When `input` is a Request and no explicit body is in `init`, let the\n * Request constructor handle body transfer naturally so it properly\n * marks the original request's body as consumed (bodyUsed = true).\n */\n const bodyInit: { body?: BodyInit | null } = !FetchRequest.isMethodWithBody(\n method\n )\n ? { body: undefined }\n : hasExplicitBody\n ? { body: init.body }\n : {}\n\n const mode =\n (FetchRequest.#resolveProperty(input, init, 'mode') as RequestMode) ??\n undefined\n const safeMode = FetchRequest.isConfigurableMode(mode) ? mode : undefined\n\n super(input, {\n ...(init || {}),\n method: safeMethod,\n mode: safeMode,\n // @ts-expect-error Untyped Node.js property.\n duplex:\n init?.duplex ??\n (FetchRequest.isMethodWithBody(method) ? 'half' : undefined),\n ...bodyInit,\n })\n\n if (method !== safeMethod) {\n this.#setInternalProperty('method', method)\n }\n\n if (method === 'CONNECT') {\n const url = new URL(input instanceof Request ? input.url : input)\n\n let authority: string\n\n /**\n * @note Node.js has a bug parsing raw CONNECT requests URLs like\n * \"http://127.0.0.1:1337/localhost:80\". It would treat \"localhost:\" as a protocol.\n */\n if (url.protocol === 'localhost:') {\n authority = url.href\n } else {\n authority = url.pathname.replace(/^\\/+/, '')\n }\n\n /**\n * @note Define \"url\" as a getter because Undici uses their own\n * logic to resolve the \"request.url\" property. Simply reassigning\n * its value doesn't do anything. This is a destructive action\n * but it's safe because \"CONNECT\" requests are forbidden per fetch.\n */\n Object.defineProperty(this, 'url', {\n get: () => authority,\n enumerable: true,\n configurable: true,\n })\n }\n\n if (mode != null && mode !== safeMode) {\n this.#setInternalProperty('mode', mode)\n }\n }\n\n #setInternalProperty<T extends keyof Request>(\n key: T,\n value: Request[T]\n ): void {\n const internalState = getValueBySymbol<UndiciRequestState>('state', this)\n\n if (internalState) {\n Reflect.set(internalState, key, value)\n } else {\n Object.defineProperty(this, key, {\n value,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n }\n}\n\nexport interface FetchResponseInit extends ResponseInit {\n url?: string\n}\n\ninterface UndiciResponseState {\n aborted: boolean\n rangeRequested: boolean\n timingAllowPassed: boolean\n requestIncludesCredentials: boolean\n type: ResponseType\n status: number\n statusText: string\n timingInfo: unknown\n cacheState: unknown\n headersList: Record<symbol, Map<string, unknown>>\n urlList: Array<URL>\n body?: {\n stream: ReadableStream\n source: unknown\n length: number\n }\n}\n\nconst kStatus = Symbol('kStatus')\nconst kUrl = Symbol('kUrl')\n\nexport class FetchResponse extends Response {\n static from(response: Response, init?: FetchResponseInit): FetchResponse {\n if (response instanceof FetchResponse) {\n return response\n }\n\n if (isResponseError(response)) {\n return response\n }\n\n const fetchResponse = new FetchResponse(response.body, {\n url: init?.url ?? response.url,\n status: init?.status || response.status,\n statusText: init?.statusText ?? response.statusText,\n headers: init?.headers ?? response.headers,\n })\n\n copyRawHeaders(response.headers, fetchResponse.headers)\n\n return fetchResponse\n }\n\n /**\n * Response status codes for responses that cannot have body.\n * @see https://fetch.spec.whatwg.org/#statuses\n */\n static readonly STATUS_CODES_WITHOUT_BODY = [101, 103, 204, 205, 304]\n\n static readonly STATUS_CODES_WITH_REDIRECT = [301, 302, 303, 307, 308]\n\n static isConfigurableStatusCode(status: number): boolean {\n return status >= 200 && status <= 599\n }\n\n static isRedirectResponse(status: number): boolean {\n return FetchResponse.STATUS_CODES_WITH_REDIRECT.includes(status)\n }\n\n /**\n * Returns a boolean indicating whether the given response status\n * code represents a response that can have a body.\n */\n static isResponseWithBody(status: number): boolean {\n return !FetchResponse.STATUS_CODES_WITHOUT_BODY.includes(status)\n }\n\n static setStatus(status: number, response: Response): void {\n /**\n * @note Undici keeps an internal \"Symbol(state)\" that holds\n * the actual value of response status. Update that in Node.js.\n */\n const internalState = getValueBySymbol<UndiciResponseState>(\n 'state',\n response\n )\n\n if (internalState) {\n internalState.status = status\n } else {\n Object.defineProperty(response, 'status', {\n value: status,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n\n Object.defineProperty(response, kStatus, {\n value: status,\n enumerable: false,\n })\n }\n\n static setUrl(url: string | undefined, response: Response): void {\n if (!url || url === 'about:' || !URL.canParse(url)) {\n return\n }\n\n const state = getValueBySymbol<UndiciResponseState>('state', response)\n\n if (state) {\n // In Undici, push the URL to the internal list of URLs.\n // This will respect the `response.url` getter logic correctly.\n state.urlList.push(new URL(url))\n } else {\n // In other libraries, redefine the `url` property directly.\n Object.defineProperty(response, 'url', {\n value: url,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n\n Object.defineProperty(response, kUrl, {\n value: url,\n enumerable: false,\n })\n }\n\n /**\n * Parses the given raw HTTP headers into a Fetch API `Headers` instance.\n */\n static parseRawHeaders(rawHeaders: Array<string>): Headers {\n const headers = new Headers()\n\n for (let line = 0; line < rawHeaders.length; line += 2) {\n headers.append(rawHeaders[line], rawHeaders[line + 1])\n }\n\n return headers\n }\n\n /**\n * Safely clones the given `Response`.\n * Coerces response clone exceptions into 500 mocked responses.\n * Handy in the environments that introduce arbitrary response\n * cloning restrictions, like \"101 Switching Protocols\" cloning\n * in \"miniflare\".\n */\n static clone(response: Response): Response {\n try {\n const clone = response.clone()\n return clone\n } catch (error) {\n return Response.json(\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : {},\n {\n status: 500,\n statusText: 'Unclonable Response',\n }\n )\n }\n }\n\n #status?: number\n #url?: string\n\n constructor(body?: BodyInit | null, init: FetchResponseInit = {}) {\n const status = init.status ?? 200\n const safeStatus = FetchResponse.isConfigurableStatusCode(status)\n ? status\n : 200\n const finalBody = FetchResponse.isResponseWithBody(status) ? body : null\n\n super(finalBody, {\n status: safeStatus,\n statusText: init.statusText,\n headers: init.headers,\n })\n\n /**\n * Since Node.js v24, Undici stores the Response state in an inaccessible field \"#state\".\n * Forward the modified status/URL to the cloned response manually.\n * @see https://github.com/nodejs/undici/blob/f734c87280e626c75f59aad55b65eb6a89cef392/lib/web/fetch/response.js#L242\n */\n if (status !== safeStatus) {\n FetchResponse.setStatus(status, this)\n }\n\n FetchResponse.setUrl(init.url, this)\n }\n\n public clone() {\n const clonedResponse = super.clone()\n\n const customStatus = Reflect.get(this, kStatus) as number | undefined\n\n if (customStatus) {\n FetchResponse.setStatus(customStatus, clonedResponse)\n }\n\n const customUrl = Reflect.get(this, kUrl) as string | undefined\n\n if (customUrl) {\n FetchResponse.setUrl(customUrl, clonedResponse)\n }\n\n return clonedResponse\n }\n}\n"],"mappings":"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"}
import { i as formatRequest } from "./create-request-id-Bk5YX1AM.js";
import { a as isResponseLike, c as RequestController, i as isResponseError, l as InterceptorError, o as isObject, r as createServerErrorResponse } from "./fetch-utils-CUOrwEQf.js";
import { TypedEvent } from "rettime";
import { until } from "@open-draft/until";
//#region src/events/http.ts
var HttpRequestEvent = class extends TypedEvent {
constructor(data) {
super(...["request", {}]);
this.request = data.request;
this.requestId = data.requestId;
this.initiator = data.initiator;
this.controller = data.controller;
}
};
var HttpResponseEvent = class extends TypedEvent {
constructor(data) {
super(...["response", {}]);
this.response = data.response;
this.responseType = data.responseType;
this.request = data.request;
this.requestId = data.requestId;
this.initiator = data.initiator;
}
};
var UnhandledHttpException = class extends TypedEvent {
constructor(data) {
super(...["unhandledException", {}]);
this.error = data.error;
this.request = data.request;
this.requestId = data.requestId;
this.initiator = data.initiator;
this.controller = data.controller;
}
};
//#endregion
//#region src/utils/is-node-like-error.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/handle-request.ts
async function handleRequest(options) {
if (options.logger?.isEnabled("default")) formatRequest(options.request).then((message) => {
options.logger?.info("[%s] %s", options.requestId, message);
});
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 resultError;
if (isNodeLikeError(error)) {
await options.controller.errorWith(error);
return true;
}
if (error instanceof Response) return await handleResponse(error);
return false;
};
const requestAbortPromise = Promise.withResolvers();
let requestAbortReason;
let isRequestAborted = false;
const onAbort = () => {
isRequestAborted = true;
requestAbortReason = options.request.signal?.reason;
requestAbortPromise.reject(requestAbortReason);
};
/**
* @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", onAbort, { once: true });
}
const [resultError] = await until(async () => {
const requestEvent = new HttpRequestEvent({
initiator: options.initiator,
requestId: options.requestId,
request: options.request,
controller: options.controller
});
const requestListenersPromise = options.emitter.emitAsPromise(requestEvent);
await Promise.race([
requestAbortPromise.promise,
requestListenersPromise,
options.controller.handled
]);
/**
* @note If the "request" listener has replaced the request instance,
* propagate that mutation back to the underlying insterceptor.
* This happens with XMLHttpRequest that replaces request instances
* to correctly reflect the "withCredentials" option on the Fetch API request.
*/
if (requestEvent.request !== options.request) options.request = requestEvent.request;
});
options.request.signal?.removeEventListener("abort", onAbort);
if (isRequestAborted) {
await options.controller.errorWith(requestAbortReason);
return;
}
if (resultError) {
if (await handleResponseError(resultError)) return;
if (options.emitter.listenerCount("unhandledException") > 0) {
const unhandledExceptionController = new RequestController(options.request, {
/**
* @note Intentionally empty passthrough handle.
* This controller is created within another controller and we only need
* to know if `unhandledException` listeners handled the 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 options.emitter.emitAsPromise(new UnhandledHttpException({
initiator: options.initiator,
error: resultError,
request: options.request,
requestId: options.requestId,
controller: unhandledExceptionController
}));
if (unhandledExceptionController.readyState !== RequestController.PENDING) return;
}
await options.controller.respondWith(createServerErrorResponse(resultError));
return;
}
if (options.controller.readyState === RequestController.PENDING) return await options.controller.passthrough();
return options.controller.handled;
}
//#endregion
export { HttpResponseEvent as n, handleRequest as t };
//# sourceMappingURL=handle-request-CqVvBhcw.js.map
{"version":3,"file":"handle-request-CqVvBhcw.js","names":[],"sources":["../../src/events/http.ts","../../src/utils/is-node-like-error.ts","../../src/utils/handle-request.ts"],"sourcesContent":["import { TypedEvent } from 'rettime'\nimport type { RequestController } from '../request-controller'\n\nexport interface HttpRequestEventData {\n request: Request\n requestId: string\n initiator: unknown\n controller: RequestController\n}\n\nexport class HttpRequestEvent<\n DataType extends HttpRequestEventData = HttpRequestEventData,\n> extends TypedEvent<DataType, void, 'request'> {\n public request: Request\n public requestId: string\n public initiator: unknown\n public controller: RequestController\n\n constructor(data: DataType) {\n super(...(['request', {}] as any))\n\n this.request = data.request\n this.requestId = data.requestId\n this.initiator = data.initiator\n this.controller = data.controller\n }\n}\n\nexport type HttpResponseType = 'mock' | 'original'\n\ninterface HttpResponseEventData {\n response: Response\n responseType: HttpResponseType\n request: Request\n requestId: string\n initiator: unknown\n}\n\nexport class HttpResponseEvent<\n DataType extends HttpResponseEventData = HttpResponseEventData,\n> extends TypedEvent<DataType, void, 'response'> {\n public response: Response\n public responseType: HttpResponseType\n public request: Request\n public requestId: string\n public initiator: unknown\n\n constructor(data: DataType) {\n super(...(['response', {}] as any))\n\n this.response = data.response\n this.responseType = data.responseType\n this.request = data.request\n this.requestId = data.requestId\n this.initiator = data.initiator\n }\n}\n\ninterface UnhandledHttpExceptionEventData {\n error: unknown\n request: Request\n requestId: string\n initiator: unknown\n controller: RequestController\n}\n\nexport class UnhandledHttpException<\n DataType extends UnhandledHttpExceptionEventData =\n UnhandledHttpExceptionEventData,\n> extends TypedEvent<DataType, void, 'unhandledException'> {\n public error: unknown\n public request: Request\n public requestId: string\n public initiator: unknown\n public controller: RequestController\n\n constructor(data: DataType) {\n super(...(['unhandledException', {}] as any))\n\n this.error = data.error\n this.request = data.request\n this.requestId = data.requestId\n this.initiator = data.initiator\n this.controller = data.controller\n }\n}\n\nexport type HttpRequestEventMap = {\n request: HttpRequestEvent\n response: HttpResponseEvent\n unhandledException: UnhandledHttpException\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 'rettime'\nimport { until } from '@open-draft/until'\nimport {\n HttpRequestEvent,\n HttpRequestEventData,\n UnhandledHttpException,\n type HttpRequestEventMap,\n} from '../events/http'\nimport { RequestController } from '../request-controller'\nimport {\n createServerErrorResponse,\n isResponseError,\n isResponseLike,\n} from './response-utils'\nimport { InterceptorError } from '../interceptor-error'\nimport { isNodeLikeError } from './is-node-like-error'\nimport { isObject } from './is-object'\nimport { formatRequest, type Logger } from './logger'\n\nexport interface HandleRequestOptions {\n initiator: unknown\n requestId: string\n request: Request\n emitter: Emitter<HttpRequestEventMap>\n controller: RequestController\n logger?: Logger\n}\n\nexport async function handleRequest(\n options: HandleRequestOptions\n): Promise<void> {\n if (options.logger?.isEnabled('default')) {\n void formatRequest(options.request).then((message) => {\n options.logger?.info('[%s] %s', options.requestId, message)\n })\n }\n\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 resultError\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 const requestAbortPromise = Promise.withResolvers<void>()\n let requestAbortReason: unknown\n let isRequestAborted = false\n const onAbort = () => {\n isRequestAborted = true\n requestAbortReason = options.request.signal?.reason\n requestAbortPromise.reject(requestAbortReason)\n }\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('abort', onAbort, { once: true })\n }\n\n const [resultError] = 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 requestEventData: HttpRequestEventData = {\n initiator: options.initiator,\n requestId: options.requestId,\n request: options.request,\n controller: options.controller,\n }\n const requestEvent = new HttpRequestEvent(requestEventData)\n const requestListenersPromise = options.emitter.emitAsPromise(requestEvent)\n\n await Promise.race([\n // Short-circuit the request handling promise if the request gets aborted.\n requestAbortPromise.promise,\n requestListenersPromise,\n options.controller.handled,\n ])\n\n /**\n * @note If the \"request\" listener has replaced the request instance,\n * propagate that mutation back to the underlying insterceptor.\n * This happens with XMLHttpRequest that replaces request instances\n * to correctly reflect the \"withCredentials\" option on the Fetch API request.\n */\n if (requestEvent.request !== options.request) {\n options.request = requestEvent.request\n }\n })\n\n options.request.signal?.removeEventListener('abort', onAbort)\n\n // Handle the request being aborted while waiting for the request listeners.\n if (isRequestAborted) {\n await options.controller.errorWith(requestAbortReason)\n return\n }\n\n if (resultError) {\n // Handle the error during the request listener execution.\n // These can be thrown responses or request errors.\n if (await handleResponseError(resultError)) {\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 options.emitter.emitAsPromise(\n new UnhandledHttpException({\n initiator: options.initiator,\n error: resultError,\n request: options.request,\n requestId: options.requestId,\n controller: unhandledExceptionController,\n })\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(resultError)\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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"}
import { n as Interceptor, r as createLogger, t as createRequestId } from "./create-request-id-Bk5YX1AM.js";
import { c as RequestController, i as isResponseError, n as FetchResponse, t as FetchRequest } from "./fetch-utils-CUOrwEQf.js";
import { n as encodeBuffer, r as toArrayBuffer, t as decodeBuffer } from "./buffer-utils-DJj7YzLG.js";
import { n as HttpResponseEvent, t as handleRequest } from "./handle-request-CqVvBhcw.js";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./has-configurable-global-Cew-dYqk.js";
import { invariant } from "outvariant";
import { until } from "@open-draft/until";
//#region src/interceptors/XMLHttpRequest/utils/concat-array-buffer.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/event-polyfill.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/progress-event-polyfill.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/create-event.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/find-property-source.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/create-proxy.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 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next);
return next();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/is-dom-parser-supported-type.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/parse-json.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/create-response.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/get-body-byte-length.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/xml-http-request-controller.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const kFetchRequest = Symbol("kFetchRequest");
const MAX_REDIRECTS = 20;
/**
* 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.sync = false;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.redirectCount = 0;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = /* @__PURE__ */ new Uint8Array();
this.request = createProxy(initialRequest, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url, async] = args;
this.sync = !(async ?? true);
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger.verbose("open %s %s", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.verbose("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.verbose("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
if (this.sync) {
console.warn(`Failed to intercept an XMLHttpRequest (${this.method} ${this.url}): synchronous requests are not supported. This request will be performed as-is.`);
return invoke();
}
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,
responseType: this[kIsRequestHandled] ? "mock" : "original",
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.verbose("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
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.verbose("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.verbose("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.verbose("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;
this.logger.verbose("responding with a mocked response: %d %s", response.status, response.statusText);
FetchResponse.setUrl(this.url.href, response);
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
}
});
this.trigger("loadstart", this.request, {
loaded: 0,
total: 0
});
let requestBodyTransmitted = 0;
let uploadComplete = false;
if (this[kFetchRequest]) {
const requestBodyLength = await getBodyByteLength(this[kFetchRequest].clone());
if (!uploadComplete) this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: requestBodyLength
});
const processRequestBodyChunkLength = (bytesLength) => {
requestBodyTransmitted += bytesLength;
if (requestBodyTransmitted < requestBodyLength) this.trigger("progress", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
};
const processRequestEndOfBody = () => {
uploadComplete = true;
this.trigger("progress", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
};
if (this[kFetchRequest]?.body != null) {
const reader = this[kFetchRequest].body.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) {
processRequestEndOfBody();
break;
}
processRequestBodyChunkLength(value.byteLength);
}
} else processRequestEndOfBody();
}
let timedOut = false;
const responseReadController = new AbortController();
const requestErrorSteps = (event) => {
this.setReadyState(this.request.DONE);
if (!uploadComplete) {
this.trigger(event, this.request.upload, {
loaded: 0,
total: 0
});
this.trigger("loadend", this.request.upload, {
loaded: 0,
total: 0
});
}
this.trigger(event, this.request, {
loaded: 0,
total: 0
});
this.trigger("loadend", this.request, {
loaded: 0,
total: 0
});
};
const processResponse = async (response) => {
const handleErrors = () => {
if (timedOut) requestErrorSteps("timeout");
else if (responseReadController.signal.aborted) requestErrorSteps("abort");
else if (isResponseError(response)) requestErrorSteps("error");
};
handleErrors();
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
if (!this.request.responseURL) define(this.request, "responseURL", response.url);
if (isResponseError(response)) return;
/**
* @note The response body length is derived ONLY from the "content-length" header.
* If that response header is not set, the "total" in all progress events must be 0.
*/
const responseBodyLength = Number(response.headers.get("content-length") ?? "0");
this.setReadyState(this.request.HEADERS_RECEIVED);
let receivedBytes = 0;
let lastReceivedResponseBytesAt = performance.now();
const processResponseBodyChunk = (bytesLength) => {
receivedBytes += bytesLength;
const now = performance.now();
const shouldBuffer = now - lastReceivedResponseBytesAt <= 60 && receivedBytes < responseBodyLength;
lastReceivedResponseBytesAt = now;
if (shouldBuffer) return;
if (this.request.readyState === this.request.HEADERS_RECEIVED) this.setReadyState(this.request.LOADING, false);
this.trigger("readystatechange", this.request);
this.trigger("progress", this.request, {
loaded: receivedBytes,
total: responseBodyLength
});
};
const processResponseBodyError = () => {
requestErrorSteps("error");
};
const processResponseEndOfBody = async () => {
handleErrors();
if (isResponseError(response)) return;
let transmitted = receivedBytes;
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: transmitted,
total: responseBodyLength
});
this.trigger("loadend", this.request, {
loaded: transmitted,
total: responseBodyLength
});
};
if (response.body == null) processResponseEndOfBody();
else {
const reader = response.body.getReader();
while (true) {
if (responseReadController.signal.aborted) break;
try {
const { value, done } = await reader.read();
if (done) {
processResponseEndOfBody();
return;
}
processResponseBodyChunk(value.byteLength);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
} catch {
processResponseBodyError();
}
}
}
};
const [redirectError, finalResponse] = await until(() => {
return this.followRedirects(response);
});
if (redirectError) return;
processResponse(finalResponse);
if (this.request.timeout) setTimeout(() => {
if (this.request.readyState !== this.request.DONE) {
timedOut = true;
responseReadController.abort();
}
}, this.request.timeout);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.verbose("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.verbose("headers not received yet, returning null");
return null;
}
const headerValue = finalResponse.headers.get(args[0]);
this.logger.verbose("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.verbose("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.verbose("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(finalResponse.headers).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.verbose("resolved all response headers to", allHeaders);
return allHeaders;
} });
}
responseBufferToText() {
return decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.verbose("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.verbose("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = toArrayBuffer(this.responseBuffer);
this.logger.verbose("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.verbose("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.verbose("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.verbose("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;
}
async followRedirects(response) {
const redirectLocation = response.headers.get("location");
if (!redirectLocation || !FetchResponse.isRedirectResponse(response.status)) return response;
this.redirectCount++;
if (this.redirectCount > MAX_REDIRECTS) throw new Error("Too many redirects");
const redirectUrl = new URL(redirectLocation, location.href);
const redirectMethod = FetchResponse.isResponseWithBody(response.status) ? this.method : "GET";
const redirectResponse = await new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.responseType = this.request.responseType;
request.addEventListener("load", () => {
this.url = new URL(request.responseURL);
resolve(createResponse(request, request.response));
});
request.addEventListener("error", () => {
this.errorWith();
reject(/* @__PURE__ */ new Error("Redirect request failed"));
});
request.open(redirectMethod, redirectUrl.href);
request.send();
});
return this.followRedirects(redirectResponse);
}
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.verbose("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, triggerReadyStateChangeEvent = true) {
this.logger.verbose("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.verbose("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.verbose("set readyState to: %d", nextReadyState);
if (!triggerReadyStateChangeEvent) return;
if (nextReadyState !== this.request.UNSENT) {
this.logger.verbose("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.verbose("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.verbose("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.verbose("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.verbose("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,
/**
* @see https://xhr.spec.whatwg.org/#cross-origin-credentials
*/
credentials: this.request.withCredentials ? "include" : "same-origin",
body: 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();
} }));
this.logger.verbose("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/xml-http-request-proxy.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.verbose("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.verbose("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.verbose("request errored %o", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
}, {
logger,
requestId
});
this.logger.verbose("awaiting mocked response");
this.logger.verbose("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await handleRequest({
initiator: this.request,
request,
requestId,
controller,
emitter,
logger
});
};
xhrRequestController.onResponse = async function({ response, responseType, request, requestId }) {
this.logger.verbose("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
await emitter.emitAsPromise(new HttpResponseEvent({
initiator: this.request,
response,
responseType,
request,
requestId
}));
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/web.ts
const logger = createLogger("xhr");
var XMLHttpRequestInterceptor = class extends Interceptor {
static {
this.symbol = Symbol.for("xhr-interceptor");
}
predicate() {
return hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
logger.verbose("patching \"XMLHttpRequest\"...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger
});
}));
logger.verbose("global \"XMLHttpRequest\" patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
export { XMLHttpRequestInterceptor as t };
//# sourceMappingURL=web-BcKSQghf.js.map
{"version":3,"file":"web-BcKSQghf.js","names":[],"sources":["../../src/interceptors/XMLHttpRequest/utils/concat-array-buffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/event-polyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/progress-event-polyfill.ts","../../src/interceptors/XMLHttpRequest/utils/create-event.ts","../../src/utils/find-property-source.ts","../../src/utils/create-proxy.ts","../../src/interceptors/XMLHttpRequest/utils/is-dom-parser-supported-type.ts","../../src/utils/parse-json.ts","../../src/interceptors/XMLHttpRequest/utils/create-response.ts","../../src/interceptors/XMLHttpRequest/utils/get-body-byte-length.ts","../../src/interceptors/XMLHttpRequest/xml-http-request-controller.ts","../../src/interceptors/XMLHttpRequest/xml-http-request-proxy.ts","../../src/interceptors/XMLHttpRequest/web.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n) {\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 './event-polyfill'\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/event-polyfill'\nimport { ProgressEventPolyfill } from '../polyfills/progress-event-polyfill'\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 './find-property-source'\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/fetch-utils'\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 { until } from '@open-draft/until'\nimport { invariant } from 'outvariant'\nimport type { Logger } from '../../utils/logger'\nimport type { HttpResponseType } from '../../events/http'\nimport { concatArrayBuffer } from './utils/concat-array-buffer'\nimport { createEvent } from './utils/create-event'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/buffer-utils'\nimport { createProxy } from '../../utils/create-proxy'\nimport { isDomParserSupportedType } from './utils/is-dom-parser-supported-type'\nimport { parseJson } from '../../utils/parse-json'\nimport { createResponse } from './utils/create-response'\nimport { createRequestId } from '../../create-request-id'\nimport { getBodyByteLength } from './utils/get-body-byte-length'\nimport { FetchRequest, FetchResponse } from '../../utils/fetch-utils'\nimport { isResponseError } from '../../utils/response-utils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst kFetchRequest = Symbol('kFetchRequest')\nconst MAX_REDIRECTS = 20\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 responseType: HttpResponseType\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n\n private sync: boolean = false\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array<ArrayBuffer>\n private redirectCount: number\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.redirectCount = 0\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 methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url, async] = args as [\n string,\n string | undefined,\n boolean | undefined,\n ]\n\n this.sync = !(async ?? true)\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.verbose('open %s %s', 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.verbose('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.verbose('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 if (this.sync) {\n console.warn(\n `Failed to intercept an XMLHttpRequest (${this.method} ${this.url}): synchronous requests are not supported. This request will be performed as-is.`\n )\n return invoke()\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 responseType: this[kIsRequestHandled] ? 'mock' : 'original',\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.verbose(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\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.verbose('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.verbose('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.verbose('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 this.logger.verbose(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n FetchResponse.setUrl(this.url.href, response)\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 // 1. Fire a progress event named loadstart at this with 0 and 0.\n this.trigger('loadstart', this.request, { loaded: 0, total: 0 })\n\n // 2. Let requestBodyTransmitted be 0.\n let requestBodyTransmitted = 0\n let uploadComplete = false\n\n if (this[kFetchRequest]) {\n const requestBodyLength = await getBodyByteLength(\n this[kFetchRequest].clone()\n )\n\n // 5. If this’s upload complete flag is unset and this’s upload listener flag is set, then fire a progress event named loadstart at this’s upload object with requestBodyTransmitted and requestBodyLength.\n if (!uploadComplete) {\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: requestBodyLength,\n })\n }\n\n const processRequestBodyChunkLength = (bytesLength: number) => {\n requestBodyTransmitted += bytesLength\n\n if (requestBodyTransmitted < requestBodyLength) {\n this.trigger('progress', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n }\n }\n\n const processRequestEndOfBody = () => {\n uploadComplete = true\n\n this.trigger('progress', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n this.trigger('loadend', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n }\n\n if (this[kFetchRequest]?.body != null) {\n const reader = this[kFetchRequest].body.getReader()\n\n while (true) {\n const { value, done } = await reader.read()\n\n if (done) {\n processRequestEndOfBody()\n break\n }\n\n processRequestBodyChunkLength(value.byteLength)\n }\n } else {\n processRequestEndOfBody()\n }\n }\n\n let timedOut = false\n const responseReadController = new AbortController()\n\n const requestErrorSteps = (\n event: keyof XMLHttpRequestEventTargetEventMap\n ) => {\n this.setReadyState(this.request.DONE)\n\n if (!uploadComplete) {\n this.trigger(event, this.request.upload, {\n loaded: 0,\n total: 0,\n })\n this.trigger('loadend', this.request.upload, {\n loaded: 0,\n total: 0,\n })\n }\n\n this.trigger(event, this.request, { loaded: 0, total: 0 })\n this.trigger('loadend', this.request, { loaded: 0, total: 0 })\n }\n\n const processResponse = async (response: Response) => {\n const handleErrors = () => {\n if (timedOut) {\n requestErrorSteps('timeout')\n } else if (responseReadController.signal.aborted) {\n requestErrorSteps('abort')\n } else if (isResponseError(response)) {\n requestErrorSteps('error')\n }\n }\n\n handleErrors()\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n\n if (!this.request.responseURL) {\n define(this.request, 'responseURL', response.url)\n }\n\n if (isResponseError(response)) {\n return\n }\n\n /**\n * @note The response body length is derived ONLY from the \"content-length\" header.\n * If that response header is not set, the \"total\" in all progress events must be 0.\n */\n const responseBodyLength = Number(\n response.headers.get('content-length') ?? '0'\n )\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n\n let receivedBytes = 0\n let lastReceivedResponseBytesAt = performance.now()\n\n const processResponseBodyChunk = (bytesLength: number) => {\n receivedBytes += bytesLength\n\n const now = performance.now()\n const shouldBuffer =\n now - lastReceivedResponseBytesAt <= 60 &&\n receivedBytes < responseBodyLength\n lastReceivedResponseBytesAt = now\n\n if (shouldBuffer) {\n return\n }\n\n if (this.request.readyState === this.request.HEADERS_RECEIVED) {\n this.setReadyState(this.request.LOADING, false)\n }\n\n this.trigger('readystatechange', this.request)\n this.trigger('progress', this.request, {\n loaded: receivedBytes,\n total: responseBodyLength,\n })\n }\n\n const processResponseBodyError = () => {\n requestErrorSteps('error')\n }\n\n const processResponseEndOfBody = async () => {\n handleErrors()\n\n if (isResponseError(response)) {\n return\n }\n\n // 3. Let transmitted be xhr’s received bytes’s length.\n let transmitted = receivedBytes\n\n // 9. Fire an event named readystatechange at xhr.\n this.setReadyState(this.request.DONE)\n // 10. Fire a progress event named load at xhr with transmitted and length.\n this.trigger('load', this.request, {\n loaded: transmitted,\n total: responseBodyLength,\n })\n // 11. Fire a progress event named loadend at xhr with transmitted and length.\n this.trigger('loadend', this.request, {\n loaded: transmitted,\n total: responseBodyLength,\n })\n }\n\n // 7. If this’s response’s body is null, then run handle response end-of-body for this and return.\n if (response.body == null) {\n processResponseEndOfBody()\n } else {\n const reader = response.body.getReader()\n\n while (true) {\n if (responseReadController.signal.aborted) {\n break\n }\n\n try {\n const { value, done } = await reader.read()\n\n if (done) {\n processResponseEndOfBody()\n return\n }\n\n processResponseBodyChunk(value.byteLength)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n } catch {\n processResponseBodyError()\n }\n }\n }\n }\n\n // Redirects are followed as a part of the fetch controller. Since we don't have one,\n // retrieve the final response and then continue with processing it instead of the mocked one.\n const [redirectError, finalResponse] = await until(() => {\n return this.followRedirects(response)\n })\n\n if (redirectError) {\n return\n }\n\n processResponse(finalResponse)\n\n // 12.1, 12.2.\n if (this.request.timeout) {\n setTimeout(() => {\n if (this.request.readyState !== this.request.DONE) {\n timedOut = true\n responseReadController.abort()\n }\n }, this.request.timeout)\n }\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.verbose('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.verbose('headers not received yet, returning null')\n return null\n }\n\n const headerValue = finalResponse.headers.get(args[0])\n this.logger.verbose(\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.verbose('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.verbose('headers not received yet, returning empty string')\n return ''\n }\n\n const headersList = Array.from(finalResponse.headers)\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.verbose('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.verbose(\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.verbose('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.verbose('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.verbose(\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.verbose(\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.verbose('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 private async followRedirects(response: Response): Promise<Response> {\n const redirectLocation = response.headers.get('location')\n\n if (\n !redirectLocation ||\n !FetchResponse.isRedirectResponse(response.status)\n ) {\n return response\n }\n\n this.redirectCount++\n\n if (this.redirectCount > MAX_REDIRECTS) {\n throw new Error('Too many redirects')\n }\n\n const redirectUrl = new URL(redirectLocation, location.href)\n const redirectMethod = FetchResponse.isResponseWithBody(response.status)\n ? this.method\n : 'GET'\n\n const redirectResponse = await new Promise<Response>((resolve, reject) => {\n const request = new XMLHttpRequest()\n request.responseType = this.request.responseType\n\n request.addEventListener('load', () => {\n this.url = new URL(request.responseURL)\n resolve(createResponse(request, request.response))\n })\n\n request.addEventListener('error', () => {\n this.errorWith()\n reject(new Error('Redirect request failed'))\n })\n\n request.open(redirectMethod, redirectUrl.href)\n request.send()\n })\n\n return this.followRedirects(redirectResponse)\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.verbose('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(\n nextReadyState: number,\n triggerReadyStateChangeEvent = true\n ): void {\n this.logger.verbose(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.verbose('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.verbose('set readyState to: %d', nextReadyState)\n\n if (!triggerReadyStateChangeEvent) {\n return\n }\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.verbose('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.verbose('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.verbose('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.verbose(\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.verbose('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: 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.verbose('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 { Emitter } from 'rettime'\nimport { HttpResponseEvent, type HttpRequestEventMap } from '../../events/http'\nimport { RequestController } from '../../request-controller'\nimport { XMLHttpRequestController } from './xml-http-request-controller'\nimport { handleRequest } from '../../utils/handle-request'\nimport type { Logger } from '../../utils/logger'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: Emitter<HttpRequestEventMap>\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.verbose('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.verbose(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.verbose('request errored %o', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n }, {\n logger,\n requestId,\n })\n\n this.logger.verbose('awaiting mocked response')\n\n this.logger.verbose(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n initiator: this.request,\n request,\n requestId,\n controller,\n emitter,\n logger,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n responseType,\n request,\n requestId,\n }) {\n this.logger.verbose(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n await emitter.emitAsPromise(\n new HttpResponseEvent({\n initiator: this.request,\n response,\n responseType,\n request,\n requestId,\n })\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 { HttpRequestEventMap } from '../../events/http'\nimport { Interceptor } from '../../interceptor'\nimport { createXMLHttpRequestProxy } from './xml-http-request-proxy'\nimport { hasConfigurableGlobal } from '../../utils/has-configurable-global'\nimport { patchesRegistry } from '../../utils/patches-registry'\nimport { createLogger } from '../../utils/logger'\n\nconst logger = createLogger('xhr')\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol.for('xhr-interceptor')\n\n protected predicate() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n logger.verbose('patching \"XMLHttpRequest\"...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger,\n })\n })\n )\n\n logger.verbose(\n 'global \"XMLHttpRequest\" patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}
import { n as Interceptor, r as createLogger, t as createRequestId } from "./create-request-id-Bk5YX1AM.js";
import { c as RequestController, i as isResponseError, n as FetchResponse, s as copyRawHeaders } from "./fetch-utils-CUOrwEQf.js";
import { n as HttpResponseEvent, t as handleRequest } from "./handle-request-CqVvBhcw.js";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./has-configurable-global-Cew-dYqk.js";
import { until } from "@open-draft/until";
//#region src/interceptors/fetch/utils/create-network-error.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/follow-redirect.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, transform) => readable.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/web.ts
const logger = createLogger("fetch");
/**
* Interceptor for `fetch` requests in the browser.
*/
var FetchInterceptor = class extends Interceptor {
static {
this.symbol = Symbol.for("fetch-interceptor");
}
predicate() {
return hasConfigurableGlobal("fetch");
}
async setup() {
logger.verbose("patching global fetch...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "fetch", (realFetch) => {
return async (input, init) => {
const requestId = createRequestId();
/**
* @note Resolve potentially relative request URL
* against the present `location`. This is mainly
* for native `fetch` in JSDOM.
* @see https://github.com/mswjs/msw/issues/1625
*/
const resolvedInput = typeof input === "string" && typeof location !== "undefined" && !URL.canParse(input) ? new URL(input, location.href) : input;
const request = new Request(resolvedInput, init);
const responsePromise = Promise.withResolvers();
const controller = new RequestController(request, {
passthrough: async () => {
logger.verbose("performing request as-is");
/**
* @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 [responseError, originalResponse] = await until(() => realFetch(request));
if (responseError) return responsePromise.reject(responseError);
logger.verbose("original fetch performed %o", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
logger.verbose("emitting the \"response\" event");
const responseClone = FetchResponse.clone(originalResponse);
await this.emitter.emitAsPromise(new HttpResponseEvent({
initiator: requestCloneForResponseEvent,
request: requestCloneForResponseEvent,
requestId,
response: responseClone,
responseType: "original"
}));
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (isResponseError(rawResponse)) {
logger.verbose("request errored %o", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
const response = new FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
copyRawHeaders(rawResponse.headers, response.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) => {
responsePromise.resolve(response);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
logger.verbose("emitting the \"response\" event");
await this.emitter.emitAsPromise(new HttpResponseEvent({
initiator: request,
response: FetchResponse.clone(response),
responseType: "mock",
request,
requestId
}));
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
logger.verbose("request aborted %o", { reason });
responsePromise.reject(reason);
}
}, {
logger,
requestId
});
logger.verbose("awaiting request resolution");
logger.verbose("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
/**
* @note Give the consumer a chance to abort the request before
* it is dispatched. Fetch queues the request processing as a
* task, so a signal aborted synchronously after `fetch()` must
* prevent the request from ever reaching the "request" listeners.
* Without this, the first listener is invoked synchronously
* within the `fetch()` call itself.
*/
await Promise.resolve();
await handleRequest({
initiator: request,
request,
requestId,
emitter: this.emitter,
controller,
logger
});
return responsePromise.promise;
};
}));
logger.verbose("global fetch patched: %s", globalThis.fetch.name);
}
};
//#endregion
export { FetchInterceptor as t };
//# sourceMappingURL=web-CdcYFjgm.js.map
{"version":3,"file":"web-CdcYFjgm.js","names":[],"sources":["../../src/interceptors/fetch/utils/create-network-error.ts","../../src/interceptors/fetch/utils/follow-redirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/web.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './create-network-error'\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 { HttpResponseEvent, type HttpRequestEventMap } from '../../events/http'\nimport { RequestController } from '../../request-controller'\nimport { handleRequest } from '../../utils/handle-request'\nimport { createRequestId } from '../../create-request-id'\nimport { createNetworkError } from './utils/create-network-error'\nimport { followFetchRedirect } from './utils/follow-redirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/has-configurable-global'\nimport { FetchResponse } from '../../utils/fetch-utils'\nimport { isResponseError } from '../../utils/response-utils'\nimport { patchesRegistry } from '../../utils/patches-registry'\nimport { copyRawHeaders } from '../ClientRequest/utils/record-raw-headers'\nimport { Interceptor } from '../../interceptor'\nimport { createLogger } from '../../utils/logger'\n\nconst logger = createLogger('fetch')\n\n/**\n * Interceptor for `fetch` requests in the browser.\n */\nexport class FetchInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol.for('fetch-interceptor')\n\n protected predicate() {\n return hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n logger.verbose('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', (realFetch) => {\n return 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 !URL.canParse(input)\n ? new URL(input, location.href)\n : input\n\n const request = new Request(resolvedInput, init)\n\n const responsePromise = Promise.withResolvers<Response>()\n\n const controller = new RequestController(\n request,\n {\n passthrough: async () => {\n logger.verbose('performing request as-is')\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 [responseError, originalResponse] = await until(() =>\n realFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n logger.verbose('original fetch performed %o', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n logger.verbose('emitting the \"response\" event')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await this.emitter.emitAsPromise(\n new HttpResponseEvent({\n initiator: requestCloneForResponseEvent,\n request: requestCloneForResponseEvent,\n requestId,\n response: responseClone,\n responseType: 'original',\n })\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 logger.verbose('request errored %o', {\n response: rawResponse,\n })\n responsePromise.reject(createNetworkError(rawResponse))\n return\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 copyRawHeaders(rawResponse.headers, response.headers)\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(\n createNetworkError('unexpected redirect')\n )\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 logger.verbose('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 this.emitter.emitAsPromise(\n new HttpResponseEvent({\n initiator: request,\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 responseType: 'mock',\n request,\n requestId,\n })\n )\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n logger.verbose('request aborted %o', { reason })\n responsePromise.reject(reason)\n },\n },\n {\n logger,\n requestId,\n }\n )\n\n logger.verbose('awaiting request resolution')\n\n logger.verbose(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n /**\n * @note Give the consumer a chance to abort the request before\n * it is dispatched. Fetch queues the request processing as a\n * task, so a signal aborted synchronously after `fetch()` must\n * prevent the request from ever reaching the \"request\" listeners.\n * Without this, the first listener is invoked synchronously\n * within the `fetch()` call itself.\n */\n await Promise.resolve()\n\n await handleRequest({\n initiator: request,\n request,\n requestId,\n emitter: this.emitter,\n controller,\n logger,\n })\n\n return responsePromise.promise\n }\n })\n )\n\n logger.verbose('global fetch patched: %s', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
+2
-2

@@ -1,3 +0,3 @@

import { n as Interceptor, t as createRequestId } from "./create-request-id-DlEd4GOA.js";
import { c as RequestController, l as InterceptorError, n as FetchResponse, t as FetchRequest } from "./fetch-utils-zxA_SD66.js";
import { n as Interceptor, t as createRequestId } from "./create-request-id-Bk5YX1AM.js";
import { c as RequestController, l as InterceptorError, n as FetchResponse, t as FetchRequest } from "./fetch-utils-CUOrwEQf.js";
import { n as encodeBuffer, t as decodeBuffer } from "./buffer-utils-DJj7YzLG.js";

@@ -4,0 +4,0 @@ import { t as resolveWebSocketUrl } from "./resolve-web-socket-url-CSvNPLGi.js";

@@ -1,2 +0,2 @@

import { t as FetchInterceptor } from "../../web-BjVDkiX0.js";
import { t as FetchInterceptor } from "../../web-CdcYFjgm.js";
export { FetchInterceptor };

@@ -1,2 +0,2 @@

import { n as Interceptor, r as createLogger, t as createRequestId } from "../../create-request-id-DlEd4GOA.js";
import { n as Interceptor, r as createLogger, t as createRequestId } from "../../create-request-id-Bk5YX1AM.js";
import { t as resolveWebSocketUrl } from "../../resolve-web-socket-url-CSvNPLGi.js";

@@ -3,0 +3,0 @@ import { n as patchesRegistry, t as hasConfigurableGlobal } from "../../has-configurable-global-Cew-dYqk.js";

@@ -1,2 +0,2 @@

import { t as XMLHttpRequestInterceptor } from "../../web-C2oKsT3Q.js";
import { t as XMLHttpRequestInterceptor } from "../../web-BcKSQghf.js";
export { XMLHttpRequestInterceptor };

@@ -1,3 +0,3 @@

import { t as FetchInterceptor } from "../web-BjVDkiX0.js";
import { t as XMLHttpRequestInterceptor } from "../web-C2oKsT3Q.js";
import { t as FetchInterceptor } from "../web-CdcYFjgm.js";
import { t as XMLHttpRequestInterceptor } from "../web-BcKSQghf.js";
//#region src/presets/browser.ts

@@ -4,0 +4,0 @@ /**

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

"description": "Low-level network interception library for Node.js.",
"version": "0.42.0",
"version": "0.42.1",
"main": "./lib/node/index.js",

@@ -8,0 +8,0 @@ "types": "./lib/node/index.d.ts",

import { Emitter } from "rettime";
import debug from "debug";
//#region src/disposable.ts
var Disposable = class {
constructor() {
this.subscriptions = [];
}
dispose() {
let subscription;
while (subscription = this.subscriptions.pop()) subscription();
}
};
//#endregion
//#region src/utils/logger.ts
const LOG_TIMESTAMP_REGEXP = /\d{2}:\d{2}:\d{2}\.\d{3}/;
function normalizeNamespace(namespace) {
return namespace.split(":").map((segment) => {
return segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase();
}).filter(Boolean).join(":");
}
function getTimestamp() {
return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
}
async function readBody(message) {
if (message.body == null) return null;
try {
return await message.clone().text();
} catch {
return null;
}
}
function formatHeaders(headers) {
return Array.from(headers.entries()).map(([name, value]) => {
return `${name}: ${value}`;
});
}
async function formatHttpMessage(startLine, message) {
const lines = [startLine, ...formatHeaders(message.headers)];
const body = await readBody(message);
lines.push("", body ?? "");
return lines.join("\n");
}
async function formatRequest(request) {
return formatHttpMessage(`${request.method} ${request.url}`, request);
}
async function formatResponse(response) {
const statusText = response.statusText ? ` ${response.statusText}` : "";
return formatHttpMessage(`HTTP ${response.status}${statusText}`, response);
}
function formatLogArguments(arguments_) {
const message = arguments_[0];
if (typeof message === "string") {
const messageWithoutDebugTimestamp = message.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z /, "");
const timestampMatch = messageWithoutDebugTimestamp.match(LOG_TIMESTAMP_REGEXP);
if (!timestampMatch || timestampMatch.index === void 0) {
arguments_[0] = messageWithoutDebugTimestamp;
return;
}
const messagePrefix = messageWithoutDebugTimestamp.slice(0, timestampMatch.index).trim();
const messageBody = messageWithoutDebugTimestamp.slice(timestampMatch.index + timestampMatch[0].length).trimStart();
arguments_[0] = `${timestampMatch[0]} ${messagePrefix} ${messageBody}`;
}
}
function useConciseTimestamp(logger) {
logger.log = (...arguments_) => {
formatLogArguments(arguments_);
debug.log(...arguments_);
};
}
function isVerboseLoggingEnabled() {
if (typeof process !== "undefined" && process.env.DEBUG_LEVEL === "verbose") return true;
/**
* @note Consult the localStorage only in browser-like environments.
* In Node.js 26+, reading "globalThis.localStorage" without the
* "--localstorage-file" flag set emits an experimental warning
* (a try/catch cannot suppress it). Node.js consumers control the
* log level via the "DEBUG_LEVEL" environment variable above.
*/
if (typeof document === "undefined") return false;
try {
return globalThis.localStorage?.getItem("debugLevel") === "verbose";
} catch {
return false;
}
}
function createLogger(namespace) {
const logger = debug(`interceptors:${normalizeNamespace(namespace)}`);
Reflect.set(logger, "useColors", true);
useConciseTimestamp(logger);
return {
info(message, ...positionals) {
logger(`${getTimestamp()} ${message}`, ...positionals);
},
verbose(message, ...positionals) {
if (!isVerboseLoggingEnabled()) return;
logger(`${getTimestamp()} ${message}`, ...positionals);
},
isEnabled(level) {
return logger.enabled && (level === "default" || isVerboseLoggingEnabled());
}
};
}
//#endregion
//#region src/interceptor.ts
const interceptorsRegistry = globalThis.__MSW_INTERCEPTORS_REGISTRY ??= /* @__PURE__ */ new Map();
var Interceptor = class extends Disposable {
#owners;
static singleton(InterceptorClass) {
const symbol = InterceptorClass.symbol;
const existing = interceptorsRegistry.get(symbol);
if (existing instanceof InterceptorClass) return existing;
const newInstance = new InterceptorClass();
interceptorsRegistry.set(symbol, newInstance);
return newInstance;
}
constructor() {
super();
this.on = (type, listener, options) => {
return this.emitter.on(type, listener, options);
};
this.once = (type, listener, options) => {
return this.emitter.once(type, listener, options);
};
this.listeners = (type) => {
return this.emitter.listeners(type);
};
this.listenerCount = (type) => {
return this.emitter.listenerCount(type);
};
this.removeListener = (type, listener) => {
return this.emitter.removeListener(type, listener);
};
this.removeAllListeners = (type) => {
this.logger.info("removeAllListeners %o", { eventType: type ?? "*" });
return this.emitter.removeAllListeners(type);
};
this.#owners = /* @__PURE__ */ new Set();
this.readyState = "INACTIVE";
this.emitter = new Emitter();
this.logger = createLogger(this.#getLoggerNamespace());
}
apply(owner = this) {
if (this.#owners.has(owner)) return;
if (this.readyState !== "ACTIVE" && !this.predicate()) return;
this.#owners.add(owner);
if (this.readyState === "ACTIVE") return;
try {
this.setup();
this.readyState = "ACTIVE";
this.logger.info("apply");
} catch (error) {
this.dispose(owner);
throw error;
}
}
dispose(owner = this) {
if (!this.#owners.delete(owner)) return;
if (this.#owners.size > 0) return;
super.dispose();
this.emitter.removeAllListeners();
this.readyState = "DISPOSED";
this.logger.info("disable");
}
#getLoggerNamespace() {
const symbolDescription = this.constructor.symbol?.description;
if (symbolDescription) return symbolDescription.replace(/-interceptor$/, "");
return this.constructor.name.replace(/Interceptor$/, "");
}
};
//#endregion
//#region src/create-request-id.ts
/**
* Generate a random ID string to represent a request.
* @example
* createRequestId()
* // "f774b6c9c600f"
*/
function createRequestId() {
return Math.random().toString(16).slice(2);
}
//#endregion
export { formatResponse as a, formatRequest as i, Interceptor as n, createLogger as r, createRequestId as t };
//# sourceMappingURL=create-request-id-DlEd4GOA.js.map
{"version":3,"file":"create-request-id-DlEd4GOA.js","names":["#owners","#getLoggerNamespace"],"sources":["../../src/disposable.ts","../../src/utils/logger.ts","../../src/interceptor.ts","../../src/create-request-id.ts"],"sourcesContent":["export type DisposableSubscription = () => void\n\nexport class Disposable {\n protected subscriptions: Array<DisposableSubscription> = []\n\n public dispose() {\n let subscription: DisposableSubscription | undefined\n\n while ((subscription = this.subscriptions.pop())) {\n subscription()\n }\n }\n}\n","import debug from 'debug'\n\nexport type LogLevel = 'default' | 'verbose'\n\nexport interface Logger {\n info(message: string, ...positionals: Array<unknown>): void\n verbose(message: string, ...positionals: Array<unknown>): void\n isEnabled(level: LogLevel): boolean\n}\n\nconst LOG_TIMESTAMP_REGEXP = /\\d{2}:\\d{2}:\\d{2}\\.\\d{3}/\n\nfunction normalizeNamespace(namespace: string): string {\n return namespace\n .split(':')\n .map((segment) => {\n return segment\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/[^a-zA-Z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase()\n })\n .filter(Boolean)\n .join(':')\n}\n\nfunction getTimestamp(): string {\n return new Date().toISOString().slice(11, 23)\n}\n\nasync function readBody(message: Request | Response): Promise<string | null> {\n if (message.body == null) {\n return null\n }\n\n try {\n return await message.clone().text()\n } catch {\n return null\n }\n}\n\nfunction formatHeaders(headers: Headers): Array<string> {\n return Array.from(headers.entries()).map(([name, value]) => {\n return `${name}: ${value}`\n })\n}\n\nasync function formatHttpMessage(\n startLine: string,\n message: Request | Response\n): Promise<string> {\n const lines = [startLine, ...formatHeaders(message.headers)]\n const body = await readBody(message)\n\n lines.push('', body ?? '')\n\n return lines.join('\\n')\n}\n\nexport async function formatRequest(request: Request): Promise<string> {\n return formatHttpMessage(`${request.method} ${request.url}`, request)\n}\n\nexport async function formatResponse(response: Response): Promise<string> {\n const statusText = response.statusText ? ` ${response.statusText}` : ''\n return formatHttpMessage(\n `HTTP ${response.status}${statusText}`,\n response\n )\n}\n\nfunction formatLogArguments(arguments_: Array<unknown>): void {\n const message = arguments_[0]\n\n if (typeof message === 'string') {\n const messageWithoutDebugTimestamp = message.replace(\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z /,\n ''\n )\n const timestampMatch = messageWithoutDebugTimestamp.match(\n LOG_TIMESTAMP_REGEXP\n )\n\n if (!timestampMatch || timestampMatch.index === undefined) {\n arguments_[0] = messageWithoutDebugTimestamp\n return\n }\n\n const messagePrefix = messageWithoutDebugTimestamp\n .slice(0, timestampMatch.index)\n .trim()\n const messageBody = messageWithoutDebugTimestamp\n .slice(timestampMatch.index + timestampMatch[0].length)\n .trimStart()\n\n arguments_[0] = `${timestampMatch[0]} ${messagePrefix} ${messageBody}`\n }\n}\n\nfunction useConciseTimestamp(logger: debug.Debugger): void {\n logger.log = (...arguments_) => {\n formatLogArguments(arguments_)\n debug.log(...arguments_)\n }\n}\n\nfunction isVerboseLoggingEnabled(): boolean {\n if (typeof process !== 'undefined' && process.env.DEBUG_LEVEL === 'verbose') {\n return true\n }\n\n /**\n * @note Consult the localStorage only in browser-like environments.\n * In Node.js 26+, reading \"globalThis.localStorage\" without the\n * \"--localstorage-file\" flag set emits an experimental warning\n * (a try/catch cannot suppress it). Node.js consumers control the\n * log level via the \"DEBUG_LEVEL\" environment variable above.\n */\n if (typeof document === 'undefined') {\n return false\n }\n\n try {\n return globalThis.localStorage?.getItem('debugLevel') === 'verbose'\n } catch {\n return false\n }\n}\n\nexport function createLogger(namespace: string): Logger {\n const normalizedNamespace = normalizeNamespace(namespace)\n const logger = debug(`interceptors:${normalizedNamespace}`)\n Reflect.set(logger, 'useColors', true)\n useConciseTimestamp(logger)\n\n return {\n info(message, ...positionals) {\n logger(`${getTimestamp()} ${message}`, ...positionals)\n },\n verbose(message, ...positionals) {\n if (!isVerboseLoggingEnabled()) {\n return\n }\n\n logger(`${getTimestamp()} ${message}`, ...positionals)\n },\n isEnabled(level) {\n return (\n logger.enabled && (level === 'default' || isVerboseLoggingEnabled())\n )\n },\n }\n}\n","import { Emitter, type DefaultEventMap } from 'rettime'\nimport { Disposable } from './disposable'\nimport { createLogger, type Logger } from './utils/logger'\n\nexport enum InterceptorReadyState {\n INACTIVE = 'INACTIVE',\n ACTIVE = 'ACTIVE',\n DISPOSED = 'DISPOSED',\n}\n\ndeclare global {\n var __MSW_INTERCEPTORS_REGISTRY: Map<symbol, Interceptor<any>> | undefined\n}\n\nconst interceptorsRegistry = (globalThis.__MSW_INTERCEPTORS_REGISTRY ??=\n new Map<symbol, Interceptor<any>>())\n\nexport abstract class Interceptor<\n Events extends DefaultEventMap,\n> extends Disposable {\n declare ['constructor']: typeof Interceptor\n\n protected emitter: Emitter<Events>\n protected readonly logger: Logger\n\n public readyState: InterceptorReadyState\n\n static readonly symbol: symbol\n\n #owners: Set<object>\n\n static singleton<T extends Interceptor<any>>(\n InterceptorClass: (new () => T) & { symbol: symbol }\n ): T {\n const symbol = InterceptorClass.symbol\n const existing = interceptorsRegistry.get(symbol)\n\n if (existing instanceof InterceptorClass) {\n return existing\n }\n\n const newInstance = new InterceptorClass()\n interceptorsRegistry.set(symbol, newInstance)\n return newInstance\n }\n\n constructor() {\n super()\n\n this.#owners = new Set()\n this.readyState = InterceptorReadyState.INACTIVE\n this.emitter = new Emitter()\n this.logger = createLogger(this.#getLoggerNamespace())\n }\n\n protected abstract predicate(): boolean\n protected abstract setup(): void\n\n public apply(owner: object = this): void {\n if (this.#owners.has(owner)) {\n return\n }\n\n if (\n this.readyState !== InterceptorReadyState.ACTIVE &&\n !this.predicate()\n ) {\n return\n }\n\n this.#owners.add(owner)\n\n if (this.readyState === InterceptorReadyState.ACTIVE) {\n return\n }\n\n try {\n this.setup()\n this.readyState = InterceptorReadyState.ACTIVE\n this.logger.info('apply')\n } catch (error) {\n this.dispose(owner)\n throw error\n }\n }\n\n public dispose(owner: object = this): void {\n if (!this.#owners.delete(owner)) {\n return\n }\n\n if (this.#owners.size > 0) {\n return\n }\n\n super.dispose()\n this.emitter.removeAllListeners()\n this.readyState = InterceptorReadyState.DISPOSED\n this.logger.info('disable')\n }\n\n public on: Emitter<Events>['on'] = (type, listener, options) => {\n return this.emitter.on(type, listener, options)\n }\n\n public once: Emitter<Events>['once'] = (type, listener, options) => {\n return this.emitter.once(type, listener, options)\n }\n\n public listeners: Emitter<Events>['listeners'] = (type) => {\n return this.emitter.listeners(type)\n }\n\n public listenerCount: Emitter<Events>['listenerCount'] = (type) => {\n return this.emitter.listenerCount(type)\n }\n\n public removeListener: Emitter<Events>['removeListener'] = (\n type,\n listener\n ) => {\n return this.emitter.removeListener(type, listener)\n }\n\n public removeAllListeners: Emitter<Events>['removeAllListeners'] = (type) => {\n this.logger.info('removeAllListeners %o', { eventType: type ?? '*' })\n return this.emitter.removeAllListeners(type)\n }\n\n #getLoggerNamespace(): string {\n const symbolDescription = this.constructor.symbol?.description\n\n if (symbolDescription) {\n return symbolDescription.replace(/-interceptor$/, '')\n }\n\n return this.constructor.name.replace(/Interceptor$/, '')\n }\n}\n","/**\n * Generate a random ID string to represent a request.\n * @example\n * createRequestId()\n * // \"f774b6c9c600f\"\n */\nexport function createRequestId(): string {\n return Math.random().toString(16).slice(2)\n}\n"],"mappings":"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"}
import { a as formatResponse } from "./create-request-id-DlEd4GOA.js";
import { invariant } from "outvariant";
//#region src/interceptor-error.ts
var InterceptorError = class InterceptorError extends Error {
constructor(message) {
super(message);
this.name = "InterceptorError";
Object.setPrototypeOf(this, InterceptorError.prototype);
}
};
//#endregion
//#region src/request-controller.ts
var RequestController = class RequestController {
static {
this.PENDING = 0;
}
static {
this.PASSTHROUGH = 1;
}
static {
this.RESPONSE = 2;
}
static {
this.ERROR = 3;
}
#handled;
constructor(request, source, options) {
this.request = request;
this.source = source;
this.options = options;
this.readyState = RequestController.PENDING;
this.#handled = Promise.withResolvers();
this.handled = this.#handled.promise;
}
/**
* Perform this request as-is.
*/
async passthrough() {
invariant.as(InterceptorError, this.readyState === RequestController.PENDING, "Failed to passthrough the \"%s %s\" request: the request has already been handled", this.request.method, this.request.url);
this.readyState = RequestController.PASSTHROUGH;
if (this.options) this.options.logger.info("[%s] passthrough", this.options.requestId);
await this.source.passthrough();
this.#handled.resolve();
}
/**
* Respond to this request with the given `Response` instance.
*
* @example
* controller.respondWith(new Response())
* controller.respondWith(Response.json({ id }))
* controller.respondWith(Response.error())
*/
respondWith(response) {
invariant.as(InterceptorError, this.readyState === RequestController.PENDING, "Failed to respond to the \"%s %s\" request with \"%d %s\": the request has already been handled (%d)", this.request.method, this.request.url, response.status, response.statusText || "OK", this.readyState);
this.readyState = RequestController.RESPONSE;
if (this.options?.logger.isEnabled("default")) {
const { logger, requestId } = this.options;
formatResponse(response).then((message) => {
logger.info("[%s] mocked %s", requestId, message);
});
}
this.#handled.resolve();
/**
* @note Although `source.respondWith()` is potentially asynchronous,
* do NOT await it for backward-compatibility. Awaiting it will short-circuit
* the request listener invocation as soon as a listener responds to a request.
* Ideally, that's what we want, but that's not what we promise the user.
*/
this.source.respondWith(response);
}
/**
* Error this request with the given reason.
*
* @example
* controller.errorWith()
* controller.errorWith(new Error('Oops!'))
* controller.errorWith({ message: 'Oops!'})
*/
errorWith(reason) {
invariant.as(InterceptorError, this.readyState === RequestController.PENDING, "Failed to error the \"%s %s\" request with \"%s\": the request has already been handled (%d)", this.request.method, this.request.url, reason?.toString(), this.readyState);
this.readyState = RequestController.ERROR;
if (this.options) this.options.logger.info("[%s] error %o", this.options.requestId, reason);
this.source.errorWith(reason);
this.#handled.resolve();
}
};
//#endregion
//#region src/interceptors/ClientRequest/utils/record-raw-headers.ts
const kRawHeaders = Symbol("kRawHeaders");
/**
* Define the raw headers symbol on the given `Headers` instance.
* If the symbol already exists, it gets overridden.
*/
function defineRawHeadersSymbol(headers, rawHeaders) {
Object.defineProperty(headers, kRawHeaders, {
value: rawHeaders,
enumerable: false,
configurable: true
});
}
function getRawFetchHeaders(headers) {
if (!Reflect.has(headers, kRawHeaders)) return Array.from(headers.entries());
const rawHeaders = Reflect.get(headers, kRawHeaders);
return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries());
}
function copyRawHeaders(source, destination) {
const rawHeaders = [...getRawFetchHeaders(source)];
if (rawHeaders.length === 0) return;
/**
* @note Add headers from trhe destination that raw headers from the source
* don't have. Undici automatically appends a "Content-Type" header for responses
* and, for some reason, that change is not recorded. This preserves it.
*/
for (const [name, value] of destination) if (rawHeaders.every((header) => header[0].toLowerCase() !== name.toLowerCase())) rawHeaders.push([name, value]);
defineRawHeadersSymbol(destination, rawHeaders);
}
//#endregion
//#region src/utils/get-value-by-symbol.ts
/**
* Returns the value behind the symbol with the given name.
*/
function getValueBySymbol(symbolName, source) {
const symbol = Object.getOwnPropertySymbols(source).find((symbol) => {
return symbol.description === symbolName;
});
if (symbol) return Reflect.get(source, symbol);
}
//#endregion
//#region src/utils/is-object.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/is-property-accessible.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/response-utils.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/fetch-utils.ts
var FetchRequest = class FetchRequest extends Request {
static #resolveProperty(input, init = {}, key) {
return init[key] ?? (input instanceof Request ? input[key] : void 0);
}
/**
* Check if the given request method is configurable.
* @see https://fetch.spec.whatwg.org/#methods
*/
static isConfigurableMethod(method) {
return method !== "CONNECT" && method !== "TRACE" && method !== "TRACK";
}
static isMethodWithBody(method) {
return method !== "HEAD" && method !== "GET" && FetchRequest.isConfigurableMethod(method);
}
/**
* Check if the given request `mode` is configurable.
* @see https://fetch.spec.whatwg.org/#concept-request-mode
*/
static isConfigurableMode(mode) {
return mode !== "navigate" && mode !== "websocket" && mode !== "webtransport";
}
constructor(input, init) {
const method = FetchRequest.#resolveProperty(input, init, "method") || "GET";
const safeMethod = FetchRequest.isConfigurableMethod(method) ? method : "GET";
const hasExplicitBody = init != null && "body" in init;
/**
* Only include `body` in the super init when it needs to be overridden.
* When `input` is a Request and no explicit body is in `init`, let the
* Request constructor handle body transfer naturally so it properly
* marks the original request's body as consumed (bodyUsed = true).
*/
const bodyInit = !FetchRequest.isMethodWithBody(method) ? { body: void 0 } : hasExplicitBody ? { body: init.body } : {};
const mode = FetchRequest.#resolveProperty(input, init, "mode") ?? void 0;
const safeMode = FetchRequest.isConfigurableMode(mode) ? mode : void 0;
super(input, {
...init || {},
method: safeMethod,
mode: safeMode,
duplex: init?.duplex ?? (FetchRequest.isMethodWithBody(method) ? "half" : void 0),
...bodyInit
});
if (method !== safeMethod) this.#setInternalProperty("method", method);
if (method === "CONNECT") {
const url = new URL(input instanceof Request ? input.url : input);
let authority;
/**
* @note Node.js has a bug parsing raw CONNECT requests URLs like
* "http://127.0.0.1:1337/localhost:80". It would treat "localhost:" as a protocol.
*/
if (url.protocol === "localhost:") authority = url.href;
else authority = url.pathname.replace(/^\/+/, "");
/**
* @note Define "url" as a getter because Undici uses their own
* logic to resolve the "request.url" property. Simply reassigning
* its value doesn't do anything. This is a destructive action
* but it's safe because "CONNECT" requests are forbidden per fetch.
*/
Object.defineProperty(this, "url", {
get: () => authority,
enumerable: true,
configurable: true
});
}
if (mode != null && mode !== safeMode) this.#setInternalProperty("mode", mode);
}
#setInternalProperty(key, value) {
const internalState = getValueBySymbol("state", this);
if (internalState) Reflect.set(internalState, key, value);
else Object.defineProperty(this, key, {
value,
enumerable: true,
configurable: true,
writable: false
});
}
};
const kStatus = Symbol("kStatus");
const kUrl = Symbol("kUrl");
var FetchResponse = class FetchResponse extends Response {
static from(response, init) {
if (response instanceof FetchResponse) return response;
if (isResponseError(response)) return response;
const fetchResponse = new FetchResponse(response.body, {
url: init?.url ?? response.url,
status: init?.status || response.status,
statusText: init?.statusText ?? response.statusText,
headers: init?.headers ?? response.headers
});
copyRawHeaders(response.headers, fetchResponse.headers);
return fetchResponse;
}
static {
this.STATUS_CODES_WITHOUT_BODY = [
101,
103,
204,
205,
304
];
}
static {
this.STATUS_CODES_WITH_REDIRECT = [
301,
302,
303,
307,
308
];
}
static isConfigurableStatusCode(status) {
return status >= 200 && status <= 599;
}
static isRedirectResponse(status) {
return FetchResponse.STATUS_CODES_WITH_REDIRECT.includes(status);
}
/**
* Returns a boolean indicating whether the given response status
* code represents a response that can have a body.
*/
static isResponseWithBody(status) {
return !FetchResponse.STATUS_CODES_WITHOUT_BODY.includes(status);
}
static setStatus(status, response) {
/**
* @note Undici keeps an internal "Symbol(state)" that holds
* the actual value of response status. Update that in Node.js.
*/
const internalState = getValueBySymbol("state", response);
if (internalState) internalState.status = status;
else Object.defineProperty(response, "status", {
value: status,
enumerable: true,
configurable: true,
writable: false
});
Object.defineProperty(response, kStatus, {
value: status,
enumerable: false
});
}
static setUrl(url, response) {
if (!url || url === "about:" || !URL.canParse(url)) return;
const state = getValueBySymbol("state", response);
if (state) state.urlList.push(new URL(url));
else Object.defineProperty(response, "url", {
value: url,
enumerable: true,
configurable: true,
writable: false
});
Object.defineProperty(response, kUrl, {
value: url,
enumerable: false
});
}
/**
* Parses the given raw HTTP headers into a Fetch API `Headers` instance.
*/
static parseRawHeaders(rawHeaders) {
const headers = new Headers();
for (let line = 0; line < rawHeaders.length; line += 2) headers.append(rawHeaders[line], rawHeaders[line + 1]);
return headers;
}
/**
* Safely clones the given `Response`.
* Coerces response clone exceptions into 500 mocked responses.
* Handy in the environments that introduce arbitrary response
* cloning restrictions, like "101 Switching Protocols" cloning
* in "miniflare".
*/
static clone(response) {
try {
return response.clone();
} catch (error) {
return Response.json(error instanceof Error ? {
name: error.name,
message: error.message,
stack: error.stack
} : {}, {
status: 500,
statusText: "Unclonable Response"
});
}
}
#status;
#url;
constructor(body, init = {}) {
const status = init.status ?? 200;
const safeStatus = FetchResponse.isConfigurableStatusCode(status) ? status : 200;
const finalBody = FetchResponse.isResponseWithBody(status) ? body : null;
super(finalBody, {
status: safeStatus,
statusText: init.statusText,
headers: init.headers
});
/**
* Since Node.js v24, Undici stores the Response state in an inaccessible field "#state".
* Forward the modified status/URL to the cloned response manually.
* @see https://github.com/nodejs/undici/blob/f734c87280e626c75f59aad55b65eb6a89cef392/lib/web/fetch/response.js#L242
*/
if (status !== safeStatus) FetchResponse.setStatus(status, this);
FetchResponse.setUrl(init.url, this);
}
clone() {
const clonedResponse = super.clone();
const customStatus = Reflect.get(this, kStatus);
if (customStatus) FetchResponse.setStatus(customStatus, clonedResponse);
const customUrl = Reflect.get(this, kUrl);
if (customUrl) FetchResponse.setUrl(customUrl, clonedResponse);
return clonedResponse;
}
};
//#endregion
export { isResponseLike as a, RequestController as c, isResponseError as i, InterceptorError as l, FetchResponse as n, isObject as o, createServerErrorResponse as r, copyRawHeaders as s, FetchRequest as t };
//# sourceMappingURL=fetch-utils-zxA_SD66.js.map
{"version":3,"file":"fetch-utils-zxA_SD66.js","names":["#handled","#resolveProperty","#setInternalProperty"],"sources":["../../src/interceptor-error.ts","../../src/request-controller.ts","../../src/interceptors/ClientRequest/utils/record-raw-headers.ts","../../src/utils/get-value-by-symbol.ts","../../src/utils/is-object.ts","../../src/utils/is-property-accessible.ts","../../src/utils/response-utils.ts","../../src/utils/fetch-utils.ts"],"sourcesContent":["export class InterceptorError extends Error {\n constructor(message?: string) {\n super(message)\n this.name = 'InterceptorError'\n Object.setPrototypeOf(this, InterceptorError.prototype)\n }\n}\n","import { invariant } from 'outvariant'\nimport { InterceptorError } from './interceptor-error'\nimport { formatResponse, type Logger } from './utils/logger'\n\nexport interface RequestControllerSource {\n passthrough(): void | Promise<void>\n respondWith(response: Response): void | Promise<void>\n errorWith(reason?: unknown): void | Promise<void>\n}\n\ninterface RequestControllerOptions {\n logger: Logger\n requestId: string\n}\n\nexport class RequestController {\n static PENDING = 0 as const\n static PASSTHROUGH = 1 as const\n static RESPONSE = 2 as const\n static ERROR = 3 as const\n\n public readyState: number\n\n /**\n * A Promise that resolves when this controller handles a request.\n * See `controller.readyState` for more information on the handling result.\n */\n public handled: Promise<void>\n\n readonly #handled: PromiseWithResolvers<void>\n\n constructor(\n protected readonly request: Request,\n protected readonly source: RequestControllerSource,\n protected readonly options?: RequestControllerOptions\n ) {\n this.readyState = RequestController.PENDING\n this.#handled = Promise.withResolvers<void>()\n this.handled = this.#handled.promise\n }\n\n /**\n * Perform this request as-is.\n */\n public async passthrough(): Promise<void> {\n invariant.as(\n InterceptorError,\n this.readyState === RequestController.PENDING,\n 'Failed to passthrough the \"%s %s\" request: the request has already been handled',\n this.request.method,\n this.request.url\n )\n\n this.readyState = RequestController.PASSTHROUGH\n if (this.options) {\n this.options.logger.info('[%s] passthrough', this.options.requestId)\n }\n await this.source.passthrough()\n this.#handled.resolve()\n }\n\n /**\n * Respond to this request with the given `Response` instance.\n *\n * @example\n * controller.respondWith(new Response())\n * controller.respondWith(Response.json({ id }))\n * controller.respondWith(Response.error())\n */\n public respondWith(response: Response): void {\n invariant.as(\n InterceptorError,\n this.readyState === RequestController.PENDING,\n 'Failed to respond to the \"%s %s\" request with \"%d %s\": the request has already been handled (%d)',\n this.request.method,\n this.request.url,\n response.status,\n response.statusText || 'OK',\n this.readyState\n )\n\n this.readyState = RequestController.RESPONSE\n if (this.options?.logger.isEnabled('default')) {\n const { logger, requestId } = this.options\n\n void formatResponse(response).then((message) => {\n logger.info('[%s] mocked %s', requestId, message)\n })\n }\n this.#handled.resolve()\n\n /**\n * @note Although `source.respondWith()` is potentially asynchronous,\n * do NOT await it for backward-compatibility. Awaiting it will short-circuit\n * the request listener invocation as soon as a listener responds to a request.\n * Ideally, that's what we want, but that's not what we promise the user.\n */\n this.source.respondWith(response)\n }\n\n /**\n * Error this request with the given reason.\n *\n * @example\n * controller.errorWith()\n * controller.errorWith(new Error('Oops!'))\n * controller.errorWith({ message: 'Oops!'})\n */\n public errorWith(reason?: unknown): void {\n invariant.as(\n InterceptorError,\n this.readyState === RequestController.PENDING,\n 'Failed to error the \"%s %s\" request with \"%s\": the request has already been handled (%d)',\n this.request.method,\n this.request.url,\n reason?.toString(),\n this.readyState\n )\n\n this.readyState = RequestController.ERROR\n if (this.options) {\n this.options.logger.info(\n '[%s] error %o',\n this.options.requestId,\n reason\n )\n }\n this.source.errorWith(reason)\n this.#handled.resolve()\n }\n}\n","import { FetchRequest, FetchResponse } from '../../../utils/fetch-utils'\n\ntype HeaderTuple = [string, string]\ntype RawHeaders = Array<HeaderTuple>\ntype SetHeaderBehavior = 'set' | 'append'\n\nconst kRawHeaders = Symbol('kRawHeaders')\nconst kRestorePatches = Symbol('kRestorePatches')\n\nfunction recordRawHeader(\n headers: Headers,\n args: HeaderTuple,\n behavior: SetHeaderBehavior\n) {\n ensureRawHeadersSymbol(headers, [])\n const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders\n\n if (behavior === 'set') {\n // When recording a set header, ensure we remove any matching existing headers.\n for (let index = rawHeaders.length - 1; index >= 0; index--) {\n if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) {\n rawHeaders.splice(index, 1)\n }\n }\n }\n\n rawHeaders.push(args)\n}\n\n/**\n * Define the raw headers symbol on the given `Headers` instance.\n * If the symbol already exists, this function does nothing.\n */\nfunction ensureRawHeadersSymbol(\n headers: Headers,\n rawHeaders: RawHeaders\n): void {\n if (Reflect.has(headers, kRawHeaders)) {\n return\n }\n\n defineRawHeadersSymbol(headers, rawHeaders)\n}\n\n/**\n * Define the raw headers symbol on the given `Headers` instance.\n * If the symbol already exists, it gets overridden.\n */\nfunction defineRawHeadersSymbol(headers: Headers, rawHeaders: RawHeaders) {\n Object.defineProperty(headers, kRawHeaders, {\n value: rawHeaders,\n enumerable: false,\n // Mark the symbol as configurable so its value can be overridden.\n // Overrides happen when merging raw headers from multiple sources.\n // E.g. new Request(new Request(url, { headers }), { headers })\n configurable: true,\n })\n}\n\n/**\n * Patch the global `Headers` class to store raw headers.\n * This is for compatibility with `IncomingMessage.prototype.rawHeaders`.\n *\n * @note Node.js has their own raw headers symbol but it\n * only records the first header name in case of multi-value headers.\n * Any other headers are normalized before comparing. This makes it\n * incompatible with the `rawHeaders` format.\n *\n * let h = new Headers()\n * h.append('X-Custom', 'one')\n * h.append('x-custom', 'two')\n * h[Symbol('headers map')] // Map { 'X-Custom' => 'one, two' }\n */\nexport function recordRawFetchHeaders(): () => void {\n // Prevent patching the Headers prototype multiple times.\n if (Reflect.get(Headers, kRestorePatches)) {\n return Reflect.get(Headers, kRestorePatches)\n }\n\n const {\n Headers: OriginalHeaders,\n Request: OriginalRequest,\n Response: OriginalResponse,\n } = globalThis\n const { set, append, delete: headersDeleteMethod } = Headers.prototype\n\n Object.defineProperty(Headers, kRestorePatches, {\n value: () => {\n Headers.prototype.set = set\n Headers.prototype.append = append\n Headers.prototype.delete = headersDeleteMethod\n globalThis.Headers = OriginalHeaders\n\n globalThis.Request = OriginalRequest\n globalThis.Response = OriginalResponse\n\n Object.setPrototypeOf(FetchRequest, OriginalRequest)\n Object.setPrototypeOf(FetchRequest.prototype, OriginalRequest.prototype)\n Object.setPrototypeOf(FetchResponse, OriginalResponse)\n Object.setPrototypeOf(FetchResponse.prototype, OriginalResponse.prototype)\n\n Reflect.deleteProperty(Headers, kRestorePatches)\n },\n enumerable: false,\n /**\n * @note Mark this property as configurable\n * so we can delete it using `Reflect.delete` during cleanup.\n */\n configurable: true,\n })\n\n Object.defineProperty(globalThis, 'Headers', {\n enumerable: true,\n writable: true,\n value: new Proxy(Headers, {\n construct(target, args, newTarget) {\n const headersInit = args[0] || []\n\n if (\n headersInit instanceof Headers &&\n Reflect.has(headersInit, kRawHeaders)\n ) {\n // Ensure each header tuple has exactly 2 elements (name, value).\n // Node.js 24+ may have stored tuples with extra internal arguments.\n const rawHeadersFromInit = Reflect.get(\n headersInit,\n kRawHeaders\n ) as RawHeaders\n const sanitizedHeaders = rawHeadersFromInit.map(\n (tuple): HeaderTuple => [tuple[0], tuple[1]]\n )\n const headers = Reflect.construct(\n target,\n [sanitizedHeaders],\n newTarget\n )\n ensureRawHeadersSymbol(headers, [\n /**\n * @note Spread the retrieved headers to clone them.\n * This prevents multiple Headers instances from pointing\n * at the same internal \"rawHeaders\" array.\n */\n ...sanitizedHeaders,\n ])\n return headers\n }\n\n const headers = Reflect.construct(target, args, newTarget)\n\n // Request/Response constructors will set the symbol\n // upon creating a new instance, using the raw developer\n // input as the raw headers. Skip the symbol altogether\n // in those cases because the input to Headers will be normalized.\n if (!Reflect.has(headers, kRawHeaders)) {\n const rawHeadersInit = Array.isArray(headersInit)\n ? headersInit\n : Object.entries(headersInit)\n ensureRawHeadersSymbol(headers, rawHeadersInit)\n }\n\n return headers\n },\n }),\n })\n\n Headers.prototype.set = new Proxy(Headers.prototype.set, {\n apply(target, thisArg, args: HeaderTuple) {\n // Use only the first two arguments (name, value) to record raw headers.\n // Node.js 24+ may pass additional internal arguments that should not\n // be included in the raw headers array.\n recordRawHeader(thisArg, [args[0], args[1]], 'set')\n return Reflect.apply(target, thisArg, args)\n },\n })\n\n Headers.prototype.append = new Proxy(Headers.prototype.append, {\n apply(target, thisArg, args: HeaderTuple) {\n // Use only the first two arguments (name, value) to record raw headers.\n // Node.js 24+ may pass additional internal arguments that should not\n // be included in the raw headers array.\n recordRawHeader(thisArg, [args[0], args[1]], 'append')\n return Reflect.apply(target, thisArg, args)\n },\n })\n\n Headers.prototype.delete = new Proxy(Headers.prototype.delete, {\n apply(target, thisArg, args: [string]) {\n const rawHeaders = Reflect.get(thisArg, kRawHeaders) as RawHeaders\n\n if (rawHeaders) {\n for (let index = rawHeaders.length - 1; index >= 0; index--) {\n if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) {\n rawHeaders.splice(index, 1)\n }\n }\n }\n\n return Reflect.apply(target, thisArg, args)\n },\n })\n\n Object.defineProperty(globalThis, 'Request', {\n enumerable: true,\n writable: true,\n value: new Proxy(Request, {\n construct(target, args, newTarget) {\n const request = Reflect.construct(target, args, newTarget)\n const inferredRawHeaders: RawHeaders = []\n\n // Infer raw headers from a `Request` instance used as init.\n if (typeof args[0] === 'object' && args[0].headers != null) {\n inferredRawHeaders.push(...inferRawHeaders(args[0].headers))\n }\n\n // Infer raw headers from the \"headers\" init argument.\n if (typeof args[1] === 'object' && args[1].headers != null) {\n inferredRawHeaders.push(...inferRawHeaders(args[1].headers))\n }\n\n if (inferredRawHeaders.length > 0) {\n ensureRawHeadersSymbol(request.headers, inferredRawHeaders)\n }\n\n return request\n },\n }),\n })\n\n Object.defineProperty(globalThis, 'Response', {\n enumerable: true,\n writable: true,\n value: new Proxy(Response, {\n construct(target, args, newTarget) {\n const response = Reflect.construct(target, args, newTarget)\n\n if (typeof args[1] === 'object' && args[1].headers != null) {\n ensureRawHeadersSymbol(\n response.headers,\n inferRawHeaders(args[1].headers)\n )\n }\n\n return response\n },\n }),\n })\n\n /**\n * Re-parent FetchRequest/FetchResponse so their `super()` calls go\n * through the proxied globalThis.Request/Response above. Without this,\n * FetchRequest extends the statically-captured (original) Request,\n * bypassing the construct proxy that records raw headers.\n */\n Object.setPrototypeOf(FetchRequest, globalThis.Request)\n Object.setPrototypeOf(FetchRequest.prototype, globalThis.Request.prototype)\n Object.setPrototypeOf(FetchResponse, globalThis.Response)\n Object.setPrototypeOf(FetchResponse.prototype, globalThis.Response.prototype)\n\n return restoreHeadersPrototype\n}\n\nexport function restoreHeadersPrototype() {\n if (!Reflect.get(Headers, kRestorePatches)) {\n return\n }\n\n Reflect.get(Headers, kRestorePatches)()\n}\n\nexport function getRawFetchHeaders(headers: Headers): RawHeaders {\n // If the raw headers recording failed for some reason,\n // use the normalized header entries instead.\n if (!Reflect.has(headers, kRawHeaders)) {\n return Array.from(headers.entries())\n }\n\n const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders\n return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries())\n}\n\n/**\n * Infers the raw headers from the given `HeadersInit` provided\n * to the Request/Response constructor.\n *\n * If the `init.headers` is a Headers instance, use it directly.\n * That means the headers were created standalone and already have\n * the raw headers stored.\n * If the `init.headers` is a HeadersInit, create a new Headers\n * instance out of it.\n */\nfunction inferRawHeaders(headers: HeadersInit): RawHeaders {\n if (headers instanceof Headers) {\n return Reflect.get(headers, kRawHeaders) || []\n }\n\n return Reflect.get(new Headers(headers), kRawHeaders)\n}\n\nexport function copyRawHeaders(source: Headers, destination: Headers): void {\n const rawHeaders = [...getRawFetchHeaders(source)]\n\n if (rawHeaders.length === 0) {\n return\n }\n\n /**\n * @note Add headers from trhe destination that raw headers from the source\n * don't have. Undici automatically appends a \"Content-Type\" header for responses\n * and, for some reason, that change is not recorded. This preserves it.\n */\n for (const [name, value] of destination) {\n if (\n rawHeaders.every(\n (header) => header[0].toLowerCase() !== name.toLowerCase()\n )\n ) {\n rawHeaders.push([name, value])\n }\n }\n\n defineRawHeadersSymbol(destination, rawHeaders)\n}\n","/**\n * Returns the value behind the symbol with the given name.\n */\nexport function getValueBySymbol<T>(\n symbolName: string,\n source: object\n): T | undefined {\n const ownSymbols = Object.getOwnPropertySymbols(source)\n\n const symbol = ownSymbols.find((symbol) => {\n return symbol.description === symbolName\n })\n\n if (symbol) {\n return Reflect.get(source, symbol)\n }\n\n return\n}\n","/**\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 './is-object'\nimport { isPropertyAccessible } from './is-property-accessible'\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 * A key on the error a mocked `Response.error()` destroys the socket\n * with, referencing that error response. Allows the client-side\n * interceptors (e.g. fetch) to surface the error response to the\n * consumer instead of the internal socket error.\n */\nexport const kErrorResponse = Symbol('kErrorResponse')\n\n/**\n * Get the mocked error response that caused the given error, if any.\n */\nexport function getErrorResponse(error: unknown): ResponseError | undefined {\n if (\n error instanceof Error &&\n kErrorResponse in error &&\n isResponseError(error[kErrorResponse])\n ) {\n return error[kErrorResponse]\n }\n\n return undefined\n}\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","import { copyRawHeaders } from '../interceptors/ClientRequest/utils/record-raw-headers'\nimport { getValueBySymbol } from './get-value-by-symbol'\nimport { isResponseError } from './response-utils'\n\ninterface UndiciRequestState extends RequestInit {}\n\ninterface FetchRequestInit extends Omit<RequestInit, 'mode'> {\n mode?: RequestMode | 'websocket' | 'webtransport'\n duplex?: 'half' | 'full'\n}\n\nexport class FetchRequest extends Request {\n static #resolveProperty<T extends keyof FetchRequestInit & keyof Request>(\n input: RequestInfo | URL,\n init: FetchRequestInit = {},\n key: T\n ): FetchRequestInit[T] {\n return init[key] ?? (input instanceof Request ? input[key] : undefined)\n }\n\n /**\n * Check if the given request method is configurable.\n * @see https://fetch.spec.whatwg.org/#methods\n */\n static isConfigurableMethod(method: string): boolean {\n return method !== 'CONNECT' && method !== 'TRACE' && method !== 'TRACK'\n }\n\n static isMethodWithBody(method: string): boolean {\n return (\n method !== 'HEAD' &&\n method !== 'GET' &&\n FetchRequest.isConfigurableMethod(method)\n )\n }\n\n /**\n * Check if the given request `mode` is configurable.\n * @see https://fetch.spec.whatwg.org/#concept-request-mode\n */\n static isConfigurableMode(mode: string): boolean {\n return (\n mode !== 'navigate' && mode !== 'websocket' && mode !== 'webtransport'\n )\n }\n\n constructor(input: URL | RequestInfo, init?: FetchRequestInit) {\n const method = FetchRequest.#resolveProperty(input, init, 'method') || 'GET'\n const safeMethod = FetchRequest.isConfigurableMethod(method)\n ? method\n : 'GET'\n\n const hasExplicitBody = init != null && 'body' in init\n\n /**\n * Only include `body` in the super init when it needs to be overridden.\n * When `input` is a Request and no explicit body is in `init`, let the\n * Request constructor handle body transfer naturally so it properly\n * marks the original request's body as consumed (bodyUsed = true).\n */\n const bodyInit: { body?: BodyInit | null } = !FetchRequest.isMethodWithBody(\n method\n )\n ? { body: undefined }\n : hasExplicitBody\n ? { body: init.body }\n : {}\n\n const mode =\n (FetchRequest.#resolveProperty(input, init, 'mode') as RequestMode) ??\n undefined\n const safeMode = FetchRequest.isConfigurableMode(mode) ? mode : undefined\n\n super(input, {\n ...(init || {}),\n method: safeMethod,\n mode: safeMode,\n // @ts-expect-error Untyped Node.js property.\n duplex:\n init?.duplex ??\n (FetchRequest.isMethodWithBody(method) ? 'half' : undefined),\n ...bodyInit,\n })\n\n if (method !== safeMethod) {\n this.#setInternalProperty('method', method)\n }\n\n if (method === 'CONNECT') {\n const url = new URL(input instanceof Request ? input.url : input)\n\n let authority: string\n\n /**\n * @note Node.js has a bug parsing raw CONNECT requests URLs like\n * \"http://127.0.0.1:1337/localhost:80\". It would treat \"localhost:\" as a protocol.\n */\n if (url.protocol === 'localhost:') {\n authority = url.href\n } else {\n authority = url.pathname.replace(/^\\/+/, '')\n }\n\n /**\n * @note Define \"url\" as a getter because Undici uses their own\n * logic to resolve the \"request.url\" property. Simply reassigning\n * its value doesn't do anything. This is a destructive action\n * but it's safe because \"CONNECT\" requests are forbidden per fetch.\n */\n Object.defineProperty(this, 'url', {\n get: () => authority,\n enumerable: true,\n configurable: true,\n })\n }\n\n if (mode != null && mode !== safeMode) {\n this.#setInternalProperty('mode', mode)\n }\n }\n\n #setInternalProperty<T extends keyof Request>(\n key: T,\n value: Request[T]\n ): void {\n const internalState = getValueBySymbol<UndiciRequestState>('state', this)\n\n if (internalState) {\n Reflect.set(internalState, key, value)\n } else {\n Object.defineProperty(this, key, {\n value,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n }\n}\n\nexport interface FetchResponseInit extends ResponseInit {\n url?: string\n}\n\ninterface UndiciResponseState {\n aborted: boolean\n rangeRequested: boolean\n timingAllowPassed: boolean\n requestIncludesCredentials: boolean\n type: ResponseType\n status: number\n statusText: string\n timingInfo: unknown\n cacheState: unknown\n headersList: Record<symbol, Map<string, unknown>>\n urlList: Array<URL>\n body?: {\n stream: ReadableStream\n source: unknown\n length: number\n }\n}\n\nconst kStatus = Symbol('kStatus')\nconst kUrl = Symbol('kUrl')\n\nexport class FetchResponse extends Response {\n static from(response: Response, init?: FetchResponseInit): FetchResponse {\n if (response instanceof FetchResponse) {\n return response\n }\n\n if (isResponseError(response)) {\n return response\n }\n\n const fetchResponse = new FetchResponse(response.body, {\n url: init?.url ?? response.url,\n status: init?.status || response.status,\n statusText: init?.statusText ?? response.statusText,\n headers: init?.headers ?? response.headers,\n })\n\n copyRawHeaders(response.headers, fetchResponse.headers)\n\n return fetchResponse\n }\n\n /**\n * Response status codes for responses that cannot have body.\n * @see https://fetch.spec.whatwg.org/#statuses\n */\n static readonly STATUS_CODES_WITHOUT_BODY = [101, 103, 204, 205, 304]\n\n static readonly STATUS_CODES_WITH_REDIRECT = [301, 302, 303, 307, 308]\n\n static isConfigurableStatusCode(status: number): boolean {\n return status >= 200 && status <= 599\n }\n\n static isRedirectResponse(status: number): boolean {\n return FetchResponse.STATUS_CODES_WITH_REDIRECT.includes(status)\n }\n\n /**\n * Returns a boolean indicating whether the given response status\n * code represents a response that can have a body.\n */\n static isResponseWithBody(status: number): boolean {\n return !FetchResponse.STATUS_CODES_WITHOUT_BODY.includes(status)\n }\n\n static setStatus(status: number, response: Response): void {\n /**\n * @note Undici keeps an internal \"Symbol(state)\" that holds\n * the actual value of response status. Update that in Node.js.\n */\n const internalState = getValueBySymbol<UndiciResponseState>(\n 'state',\n response\n )\n\n if (internalState) {\n internalState.status = status\n } else {\n Object.defineProperty(response, 'status', {\n value: status,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n\n Object.defineProperty(response, kStatus, {\n value: status,\n enumerable: false,\n })\n }\n\n static setUrl(url: string | undefined, response: Response): void {\n if (!url || url === 'about:' || !URL.canParse(url)) {\n return\n }\n\n const state = getValueBySymbol<UndiciResponseState>('state', response)\n\n if (state) {\n // In Undici, push the URL to the internal list of URLs.\n // This will respect the `response.url` getter logic correctly.\n state.urlList.push(new URL(url))\n } else {\n // In other libraries, redefine the `url` property directly.\n Object.defineProperty(response, 'url', {\n value: url,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n\n Object.defineProperty(response, kUrl, {\n value: url,\n enumerable: false,\n })\n }\n\n /**\n * Parses the given raw HTTP headers into a Fetch API `Headers` instance.\n */\n static parseRawHeaders(rawHeaders: Array<string>): Headers {\n const headers = new Headers()\n\n for (let line = 0; line < rawHeaders.length; line += 2) {\n headers.append(rawHeaders[line], rawHeaders[line + 1])\n }\n\n return headers\n }\n\n /**\n * Safely clones the given `Response`.\n * Coerces response clone exceptions into 500 mocked responses.\n * Handy in the environments that introduce arbitrary response\n * cloning restrictions, like \"101 Switching Protocols\" cloning\n * in \"miniflare\".\n */\n static clone(response: Response): Response {\n try {\n const clone = response.clone()\n return clone\n } catch (error) {\n return Response.json(\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : {},\n {\n status: 500,\n statusText: 'Unclonable Response',\n }\n )\n }\n }\n\n #status?: number\n #url?: string\n\n constructor(body?: BodyInit | null, init: FetchResponseInit = {}) {\n const status = init.status ?? 200\n const safeStatus = FetchResponse.isConfigurableStatusCode(status)\n ? status\n : 200\n const finalBody = FetchResponse.isResponseWithBody(status) ? body : null\n\n super(finalBody, {\n status: safeStatus,\n statusText: init.statusText,\n headers: init.headers,\n })\n\n /**\n * Since Node.js v24, Undici stores the Response state in an inaccessible field \"#state\".\n * Forward the modified status/URL to the cloned response manually.\n * @see https://github.com/nodejs/undici/blob/f734c87280e626c75f59aad55b65eb6a89cef392/lib/web/fetch/response.js#L242\n */\n if (status !== safeStatus) {\n FetchResponse.setStatus(status, this)\n }\n\n FetchResponse.setUrl(init.url, this)\n }\n\n public clone() {\n const clonedResponse = super.clone()\n\n const customStatus = Reflect.get(this, kStatus) as number | undefined\n\n if (customStatus) {\n FetchResponse.setStatus(customStatus, clonedResponse)\n }\n\n const customUrl = Reflect.get(this, kUrl) as string | undefined\n\n if (customUrl) {\n FetchResponse.setUrl(customUrl, clonedResponse)\n }\n\n return clonedResponse\n }\n}\n"],"mappings":"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"}
import { i as formatRequest } from "./create-request-id-DlEd4GOA.js";
import { a as isResponseLike, c as RequestController, i as isResponseError, l as InterceptorError, o as isObject, r as createServerErrorResponse } from "./fetch-utils-zxA_SD66.js";
import { TypedEvent } from "rettime";
import { until } from "@open-draft/until";
//#region src/events/http.ts
var HttpRequestEvent = class extends TypedEvent {
constructor(data) {
super(...["request", {}]);
this.request = data.request;
this.requestId = data.requestId;
this.initiator = data.initiator;
this.controller = data.controller;
}
};
var HttpResponseEvent = class extends TypedEvent {
constructor(data) {
super(...["response", {}]);
this.response = data.response;
this.responseType = data.responseType;
this.request = data.request;
this.requestId = data.requestId;
this.initiator = data.initiator;
}
};
var UnhandledHttpException = class extends TypedEvent {
constructor(data) {
super(...["unhandledException", {}]);
this.error = data.error;
this.request = data.request;
this.requestId = data.requestId;
this.initiator = data.initiator;
this.controller = data.controller;
}
};
//#endregion
//#region src/utils/is-node-like-error.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/handle-request.ts
async function handleRequest(options) {
if (options.logger?.isEnabled("default")) formatRequest(options.request).then((message) => {
options.logger?.info("[%s] %s", options.requestId, message);
});
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 resultError;
if (isNodeLikeError(error)) {
await options.controller.errorWith(error);
return true;
}
if (error instanceof Response) return await handleResponse(error);
return false;
};
const requestAbortPromise = Promise.withResolvers();
let requestAbortReason;
let isRequestAborted = false;
const onAbort = () => {
isRequestAborted = true;
requestAbortReason = options.request.signal?.reason;
requestAbortPromise.reject(requestAbortReason);
};
/**
* @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", onAbort, { once: true });
}
const [resultError] = await until(async () => {
const requestEvent = new HttpRequestEvent({
initiator: options.initiator,
requestId: options.requestId,
request: options.request,
controller: options.controller
});
const requestListenersPromise = options.emitter.emitAsPromise(requestEvent);
await Promise.race([
requestAbortPromise.promise,
requestListenersPromise,
options.controller.handled
]);
/**
* @note If the "request" listener has replaced the request instance,
* propagate that mutation back to the underlying insterceptor.
* This happens with XMLHttpRequest that replaces request instances
* to correctly reflect the "withCredentials" option on the Fetch API request.
*/
if (requestEvent.request !== options.request) options.request = requestEvent.request;
});
options.request.signal?.removeEventListener("abort", onAbort);
if (isRequestAborted) {
await options.controller.errorWith(requestAbortReason);
return;
}
if (resultError) {
if (await handleResponseError(resultError)) return;
if (options.emitter.listenerCount("unhandledException") > 0) {
const unhandledExceptionController = new RequestController(options.request, {
/**
* @note Intentionally empty passthrough handle.
* This controller is created within another controller and we only need
* to know if `unhandledException` listeners handled the 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 options.emitter.emitAsPromise(new UnhandledHttpException({
initiator: options.initiator,
error: resultError,
request: options.request,
requestId: options.requestId,
controller: unhandledExceptionController
}));
if (unhandledExceptionController.readyState !== RequestController.PENDING) return;
}
await options.controller.respondWith(createServerErrorResponse(resultError));
return;
}
if (options.controller.readyState === RequestController.PENDING) return await options.controller.passthrough();
return options.controller.handled;
}
//#endregion
export { HttpResponseEvent as n, handleRequest as t };
//# sourceMappingURL=handle-request-CIOa9O-N.js.map
{"version":3,"file":"handle-request-CIOa9O-N.js","names":[],"sources":["../../src/events/http.ts","../../src/utils/is-node-like-error.ts","../../src/utils/handle-request.ts"],"sourcesContent":["import { TypedEvent } from 'rettime'\nimport type { RequestController } from '../request-controller'\n\nexport interface HttpRequestEventData {\n request: Request\n requestId: string\n initiator: unknown\n controller: RequestController\n}\n\nexport class HttpRequestEvent<\n DataType extends HttpRequestEventData = HttpRequestEventData,\n> extends TypedEvent<DataType, void, 'request'> {\n public request: Request\n public requestId: string\n public initiator: unknown\n public controller: RequestController\n\n constructor(data: DataType) {\n super(...(['request', {}] as any))\n\n this.request = data.request\n this.requestId = data.requestId\n this.initiator = data.initiator\n this.controller = data.controller\n }\n}\n\nexport type HttpResponseType = 'mock' | 'original'\n\ninterface HttpResponseEventData {\n response: Response\n responseType: HttpResponseType\n request: Request\n requestId: string\n initiator: unknown\n}\n\nexport class HttpResponseEvent<\n DataType extends HttpResponseEventData = HttpResponseEventData,\n> extends TypedEvent<DataType, void, 'response'> {\n public response: Response\n public responseType: HttpResponseType\n public request: Request\n public requestId: string\n public initiator: unknown\n\n constructor(data: DataType) {\n super(...(['response', {}] as any))\n\n this.response = data.response\n this.responseType = data.responseType\n this.request = data.request\n this.requestId = data.requestId\n this.initiator = data.initiator\n }\n}\n\ninterface UnhandledHttpExceptionEventData {\n error: unknown\n request: Request\n requestId: string\n initiator: unknown\n controller: RequestController\n}\n\nexport class UnhandledHttpException<\n DataType extends UnhandledHttpExceptionEventData =\n UnhandledHttpExceptionEventData,\n> extends TypedEvent<DataType, void, 'unhandledException'> {\n public error: unknown\n public request: Request\n public requestId: string\n public initiator: unknown\n public controller: RequestController\n\n constructor(data: DataType) {\n super(...(['unhandledException', {}] as any))\n\n this.error = data.error\n this.request = data.request\n this.requestId = data.requestId\n this.initiator = data.initiator\n this.controller = data.controller\n }\n}\n\nexport type HttpRequestEventMap = {\n request: HttpRequestEvent\n response: HttpResponseEvent\n unhandledException: UnhandledHttpException\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 'rettime'\nimport { until } from '@open-draft/until'\nimport {\n HttpRequestEvent,\n HttpRequestEventData,\n UnhandledHttpException,\n type HttpRequestEventMap,\n} from '../events/http'\nimport { RequestController } from '../request-controller'\nimport {\n createServerErrorResponse,\n isResponseError,\n isResponseLike,\n} from './response-utils'\nimport { InterceptorError } from '../interceptor-error'\nimport { isNodeLikeError } from './is-node-like-error'\nimport { isObject } from './is-object'\nimport { formatRequest, type Logger } from './logger'\n\nexport interface HandleRequestOptions {\n initiator: unknown\n requestId: string\n request: Request\n emitter: Emitter<HttpRequestEventMap>\n controller: RequestController\n logger?: Logger\n}\n\nexport async function handleRequest(\n options: HandleRequestOptions\n): Promise<void> {\n if (options.logger?.isEnabled('default')) {\n void formatRequest(options.request).then((message) => {\n options.logger?.info('[%s] %s', options.requestId, message)\n })\n }\n\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 resultError\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 const requestAbortPromise = Promise.withResolvers<void>()\n let requestAbortReason: unknown\n let isRequestAborted = false\n const onAbort = () => {\n isRequestAborted = true\n requestAbortReason = options.request.signal?.reason\n requestAbortPromise.reject(requestAbortReason)\n }\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('abort', onAbort, { once: true })\n }\n\n const [resultError] = 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 requestEventData: HttpRequestEventData = {\n initiator: options.initiator,\n requestId: options.requestId,\n request: options.request,\n controller: options.controller,\n }\n const requestEvent = new HttpRequestEvent(requestEventData)\n const requestListenersPromise = options.emitter.emitAsPromise(requestEvent)\n\n await Promise.race([\n // Short-circuit the request handling promise if the request gets aborted.\n requestAbortPromise.promise,\n requestListenersPromise,\n options.controller.handled,\n ])\n\n /**\n * @note If the \"request\" listener has replaced the request instance,\n * propagate that mutation back to the underlying insterceptor.\n * This happens with XMLHttpRequest that replaces request instances\n * to correctly reflect the \"withCredentials\" option on the Fetch API request.\n */\n if (requestEvent.request !== options.request) {\n options.request = requestEvent.request\n }\n })\n\n options.request.signal?.removeEventListener('abort', onAbort)\n\n // Handle the request being aborted while waiting for the request listeners.\n if (isRequestAborted) {\n await options.controller.errorWith(requestAbortReason)\n return\n }\n\n if (resultError) {\n // Handle the error during the request listener execution.\n // These can be thrown responses or request errors.\n if (await handleResponseError(resultError)) {\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 options.emitter.emitAsPromise(\n new UnhandledHttpException({\n initiator: options.initiator,\n error: resultError,\n request: options.request,\n requestId: options.requestId,\n controller: unhandledExceptionController,\n })\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(resultError)\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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"}
import { n as Interceptor, r as createLogger, t as createRequestId } from "./create-request-id-DlEd4GOA.js";
import { c as RequestController, i as isResponseError, n as FetchResponse, s as copyRawHeaders } from "./fetch-utils-zxA_SD66.js";
import { n as HttpResponseEvent, t as handleRequest } from "./handle-request-CIOa9O-N.js";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./has-configurable-global-Cew-dYqk.js";
import { until } from "@open-draft/until";
//#region src/interceptors/fetch/utils/create-network-error.ts
function createNetworkError(cause) {
return Object.assign(/* @__PURE__ */ new TypeError("Failed to fetch"), { cause });
}
//#endregion
//#region src/interceptors/fetch/utils/follow-redirect.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, transform) => readable.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/web.ts
const logger = createLogger("fetch");
/**
* Interceptor for `fetch` requests in the browser.
*/
var FetchInterceptor = class extends Interceptor {
static {
this.symbol = Symbol.for("fetch-interceptor");
}
predicate() {
return hasConfigurableGlobal("fetch");
}
async setup() {
logger.verbose("patching global fetch...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "fetch", (realFetch) => {
return async (input, init) => {
const requestId = createRequestId();
/**
* @note Resolve potentially relative request URL
* against the present `location`. This is mainly
* for native `fetch` in JSDOM.
* @see https://github.com/mswjs/msw/issues/1625
*/
const resolvedInput = typeof input === "string" && typeof location !== "undefined" && !URL.canParse(input) ? new URL(input, location.href) : input;
const request = new Request(resolvedInput, init);
const responsePromise = Promise.withResolvers();
const controller = new RequestController(request, {
passthrough: async () => {
logger.verbose("performing request as-is");
/**
* @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 [responseError, originalResponse] = await until(() => realFetch(request));
if (responseError) return responsePromise.reject(responseError);
logger.verbose("original fetch performed %o", originalResponse);
if (this.emitter.listenerCount("response") > 0) {
logger.verbose("emitting the \"response\" event");
const responseClone = FetchResponse.clone(originalResponse);
await this.emitter.emitAsPromise(new HttpResponseEvent({
initiator: requestCloneForResponseEvent,
request: requestCloneForResponseEvent,
requestId,
response: responseClone,
responseType: "original"
}));
}
responsePromise.resolve(originalResponse);
},
respondWith: async (rawResponse) => {
if (isResponseError(rawResponse)) {
logger.verbose("request errored %o", { response: rawResponse });
responsePromise.reject(createNetworkError(rawResponse));
return;
}
const response = new FetchResponse(decompressResponse(rawResponse) || rawResponse.body, {
url: request.url,
status: rawResponse.status,
statusText: rawResponse.statusText,
headers: rawResponse.headers
});
copyRawHeaders(rawResponse.headers, response.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) => {
responsePromise.resolve(response);
}, (reason) => {
responsePromise.reject(reason);
});
return;
}
}
if (this.emitter.listenerCount("response") > 0) {
logger.verbose("emitting the \"response\" event");
await this.emitter.emitAsPromise(new HttpResponseEvent({
initiator: request,
response: FetchResponse.clone(response),
responseType: "mock",
request,
requestId
}));
}
responsePromise.resolve(response);
},
errorWith: (reason) => {
logger.verbose("request aborted %o", { reason });
responsePromise.reject(reason);
}
}, {
logger,
requestId
});
logger.verbose("awaiting request resolution");
logger.verbose("emitting the \"request\" event for %s listener(s)...", this.emitter.listenerCount("request"));
/**
* @note Give the consumer a chance to abort the request before
* it is dispatched. Fetch queues the request processing as a
* task, so a signal aborted synchronously after `fetch()` must
* prevent the request from ever reaching the "request" listeners.
* Without this, the first listener is invoked synchronously
* within the `fetch()` call itself.
*/
await Promise.resolve();
await handleRequest({
initiator: request,
request,
requestId,
emitter: this.emitter,
controller,
logger
});
return responsePromise.promise;
};
}));
logger.verbose("global fetch patched: %s", globalThis.fetch.name);
}
};
//#endregion
export { FetchInterceptor as t };
//# sourceMappingURL=web-BjVDkiX0.js.map
{"version":3,"file":"web-BjVDkiX0.js","names":[],"sources":["../../src/interceptors/fetch/utils/create-network-error.ts","../../src/interceptors/fetch/utils/follow-redirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts","../../src/interceptors/fetch/web.ts"],"sourcesContent":["export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './create-network-error'\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 { HttpResponseEvent, type HttpRequestEventMap } from '../../events/http'\nimport { RequestController } from '../../request-controller'\nimport { handleRequest } from '../../utils/handle-request'\nimport { createRequestId } from '../../create-request-id'\nimport { createNetworkError } from './utils/create-network-error'\nimport { followFetchRedirect } from './utils/follow-redirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/has-configurable-global'\nimport { FetchResponse } from '../../utils/fetch-utils'\nimport { isResponseError } from '../../utils/response-utils'\nimport { patchesRegistry } from '../../utils/patches-registry'\nimport { copyRawHeaders } from '../ClientRequest/utils/record-raw-headers'\nimport { Interceptor } from '../../interceptor'\nimport { createLogger } from '../../utils/logger'\n\nconst logger = createLogger('fetch')\n\n/**\n * Interceptor for `fetch` requests in the browser.\n */\nexport class FetchInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol.for('fetch-interceptor')\n\n protected predicate() {\n return hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n logger.verbose('patching global fetch...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'fetch', (realFetch) => {\n return 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 !URL.canParse(input)\n ? new URL(input, location.href)\n : input\n\n const request = new Request(resolvedInput, init)\n\n const responsePromise = Promise.withResolvers<Response>()\n\n const controller = new RequestController(\n request,\n {\n passthrough: async () => {\n logger.verbose('performing request as-is')\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 [responseError, originalResponse] = await until(() =>\n realFetch(request)\n )\n\n if (responseError) {\n return responsePromise.reject(responseError)\n }\n\n logger.verbose('original fetch performed %o', originalResponse)\n\n if (this.emitter.listenerCount('response') > 0) {\n logger.verbose('emitting the \"response\" event')\n\n const responseClone = FetchResponse.clone(originalResponse)\n await this.emitter.emitAsPromise(\n new HttpResponseEvent({\n initiator: requestCloneForResponseEvent,\n request: requestCloneForResponseEvent,\n requestId,\n response: responseClone,\n responseType: 'original',\n })\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 logger.verbose('request errored %o', {\n response: rawResponse,\n })\n responsePromise.reject(createNetworkError(rawResponse))\n return\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 copyRawHeaders(rawResponse.headers, response.headers)\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(\n createNetworkError('unexpected redirect')\n )\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 logger.verbose('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 this.emitter.emitAsPromise(\n new HttpResponseEvent({\n initiator: request,\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 responseType: 'mock',\n request,\n requestId,\n })\n )\n }\n\n responsePromise.resolve(response)\n },\n errorWith: (reason) => {\n logger.verbose('request aborted %o', { reason })\n responsePromise.reject(reason)\n },\n },\n {\n logger,\n requestId,\n }\n )\n\n logger.verbose('awaiting request resolution')\n\n logger.verbose(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n /**\n * @note Give the consumer a chance to abort the request before\n * it is dispatched. Fetch queues the request processing as a\n * task, so a signal aborted synchronously after `fetch()` must\n * prevent the request from ever reaching the \"request\" listeners.\n * Without this, the first listener is invoked synchronously\n * within the `fetch()` call itself.\n */\n await Promise.resolve()\n\n await handleRequest({\n initiator: request,\n request,\n requestId,\n emitter: this.emitter,\n controller,\n logger,\n })\n\n return responsePromise.promise\n }\n })\n )\n\n logger.verbose('global fetch patched: %s', globalThis.fetch.name)\n }\n}\n"],"mappings":"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"}
import { n as Interceptor, r as createLogger, t as createRequestId } from "./create-request-id-DlEd4GOA.js";
import { c as RequestController, i as isResponseError, n as FetchResponse, t as FetchRequest } from "./fetch-utils-zxA_SD66.js";
import { n as encodeBuffer, r as toArrayBuffer, t as decodeBuffer } from "./buffer-utils-DJj7YzLG.js";
import { n as HttpResponseEvent, t as handleRequest } from "./handle-request-CIOa9O-N.js";
import { n as patchesRegistry, t as hasConfigurableGlobal } from "./has-configurable-global-Cew-dYqk.js";
import { invariant } from "outvariant";
import { until } from "@open-draft/until";
//#region src/interceptors/XMLHttpRequest/utils/concat-array-buffer.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/event-polyfill.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/progress-event-polyfill.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/create-event.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/find-property-source.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/create-proxy.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 = value.bind(target, ...args);
if (typeof methodCall !== "undefined") return methodCall.call(target, [propertyName, args], next);
return next();
};
return value;
};
return handler;
}
//#endregion
//#region src/interceptors/XMLHttpRequest/utils/is-dom-parser-supported-type.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/parse-json.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/create-response.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/get-body-byte-length.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/xml-http-request-controller.ts
const kIsRequestHandled = Symbol("kIsRequestHandled");
const kFetchRequest = Symbol("kFetchRequest");
const MAX_REDIRECTS = 20;
/**
* 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.sync = false;
this.method = "GET";
this.url = null;
this[kIsRequestHandled] = false;
this.redirectCount = 0;
this.events = /* @__PURE__ */ new Map();
this.uploadEvents = /* @__PURE__ */ new Map();
this.requestId = createRequestId();
this.requestHeaders = new Headers();
this.responseBuffer = /* @__PURE__ */ new Uint8Array();
this.request = createProxy(initialRequest, { methodCall: ([methodName, args], invoke) => {
switch (methodName) {
case "open": {
const [method, url, async] = args;
this.sync = !(async ?? true);
if (typeof url === "undefined") {
this.method = "GET";
this.url = toAbsoluteUrl(method);
} else {
this.method = method;
this.url = toAbsoluteUrl(url);
}
this.logger.verbose("open %s %s", this.method, this.url.href);
return invoke();
}
case "addEventListener": {
const [eventName, listener] = args;
this.registerEvent(eventName, listener);
this.logger.verbose("addEventListener", eventName, listener);
return invoke();
}
case "setRequestHeader": {
const [name, value] = args;
this.requestHeaders.set(name, value);
this.logger.verbose("setRequestHeader", name, value);
return invoke();
}
case "send": {
const [body] = args;
if (this.sync) {
console.warn(`Failed to intercept an XMLHttpRequest (${this.method} ${this.url}): synchronous requests are not supported. This request will be performed as-is.`);
return invoke();
}
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,
responseType: this[kIsRequestHandled] ? "mock" : "original",
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.verbose("request callback settled but request has not been handled (readystate %d), performing as-is...", this.request.readyState);
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.verbose("upload.addEventListener", eventName, listener);
return invoke();
}
}
}
}));
}
registerEvent(eventName, listener) {
const nextEvents = (this.events.get(eventName) || []).concat(listener);
this.events.set(eventName, nextEvents);
this.logger.verbose("registered event \"%s\"", eventName, listener);
}
registerUploadEvent(eventName, listener) {
const nextEvents = (this.uploadEvents.get(eventName) || []).concat(listener);
this.uploadEvents.set(eventName, nextEvents);
this.logger.verbose("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;
this.logger.verbose("responding with a mocked response: %d %s", response.status, response.statusText);
FetchResponse.setUrl(this.url.href, response);
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
}
});
this.trigger("loadstart", this.request, {
loaded: 0,
total: 0
});
let requestBodyTransmitted = 0;
let uploadComplete = false;
if (this[kFetchRequest]) {
const requestBodyLength = await getBodyByteLength(this[kFetchRequest].clone());
if (!uploadComplete) this.trigger("loadstart", this.request.upload, {
loaded: 0,
total: requestBodyLength
});
const processRequestBodyChunkLength = (bytesLength) => {
requestBodyTransmitted += bytesLength;
if (requestBodyTransmitted < requestBodyLength) this.trigger("progress", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
};
const processRequestEndOfBody = () => {
uploadComplete = true;
this.trigger("progress", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
this.trigger("load", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
this.trigger("loadend", this.request.upload, {
loaded: requestBodyTransmitted,
total: requestBodyLength
});
};
if (this[kFetchRequest]?.body != null) {
const reader = this[kFetchRequest].body.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) {
processRequestEndOfBody();
break;
}
processRequestBodyChunkLength(value.byteLength);
}
} else processRequestEndOfBody();
}
let timedOut = false;
const responseReadController = new AbortController();
const requestErrorSteps = (event) => {
this.setReadyState(this.request.DONE);
if (!uploadComplete) {
this.trigger(event, this.request.upload, {
loaded: 0,
total: 0
});
this.trigger("loadend", this.request.upload, {
loaded: 0,
total: 0
});
}
this.trigger(event, this.request, {
loaded: 0,
total: 0
});
this.trigger("loadend", this.request, {
loaded: 0,
total: 0
});
};
const processResponse = async (response) => {
const handleErrors = () => {
if (timedOut) requestErrorSteps("timeout");
else if (responseReadController.signal.aborted) requestErrorSteps("abort");
else if (isResponseError(response)) requestErrorSteps("error");
};
handleErrors();
define(this.request, "status", response.status);
define(this.request, "statusText", response.statusText);
if (!this.request.responseURL) define(this.request, "responseURL", response.url);
if (isResponseError(response)) return;
/**
* @note The response body length is derived ONLY from the "content-length" header.
* If that response header is not set, the "total" in all progress events must be 0.
*/
const responseBodyLength = Number(response.headers.get("content-length") ?? "0");
this.setReadyState(this.request.HEADERS_RECEIVED);
let receivedBytes = 0;
let lastReceivedResponseBytesAt = performance.now();
const processResponseBodyChunk = (bytesLength) => {
receivedBytes += bytesLength;
const now = performance.now();
const shouldBuffer = now - lastReceivedResponseBytesAt <= 60 && receivedBytes < responseBodyLength;
lastReceivedResponseBytesAt = now;
if (shouldBuffer) return;
if (this.request.readyState === this.request.HEADERS_RECEIVED) this.setReadyState(this.request.LOADING, false);
this.trigger("readystatechange", this.request);
this.trigger("progress", this.request, {
loaded: receivedBytes,
total: responseBodyLength
});
};
const processResponseBodyError = () => {
requestErrorSteps("error");
};
const processResponseEndOfBody = async () => {
handleErrors();
if (isResponseError(response)) return;
let transmitted = receivedBytes;
this.setReadyState(this.request.DONE);
this.trigger("load", this.request, {
loaded: transmitted,
total: responseBodyLength
});
this.trigger("loadend", this.request, {
loaded: transmitted,
total: responseBodyLength
});
};
if (response.body == null) processResponseEndOfBody();
else {
const reader = response.body.getReader();
while (true) {
if (responseReadController.signal.aborted) break;
try {
const { value, done } = await reader.read();
if (done) {
processResponseEndOfBody();
return;
}
processResponseBodyChunk(value.byteLength);
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value);
} catch {
processResponseBodyError();
}
}
}
};
const [redirectError, finalResponse] = await until(() => {
return this.followRedirects(response);
});
if (redirectError) return;
processResponse(finalResponse);
if (this.request.timeout) setTimeout(() => {
if (this.request.readyState !== this.request.DONE) {
timedOut = true;
responseReadController.abort();
}
}, this.request.timeout);
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, { apply: (_, __, args) => {
this.logger.verbose("getResponseHeader", args[0]);
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.verbose("headers not received yet, returning null");
return null;
}
const headerValue = finalResponse.headers.get(args[0]);
this.logger.verbose("resolved response header \"%s\" to", args[0], headerValue);
return headerValue;
} });
this.request.getAllResponseHeaders = new Proxy(this.request.getAllResponseHeaders, { apply: () => {
this.logger.verbose("getAllResponseHeaders");
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
this.logger.verbose("headers not received yet, returning empty string");
return "";
}
const allHeaders = Array.from(finalResponse.headers).map(([headerName, headerValue]) => {
return `${headerName}: ${headerValue}`;
}).join("\r\n");
this.logger.verbose("resolved all response headers to", allHeaders);
return allHeaders;
} });
}
responseBufferToText() {
return decodeBuffer(this.responseBuffer);
}
get response() {
this.logger.verbose("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.verbose("resolved response JSON", responseJson);
return responseJson;
}
case "arraybuffer": {
const arrayBuffer = toArrayBuffer(this.responseBuffer);
this.logger.verbose("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.verbose("resolved response Blob (mime type: %s)", responseBlob, mimeType);
return responseBlob;
}
default: {
const responseText = this.responseBufferToText();
this.logger.verbose("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.verbose("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;
}
async followRedirects(response) {
const redirectLocation = response.headers.get("location");
if (!redirectLocation || !FetchResponse.isRedirectResponse(response.status)) return response;
this.redirectCount++;
if (this.redirectCount > MAX_REDIRECTS) throw new Error("Too many redirects");
const redirectUrl = new URL(redirectLocation, location.href);
const redirectMethod = FetchResponse.isResponseWithBody(response.status) ? this.method : "GET";
const redirectResponse = await new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.responseType = this.request.responseType;
request.addEventListener("load", () => {
this.url = new URL(request.responseURL);
resolve(createResponse(request, request.response));
});
request.addEventListener("error", () => {
this.errorWith();
reject(/* @__PURE__ */ new Error("Redirect request failed"));
});
request.open(redirectMethod, redirectUrl.href);
request.send();
});
return this.followRedirects(redirectResponse);
}
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.verbose("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, triggerReadyStateChangeEvent = true) {
this.logger.verbose("setReadyState: %d -> %d", this.request.readyState, nextReadyState);
if (this.request.readyState === nextReadyState) {
this.logger.verbose("ready state identical, skipping transition...");
return;
}
define(this.request, "readyState", nextReadyState);
this.logger.verbose("set readyState to: %d", nextReadyState);
if (!triggerReadyStateChangeEvent) return;
if (nextReadyState !== this.request.UNSENT) {
this.logger.verbose("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.verbose("trigger \"%s\"", eventName, options || "");
if (typeof callback === "function") {
this.logger.verbose("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.verbose("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.verbose("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,
/**
* @see https://xhr.spec.whatwg.org/#cross-origin-credentials
*/
credentials: this.request.withCredentials ? "include" : "same-origin",
body: 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();
} }));
this.logger.verbose("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/xml-http-request-proxy.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.verbose("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.verbose("no mocked response received, performing request as-is...");
},
respondWith: async (response) => {
await this.respondWith(response);
},
errorWith: (reason) => {
this.logger.verbose("request errored %o", { error: reason });
if (reason instanceof Error) this.errorWith(reason);
}
}, {
logger,
requestId
});
this.logger.verbose("awaiting mocked response");
this.logger.verbose("emitting the \"request\" event for %s listener(s)...", emitter.listenerCount("request"));
await handleRequest({
initiator: this.request,
request,
requestId,
controller,
emitter,
logger
});
};
xhrRequestController.onResponse = async function({ response, responseType, request, requestId }) {
this.logger.verbose("emitting the \"response\" event for %s listener(s)...", emitter.listenerCount("response"));
await emitter.emitAsPromise(new HttpResponseEvent({
initiator: this.request,
response,
responseType,
request,
requestId
}));
};
return xhrRequestController.request;
} });
}
//#endregion
//#region src/interceptors/XMLHttpRequest/web.ts
const logger = createLogger("xhr");
var XMLHttpRequestInterceptor = class extends Interceptor {
static {
this.symbol = Symbol.for("xhr-interceptor");
}
predicate() {
return hasConfigurableGlobal("XMLHttpRequest");
}
setup() {
logger.verbose("patching \"XMLHttpRequest\"...");
this.subscriptions.push(patchesRegistry.applyPatch(globalThis, "XMLHttpRequest", () => {
return createXMLHttpRequestProxy({
emitter: this.emitter,
logger
});
}));
logger.verbose("global \"XMLHttpRequest\" patched!", globalThis.XMLHttpRequest.name);
}
};
//#endregion
export { XMLHttpRequestInterceptor as t };
//# sourceMappingURL=web-C2oKsT3Q.js.map
{"version":3,"file":"web-C2oKsT3Q.js","names":[],"sources":["../../src/interceptors/XMLHttpRequest/utils/concat-array-buffer.ts","../../src/interceptors/XMLHttpRequest/polyfills/event-polyfill.ts","../../src/interceptors/XMLHttpRequest/polyfills/progress-event-polyfill.ts","../../src/interceptors/XMLHttpRequest/utils/create-event.ts","../../src/utils/find-property-source.ts","../../src/utils/create-proxy.ts","../../src/interceptors/XMLHttpRequest/utils/is-dom-parser-supported-type.ts","../../src/utils/parse-json.ts","../../src/interceptors/XMLHttpRequest/utils/create-response.ts","../../src/interceptors/XMLHttpRequest/utils/get-body-byte-length.ts","../../src/interceptors/XMLHttpRequest/xml-http-request-controller.ts","../../src/interceptors/XMLHttpRequest/xml-http-request-proxy.ts","../../src/interceptors/XMLHttpRequest/web.ts"],"sourcesContent":["/**\n * Concatenate two `Uint8Array` buffers.\n */\nexport function concatArrayBuffer(\n left: Uint8Array,\n right: Uint8Array\n) {\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 './event-polyfill'\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/event-polyfill'\nimport { ProgressEventPolyfill } from '../polyfills/progress-event-polyfill'\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 './find-property-source'\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/fetch-utils'\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 { until } from '@open-draft/until'\nimport { invariant } from 'outvariant'\nimport type { Logger } from '../../utils/logger'\nimport type { HttpResponseType } from '../../events/http'\nimport { concatArrayBuffer } from './utils/concat-array-buffer'\nimport { createEvent } from './utils/create-event'\nimport {\n decodeBuffer,\n encodeBuffer,\n toArrayBuffer,\n} from '../../utils/buffer-utils'\nimport { createProxy } from '../../utils/create-proxy'\nimport { isDomParserSupportedType } from './utils/is-dom-parser-supported-type'\nimport { parseJson } from '../../utils/parse-json'\nimport { createResponse } from './utils/create-response'\nimport { createRequestId } from '../../create-request-id'\nimport { getBodyByteLength } from './utils/get-body-byte-length'\nimport { FetchRequest, FetchResponse } from '../../utils/fetch-utils'\nimport { isResponseError } from '../../utils/response-utils'\n\nconst kIsRequestHandled = Symbol('kIsRequestHandled')\nconst kFetchRequest = Symbol('kFetchRequest')\nconst MAX_REDIRECTS = 20\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 responseType: HttpResponseType\n request: Request\n requestId: string\n }\n ) => void;\n\n [kIsRequestHandled]: boolean;\n [kFetchRequest]?: Request\n\n private sync: boolean = false\n private method: string = 'GET'\n private url: URL = null as any\n private requestHeaders: Headers\n private responseBuffer: Uint8Array<ArrayBuffer>\n private redirectCount: number\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.redirectCount = 0\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 methodCall: ([methodName, args], invoke) => {\n switch (methodName) {\n case 'open': {\n const [method, url, async] = args as [\n string,\n string | undefined,\n boolean | undefined,\n ]\n\n this.sync = !(async ?? true)\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.verbose('open %s %s', 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.verbose('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.verbose('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 if (this.sync) {\n console.warn(\n `Failed to intercept an XMLHttpRequest (${this.method} ${this.url}): synchronous requests are not supported. This request will be performed as-is.`\n )\n return invoke()\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 responseType: this[kIsRequestHandled] ? 'mock' : 'original',\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.verbose(\n 'request callback settled but request has not been handled (readystate %d), performing as-is...',\n this.request.readyState\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.verbose('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.verbose('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.verbose('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 this.logger.verbose(\n 'responding with a mocked response: %d %s',\n response.status,\n response.statusText\n )\n\n FetchResponse.setUrl(this.url.href, response)\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 // 1. Fire a progress event named loadstart at this with 0 and 0.\n this.trigger('loadstart', this.request, { loaded: 0, total: 0 })\n\n // 2. Let requestBodyTransmitted be 0.\n let requestBodyTransmitted = 0\n let uploadComplete = false\n\n if (this[kFetchRequest]) {\n const requestBodyLength = await getBodyByteLength(\n this[kFetchRequest].clone()\n )\n\n // 5. If this’s upload complete flag is unset and this’s upload listener flag is set, then fire a progress event named loadstart at this’s upload object with requestBodyTransmitted and requestBodyLength.\n if (!uploadComplete) {\n this.trigger('loadstart', this.request.upload, {\n loaded: 0,\n total: requestBodyLength,\n })\n }\n\n const processRequestBodyChunkLength = (bytesLength: number) => {\n requestBodyTransmitted += bytesLength\n\n if (requestBodyTransmitted < requestBodyLength) {\n this.trigger('progress', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n }\n }\n\n const processRequestEndOfBody = () => {\n uploadComplete = true\n\n this.trigger('progress', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n this.trigger('load', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n this.trigger('loadend', this.request.upload, {\n loaded: requestBodyTransmitted,\n total: requestBodyLength,\n })\n }\n\n if (this[kFetchRequest]?.body != null) {\n const reader = this[kFetchRequest].body.getReader()\n\n while (true) {\n const { value, done } = await reader.read()\n\n if (done) {\n processRequestEndOfBody()\n break\n }\n\n processRequestBodyChunkLength(value.byteLength)\n }\n } else {\n processRequestEndOfBody()\n }\n }\n\n let timedOut = false\n const responseReadController = new AbortController()\n\n const requestErrorSteps = (\n event: keyof XMLHttpRequestEventTargetEventMap\n ) => {\n this.setReadyState(this.request.DONE)\n\n if (!uploadComplete) {\n this.trigger(event, this.request.upload, {\n loaded: 0,\n total: 0,\n })\n this.trigger('loadend', this.request.upload, {\n loaded: 0,\n total: 0,\n })\n }\n\n this.trigger(event, this.request, { loaded: 0, total: 0 })\n this.trigger('loadend', this.request, { loaded: 0, total: 0 })\n }\n\n const processResponse = async (response: Response) => {\n const handleErrors = () => {\n if (timedOut) {\n requestErrorSteps('timeout')\n } else if (responseReadController.signal.aborted) {\n requestErrorSteps('abort')\n } else if (isResponseError(response)) {\n requestErrorSteps('error')\n }\n }\n\n handleErrors()\n\n define(this.request, 'status', response.status)\n define(this.request, 'statusText', response.statusText)\n\n if (!this.request.responseURL) {\n define(this.request, 'responseURL', response.url)\n }\n\n if (isResponseError(response)) {\n return\n }\n\n /**\n * @note The response body length is derived ONLY from the \"content-length\" header.\n * If that response header is not set, the \"total\" in all progress events must be 0.\n */\n const responseBodyLength = Number(\n response.headers.get('content-length') ?? '0'\n )\n\n this.setReadyState(this.request.HEADERS_RECEIVED)\n\n let receivedBytes = 0\n let lastReceivedResponseBytesAt = performance.now()\n\n const processResponseBodyChunk = (bytesLength: number) => {\n receivedBytes += bytesLength\n\n const now = performance.now()\n const shouldBuffer =\n now - lastReceivedResponseBytesAt <= 60 &&\n receivedBytes < responseBodyLength\n lastReceivedResponseBytesAt = now\n\n if (shouldBuffer) {\n return\n }\n\n if (this.request.readyState === this.request.HEADERS_RECEIVED) {\n this.setReadyState(this.request.LOADING, false)\n }\n\n this.trigger('readystatechange', this.request)\n this.trigger('progress', this.request, {\n loaded: receivedBytes,\n total: responseBodyLength,\n })\n }\n\n const processResponseBodyError = () => {\n requestErrorSteps('error')\n }\n\n const processResponseEndOfBody = async () => {\n handleErrors()\n\n if (isResponseError(response)) {\n return\n }\n\n // 3. Let transmitted be xhr’s received bytes’s length.\n let transmitted = receivedBytes\n\n // 9. Fire an event named readystatechange at xhr.\n this.setReadyState(this.request.DONE)\n // 10. Fire a progress event named load at xhr with transmitted and length.\n this.trigger('load', this.request, {\n loaded: transmitted,\n total: responseBodyLength,\n })\n // 11. Fire a progress event named loadend at xhr with transmitted and length.\n this.trigger('loadend', this.request, {\n loaded: transmitted,\n total: responseBodyLength,\n })\n }\n\n // 7. If this’s response’s body is null, then run handle response end-of-body for this and return.\n if (response.body == null) {\n processResponseEndOfBody()\n } else {\n const reader = response.body.getReader()\n\n while (true) {\n if (responseReadController.signal.aborted) {\n break\n }\n\n try {\n const { value, done } = await reader.read()\n\n if (done) {\n processResponseEndOfBody()\n return\n }\n\n processResponseBodyChunk(value.byteLength)\n this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)\n } catch {\n processResponseBodyError()\n }\n }\n }\n }\n\n // Redirects are followed as a part of the fetch controller. Since we don't have one,\n // retrieve the final response and then continue with processing it instead of the mocked one.\n const [redirectError, finalResponse] = await until(() => {\n return this.followRedirects(response)\n })\n\n if (redirectError) {\n return\n }\n\n processResponse(finalResponse)\n\n // 12.1, 12.2.\n if (this.request.timeout) {\n setTimeout(() => {\n if (this.request.readyState !== this.request.DONE) {\n timedOut = true\n responseReadController.abort()\n }\n }, this.request.timeout)\n }\n\n this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {\n apply: (_, __, args: [name: string]) => {\n this.logger.verbose('getResponseHeader', args[0])\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.verbose('headers not received yet, returning null')\n return null\n }\n\n const headerValue = finalResponse.headers.get(args[0])\n this.logger.verbose(\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.verbose('getAllResponseHeaders')\n\n if (this.request.readyState < this.request.HEADERS_RECEIVED) {\n this.logger.verbose('headers not received yet, returning empty string')\n return ''\n }\n\n const headersList = Array.from(finalResponse.headers)\n const allHeaders = headersList\n .map(([headerName, headerValue]) => {\n return `${headerName}: ${headerValue}`\n })\n .join('\\r\\n')\n\n this.logger.verbose('resolved all response headers to', allHeaders)\n\n return allHeaders\n },\n }\n )\n }\n\n private responseBufferToText(): string {\n return decodeBuffer(this.responseBuffer)\n }\n\n get response(): unknown {\n this.logger.verbose(\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.verbose('resolved response JSON', responseJson)\n\n return responseJson\n }\n\n case 'arraybuffer': {\n const arrayBuffer = toArrayBuffer(this.responseBuffer)\n this.logger.verbose('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.verbose(\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.verbose(\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.verbose('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 private async followRedirects(response: Response): Promise<Response> {\n const redirectLocation = response.headers.get('location')\n\n if (\n !redirectLocation ||\n !FetchResponse.isRedirectResponse(response.status)\n ) {\n return response\n }\n\n this.redirectCount++\n\n if (this.redirectCount > MAX_REDIRECTS) {\n throw new Error('Too many redirects')\n }\n\n const redirectUrl = new URL(redirectLocation, location.href)\n const redirectMethod = FetchResponse.isResponseWithBody(response.status)\n ? this.method\n : 'GET'\n\n const redirectResponse = await new Promise<Response>((resolve, reject) => {\n const request = new XMLHttpRequest()\n request.responseType = this.request.responseType\n\n request.addEventListener('load', () => {\n this.url = new URL(request.responseURL)\n resolve(createResponse(request, request.response))\n })\n\n request.addEventListener('error', () => {\n this.errorWith()\n reject(new Error('Redirect request failed'))\n })\n\n request.open(redirectMethod, redirectUrl.href)\n request.send()\n })\n\n return this.followRedirects(redirectResponse)\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.verbose('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(\n nextReadyState: number,\n triggerReadyStateChangeEvent = true\n ): void {\n this.logger.verbose(\n 'setReadyState: %d -> %d',\n this.request.readyState,\n nextReadyState\n )\n\n if (this.request.readyState === nextReadyState) {\n this.logger.verbose('ready state identical, skipping transition...')\n return\n }\n\n define(this.request, 'readyState', nextReadyState)\n\n this.logger.verbose('set readyState to: %d', nextReadyState)\n\n if (!triggerReadyStateChangeEvent) {\n return\n }\n\n if (nextReadyState !== this.request.UNSENT) {\n this.logger.verbose('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.verbose('trigger \"%s\"', eventName, options || '')\n\n // Invoke direct callbacks.\n if (typeof callback === 'function') {\n this.logger.verbose('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.verbose(\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.verbose('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: 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.verbose('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 { Emitter } from 'rettime'\nimport { HttpResponseEvent, type HttpRequestEventMap } from '../../events/http'\nimport { RequestController } from '../../request-controller'\nimport { XMLHttpRequestController } from './xml-http-request-controller'\nimport { handleRequest } from '../../utils/handle-request'\nimport type { Logger } from '../../utils/logger'\n\nexport interface XMLHttpRequestProxyOptions {\n emitter: Emitter<HttpRequestEventMap>\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.verbose('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.verbose(\n 'no mocked response received, performing request as-is...'\n )\n },\n respondWith: async (response) => {\n await this.respondWith(response)\n },\n errorWith: (reason) => {\n this.logger.verbose('request errored %o', { error: reason })\n\n if (reason instanceof Error) {\n this.errorWith(reason)\n }\n },\n }, {\n logger,\n requestId,\n })\n\n this.logger.verbose('awaiting mocked response')\n\n this.logger.verbose(\n 'emitting the \"request\" event for %s listener(s)...',\n emitter.listenerCount('request')\n )\n\n await handleRequest({\n initiator: this.request,\n request,\n requestId,\n controller,\n emitter,\n logger,\n })\n }\n\n xhrRequestController.onResponse = async function ({\n response,\n responseType,\n request,\n requestId,\n }) {\n this.logger.verbose(\n 'emitting the \"response\" event for %s listener(s)...',\n emitter.listenerCount('response')\n )\n\n await emitter.emitAsPromise(\n new HttpResponseEvent({\n initiator: this.request,\n response,\n responseType,\n request,\n requestId,\n })\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 { HttpRequestEventMap } from '../../events/http'\nimport { Interceptor } from '../../interceptor'\nimport { createXMLHttpRequestProxy } from './xml-http-request-proxy'\nimport { hasConfigurableGlobal } from '../../utils/has-configurable-global'\nimport { patchesRegistry } from '../../utils/patches-registry'\nimport { createLogger } from '../../utils/logger'\n\nconst logger = createLogger('xhr')\n\nexport class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol.for('xhr-interceptor')\n\n protected predicate() {\n return hasConfigurableGlobal('XMLHttpRequest')\n }\n\n protected setup() {\n logger.verbose('patching \"XMLHttpRequest\"...')\n\n this.subscriptions.push(\n patchesRegistry.applyPatch(globalThis, 'XMLHttpRequest', () => {\n return createXMLHttpRequestProxy({\n emitter: this.emitter,\n logger,\n })\n })\n )\n\n logger.verbose(\n 'global \"XMLHttpRequest\" patched!',\n globalThis.XMLHttpRequest.name\n )\n }\n}\n"],"mappings":"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"}