vite-plugin-ai-mock 0.1.4 → 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/README.md +196 -39
- package/README.zh-CN.md +198 -38
- package/dist/index.cjs +180 -127
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -2
- package/dist/index.d.ts +36 -2
- package/dist/index.js +180 -127
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
[](https://github.com/quanzhiyuan/vite-plugin-ai-mock/actions)
|
|
9
9
|
[](./LICENSE)
|
|
10
10
|
|
|
11
|
-
A standalone Vite plugin for AI scene mocking.
|
|
11
|
+
A standalone Vite plugin for AI scene mocking. Supports both JSON and TypeScript mock files, returning SSE streaming or JSON responses to simulate various AI scenarios.
|
|
12
12
|
|
|
13
|
-
- Reads mock files from `mock
|
|
13
|
+
- Reads mock files from `mock/*.json` or `mock/*.ts`
|
|
14
14
|
- Returns SSE streaming response by default
|
|
15
15
|
- Use `?transport=json` to get JSON format response
|
|
16
16
|
- Supports 11 streaming scenarios with request parameters
|
|
17
|
+
- TypeScript mock files support dynamic data and full middleware control — **hot-reloaded without server restart**
|
|
17
18
|
|
|
18
19
|
## Install
|
|
19
20
|
|
|
@@ -33,19 +34,20 @@ pnpm add vite-plugin-ai-mock -D
|
|
|
33
34
|
project/
|
|
34
35
|
├── mock/
|
|
35
36
|
│ └── ai/
|
|
36
|
-
│ ├── chat.json
|
|
37
|
+
│ ├── chat.json ← JSON mock
|
|
38
|
+
│ ├── sessions.ts ← TypeScript mock
|
|
37
39
|
│ └── default.json
|
|
38
40
|
├── src/
|
|
39
41
|
└── vite.config.ts
|
|
42
|
+
```
|
|
40
43
|
|
|
44
|
+
**Request Example**
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
```ts
|
|
47
|
+
// SSE streaming (default)
|
|
48
|
+
const res = await fetch("/api/chat");
|
|
49
|
+
// JSON response
|
|
50
|
+
const json = await fetch("/api/chat?transport=json");
|
|
49
51
|
```
|
|
50
52
|
|
|
51
53
|
</td>
|
|
@@ -60,14 +62,14 @@ import { aiMockPlugin } from "vite-plugin-ai-mock";
|
|
|
60
62
|
export default defineConfig({
|
|
61
63
|
plugins: [
|
|
62
64
|
aiMockPlugin({
|
|
63
|
-
dataDir: "mock
|
|
64
|
-
endpoint: "/api
|
|
65
|
+
dataDir: "mock",
|
|
66
|
+
endpoint: "/api", // /api/chat → chat.json
|
|
65
67
|
}),
|
|
66
68
|
],
|
|
67
69
|
});
|
|
68
70
|
```
|
|
69
71
|
|
|
70
|
-
**mock/
|
|
72
|
+
**mock/chat.json**
|
|
71
73
|
|
|
72
74
|
```json
|
|
73
75
|
{
|
|
@@ -86,6 +88,75 @@ export default defineConfig({
|
|
|
86
88
|
|
|
87
89
|
> 💡 See full examples: [examples](https://github.com/quanzhiyuan/vite-plugin-ai-mock/tree/main/examples) (includes Ant Design X, Assistant UI, Lobe Chat integrations)
|
|
88
90
|
|
|
91
|
+
## TypeScript Mock Files
|
|
92
|
+
|
|
93
|
+
In addition to `.json` files, mock files can be written in TypeScript. `.ts` takes priority over `.json` when both exist.
|
|
94
|
+
|
|
95
|
+
Three export patterns are supported:
|
|
96
|
+
|
|
97
|
+
### Pattern 1 — Static default export (same as JSON)
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// mock/ai/chat.ts
|
|
101
|
+
export default {
|
|
102
|
+
chunks: [
|
|
103
|
+
{ id: "1", data: { delta: "Hello" } },
|
|
104
|
+
{ id: "2", data: { delta: " World" } },
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Pattern 2 — Factory function (called per request)
|
|
110
|
+
|
|
111
|
+
The function receives the incoming request and can return dynamic data. Supports `async`.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// mock/ai/chat.ts
|
|
115
|
+
import type { Connect } from "vite";
|
|
116
|
+
|
|
117
|
+
export default (req: Connect.IncomingMessage) => {
|
|
118
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
119
|
+
const lang = url.searchParams.get("lang") ?? "en";
|
|
120
|
+
return {
|
|
121
|
+
chunks: [
|
|
122
|
+
{ id: "1", data: { delta: lang === "zh" ? "你好" : "Hello" } },
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Pattern 3 — Handler (full middleware control)
|
|
129
|
+
|
|
130
|
+
Replaces the need to write a separate `configureServer` plugin. Receives `(req, res, next)` and handles the response directly — ideal for dynamic routes like session create/delete.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// mock/ai/session.ts
|
|
134
|
+
import type { MockRequestHandler } from "vite-plugin-ai-mock";
|
|
135
|
+
|
|
136
|
+
const sessions = new Set<string>();
|
|
137
|
+
|
|
138
|
+
export const handler: MockRequestHandler = (req, res) => {
|
|
139
|
+
if (req.method === "POST") {
|
|
140
|
+
const id = `sess-${Date.now()}`;
|
|
141
|
+
sessions.add(id);
|
|
142
|
+
res.setHeader("Content-Type", "application/json");
|
|
143
|
+
res.end(JSON.stringify({ status: "success", data: { session_id: id } }));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (req.method === "DELETE") {
|
|
147
|
+
const id = (req.url ?? "").split("/").pop() ?? "";
|
|
148
|
+
sessions.delete(id);
|
|
149
|
+
res.setHeader("Content-Type", "application/json");
|
|
150
|
+
res.end(JSON.stringify({ status: "success" }));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
res.statusCode = 405;
|
|
154
|
+
res.end();
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Hot reload**: TypeScript mock files are loaded via Vite's `ssrLoadModule`. Any edit is picked up on the next request — **no server restart required**.
|
|
159
|
+
|
|
89
160
|
## Scenarios (11)
|
|
90
161
|
|
|
91
162
|
1. Normal completion (default)
|
|
@@ -114,20 +185,20 @@ Scenarios can be configured in two ways, in order of precedence:
|
|
|
114
185
|
Append parameters directly to the request URL, useful for debugging a single endpoint:
|
|
115
186
|
|
|
116
187
|
```
|
|
117
|
-
/api/
|
|
118
|
-
/api/
|
|
188
|
+
/api/default?scenario=jitter
|
|
189
|
+
/api/default?firstChunkDelayMs=1000&errorAt=3
|
|
119
190
|
```
|
|
120
191
|
|
|
121
192
|
```ts
|
|
122
193
|
// Default returns SSE streaming response
|
|
123
|
-
const response = await fetch("/api/
|
|
194
|
+
const response = await fetch("/api/default?firstChunkDelayMs=4800", {
|
|
124
195
|
method: "POST",
|
|
125
196
|
headers: { "Content-Type": "application/json" },
|
|
126
197
|
body: JSON.stringify({}),
|
|
127
198
|
});
|
|
128
199
|
|
|
129
200
|
// Use ?transport=json to get JSON format
|
|
130
|
-
const jsonResponse = await fetch("/api/
|
|
201
|
+
const jsonResponse = await fetch("/api/default?transport=json");
|
|
131
202
|
```
|
|
132
203
|
|
|
133
204
|
**2. Plugin option `defaultScenario` (global)**
|
|
@@ -150,9 +221,26 @@ aiMockPlugin({
|
|
|
150
221
|
|
|
151
222
|
When `defaultScenario` is not set, the `normal` scenario is used (no delay, completes normally).
|
|
152
223
|
|
|
224
|
+
**3. Plugin option `jsonApis` (specify JSON-returning APIs)**
|
|
225
|
+
|
|
226
|
+
Configure in `vite.config.ts` to specify which API paths should return JSON format instead of SSE:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
aiMockPlugin({
|
|
230
|
+
endpoint: "/api",
|
|
231
|
+
jsonApis: [
|
|
232
|
+
"/api/config", // exact match
|
|
233
|
+
"/api/history", // exact match
|
|
234
|
+
/^\/api\/static\/.*/, // regex match
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Precedence: `?transport=json` / `?transport=sse` > `jsonApis` config > default SSE
|
|
240
|
+
|
|
153
241
|
## Mock file format
|
|
154
242
|
|
|
155
|
-
Each file is a JSON object with a `chunks` array. Every chunk maps to one SSE event:
|
|
243
|
+
Each mock file is a JSON object (or TypeScript module) with a `chunks` array. Every chunk maps to one SSE event:
|
|
156
244
|
|
|
157
245
|
| Field | Type | Description |
|
|
158
246
|
| --------- | ------ | --------------------------------------------------------------- |
|
|
@@ -175,18 +263,18 @@ Each file is a JSON object with a `chunks` array. Every chunk maps to one SSE ev
|
|
|
175
263
|
|
|
176
264
|
### Real-world format examples
|
|
177
265
|
|
|
178
|
-
The `data` field can mirror any real API response. The package ships with ready-to-use examples in `mock
|
|
266
|
+
The `data` field can mirror any real API response. The package ships with ready-to-use examples in `mock/` — copy them into your project as a starting point:
|
|
179
267
|
|
|
180
|
-
| File
|
|
181
|
-
|
|
|
182
|
-
| `mock/
|
|
183
|
-
| `mock/
|
|
184
|
-
| `mock/
|
|
185
|
-
| `mock/
|
|
186
|
-
| `mock/
|
|
187
|
-
| `mock/
|
|
188
|
-
| `mock/
|
|
189
|
-
| `mock/
|
|
268
|
+
| File | Provider |
|
|
269
|
+
| --------------------------- | ------------------- |
|
|
270
|
+
| `mock/openai.json` | OpenAI / compatible |
|
|
271
|
+
| `mock/claude.json` | Anthropic Claude |
|
|
272
|
+
| `mock/gemini.json` | Google Gemini |
|
|
273
|
+
| `mock/deepseek.json` | DeepSeek |
|
|
274
|
+
| `mock/deepseek-reasoner.json` | DeepSeek Reasoner |
|
|
275
|
+
| `mock/qwen.json` | Qwen (Alibaba) |
|
|
276
|
+
| `mock/qwen-thinking.json` | Qwen Thinking |
|
|
277
|
+
| `mock/doubao.json` | Doubao (ByteDance) |
|
|
190
278
|
|
|
191
279
|
**OpenAI / compatible** (`openai.json`) — `data` ends with `"[DONE]"` string:
|
|
192
280
|
|
|
@@ -278,7 +366,7 @@ import { DefaultChatTransport } from "ai";
|
|
|
278
366
|
|
|
279
367
|
const { messages, sendMessage, status } = useChat({
|
|
280
368
|
transport: new DefaultChatTransport({
|
|
281
|
-
api: "/api/
|
|
369
|
+
api: "/api/chat",
|
|
282
370
|
}),
|
|
283
371
|
});
|
|
284
372
|
```
|
|
@@ -295,10 +383,10 @@ const { messages, sendMessage, status } = useChat({
|
|
|
295
383
|
|
|
296
384
|
```ts
|
|
297
385
|
// string (default)
|
|
298
|
-
endpoint: "/api
|
|
299
|
-
// /api
|
|
300
|
-
// /api/
|
|
301
|
-
// /api/
|
|
386
|
+
endpoint: "/api";
|
|
387
|
+
// /api → file = "default"
|
|
388
|
+
// /api/chat → file = "chat"
|
|
389
|
+
// /api/i18n/zh-CN → file = "i18n/zh-CN" (nested directory)
|
|
302
390
|
|
|
303
391
|
// RegExp
|
|
304
392
|
endpoint: /^\/api\/ai\/.*/;
|
|
@@ -308,11 +396,11 @@ endpoint: /^\/api\/ai\/.*/;
|
|
|
308
396
|
endpoint: ["/api/chat", /^\/v2\/ai\/.*/];
|
|
309
397
|
```
|
|
310
398
|
|
|
311
|
-
Nested directories are supported. For example, `/api/
|
|
399
|
+
Nested directories are supported. For example, `/api/i18n/zh-CN` maps to `mock/i18n/zh-CN.json`.
|
|
312
400
|
|
|
313
|
-
- `/api
|
|
314
|
-
- `/api
|
|
315
|
-
- `/api
|
|
401
|
+
- `/api`
|
|
402
|
+
- `/api/<file>`
|
|
403
|
+
- `/api/<dir>/<file>` (nested)
|
|
316
404
|
- `?file=<file>` or `?file=<dir>/<file>`
|
|
317
405
|
|
|
318
406
|
## Test
|
|
@@ -333,4 +421,73 @@ pnpm build
|
|
|
333
421
|
pnpm release:npm
|
|
334
422
|
```
|
|
335
423
|
|
|
336
|
-
`prepublishOnly` will automatically run build, tests and typecheck
|
|
424
|
+
`prepublishOnly` will automatically run build, tests and typecheck.
|
|
425
|
+
|
|
426
|
+
## Configuration Options
|
|
427
|
+
|
|
428
|
+
### Plugin Options `AiMockPluginOptions`
|
|
429
|
+
|
|
430
|
+
| Option | Type | Default | Description |
|
|
431
|
+
| ----------------- | ------------------------------------------ | ---------------- | ----------------------------------------------------------------------------------------- |
|
|
432
|
+
| `dataDir` | `string` | `"mock"` | Directory for mock files (`.json` or `.ts`), relative to project root |
|
|
433
|
+
| `endpoint` | `string \| RegExp \| (string \| RegExp)[]` | `"/api"` | API path to intercept, supports string, RegExp, or array |
|
|
434
|
+
| `defaultScenario` | `DefaultScenarioConfig` | `undefined` | Global default scenario config, can be overridden by URL params |
|
|
435
|
+
| `jsonApis` | `(string \| RegExp)[]` | `undefined` | List of API paths that should return JSON format (applies to both `.json` and `.ts` files)|
|
|
436
|
+
|
|
437
|
+
### Scenario Config `DefaultScenarioConfig`
|
|
438
|
+
|
|
439
|
+
| Option | Type | Default | Description |
|
|
440
|
+
| ------------------- | -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
441
|
+
| `scenario` | `ScenarioName` | `undefined` | Preset scenario name: `normal`, `first-delay`, `jitter`, `disconnect`, `timeout`, `error`, `malformed`, `duplicate`, `out-of-order`, `reconnect`, `heartbeat` |
|
|
442
|
+
| `firstChunkDelayMs` | `number` | `0` | Delay before sending first chunk (ms) |
|
|
443
|
+
| `minIntervalMs` | `number` | `200` | Minimum interval between chunks (ms) |
|
|
444
|
+
| `maxIntervalMs` | `number` | `700` | Maximum interval between chunks (ms) |
|
|
445
|
+
| `disconnectAt` | `number` | `-1` | Disconnect at chunk N (-1 to disable) |
|
|
446
|
+
| `stallAfter` | `number` | `-1` | Stop sending after chunk N (-1 to disable) |
|
|
447
|
+
| `stallMs` | `number` | `30000` | Wait time after stalling (ms) |
|
|
448
|
+
| `errorAt` | `number` | `-1` | Send error event at chunk N (-1 to disable) |
|
|
449
|
+
| `errorMessage` | `string` | `"mock_error"` | Error event message content |
|
|
450
|
+
| `malformedAt` | `number` | `-1` | Send malformed data at chunk N (-1 to disable) |
|
|
451
|
+
| `duplicateAt` | `number` | `-1` | Send duplicate chunk at N (-1 to disable) |
|
|
452
|
+
| `outOfOrder` | `boolean` | `false` | Shuffle chunk order (swaps chunks 2 and 3) |
|
|
453
|
+
| `heartbeatMs` | `number` | `0` | Heartbeat interval (ms), 0 to disable |
|
|
454
|
+
| `reconnect` | `boolean` | `false` | Enable reconnect mode, use with `lastEventId` |
|
|
455
|
+
|
|
456
|
+
### URL Parameters
|
|
457
|
+
|
|
458
|
+
In addition to scenario config params, the following URL-only params are supported:
|
|
459
|
+
|
|
460
|
+
| Param | Type | Default | Description |
|
|
461
|
+
| ----------------- | ------------------- | ----------- | ------------------------------------------------------------ |
|
|
462
|
+
| `file` | `string` | `"default"` | Mock file name (without `.json` extension) |
|
|
463
|
+
| `transport` | `"sse" \| "json"` | `"sse"` | Response format: `sse` for streaming, `json` for direct JSON |
|
|
464
|
+
| `httpErrorStatus` | `number` | `0` | Return specified HTTP error status (e.g., 401, 500) |
|
|
465
|
+
| `includeDone` | `"true" \| "false"` | `"true"` | Whether to send `done` event at stream end |
|
|
466
|
+
| `lastEventId` | `string` | `undefined` | Last event ID for reconnection, resumes after this ID |
|
|
467
|
+
|
|
468
|
+
### Full Configuration Example
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
import { defineConfig } from "vite";
|
|
472
|
+
import { aiMockPlugin } from "vite-plugin-ai-mock";
|
|
473
|
+
|
|
474
|
+
export default defineConfig({
|
|
475
|
+
plugins: [
|
|
476
|
+
aiMockPlugin({
|
|
477
|
+
// Mock file directory
|
|
478
|
+
dataDir: "mock",
|
|
479
|
+
// API path to intercept
|
|
480
|
+
endpoint: "/api",
|
|
481
|
+
// Global default scenario
|
|
482
|
+
defaultScenario: {
|
|
483
|
+
scenario: "jitter",
|
|
484
|
+
firstChunkDelayMs: 500,
|
|
485
|
+
minIntervalMs: 100,
|
|
486
|
+
maxIntervalMs: 800,
|
|
487
|
+
},
|
|
488
|
+
// APIs that return JSON format
|
|
489
|
+
jsonApis: ["/api/config", /^\/api\/static\/.*/],
|
|
490
|
+
}),
|
|
491
|
+
],
|
|
492
|
+
});
|
|
493
|
+
```
|
package/README.zh-CN.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# vite-plugin-ai-mock
|
|
2
2
|
|
|
3
|
-
一个用于 AI 场景模拟的独立 Vite
|
|
3
|
+
一个用于 AI 场景模拟的独立 Vite 插件。支持 JSON 和 TypeScript 两种 mock 文件格式,返回 SSE 流式或 JSON 响应,模拟不同的 AI 场景。
|
|
4
4
|
|
|
5
5
|
> [English](./README.md) | 中文
|
|
6
6
|
|
|
7
|
-
- 从 `mock
|
|
7
|
+
- 从 `mock/*.json` 或 `mock/*.ts` 读取 mock 文件
|
|
8
8
|
- 默认返回 SSE 流式响应
|
|
9
9
|
- 使用 `?transport=json` 获取 JSON 格式响应
|
|
10
10
|
- 支持 11 种流式场景,通过请求参数控制
|
|
11
|
+
- TypeScript mock 文件支持动态数据和完整中间件控制——**修改后无需重启服务**
|
|
11
12
|
|
|
12
13
|
## 安装
|
|
13
14
|
|
|
@@ -27,19 +28,20 @@ pnpm add vite-plugin-ai-mock -D
|
|
|
27
28
|
project/
|
|
28
29
|
├── mock/
|
|
29
30
|
│ └── ai/
|
|
30
|
-
│ ├── chat.json
|
|
31
|
+
│ ├── chat.json ← JSON mock
|
|
32
|
+
│ ├── sessions.ts ← TypeScript mock
|
|
31
33
|
│ └── default.json
|
|
32
34
|
├── src/
|
|
33
35
|
└── vite.config.ts
|
|
36
|
+
```
|
|
34
37
|
|
|
38
|
+
**请求示例**
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
```ts
|
|
41
|
+
// SSE 流式响应(默认)
|
|
42
|
+
const res = await fetch("/api/chat");
|
|
43
|
+
// JSON 响应
|
|
44
|
+
const json = await fetch("/api/chat?transport=json");
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
</td>
|
|
@@ -54,14 +56,14 @@ import { aiMockPlugin } from "vite-plugin-ai-mock";
|
|
|
54
56
|
export default defineConfig({
|
|
55
57
|
plugins: [
|
|
56
58
|
aiMockPlugin({
|
|
57
|
-
dataDir: "mock
|
|
58
|
-
endpoint: "/api
|
|
59
|
+
dataDir: "mock",
|
|
60
|
+
endpoint: "/api", // /api/chat → chat.json
|
|
59
61
|
}),
|
|
60
62
|
],
|
|
61
63
|
});
|
|
62
64
|
```
|
|
63
65
|
|
|
64
|
-
**mock/
|
|
66
|
+
**mock/chat.json**
|
|
65
67
|
|
|
66
68
|
```json
|
|
67
69
|
{
|
|
@@ -80,6 +82,75 @@ export default defineConfig({
|
|
|
80
82
|
|
|
81
83
|
> 💡 查看完整示例:[examples](https://github.com/quanzhiyuan/vite-plugin-ai-mock/tree/main/examples)(包含 Ant Design X、Assistant UI、Lobe Chat 等集成示例)
|
|
82
84
|
|
|
85
|
+
## TypeScript Mock 文件
|
|
86
|
+
|
|
87
|
+
除 `.json` 外,mock 文件可以用 TypeScript 编写。当同名 `.ts` 和 `.json` 文件同时存在时,`.ts` 优先。
|
|
88
|
+
|
|
89
|
+
支持三种 export 模式:
|
|
90
|
+
|
|
91
|
+
### 模式一 — 静态 default export(等同于 JSON)
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// mock/ai/chat.ts
|
|
95
|
+
export default {
|
|
96
|
+
chunks: [
|
|
97
|
+
{ id: "1", data: { delta: "你好" } },
|
|
98
|
+
{ id: "2", data: { delta: "世界" } },
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 模式二 — 工厂函数(每次请求都会调用)
|
|
104
|
+
|
|
105
|
+
函数接收请求对象,可返回动态数据。支持 `async`。
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// mock/ai/chat.ts
|
|
109
|
+
import type { Connect } from "vite";
|
|
110
|
+
|
|
111
|
+
export default (req: Connect.IncomingMessage) => {
|
|
112
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
113
|
+
const lang = url.searchParams.get("lang") ?? "en";
|
|
114
|
+
return {
|
|
115
|
+
chunks: [
|
|
116
|
+
{ id: "1", data: { delta: lang === "zh" ? "你好" : "Hello" } },
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 模式三 — Handler(完全控制 req/res)
|
|
123
|
+
|
|
124
|
+
替代在 `vite.config.ts` 中单独编写 `configureServer` 插件。接收 `(req, res, next)`,直接控制响应——适用于会话创建/删除等动态路由。
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// mock/ai/session.ts
|
|
128
|
+
import type { MockRequestHandler } from "vite-plugin-ai-mock";
|
|
129
|
+
|
|
130
|
+
const sessions = new Set<string>();
|
|
131
|
+
|
|
132
|
+
export const handler: MockRequestHandler = (req, res) => {
|
|
133
|
+
if (req.method === "POST") {
|
|
134
|
+
const id = `sess-${Date.now()}`;
|
|
135
|
+
sessions.add(id);
|
|
136
|
+
res.setHeader("Content-Type", "application/json");
|
|
137
|
+
res.end(JSON.stringify({ status: "success", data: { session_id: id } }));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (req.method === "DELETE") {
|
|
141
|
+
const id = (req.url ?? "").split("/").pop() ?? "";
|
|
142
|
+
sessions.delete(id);
|
|
143
|
+
res.setHeader("Content-Type", "application/json");
|
|
144
|
+
res.end(JSON.stringify({ status: "success" }));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
res.statusCode = 405;
|
|
148
|
+
res.end();
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**热更新**:TypeScript mock 文件通过 Vite 的 `ssrLoadModule` 加载。每次修改后,下一个请求即可生效,**无需重启开发服务器**。
|
|
153
|
+
|
|
83
154
|
## 场景(11 种)
|
|
84
155
|
|
|
85
156
|
1. 正常完成(默认)
|
|
@@ -108,20 +179,20 @@ export default defineConfig({
|
|
|
108
179
|
直接在请求 URL 上附加参数,适合临时调试单个接口:
|
|
109
180
|
|
|
110
181
|
```
|
|
111
|
-
/api/
|
|
112
|
-
/api/
|
|
182
|
+
/api/default?scenario=jitter
|
|
183
|
+
/api/default?firstChunkDelayMs=1000&errorAt=3
|
|
113
184
|
```
|
|
114
185
|
|
|
115
186
|
```ts
|
|
116
187
|
// 默认返回 SSE 流式响应
|
|
117
|
-
const response = await fetch("/api/
|
|
188
|
+
const response = await fetch("/api/default?firstChunkDelayMs=4800", {
|
|
118
189
|
method: "POST",
|
|
119
190
|
headers: { "Content-Type": "application/json" },
|
|
120
191
|
body: JSON.stringify({}),
|
|
121
192
|
});
|
|
122
193
|
|
|
123
194
|
// 使用 ?transport=json 获取 JSON 格式
|
|
124
|
-
const jsonResponse = await fetch("/api/
|
|
195
|
+
const jsonResponse = await fetch("/api/default?transport=json");
|
|
125
196
|
```
|
|
126
197
|
|
|
127
198
|
**2. 插件选项 `defaultScenario`(全局生效)**
|
|
@@ -144,9 +215,26 @@ aiMockPlugin({
|
|
|
144
215
|
|
|
145
216
|
不配置 `defaultScenario` 时,默认使用 `normal` 场景(无延迟,正常完成)。
|
|
146
217
|
|
|
218
|
+
**3. 插件选项 `jsonApis`(指定返回 JSON 格式的 API)**
|
|
219
|
+
|
|
220
|
+
在 `vite.config.ts` 中配置,指定哪些 API 路径返回 JSON 格式而不是 SSE 格式:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
aiMockPlugin({
|
|
224
|
+
endpoint: "/api",
|
|
225
|
+
jsonApis: [
|
|
226
|
+
"/api/config", // 精确匹配
|
|
227
|
+
"/api/history", // 精确匹配
|
|
228
|
+
/^\/api\/static\/.*/, // 正则匹配
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
优先级:`?transport=json` / `?transport=sse` > `jsonApis` 配置 > 默认 SSE
|
|
234
|
+
|
|
147
235
|
## Mock 文件格式
|
|
148
236
|
|
|
149
|
-
|
|
237
|
+
每个 mock 文件(JSON 或 TypeScript)包含一个 `chunks` 数组,每个 chunk 对应一条 SSE 事件:
|
|
150
238
|
|
|
151
239
|
| 字段 | 类型 | 说明 |
|
|
152
240
|
| --------- | ------ | --------------------------------------------------- |
|
|
@@ -169,18 +257,18 @@ aiMockPlugin({
|
|
|
169
257
|
|
|
170
258
|
### 主流格式示例
|
|
171
259
|
|
|
172
|
-
`data` 字段可以完整模拟真实 API 的响应结构。npm 包内置了以下示例文件(位于 `mock
|
|
260
|
+
`data` 字段可以完整模拟真实 API 的响应结构。npm 包内置了以下示例文件(位于 `mock/`),可直接复制到项目中使用:
|
|
173
261
|
|
|
174
|
-
| 文件
|
|
175
|
-
|
|
|
176
|
-
| `mock/
|
|
177
|
-
| `mock/
|
|
178
|
-
| `mock/
|
|
179
|
-
| `mock/
|
|
180
|
-
| `mock/
|
|
181
|
-
| `mock/
|
|
182
|
-
| `mock/
|
|
183
|
-
| `mock/
|
|
262
|
+
| 文件 | 提供商 |
|
|
263
|
+
| --------------------------- | ----------------- |
|
|
264
|
+
| `mock/openai.json` | OpenAI / 兼容格式 |
|
|
265
|
+
| `mock/claude.json` | Anthropic Claude |
|
|
266
|
+
| `mock/gemini.json` | Google Gemini |
|
|
267
|
+
| `mock/deepseek.json` | DeepSeek |
|
|
268
|
+
| `mock/deepseek-reasoner.json` | DeepSeek Reasoner |
|
|
269
|
+
| `mock/qwen.json` | 通义千问(阿里) |
|
|
270
|
+
| `mock/qwen-thinking.json` | 通义千问 Thinking |
|
|
271
|
+
| `mock/doubao.json` | 豆包(字节跳动) |
|
|
184
272
|
|
|
185
273
|
**OpenAI / 兼容格式**(`openai.json`)——最后一条 `data` 为字符串 `"[DONE]"`:
|
|
186
274
|
|
|
@@ -272,7 +360,7 @@ import { DefaultChatTransport } from "ai";
|
|
|
272
360
|
|
|
273
361
|
const { messages, sendMessage, status } = useChat({
|
|
274
362
|
transport: new DefaultChatTransport({
|
|
275
|
-
api: "/api/
|
|
363
|
+
api: "/api/chat",
|
|
276
364
|
}),
|
|
277
365
|
});
|
|
278
366
|
```
|
|
@@ -289,10 +377,10 @@ const { messages, sendMessage, status } = useChat({
|
|
|
289
377
|
|
|
290
378
|
```ts
|
|
291
379
|
// string(默认)
|
|
292
|
-
endpoint: "/api
|
|
293
|
-
// /api
|
|
294
|
-
// /api/
|
|
295
|
-
// /api/
|
|
380
|
+
endpoint: "/api";
|
|
381
|
+
// /api → file = "default"
|
|
382
|
+
// /api/chat → file = "chat"
|
|
383
|
+
// /api/i18n/zh-CN → file = "i18n/zh-CN"(多层级目录)
|
|
296
384
|
|
|
297
385
|
// RegExp
|
|
298
386
|
endpoint: /^\/api\/ai\/.*/;
|
|
@@ -302,11 +390,11 @@ endpoint: /^\/api\/ai\/.*/;
|
|
|
302
390
|
endpoint: ["/api/chat", /^\/v2\/ai\/.*/];
|
|
303
391
|
```
|
|
304
392
|
|
|
305
|
-
支持多层级目录。例如 `/api/
|
|
393
|
+
支持多层级目录。例如 `/api/i18n/zh-CN` 会映射到 `mock/i18n/zh-CN.json`。
|
|
306
394
|
|
|
307
|
-
- `/api
|
|
308
|
-
- `/api
|
|
309
|
-
- `/api
|
|
395
|
+
- `/api`
|
|
396
|
+
- `/api/<file>`
|
|
397
|
+
- `/api/<dir>/<file>`(多层级)
|
|
310
398
|
- `?file=<file>` 或 `?file=<dir>/<file>`
|
|
311
399
|
|
|
312
400
|
## 测试
|
|
@@ -328,3 +416,75 @@ pnpm release:npm
|
|
|
328
416
|
```
|
|
329
417
|
|
|
330
418
|
`prepublishOnly` 会自动执行构建、测试和类型检查。
|
|
419
|
+
|
|
420
|
+
## 配置选项
|
|
421
|
+
|
|
422
|
+
### 插件配置 `AiMockPluginOptions`
|
|
423
|
+
|
|
424
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
425
|
+
| --- | --- | --- | --- |
|
|
426
|
+
| `dataDir` | `string` | `"mock"` | mock 文件目录(支持 `.json` 和 `.ts`),相对于项目根目录 |
|
|
427
|
+
| `endpoint` | `string \| RegExp \| (string \| RegExp)[]` | `"/api"` | 拦截的 API 路径,支持字符串、正则或数组 |
|
|
428
|
+
| `defaultScenario` | `DefaultScenarioConfig` | `undefined` | 全局默认场景配置,可被 URL 参数覆盖 |
|
|
429
|
+
| `jsonApis` | `(string \| RegExp)[]` | `undefined` | 指定返回 JSON 格式的 API 路径(对 `.json` 和 `.ts` 均生效)|
|
|
430
|
+
|
|
431
|
+
### 场景配置 `DefaultScenarioConfig`
|
|
432
|
+
|
|
433
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
434
|
+
| --- | --- | --- | --- |
|
|
435
|
+
| `scenario` | `ScenarioName` | `undefined` | 预设场景名:`normal`、`first-delay`、`jitter`、`disconnect`、`timeout`、`error`、`malformed`、`duplicate`、`out-of-order`、`reconnect`、`heartbeat` |
|
|
436
|
+
| `firstChunkDelayMs` | `number` | `0` | 首个数据块发送前的延迟时间(毫秒) |
|
|
437
|
+
| `minIntervalMs` | `number` | `200` | 数据块之间的最小间隔时间(毫秒) |
|
|
438
|
+
| `maxIntervalMs` | `number` | `700` | 数据块之间的最大间隔时间(毫秒) |
|
|
439
|
+
| `disconnectAt` | `number` | `-1` | 在第 N 个数据块处断开连接(-1 为不断开) |
|
|
440
|
+
| `stallAfter` | `number` | `-1` | 在第 N 个数据块后停止发送(-1 为不停止) |
|
|
441
|
+
| `stallMs` | `number` | `30000` | 停止发送后等待的时间(毫秒) |
|
|
442
|
+
| `errorAt` | `number` | `-1` | 在第 N 个数据块处发送错误事件(-1 为不发送) |
|
|
443
|
+
| `errorMessage` | `string` | `"mock_error"` | 错误事件的消息内容 |
|
|
444
|
+
| `malformedAt` | `number` | `-1` | 在第 N 个数据块处发送格式错误的数据(-1 为不发送) |
|
|
445
|
+
| `duplicateAt` | `number` | `-1` | 在第 N 个数据块处发送重复数据(-1 为不发送) |
|
|
446
|
+
| `outOfOrder` | `boolean` | `false` | 是否打乱数据块顺序(交换第 2、3 块) |
|
|
447
|
+
| `heartbeatMs` | `number` | `0` | 心跳间隔时间(毫秒),0 为不发送心跳 |
|
|
448
|
+
| `reconnect` | `boolean` | `false` | 是否启用重连模式,配合 `lastEventId` 使用 |
|
|
449
|
+
|
|
450
|
+
### URL 参数
|
|
451
|
+
|
|
452
|
+
除了上述场景配置参数外,还支持以下 URL 专用参数:
|
|
453
|
+
|
|
454
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
455
|
+
| --- | --- | --- | --- |
|
|
456
|
+
| `file` | `string` | `"default"` | 指定 mock 文件名(不含 `.json` 后缀) |
|
|
457
|
+
| `transport` | `"sse" \| "json"` | `"sse"` | 响应格式:`sse` 流式响应,`json` 直接返回 JSON |
|
|
458
|
+
| `httpErrorStatus` | `number` | `0` | 返回指定 HTTP 状态码错误(如 401、500) |
|
|
459
|
+
| `includeDone` | `"true" \| "false"` | `"true"` | 是否在流结束时发送 `done` 事件 |
|
|
460
|
+
| `lastEventId` | `string` | `undefined` | 断线重连时的最后事件 ID,从该 ID 之后继续发送 |
|
|
461
|
+
|
|
462
|
+
### 完整配置示例
|
|
463
|
+
|
|
464
|
+
```ts
|
|
465
|
+
import { defineConfig } from "vite";
|
|
466
|
+
import { aiMockPlugin } from "vite-plugin-ai-mock";
|
|
467
|
+
|
|
468
|
+
export default defineConfig({
|
|
469
|
+
plugins: [
|
|
470
|
+
aiMockPlugin({
|
|
471
|
+
// mock 文件目录
|
|
472
|
+
dataDir: "mock",
|
|
473
|
+
// 拦截的 API 路径
|
|
474
|
+
endpoint: "/api",
|
|
475
|
+
// 全局默认场景
|
|
476
|
+
defaultScenario: {
|
|
477
|
+
scenario: "jitter",
|
|
478
|
+
firstChunkDelayMs: 500,
|
|
479
|
+
minIntervalMs: 100,
|
|
480
|
+
maxIntervalMs: 800,
|
|
481
|
+
},
|
|
482
|
+
// 返回 JSON 格式的 API 列表
|
|
483
|
+
jsonApis: [
|
|
484
|
+
"/api/config",
|
|
485
|
+
/^\/api\/static\/.*/,
|
|
486
|
+
],
|
|
487
|
+
}),
|
|
488
|
+
],
|
|
489
|
+
});
|
|
490
|
+
```
|