@mswjs/interceptors
Advanced tools
| 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":";;;;;AAUA,IAAa,mBAAb,cAEU,WAAsC;CAM9C,YAAY,MAAgB;EAC1B,MAAM,GAAI,CAAC,WAAW,CAAC,CAAC,CAAS;EAEjC,KAAK,UAAU,KAAK;EACpB,KAAK,YAAY,KAAK;EACtB,KAAK,YAAY,KAAK;EACtB,KAAK,aAAa,KAAK;CACzB;AACF;AAYA,IAAa,oBAAb,cAEU,WAAuC;CAO/C,YAAY,MAAgB;EAC1B,MAAM,GAAI,CAAC,YAAY,CAAC,CAAC,CAAS;EAElC,KAAK,WAAW,KAAK;EACrB,KAAK,eAAe,KAAK;EACzB,KAAK,UAAU,KAAK;EACpB,KAAK,YAAY,KAAK;EACtB,KAAK,YAAY,KAAK;CACxB;AACF;AAUA,IAAa,yBAAb,cAGU,WAAiD;CAOzD,YAAY,MAAgB;EAC1B,MAAM,GAAI,CAAC,sBAAsB,CAAC,CAAC,CAAS;EAE5C,KAAK,QAAQ,KAAK;EAClB,KAAK,UAAU,KAAK;EACpB,KAAK,YAAY,KAAK;EACtB,KAAK,YAAY,KAAK;EACtB,KAAK,aAAa,KAAK;CACzB;AACF;;;ACrFA,SAAgB,gBACd,OACgC;CAChC,IAAI,SAAS,MACX,OAAO;CAGT,IAAI,EAAE,iBAAiB,QACrB,OAAO;CAGT,OAAO,UAAU,SAAS,WAAW;AACvC;;;ACgBA,eAAsB,cACpB,SACe;CACf,IAAI,QAAQ,QAAQ,UAAU,SAAS,GACrC,cAAmB,QAAQ,OAAO,CAAC,CAAC,MAAM,YAAY;EACpD,QAAQ,QAAQ,KAAK,WAAW,QAAQ,WAAW,OAAO;CAC5D,CAAC;CAGH,MAAM,iBAAiB,OACrB,aACG;EACH,IAAI,oBAAoB,OAAO;GAC7B,MAAM,QAAQ,WAAW,UAAU,QAAQ;GAC3C,OAAO;EACT;EAGA,IAAI,gBAAgB,QAAQ,GAAG;GAC7B,MAAM,QAAQ,WAAW,YAAY,QAAQ;GAC7C,OAAO;EACT;;;;;;EAOA,IAAI,eAAe,QAAQ,GAAG;GAC5B,MAAM,QAAQ,WAAW,YAAY,QAAQ;GAC7C,OAAO;EACT;EAGA,IAAI,SAAS,QAAQ,GAAG;GACtB,MAAM,QAAQ,WAAW,UAAU,QAAQ;GAC3C,OAAO;EACT;EAEA,OAAO;CACT;CAEA,MAAM,sBAAsB,OAAO,UAAqC;EAGtE,IAAI,iBAAiB,kBACnB,MAAM;EAIR,IAAI,gBAAgB,KAAK,GAAG;GAC1B,MAAM,QAAQ,WAAW,UAAU,KAAK;GACxC,OAAO;EACT;EAGA,IAAI,iBAAiB,UACnB,OAAO,MAAM,eAAe,KAAK;EAGnC,OAAO;CACT;CAEA,MAAM,sBAAsB,QAAQ,cAAoB;CACxD,IAAI;CACJ,IAAI,mBAAmB;CACvB,MAAM,gBAAgB;EACpB,mBAAmB;EACnB,qBAAqB,QAAQ,QAAQ,QAAQ;EAC7C,oBAAoB,OAAO,kBAAkB;CAC/C;;;;CAKA,IAAI,QAAQ,QAAQ,QAAQ;EAC1B,IAAI,QAAQ,QAAQ,OAAO,SAAS;GAClC,MAAM,QAAQ,WAAW,UAAU,QAAQ,QAAQ,OAAO,MAAM;GAChE;EACF;EAEA,QAAQ,QAAQ,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC1E;CAEA,MAAM,CAAC,eAAe,MAAM,MAAM,YAAY;EAW5C,MAAM,eAAe,IAAI,iBAAiB;GALxC,WAAW,QAAQ;GACnB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,YAAY,QAAQ;EAEmC,CAAC;EAC1D,MAAM,0BAA0B,QAAQ,QAAQ,cAAc,YAAY;EAE1E,MAAM,QAAQ,KAAK;GAEjB,oBAAoB;GACpB;GACA,QAAQ,WAAW;EACrB,CAAC;;;;;;;EAQD,IAAI,aAAa,YAAY,QAAQ,SACnC,QAAQ,UAAU,aAAa;CAEnC,CAAC;CAED,QAAQ,QAAQ,QAAQ,oBAAoB,SAAS,OAAO;CAG5D,IAAI,kBAAkB;EACpB,MAAM,QAAQ,WAAW,UAAU,kBAAkB;EACrD;CACF;CAEA,IAAI,aAAa;EAGf,IAAI,MAAM,oBAAoB,WAAW,GACvC;EAMF,IAAI,QAAQ,QAAQ,cAAc,oBAAoB,IAAI,GAAG;GAI3D,MAAM,+BAA+B,IAAI,kBACvC,QAAQ,SACR;;;;;;IAME,cAAc,CAAC;IACf,MAAM,YAAY,UAAU;KAC1B,MAAM,eAAe,QAAQ;IAC/B;IACA,MAAM,UAAU,QAAQ;;;;;;;;KAQtB,MAAM,QAAQ,WAAW,UAAU,MAAM;IAC3C;GACF,CACF;GAEA,MAAM,QAAQ,QAAQ,cACpB,IAAI,uBAAuB;IACzB,WAAW,QAAQ;IACnB,OAAO;IACP,SAAS,QAAQ;IACjB,WAAW,QAAQ;IACnB,YAAY;GACd,CAAC,CACH;GAIA,IACE,6BAA6B,eAAe,kBAAkB,SAE9D;EAEJ;EAGA,MAAM,QAAQ,WAAW,YACvB,0BAA0B,WAAW,CACvC;EACA;CACF;CAGA,IAAI,QAAQ,WAAW,eAAe,kBAAkB,SACtD,OAAO,MAAM,QAAQ,WAAW,YAAY;CAG9C,OAAO,QAAQ,WAAW;AAC5B"} |
| 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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"} |
@@ -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 @@ /** |
+1
-1
@@ -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":";;;AAEA,IAAa,aAAb,MAAwB;;EACmC,KAAA,gBAAA,CAAC;;CAE1D,UAAiB;EACf,IAAI;EAEJ,OAAQ,eAAe,KAAK,cAAc,IAAI,GAC5C,aAAa;CAEjB;AACF;;;ACFA,MAAM,uBAAuB;AAE7B,SAAS,mBAAmB,WAA2B;CACrD,OAAO,UACJ,MAAM,GAAG,CAAC,CACV,KAAK,YAAY;EAChB,OAAO,QACJ,QAAQ,sBAAsB,OAAO,CAAC,CACtC,QAAQ,kBAAkB,GAAG,CAAC,CAC9B,QAAQ,UAAU,EAAE,CAAC,CACrB,YAAY;CACjB,CAAC,CAAC,CACD,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb;AAEA,SAAS,eAAuB;CAC9B,wBAAO,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,MAAM,IAAI,EAAE;AAC9C;AAEA,eAAe,SAAS,SAAqD;CAC3E,IAAI,QAAQ,QAAQ,MAClB,OAAO;CAGT,IAAI;EACF,OAAO,MAAM,QAAQ,MAAM,CAAC,CAAC,KAAK;CACpC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,SAAiC;CACtD,OAAO,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW;EAC1D,OAAO,GAAG,KAAK,IAAI;CACrB,CAAC;AACH;AAEA,eAAe,kBACb,WACA,SACiB;CACjB,MAAM,QAAQ,CAAC,WAAW,GAAG,cAAc,QAAQ,OAAO,CAAC;CAC3D,MAAM,OAAO,MAAM,SAAS,OAAO;CAEnC,MAAM,KAAK,IAAI,QAAQ,EAAE;CAEzB,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,eAAsB,cAAc,SAAmC;CACrE,OAAO,kBAAkB,GAAG,QAAQ,OAAO,GAAG,QAAQ,OAAO,OAAO;AACtE;AAEA,eAAsB,eAAe,UAAqC;CACxE,MAAM,aAAa,SAAS,aAAa,IAAI,SAAS,eAAe;CACrE,OAAO,kBACL,QAAQ,SAAS,SAAS,cAC1B,QACF;AACF;AAEA,SAAS,mBAAmB,YAAkC;CAC5D,MAAM,UAAU,WAAW;CAE3B,IAAI,OAAO,YAAY,UAAU;EAC/B,MAAM,+BAA+B,QAAQ,QAC3C,iDACA,EACF;EACA,MAAM,iBAAiB,6BAA6B,MAClD,oBACF;EAEA,IAAI,CAAC,kBAAkB,eAAe,UAAU,KAAA,GAAW;GACzD,WAAW,KAAK;GAChB;EACF;EAEA,MAAM,gBAAgB,6BACnB,MAAM,GAAG,eAAe,KAAK,CAAC,CAC9B,KAAK;EACR,MAAM,cAAc,6BACjB,MAAM,eAAe,QAAQ,eAAe,EAAE,CAAC,MAAM,CAAC,CACtD,UAAU;EAEb,WAAW,KAAK,GAAG,eAAe,GAAG,GAAG,cAAc,GAAG;CAC3D;AACF;AAEA,SAAS,oBAAoB,QAA8B;CACzD,OAAO,OAAO,GAAG,eAAe;EAC9B,mBAAmB,UAAU;EAC7B,MAAM,IAAI,GAAG,UAAU;CACzB;AACF;AAEA,SAAS,0BAAmC;CAC1C,IAAI,OAAO,YAAY,eAAe,QAAQ,IAAI,gBAAgB,WAChE,OAAO;;;;;;;;CAUT,IAAI,OAAO,aAAa,aACtB,OAAO;CAGT,IAAI;EACF,OAAO,WAAW,cAAc,QAAQ,YAAY,MAAM;CAC5D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,aAAa,WAA2B;CAEtD,MAAM,SAAS,MAAM,gBADO,mBAAmB,SACQ,GAAG;CAC1D,QAAQ,IAAI,QAAQ,aAAa,IAAI;CACrC,oBAAoB,MAAM;CAE1B,OAAO;EACL,KAAK,SAAS,GAAG,aAAa;GAC5B,OAAO,GAAG,aAAa,EAAE,GAAG,WAAW,GAAG,WAAW;EACvD;EACA,QAAQ,SAAS,GAAG,aAAa;GAC/B,IAAI,CAAC,wBAAwB,GAC3B;GAGF,OAAO,GAAG,aAAa,EAAE,GAAG,WAAW,GAAG,WAAW;EACvD;EACA,UAAU,OAAO;GACf,OACE,OAAO,YAAY,UAAU,aAAa,wBAAwB;EAEtE;CACF;AACF;;;AC3IA,MAAM,uBAAwB,WAAW,gDACvC,IAAI,IAA8B;AAEpC,IAAsB,cAAtB,cAEU,WAAW;CAUnB;CAEA,OAAO,UACL,kBACG;EACH,MAAM,SAAS,iBAAiB;EAChC,MAAM,WAAW,qBAAqB,IAAI,MAAM;EAEhD,IAAI,oBAAoB,kBACtB,OAAO;EAGT,MAAM,cAAc,IAAI,iBAAiB;EACzC,qBAAqB,IAAI,QAAQ,WAAW;EAC5C,OAAO;CACT;CAEA,cAAc;EACZ,MAAM;EAsD4B,KAAA,MAAA,MAAM,UAAU,YAAY;GAC9D,OAAO,KAAK,QAAQ,GAAG,MAAM,UAAU,OAAO;EAChD;EAEwC,KAAA,QAAA,MAAM,UAAU,YAAY;GAClE,OAAO,KAAK,QAAQ,KAAK,MAAM,UAAU,OAAO;EAClD;EAEkD,KAAA,aAAA,SAAS;GACzD,OAAO,KAAK,QAAQ,UAAU,IAAI;EACpC;EAE0D,KAAA,iBAAA,SAAS;GACjE,OAAO,KAAK,QAAQ,cAAc,IAAI;EACxC;EAGE,KAAA,kBAAA,MACA,aACG;GACH,OAAO,KAAK,QAAQ,eAAe,MAAM,QAAQ;EACnD;EAEoE,KAAA,sBAAA,SAAS;GAC3E,KAAK,OAAO,KAAK,yBAAyB,EAAE,WAAW,QAAQ,IAAI,CAAC;GACpE,OAAO,KAAK,QAAQ,mBAAmB,IAAI;EAC7C;EA9EE,KAAKA,0BAAU,IAAI,IAAI;EACvB,KAAK,aAAA;EACL,KAAK,UAAU,IAAI,QAAQ;EAC3B,KAAK,SAAS,aAAa,KAAKC,oBAAoB,CAAC;CACvD;CAKA,MAAa,QAAgB,MAAY;EACvC,IAAI,KAAKD,QAAQ,IAAI,KAAK,GACxB;EAGF,IACE,KAAK,eAAA,YACL,CAAC,KAAK,UAAU,GAEhB;EAGF,KAAKA,QAAQ,IAAI,KAAK;EAEtB,IAAI,KAAK,eAAA,UACP;EAGF,IAAI;GACF,KAAK,MAAM;GACX,KAAK,aAAA;GACL,KAAK,OAAO,KAAK,OAAO;EAC1B,SAAS,OAAO;GACd,KAAK,QAAQ,KAAK;GAClB,MAAM;EACR;CACF;CAEA,QAAe,QAAgB,MAAY;EACzC,IAAI,CAAC,KAAKA,QAAQ,OAAO,KAAK,GAC5B;EAGF,IAAI,KAAKA,QAAQ,OAAO,GACtB;EAGF,MAAM,QAAQ;EACd,KAAK,QAAQ,mBAAmB;EAChC,KAAK,aAAA;EACL,KAAK,OAAO,KAAK,SAAS;CAC5B;CA8BA,sBAA8B;EAC5B,MAAM,oBAAoB,KAAK,YAAY,QAAQ;EAEnD,IAAI,mBACF,OAAO,kBAAkB,QAAQ,iBAAiB,EAAE;EAGtD,OAAO,KAAK,YAAY,KAAK,QAAQ,gBAAgB,EAAE;CACzD;AACF;;;;;;;;;ACpIA,SAAgB,kBAA0B;CACxC,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC;AAC3C"} |
| 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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"} |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1510855
2.71%21153
2.61%36
-2.7%