vite-plugin-automock 1.1.5 → 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.
@@ -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 filePathKey = `${url}/${methodLower}.js`;
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 mock = this.getMock(url, method);
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?.(url, method, mock);
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?.(url, method, reason);
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-DJWYFFPU.mjs.map
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":"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","names":[]}
@@ -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
  */
@@ -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
  */
@@ -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 filePathKey = `${url}/${methodLower}.js`;
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 mock = this.getMock(url, method);
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?.(url, method, mock);
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?.(url, method, reason);
139
+ this.options.onBypass?.(normalizedUrl, method, reason);
116
140
  }
117
141
  return config;
118
142
  },
@@ -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":"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","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":[]}
@@ -6,7 +6,7 @@ import {
6
6
  loadMockData,
7
7
  registerHttpInstance,
8
8
  setMockEnabled
9
- } from "../chunk-DJWYFFPU.mjs";
9
+ } from "../chunk-2KRAWI7J.mjs";
10
10
  export {
11
11
  createMockInterceptor,
12
12
  initMockInterceptor,
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 new mock files from proxy responses, default true. */
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 new mock files from proxy responses, default true. */
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.js CHANGED
@@ -759,6 +759,11 @@ function serveMockResponse(res, mockFilePath, mockResult) {
759
759
  }
760
760
  }, delay);
761
761
  }
762
+ function matchesApiPrefix(pathname, apiPrefix) {
763
+ if (!apiPrefix || apiPrefix === "/") return true;
764
+ const normalizedPrefix = apiPrefix.endsWith("/") && apiPrefix !== "/" ? apiPrefix.slice(0, -1) : apiPrefix;
765
+ return pathname === normalizedPrefix || pathname.startsWith(`${normalizedPrefix}/`);
766
+ }
762
767
  function proxyAndCapture(req, res, options) {
763
768
  const { proxyBaseUrl, pathRewrite, shouldSave, method, pathname, mockDir, onMockSaved } = options;
764
769
  const targetUrl = proxyBaseUrl + pathRewrite(req.url || "");
@@ -934,7 +939,8 @@ function automock(options) {
934
939
  const absolutePath = resolveAbsolutePath(mockFilePath);
935
940
  if (!import_fs_extra3.default.existsSync(absolutePath)) return null;
936
941
  const { default: mockModule } = await import(`${absolutePath}?t=${Date.now()}`);
937
- const mockResult = typeof mockModule.data === "function" ? mockModule.data() : mockModule;
942
+ const exportedConfig = typeof mockModule === "function" ? await mockModule() : mockModule;
943
+ const mockResult = typeof exportedConfig?.data === "function" ? { ...exportedConfig, data: await exportedConfig.data() } : exportedConfig;
938
944
  const { enable = true } = mockResult || {};
939
945
  return enable ? { absolutePath, mockResult } : null;
940
946
  }
@@ -945,7 +951,11 @@ function automock(options) {
945
951
  const handled = await inspectorHandler(req, res);
946
952
  if (handled) return;
947
953
  }
948
- if (!req.url?.startsWith(apiPrefix)) {
954
+ if (!req.url) {
955
+ return next();
956
+ }
957
+ const pathname = new URL(req.url, "http://localhost").pathname;
958
+ if (!matchesApiPrefix(pathname, apiPrefix)) {
949
959
  return next();
950
960
  }
951
961
  const accept = req.headers.accept || "";
@@ -954,7 +964,6 @@ function automock(options) {
954
964
  return next();
955
965
  }
956
966
  const method = (req.method || "GET").toLowerCase();
957
- const pathname = new URL(req.url, "http://localhost").pathname;
958
967
  const key = `${pathname}/${method}.js`.toLowerCase();
959
968
  try {
960
969
  const mock = await tryLoadMock(key);
@@ -1061,6 +1070,24 @@ var MockInterceptor = class {
1061
1070
  constructor(options) {
1062
1071
  this.options = options;
1063
1072
  }
1073
+ normalizeUrlPath(url, baseURL) {
1074
+ if (!url) return "";
1075
+ try {
1076
+ const isAbsoluteUrl = /^[a-z][a-z\d+\-.]*:\/\//i.test(url);
1077
+ const browserOrigin = typeof window !== "undefined" && window.location?.origin ? window.location.origin : "http://localhost";
1078
+ if (isAbsoluteUrl) {
1079
+ return new URL(url).pathname;
1080
+ }
1081
+ if (baseURL) {
1082
+ const normalizedBase = baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
1083
+ const normalizedUrl = url.replace(/^\/+/, "");
1084
+ return new URL(normalizedUrl, normalizedBase).pathname;
1085
+ }
1086
+ return new URL(url, browserOrigin).pathname;
1087
+ } catch {
1088
+ return url.split(/[?#]/)[0] || "";
1089
+ }
1090
+ }
1064
1091
  /**
1065
1092
  * Check if mock is enabled
1066
1093
  */
@@ -1079,14 +1106,19 @@ var MockInterceptor = class {
1079
1106
  * - "GET /api/v1/asset/xxx" (HTTP method + URL)
1080
1107
  * - "/api/v1/asset/xxx/get.js" (File path format from automock plugin)
1081
1108
  */
1082
- findMock(url, method) {
1109
+ findMock(url, method, baseURL) {
1083
1110
  const methodUpper = method.toUpperCase();
1084
1111
  const methodLower = method.toLowerCase();
1112
+ const normalizedPath = this.normalizeUrlPath(url, baseURL);
1085
1113
  const httpMethodKey = `${methodUpper} ${url}`;
1086
1114
  if (this.options.mockData[httpMethodKey]) {
1087
1115
  return this.options.mockData[httpMethodKey];
1088
1116
  }
1089
- const filePathKey = `${url}/${methodLower}.js`;
1117
+ const normalizedHttpMethodKey = `${methodUpper} ${normalizedPath}`;
1118
+ if (this.options.mockData[normalizedHttpMethodKey]) {
1119
+ return this.options.mockData[normalizedHttpMethodKey];
1120
+ }
1121
+ const filePathKey = `${normalizedPath}/${methodLower}.js`;
1090
1122
  if (this.options.mockData[filePathKey]) {
1091
1123
  return this.options.mockData[filePathKey];
1092
1124
  }
@@ -1095,21 +1127,21 @@ var MockInterceptor = class {
1095
1127
  /**
1096
1128
  * Determine if request should be mocked
1097
1129
  */
1098
- shouldMock(url, method) {
1130
+ shouldMock(url, method, baseURL) {
1099
1131
  if (!this.isEnabled()) {
1100
1132
  return false;
1101
1133
  }
1102
- const mock = this.findMock(url, method);
1134
+ const mock = this.findMock(url, method, baseURL);
1103
1135
  return mock !== null && mock.enable;
1104
1136
  }
1105
1137
  /**
1106
1138
  * Get mock data for request
1107
1139
  */
1108
- getMock(url, method) {
1109
- if (!this.shouldMock(url, method)) {
1140
+ getMock(url, method, baseURL) {
1141
+ if (!this.shouldMock(url, method, baseURL)) {
1110
1142
  return null;
1111
1143
  }
1112
- return this.findMock(url, method);
1144
+ return this.findMock(url, method, baseURL);
1113
1145
  }
1114
1146
  /**
1115
1147
  * Setup axios interceptor
@@ -1119,9 +1151,10 @@ var MockInterceptor = class {
1119
1151
  async (config) => {
1120
1152
  const url = config.url || "";
1121
1153
  const method = config.method?.toUpperCase() || "GET";
1122
- const mock = this.getMock(url, method);
1154
+ const normalizedUrl = this.normalizeUrlPath(url, config.baseURL);
1155
+ const mock = this.getMock(url, method, config.baseURL);
1123
1156
  if (mock) {
1124
- this.options.onMockHit?.(url, method, mock);
1157
+ this.options.onMockHit?.(normalizedUrl, method, mock);
1125
1158
  config.adapter = async () => {
1126
1159
  const { data, delay = 0, status = 200 } = mock;
1127
1160
  if (delay > 0) {
@@ -1137,7 +1170,7 @@ var MockInterceptor = class {
1137
1170
  };
1138
1171
  } else {
1139
1172
  const reason = !this.isEnabled() ? "Mock disabled globally" : "No matching mock found or mock disabled";
1140
- this.options.onBypass?.(url, method, reason);
1173
+ this.options.onBypass?.(normalizedUrl, method, reason);
1141
1174
  }
1142
1175
  return config;
1143
1176
  },
@@ -1209,6 +1242,11 @@ async function initMockInterceptorForPureHttp() {
1209
1242
  }
1210
1243
 
1211
1244
  // src/index.ts
1245
+ function resolveProductionMockEnabled(productionMock, mode = process.env.NODE_ENV) {
1246
+ if (productionMock === true) return true;
1247
+ if (productionMock === "auto") return mode !== "production";
1248
+ return false;
1249
+ }
1212
1250
  function automock2(options = {}) {
1213
1251
  const {
1214
1252
  bundleMockData = true,
@@ -1217,8 +1255,10 @@ function automock2(options = {}) {
1217
1255
  } = options;
1218
1256
  const basePlugin = automock(pluginOptions);
1219
1257
  let cachedBundle = null;
1258
+ let productionMockEnabled = resolveProductionMockEnabled(pluginOptions.productionMock);
1259
+ const resolveBundleOutputPath = () => import_path6.default.isAbsolute(bundleOutputPath) ? bundleOutputPath : import_path6.default.join(process.cwd(), bundleOutputPath);
1220
1260
  const ensureBundleExists = () => {
1221
- const outputPath = import_path6.default.join(process.cwd(), bundleOutputPath);
1261
+ const outputPath = resolveBundleOutputPath();
1222
1262
  if (!import_fs_extra5.default.existsSync(outputPath) && cachedBundle) {
1223
1263
  console.log("[automock] Re-writing mock bundle...");
1224
1264
  writeMockBundle(cachedBundle, outputPath);
@@ -1227,16 +1267,22 @@ function automock2(options = {}) {
1227
1267
  return {
1228
1268
  ...basePlugin,
1229
1269
  name: "vite-plugin-automock-with-bundle",
1230
- config() {
1231
- const enabled = pluginOptions.productionMock !== false;
1270
+ config(userConfig, env) {
1271
+ if (typeof basePlugin.config === "function") {
1272
+ basePlugin.config.call(this, userConfig, env);
1273
+ }
1274
+ productionMockEnabled = resolveProductionMockEnabled(
1275
+ pluginOptions.productionMock,
1276
+ env.mode
1277
+ );
1232
1278
  return {
1233
1279
  define: {
1234
- __AUTOMOCK_ENABLED__: JSON.stringify(enabled)
1280
+ __AUTOMOCK_ENABLED__: JSON.stringify(productionMockEnabled)
1235
1281
  }
1236
1282
  };
1237
1283
  },
1238
1284
  buildEnd: async () => {
1239
- if (bundleMockData && pluginOptions.productionMock !== false) {
1285
+ if (bundleMockData && productionMockEnabled) {
1240
1286
  try {
1241
1287
  const mockDir = pluginOptions.mockDir || import_path6.default.join(process.cwd(), "mock");
1242
1288
  if (!import_fs_extra5.default.existsSync(mockDir)) {
@@ -1250,7 +1296,7 @@ function automock2(options = {}) {
1250
1296
  return;
1251
1297
  }
1252
1298
  cachedBundle = await bundleMockFiles({ mockDir });
1253
- const outputPath = import_path6.default.join(process.cwd(), bundleOutputPath);
1299
+ const outputPath = resolveBundleOutputPath();
1254
1300
  writeMockBundle(cachedBundle, outputPath);
1255
1301
  console.log(`[automock] Mock bundle written to: ${outputPath}`);
1256
1302
  } catch (error) {