vite-plugin-automock 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +350 -0
- package/dist/chunk-KZB7ARYV.mjs +338 -0
- package/dist/chunk-KZB7ARYV.mjs.map +1 -0
- package/dist/chunk-NWIN2A3G.mjs +180 -0
- package/dist/chunk-NWIN2A3G.mjs.map +1 -0
- package/dist/client/index.d.mts +90 -0
- package/dist/client/index.d.ts +90 -0
- package/dist/client/index.js +213 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +19 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/index.d.mts +63 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +3239 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2682 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mockFileUtils-HQCNPI3U.mjs +15 -0
- package/dist/mockFileUtils-HQCNPI3U.mjs.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mockFileUtils.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs-extra\";\nimport prettier from \"prettier\";\n\nexport interface MockFileConfig {\n enable: boolean;\n data: unknown;\n delay: number;\n status: number;\n [key: string]: unknown;\n}\n\nexport interface MockFileInfo {\n config: MockFileConfig;\n serializable: boolean;\n hasDynamicData: boolean;\n headerComment?: string;\n description?: string;\n dataText: string;\n isBinary?: boolean;\n}\n\nexport const DEFAULT_CONFIG: MockFileConfig = {\n enable: true,\n data: null,\n delay: 0,\n status: 200,\n};\n\n// 简单实现 cross-platform 路径分隔符转换\nfunction toPosixPath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\n// 检测是否为二进制文件\nconst isBinaryResponse = (\n contentType: string | undefined,\n data: Buffer,\n): boolean => {\n if (!contentType) return false;\n\n const binaryTypes = [\n \"application/octet-stream\",\n \"application/pdf\",\n \"application/zip\",\n \"application/x-zip-compressed\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"image/\",\n \"video/\",\n \"audio/\",\n ];\n\n return (\n binaryTypes.some((type) => contentType.toLowerCase().includes(type)) ||\n !isBufferTextLike(data)\n );\n};\n\n// 检测Buffer是否像文本数据\nconst isBufferTextLike = (buffer: Buffer): boolean => {\n try {\n // 检查前100字节是否包含非文本字符\n const sample = buffer.slice(0, 100);\n const text = sample.toString(\"utf8\");\n\n // 如果包含null字节或过多的控制字符,可能是二进制\n const nullBytes = [...sample].filter((b) => b === 0).length;\n const controlChars = [...sample].filter(\n (b) => b < 32 && b !== 9 && b !== 10 && b !== 13,\n ).length;\n\n return nullBytes === 0 && controlChars < 5;\n } catch {\n return false;\n }\n};\n\n// 从Content-Type获取文件扩展名\nconst getFileExtension = (\n contentType: string | undefined,\n url: string,\n): string => {\n // 从Content-Type映射\n const mimeMap: Record<string, string> = {\n \"application/json\": \"json\",\n \"application/pdf\": \"pdf\",\n \"application/zip\": \"zip\",\n \"application/vnd.ms-excel\": \"xls\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\": \"xlsx\",\n \"application/msword\": \"doc\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\":\n \"docx\",\n \"application/vnd.ms-powerpoint\": \"ppt\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\":\n \"pptx\",\n \"image/jpeg\": \"jpg\",\n \"image/png\": \"png\",\n \"image/gif\": \"gif\",\n \"text/plain\": \"txt\",\n \"text/html\": \"html\",\n \"text/css\": \"css\",\n \"application/javascript\": \"js\",\n \"text/xml\": \"xml\",\n };\n\n if (contentType && mimeMap[contentType.toLowerCase()]) {\n return mimeMap[contentType.toLowerCase()];\n }\n\n // 尝试从URL的查询参数中解析文件名\n try {\n const urlObj = new URL(url, \"http://localhost\");\n const fileName = urlObj.searchParams.get(\"file_name\");\n if (fileName) {\n const extensionMatch = fileName.match(/\\.([a-zA-Z0-9]+)$/);\n if (extensionMatch) {\n return extensionMatch[1];\n }\n }\n } catch (error) {\n // 忽略URL解析错误\n }\n\n return \"bin\";\n};\n\nconst saveMockData = async (\n url: string,\n method: string,\n data: Buffer | string,\n rootDir: string,\n statusCode?: number,\n contentType?: string,\n): Promise<string | null> => {\n try {\n const absoluteRootDir = path.isAbsolute(rootDir)\n ? rootDir\n : path.resolve(process.cwd(), rootDir);\n\n // 正确解析URL,处理查询参数和路径\n let pathname: string;\n let search: string;\n\n try {\n // 如果url包含协议信息,直接解析\n if (url.startsWith(\"http\")) {\n const urlObj = new URL(url);\n pathname = urlObj.pathname;\n search = urlObj.search;\n } else {\n // 如果是相对路径,先补全协议\n const urlObj = new URL(url, \"http://localhost\");\n pathname = urlObj.pathname;\n search = urlObj.search;\n }\n } catch (error) {\n // 如果URL解析失败,尝试手动处理\n const [pathPart, ...searchPart] = url.split(\"?\");\n pathname = pathPart;\n search = searchPart.length > 0 ? \"?\" + searchPart.join(\"?\") : \"\";\n }\n\n const filePath = path.join(\n absoluteRootDir,\n pathname.replace(/^\\//, \"\"),\n method.toLowerCase() + \".js\",\n );\n\n const dir = path.dirname(filePath);\n fs.ensureDirSync(dir);\n\n // 检测是否为二进制文件\n const isBuffer = Buffer.isBuffer(data);\n const binaryData = isBuffer ? data : Buffer.from(data || \"\");\n const isBinary = isBinaryResponse(contentType, binaryData);\n\n if (isBinary) {\n // 二进制文件处理\n const extension = getFileExtension(contentType, url);\n const binaryFilePath = filePath.replace(/\\.js$/, \".\" + extension);\n\n // 检查文件是否已存在,如果存在则不覆盖\n if (fs.existsSync(binaryFilePath)) {\n return null;\n }\n\n // 保存二进制文件\n fs.writeFileSync(binaryFilePath, binaryData);\n\n // 创建对应的JS配置文件\n const configContent = `/**\n * Mock data for ${pathname} (${method.toUpperCase()})${search || \"\"}\n * @description ${pathname}${search || \"\"} - Binary file (${extension})\n * Generated at ${new Date().toISOString()}\n */\nexport default {\n enable: false,\n data: {\n __binaryFile: '${extension}',\n __originalPath: '${pathname}',\n __originalQuery: '${search}',\n __originalUrl: '${pathname}${search || \"\"}',\n __contentType: '${contentType}',\n __fileSize: ${binaryData.length}\n },\n delay: 0,\n status: ${statusCode || 200}\n}`;\n\n try {\n const formattedCode = await prettier.format(configContent, {\n parser: \"babel\",\n });\n fs.writeFileSync(filePath, formattedCode, \"utf-8\");\n } catch (error) {\n fs.writeFileSync(filePath, configContent, \"utf-8\");\n }\n\n return filePath;\n } else {\n // JSON文件处理\n // 检查文件是否已存在,如果存在则不覆盖\n if (fs.existsSync(filePath)) {\n return null;\n }\n\n const dataStr = isBuffer ? data.toString(\"utf8\") : data || \"\";\n let jsonData;\n // 如果响应为空或只包含空白字符\n if (!dataStr || dataStr.trim() === \"\") {\n jsonData = {\n error: true,\n message: `Empty response (${statusCode || \"unknown status\"})`,\n status: statusCode || 404,\n data: null,\n };\n } else {\n try {\n jsonData = JSON.parse(dataStr);\n // 如果解析成功且是错误状态码,添加错误标记\n if (statusCode && statusCode >= 400) {\n if (typeof jsonData === \"object\" && jsonData !== null) {\n jsonData = {\n ...jsonData,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n } else {\n jsonData = {\n originalData: jsonData,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n }\n }\n } catch {\n // 如果不是JSON格式,可能是HTML错误页面或其他格式\n jsonData = {\n error: true,\n message: `Non-JSON response (${statusCode || \"unknown status\"})`,\n status: statusCode || 404,\n data: dataStr,\n __mockStatusCode: statusCode,\n __isErrorResponse: true,\n };\n }\n }\n\n const content = `/**\n * Mock data for ${pathname} (${method.toUpperCase()})${search || \"\"}\n * @description ${pathname}${search || \"\"}\n * Generated at ${new Date().toISOString()}\n */\nexport default {\n enable: false,\n data: ${JSON.stringify(jsonData)},\n delay: 0,\n status: ${statusCode || 200}\n}`;\n\n try {\n const formattedCode = await prettier.format(content, {\n parser: \"babel\",\n });\n fs.writeFileSync(filePath, formattedCode, \"utf-8\");\n return filePath;\n } catch (error) {\n fs.writeFileSync(filePath, content, \"utf-8\");\n return filePath;\n }\n }\n } catch (error) {\n console.error(`Failed to save mock data for ${url}:`, error);\n console.error(\n `URL details: url=${url}, method=${method}, statusCode=${statusCode}, contentType=${contentType}`,\n );\n throw error;\n }\n};\n\nconst recursiveReadAllFiles = (dir: string): string[] => {\n if (!fs.existsSync(dir)) return [];\n\n const files: string[] = [];\n try {\n const list = fs.readdirSync(dir);\n list.forEach((file) => {\n const filePath = path.join(dir, file);\n const stat = fs.statSync(filePath);\n if (stat.isDirectory()) {\n files.push(...recursiveReadAllFiles(filePath));\n } else {\n files.push(filePath);\n }\n });\n } catch (error) {\n console.error(`Error reading directory ${dir}:`, error);\n }\n return files;\n};\n\nconst buildMockIndex = (mockDir: string): Map<string, string> => {\n const mockFileMap = new Map<string, string>();\n\n if (!fs.existsSync(mockDir)) {\n fs.ensureDirSync(mockDir);\n return mockFileMap;\n }\n\n const files = recursiveReadAllFiles(mockDir);\n\n files.forEach((filePath) => {\n if (!filePath.endsWith(\".js\")) {\n return;\n }\n\n try {\n const relativePath = path.relative(mockDir, filePath);\n const method = path.basename(filePath, \".js\");\n const dirPath = path.dirname(relativePath);\n const urlPath = \"/\" + toPosixPath(dirPath);\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n const key = `${urlPath}/${method}.js`.toLowerCase();\n\n mockFileMap.set(key, absolutePath);\n } catch (error) {\n console.error(`❌ [automock] 处理文件失败 ${filePath}:`, error);\n }\n });\n\n return mockFileMap;\n};\n\nconst parseMockModule = async (filePath: string): Promise<MockFileInfo> => {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n\n if (!fs.existsSync(absolutePath)) {\n throw new Error(`Mock file does not exist: ${absolutePath}`);\n }\n\n const content = fs.readFileSync(absolutePath, \"utf-8\");\n const headerCommentMatch = content.match(/^(\\/\\*\\*[\\s\\S]*?\\*\\/)/);\n const headerComment = headerCommentMatch ? headerCommentMatch[1] : undefined;\n\n // 提取 @description\n let description: string | undefined;\n if (headerComment) {\n const descMatch = headerComment.match(/@description\\s+(.+?)(?:\\n|\\*\\/)/s);\n if (descMatch) {\n description = descMatch[1].trim();\n }\n }\n\n const { default: mockModule } = await import(\n `${absolutePath}?t=${Date.now()}`\n );\n\n const exportedConfig =\n typeof mockModule === \"function\" ? mockModule() : mockModule;\n const hasDynamicData = typeof exportedConfig?.data === \"function\";\n const config: MockFileConfig = {\n ...DEFAULT_CONFIG,\n ...(exportedConfig ?? {}),\n };\n\n const serializable = !hasDynamicData;\n const dataObj =\n typeof config.data === \"object\" && config.data !== null\n ? (config.data as Record<string, unknown>)\n : null;\n const isBinary = dataObj !== null && \"__binaryFile\" in dataObj;\n\n return {\n config,\n serializable,\n hasDynamicData,\n headerComment,\n description,\n dataText: isBinary\n ? `/* Binary file: ${dataObj?.__binaryFile} */`\n : serializable\n ? JSON.stringify(config.data ?? null, null, 2)\n : \"/* data is generated dynamically and cannot be edited here */\",\n isBinary,\n };\n};\n\nconst writeMockFile = async (\n filePath: string,\n mockInfo: Pick<MockFileInfo, \"config\" | \"headerComment\" | \"description\">,\n): Promise<void> => {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath);\n\n let header = mockInfo.headerComment || \"\";\n\n // 如果提供了 description,更新或添加到注释中\n if (mockInfo.description !== undefined) {\n if (header) {\n // 替换现有的 @description\n if (/@description/.test(header)) {\n header = header.replace(\n /@description\\s+.+?(?=\\n|\\*\\/)/s,\n `@description ${mockInfo.description}`,\n );\n } else {\n // 在最后一行之前添加 @description\n header = header.replace(\n /\\*\\//,\n ` * @description ${mockInfo.description}\\n */`,\n );\n }\n }\n }\n\n const content = header ? `${header}\\n` : \"\";\n const finalContent = `${content}export default ${JSON.stringify(mockInfo.config, null, 2)}\\n`;\n\n try {\n const formattedCode = await prettier.format(finalContent, {\n parser: \"babel\",\n });\n fs.writeFileSync(absolutePath, formattedCode, \"utf-8\");\n } catch (error) {\n console.error(\"Error formatting code with prettier:\", error);\n fs.writeFileSync(absolutePath, finalContent, \"utf-8\");\n }\n};\n\nexport { saveMockData, buildMockIndex, parseMockModule, writeMockFile };\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// src/client/interceptor.ts
|
|
2
|
+
function getEnvVar(name) {
|
|
3
|
+
if (typeof import.meta !== "undefined" && import.meta.env) {
|
|
4
|
+
return import.meta.env[name];
|
|
5
|
+
}
|
|
6
|
+
if (typeof process !== "undefined" && process.env) {
|
|
7
|
+
return process.env[name];
|
|
8
|
+
}
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
var MockInterceptor = class {
|
|
12
|
+
options;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.options = options;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check if mock is enabled
|
|
18
|
+
*/
|
|
19
|
+
isEnabled() {
|
|
20
|
+
if (window.__MOCK_ENABLED__ !== void 0) {
|
|
21
|
+
return window.__MOCK_ENABLED__;
|
|
22
|
+
}
|
|
23
|
+
if (typeof this.options.enabled === "function") {
|
|
24
|
+
return this.options.enabled();
|
|
25
|
+
}
|
|
26
|
+
return this.options.enabled ?? false;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Find matching mock data for the request
|
|
30
|
+
* Supports both formats:
|
|
31
|
+
* - "GET /api/v1/asset/xxx" (HTTP method + URL)
|
|
32
|
+
* - "/api/v1/asset/xxx/get.js" (File path format from automock plugin)
|
|
33
|
+
*/
|
|
34
|
+
findMock(url, method) {
|
|
35
|
+
const methodUpper = method.toUpperCase();
|
|
36
|
+
const methodLower = method.toLowerCase();
|
|
37
|
+
const httpMethodKey = `${methodUpper} ${url}`;
|
|
38
|
+
if (this.options.mockData[httpMethodKey]) {
|
|
39
|
+
return this.options.mockData[httpMethodKey];
|
|
40
|
+
}
|
|
41
|
+
const filePathKey = `${url}/${methodLower}.js`;
|
|
42
|
+
if (this.options.mockData[filePathKey]) {
|
|
43
|
+
return this.options.mockData[filePathKey];
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Determine if request should be mocked
|
|
49
|
+
*/
|
|
50
|
+
shouldMock(url, method) {
|
|
51
|
+
if (!this.isEnabled()) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const mock = this.findMock(url, method);
|
|
55
|
+
return mock !== null && mock.enable;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get mock data for request
|
|
59
|
+
*/
|
|
60
|
+
getMock(url, method) {
|
|
61
|
+
if (!this.shouldMock(url, method)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return this.findMock(url, method);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Setup axios interceptor
|
|
68
|
+
*/
|
|
69
|
+
setupAxios(axiosInstance) {
|
|
70
|
+
axiosInstance.interceptors.request.use(
|
|
71
|
+
async (config) => {
|
|
72
|
+
const url = config.url || "";
|
|
73
|
+
const method = config.method?.toUpperCase() || "GET";
|
|
74
|
+
const mock = this.getMock(url, method);
|
|
75
|
+
if (mock) {
|
|
76
|
+
this.options.onMockHit?.(url, method, mock);
|
|
77
|
+
config.adapter = async () => {
|
|
78
|
+
const { data, delay = 0, status = 200 } = mock;
|
|
79
|
+
if (delay > 0) {
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
data,
|
|
84
|
+
status,
|
|
85
|
+
statusText: "OK",
|
|
86
|
+
headers: {},
|
|
87
|
+
config
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
} else {
|
|
91
|
+
const reason = !this.isEnabled() ? "Mock disabled globally" : "No matching mock found or mock disabled";
|
|
92
|
+
this.options.onBypass?.(url, method, reason);
|
|
93
|
+
}
|
|
94
|
+
return config;
|
|
95
|
+
},
|
|
96
|
+
(error) => Promise.reject(error)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function createMockInterceptor(options) {
|
|
101
|
+
return new MockInterceptor(options);
|
|
102
|
+
}
|
|
103
|
+
async function loadMockData() {
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch("/mock-data.json");
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
console.warn(
|
|
108
|
+
"[MockInterceptor] Failed to load mock-data.json:",
|
|
109
|
+
response.statusText
|
|
110
|
+
);
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
const data = await response.json();
|
|
114
|
+
return data;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.warn("[MockInterceptor] Failed to load mock-data.json:", error);
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async function initMockInterceptor(axiosInstance) {
|
|
121
|
+
const mockData = await loadMockData();
|
|
122
|
+
if (!axiosInstance) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"[MockInterceptor] axiosInstance is required. Please provide an axios instance."
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const isEnvEnabled = getEnvVar("VITE_USE_MOCK") === "true";
|
|
128
|
+
const interceptor = createMockInterceptor({
|
|
129
|
+
mockData,
|
|
130
|
+
enabled: isEnvEnabled,
|
|
131
|
+
onMockHit: (url, method) => {
|
|
132
|
+
console.log(`[MOCK HIT] ${method} ${url}`);
|
|
133
|
+
},
|
|
134
|
+
onBypass: (url, method, reason) => {
|
|
135
|
+
console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
interceptor.setupAxios(axiosInstance);
|
|
139
|
+
}
|
|
140
|
+
function setMockEnabled(enabled) {
|
|
141
|
+
window.__MOCK_ENABLED__ = enabled;
|
|
142
|
+
}
|
|
143
|
+
function isMockEnabled() {
|
|
144
|
+
return !!window.__MOCK_ENABLED__;
|
|
145
|
+
}
|
|
146
|
+
var httpInstanceRef;
|
|
147
|
+
function registerHttpInstance(http) {
|
|
148
|
+
httpInstanceRef = http;
|
|
149
|
+
}
|
|
150
|
+
async function initMockInterceptorForPureHttp() {
|
|
151
|
+
if (!httpInstanceRef) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"[MockInterceptor] http instance not registered. Call registerHttpInstance(http) first."
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
const mockData = await loadMockData();
|
|
157
|
+
const isEnvEnabled = getEnvVar("VITE_USE_MOCK") === "true";
|
|
158
|
+
const interceptor = createMockInterceptor({
|
|
159
|
+
mockData,
|
|
160
|
+
enabled: isEnvEnabled,
|
|
161
|
+
onMockHit: (url, method) => {
|
|
162
|
+
console.log(`[MOCK HIT] ${method} ${url}`);
|
|
163
|
+
},
|
|
164
|
+
onBypass: (url, method, reason) => {
|
|
165
|
+
console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export {
|
|
172
|
+
createMockInterceptor,
|
|
173
|
+
loadMockData,
|
|
174
|
+
initMockInterceptor,
|
|
175
|
+
setMockEnabled,
|
|
176
|
+
isMockEnabled,
|
|
177
|
+
registerHttpInstance,
|
|
178
|
+
initMockInterceptorForPureHttp
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=chunk-NWIN2A3G.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/interceptor.ts"],"sourcesContent":["import type {\n AxiosInstance,\n AxiosRequestConfig,\n InternalAxiosRequestConfig,\n} from \"axios\";\n\n/**\n * Helper function to safely get environment variables\n * Works in both ESM and CJS environments\n */\nfunction getEnvVar(name: string): string | undefined {\n // Try ESM environment (Vite)\n if (typeof import.meta !== \"undefined\" && import.meta.env) {\n return import.meta.env[name as keyof ImportMetaEnv];\n }\n // Fallback to CommonJS (Node.js)\n if (typeof process !== \"undefined\" && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n\nexport type MockBundleData = {\n enable: boolean;\n data: unknown;\n delay?: number;\n status?: number;\n isBinary?: boolean;\n};\n\nexport type MockInterceptorOptions = {\n mockData: Record<string, MockBundleData>;\n enabled?: boolean | (() => boolean);\n onMockHit?: (url: string, method: string, mock: MockBundleData) => void;\n onBypass?: (url: string, method: string, reason: string) => void;\n};\n\ntype MockEnabledState = boolean | undefined;\n\ndeclare global {\n interface Window {\n __MOCK_ENABLED__?: MockEnabledState;\n }\n}\n\nclass MockInterceptor {\n private options: MockInterceptorOptions;\n\n constructor(options: MockInterceptorOptions) {\n this.options = options;\n }\n\n /**\n * Check if mock is enabled\n */\n isEnabled(): boolean {\n // Check runtime flag first\n if (window.__MOCK_ENABLED__ !== undefined) {\n return window.__MOCK_ENABLED__;\n }\n // Check environment variable\n if (typeof this.options.enabled === \"function\") {\n return this.options.enabled();\n }\n return this.options.enabled ?? false;\n }\n\n /**\n * Find matching mock data for the request\n * Supports both formats:\n * - \"GET /api/v1/asset/xxx\" (HTTP method + URL)\n * - \"/api/v1/asset/xxx/get.js\" (File path format from automock plugin)\n */\n private findMock(url: string, method: string): MockBundleData | null {\n const methodUpper = method.toUpperCase();\n const methodLower = method.toLowerCase();\n\n // Try HTTP method + URL format first (for compatibility)\n const httpMethodKey = `${methodUpper} ${url}`;\n if (this.options.mockData[httpMethodKey]) {\n return this.options.mockData[httpMethodKey];\n }\n\n // Try file path format from automock plugin: /api/v1/asset/xxx/get.js\n const filePathKey = `${url}/${methodLower}.js`;\n if (this.options.mockData[filePathKey]) {\n return this.options.mockData[filePathKey];\n }\n\n return null;\n }\n\n /**\n * Determine if request should be mocked\n */\n shouldMock(url: string, method: string): boolean {\n if (!this.isEnabled()) {\n return false;\n }\n\n const mock = this.findMock(url, method);\n return mock !== null && mock.enable;\n }\n\n /**\n * Get mock data for request\n */\n getMock(url: string, method: string): MockBundleData | null {\n if (!this.shouldMock(url, method)) {\n return null;\n }\n return this.findMock(url, method);\n }\n\n /**\n * Setup axios interceptor\n */\n setupAxios(axiosInstance: AxiosInstance): void {\n axiosInstance.interceptors.request.use(\n async (config: InternalAxiosRequestConfig) => {\n const url = config.url || \"\";\n const method = config.method?.toUpperCase() || \"GET\";\n\n const mock = this.getMock(url, method);\n\n if (mock) {\n // Trigger mock hit callback\n this.options.onMockHit?.(url, method, mock);\n\n // Set adapter to return mock data\n (config as any).adapter = async () => {\n const { data, delay = 0, status = 200 } = mock;\n\n // Simulate delay\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n return {\n data,\n status,\n statusText: \"OK\",\n headers: {},\n config,\n };\n };\n } else {\n // Trigger bypass callback\n const reason = !this.isEnabled()\n ? \"Mock disabled globally\"\n : \"No matching mock found or mock disabled\";\n this.options.onBypass?.(url, method, reason);\n }\n\n return config;\n },\n (error: unknown) => Promise.reject(error),\n );\n }\n}\n\n/**\n * Create mock interceptor instance\n */\nexport function createMockInterceptor(\n options: MockInterceptorOptions,\n): MockInterceptor {\n return new MockInterceptor(options);\n}\n\n/**\n * Load mock data from bundled JSON file\n */\nexport async function loadMockData(): Promise<Record<string, MockBundleData>> {\n try {\n // In production, mock-data.json is generated by automock plugin\n // Use absolute path to load it from dist directory\n const response = await fetch(\"/mock-data.json\");\n if (!response.ok) {\n console.warn(\n \"[MockInterceptor] Failed to load mock-data.json:\",\n response.statusText,\n );\n return {};\n }\n const data = (await response.json()) as Record<string, MockBundleData>;\n return data;\n } catch (error) {\n console.warn(\"[MockInterceptor] Failed to load mock-data.json:\", error);\n return {};\n }\n}\n\n/**\n * Initialize mock interceptor with default settings\n */\nexport async function initMockInterceptor(\n axiosInstance?: AxiosInstance,\n): Promise<void> {\n const mockData = await loadMockData();\n\n if (!axiosInstance) {\n throw new Error(\n \"[MockInterceptor] axiosInstance is required. Please provide an axios instance.\",\n );\n }\n\n // Check if mock is enabled via environment variable\n const isEnvEnabled = getEnvVar(\"VITE_USE_MOCK\") === \"true\";\n\n const interceptor = createMockInterceptor({\n mockData,\n enabled: isEnvEnabled,\n onMockHit: (url: string, method: string) => {\n console.log(`[MOCK HIT] ${method} ${url}`);\n },\n onBypass: (url: string, method: string, reason: string) => {\n console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);\n },\n });\n\n // Setup interceptor on the axios instance\n interceptor.setupAxios(axiosInstance);\n}\n\n/**\n * Set mock enabled state at runtime\n */\nexport function setMockEnabled(enabled: boolean): void {\n window.__MOCK_ENABLED__ = enabled;\n}\n\n/**\n * Get current mock enabled state\n */\nexport function isMockEnabled(): boolean {\n return !!window.__MOCK_ENABLED__;\n}\n\n/**\n * Get the http instance (PureHttp wrapper)\n * This is a placeholder - users should provide their own http instance\n * or use initMockInterceptorForPureHttp with their specific setup\n */\ntype HttpInstance = {\n constructor: { axiosInstance: AxiosInstance };\n};\n\n// This is a reference to the user's http instance\n// It should be set by the user's application code\nlet httpInstanceRef: HttpInstance | undefined;\n\n/**\n * Register the http instance for PureHttp compatibility\n * Call this before initMockInterceptorForPureHttp\n */\nexport function registerHttpInstance(http: HttpInstance): void {\n httpInstanceRef = http;\n}\n\n/**\n * Initialize mock interceptor for PureHttp\n * Should be called after PureHttp is instantiated and registered\n */\nexport async function initMockInterceptorForPureHttp(): Promise<void> {\n if (!httpInstanceRef) {\n throw new Error(\n \"[MockInterceptor] http instance not registered. Call registerHttpInstance(http) first.\",\n );\n }\n\n const mockData = await loadMockData();\n\n // Check if mock is enabled via environment variable\n const isEnvEnabled = getEnvVar(\"VITE_USE_MOCK\") === \"true\";\n\n const interceptor = createMockInterceptor({\n mockData,\n enabled: isEnvEnabled,\n onMockHit: (url: string, method: string) => {\n console.log(`[MOCK HIT] ${method} ${url}`);\n },\n onBypass: (url: string, method: string, reason: string) => {\n console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);\n },\n });\n\n // Get axios instance from PureHttp and setup interceptor\n interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
type MockBundleData = {
|
|
4
|
+
enable: boolean;
|
|
5
|
+
data: unknown;
|
|
6
|
+
delay?: number;
|
|
7
|
+
status?: number;
|
|
8
|
+
isBinary?: boolean;
|
|
9
|
+
};
|
|
10
|
+
type MockInterceptorOptions = {
|
|
11
|
+
mockData: Record<string, MockBundleData>;
|
|
12
|
+
enabled?: boolean | (() => boolean);
|
|
13
|
+
onMockHit?: (url: string, method: string, mock: MockBundleData) => void;
|
|
14
|
+
onBypass?: (url: string, method: string, reason: string) => void;
|
|
15
|
+
};
|
|
16
|
+
type MockEnabledState = boolean | undefined;
|
|
17
|
+
declare global {
|
|
18
|
+
interface Window {
|
|
19
|
+
__MOCK_ENABLED__?: MockEnabledState;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
declare class MockInterceptor {
|
|
23
|
+
private options;
|
|
24
|
+
constructor(options: MockInterceptorOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Check if mock is enabled
|
|
27
|
+
*/
|
|
28
|
+
isEnabled(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Find matching mock data for the request
|
|
31
|
+
* Supports both formats:
|
|
32
|
+
* - "GET /api/v1/asset/xxx" (HTTP method + URL)
|
|
33
|
+
* - "/api/v1/asset/xxx/get.js" (File path format from automock plugin)
|
|
34
|
+
*/
|
|
35
|
+
private findMock;
|
|
36
|
+
/**
|
|
37
|
+
* Determine if request should be mocked
|
|
38
|
+
*/
|
|
39
|
+
shouldMock(url: string, method: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Get mock data for request
|
|
42
|
+
*/
|
|
43
|
+
getMock(url: string, method: string): MockBundleData | null;
|
|
44
|
+
/**
|
|
45
|
+
* Setup axios interceptor
|
|
46
|
+
*/
|
|
47
|
+
setupAxios(axiosInstance: AxiosInstance): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create mock interceptor instance
|
|
51
|
+
*/
|
|
52
|
+
declare function createMockInterceptor(options: MockInterceptorOptions): MockInterceptor;
|
|
53
|
+
/**
|
|
54
|
+
* Load mock data from bundled JSON file
|
|
55
|
+
*/
|
|
56
|
+
declare function loadMockData(): Promise<Record<string, MockBundleData>>;
|
|
57
|
+
/**
|
|
58
|
+
* Initialize mock interceptor with default settings
|
|
59
|
+
*/
|
|
60
|
+
declare function initMockInterceptor(axiosInstance?: AxiosInstance): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Set mock enabled state at runtime
|
|
63
|
+
*/
|
|
64
|
+
declare function setMockEnabled(enabled: boolean): void;
|
|
65
|
+
/**
|
|
66
|
+
* Get current mock enabled state
|
|
67
|
+
*/
|
|
68
|
+
declare function isMockEnabled(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Get the http instance (PureHttp wrapper)
|
|
71
|
+
* This is a placeholder - users should provide their own http instance
|
|
72
|
+
* or use initMockInterceptorForPureHttp with their specific setup
|
|
73
|
+
*/
|
|
74
|
+
type HttpInstance = {
|
|
75
|
+
constructor: {
|
|
76
|
+
axiosInstance: AxiosInstance;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Register the http instance for PureHttp compatibility
|
|
81
|
+
* Call this before initMockInterceptorForPureHttp
|
|
82
|
+
*/
|
|
83
|
+
declare function registerHttpInstance(http: HttpInstance): void;
|
|
84
|
+
/**
|
|
85
|
+
* Initialize mock interceptor for PureHttp
|
|
86
|
+
* Should be called after PureHttp is instantiated and registered
|
|
87
|
+
*/
|
|
88
|
+
declare function initMockInterceptorForPureHttp(): Promise<void>;
|
|
89
|
+
|
|
90
|
+
export { type MockBundleData, type MockInterceptorOptions, createMockInterceptor, initMockInterceptor, initMockInterceptorForPureHttp, isMockEnabled, loadMockData, registerHttpInstance, setMockEnabled };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
type MockBundleData = {
|
|
4
|
+
enable: boolean;
|
|
5
|
+
data: unknown;
|
|
6
|
+
delay?: number;
|
|
7
|
+
status?: number;
|
|
8
|
+
isBinary?: boolean;
|
|
9
|
+
};
|
|
10
|
+
type MockInterceptorOptions = {
|
|
11
|
+
mockData: Record<string, MockBundleData>;
|
|
12
|
+
enabled?: boolean | (() => boolean);
|
|
13
|
+
onMockHit?: (url: string, method: string, mock: MockBundleData) => void;
|
|
14
|
+
onBypass?: (url: string, method: string, reason: string) => void;
|
|
15
|
+
};
|
|
16
|
+
type MockEnabledState = boolean | undefined;
|
|
17
|
+
declare global {
|
|
18
|
+
interface Window {
|
|
19
|
+
__MOCK_ENABLED__?: MockEnabledState;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
declare class MockInterceptor {
|
|
23
|
+
private options;
|
|
24
|
+
constructor(options: MockInterceptorOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Check if mock is enabled
|
|
27
|
+
*/
|
|
28
|
+
isEnabled(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Find matching mock data for the request
|
|
31
|
+
* Supports both formats:
|
|
32
|
+
* - "GET /api/v1/asset/xxx" (HTTP method + URL)
|
|
33
|
+
* - "/api/v1/asset/xxx/get.js" (File path format from automock plugin)
|
|
34
|
+
*/
|
|
35
|
+
private findMock;
|
|
36
|
+
/**
|
|
37
|
+
* Determine if request should be mocked
|
|
38
|
+
*/
|
|
39
|
+
shouldMock(url: string, method: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Get mock data for request
|
|
42
|
+
*/
|
|
43
|
+
getMock(url: string, method: string): MockBundleData | null;
|
|
44
|
+
/**
|
|
45
|
+
* Setup axios interceptor
|
|
46
|
+
*/
|
|
47
|
+
setupAxios(axiosInstance: AxiosInstance): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create mock interceptor instance
|
|
51
|
+
*/
|
|
52
|
+
declare function createMockInterceptor(options: MockInterceptorOptions): MockInterceptor;
|
|
53
|
+
/**
|
|
54
|
+
* Load mock data from bundled JSON file
|
|
55
|
+
*/
|
|
56
|
+
declare function loadMockData(): Promise<Record<string, MockBundleData>>;
|
|
57
|
+
/**
|
|
58
|
+
* Initialize mock interceptor with default settings
|
|
59
|
+
*/
|
|
60
|
+
declare function initMockInterceptor(axiosInstance?: AxiosInstance): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Set mock enabled state at runtime
|
|
63
|
+
*/
|
|
64
|
+
declare function setMockEnabled(enabled: boolean): void;
|
|
65
|
+
/**
|
|
66
|
+
* Get current mock enabled state
|
|
67
|
+
*/
|
|
68
|
+
declare function isMockEnabled(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Get the http instance (PureHttp wrapper)
|
|
71
|
+
* This is a placeholder - users should provide their own http instance
|
|
72
|
+
* or use initMockInterceptorForPureHttp with their specific setup
|
|
73
|
+
*/
|
|
74
|
+
type HttpInstance = {
|
|
75
|
+
constructor: {
|
|
76
|
+
axiosInstance: AxiosInstance;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Register the http instance for PureHttp compatibility
|
|
81
|
+
* Call this before initMockInterceptorForPureHttp
|
|
82
|
+
*/
|
|
83
|
+
declare function registerHttpInstance(http: HttpInstance): void;
|
|
84
|
+
/**
|
|
85
|
+
* Initialize mock interceptor for PureHttp
|
|
86
|
+
* Should be called after PureHttp is instantiated and registered
|
|
87
|
+
*/
|
|
88
|
+
declare function initMockInterceptorForPureHttp(): Promise<void>;
|
|
89
|
+
|
|
90
|
+
export { type MockBundleData, type MockInterceptorOptions, createMockInterceptor, initMockInterceptor, initMockInterceptorForPureHttp, isMockEnabled, loadMockData, registerHttpInstance, setMockEnabled };
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/client/index.ts
|
|
21
|
+
var client_exports = {};
|
|
22
|
+
__export(client_exports, {
|
|
23
|
+
createMockInterceptor: () => createMockInterceptor,
|
|
24
|
+
initMockInterceptor: () => initMockInterceptor,
|
|
25
|
+
initMockInterceptorForPureHttp: () => initMockInterceptorForPureHttp,
|
|
26
|
+
isMockEnabled: () => isMockEnabled,
|
|
27
|
+
loadMockData: () => loadMockData,
|
|
28
|
+
registerHttpInstance: () => registerHttpInstance,
|
|
29
|
+
setMockEnabled: () => setMockEnabled
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(client_exports);
|
|
32
|
+
|
|
33
|
+
// src/client/interceptor.ts
|
|
34
|
+
var import_meta = {};
|
|
35
|
+
function getEnvVar(name) {
|
|
36
|
+
if (typeof import_meta !== "undefined" && import_meta.env) {
|
|
37
|
+
return import_meta.env[name];
|
|
38
|
+
}
|
|
39
|
+
if (typeof process !== "undefined" && process.env) {
|
|
40
|
+
return process.env[name];
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
var MockInterceptor = class {
|
|
45
|
+
options;
|
|
46
|
+
constructor(options) {
|
|
47
|
+
this.options = options;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if mock is enabled
|
|
51
|
+
*/
|
|
52
|
+
isEnabled() {
|
|
53
|
+
if (window.__MOCK_ENABLED__ !== void 0) {
|
|
54
|
+
return window.__MOCK_ENABLED__;
|
|
55
|
+
}
|
|
56
|
+
if (typeof this.options.enabled === "function") {
|
|
57
|
+
return this.options.enabled();
|
|
58
|
+
}
|
|
59
|
+
return this.options.enabled ?? false;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Find matching mock data for the request
|
|
63
|
+
* Supports both formats:
|
|
64
|
+
* - "GET /api/v1/asset/xxx" (HTTP method + URL)
|
|
65
|
+
* - "/api/v1/asset/xxx/get.js" (File path format from automock plugin)
|
|
66
|
+
*/
|
|
67
|
+
findMock(url, method) {
|
|
68
|
+
const methodUpper = method.toUpperCase();
|
|
69
|
+
const methodLower = method.toLowerCase();
|
|
70
|
+
const httpMethodKey = `${methodUpper} ${url}`;
|
|
71
|
+
if (this.options.mockData[httpMethodKey]) {
|
|
72
|
+
return this.options.mockData[httpMethodKey];
|
|
73
|
+
}
|
|
74
|
+
const filePathKey = `${url}/${methodLower}.js`;
|
|
75
|
+
if (this.options.mockData[filePathKey]) {
|
|
76
|
+
return this.options.mockData[filePathKey];
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Determine if request should be mocked
|
|
82
|
+
*/
|
|
83
|
+
shouldMock(url, method) {
|
|
84
|
+
if (!this.isEnabled()) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const mock = this.findMock(url, method);
|
|
88
|
+
return mock !== null && mock.enable;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get mock data for request
|
|
92
|
+
*/
|
|
93
|
+
getMock(url, method) {
|
|
94
|
+
if (!this.shouldMock(url, method)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return this.findMock(url, method);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Setup axios interceptor
|
|
101
|
+
*/
|
|
102
|
+
setupAxios(axiosInstance) {
|
|
103
|
+
axiosInstance.interceptors.request.use(
|
|
104
|
+
async (config) => {
|
|
105
|
+
const url = config.url || "";
|
|
106
|
+
const method = config.method?.toUpperCase() || "GET";
|
|
107
|
+
const mock = this.getMock(url, method);
|
|
108
|
+
if (mock) {
|
|
109
|
+
this.options.onMockHit?.(url, method, mock);
|
|
110
|
+
config.adapter = async () => {
|
|
111
|
+
const { data, delay = 0, status = 200 } = mock;
|
|
112
|
+
if (delay > 0) {
|
|
113
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
data,
|
|
117
|
+
status,
|
|
118
|
+
statusText: "OK",
|
|
119
|
+
headers: {},
|
|
120
|
+
config
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
} else {
|
|
124
|
+
const reason = !this.isEnabled() ? "Mock disabled globally" : "No matching mock found or mock disabled";
|
|
125
|
+
this.options.onBypass?.(url, method, reason);
|
|
126
|
+
}
|
|
127
|
+
return config;
|
|
128
|
+
},
|
|
129
|
+
(error) => Promise.reject(error)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
function createMockInterceptor(options) {
|
|
134
|
+
return new MockInterceptor(options);
|
|
135
|
+
}
|
|
136
|
+
async function loadMockData() {
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch("/mock-data.json");
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
console.warn(
|
|
141
|
+
"[MockInterceptor] Failed to load mock-data.json:",
|
|
142
|
+
response.statusText
|
|
143
|
+
);
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
const data = await response.json();
|
|
147
|
+
return data;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.warn("[MockInterceptor] Failed to load mock-data.json:", error);
|
|
150
|
+
return {};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function initMockInterceptor(axiosInstance) {
|
|
154
|
+
const mockData = await loadMockData();
|
|
155
|
+
if (!axiosInstance) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
"[MockInterceptor] axiosInstance is required. Please provide an axios instance."
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
const isEnvEnabled = getEnvVar("VITE_USE_MOCK") === "true";
|
|
161
|
+
const interceptor = createMockInterceptor({
|
|
162
|
+
mockData,
|
|
163
|
+
enabled: isEnvEnabled,
|
|
164
|
+
onMockHit: (url, method) => {
|
|
165
|
+
console.log(`[MOCK HIT] ${method} ${url}`);
|
|
166
|
+
},
|
|
167
|
+
onBypass: (url, method, reason) => {
|
|
168
|
+
console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
interceptor.setupAxios(axiosInstance);
|
|
172
|
+
}
|
|
173
|
+
function setMockEnabled(enabled) {
|
|
174
|
+
window.__MOCK_ENABLED__ = enabled;
|
|
175
|
+
}
|
|
176
|
+
function isMockEnabled() {
|
|
177
|
+
return !!window.__MOCK_ENABLED__;
|
|
178
|
+
}
|
|
179
|
+
var httpInstanceRef;
|
|
180
|
+
function registerHttpInstance(http) {
|
|
181
|
+
httpInstanceRef = http;
|
|
182
|
+
}
|
|
183
|
+
async function initMockInterceptorForPureHttp() {
|
|
184
|
+
if (!httpInstanceRef) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
"[MockInterceptor] http instance not registered. Call registerHttpInstance(http) first."
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
const mockData = await loadMockData();
|
|
190
|
+
const isEnvEnabled = getEnvVar("VITE_USE_MOCK") === "true";
|
|
191
|
+
const interceptor = createMockInterceptor({
|
|
192
|
+
mockData,
|
|
193
|
+
enabled: isEnvEnabled,
|
|
194
|
+
onMockHit: (url, method) => {
|
|
195
|
+
console.log(`[MOCK HIT] ${method} ${url}`);
|
|
196
|
+
},
|
|
197
|
+
onBypass: (url, method, reason) => {
|
|
198
|
+
console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);
|
|
202
|
+
}
|
|
203
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
204
|
+
0 && (module.exports = {
|
|
205
|
+
createMockInterceptor,
|
|
206
|
+
initMockInterceptor,
|
|
207
|
+
initMockInterceptorForPureHttp,
|
|
208
|
+
isMockEnabled,
|
|
209
|
+
loadMockData,
|
|
210
|
+
registerHttpInstance,
|
|
211
|
+
setMockEnabled
|
|
212
|
+
});
|
|
213
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts","../../src/client/interceptor.ts"],"sourcesContent":["export {\n createMockInterceptor,\n initMockInterceptor,\n initMockInterceptorForPureHttp,\n setMockEnabled,\n isMockEnabled,\n loadMockData,\n registerHttpInstance\n} from './interceptor'\n\nexport type { MockInterceptorOptions, MockBundleData } from './interceptor'\n","import type {\n AxiosInstance,\n AxiosRequestConfig,\n InternalAxiosRequestConfig,\n} from \"axios\";\n\n/**\n * Helper function to safely get environment variables\n * Works in both ESM and CJS environments\n */\nfunction getEnvVar(name: string): string | undefined {\n // Try ESM environment (Vite)\n if (typeof import.meta !== \"undefined\" && import.meta.env) {\n return import.meta.env[name as keyof ImportMetaEnv];\n }\n // Fallback to CommonJS (Node.js)\n if (typeof process !== \"undefined\" && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n\nexport type MockBundleData = {\n enable: boolean;\n data: unknown;\n delay?: number;\n status?: number;\n isBinary?: boolean;\n};\n\nexport type MockInterceptorOptions = {\n mockData: Record<string, MockBundleData>;\n enabled?: boolean | (() => boolean);\n onMockHit?: (url: string, method: string, mock: MockBundleData) => void;\n onBypass?: (url: string, method: string, reason: string) => void;\n};\n\ntype MockEnabledState = boolean | undefined;\n\ndeclare global {\n interface Window {\n __MOCK_ENABLED__?: MockEnabledState;\n }\n}\n\nclass MockInterceptor {\n private options: MockInterceptorOptions;\n\n constructor(options: MockInterceptorOptions) {\n this.options = options;\n }\n\n /**\n * Check if mock is enabled\n */\n isEnabled(): boolean {\n // Check runtime flag first\n if (window.__MOCK_ENABLED__ !== undefined) {\n return window.__MOCK_ENABLED__;\n }\n // Check environment variable\n if (typeof this.options.enabled === \"function\") {\n return this.options.enabled();\n }\n return this.options.enabled ?? false;\n }\n\n /**\n * Find matching mock data for the request\n * Supports both formats:\n * - \"GET /api/v1/asset/xxx\" (HTTP method + URL)\n * - \"/api/v1/asset/xxx/get.js\" (File path format from automock plugin)\n */\n private findMock(url: string, method: string): MockBundleData | null {\n const methodUpper = method.toUpperCase();\n const methodLower = method.toLowerCase();\n\n // Try HTTP method + URL format first (for compatibility)\n const httpMethodKey = `${methodUpper} ${url}`;\n if (this.options.mockData[httpMethodKey]) {\n return this.options.mockData[httpMethodKey];\n }\n\n // Try file path format from automock plugin: /api/v1/asset/xxx/get.js\n const filePathKey = `${url}/${methodLower}.js`;\n if (this.options.mockData[filePathKey]) {\n return this.options.mockData[filePathKey];\n }\n\n return null;\n }\n\n /**\n * Determine if request should be mocked\n */\n shouldMock(url: string, method: string): boolean {\n if (!this.isEnabled()) {\n return false;\n }\n\n const mock = this.findMock(url, method);\n return mock !== null && mock.enable;\n }\n\n /**\n * Get mock data for request\n */\n getMock(url: string, method: string): MockBundleData | null {\n if (!this.shouldMock(url, method)) {\n return null;\n }\n return this.findMock(url, method);\n }\n\n /**\n * Setup axios interceptor\n */\n setupAxios(axiosInstance: AxiosInstance): void {\n axiosInstance.interceptors.request.use(\n async (config: InternalAxiosRequestConfig) => {\n const url = config.url || \"\";\n const method = config.method?.toUpperCase() || \"GET\";\n\n const mock = this.getMock(url, method);\n\n if (mock) {\n // Trigger mock hit callback\n this.options.onMockHit?.(url, method, mock);\n\n // Set adapter to return mock data\n (config as any).adapter = async () => {\n const { data, delay = 0, status = 200 } = mock;\n\n // Simulate delay\n if (delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n return {\n data,\n status,\n statusText: \"OK\",\n headers: {},\n config,\n };\n };\n } else {\n // Trigger bypass callback\n const reason = !this.isEnabled()\n ? \"Mock disabled globally\"\n : \"No matching mock found or mock disabled\";\n this.options.onBypass?.(url, method, reason);\n }\n\n return config;\n },\n (error: unknown) => Promise.reject(error),\n );\n }\n}\n\n/**\n * Create mock interceptor instance\n */\nexport function createMockInterceptor(\n options: MockInterceptorOptions,\n): MockInterceptor {\n return new MockInterceptor(options);\n}\n\n/**\n * Load mock data from bundled JSON file\n */\nexport async function loadMockData(): Promise<Record<string, MockBundleData>> {\n try {\n // In production, mock-data.json is generated by automock plugin\n // Use absolute path to load it from dist directory\n const response = await fetch(\"/mock-data.json\");\n if (!response.ok) {\n console.warn(\n \"[MockInterceptor] Failed to load mock-data.json:\",\n response.statusText,\n );\n return {};\n }\n const data = (await response.json()) as Record<string, MockBundleData>;\n return data;\n } catch (error) {\n console.warn(\"[MockInterceptor] Failed to load mock-data.json:\", error);\n return {};\n }\n}\n\n/**\n * Initialize mock interceptor with default settings\n */\nexport async function initMockInterceptor(\n axiosInstance?: AxiosInstance,\n): Promise<void> {\n const mockData = await loadMockData();\n\n if (!axiosInstance) {\n throw new Error(\n \"[MockInterceptor] axiosInstance is required. Please provide an axios instance.\",\n );\n }\n\n // Check if mock is enabled via environment variable\n const isEnvEnabled = getEnvVar(\"VITE_USE_MOCK\") === \"true\";\n\n const interceptor = createMockInterceptor({\n mockData,\n enabled: isEnvEnabled,\n onMockHit: (url: string, method: string) => {\n console.log(`[MOCK HIT] ${method} ${url}`);\n },\n onBypass: (url: string, method: string, reason: string) => {\n console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);\n },\n });\n\n // Setup interceptor on the axios instance\n interceptor.setupAxios(axiosInstance);\n}\n\n/**\n * Set mock enabled state at runtime\n */\nexport function setMockEnabled(enabled: boolean): void {\n window.__MOCK_ENABLED__ = enabled;\n}\n\n/**\n * Get current mock enabled state\n */\nexport function isMockEnabled(): boolean {\n return !!window.__MOCK_ENABLED__;\n}\n\n/**\n * Get the http instance (PureHttp wrapper)\n * This is a placeholder - users should provide their own http instance\n * or use initMockInterceptorForPureHttp with their specific setup\n */\ntype HttpInstance = {\n constructor: { axiosInstance: AxiosInstance };\n};\n\n// This is a reference to the user's http instance\n// It should be set by the user's application code\nlet httpInstanceRef: HttpInstance | undefined;\n\n/**\n * Register the http instance for PureHttp compatibility\n * Call this before initMockInterceptorForPureHttp\n */\nexport function registerHttpInstance(http: HttpInstance): void {\n httpInstanceRef = http;\n}\n\n/**\n * Initialize mock interceptor for PureHttp\n * Should be called after PureHttp is instantiated and registered\n */\nexport async function initMockInterceptorForPureHttp(): Promise<void> {\n if (!httpInstanceRef) {\n throw new Error(\n \"[MockInterceptor] http instance not registered. Call registerHttpInstance(http) first.\",\n );\n }\n\n const mockData = await loadMockData();\n\n // Check if mock is enabled via environment variable\n const isEnvEnabled = getEnvVar(\"VITE_USE_MOCK\") === \"true\";\n\n const interceptor = createMockInterceptor({\n mockData,\n enabled: isEnvEnabled,\n onMockHit: (url: string, method: string) => {\n console.log(`[MOCK HIT] ${method} ${url}`);\n },\n onBypass: (url: string, method: string, reason: string) => {\n console.log(`[MOCK BYPASS] ${method} ${url} - ${reason}`);\n },\n });\n\n // Get axios instance from PureHttp and setup interceptor\n interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);\n}\n"],"mappings":"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","names":[]}
|