vite-plugin-mock-dev-server
Version:
Vite Plugin for API mock dev server.
810 lines (800 loc) • 28.7 kB
JavaScript
const require_dist = require('./dist-DrfpZ4UT.cjs');
const picocolors = require_dist.__toESM(require("picocolors"));
const node_fs = require_dist.__toESM(require("node:fs"));
const node_path = require_dist.__toESM(require("node:path"));
const node_url = require_dist.__toESM(require("node:url"));
const node_os = require_dist.__toESM(require("node:os"));
const node_querystring = require_dist.__toESM(require("node:querystring"));
const debug = require_dist.__toESM(require("debug"));
const path_to_regexp = require_dist.__toESM(require("path-to-regexp"));
const node_buffer = require_dist.__toESM(require("node:buffer"));
const cookies = require_dist.__toESM(require("cookies"));
const http_status = require_dist.__toESM(require("http-status"));
const mime_types = require_dist.__toESM(require("mime-types"));
const co_body = require_dist.__toESM(require("co-body"));
const formidable = require_dist.__toESM(require("formidable"));
const ws = require_dist.__toESM(require("ws"));
//#region src/core/utils.ts
function isStream(stream) {
return stream !== null && typeof stream === "object" && typeof stream.pipe === "function";
}
function isReadableStream(stream) {
return isStream(stream) && stream.readable !== false && typeof stream._read === "function" && typeof stream._readableState === "object";
}
const debug$1 = (0, debug.default)("vite:mock-dev-server");
function lookupFile(dir, formats, options) {
for (const format of formats) {
const fullPath = node_path.default.join(dir, format);
if (node_fs.default.existsSync(fullPath) && node_fs.default.statSync(fullPath).isFile()) {
const result = options?.pathOnly ? fullPath : node_fs.default.readFileSync(fullPath, "utf-8");
if (!options?.predicate || options.predicate(result)) return result;
}
}
const parentDir = node_path.default.dirname(dir);
if (parentDir !== dir && (!options?.rootDir || parentDir.startsWith(options?.rootDir))) return lookupFile(parentDir, formats, options);
}
function ensureProxies(serverProxy = {}) {
const httpProxies = [];
const wsProxies = [];
Object.keys(serverProxy).forEach((key) => {
const value = serverProxy[key];
if (typeof value === "string" || !value.ws && !value.target?.toString().startsWith("ws:") && !value.target?.toString().startsWith("wss:")) httpProxies.push(key);
else wsProxies.push(key);
});
return {
httpProxies,
wsProxies
};
}
function doesProxyContextMatchUrl(context, url) {
return context[0] === "^" && new RegExp(context).test(url) || url.startsWith(context);
}
function parseParams(pattern, url) {
const urlMatch = (0, path_to_regexp.match)(pattern, { decode: decodeURIComponent })(url) || { params: {} };
return urlMatch.params || {};
}
/**
* nodejs 从 19.0.0 开始 弃用 url.parse,因此使用 url.parse 来解析 可能会报错,
* 使用 URL 来解析
*/
function urlParse(input) {
const url = new node_url.URL(input, "http://example.com");
const pathname = decodeURIComponent(url.pathname);
const query = (0, node_querystring.parse)(url.search.replace(/^\?/, ""));
return {
pathname,
query
};
}
const windowsSlashRE = /\\/g;
const isWindows = node_os.default.platform() === "win32";
function slash(p) {
return p.replace(windowsSlashRE, "/");
}
function normalizePath(id) {
return node_path.default.posix.normalize(isWindows ? slash(id) : id);
}
//#endregion
//#region src/core/matchingWeight.ts
const tokensCache = {};
function getTokens(rule) {
if (tokensCache[rule]) return tokensCache[rule];
const tks = (0, path_to_regexp.parse)(rule);
const tokens = [];
for (const tk of tks) if (!require_dist.isString(tk)) tokens.push(tk);
else {
const hasPrefix = tk[0] === "/";
const subTks = hasPrefix ? tk.slice(1).split("/") : tk.split("/");
tokens.push(`${hasPrefix ? "/" : ""}${subTks[0]}`, ...subTks.slice(1).map((t) => `/${t}`));
}
tokensCache[rule] = tokens;
return tokens;
}
function getHighest(rules) {
let weights = rules.map((rule) => getTokens(rule).length);
weights = weights.length === 0 ? [1] : weights;
return Math.max(...weights) + 2;
}
function sortFn(rule) {
const tokens = getTokens(rule);
let w = 0;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (!require_dist.isString(token)) w += 10 ** (i + 1);
w += 10 ** (i + 1);
}
return w;
}
function preSort(rules) {
let matched = [];
const preMatch = [];
for (const rule of rules) {
const tokens = getTokens(rule);
const len = tokens.filter((token) => typeof token !== "string").length;
if (!preMatch[len]) preMatch[len] = [];
preMatch[len].push(rule);
}
for (const match$1 of preMatch.filter((v) => v && v.length > 0)) matched = [...matched, ...require_dist.sortBy(match$1, sortFn).reverse()];
return matched;
}
function defaultPriority(rules) {
const highest = getHighest(rules);
return require_dist.sortBy(rules, (rule) => {
const tokens = getTokens(rule);
const dym = tokens.filter((token) => typeof token !== "string");
if (dym.length === 0) return 0;
let weight = dym.length;
let exp = 0;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
const isDynamic = !require_dist.isString(token);
const { pattern = "", modifier, prefix, name } = isDynamic ? token : {};
const isGlob = pattern && pattern.includes(".*");
const isSlash = prefix === "/";
const isNamed = require_dist.isString(name);
exp += isDynamic && isSlash ? 1 : 0;
if (i === tokens.length - 1 && isGlob) weight += 5 * 10 ** (tokens.length === 1 ? highest + 1 : highest);
else if (isGlob) weight += 3 * 10 ** (highest - 1);
else if (pattern) if (isSlash) weight += (isNamed ? 2 : 1) * 10 ** (exp + 1);
else weight -= 1 * 10 ** exp;
if (modifier === "+") weight += 1 * 10 ** (highest - 1);
if (modifier === "*") weight += 1 * 10 ** (highest - 1) + 1;
if (modifier === "?") weight += 1 * 10 ** (exp + (isSlash ? 1 : 0));
}
return weight;
});
}
function matchingWeight(rules, url, priority) {
let matched = defaultPriority(preSort(rules.filter((rule) => (0, path_to_regexp.pathToRegexp)(rule).test(url))));
const { global = [], special = {} } = priority;
if (global.length === 0 && require_dist.isEmptyObject(special) || matched.length === 0) return matched;
const [statics, dynamics] = twoPartMatch(matched);
const globalMatch = global.filter((rule) => dynamics.includes(rule));
if (globalMatch.length > 0) matched = require_dist.uniq([
...statics,
...globalMatch,
...dynamics
]);
if (require_dist.isEmptyObject(special)) return matched;
const specialRule = Object.keys(special).filter((rule) => matched.includes(rule))[0];
if (!specialRule) return matched;
const options = special[specialRule];
const { rules: lowerRules, when } = require_dist.isArray(options) ? {
rules: options,
when: []
} : options;
if (lowerRules.includes(matched[0])) {
if (when.length === 0 || when.some((path$1) => (0, path_to_regexp.pathToRegexp)(path$1).test(url))) matched = require_dist.uniq([specialRule, ...matched]);
}
return matched;
}
function twoPartMatch(rules) {
const statics = [];
const dynamics = [];
for (const rule of rules) {
const tokens = getTokens(rule);
const dym = tokens.filter((token) => typeof token !== "string");
if (dym.length > 0) dynamics.push(rule);
else statics.push(rule);
}
return [statics, dynamics];
}
//#endregion
//#region src/core/parseReqBody.ts
const DEFAULT_FORMIDABLE_OPTIONS = {
keepExtensions: true,
filename(name, ext, part) {
return part?.originalFilename || `${name}.${Date.now()}${ext ? `.${ext}` : ""}`;
}
};
async function parseReqBody(req, formidableOptions, bodyParserOptions = {}) {
const method = req.method.toUpperCase();
if (["HEAD", "OPTIONS"].includes(method)) return void 0;
const type = req.headers["content-type"]?.toLocaleLowerCase() || "";
const { limit, formLimit, jsonLimit, textLimit,...rest } = bodyParserOptions;
try {
if (type.startsWith("application/json")) return await co_body.default.json(req, {
limit: jsonLimit || limit,
...rest
});
if (type.startsWith("application/x-www-form-urlencoded")) return await co_body.default.form(req, {
limit: formLimit || limit,
...rest
});
if (type.startsWith("text/plain")) return await co_body.default.text(req, {
limit: textLimit || limit,
...rest
});
if (type.startsWith("multipart/form-data")) return await parseMultipart(req, formidableOptions);
} catch (e) {
console.error(e);
}
return void 0;
}
async function parseMultipart(req, options) {
const form = (0, formidable.default)({
...DEFAULT_FORMIDABLE_OPTIONS,
...options
});
return new Promise((resolve, reject) => {
form.parse(req, (error, fields, files) => {
if (error) {
reject(error);
return;
}
resolve({
...fields,
...files
});
});
});
}
//#endregion
//#region src/core/requestRecovery.ts
const cache = /* @__PURE__ */ new WeakMap();
function collectRequest(req) {
const chunks = [];
req.addListener("data", (chunk) => {
chunks.push(node_buffer.Buffer.from(chunk));
});
req.addListener("end", () => {
if (chunks.length) cache.set(req, node_buffer.Buffer.concat(chunks));
});
}
/**
* vite 在 proxy 配置中,允许通过 configure 访问 http-proxy 实例,
* 通过 http-proxy 的 proxyReq 事件,重新写入请求流
*/
function recoverRequest(config) {
if (!config.server) return;
const proxies = config.server.proxy || {};
Object.keys(proxies).forEach((key) => {
const target = proxies[key];
const options = typeof target === "string" ? { target } : target;
if (options.ws) return;
const { configure,...rest } = options;
proxies[key] = {
...rest,
configure(proxy, options$1) {
configure?.(proxy, options$1);
proxy.on("proxyReq", (proxyReq, req) => {
const buffer = cache.get(req);
if (buffer) {
cache.delete(req);
/**
* 使用 http-proxy 的 agent 配置会提前写入代理请求流
* https://github.com/http-party/node-http-proxy/issues/1287
*/
if (!proxyReq.headersSent) proxyReq.setHeader("Content-Length", buffer.byteLength);
if (!proxyReq.writableEnded) proxyReq.write(buffer);
}
});
}
};
});
}
//#endregion
//#region src/core/validator.ts
function validate(request, validator) {
return isObjectSubset(request.headers, validator.headers) && isObjectSubset(request.body, validator.body) && isObjectSubset(request.params, validator.params) && isObjectSubset(request.query, validator.query) && isObjectSubset(request.refererQuery, validator.refererQuery);
}
/**
* Checks if target object is a subset of source object.
* That is, all properties and their corresponding values in target exist in source.
*
* 深度比较两个对象之间,target 是否属于 source 的子集,
* 即 target 的所有属性和对应的值,都在 source 中,
*/
function isObjectSubset(source, target) {
if (!target) return true;
for (const key in target) if (!isIncluded(source[key], target[key])) return false;
return true;
}
function isIncluded(source, target) {
if (require_dist.isArray(source) && require_dist.isArray(target)) {
const seen = /* @__PURE__ */ new Set();
return target.every((ti) => source.some((si, i) => {
if (seen.has(i)) return false;
const included = isIncluded(si, ti);
if (included) seen.add(i);
return included;
}));
}
if (require_dist.isPlainObject(source) && require_dist.isPlainObject(target)) return isObjectSubset(source, target);
return Object.is(source, target);
}
//#endregion
//#region src/core/baseMiddleware.ts
function baseMiddleware(compiler, { formidableOptions = {}, bodyParserOptions = {}, proxies, cookiesOptions, logger, priority = {} }) {
return async function(req, res, next) {
const startTime = require_dist.timestamp();
const { query, pathname } = urlParse(req.url);
if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) return next();
const mockData = compiler.mockData;
const mockUrls = matchingWeight(Object.keys(mockData), pathname, priority);
if (mockUrls.length === 0) return next();
collectRequest(req);
const { query: refererQuery } = urlParse(req.headers.referer || "");
const reqBody = await parseReqBody(req, formidableOptions, bodyParserOptions);
const cookies$1 = new cookies.default(req, res, cookiesOptions);
const getCookie = cookies$1.get.bind(cookies$1);
const method = req.method.toUpperCase();
let mock;
let _mockUrl;
for (const mockUrl of mockUrls) {
mock = fineMock(mockData[mockUrl], logger, {
pathname,
method,
request: {
query,
refererQuery,
body: reqBody,
headers: req.headers,
getCookie
}
});
if (mock) {
_mockUrl = mockUrl;
break;
}
}
if (!mock) {
const matched = mockUrls.map((m) => m === _mockUrl ? picocolors.default.underline(picocolors.default.bold(m)) : picocolors.default.dim(m)).join(", ");
logger.warn(`${picocolors.default.green(pathname)} matches ${matched} , but mock data is not found.`);
return next();
}
const request = req;
const response = res;
request.body = reqBody;
request.query = query;
request.refererQuery = refererQuery;
request.params = parseParams(mock.url, pathname);
request.getCookie = getCookie;
response.setCookie = cookies$1.set.bind(cookies$1);
const { body, delay, type = "json", response: responseFn, status = 200, statusText, log: logLevel, __filepath__: filepath } = mock;
responseStatus(response, status, statusText);
await provideHeaders(request, response, mock, logger);
await provideCookies(request, response, mock, logger);
logger.info(requestLog(request, filepath), logLevel);
logger.debug(`${picocolors.default.magenta("DEBUG")} ${picocolors.default.underline(pathname)} matches: [ ${mockUrls.map((m) => m === _mockUrl ? picocolors.default.underline(picocolors.default.bold(m)) : picocolors.default.dim(m)).join(", ")} ]\n`);
if (body) {
try {
const content = require_dist.isFunction(body) ? await body(request) : body;
await realDelay(startTime, delay);
sendData(response, content, type);
} catch (e) {
logger.error(`${picocolors.default.red(`mock error at ${pathname}`)}\n${e}\n at body (${picocolors.default.underline(filepath)})`, logLevel);
responseStatus(response, 500);
res.end("");
}
return;
}
if (responseFn) {
try {
await realDelay(startTime, delay);
await responseFn(request, response, next);
} catch (e) {
logger.error(`${picocolors.default.red(`mock error at ${pathname}`)}\n${e}\n at response (${picocolors.default.underline(filepath)})`, logLevel);
responseStatus(response, 500);
res.end("");
}
return;
}
res.end("");
};
}
function fineMock(mockList, logger, { pathname, method, request }) {
return mockList.find((mock) => {
if (!pathname || !mock || !mock.url || mock.ws === true) return false;
const methods = mock.method ? require_dist.isArray(mock.method) ? mock.method : [mock.method] : ["GET", "POST"];
if (!methods.includes(method)) return false;
const hasMock = (0, path_to_regexp.pathToRegexp)(mock.url).test(pathname);
if (hasMock && mock.validator) {
const params = parseParams(mock.url, pathname);
if (require_dist.isFunction(mock.validator)) return mock.validator({
params,
...request
});
else try {
return validate({
params,
...request
}, mock.validator);
} catch (e) {
const file = mock.__filepath__;
logger.error(`${picocolors.default.red(`mock error at ${pathname}`)}\n${e}\n at validator (${picocolors.default.underline(file)})`, mock.log);
return false;
}
}
return hasMock;
});
}
function responseStatus(response, status = 200, statusText) {
response.statusCode = status;
response.statusMessage = statusText || getHTTPStatusText(status);
}
async function provideHeaders(req, res, mock, logger) {
const { headers, type = "json" } = mock;
const filepath = mock.__filepath__;
const contentType = mime_types.contentType(type) || mime_types.contentType(mime_types.lookup(type) || "");
if (contentType) res.setHeader("Content-Type", contentType);
res.setHeader("Cache-Control", "no-cache,max-age=0");
res.setHeader("X-Mock-Power-By", "vite-plugin-mock-dev-server");
if (filepath) res.setHeader("X-File-Path", filepath);
if (!headers) return;
try {
const raw = require_dist.isFunction(headers) ? await headers(req) : headers;
Object.keys(raw).forEach((key) => {
res.setHeader(key, raw[key]);
});
} catch (e) {
logger.error(`${picocolors.default.red(`mock error at ${req.url.split("?")[0]}`)}\n${e}\n at headers (${picocolors.default.underline(filepath)})`, mock.log);
}
}
async function provideCookies(req, res, mock, logger) {
const { cookies: cookies$1 } = mock;
const filepath = mock.__filepath__;
if (!cookies$1) return;
try {
const raw = require_dist.isFunction(cookies$1) ? await cookies$1(req) : cookies$1;
Object.keys(raw).forEach((key) => {
const cookie = raw[key];
if (require_dist.isArray(cookie)) {
const [value, options] = cookie;
res.setCookie(key, value, options);
} else res.setCookie(key, cookie);
});
} catch (e) {
logger.error(`${picocolors.default.red(`mock error at ${req.url.split("?")[0]}`)}\n${e}\n at cookies (${picocolors.default.underline(filepath)})`, mock.log);
}
}
function sendData(res, raw, type) {
if (isReadableStream(raw)) raw.pipe(res);
else if (node_buffer.Buffer.isBuffer(raw)) res.end(type === "text" || type === "json" ? raw.toString("utf-8") : raw);
else {
const content = typeof raw === "string" ? raw : JSON.stringify(raw);
res.end(type === "buffer" ? node_buffer.Buffer.from(content) : content);
}
}
async function realDelay(startTime, delay) {
if (!delay || typeof delay === "number" && delay <= 0 || require_dist.isArray(delay) && delay.length !== 2) return;
let realDelay$1 = 0;
if (require_dist.isArray(delay)) {
const [min, max] = delay;
realDelay$1 = require_dist.random(min, max);
} else realDelay$1 = delay - (require_dist.timestamp() - startTime);
if (realDelay$1 > 0) await require_dist.sleep(realDelay$1);
}
function getHTTPStatusText(status) {
return http_status.default[status] || "Unknown";
}
function requestLog(request, filepath) {
const { url, method, query, params, body } = request;
let { pathname } = new URL(url, "http://example.com");
pathname = picocolors.default.green(decodeURIComponent(pathname));
const format = (prefix, data) => {
return !data || require_dist.isEmptyObject(data) ? "" : ` ${picocolors.default.gray(`${prefix}:`)}${JSON.stringify(data)}`;
};
const ms = picocolors.default.magenta(picocolors.default.bold(method));
const qs = format("query", query);
const ps = format("params", params);
const bs = format("body", body);
const file = ` ${picocolors.default.dim(picocolors.default.underline(`(${filepath})`))}`;
return `${ms} ${pathname}${qs}${ps}${bs}${file}`;
}
//#endregion
//#region src/core/transform.ts
function transformRawData(raw, __filepath__) {
let mockConfig;
if (require_dist.isArray(raw)) mockConfig = raw.map((item) => ({
...item,
__filepath__
}));
else if ("url" in raw) mockConfig = {
...raw,
__filepath__
};
else {
mockConfig = [];
Object.keys(raw).forEach((key) => {
const data = raw[key];
if (require_dist.isArray(data)) mockConfig.push(...data.map((item) => ({
...item,
__filepath__
})));
else mockConfig.push({
...data,
__filepath__
});
});
}
return mockConfig;
}
function transformMockData(mockList) {
const list = [];
for (const [, handle] of mockList.entries()) if (handle) list.push(...require_dist.toArray(handle));
const mocks = {};
list.filter((mock) => require_dist.isPlainObject(mock) && mock.enabled !== false && mock.url).forEach((mock) => {
const { pathname, query } = urlParse(mock.url);
const list$1 = mocks[pathname] ??= [];
const current = {
...mock,
url: pathname
};
if (current.ws !== true) {
const validator = current.validator;
if (!require_dist.isEmptyObject(query)) if (require_dist.isFunction(validator)) current.validator = function(request) {
return isObjectSubset(request.query, query) && validator(request);
};
else if (validator) {
current.validator = { ...validator };
current.validator.query = current.validator.query ? {
...query,
...current.validator.query
} : query;
} else current.validator = { query };
}
list$1.push(current);
});
Object.keys(mocks).forEach((key) => {
mocks[key] = sortByValidator(mocks[key]);
});
return mocks;
}
function sortByValidator(mocks) {
return require_dist.sortBy(mocks, (item) => {
if (item.ws === true) return 0;
const { validator } = item;
if (!validator || require_dist.isEmptyObject(validator)) return 2;
if (require_dist.isFunction(validator)) return 0;
const count = Object.keys(validator).reduce((prev, key) => prev + keysCount(validator[key]), 0);
return 1 / count;
});
}
function keysCount(obj) {
if (!obj) return 0;
return Object.keys(obj).length;
}
//#endregion
//#region src/core/ws.ts
/**
* mock websocket
*/
function mockWebSocket(compiler, server, { wsProxies: proxies, cookiesOptions, logger }) {
const hmrMap = /* @__PURE__ */ new Map();
const poolMap = /* @__PURE__ */ new Map();
const wssContextMap = /* @__PURE__ */ new WeakMap();
const getWssMap = (mockUrl) => {
let wssMap = poolMap.get(mockUrl);
if (!wssMap) poolMap.set(mockUrl, wssMap = /* @__PURE__ */ new Map());
return wssMap;
};
const getWss = (wssMap, pathname) => {
let wss = wssMap.get(pathname);
if (!wss) wssMap.set(pathname, wss = new ws.WebSocketServer({ noServer: true }));
return wss;
};
const addHmr = (filepath, mockUrl) => {
let urlList = hmrMap.get(filepath);
if (!urlList) hmrMap.set(filepath, urlList = /* @__PURE__ */ new Set());
urlList.add(mockUrl);
};
const setupWss = (wssMap, wss, mock, context, pathname, filepath) => {
try {
mock.setup?.(wss, context);
wss.on("close", () => wssMap.delete(pathname));
wss.on("error", (e) => {
logger.error(`${picocolors.default.red(`WebSocket mock error at ${wss.path}`)}\n${e}\n at setup (${filepath})`, mock.log);
});
} catch (e) {
logger.error(`${picocolors.default.red(`WebSocket mock error at ${wss.path}`)}\n${e}\n at setup (${filepath})`, mock.log);
}
};
const emitConnection = (wss, ws$1, req, connectionList) => {
wss.emit("connection", ws$1, req);
ws$1.on("close", () => {
const i = connectionList.findIndex((item) => item.ws === ws$1);
if (i !== -1) connectionList.splice(i, 1);
});
};
const restartWss = (wssMap, wss, mock, pathname, filepath) => {
const { cleanupList, connectionList, context } = wssContextMap.get(wss);
cleanupRunner(cleanupList);
connectionList.forEach(({ ws: ws$1 }) => ws$1.removeAllListeners());
wss.removeAllListeners();
setupWss(wssMap, wss, mock, context, pathname, filepath);
connectionList.forEach(({ ws: ws$1, req }) => emitConnection(wss, ws$1, req, connectionList));
};
compiler.on?.("mock:update-end", (filepath) => {
if (!hmrMap.has(filepath)) return;
const mockUrlList = hmrMap.get(filepath);
if (!mockUrlList) return;
for (const mockUrl of mockUrlList.values()) for (const mock of compiler.mockData[mockUrl]) {
if (!mock.ws || mock.__filepath__ !== filepath) return;
const wssMap = getWssMap(mockUrl);
for (const [pathname, wss] of wssMap.entries()) restartWss(wssMap, wss, mock, pathname, filepath);
}
});
server?.on("upgrade", (req, socket, head) => {
const { pathname, query } = urlParse(req.url);
if (!pathname || proxies.length === 0 || !proxies.some((context) => doesProxyContextMatchUrl(context, req.url))) return;
const mockData = compiler.mockData;
const mockUrl = Object.keys(mockData).find((key) => {
return (0, path_to_regexp.pathToRegexp)(key).test(pathname);
});
if (!mockUrl) return;
const mock = mockData[mockUrl].find((mock$1) => {
return mock$1.url && mock$1.ws && (0, path_to_regexp.pathToRegexp)(mock$1.url).test(pathname);
});
if (!mock) return;
const filepath = mock.__filepath__;
addHmr(filepath, mockUrl);
const wssMap = getWssMap(mockUrl);
const wss = getWss(wssMap, pathname);
let wssContext = wssContextMap.get(wss);
if (!wssContext) {
const cleanupList = [];
const context = { onCleanup: (cleanup) => cleanupList.push(cleanup) };
wssContext = {
cleanupList,
context,
connectionList: []
};
wssContextMap.set(wss, wssContext);
setupWss(wssMap, wss, mock, context, pathname, filepath);
}
const request = req;
const cookies$1 = new cookies.default(req, req, cookiesOptions);
const { query: refererQuery } = urlParse(req.headers.referer || "");
request.query = query;
request.refererQuery = refererQuery;
request.params = parseParams(mockUrl, pathname);
request.getCookie = cookies$1.get.bind(cookies$1);
wss.handleUpgrade(request, socket, head, (ws$1) => {
logger.info(`${picocolors.default.magenta(picocolors.default.bold("WebSocket"))} ${picocolors.default.green(req.url)} connected ${picocolors.default.dim(`(${filepath})`)}`, mock.log);
wssContext.connectionList.push({
req: request,
ws: ws$1
});
emitConnection(wss, ws$1, request, wssContext.connectionList);
});
});
server?.on("close", () => {
for (const wssMap of poolMap.values()) {
for (const wss of wssMap.values()) {
const wssContext = wssContextMap.get(wss);
cleanupRunner(wssContext.cleanupList);
wss.close();
}
wssMap.clear();
}
poolMap.clear();
hmrMap.clear();
});
}
function cleanupRunner(cleanupList) {
let cleanup;
while (cleanup = cleanupList.shift()) cleanup?.();
}
//#endregion
//#region src/core/logger.ts
const logLevels = {
silent: 0,
error: 1,
warn: 2,
info: 3,
debug: 4
};
function createLogger(prefix, defaultLevel = "info") {
prefix = `[${prefix}]`;
function output(type, msg, level) {
level = require_dist.isBoolean(level) ? level ? defaultLevel : "error" : level;
const thresh = logLevels[level];
if (thresh >= logLevels[type]) {
const method = type === "info" || type === "debug" ? "log" : type;
const tag = type === "debug" ? picocolors.default.magenta(picocolors.default.bold(prefix)) : type === "info" ? picocolors.default.cyan(picocolors.default.bold(prefix)) : type === "warn" ? picocolors.default.yellow(picocolors.default.bold(prefix)) : picocolors.default.red(picocolors.default.bold(prefix));
const format = `${picocolors.default.dim((/* @__PURE__ */ new Date()).toLocaleTimeString())} ${tag} ${msg}`;
console[method](format);
}
}
const logger = {
debug(msg, level = defaultLevel) {
output("debug", msg, level);
},
info(msg, level = defaultLevel) {
output("info", msg, level);
},
warn(msg, level = defaultLevel) {
output("warn", msg, level);
},
error(msg, level = defaultLevel) {
output("error", msg, level);
}
};
return logger;
}
//#endregion
Object.defineProperty(exports, 'baseMiddleware', {
enumerable: true,
get: function () {
return baseMiddleware;
}
});
Object.defineProperty(exports, 'createLogger', {
enumerable: true,
get: function () {
return createLogger;
}
});
Object.defineProperty(exports, 'debug', {
enumerable: true,
get: function () {
return debug$1;
}
});
Object.defineProperty(exports, 'doesProxyContextMatchUrl', {
enumerable: true,
get: function () {
return doesProxyContextMatchUrl;
}
});
Object.defineProperty(exports, 'ensureProxies', {
enumerable: true,
get: function () {
return ensureProxies;
}
});
Object.defineProperty(exports, 'logLevels', {
enumerable: true,
get: function () {
return logLevels;
}
});
Object.defineProperty(exports, 'lookupFile', {
enumerable: true,
get: function () {
return lookupFile;
}
});
Object.defineProperty(exports, 'mockWebSocket', {
enumerable: true,
get: function () {
return mockWebSocket;
}
});
Object.defineProperty(exports, 'normalizePath', {
enumerable: true,
get: function () {
return normalizePath;
}
});
Object.defineProperty(exports, 'recoverRequest', {
enumerable: true,
get: function () {
return recoverRequest;
}
});
Object.defineProperty(exports, 'sortByValidator', {
enumerable: true,
get: function () {
return sortByValidator;
}
});
Object.defineProperty(exports, 'transformMockData', {
enumerable: true,
get: function () {
return transformMockData;
}
});
Object.defineProperty(exports, 'transformRawData', {
enumerable: true,
get: function () {
return transformRawData;
}
});
Object.defineProperty(exports, 'urlParse', {
enumerable: true,
get: function () {
return urlParse;
}
});