vite-plugin-automock 1.1.6 → 1.2.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/README.md +11 -25
- package/dist/{chunk-DJWYFFPU.mjs → chunk-2KRAWI7J.mjs} +35 -11
- package/dist/chunk-2KRAWI7J.mjs.map +1 -0
- package/dist/client/index.d.mts +3 -2
- package/dist/client/index.d.ts +3 -2
- package/dist/client/index.js +34 -10
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +65 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -10
- package/dist/index.mjs.map +1 -1
- package/docs/API_CN.md +398 -0
- package/docs/CONFIGURATION_CN.md +196 -0
- package/package.json +3 -2
- package/dist/chunk-DJWYFFPU.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ import { automock } from 'vite-plugin-automock'
|
|
|
34
34
|
export default defineConfig({
|
|
35
35
|
plugins: [
|
|
36
36
|
automock({
|
|
37
|
-
proxyBaseUrl: 'https://api.example.com', // Required
|
|
37
|
+
proxyBaseUrl: 'https://api.example.com', // Required for proxy/capture
|
|
38
38
|
})
|
|
39
39
|
]
|
|
40
40
|
})
|
|
@@ -52,14 +52,14 @@ That's it! The plugin will:
|
|
|
52
52
|
|
|
53
53
|
| Option | Type | Required | Default | Description |
|
|
54
54
|
| ------ | ---- | -------- | ------- | ----------- |
|
|
55
|
-
| `proxyBaseUrl` | `string` |
|
|
55
|
+
| `proxyBaseUrl` | `string` | No | - | Base URL of your API server. Required only when `proxy` or `capture` is used |
|
|
56
56
|
| `mockDir` | `string` | No | `'mock'` | Directory to store mock files |
|
|
57
57
|
| `apiPrefix` | `string` | No | `'/api'` | API path prefix to intercept |
|
|
58
58
|
| `pathRewrite` | `(path: string) => string` | No | `(path) => path` | Function to rewrite request paths |
|
|
59
59
|
| `enabled` | `boolean` | No | `true` | Global switch. When `false`, all requests pass through without mock handling |
|
|
60
60
|
| `proxy` | `boolean` | No | `true` | Whether to proxy unmatched requests to `proxyBaseUrl` |
|
|
61
61
|
| `capture` | `boolean` | No | `true` | Whether to auto-capture proxy responses and save as mock files |
|
|
62
|
-
| `productionMock` | `boolean \| 'auto'` | No | `false` | Enable mock
|
|
62
|
+
| `productionMock` | `boolean \| 'auto'` | No | `false` | Enable client-side mock bundle support |
|
|
63
63
|
| `inspector` | `boolean \| InspectorOptions` | No | `false` | Enable visual mock inspector UI |
|
|
64
64
|
|
|
65
65
|
### Bundle Options
|
|
@@ -71,6 +71,8 @@ These options are available when using the `automock()` function (which includes
|
|
|
71
71
|
| `bundleMockData` | `boolean` | `true` | Whether to bundle mock data for production |
|
|
72
72
|
| `bundleOutputPath` | `string` | `'public/mock-data.json'` | Output path for the mock bundle file |
|
|
73
73
|
|
|
74
|
+
For a complete explanation of option interactions, see [Configuration Guide](docs/CONFIGURATION_CN.md).
|
|
75
|
+
|
|
74
76
|
### Inspector Options
|
|
75
77
|
|
|
76
78
|
When `inspector` is an object:
|
|
@@ -114,22 +116,9 @@ export default defineConfig({
|
|
|
114
116
|
})
|
|
115
117
|
```
|
|
116
118
|
|
|
117
|
-
##
|
|
118
|
-
|
|
119
|
-
The `enabled`, `proxy`, and `capture` options provide independent control over the plugin's behavior:
|
|
120
|
-
|
|
121
|
-
### Record-only Mode
|
|
119
|
+
## Behavior Modes
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
automock({
|
|
127
|
-
proxyBaseUrl: 'https://api.example.com',
|
|
128
|
-
enabled: false, // Don't intercept
|
|
129
|
-
proxy: true, // Proxy to backend
|
|
130
|
-
capture: true, // Save responses as mock files
|
|
131
|
-
})
|
|
132
|
-
```
|
|
121
|
+
The `enabled`, `proxy`, and `capture` options control different parts of the development middleware. `enabled=false` is a global bypass: the plugin will not read mocks, proxy, or capture.
|
|
133
122
|
|
|
134
123
|
### Mock-only Mode
|
|
135
124
|
|
|
@@ -144,16 +133,13 @@ automock({
|
|
|
144
133
|
})
|
|
145
134
|
```
|
|
146
135
|
|
|
147
|
-
###
|
|
136
|
+
### Pass-through Mode
|
|
148
137
|
|
|
149
|
-
Bypass all
|
|
138
|
+
Bypass all automock handling and let later Vite middleware, including `server.proxy`, handle the request:
|
|
150
139
|
|
|
151
140
|
```typescript
|
|
152
141
|
automock({
|
|
153
|
-
|
|
154
|
-
enabled: false, // Don't intercept
|
|
155
|
-
proxy: true, // Proxy to backend
|
|
156
|
-
capture: false, // Don't save mocks
|
|
142
|
+
enabled: false,
|
|
157
143
|
})
|
|
158
144
|
```
|
|
159
145
|
|
|
@@ -265,7 +251,7 @@ automock({
|
|
|
265
251
|
|
|
266
252
|
3. Initialize the client interceptor in your app (see [Client API Reference](#client-api-reference)).
|
|
267
253
|
|
|
268
|
-
4. The `productionMock: 'auto'` option
|
|
254
|
+
4. The `productionMock: 'auto'` option enables mock outside `production` mode and disables it in `production` mode.
|
|
269
255
|
|
|
270
256
|
## Mock Inspector
|
|
271
257
|
|
|
@@ -4,6 +4,24 @@ var MockInterceptor = class {
|
|
|
4
4
|
constructor(options) {
|
|
5
5
|
this.options = options;
|
|
6
6
|
}
|
|
7
|
+
normalizeUrlPath(url, baseURL) {
|
|
8
|
+
if (!url) return "";
|
|
9
|
+
try {
|
|
10
|
+
const isAbsoluteUrl = /^[a-z][a-z\d+\-.]*:\/\//i.test(url);
|
|
11
|
+
const browserOrigin = typeof window !== "undefined" && window.location?.origin ? window.location.origin : "http://localhost";
|
|
12
|
+
if (isAbsoluteUrl) {
|
|
13
|
+
return new URL(url).pathname;
|
|
14
|
+
}
|
|
15
|
+
if (baseURL) {
|
|
16
|
+
const normalizedBase = baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
|
|
17
|
+
const normalizedUrl = url.replace(/^\/+/, "");
|
|
18
|
+
return new URL(normalizedUrl, normalizedBase).pathname;
|
|
19
|
+
}
|
|
20
|
+
return new URL(url, browserOrigin).pathname;
|
|
21
|
+
} catch {
|
|
22
|
+
return url.split(/[?#]/)[0] || "";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
7
25
|
/**
|
|
8
26
|
* Check if mock is enabled
|
|
9
27
|
*/
|
|
@@ -22,14 +40,19 @@ var MockInterceptor = class {
|
|
|
22
40
|
* - "GET /api/v1/asset/xxx" (HTTP method + URL)
|
|
23
41
|
* - "/api/v1/asset/xxx/get.js" (File path format from automock plugin)
|
|
24
42
|
*/
|
|
25
|
-
findMock(url, method) {
|
|
43
|
+
findMock(url, method, baseURL) {
|
|
26
44
|
const methodUpper = method.toUpperCase();
|
|
27
45
|
const methodLower = method.toLowerCase();
|
|
46
|
+
const normalizedPath = this.normalizeUrlPath(url, baseURL);
|
|
28
47
|
const httpMethodKey = `${methodUpper} ${url}`;
|
|
29
48
|
if (this.options.mockData[httpMethodKey]) {
|
|
30
49
|
return this.options.mockData[httpMethodKey];
|
|
31
50
|
}
|
|
32
|
-
const
|
|
51
|
+
const normalizedHttpMethodKey = `${methodUpper} ${normalizedPath}`;
|
|
52
|
+
if (this.options.mockData[normalizedHttpMethodKey]) {
|
|
53
|
+
return this.options.mockData[normalizedHttpMethodKey];
|
|
54
|
+
}
|
|
55
|
+
const filePathKey = `${normalizedPath}/${methodLower}.js`;
|
|
33
56
|
if (this.options.mockData[filePathKey]) {
|
|
34
57
|
return this.options.mockData[filePathKey];
|
|
35
58
|
}
|
|
@@ -38,21 +61,21 @@ var MockInterceptor = class {
|
|
|
38
61
|
/**
|
|
39
62
|
* Determine if request should be mocked
|
|
40
63
|
*/
|
|
41
|
-
shouldMock(url, method) {
|
|
64
|
+
shouldMock(url, method, baseURL) {
|
|
42
65
|
if (!this.isEnabled()) {
|
|
43
66
|
return false;
|
|
44
67
|
}
|
|
45
|
-
const mock = this.findMock(url, method);
|
|
68
|
+
const mock = this.findMock(url, method, baseURL);
|
|
46
69
|
return mock !== null && mock.enable;
|
|
47
70
|
}
|
|
48
71
|
/**
|
|
49
72
|
* Get mock data for request
|
|
50
73
|
*/
|
|
51
|
-
getMock(url, method) {
|
|
52
|
-
if (!this.shouldMock(url, method)) {
|
|
74
|
+
getMock(url, method, baseURL) {
|
|
75
|
+
if (!this.shouldMock(url, method, baseURL)) {
|
|
53
76
|
return null;
|
|
54
77
|
}
|
|
55
|
-
return this.findMock(url, method);
|
|
78
|
+
return this.findMock(url, method, baseURL);
|
|
56
79
|
}
|
|
57
80
|
/**
|
|
58
81
|
* Setup axios interceptor
|
|
@@ -62,9 +85,10 @@ var MockInterceptor = class {
|
|
|
62
85
|
async (config) => {
|
|
63
86
|
const url = config.url || "";
|
|
64
87
|
const method = config.method?.toUpperCase() || "GET";
|
|
65
|
-
const
|
|
88
|
+
const normalizedUrl = this.normalizeUrlPath(url, config.baseURL);
|
|
89
|
+
const mock = this.getMock(url, method, config.baseURL);
|
|
66
90
|
if (mock) {
|
|
67
|
-
this.options.onMockHit?.(
|
|
91
|
+
this.options.onMockHit?.(normalizedUrl, method, mock);
|
|
68
92
|
config.adapter = async () => {
|
|
69
93
|
const { data, delay = 0, status = 200 } = mock;
|
|
70
94
|
if (delay > 0) {
|
|
@@ -80,7 +104,7 @@ var MockInterceptor = class {
|
|
|
80
104
|
};
|
|
81
105
|
} else {
|
|
82
106
|
const reason = !this.isEnabled() ? "Mock disabled globally" : "No matching mock found or mock disabled";
|
|
83
|
-
this.options.onBypass?.(
|
|
107
|
+
this.options.onBypass?.(normalizedUrl, method, reason);
|
|
84
108
|
}
|
|
85
109
|
return config;
|
|
86
110
|
},
|
|
@@ -160,4 +184,4 @@ export {
|
|
|
160
184
|
registerHttpInstance,
|
|
161
185
|
initMockInterceptorForPureHttp
|
|
162
186
|
};
|
|
163
|
-
//# sourceMappingURL=chunk-
|
|
187
|
+
//# sourceMappingURL=chunk-2KRAWI7J.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/interceptor.ts"],"sourcesContent":["import type {\n AxiosInstance,\n AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\n\ndeclare const __AUTOMOCK_ENABLED__: boolean;\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 private normalizeUrlPath(url: string, baseURL?: string): string {\n if (!url) return \"\";\n\n try {\n const isAbsoluteUrl = /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(url);\n const browserOrigin =\n typeof window !== \"undefined\" && window.location?.origin\n ? window.location.origin\n : \"http://localhost\";\n\n if (isAbsoluteUrl) {\n return new URL(url).pathname;\n }\n\n if (baseURL) {\n const normalizedBase = baseURL.endsWith(\"/\") ? baseURL : `${baseURL}/`;\n const normalizedUrl = url.replace(/^\\/+/, \"\");\n return new URL(normalizedUrl, normalizedBase).pathname;\n }\n\n return new URL(url, browserOrigin).pathname;\n } catch {\n return url.split(/[?#]/)[0] || \"\";\n }\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, baseURL?: string): MockBundleData | null {\n const methodUpper = method.toUpperCase();\n const methodLower = method.toLowerCase();\n const normalizedPath = this.normalizeUrlPath(url, baseURL);\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 const normalizedHttpMethodKey = `${methodUpper} ${normalizedPath}`;\n if (this.options.mockData[normalizedHttpMethodKey]) {\n return this.options.mockData[normalizedHttpMethodKey];\n }\n\n // Try file path format from automock plugin: /api/v1/asset/xxx/get.js\n const filePathKey = `${normalizedPath}/${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, baseURL?: string): boolean {\n if (!this.isEnabled()) {\n return false;\n }\n\n const mock = this.findMock(url, method, baseURL);\n return mock !== null && mock.enable;\n }\n\n /**\n * Get mock data for request\n */\n getMock(url: string, method: string, baseURL?: string): MockBundleData | null {\n if (!this.shouldMock(url, method, baseURL)) {\n return null;\n }\n return this.findMock(url, method, baseURL);\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 const normalizedUrl = this.normalizeUrlPath(url, config.baseURL);\n\n const mock = this.getMock(url, method, config.baseURL);\n\n if (mock) {\n // Trigger mock hit callback\n this.options.onMockHit?.(normalizedUrl, method, mock);\n\n // Set adapter to return mock data\n (config as InternalAxiosRequestConfig & { adapter: () => Promise<AxiosResponse> }).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?.(normalizedUrl, 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\nasync function createInterceptorFromBundle(): Promise<MockInterceptor> {\n const mockData = await loadMockData();\n const isEnabled = typeof __AUTOMOCK_ENABLED__ !== \"undefined\" && __AUTOMOCK_ENABLED__;\n\n return new MockInterceptor({\n mockData,\n enabled: isEnabled,\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\nexport async function initMockInterceptor(\n axiosInstance?: AxiosInstance,\n): Promise<void> {\n if (!axiosInstance) {\n throw new Error(\n \"[MockInterceptor] axiosInstance is required. Please provide an axios instance.\",\n );\n }\n\n const interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(axiosInstance);\n}\n\nexport function setMockEnabled(enabled: boolean): void {\n window.__MOCK_ENABLED__ = enabled;\n}\n\nexport function isMockEnabled(): boolean {\n return !!window.__MOCK_ENABLED__;\n}\n\ntype HttpInstance = {\n constructor: { axiosInstance: AxiosInstance };\n};\n\nlet httpInstanceRef: HttpInstance | undefined;\n\nexport function registerHttpInstance(http: HttpInstance): void {\n httpInstanceRef = http;\n}\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 interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);\n}\n"],"mappings":";AA+BA,IAAM,kBAAN,MAAsB;AAAA,EACZ;AAAA,EAER,YAAY,SAAiC;AAC3C,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,iBAAiB,KAAa,SAA0B;AAC9D,QAAI,CAAC,IAAK,QAAO;AAEjB,QAAI;AACF,YAAM,gBAAgB,2BAA2B,KAAK,GAAG;AACzD,YAAM,gBACJ,OAAO,WAAW,eAAe,OAAO,UAAU,SAC9C,OAAO,SAAS,SAChB;AAEN,UAAI,eAAe;AACjB,eAAO,IAAI,IAAI,GAAG,EAAE;AAAA,MACtB;AAEA,UAAI,SAAS;AACX,cAAM,iBAAiB,QAAQ,SAAS,GAAG,IAAI,UAAU,GAAG,OAAO;AACnE,cAAM,gBAAgB,IAAI,QAAQ,QAAQ,EAAE;AAC5C,eAAO,IAAI,IAAI,eAAe,cAAc,EAAE;AAAA,MAChD;AAEA,aAAO,IAAI,IAAI,KAAK,aAAa,EAAE;AAAA,IACrC,QAAQ;AACN,aAAO,IAAI,MAAM,MAAM,EAAE,CAAC,KAAK;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAqB;AAEnB,QAAI,OAAO,qBAAqB,QAAW;AACzC,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,OAAO,KAAK,QAAQ,YAAY,YAAY;AAC9C,aAAO,KAAK,QAAQ,QAAQ;AAAA,IAC9B;AACA,WAAO,KAAK,QAAQ,WAAW;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,SAAS,KAAa,QAAgB,SAAyC;AACrF,UAAM,cAAc,OAAO,YAAY;AACvC,UAAM,cAAc,OAAO,YAAY;AACvC,UAAM,iBAAiB,KAAK,iBAAiB,KAAK,OAAO;AAGzD,UAAM,gBAAgB,GAAG,WAAW,IAAI,GAAG;AAC3C,QAAI,KAAK,QAAQ,SAAS,aAAa,GAAG;AACxC,aAAO,KAAK,QAAQ,SAAS,aAAa;AAAA,IAC5C;AAEA,UAAM,0BAA0B,GAAG,WAAW,IAAI,cAAc;AAChE,QAAI,KAAK,QAAQ,SAAS,uBAAuB,GAAG;AAClD,aAAO,KAAK,QAAQ,SAAS,uBAAuB;AAAA,IACtD;AAGA,UAAM,cAAc,GAAG,cAAc,IAAI,WAAW;AACpD,QAAI,KAAK,QAAQ,SAAS,WAAW,GAAG;AACtC,aAAO,KAAK,QAAQ,SAAS,WAAW;AAAA,IAC1C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,KAAa,QAAgB,SAA2B;AACjE,QAAI,CAAC,KAAK,UAAU,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,KAAK,SAAS,KAAK,QAAQ,OAAO;AAC/C,WAAO,SAAS,QAAQ,KAAK;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,KAAa,QAAgB,SAAyC;AAC5E,QAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,OAAO,GAAG;AAC1C,aAAO;AAAA,IACT;AACA,WAAO,KAAK,SAAS,KAAK,QAAQ,OAAO;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,eAAoC;AAC7C,kBAAc,aAAa,QAAQ;AAAA,MACjC,OAAO,WAAuC;AAC5C,cAAM,MAAM,OAAO,OAAO;AAC1B,cAAM,SAAS,OAAO,QAAQ,YAAY,KAAK;AAC/C,cAAM,gBAAgB,KAAK,iBAAiB,KAAK,OAAO,OAAO;AAE/D,cAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,OAAO,OAAO;AAErD,YAAI,MAAM;AAER,eAAK,QAAQ,YAAY,eAAe,QAAQ,IAAI;AAGpD,UAAC,OAAkF,UAAU,YAAY;AACvG,kBAAM,EAAE,MAAM,QAAQ,GAAG,SAAS,IAAI,IAAI;AAG1C,gBAAI,QAAQ,GAAG;AACb,oBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,YAC3D;AAEA,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,cACA,YAAY;AAAA,cACZ,SAAS,CAAC;AAAA,cACV;AAAA,YACF;AAAA,UACF;AAAA,QACF,OAAO;AAEL,gBAAM,SAAS,CAAC,KAAK,UAAU,IAC3B,2BACA;AACJ,eAAK,QAAQ,WAAW,eAAe,QAAQ,MAAM;AAAA,QACvD;AAEA,eAAO;AAAA,MACT;AAAA,MACA,CAAC,UAAmB,QAAQ,OAAO,KAAK;AAAA,IAC1C;AAAA,EACF;AACF;AAKO,SAAS,sBACd,SACiB;AACjB,SAAO,IAAI,gBAAgB,OAAO;AACpC;AAKA,eAAsB,eAAwD;AAC5E,MAAI;AAGF,UAAM,WAAW,MAAM,MAAM,iBAAiB;AAC9C,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ;AAAA,QACN;AAAA,QACA,SAAS;AAAA,MACX;AACA,aAAO,CAAC;AAAA,IACV;AACA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,KAAK,oDAAoD,KAAK;AACtE,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,8BAAwD;AACrE,QAAM,WAAW,MAAM,aAAa;AACpC,QAAM,YAAY,OAAO,yBAAyB,eAAe;AAEjE,SAAO,IAAI,gBAAgB;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,IACT,WAAW,CAAC,KAAa,WAAmB;AAC1C,cAAQ,IAAI,cAAc,MAAM,IAAI,GAAG,EAAE;AAAA,IAC3C;AAAA,IACA,UAAU,CAAC,KAAa,QAAgB,WAAmB;AACzD,cAAQ,IAAI,iBAAiB,MAAM,IAAI,GAAG,MAAM,MAAM,EAAE;AAAA,IAC1D;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,oBACpB,eACe;AACf,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,MAAM,4BAA4B;AACtD,cAAY,WAAW,aAAa;AACtC;AAEO,SAAS,eAAe,SAAwB;AACrD,SAAO,mBAAmB;AAC5B;AAEO,SAAS,gBAAyB;AACvC,SAAO,CAAC,CAAC,OAAO;AAClB;AAMA,IAAI;AAEG,SAAS,qBAAqB,MAA0B;AAC7D,oBAAkB;AACpB;AAEA,eAAsB,iCAAgD;AACpE,MAAI,CAAC,iBAAiB;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,MAAM,4BAA4B;AACtD,cAAY,WAAW,gBAAgB,YAAY,aAAa;AAClE;","names":[]}
|
package/dist/client/index.d.mts
CHANGED
|
@@ -22,6 +22,7 @@ declare global {
|
|
|
22
22
|
declare class MockInterceptor {
|
|
23
23
|
private options;
|
|
24
24
|
constructor(options: MockInterceptorOptions);
|
|
25
|
+
private normalizeUrlPath;
|
|
25
26
|
/**
|
|
26
27
|
* Check if mock is enabled
|
|
27
28
|
*/
|
|
@@ -36,11 +37,11 @@ declare class MockInterceptor {
|
|
|
36
37
|
/**
|
|
37
38
|
* Determine if request should be mocked
|
|
38
39
|
*/
|
|
39
|
-
shouldMock(url: string, method: string): boolean;
|
|
40
|
+
shouldMock(url: string, method: string, baseURL?: string): boolean;
|
|
40
41
|
/**
|
|
41
42
|
* Get mock data for request
|
|
42
43
|
*/
|
|
43
|
-
getMock(url: string, method: string): MockBundleData | null;
|
|
44
|
+
getMock(url: string, method: string, baseURL?: string): MockBundleData | null;
|
|
44
45
|
/**
|
|
45
46
|
* Setup axios interceptor
|
|
46
47
|
*/
|
package/dist/client/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare global {
|
|
|
22
22
|
declare class MockInterceptor {
|
|
23
23
|
private options;
|
|
24
24
|
constructor(options: MockInterceptorOptions);
|
|
25
|
+
private normalizeUrlPath;
|
|
25
26
|
/**
|
|
26
27
|
* Check if mock is enabled
|
|
27
28
|
*/
|
|
@@ -36,11 +37,11 @@ declare class MockInterceptor {
|
|
|
36
37
|
/**
|
|
37
38
|
* Determine if request should be mocked
|
|
38
39
|
*/
|
|
39
|
-
shouldMock(url: string, method: string): boolean;
|
|
40
|
+
shouldMock(url: string, method: string, baseURL?: string): boolean;
|
|
40
41
|
/**
|
|
41
42
|
* Get mock data for request
|
|
42
43
|
*/
|
|
43
|
-
getMock(url: string, method: string): MockBundleData | null;
|
|
44
|
+
getMock(url: string, method: string, baseURL?: string): MockBundleData | null;
|
|
44
45
|
/**
|
|
45
46
|
* Setup axios interceptor
|
|
46
47
|
*/
|
package/dist/client/index.js
CHANGED
|
@@ -36,6 +36,24 @@ var MockInterceptor = class {
|
|
|
36
36
|
constructor(options) {
|
|
37
37
|
this.options = options;
|
|
38
38
|
}
|
|
39
|
+
normalizeUrlPath(url, baseURL) {
|
|
40
|
+
if (!url) return "";
|
|
41
|
+
try {
|
|
42
|
+
const isAbsoluteUrl = /^[a-z][a-z\d+\-.]*:\/\//i.test(url);
|
|
43
|
+
const browserOrigin = typeof window !== "undefined" && window.location?.origin ? window.location.origin : "http://localhost";
|
|
44
|
+
if (isAbsoluteUrl) {
|
|
45
|
+
return new URL(url).pathname;
|
|
46
|
+
}
|
|
47
|
+
if (baseURL) {
|
|
48
|
+
const normalizedBase = baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
|
|
49
|
+
const normalizedUrl = url.replace(/^\/+/, "");
|
|
50
|
+
return new URL(normalizedUrl, normalizedBase).pathname;
|
|
51
|
+
}
|
|
52
|
+
return new URL(url, browserOrigin).pathname;
|
|
53
|
+
} catch {
|
|
54
|
+
return url.split(/[?#]/)[0] || "";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
39
57
|
/**
|
|
40
58
|
* Check if mock is enabled
|
|
41
59
|
*/
|
|
@@ -54,14 +72,19 @@ var MockInterceptor = class {
|
|
|
54
72
|
* - "GET /api/v1/asset/xxx" (HTTP method + URL)
|
|
55
73
|
* - "/api/v1/asset/xxx/get.js" (File path format from automock plugin)
|
|
56
74
|
*/
|
|
57
|
-
findMock(url, method) {
|
|
75
|
+
findMock(url, method, baseURL) {
|
|
58
76
|
const methodUpper = method.toUpperCase();
|
|
59
77
|
const methodLower = method.toLowerCase();
|
|
78
|
+
const normalizedPath = this.normalizeUrlPath(url, baseURL);
|
|
60
79
|
const httpMethodKey = `${methodUpper} ${url}`;
|
|
61
80
|
if (this.options.mockData[httpMethodKey]) {
|
|
62
81
|
return this.options.mockData[httpMethodKey];
|
|
63
82
|
}
|
|
64
|
-
const
|
|
83
|
+
const normalizedHttpMethodKey = `${methodUpper} ${normalizedPath}`;
|
|
84
|
+
if (this.options.mockData[normalizedHttpMethodKey]) {
|
|
85
|
+
return this.options.mockData[normalizedHttpMethodKey];
|
|
86
|
+
}
|
|
87
|
+
const filePathKey = `${normalizedPath}/${methodLower}.js`;
|
|
65
88
|
if (this.options.mockData[filePathKey]) {
|
|
66
89
|
return this.options.mockData[filePathKey];
|
|
67
90
|
}
|
|
@@ -70,21 +93,21 @@ var MockInterceptor = class {
|
|
|
70
93
|
/**
|
|
71
94
|
* Determine if request should be mocked
|
|
72
95
|
*/
|
|
73
|
-
shouldMock(url, method) {
|
|
96
|
+
shouldMock(url, method, baseURL) {
|
|
74
97
|
if (!this.isEnabled()) {
|
|
75
98
|
return false;
|
|
76
99
|
}
|
|
77
|
-
const mock = this.findMock(url, method);
|
|
100
|
+
const mock = this.findMock(url, method, baseURL);
|
|
78
101
|
return mock !== null && mock.enable;
|
|
79
102
|
}
|
|
80
103
|
/**
|
|
81
104
|
* Get mock data for request
|
|
82
105
|
*/
|
|
83
|
-
getMock(url, method) {
|
|
84
|
-
if (!this.shouldMock(url, method)) {
|
|
106
|
+
getMock(url, method, baseURL) {
|
|
107
|
+
if (!this.shouldMock(url, method, baseURL)) {
|
|
85
108
|
return null;
|
|
86
109
|
}
|
|
87
|
-
return this.findMock(url, method);
|
|
110
|
+
return this.findMock(url, method, baseURL);
|
|
88
111
|
}
|
|
89
112
|
/**
|
|
90
113
|
* Setup axios interceptor
|
|
@@ -94,9 +117,10 @@ var MockInterceptor = class {
|
|
|
94
117
|
async (config) => {
|
|
95
118
|
const url = config.url || "";
|
|
96
119
|
const method = config.method?.toUpperCase() || "GET";
|
|
97
|
-
const
|
|
120
|
+
const normalizedUrl = this.normalizeUrlPath(url, config.baseURL);
|
|
121
|
+
const mock = this.getMock(url, method, config.baseURL);
|
|
98
122
|
if (mock) {
|
|
99
|
-
this.options.onMockHit?.(
|
|
123
|
+
this.options.onMockHit?.(normalizedUrl, method, mock);
|
|
100
124
|
config.adapter = async () => {
|
|
101
125
|
const { data, delay = 0, status = 200 } = mock;
|
|
102
126
|
if (delay > 0) {
|
|
@@ -112,7 +136,7 @@ var MockInterceptor = class {
|
|
|
112
136
|
};
|
|
113
137
|
} else {
|
|
114
138
|
const reason = !this.isEnabled() ? "Mock disabled globally" : "No matching mock found or mock disabled";
|
|
115
|
-
this.options.onBypass?.(
|
|
139
|
+
this.options.onBypass?.(normalizedUrl, method, reason);
|
|
116
140
|
}
|
|
117
141
|
return config;
|
|
118
142
|
},
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +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 AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\n\ndeclare const __AUTOMOCK_ENABLED__: boolean;\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 InternalAxiosRequestConfig & { adapter: () => Promise<AxiosResponse> }).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\nasync function createInterceptorFromBundle(): Promise<MockInterceptor> {\n const mockData = await loadMockData();\n const isEnabled = typeof __AUTOMOCK_ENABLED__ !== \"undefined\" && __AUTOMOCK_ENABLED__;\n\n return new MockInterceptor({\n mockData,\n enabled: isEnabled,\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\nexport async function initMockInterceptor(\n axiosInstance?: AxiosInstance,\n): Promise<void> {\n if (!axiosInstance) {\n throw new Error(\n \"[MockInterceptor] axiosInstance is required. Please provide an axios instance.\",\n );\n }\n\n const interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(axiosInstance);\n}\n\nexport function setMockEnabled(enabled: boolean): void {\n window.__MOCK_ENABLED__ = enabled;\n}\n\nexport function isMockEnabled(): boolean {\n return !!window.__MOCK_ENABLED__;\n}\n\ntype HttpInstance = {\n constructor: { axiosInstance: AxiosInstance };\n};\n\nlet httpInstanceRef: HttpInstance | undefined;\n\nexport function registerHttpInstance(http: HttpInstance): void {\n httpInstanceRef = http;\n}\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 interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC+BA,IAAM,kBAAN,MAAsB;AAAA,EACZ;AAAA,EAER,YAAY,SAAiC;AAC3C,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAqB;AAEnB,QAAI,OAAO,qBAAqB,QAAW;AACzC,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,OAAO,KAAK,QAAQ,YAAY,YAAY;AAC9C,aAAO,KAAK,QAAQ,QAAQ;AAAA,IAC9B;AACA,WAAO,KAAK,QAAQ,WAAW;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,SAAS,KAAa,QAAuC;AACnE,UAAM,cAAc,OAAO,YAAY;AACvC,UAAM,cAAc,OAAO,YAAY;AAGvC,UAAM,gBAAgB,GAAG,WAAW,IAAI,GAAG;AAC3C,QAAI,KAAK,QAAQ,SAAS,aAAa,GAAG;AACxC,aAAO,KAAK,QAAQ,SAAS,aAAa;AAAA,IAC5C;AAGA,UAAM,cAAc,GAAG,GAAG,IAAI,WAAW;AACzC,QAAI,KAAK,QAAQ,SAAS,WAAW,GAAG;AACtC,aAAO,KAAK,QAAQ,SAAS,WAAW;AAAA,IAC1C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,KAAa,QAAyB;AAC/C,QAAI,CAAC,KAAK,UAAU,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,KAAK,SAAS,KAAK,MAAM;AACtC,WAAO,SAAS,QAAQ,KAAK;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,KAAa,QAAuC;AAC1D,QAAI,CAAC,KAAK,WAAW,KAAK,MAAM,GAAG;AACjC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,SAAS,KAAK,MAAM;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,eAAoC;AAC7C,kBAAc,aAAa,QAAQ;AAAA,MACjC,OAAO,WAAuC;AAC5C,cAAM,MAAM,OAAO,OAAO;AAC1B,cAAM,SAAS,OAAO,QAAQ,YAAY,KAAK;AAE/C,cAAM,OAAO,KAAK,QAAQ,KAAK,MAAM;AAErC,YAAI,MAAM;AAER,eAAK,QAAQ,YAAY,KAAK,QAAQ,IAAI;AAG1C,UAAC,OAAkF,UAAU,YAAY;AACvG,kBAAM,EAAE,MAAM,QAAQ,GAAG,SAAS,IAAI,IAAI;AAG1C,gBAAI,QAAQ,GAAG;AACb,oBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,YAC3D;AAEA,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,cACA,YAAY;AAAA,cACZ,SAAS,CAAC;AAAA,cACV;AAAA,YACF;AAAA,UACF;AAAA,QACF,OAAO;AAEL,gBAAM,SAAS,CAAC,KAAK,UAAU,IAC3B,2BACA;AACJ,eAAK,QAAQ,WAAW,KAAK,QAAQ,MAAM;AAAA,QAC7C;AAEA,eAAO;AAAA,MACT;AAAA,MACA,CAAC,UAAmB,QAAQ,OAAO,KAAK;AAAA,IAC1C;AAAA,EACF;AACF;AAKO,SAAS,sBACd,SACiB;AACjB,SAAO,IAAI,gBAAgB,OAAO;AACpC;AAKA,eAAsB,eAAwD;AAC5E,MAAI;AAGF,UAAM,WAAW,MAAM,MAAM,iBAAiB;AAC9C,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ;AAAA,QACN;AAAA,QACA,SAAS;AAAA,MACX;AACA,aAAO,CAAC;AAAA,IACV;AACA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,KAAK,oDAAoD,KAAK;AACtE,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,8BAAwD;AACrE,QAAM,WAAW,MAAM,aAAa;AACpC,QAAM,YAAY,OAAO,yBAAyB,eAAe;AAEjE,SAAO,IAAI,gBAAgB;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,IACT,WAAW,CAAC,KAAa,WAAmB;AAC1C,cAAQ,IAAI,cAAc,MAAM,IAAI,GAAG,EAAE;AAAA,IAC3C;AAAA,IACA,UAAU,CAAC,KAAa,QAAgB,WAAmB;AACzD,cAAQ,IAAI,iBAAiB,MAAM,IAAI,GAAG,MAAM,MAAM,EAAE;AAAA,IAC1D;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,oBACpB,eACe;AACf,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,MAAM,4BAA4B;AACtD,cAAY,WAAW,aAAa;AACtC;AAEO,SAAS,eAAe,SAAwB;AACrD,SAAO,mBAAmB;AAC5B;AAEO,SAAS,gBAAyB;AACvC,SAAO,CAAC,CAAC,OAAO;AAClB;AAMA,IAAI;AAEG,SAAS,qBAAqB,MAA0B;AAC7D,oBAAkB;AACpB;AAEA,eAAsB,iCAAgD;AACpE,MAAI,CAAC,iBAAiB;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,MAAM,4BAA4B;AACtD,cAAY,WAAW,gBAAgB,YAAY,aAAa;AAClE;","names":[]}
|
|
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 AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\n\ndeclare const __AUTOMOCK_ENABLED__: boolean;\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 private normalizeUrlPath(url: string, baseURL?: string): string {\n if (!url) return \"\";\n\n try {\n const isAbsoluteUrl = /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(url);\n const browserOrigin =\n typeof window !== \"undefined\" && window.location?.origin\n ? window.location.origin\n : \"http://localhost\";\n\n if (isAbsoluteUrl) {\n return new URL(url).pathname;\n }\n\n if (baseURL) {\n const normalizedBase = baseURL.endsWith(\"/\") ? baseURL : `${baseURL}/`;\n const normalizedUrl = url.replace(/^\\/+/, \"\");\n return new URL(normalizedUrl, normalizedBase).pathname;\n }\n\n return new URL(url, browserOrigin).pathname;\n } catch {\n return url.split(/[?#]/)[0] || \"\";\n }\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, baseURL?: string): MockBundleData | null {\n const methodUpper = method.toUpperCase();\n const methodLower = method.toLowerCase();\n const normalizedPath = this.normalizeUrlPath(url, baseURL);\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 const normalizedHttpMethodKey = `${methodUpper} ${normalizedPath}`;\n if (this.options.mockData[normalizedHttpMethodKey]) {\n return this.options.mockData[normalizedHttpMethodKey];\n }\n\n // Try file path format from automock plugin: /api/v1/asset/xxx/get.js\n const filePathKey = `${normalizedPath}/${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, baseURL?: string): boolean {\n if (!this.isEnabled()) {\n return false;\n }\n\n const mock = this.findMock(url, method, baseURL);\n return mock !== null && mock.enable;\n }\n\n /**\n * Get mock data for request\n */\n getMock(url: string, method: string, baseURL?: string): MockBundleData | null {\n if (!this.shouldMock(url, method, baseURL)) {\n return null;\n }\n return this.findMock(url, method, baseURL);\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 const normalizedUrl = this.normalizeUrlPath(url, config.baseURL);\n\n const mock = this.getMock(url, method, config.baseURL);\n\n if (mock) {\n // Trigger mock hit callback\n this.options.onMockHit?.(normalizedUrl, method, mock);\n\n // Set adapter to return mock data\n (config as InternalAxiosRequestConfig & { adapter: () => Promise<AxiosResponse> }).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?.(normalizedUrl, 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\nasync function createInterceptorFromBundle(): Promise<MockInterceptor> {\n const mockData = await loadMockData();\n const isEnabled = typeof __AUTOMOCK_ENABLED__ !== \"undefined\" && __AUTOMOCK_ENABLED__;\n\n return new MockInterceptor({\n mockData,\n enabled: isEnabled,\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\nexport async function initMockInterceptor(\n axiosInstance?: AxiosInstance,\n): Promise<void> {\n if (!axiosInstance) {\n throw new Error(\n \"[MockInterceptor] axiosInstance is required. Please provide an axios instance.\",\n );\n }\n\n const interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(axiosInstance);\n}\n\nexport function setMockEnabled(enabled: boolean): void {\n window.__MOCK_ENABLED__ = enabled;\n}\n\nexport function isMockEnabled(): boolean {\n return !!window.__MOCK_ENABLED__;\n}\n\ntype HttpInstance = {\n constructor: { axiosInstance: AxiosInstance };\n};\n\nlet httpInstanceRef: HttpInstance | undefined;\n\nexport function registerHttpInstance(http: HttpInstance): void {\n httpInstanceRef = http;\n}\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 interceptor = await createInterceptorFromBundle();\n interceptor.setupAxios(httpInstanceRef.constructor.axiosInstance);\n}\n"],"mappings":"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","names":[]}
|
package/dist/client/index.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -7,17 +7,22 @@ interface InspectorOptions {
|
|
|
7
7
|
enableToggle?: boolean;
|
|
8
8
|
}
|
|
9
9
|
interface AutomockPluginOptions {
|
|
10
|
+
/** Directory for mock files, default "mock". */
|
|
10
11
|
mockDir?: string;
|
|
12
|
+
/** API path prefix handled by automock, default "/api". */
|
|
11
13
|
apiPrefix?: string;
|
|
14
|
+
/** Rewrite request path before proxying to proxyBaseUrl. */
|
|
12
15
|
pathRewrite?: (path: string) => string;
|
|
16
|
+
/** Real backend base URL. Required only when automock proxy/capture is used. */
|
|
13
17
|
proxyBaseUrl?: string;
|
|
14
18
|
inspector?: boolean | InspectorOptions;
|
|
19
|
+
/** Client-side production mock switch. Default false; "auto" enables outside production mode. */
|
|
15
20
|
productionMock?: boolean | 'auto';
|
|
16
|
-
/** Global switch, default true. When false, middleware passes through all requests. */
|
|
21
|
+
/** Global switch, default true. When false, middleware passes through all requests without proxy/capture. */
|
|
17
22
|
enabled?: boolean;
|
|
18
23
|
/** Whether to proxy unmatched requests to proxyBaseUrl, default true. */
|
|
19
24
|
proxy?: boolean;
|
|
20
|
-
/** Whether to capture and save
|
|
25
|
+
/** Whether to capture and save JSON proxy responses as mock files, default true. */
|
|
21
26
|
capture?: boolean;
|
|
22
27
|
}
|
|
23
28
|
|
package/dist/index.d.ts
CHANGED
|
@@ -7,17 +7,22 @@ interface InspectorOptions {
|
|
|
7
7
|
enableToggle?: boolean;
|
|
8
8
|
}
|
|
9
9
|
interface AutomockPluginOptions {
|
|
10
|
+
/** Directory for mock files, default "mock". */
|
|
10
11
|
mockDir?: string;
|
|
12
|
+
/** API path prefix handled by automock, default "/api". */
|
|
11
13
|
apiPrefix?: string;
|
|
14
|
+
/** Rewrite request path before proxying to proxyBaseUrl. */
|
|
12
15
|
pathRewrite?: (path: string) => string;
|
|
16
|
+
/** Real backend base URL. Required only when automock proxy/capture is used. */
|
|
13
17
|
proxyBaseUrl?: string;
|
|
14
18
|
inspector?: boolean | InspectorOptions;
|
|
19
|
+
/** Client-side production mock switch. Default false; "auto" enables outside production mode. */
|
|
15
20
|
productionMock?: boolean | 'auto';
|
|
16
|
-
/** Global switch, default true. When false, middleware passes through all requests. */
|
|
21
|
+
/** Global switch, default true. When false, middleware passes through all requests without proxy/capture. */
|
|
17
22
|
enabled?: boolean;
|
|
18
23
|
/** Whether to proxy unmatched requests to proxyBaseUrl, default true. */
|
|
19
24
|
proxy?: boolean;
|
|
20
|
-
/** Whether to capture and save
|
|
25
|
+
/** Whether to capture and save JSON proxy responses as mock files, default true. */
|
|
21
26
|
capture?: boolean;
|
|
22
27
|
}
|
|
23
28
|
|