@chatbi-v/mocks 2.0.5 → 2.1.1

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/dist/index.mjs CHANGED
@@ -1,25 +1,799 @@
1
- import{dateUtils as L}from"@chatbi-v/core";import{createLogger as B,dateUtils as M}from"@chatbi-v/core";import N from"mockjs";import E from"mockjs";import{dateUtils as D}from"@chatbi-v/core";var v=class{events=[];sessionId;agentName;currentTodos=[];constructor(e="Assistant",t){this.sessionId=t||"conv_"+D.now(),this.agentName=e}emitHistory(e){return e.forEach(t=>{let s={content:t.content,sessionId:this.sessionId,role:t.role,completed:!0,agentName:this.agentName,createTime:t.createTime};this.pushEvent("data",s),t.todos&&this.pushEvent("todos",{items:t.todos})}),this}initPlan(e){return this.currentTodos=e.map(t=>({content:t,status:"PENDING"})),this.pushTodos(),this}updatePlanStatus(e){return this.currentTodos=this.currentTodos.map((t,s)=>s<e?{...t,status:"COMPLETED"}:s===e?{...t,status:"IN_PROGRESS"}:{...t,status:"PENDING"}),this.pushTodos(),this}completePlan(){return this.currentTodos=this.currentTodos.map(e=>({...e,status:"COMPLETED"})),this.pushTodos(),this}addLog(e,t="info",s){return this.pushEvent("log",{type:t,content:e,agentName:s||this.agentName}),this}addError(e,t){return this.pushEvent("error",{errorCode:e,errorMessage:t}),this}streamContent(e,t=20){for(let s=0;s<e.length;s+=t){let o=e.slice(s,s+t);this.pushEvent("data",{content:o,sessionId:this.sessionId,completed:!1,agentName:this.agentName})}return this}finish(){return this.pushEvent("data",{content:"",sessionId:this.sessionId,completed:!0,agentName:this.agentName}),this}emitA2UI(e){let t=JSON.stringify(e,null,2);return this.streamContent(t)}toString(){return this.events.join(`
2
- `)}pushTodos(){this.pushEvent("todos",{items:this.currentTodos})}pushEvent(e,t){this.events.push(`event: ${e}
3
- data: ${JSON.stringify(t)}
4
- `)}};import j from"dayjs";import P from"mockjs";var k=a=>new Promise(e=>setTimeout(e,a));function C(a,e){if(typeof a=="string"){if(/^\{\{.*\}\}$/.test(a)){let t=a.slice(2,-2).trim();try{let s=Object.keys(e),o=Object.values(e);return new Function(...s,`return ${t}`)(...o)}catch(s){return console.warn(`[Mock] Failed to evaluate template: ${a}`,s),a}}return a}if(Array.isArray(a))return a.map(t=>C(t,e));if(typeof a=="object"&&a!==null){let t={};for(let s in a)t[s]=C(a[s],e);return t}return a}function T(a,e={}){let t={$query:e,$body:e,$param:e?.param||e};P.Random.extend({$query:()=>t.$query,$body:()=>t.$body,$param:()=>t.$param});let s=P.mock(a);return Object.values(s).flat().map(o=>C(o,t)).sort((o,i)=>{let r=typeof o.delay=="number"?o.delay:parseInt(String(o.delay||0)),n=typeof i.delay=="number"?i.delay:parseInt(String(i.delay||0));return r-n})}function q(a){let e=new TextEncoder;return new ReadableStream({start(t){if(!a||a.length===0){t.close();return}(async()=>{try{let s=j().valueOf();for(let o of a){let i=typeof o.delay=="number"?o.delay:parseInt(String(o.delay||0)),r=j().valueOf()-s,n=Math.max(0,i-r);n>0&&await k(n);let c=`event: ${o.event}
5
- data: ${JSON.stringify(o.data)}
1
+ // src/index.ts
2
+ import { dateUtils as dateUtils3 } from "@chatbi-v/core";
6
3
 
7
- `;t.enqueue(e.encode(c))}t.close()}catch(s){t.error(s)}})()}})}var x=class{generate(e){let t=e,s=t.agentName||"BI\u52A9\u624B",o=new v(s),i=t.data?E.mock(t.data):{};t.plan&&o.initPlan(t.plan),t.timeline&&t.timeline.forEach(n=>{if(n.planIndex!==void 0&&o.updatePlanStatus(n.planIndex),n.log){let c=typeof n.log=="string"?n.log:n.log.content,p=typeof n.log=="object"?n.log.level:"info",d=typeof n.log=="object"?n.log.agent:void 0;o.addLog(c,p,d)}});let r;return typeof t.content=="function"?r=t.content(i):r=t.content,typeof r=="object"&&r!==null?o.emitA2UI(r):o.streamContent(r),o.completePlan(),o.finish(),o.toString().split(`
4
+ // src/adapter.ts
5
+ import {
6
+ createLogger,
7
+ dateUtils as dateUtils2
8
+ } from "@chatbi-v/core";
9
+ import Mock3 from "mockjs";
8
10
 
9
- `).map(n=>n+`
11
+ // src/strategies.ts
12
+ import Mock2 from "mockjs";
10
13
 
11
- `)}},$=class{generate(e){let t=e,s=new v("BI\u52A9\u624B"),o=E.mock(t.template),i=[...t.prepend||[],...o.list||o];return s.emitHistory(i),s.toString().split(`
14
+ // src/generator.ts
15
+ import { dateUtils } from "@chatbi-v/core";
16
+ var MockResponseGenerator = class {
17
+ events = [];
18
+ sessionId;
19
+ agentName;
20
+ currentTodos = [];
21
+ constructor(agentName = "Assistant", sessionId) {
22
+ this.sessionId = sessionId || "conv_" + dateUtils.now();
23
+ this.agentName = agentName;
24
+ }
25
+ /**
26
+ * 生成历史消息流
27
+ */
28
+ emitHistory(history) {
29
+ history.forEach((msg) => {
30
+ const data = {
31
+ content: msg.content,
32
+ sessionId: this.sessionId,
33
+ role: msg.role,
34
+ completed: true,
35
+ agentName: this.agentName,
36
+ createTime: msg.createTime
37
+ };
38
+ this.pushEvent("data", data);
39
+ if (msg.todos) {
40
+ this.pushEvent("todos", { items: msg.todos });
41
+ }
42
+ });
43
+ return this;
44
+ }
45
+ /**
46
+ * 初始化执行计划
47
+ * @param steps 计划步骤列表
48
+ */
49
+ initPlan(steps) {
50
+ this.currentTodos = steps.map((step) => ({
51
+ content: step,
52
+ status: "PENDING"
53
+ }));
54
+ this.pushTodos();
55
+ return this;
56
+ }
57
+ /**
58
+ * 更新执行计划状态
59
+ * @param activeIndex 当前正在进行的步骤索引
60
+ */
61
+ updatePlanStatus(activeIndex) {
62
+ this.currentTodos = this.currentTodos.map((todo, index) => {
63
+ if (index < activeIndex) {
64
+ return { ...todo, status: "COMPLETED" };
65
+ } else if (index === activeIndex) {
66
+ return { ...todo, status: "IN_PROGRESS" };
67
+ } else {
68
+ return { ...todo, status: "PENDING" };
69
+ }
70
+ });
71
+ this.pushTodos();
72
+ return this;
73
+ }
74
+ /**
75
+ * 标记所有计划为完成
76
+ */
77
+ completePlan() {
78
+ this.currentTodos = this.currentTodos.map((todo) => ({ ...todo, status: "COMPLETED" }));
79
+ this.pushTodos();
80
+ return this;
81
+ }
82
+ /**
83
+ * 添加日志
84
+ * @param content 日志内容
85
+ * @param type 日志类型
86
+ * @param agentName 可选的 Agent 名称
87
+ */
88
+ addLog(content, type = "info", agentName) {
89
+ this.pushEvent("log", {
90
+ type,
91
+ content,
92
+ agentName: agentName || this.agentName
93
+ });
94
+ return this;
95
+ }
96
+ /**
97
+ * 添加系统错误事件
98
+ * @param errorCode 错误码
99
+ * @param errorMessage 错误信息
100
+ */
101
+ addError(errorCode, errorMessage) {
102
+ this.pushEvent("error", {
103
+ errorCode,
104
+ errorMessage
105
+ });
106
+ return this;
107
+ }
108
+ /**
109
+ * 添加流式内容块
110
+ * @param content 完整内容
111
+ * @param chunkSize 分块大小
112
+ */
113
+ streamContent(content, chunkSize = 20) {
114
+ for (let i = 0; i < content.length; i += chunkSize) {
115
+ const chunk = content.slice(i, i + chunkSize);
116
+ this.pushEvent("data", {
117
+ content: chunk,
118
+ sessionId: this.sessionId,
119
+ completed: false,
120
+ agentName: this.agentName
121
+ });
122
+ }
123
+ return this;
124
+ }
125
+ /**
126
+ * 结束流
127
+ */
128
+ finish() {
129
+ this.pushEvent("data", {
130
+ content: "",
131
+ sessionId: this.sessionId,
132
+ completed: true,
133
+ agentName: this.agentName
134
+ });
135
+ return this;
136
+ }
137
+ /**
138
+ * 生成 A2UI 响应
139
+ * @param component A2UI 组件配置对象
140
+ */
141
+ emitA2UI(component) {
142
+ const content = JSON.stringify(component, null, 2);
143
+ return this.streamContent(content);
144
+ }
145
+ /**
146
+ * 生成最终的响应字符串
147
+ */
148
+ toString() {
149
+ return this.events.join("\n");
150
+ }
151
+ pushTodos() {
152
+ this.pushEvent("todos", { items: this.currentTodos });
153
+ }
154
+ pushEvent(type, data) {
155
+ this.events.push(`event: ${type}
156
+ data: ${JSON.stringify(data)}
157
+ `);
158
+ }
159
+ };
12
160
 
13
- `).map(r=>r+`
161
+ // src/utils.ts
162
+ import dayjs from "dayjs";
163
+ import Mock from "mockjs";
164
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
165
+ function processTemplate(data, context) {
166
+ if (typeof data === "string") {
167
+ if (/^\{\{.*\}\}$/.test(data)) {
168
+ const expression = data.slice(2, -2).trim();
169
+ try {
170
+ const keys = Object.keys(context);
171
+ const values = Object.values(context);
172
+ const fn = new Function(...keys, `return ${expression}`);
173
+ return fn(...values);
174
+ } catch (e) {
175
+ console.warn(`[Mock] Failed to evaluate template: ${data}`, e);
176
+ return data;
177
+ }
178
+ }
179
+ return data;
180
+ }
181
+ if (Array.isArray(data)) {
182
+ return data.map((item) => processTemplate(item, context));
183
+ }
184
+ if (typeof data === "object" && data !== null) {
185
+ const result = {};
186
+ for (const key in data) {
187
+ result[key] = processTemplate(data[key], context);
188
+ }
189
+ return result;
190
+ }
191
+ return data;
192
+ }
193
+ function flatEvents(eventsSchema, requestData = {}) {
194
+ const context = {
195
+ $query: requestData,
196
+ // Backward compatibility
197
+ $body: requestData,
198
+ $param: requestData?.param || requestData
199
+ };
200
+ Mock.Random.extend({
201
+ $query: () => context.$query,
202
+ $body: () => context.$body,
203
+ $param: () => context.$param
204
+ });
205
+ const compiled = Mock.mock(eventsSchema);
206
+ return Object.values(compiled).flat().map((event) => processTemplate(event, context)).sort((a, b) => {
207
+ const delayA = typeof a.delay === "number" ? a.delay : parseInt(String(a.delay || 0));
208
+ const delayB = typeof b.delay === "number" ? b.delay : parseInt(String(b.delay || 0));
209
+ return delayA - delayB;
210
+ });
211
+ }
212
+ function eventsToStream(events) {
213
+ const encoder = new TextEncoder();
214
+ return new ReadableStream({
215
+ start(ctrl) {
216
+ if (!events || events.length === 0) {
217
+ ctrl.close();
218
+ return;
219
+ }
220
+ (async () => {
221
+ try {
222
+ const startTime = dayjs().valueOf();
223
+ for (const eventItem of events) {
224
+ const delay = typeof eventItem.delay === "number" ? eventItem.delay : parseInt(String(eventItem.delay || 0));
225
+ const elapsed = dayjs().valueOf() - startTime;
226
+ const remaining = Math.max(0, delay - elapsed);
227
+ if (remaining > 0) {
228
+ await sleep(remaining);
229
+ }
230
+ const payload = `event: ${eventItem.event}
231
+ data: ${JSON.stringify(eventItem.data)}
14
232
 
15
- `)}},_=class{process(e,t){let s=e;return s.responseSchema?E.mock(s.responseSchema):{}}},I=class{process(e,t={}){let s=e;return s.responseSchema?T(s.responseSchema,t):[]}},A=class{process(e,t={}){let s=e;if(!s.responseSchema)return[];let o=T(s.responseSchema,t);if(s.pageEvent){let r={$query:t,$body:t,$param:t?.param||t};E.Random.extend({$query:()=>r.$query,$body:()=>r.$body,$param:()=>r.$param});let n=E.mock(s.pageEvent);n=C(n,r);let c=0;if(o.length>0){let d=o[o.length-1];c=typeof d.delay=="number"?d.delay:parseInt(String(d.delay||0))}(typeof n.delay=="number"?n.delay:parseInt(String(n.delay||0)))<=c&&(n.delay=c+20),o.push(n)}let i=o.sort((r,n)=>{let c=typeof r.delay=="number"?r.delay:parseInt(String(r.delay||0)),p=typeof n.delay=="number"?n.delay:parseInt(String(n.delay||0));return c-p});return console.log(i),i}},w={getStrategy(a="json"){switch(a){case"sse":return new I;case"sse-page":return new A;default:return new _}}};var l=B("MockAdapter"),G=class a{delay;static strategies={chat_stream:new x,history_stream:new $};constructor(e=300){this.delay=e}static registerStrategy(e,t){this.strategies[e]=t}async request(e,t){let s=t?.delay??this.delay;return new Promise((o,i)=>{setTimeout(()=>{try{if(!t||!t.responseSchema){l.warn(`\u672A\u627E\u5230\u54CD\u5E94\u67B6\u6784\u914D\u7F6E: ${e.url}`),o({});return}let r=t.responseSchema,n;typeof r=="function"&&(r=r(e));let c={type:t.type,status:t.status||200,pageEvent:t.pageEvent,responseSchema:r,...typeof r=="object"?r:{}};if(c.status!==200){let p=new Error(`Request failed with status code ${c.status}`);p.response={status:c.status,data:N.mock(r),headers:{}},i(p);return}if(typeof r=="string")n=r;else if(this.isLegacySchema(r)){let p=a.strategies[r._type];if(p){n=p.generate(r).join("").replace(/event: data\ndata: /g,"").replace(/\n\n/g,"");try{n=JSON.parse(n)}catch{}}else l.warn(`\u672A\u627E\u5230\u5BF9\u5E94\u7684\u7B56\u7565\u7C7B\u578B: ${r._type}`),n={}}else{let p=c.type||"json";n=w.getStrategy(p).process(c,e.params||e.data)}l.info(`Request: ${e.method} ${e.url}`,e.data||e.params),l.info("Response:",n),o(n)}catch(r){i(r)}},s)})}async stream(e,t,s){let{onMessage:o,onFinish:i,onError:r}=t,n=e.signal,c=s?.delay??this.delay;return new Promise((p,d)=>{if(n&&n.aborted){i&&i(),p();return}setTimeout(async()=>{try{if(!s||!s.responseSchema){l.warn(`\u672A\u627E\u5230\u6D41\u5F0F\u54CD\u5E94\u67B6\u6784: ${e.url}`),i&&i(),p();return}let S=M.now();l.info(`[SSE Start] Request: ${e.method} ${e.url}`,{params:e.data||e.params,time:M.dayjs().toISOString()});let m=s.responseSchema;typeof m=="function"&&(m=m(e));let f={type:s.type,status:s.status||200,pageEvent:s.pageEvent,responseSchema:m,...typeof m=="object"?m:{}};if(f.status!==200){let g={status:f.status,data:N.mock(m),headers:{}};if(t.onResponse&&await t.onResponse(g)){p();return}let y=new Error(`Stream request failed with status code ${f.status}`);y.response=g,l.error(`[SSE Error] Request failed with status ${f.status}`,y),r&&r(y),d(y);return}if(t.onResponse&&await t.onResponse({status:200,data:null,headers:{}})){p();return}if(this.isNewConfig(f)){let g=f.type||"sse",u=w.getStrategy(g).process(f,e.params||e.data);if(Array.isArray(u)){let R=M.now(),b=0;l.info(`[SSE Processing] Generated ${u.length} events for ${g} strategy`);for(let h of u){if(n&&n.aborted){l.info(`[SSE Abort] Stream aborted by user after ${M.now()-S}ms`);break}let H=typeof h.delay=="number"?h.delay:parseInt(String(h.delay||0)),J=M.now()-R,O=Math.max(0,H-J);if(O>0&&await k(O),n&&n.aborted)break;let U=`event: ${h.event}
16
- data: ${JSON.stringify(h.data)}
233
+ `;
234
+ ctrl.enqueue(encoder.encode(payload));
235
+ }
236
+ ctrl.close();
237
+ } catch (e) {
238
+ ctrl.error(e);
239
+ }
240
+ })();
241
+ }
242
+ });
243
+ }
17
244
 
18
- `;["error","todos","page"].includes(h.event)?l.info(`[SSE Event] Emitting special event: ${h.event}`,h.data):(b===0||b===u.length-1)&&l.info(`[SSE Event] Emitting ${b===0?"first":"last"} data event`,{preview:JSON.stringify(h.data).slice(0,50)+"..."}),o&&o(U),b++}}}else{let g=[];if(typeof m=="string")g=m.split(`
245
+ // src/strategies.ts
246
+ var ChatStreamStrategy = class {
247
+ generate(schema) {
248
+ const config = schema;
249
+ const defaultName = config.agentName || "BI\u52A9\u624B";
250
+ const generator = new MockResponseGenerator(defaultName);
251
+ const mockData2 = config.data ? Mock2.mock(config.data) : {};
252
+ if (config.plan) {
253
+ generator.initPlan(config.plan);
254
+ }
255
+ if (config.timeline) {
256
+ config.timeline.forEach((step) => {
257
+ if (step.planIndex !== void 0) {
258
+ generator.updatePlanStatus(step.planIndex);
259
+ }
260
+ if (step.log) {
261
+ const logContent = typeof step.log === "string" ? step.log : step.log.content;
262
+ const logLevel = typeof step.log === "object" ? step.log.level : "info";
263
+ const logAgent = typeof step.log === "object" ? step.log.agent : void 0;
264
+ generator.addLog(logContent, logLevel, logAgent);
265
+ }
266
+ });
267
+ }
268
+ let content;
269
+ if (typeof config.content === "function") {
270
+ content = config.content(mockData2);
271
+ } else {
272
+ content = config.content;
273
+ }
274
+ if (typeof content === "object" && content !== null) {
275
+ generator.emitA2UI(content);
276
+ } else {
277
+ generator.streamContent(content);
278
+ }
279
+ generator.completePlan();
280
+ generator.finish();
281
+ return generator.toString().split("\n\n").map((chunk) => chunk + "\n\n");
282
+ }
283
+ };
284
+ var HistoryStreamStrategy = class {
285
+ generate(schema) {
286
+ const config = schema;
287
+ const generator = new MockResponseGenerator("BI\u52A9\u624B");
288
+ const historyMock = Mock2.mock(config.template);
289
+ const fullHistory = [...config.prepend || [], ...historyMock.list || historyMock];
290
+ generator.emitHistory(fullHistory);
291
+ return generator.toString().split("\n\n").map((chunk) => chunk + "\n\n");
292
+ }
293
+ };
294
+ var JsonStrategy = class {
295
+ process(config, _requestParams) {
296
+ const jsonConfig = config;
297
+ if (!jsonConfig.responseSchema) return {};
298
+ return Mock2.mock(jsonConfig.responseSchema);
299
+ }
300
+ };
301
+ var SseStrategy = class {
302
+ process(config, requestParams = {}) {
303
+ const sseConfig = config;
304
+ if (!sseConfig.responseSchema) return [];
305
+ return flatEvents(sseConfig.responseSchema, requestParams);
306
+ }
307
+ };
308
+ var SsePageStrategy = class {
309
+ process(config, requestParams = {}) {
310
+ const ssePageConfig = config;
311
+ if (!ssePageConfig.responseSchema) return [];
312
+ const events = flatEvents(ssePageConfig.responseSchema, requestParams);
313
+ if (ssePageConfig.pageEvent) {
314
+ const context = {
315
+ $query: requestParams,
316
+ // Backward compatibility
317
+ $body: requestParams,
318
+ $param: requestParams?.param || requestParams
319
+ };
320
+ Mock2.Random.extend({
321
+ $query: () => context.$query,
322
+ $body: () => context.$body,
323
+ $param: () => context.$param
324
+ });
325
+ let pageEvent = Mock2.mock(ssePageConfig.pageEvent);
326
+ pageEvent = processTemplate(pageEvent, context);
327
+ let maxDelay = 0;
328
+ if (events.length > 0) {
329
+ const lastEvent = events[events.length - 1];
330
+ maxDelay = typeof lastEvent.delay === "number" ? lastEvent.delay : parseInt(String(lastEvent.delay || 0));
331
+ }
332
+ const pageDelay = typeof pageEvent.delay === "number" ? pageEvent.delay : parseInt(String(pageEvent.delay || 0));
333
+ if (pageDelay <= maxDelay) {
334
+ pageEvent.delay = maxDelay + 20;
335
+ }
336
+ events.push(pageEvent);
337
+ }
338
+ const es = events.sort((a, b) => {
339
+ const delayA = typeof a.delay === "number" ? a.delay : parseInt(String(a.delay || 0));
340
+ const delayB = typeof b.delay === "number" ? b.delay : parseInt(String(b.delay || 0));
341
+ return delayA - delayB;
342
+ });
343
+ return es;
344
+ }
345
+ };
346
+ var StrategyFactory = {
347
+ getStrategy(type = "json") {
348
+ switch (type) {
349
+ case "sse":
350
+ return new SseStrategy();
351
+ case "sse-page":
352
+ return new SsePageStrategy();
353
+ case "json":
354
+ default:
355
+ return new JsonStrategy();
356
+ }
357
+ }
358
+ };
19
359
 
20
- `).map(y=>y+`
360
+ // src/adapter.ts
361
+ var logger = createLogger("MockAdapter");
362
+ var MockAdapter = class _MockAdapter {
363
+ delay;
364
+ // 遗留策略(为了兼容旧版代码)
365
+ static strategies = {
366
+ chat_stream: new ChatStreamStrategy(),
367
+ history_stream: new HistoryStreamStrategy()
368
+ };
369
+ /**
370
+ * 构造函数
371
+ * @param delay 全局模拟延迟时间(毫秒),默认 300ms
372
+ */
373
+ constructor(delay = 300) {
374
+ this.delay = delay;
375
+ }
376
+ /**
377
+ * 注册自定义 Mock 生成策略
378
+ * @param key 策略唯一标识符 (e.g., 'chat_stream', 'history_stream')
379
+ * @param strategy 实现了 MockGeneratorStrategy 接口的策略实例
380
+ */
381
+ static registerStrategy(key, strategy) {
382
+ this.strategies[key] = strategy;
383
+ }
384
+ /**
385
+ * 处理普通 HTTP 请求(非流式)
386
+ * @param config 请求配置对象
387
+ * @param endpointConfig API 端点配置,包含 responseSchema
388
+ * @returns Promise 返回模拟的响应数据
389
+ */
390
+ async request(config, endpointConfig) {
391
+ const delay = endpointConfig?.delay ?? this.delay;
392
+ return new Promise((resolve, reject) => {
393
+ setTimeout(() => {
394
+ try {
395
+ if (!endpointConfig || !endpointConfig.responseSchema) {
396
+ logger.warn(`\u672A\u627E\u5230\u54CD\u5E94\u67B6\u6784\u914D\u7F6E: ${config.url}`);
397
+ resolve({});
398
+ return;
399
+ }
400
+ let schema = endpointConfig.responseSchema;
401
+ let mockData2;
402
+ if (typeof schema === "function") {
403
+ schema = schema(config);
404
+ }
405
+ const effectiveConfig = {
406
+ type: endpointConfig.type,
407
+ status: endpointConfig.status || 200,
408
+ pageEvent: endpointConfig.pageEvent,
409
+ responseSchema: schema,
410
+ ...typeof schema === "object" ? schema : {}
411
+ };
412
+ if (effectiveConfig.status !== 200) {
413
+ const error = new Error(`Request failed with status code ${effectiveConfig.status}`);
414
+ error.response = {
415
+ status: effectiveConfig.status,
416
+ data: Mock3.mock(schema),
417
+ headers: {}
418
+ };
419
+ reject(error);
420
+ return;
421
+ }
422
+ if (typeof schema === "string") {
423
+ mockData2 = schema;
424
+ } else if (this.isLegacySchema(schema)) {
425
+ const strategy = _MockAdapter.strategies[schema._type];
426
+ if (strategy) {
427
+ const chunks = strategy.generate(schema);
428
+ mockData2 = chunks.join("").replace(/event: data\ndata: /g, "").replace(/\n\n/g, "");
429
+ try {
430
+ mockData2 = JSON.parse(mockData2);
431
+ } catch (e) {
432
+ }
433
+ } else {
434
+ logger.warn(`\u672A\u627E\u5230\u5BF9\u5E94\u7684\u7B56\u7565\u7C7B\u578B: ${schema._type}`);
435
+ mockData2 = {};
436
+ }
437
+ } else {
438
+ const type = effectiveConfig.type || "json";
439
+ const strategy = StrategyFactory.getStrategy(type);
440
+ mockData2 = strategy.process(effectiveConfig, config.params || config.data);
441
+ }
442
+ logger.info(`Request: ${config.method} ${config.url}`, config.data || config.params);
443
+ logger.info(`Response:`, mockData2);
444
+ resolve(mockData2);
445
+ } catch (error) {
446
+ reject(error);
447
+ }
448
+ }, delay);
449
+ });
450
+ }
451
+ /**
452
+ * 处理流式请求 (SSE)
453
+ * @param config 请求配置对象
454
+ * @param callbacks 流式回调函数集合 (onMessage, onFinish, onError)
455
+ * @param endpointConfig API 端点配置,包含 responseSchema
456
+ */
457
+ async stream(config, callbacks, endpointConfig) {
458
+ const { onMessage, onFinish, onError } = callbacks;
459
+ const signal = config.signal;
460
+ const delay = endpointConfig?.delay ?? this.delay;
461
+ return new Promise((resolve, reject) => {
462
+ if (signal && signal.aborted) {
463
+ if (onFinish) onFinish();
464
+ resolve();
465
+ return;
466
+ }
467
+ setTimeout(async () => {
468
+ try {
469
+ if (!endpointConfig || !endpointConfig.responseSchema) {
470
+ logger.warn(`\u672A\u627E\u5230\u6D41\u5F0F\u54CD\u5E94\u67B6\u6784: ${config.url}`);
471
+ if (onFinish) onFinish();
472
+ resolve();
473
+ return;
474
+ }
475
+ const requestStart = dateUtils2.now();
476
+ logger.info(`[SSE Start] Request: ${config.method} ${config.url}`, {
477
+ params: config.data || config.params,
478
+ time: dateUtils2.dayjs().toISOString()
479
+ });
480
+ let schema = endpointConfig.responseSchema;
481
+ if (typeof schema === "function") {
482
+ schema = schema(config);
483
+ }
484
+ const effectiveConfig = {
485
+ type: endpointConfig.type,
486
+ status: endpointConfig.status || 200,
487
+ pageEvent: endpointConfig.pageEvent,
488
+ responseSchema: schema,
489
+ ...typeof schema === "object" ? schema : {}
490
+ };
491
+ if (effectiveConfig.status !== 200) {
492
+ const response = {
493
+ status: effectiveConfig.status,
494
+ data: Mock3.mock(schema),
495
+ headers: {}
496
+ };
497
+ if (callbacks.onResponse) {
498
+ const hijacked = await callbacks.onResponse(response);
499
+ if (hijacked) {
500
+ resolve();
501
+ return;
502
+ }
503
+ }
504
+ const error = new Error(`Stream request failed with status code ${effectiveConfig.status}`);
505
+ error.response = response;
506
+ logger.error(`[SSE Error] Request failed with status ${effectiveConfig.status}`, error);
507
+ if (onError) onError(error);
508
+ reject(error);
509
+ return;
510
+ }
511
+ if (callbacks.onResponse) {
512
+ const hijacked = await callbacks.onResponse({
513
+ status: 200,
514
+ data: null,
515
+ headers: {}
516
+ });
517
+ if (hijacked) {
518
+ resolve();
519
+ return;
520
+ }
521
+ }
522
+ if (this.isNewConfig(effectiveConfig)) {
523
+ const type = effectiveConfig.type || "sse";
524
+ const strategy = StrategyFactory.getStrategy(type);
525
+ const events = strategy.process(
526
+ effectiveConfig,
527
+ config.params || config.data
528
+ );
529
+ if (Array.isArray(events)) {
530
+ const startTime = dateUtils2.now();
531
+ let eventCount = 0;
532
+ logger.info(
533
+ `[SSE Processing] Generated ${events.length} events for ${type} strategy`
534
+ );
535
+ for (const event of events) {
536
+ if (signal && signal.aborted) {
537
+ logger.info(
538
+ `[SSE Abort] Stream aborted by user after ${dateUtils2.now() - requestStart}ms`
539
+ );
540
+ break;
541
+ }
542
+ const delay2 = typeof event.delay === "number" ? event.delay : parseInt(String(event.delay || 0));
543
+ const elapsed = dateUtils2.now() - startTime;
544
+ const remaining = Math.max(0, delay2 - elapsed);
545
+ if (remaining > 0) {
546
+ await sleep(remaining);
547
+ }
548
+ if (signal && signal.aborted) break;
549
+ const chunk = `event: ${event.event}
550
+ data: ${JSON.stringify(event.data)}
21
551
 
22
- `);else if(this.isLegacySchema(m))g=this.generateAdvancedChunks(m);else{let y=N.mock(m);g=[`data: ${JSON.stringify(y)}
552
+ `;
553
+ if (["error", "todos", "page"].includes(event.event)) {
554
+ logger.info(`[SSE Event] Emitting special event: ${event.event}`, event.data);
555
+ } else if (eventCount === 0 || eventCount === events.length - 1) {
556
+ logger.info(
557
+ `[SSE Event] Emitting ${eventCount === 0 ? "first" : "last"} data event`,
558
+ { preview: JSON.stringify(event.data).slice(0, 50) + "..." }
559
+ );
560
+ }
561
+ if (onMessage) onMessage(chunk);
562
+ eventCount++;
563
+ }
564
+ }
565
+ } else {
566
+ let chunks = [];
567
+ if (typeof schema === "string") {
568
+ chunks = schema.split("\n\n").map((chunk) => chunk + "\n\n");
569
+ } else if (this.isLegacySchema(schema)) {
570
+ chunks = this.generateAdvancedChunks(schema);
571
+ } else {
572
+ const mockData2 = Mock3.mock(schema);
573
+ chunks = [`data: ${JSON.stringify(mockData2)}
23
574
 
24
- `]}for(let y of g){if(n&&n.aborted){l.info("[SSE Abort] Stream aborted by user");break}y.trim()&&(o&&o(y),await new Promise(u=>setTimeout(u,200)))}}(!n||!n.aborted)&&(l.info(`[SSE Complete] Stream finished successfully in ${M.now()-S}ms`),i&&i()),p()}catch(S){l.error("[SSE Error] Stream processing failed",S),r&&r(S),p()}},c)})}isLegacySchema(e){return e&&typeof e=="object"&&"_type"in e}isNewConfig(e){return e&&typeof e=="object"&&(["json","sse","sse-page"].includes(e.type)||"responseSchema"in e)}generateAdvancedChunks(e){let t=a.strategies[e._type];return t?t.generate(e):(l.warn(`\u672A\u627E\u5230\u5BF9\u5E94\u7684\u7B56\u7565\u7C7B\u578B: ${e._type}`),[])}};function K(a){window._originalFetch||(window._originalFetch=window.fetch),window.fetch=async(e,t={})=>{let s=typeof e=="string"?e:e instanceof URL?e.toString():e.url,o=(t.method||"GET").toUpperCase(),r=Object.values(a).find(f=>{if(!f.url)return!1;let g=f.url.replace(/:[a-zA-Z0-9_]+/g,"([^/]+)"),y=new RegExp(`^${g}$`),[u]=s.split("?"),R=(f.method||"GET").toUpperCase();return y.test(u)&&R===o});if(!r)return window._originalFetch(e,t);console.log(`[Mock] Intercepted ${o} ${s}`,r);let n=r.status||200;if(n!==200)return await k(r.delay||0),new Response(JSON.stringify({message:`Mock error status ${n}`,code:n}),{status:n,headers:{"Content-Type":"application/json"}});await k(r.delay||0);let c=r.type||"json",p=w.getStrategy(c),d=new URL(s,window.location.origin),S=Object.fromEntries(d.searchParams),m=p.process(r,S);if(c==="json")return new Response(JSON.stringify(m),{status:n,headers:{"Content-Type":"application/json"}});if(c==="sse"||c==="sse-page"){let g=q(m);return new Response(g,{status:n,headers:{"Content-Type":"text/event-stream"}})}return window._originalFetch(e,t)}}function ue(){}function he(a){typeof window<"u"&&(K(a),void 0)}function ke(a){return{_type:"chat_stream",...a}}function Me(a){return{_type:"history_stream",...a}}var Q={id:"u_001",name:"Admin User",avatar:"https://api.dicebear.com/7.x/avataaars/svg?seed=Admin",role:"admin"},V=[{id:"s_001",title:"2024 Q1 \u9500\u552E\u5206\u6790",date:"2024-03-15",lastMessage:"\u597D\u7684\uFF0C\u6B63\u5728\u4E3A\u60A8\u5206\u6790 Q1 \u9500\u552E\u6570\u636E..."},{id:"s_002",title:"\u7528\u6237\u589E\u957F\u8D8B\u52BF",date:"2024-03-10",lastMessage:"\u7528\u6237\u589E\u957F\u66F2\u7EBF\u663E\u793A..."},{id:"s_003",title:"\u7ADE\u54C1\u5206\u6790\u62A5\u544A",date:"2024-03-08",lastMessage:"\u4E3B\u8981\u7ADE\u54C1 A \u7684\u5E02\u573A\u4EFD\u989D..."},{id:"s_004",title:"\u8425\u9500\u6D3B\u52A8\u590D\u76D8",date:"2024-03-05",lastMessage:"ROI \u63D0\u5347\u4E86 15%..."},{id:"s_005",title:"\u8D22\u52A1\u62A5\u8868\u5BA1\u8BA1",date:"2024-03-01",lastMessage:"\u8BF7\u786E\u8BA4\u4EE5\u4E0B\u8D22\u52A1\u6307\u6807..."}],F=[{id:"fav-1",title:"\u5B63\u5EA6\u9500\u552E\u603B\u7ED3",date:"2025/12/15"},{id:"fav-2",title:"\u5E74\u5EA6\u9884\u7B97\u89C4\u5212",date:"2025/12/10"},{id:"fav-3",title:"\u6838\u5FC3\u6307\u6807\u76D1\u63A7",date:"2025/11/20"}],W=[{id:"1",name:"\u9500\u552E\u6570\u636E\u5206\u6790",description:"\u5168\u65B9\u4F4D\u5206\u6790\u9500\u552E\u4E1A\u7EE9\uFF0C\u5305\u62EC\u533A\u57DF\u3001\u4EA7\u54C1\u3001\u65F6\u95F4\u7EF4\u5EA6",code:"SALES_001",creator:"\u7BA1\u7406\u5458",createTime:"2023-01-01",tableCount:5,kbCount:2,qaCount:10,cover:"https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&q=80"},{id:"2",name:"\u4EBA\u529B\u8D44\u6E90\u6982\u89C8",description:"\u5458\u5DE5\u5165\u804C\u3001\u79BB\u804C\u3001\u7EE9\u6548\u5206\u5E03\u4E0E\u57F9\u8BAD\u60C5\u51B5\u5206\u6790",code:"HR_001",creator:"HR \u7ECF\u7406",createTime:"2023-02-15",tableCount:3,kbCount:1,qaCount:5,cover:"https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=800&q=80"},{id:"3",name:"\u4F9B\u5E94\u94FE\u4F18\u5316",description:"\u5E93\u5B58\u5468\u8F6C\u3001\u7269\u6D41\u6210\u672C\u4E0E\u4F9B\u5E94\u5546\u7EE9\u6548\u8BC4\u4F30",code:"SCM_001",creator:"\u4F9B\u5E94\u94FE\u603B\u76D1",createTime:"2023-03-20",tableCount:8,kbCount:0,qaCount:15,cover:"https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?w=800&q=80"}],Ee=a=>[{key:`msg_${a}_1`,role:"assistant",content:`\u60A8\u597D\uFF01\u6211\u662F\u60A8\u7684\u667A\u80FD\u52A9\u624B\u3002\u5F53\u524D\u4F1A\u8BDD ID: ${a}\u3002\u8BF7\u95EE\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u60A8\uFF1F`,time:L.dayjs(L.now()-1e4).toISOString()}],we={user:Q,sessions:V,favorites:F,scenes:W,favoritesList:F};export{x as ChatStreamStrategy,$ as HistoryStreamStrategy,_ as JsonStrategy,F as MOCK_FAVORITES,W as MOCK_SCENES,V as MOCK_SESSIONS,Q as MOCK_USER,G as MockAdapter,v as MockResponseGenerator,A as SsePageStrategy,I as SseStrategy,w as StrategyFactory,ke as createChatStream,Me as createHistoryStream,q as eventsToStream,T as flatEvents,Ee as generateMockMessages,K as installFetchMock,ue as installSSEMock,we as mockData,C as processTemplate,he as setupMock,k as sleep};
575
+ `];
576
+ }
577
+ for (const chunk of chunks) {
578
+ if (signal && signal.aborted) {
579
+ logger.info("[SSE Abort] Stream aborted by user");
580
+ break;
581
+ }
582
+ if (chunk.trim()) {
583
+ if (onMessage) onMessage(chunk);
584
+ await new Promise((r) => setTimeout(r, 200));
585
+ }
586
+ }
587
+ }
588
+ if (!signal || !signal.aborted) {
589
+ logger.info(
590
+ `[SSE Complete] Stream finished successfully in ${dateUtils2.now() - requestStart}ms`
591
+ );
592
+ if (onFinish) onFinish();
593
+ }
594
+ resolve();
595
+ } catch (error) {
596
+ logger.error(`[SSE Error] Stream processing failed`, error);
597
+ if (onError) onError(error);
598
+ resolve();
599
+ }
600
+ }, delay);
601
+ });
602
+ }
603
+ /**
604
+ * 判断是否为遗留的 schema 格式
605
+ */
606
+ isLegacySchema(schema) {
607
+ return schema && typeof schema === "object" && "_type" in schema;
608
+ }
609
+ /**
610
+ * 判断是否为新版配置格式
611
+ * @description 检查配置对象中是否包含有效的 type 字段
612
+ */
613
+ isNewConfig(config) {
614
+ return config && typeof config === "object" && (["json", "sse", "sse-page"].includes(config.type) || "responseSchema" in config);
615
+ }
616
+ /**
617
+ * 生成遗留的高级 schema 数据块
618
+ */
619
+ generateAdvancedChunks(schema) {
620
+ const strategy = _MockAdapter.strategies[schema._type];
621
+ if (strategy) {
622
+ return strategy.generate(schema);
623
+ }
624
+ logger.warn(`\u672A\u627E\u5230\u5BF9\u5E94\u7684\u7B56\u7565\u7C7B\u578B: ${schema._type}`);
625
+ return [];
626
+ }
627
+ };
628
+
629
+ // src/interceptor.ts
630
+ function installFetchMock(schema) {
631
+ if (!window._originalFetch) {
632
+ window._originalFetch = window.fetch;
633
+ }
634
+ window.fetch = async (input, init = {}) => {
635
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
636
+ const method = (init.method || "GET").toUpperCase();
637
+ const rules = Object.values(schema);
638
+ const rule = rules.find((v) => {
639
+ if (!v.url) return false;
640
+ const pattern = v.url.replace(/:[a-zA-Z0-9_]+/g, "([^/]+)");
641
+ const regex = new RegExp(`^${pattern}$`);
642
+ const [path] = url.split("?");
643
+ const ruleMethod = (v.method || "GET").toUpperCase();
644
+ return regex.test(path) && ruleMethod === method;
645
+ });
646
+ if (!rule) {
647
+ return window._originalFetch(input, init);
648
+ }
649
+ console.log(`[Mock] Intercepted ${method} ${url}`, rule);
650
+ const status = rule.status || 200;
651
+ if (status !== 200) {
652
+ await sleep(rule.delay || 0);
653
+ return new Response(JSON.stringify({
654
+ message: `Mock error status ${status}`,
655
+ code: status
656
+ }), {
657
+ status,
658
+ headers: { "Content-Type": "application/json" }
659
+ });
660
+ }
661
+ await sleep(rule.delay || 0);
662
+ const type = rule.type || "json";
663
+ const strategy = StrategyFactory.getStrategy(type);
664
+ const urlObj = new URL(url, window.location.origin);
665
+ const query = Object.fromEntries(urlObj.searchParams);
666
+ const result = strategy.process(rule, query);
667
+ if (type === "json") {
668
+ return new Response(JSON.stringify(result), {
669
+ status,
670
+ headers: { "Content-Type": "application/json" }
671
+ });
672
+ }
673
+ if (type === "sse" || type === "sse-page") {
674
+ const events = result;
675
+ const stream = eventsToStream(events);
676
+ return new Response(stream, {
677
+ status,
678
+ headers: { "Content-Type": "text/event-stream" }
679
+ });
680
+ }
681
+ return window._originalFetch(input, init);
682
+ };
683
+ }
684
+ function installSSEMock() {
685
+ }
686
+ function setupMock(schema) {
687
+ if (typeof window !== "undefined") {
688
+ installFetchMock(schema);
689
+ installSSEMock();
690
+ }
691
+ }
692
+
693
+ // src/types.ts
694
+ function createChatStream(config) {
695
+ return { _type: "chat_stream", ...config };
696
+ }
697
+ function createHistoryStream(config) {
698
+ return { _type: "history_stream", ...config };
699
+ }
700
+
701
+ // src/index.ts
702
+ var MOCK_USER = {
703
+ id: "u_001",
704
+ name: "Admin User",
705
+ avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Admin",
706
+ role: "admin"
707
+ };
708
+ var MOCK_SESSIONS = [
709
+ { id: "s_001", title: "2024 Q1 \u9500\u552E\u5206\u6790", date: "2024-03-15", lastMessage: "\u597D\u7684\uFF0C\u6B63\u5728\u4E3A\u60A8\u5206\u6790 Q1 \u9500\u552E\u6570\u636E..." },
710
+ { id: "s_002", title: "\u7528\u6237\u589E\u957F\u8D8B\u52BF", date: "2024-03-10", lastMessage: "\u7528\u6237\u589E\u957F\u66F2\u7EBF\u663E\u793A..." },
711
+ { id: "s_003", title: "\u7ADE\u54C1\u5206\u6790\u62A5\u544A", date: "2024-03-08", lastMessage: "\u4E3B\u8981\u7ADE\u54C1 A \u7684\u5E02\u573A\u4EFD\u989D..." },
712
+ { id: "s_004", title: "\u8425\u9500\u6D3B\u52A8\u590D\u76D8", date: "2024-03-05", lastMessage: "ROI \u63D0\u5347\u4E86 15%..." },
713
+ { id: "s_005", title: "\u8D22\u52A1\u62A5\u8868\u5BA1\u8BA1", date: "2024-03-01", lastMessage: "\u8BF7\u786E\u8BA4\u4EE5\u4E0B\u8D22\u52A1\u6307\u6807..." }
714
+ ];
715
+ var MOCK_FAVORITES = [
716
+ { id: "fav-1", title: "\u5B63\u5EA6\u9500\u552E\u603B\u7ED3", date: "2025/12/15" },
717
+ { id: "fav-2", title: "\u5E74\u5EA6\u9884\u7B97\u89C4\u5212", date: "2025/12/10" },
718
+ { id: "fav-3", title: "\u6838\u5FC3\u6307\u6807\u76D1\u63A7", date: "2025/11/20" }
719
+ ];
720
+ var MOCK_SCENES = [
721
+ {
722
+ id: "1",
723
+ name: "\u9500\u552E\u6570\u636E\u5206\u6790",
724
+ description: "\u5168\u65B9\u4F4D\u5206\u6790\u9500\u552E\u4E1A\u7EE9\uFF0C\u5305\u62EC\u533A\u57DF\u3001\u4EA7\u54C1\u3001\u65F6\u95F4\u7EF4\u5EA6",
725
+ code: "SALES_001",
726
+ creator: "\u7BA1\u7406\u5458",
727
+ createTime: "2023-01-01",
728
+ tableCount: 5,
729
+ kbCount: 2,
730
+ qaCount: 10,
731
+ cover: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&q=80"
732
+ },
733
+ {
734
+ id: "2",
735
+ name: "\u4EBA\u529B\u8D44\u6E90\u6982\u89C8",
736
+ description: "\u5458\u5DE5\u5165\u804C\u3001\u79BB\u804C\u3001\u7EE9\u6548\u5206\u5E03\u4E0E\u57F9\u8BAD\u60C5\u51B5\u5206\u6790",
737
+ code: "HR_001",
738
+ creator: "HR \u7ECF\u7406",
739
+ createTime: "2023-02-15",
740
+ tableCount: 3,
741
+ kbCount: 1,
742
+ qaCount: 5,
743
+ cover: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=800&q=80"
744
+ },
745
+ {
746
+ id: "3",
747
+ name: "\u4F9B\u5E94\u94FE\u4F18\u5316",
748
+ description: "\u5E93\u5B58\u5468\u8F6C\u3001\u7269\u6D41\u6210\u672C\u4E0E\u4F9B\u5E94\u5546\u7EE9\u6548\u8BC4\u4F30",
749
+ code: "SCM_001",
750
+ creator: "\u4F9B\u5E94\u94FE\u603B\u76D1",
751
+ createTime: "2023-03-20",
752
+ tableCount: 8,
753
+ kbCount: 0,
754
+ qaCount: 15,
755
+ cover: "https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?w=800&q=80"
756
+ }
757
+ ];
758
+ var generateMockMessages = (sessionId) => [
759
+ {
760
+ key: `msg_${sessionId}_1`,
761
+ role: "assistant",
762
+ content: `\u60A8\u597D\uFF01\u6211\u662F\u60A8\u7684\u667A\u80FD\u52A9\u624B\u3002\u5F53\u524D\u4F1A\u8BDD ID: ${sessionId}\u3002\u8BF7\u95EE\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u60A8\uFF1F`,
763
+ time: dateUtils3.dayjs(dateUtils3.now() - 1e4).toISOString()
764
+ }
765
+ ];
766
+ var mockData = {
767
+ user: MOCK_USER,
768
+ sessions: MOCK_SESSIONS,
769
+ favorites: MOCK_FAVORITES,
770
+ scenes: MOCK_SCENES,
771
+ favoritesList: MOCK_FAVORITES
772
+ // Alias for compatibility if needed
773
+ };
774
+ export {
775
+ ChatStreamStrategy,
776
+ HistoryStreamStrategy,
777
+ JsonStrategy,
778
+ MOCK_FAVORITES,
779
+ MOCK_SCENES,
780
+ MOCK_SESSIONS,
781
+ MOCK_USER,
782
+ MockAdapter,
783
+ MockResponseGenerator,
784
+ SsePageStrategy,
785
+ SseStrategy,
786
+ StrategyFactory,
787
+ createChatStream,
788
+ createHistoryStream,
789
+ eventsToStream,
790
+ flatEvents,
791
+ generateMockMessages,
792
+ installFetchMock,
793
+ installSSEMock,
794
+ mockData,
795
+ processTemplate,
796
+ setupMock,
797
+ sleep
798
+ };
25
799
  //# sourceMappingURL=index.mjs.map