@blueking/chat-helper 0.0.12-beta.1 → 0.0.12-beta.11

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.
@@ -37,7 +37,7 @@
37
37
  }
38
38
  import { transferMessageApi2Message } from '../http/transform/index.ts.js';
39
39
  import { ActivityType, MessageRole, MessageStatus, MessageType } from '../message/index.ts.js';
40
- import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
40
+ import { CustomEventName, EventType, FlowTaskState, RunFinishedOutcomeType } from './type.ts.js';
41
41
  /**
42
42
  * AGUI 协议
43
43
  * @param message - 消息模块
@@ -49,7 +49,15 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
49
49
  /**
50
50
  * 处理活动快照事件
51
51
  * 记录活动的完整状态
52
- */ handleActivitySnapshotEvent(_event) {}
52
+ */ handleActivitySnapshotEvent(event) {
53
+ const message = this.messageModule.getCurrentLoadingMessage();
54
+ if (event.activityType === ActivityType.Interrupt) {
55
+ if ((message === null || message === void 0 ? void 0 : message.role) === MessageRole.Interrupt) {
56
+ message.content = event.content;
57
+ message.status = MessageStatus.Complete;
58
+ }
59
+ }
60
+ }
53
61
  /**
54
62
  * 处理自定义事件
55
63
  */ handleCustomEvent(event) {
@@ -63,6 +71,9 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
63
71
  case CustomEventName.FlowAgentEnd:
64
72
  this.handleFlowAgentEndCustomEvent(event);
65
73
  break;
74
+ case CustomEventName.FlowAgentRestart:
75
+ this.handleFlowAgentRestartCustomEvent(event);
76
+ break;
66
77
  case CustomEventName.ReferenceDocument:
67
78
  this.handleReferenceDocumentCustomEvent(event);
68
79
  break;
@@ -81,6 +92,12 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
81
92
  case CustomEventName.TempMessage:
82
93
  this.handleTempMessageCustomEvent(event);
83
94
  break;
95
+ case CustomEventName.ApprovalResult:
96
+ this.handleApprovalResultCustomEvent(event);
97
+ break;
98
+ case CustomEventName.FlowAgentUpdate:
99
+ this.handleFlowAgentUpdateCustomEvent(event);
100
+ break;
84
101
  default:
85
102
  break;
86
103
  }
@@ -102,6 +119,20 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
102
119
  }
103
120
  }
104
121
  /**
122
+ * 自定义事件 处理流程编排任务重启
123
+ */ handleFlowAgentRestartCustomEvent(event) {
124
+ const value = event.value;
125
+ this.messageModule.list.value.forEach((item)=>{
126
+ if (item.role === MessageRole.Activity && item.activityType === ActivityType.FlowAgent) {
127
+ const isSameTask = item.content[0].task_id === value[0].task_id;
128
+ // 通过第一个 task_id 判断是否是同一个任务,是的话直接更新消息状态为 streaming
129
+ if (isSameTask) {
130
+ item.status = MessageStatus.Streaming;
131
+ }
132
+ }
133
+ });
134
+ }
135
+ /**
105
136
  * 自定义事件 处理流程编排任务结果(增量更新节点状态)
106
137
  */ handleFlowAgentResultCustomEvent(event) {
107
138
  const message = this.messageModule.getCurrentLoadingMessage();
@@ -118,7 +149,7 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
118
149
  activityType: ActivityType.FlowAgent,
119
150
  content: value.map((item)=>({
120
151
  nodes: {},
121
- task_id: Number(item.task_id),
152
+ task_id: item.task_id,
122
153
  task_name: '',
123
154
  task_outputs: [],
124
155
  task_state: FlowTaskState.Created
@@ -127,6 +158,20 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
127
158
  });
128
159
  }
129
160
  /**
161
+ * 自定义事件 处理流程编排任务更新
162
+ */ handleFlowAgentUpdateCustomEvent(event) {
163
+ const value = event.value;
164
+ this.messageModule.list.value.forEach((item)=>{
165
+ if (item.role === MessageRole.Activity && item.activityType === ActivityType.FlowAgent) {
166
+ const isSameTask = item.content[0].task_id === value[0].task_id;
167
+ // 通过第一个 task_id 判断是否是同一个任务,是的话直接更新消息内容
168
+ if (isSameTask) {
169
+ item.content = value;
170
+ }
171
+ }
172
+ });
173
+ }
174
+ /**
130
175
  * 自定义事件 处理意图识别结束
131
176
  */ handleKnowledgeRagEndCustomEvent(_event) {
132
177
  const message = this.messageModule.getCurrentLoadingMessage();
@@ -206,11 +251,24 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
206
251
  }
207
252
  /**
208
253
  * 处理运行完成事件
209
- */ handleRunFinishedEvent(_event) {
254
+ */ handleRunFinishedEvent(event) {
255
+ var _event_outcome, _event_outcome1;
210
256
  const message = this.messageModule.getCurrentLoadingMessage();
211
257
  if (message) {
212
258
  message.status = MessageStatus.Complete;
213
259
  }
260
+ // 如果是中断消息,则创建一个中断消息
261
+ if (((_event_outcome = event.outcome) === null || _event_outcome === void 0 ? void 0 : _event_outcome.type) === RunFinishedOutcomeType.Interrupt) {
262
+ this.messageModule.plusMessage({
263
+ role: MessageRole.Interrupt,
264
+ content: event,
265
+ status: MessageStatus.Pending
266
+ });
267
+ }
268
+ // 二次返回,直接更新消息内容
269
+ if (((_event_outcome1 = event.outcome) === null || _event_outcome1 === void 0 ? void 0 : _event_outcome1.type) === RunFinishedOutcomeType.Success && (message === null || message === void 0 ? void 0 : message.role) === MessageRole.Interrupt) {
270
+ message.content = event;
271
+ }
214
272
  }
215
273
  /**
216
274
  * 处理运行开始事件
@@ -249,6 +307,16 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
249
307
  });
250
308
  }
251
309
  /**
310
+ * 自定义事件 处理审批结果
311
+ */ handleApprovalResultCustomEvent(event) {
312
+ const value = event.value;
313
+ const message = this.messageModule.getCurrentLoadingMessage();
314
+ if (message) {
315
+ message.content = value;
316
+ message.status = MessageStatus.Complete;
317
+ }
318
+ }
319
+ /**
252
320
  * 处理文本消息块事件
253
321
  */ handleTextMessageChunkEvent(event) {
254
322
  const message = this.messageModule.getCurrentLoadingMessage();
@@ -281,7 +349,14 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
281
349
  }
282
350
  /**
283
351
  * 处理文本消息开始事件
352
+ * 断线续传时 DLQ 会重放事件:同 messageId 已存在则重置内容,避免重复气泡/文本翻倍
284
353
  */ handleTextMessageStartEvent(event) {
354
+ const existing = this.messageModule.getMessageByMessageId(event.messageId);
355
+ if (existing) {
356
+ existing.content = '';
357
+ existing.status = MessageStatus.Streaming;
358
+ return;
359
+ }
285
360
  this.messageModule.plusMessage({
286
361
  role: event.role,
287
362
  content: '',
@@ -397,10 +472,10 @@ import { CustomEventName, EventType, FlowTaskState } from './type.ts.js';
397
472
  (_this_onDoneCallback = (_this = this).onDoneCallback) === null || _this_onDoneCallback === void 0 ? void 0 : _this_onDoneCallback.call(_this);
398
473
  }
399
474
  onError(error) {
400
- var // 回调给上层
475
+ var // 在聊天区域显示错误消息
476
+ _this_messageModule, // 回调给上层
401
477
  _this_onErrorCallback, _this;
402
- // 创建一个错误消息
403
- this.messageModule.plusMessage({
478
+ (_this_messageModule = this.messageModule) === null || _this_messageModule === void 0 ? void 0 : _this_messageModule.plusMessage({
404
479
  role: MessageRole.Assistant,
405
480
  content: error.message,
406
481
  status: MessageStatus.Error
@@ -1,14 +1,18 @@
1
1
  import { type IMessageApi, type MessageRole, type MessageStatus } from '../message/type';
2
+ import type { JSONSchema4 } from 'json-schema';
2
3
  export declare enum CustomEventName {
3
4
  FlowAgentEnd = "flow_agent_end",
4
5
  FlowAgentResult = "flow_agent_result",
5
6
  FlowAgentStart = "flow_agent_start",
7
+ FlowAgentUpdate = "flow_agent_update",
8
+ FlowAgentRestart = "flow_agent_restart",
6
9
  KnowledgeRagEnd = "knowledge_rag_end",
7
10
  KnowledgeRagResult = "knowledge_rag_result",
8
11
  KnowledgeRagStart = "knowledge_rag_start",
9
12
  KnowledgeRagTextContent = "knowledge_rag_text_content",
10
13
  ReferenceDocument = "reference_document",
11
- TempMessage = "temp_message"
14
+ TempMessage = "temp_message",
15
+ ApprovalResult = "approval_result"
12
16
  }
13
17
  export declare enum EventType {
14
18
  ActivityDelta = "ACTIVITY_DELTA",
@@ -40,42 +44,51 @@ export declare enum EventType {
40
44
  }
41
45
  /** BKFlow 收敛后的节点状态 - task.node 的状态枚举 */
42
46
  export declare enum FlowNodeState {
43
- /** 失败 */
44
47
  Failed = "failed",
45
- /** 执行中 */
46
48
  Running = "running",
47
- /** 成功 */
48
49
  Success = "success",
49
- /** 挂起 */
50
50
  Suspended = "suspended"
51
51
  }
52
52
  /** BKFLOW 原始状态 - task 的状态枚举 */
53
53
  export declare enum FlowTaskState {
54
- /** 执行中 */
55
54
  Blocked = "BLOCKED",
56
- /** 执行中 */
57
55
  Created = "CREATED",
58
- /** 失败 */
59
56
  Failed = "FAILED",
60
- /** 成功 */
61
57
  Finished = "FINISHED",
62
- /** 执行中 */
63
58
  LoopReady = "LOOP_READY",
64
- /** 执行中 */
65
59
  Ready = "READY",
66
- /** 失败 */
67
60
  Revoked = "REVOKED",
68
- /** 失败 */
69
61
  RollBackFailed = "ROLL_BACK_FAILED",
70
- /** 执行中 */
71
62
  RollBackSuccess = "ROLL_BACK_SUCCESS",
72
- /** 执行中 */
73
63
  RollingBack = "ROLLING_BACK",
74
- /** 执行中 */
75
64
  Running = "RUNNING",
76
- /** 挂起 */
77
65
  Suspended = "SUSPENDED"
78
66
  }
67
+ /** 运行完成结果 */
68
+ export declare enum RunFinishedOutcomeType {
69
+ Success = "success",
70
+ Interrupt = "interrupt"
71
+ }
72
+ /** 审批状态 */
73
+ export declare enum ApprovalInterruptTicketStatus {
74
+ Abandoned = "abandoned",
75
+ Approved = "approved",
76
+ Cancelled = "cancelled",
77
+ Draft = "draft",
78
+ Expired = "expired",
79
+ Rejected = "rejected",
80
+ Pending = "pending"
81
+ }
82
+ /** 中断原因 */
83
+ export declare enum InterruptReason {
84
+ AIDevToolApproval = "aidev:tool_approval",
85
+ UserQuestion = "aidev:user_question"
86
+ }
87
+ /** 恢复状态 */
88
+ export declare enum ResumeStatus {
89
+ Resolved = "resolved",
90
+ Cancelled = "cancelled"
91
+ }
79
92
  /**
80
93
  * Activity 代表 Agent 执行过程中的活动/动作/任务,可能包含结构化数据
81
94
  * 活动状态的增量更新
@@ -112,25 +125,25 @@ export interface IBaseEvent {
112
125
  export interface ICustomEvent extends IBaseEvent {
113
126
  name: CustomEventName;
114
127
  type: EventType.Custom;
115
- value: IFlowAgentEndCustomValue | IFlowAgentResultCustomValue | IFlowAgentStartCustomValue | IKnowledgeRagResultCustomValue | IKnowledgeRagTextContentCustomValue | IReferenceDocumentCustomValue | ITempMessageCustomValue;
128
+ value: IFlowAgentEndCustomValue | IFlowAgentResultCustomValue | IFlowAgentStartCustomValue | IFlowAgentRestartCustomValue | IKnowledgeRagResultCustomValue | IKnowledgeRagTextContentCustomValue | IReferenceDocumentCustomValue | ITempMessageCustomValue | IApprovalResultCustomValue;
116
129
  }
117
130
  export type IEvent = IActivityDeltaEvent | IActivitySnapshotEvent | ICustomEvent | IMessagesSnapshotEvent | IRawEvent | IRunErrorEvent | IRunFinishedEvent | IRunStartedEvent | IStateDeltaEvent | IStateSnapshotEvent | IStepFinishedEvent | IStepStartedEvent | ITextMessageChunkEvent | ITextMessageContentEvent | ITextMessageEndEvent | ITextMessageStartEvent | IThinkingEndEvent | IThinkingStartEvent | IThinkingTextMessageContentEvent | IThinkingTextMessageEndEvent | IThinkingTextMessageStartEvent | IToolCallArgsEvent | IToolCallChunkEvent | IToolCallEndEvent | IToolCallResultEvent | IToolCallStartEvent;
118
131
  export type IFlowAgentEndCustomValue = {
119
132
  task_outputs: string[];
120
133
  }[];
121
134
  export interface IFlowAgentNode {
122
- /** 耗时(s) */
123
135
  elapsed_time: number;
124
- /** 节点id */
125
136
  id: string;
126
- /** 节点名称 */
127
137
  name: string;
128
- /** 节点状态 */
129
138
  state: FlowNodeState;
139
+ retry: number;
140
+ skip: boolean;
141
+ retryable: boolean;
142
+ skippable: boolean;
130
143
  }
131
144
  export type IFlowAgentResultCustomValue = {
132
145
  nodes: Record<string, IFlowAgentNode>;
133
- task_id: number;
146
+ task_id: string;
134
147
  task_name: string;
135
148
  task_outputs: string[];
136
149
  task_state: FlowTaskState;
@@ -138,6 +151,7 @@ export type IFlowAgentResultCustomValue = {
138
151
  export type IFlowAgentStartCustomValue = {
139
152
  task_id: string;
140
153
  }[];
154
+ export type IFlowAgentRestartCustomValue = IFlowAgentStartCustomValue;
141
155
  export type IKnowledgeRagResultCustomValue = IReferenceDocumentCustomValue;
142
156
  export interface IKnowledgeRagTextContentCustomValue {
143
157
  cover: boolean;
@@ -173,15 +187,66 @@ export interface IRunErrorEvent extends IBaseEvent {
173
187
  message: string;
174
188
  type: EventType.RunError;
175
189
  }
190
+ export interface IInterrupt<T extends InterruptReason, P extends Record<string, unknown>> {
191
+ id: string;
192
+ reason: T;
193
+ message?: string;
194
+ toolCallId?: string;
195
+ responseSchema?: JSONSchema4;
196
+ expiresAt?: string;
197
+ metadata?: P;
198
+ }
199
+ export type IApprovalInterrupt = IInterrupt<InterruptReason.AIDevToolApproval, {
200
+ ticket: {
201
+ approvers: string[];
202
+ sn: string;
203
+ status: ApprovalInterruptTicketStatus;
204
+ submit_time: string;
205
+ title: string;
206
+ url: string;
207
+ };
208
+ }>;
209
+ export type IUserQuestionInterrupt = IInterrupt<InterruptReason.UserQuestion, {
210
+ questions: {
211
+ header: string;
212
+ multiSelect?: boolean;
213
+ options?: {
214
+ description: string;
215
+ label: string;
216
+ }[];
217
+ question: string;
218
+ }[];
219
+ }>;
220
+ export interface IResume {
221
+ interruptId: string;
222
+ status: ResumeStatus;
223
+ payload?: {
224
+ answers: {
225
+ answer: {
226
+ description: string;
227
+ label: string;
228
+ }[];
229
+ multiSelect?: boolean;
230
+ question: string;
231
+ }[];
232
+ };
233
+ }
234
+ export type IRunFinishedOutcome = {
235
+ type: RunFinishedOutcomeType.Success;
236
+ } | {
237
+ type: RunFinishedOutcomeType.Interrupt;
238
+ interrupts: Array<IApprovalInterrupt | IUserQuestionInterrupt>;
239
+ };
176
240
  /**
177
241
  * 标记 Agent 运行正常结束
178
242
  * 返回最终结果 result
179
243
  */
180
244
  export interface IRunFinishedEvent extends IBaseEvent {
181
- result?: unknown;
245
+ result?: IResume;
182
246
  runId: number;
183
247
  threadId: string;
184
248
  type: EventType.RunFinished;
249
+ outcome?: IRunFinishedOutcome;
185
250
  }
186
251
  /**
187
252
  * 标记一次完整的 Agent 执行开始
@@ -234,6 +299,7 @@ export interface ITempMessageCustomValue {
234
299
  message: string;
235
300
  status: MessageStatus;
236
301
  }
302
+ export type IApprovalResultCustomValue = IRunFinishedEvent;
237
303
  export interface ITextMessageChunkEvent extends IBaseEvent {
238
304
  delta?: string;
239
305
  messageId: string;
@@ -27,12 +27,15 @@
27
27
  CustomEventName["FlowAgentEnd"] = "flow_agent_end";
28
28
  CustomEventName["FlowAgentResult"] = "flow_agent_result";
29
29
  CustomEventName["FlowAgentStart"] = "flow_agent_start";
30
+ CustomEventName["FlowAgentUpdate"] = "flow_agent_update";
31
+ CustomEventName["FlowAgentRestart"] = "flow_agent_restart";
30
32
  CustomEventName["KnowledgeRagEnd"] = "knowledge_rag_end";
31
33
  CustomEventName["KnowledgeRagResult"] = "knowledge_rag_result";
32
34
  CustomEventName["KnowledgeRagStart"] = "knowledge_rag_start";
33
35
  CustomEventName["KnowledgeRagTextContent"] = "knowledge_rag_text_content";
34
36
  CustomEventName["ReferenceDocument"] = "reference_document";
35
37
  CustomEventName["TempMessage"] = "temp_message";
38
+ CustomEventName["ApprovalResult"] = "approval_result";
36
39
  })(CustomEventName || (CustomEventName = {}));
37
40
  export var EventType;
38
41
  (function(EventType) {
@@ -65,23 +68,48 @@ export var EventType;
65
68
  })(EventType || (EventType = {}));
66
69
  export var FlowNodeState;
67
70
  (function(FlowNodeState) {
68
- /** 失败 */ FlowNodeState["Failed"] = "failed";
69
- /** 执行中 */ FlowNodeState["Running"] = "running";
70
- /** 成功 */ FlowNodeState["Success"] = "success";
71
- /** 挂起 */ FlowNodeState["Suspended"] = "suspended";
71
+ FlowNodeState["Failed"] = "failed";
72
+ FlowNodeState["Running"] = "running";
73
+ FlowNodeState["Success"] = "success";
74
+ FlowNodeState["Suspended"] = "suspended";
72
75
  })(FlowNodeState || (FlowNodeState = {}));
73
76
  export var FlowTaskState;
74
77
  (function(FlowTaskState) {
75
- /** 执行中 */ FlowTaskState["Blocked"] = "BLOCKED";
76
- /** 执行中 */ FlowTaskState["Created"] = "CREATED";
77
- /** 失败 */ FlowTaskState["Failed"] = "FAILED";
78
- /** 成功 */ FlowTaskState["Finished"] = "FINISHED";
79
- /** 执行中 */ FlowTaskState["LoopReady"] = "LOOP_READY";
80
- /** 执行中 */ FlowTaskState["Ready"] = "READY";
81
- /** 失败 */ FlowTaskState["Revoked"] = "REVOKED";
82
- /** 失败 */ FlowTaskState["RollBackFailed"] = "ROLL_BACK_FAILED";
83
- /** 执行中 */ FlowTaskState["RollBackSuccess"] = "ROLL_BACK_SUCCESS";
84
- /** 执行中 */ FlowTaskState["RollingBack"] = "ROLLING_BACK";
85
- /** 执行中 */ FlowTaskState["Running"] = "RUNNING";
86
- /** 挂起 */ FlowTaskState["Suspended"] = "SUSPENDED";
78
+ FlowTaskState["Blocked"] = "BLOCKED";
79
+ FlowTaskState["Created"] = "CREATED";
80
+ FlowTaskState["Failed"] = "FAILED";
81
+ FlowTaskState["Finished"] = "FINISHED";
82
+ FlowTaskState["LoopReady"] = "LOOP_READY";
83
+ FlowTaskState["Ready"] = "READY";
84
+ FlowTaskState["Revoked"] = "REVOKED";
85
+ FlowTaskState["RollBackFailed"] = "ROLL_BACK_FAILED";
86
+ FlowTaskState["RollBackSuccess"] = "ROLL_BACK_SUCCESS";
87
+ FlowTaskState["RollingBack"] = "ROLLING_BACK";
88
+ FlowTaskState["Running"] = "RUNNING";
89
+ FlowTaskState["Suspended"] = "SUSPENDED";
87
90
  })(FlowTaskState || (FlowTaskState = {}));
91
+ export var RunFinishedOutcomeType;
92
+ (function(RunFinishedOutcomeType) {
93
+ RunFinishedOutcomeType["Success"] = "success";
94
+ RunFinishedOutcomeType["Interrupt"] = "interrupt";
95
+ })(RunFinishedOutcomeType || (RunFinishedOutcomeType = {}));
96
+ export var ApprovalInterruptTicketStatus;
97
+ (function(ApprovalInterruptTicketStatus) {
98
+ ApprovalInterruptTicketStatus["Abandoned"] = "abandoned";
99
+ ApprovalInterruptTicketStatus["Approved"] = "approved";
100
+ ApprovalInterruptTicketStatus["Cancelled"] = "cancelled";
101
+ ApprovalInterruptTicketStatus["Draft"] = "draft";
102
+ ApprovalInterruptTicketStatus["Expired"] = "expired";
103
+ ApprovalInterruptTicketStatus["Rejected"] = "rejected";
104
+ ApprovalInterruptTicketStatus["Pending"] = "pending";
105
+ })(ApprovalInterruptTicketStatus || (ApprovalInterruptTicketStatus = {}));
106
+ export var InterruptReason;
107
+ (function(InterruptReason) {
108
+ InterruptReason["AIDevToolApproval"] = "aidev:tool_approval";
109
+ InterruptReason["UserQuestion"] = "aidev:user_question";
110
+ })(InterruptReason || (InterruptReason = {}));
111
+ export var ResumeStatus;
112
+ (function(ResumeStatus) {
113
+ ResumeStatus["Resolved"] = "resolved";
114
+ ResumeStatus["Cancelled"] = "cancelled";
115
+ })(ResumeStatus || (ResumeStatus = {}));
@@ -45,6 +45,13 @@ export interface ISSEProtocol {
45
45
  onMessage?: (event: unknown) => void;
46
46
  onStart?: () => void;
47
47
  }
48
+ /** 全局错误处理器类型 */
49
+ export type GlobalErrorHandler = (error: IRequestError) => void;
50
+ /** 错误处理器配置 */
51
+ export interface ErrorHandlerOptions {
52
+ /** 忽略的 URL 模式(字符串包含匹配或正则) */
53
+ ignoreErrors?: Array<string | RegExp>;
54
+ }
48
55
  interface IInterceptor<T> {
49
56
  fulfilled?: (value: T) => T;
50
57
  rejected?: (error: unknown) => unknown;
@@ -62,7 +69,20 @@ export declare class FetchClient {
62
69
  request: InterceptorManager<IRequestConfig>;
63
70
  response: InterceptorManager<IResponse>;
64
71
  };
72
+ /** 全局错误处理器 */
73
+ private globalErrorHandler?;
74
+ /** 忽略的错误 URL 模式 */
75
+ private ignoreErrorPatterns;
65
76
  constructor(config?: IRequestConfig);
77
+ /**
78
+ * 注册全局错误处理器
79
+ * 所有 HTTP 错误(request 和 streamRequest)都会经过此处理器
80
+ */
81
+ onError(handler: GlobalErrorHandler, options?: ErrorHandlerOptions): void;
82
+ /** 判断是否应忽略该错误 */
83
+ private shouldIgnoreError;
84
+ /** 统一错误分发 */
85
+ private dispatchError;
66
86
  applyResponseErrorInterceptors(error: unknown): unknown;
67
87
  create(config?: IRequestConfig): FetchClient;
68
88
  delete<T = unknown>(url: string, params?: Record<string, unknown>, config?: IRequestConfig): Promise<T>;
@@ -132,6 +132,35 @@ class InterceptorManager {
132
132
  }
133
133
  }
134
134
  export class FetchClient {
135
+ /**
136
+ * 注册全局错误处理器
137
+ * 所有 HTTP 错误(request 和 streamRequest)都会经过此处理器
138
+ */ onError(handler, options) {
139
+ this.globalErrorHandler = handler;
140
+ if (options === null || options === void 0 ? void 0 : options.ignoreErrors) {
141
+ this.ignoreErrorPatterns = options.ignoreErrors;
142
+ }
143
+ }
144
+ /** 判断是否应忽略该错误 */ shouldIgnoreError(error) {
145
+ var _error_config, _error_config1;
146
+ // 同时匹配相对路径和完整 URL
147
+ const url = ((_error_config = error.config) === null || _error_config === void 0 ? void 0 : _error_config.url) || '';
148
+ const baseURL = ((_error_config1 = error.config) === null || _error_config1 === void 0 ? void 0 : _error_config1.baseURL) || '';
149
+ const fullURL = baseURL && !url.startsWith('http') ? baseURL + url : url;
150
+ return this.ignoreErrorPatterns.some((pattern)=>{
151
+ if (typeof pattern === 'string') {
152
+ // 字符串模式:同时检查相对路径和完整 URL
153
+ return url.includes(pattern) || fullURL.includes(pattern);
154
+ }
155
+ // 正则模式:同时检查相对路径和完整 URL
156
+ return pattern.test(url) || pattern.test(fullURL);
157
+ });
158
+ }
159
+ /** 统一错误分发 */ dispatchError(error) {
160
+ if (this.globalErrorHandler && !this.shouldIgnoreError(error)) {
161
+ this.globalErrorHandler(error);
162
+ }
163
+ }
135
164
  applyResponseErrorInterceptors(error) {
136
165
  let rejectedError = error;
137
166
  this.interceptors.response.forEach((interceptor)=>{
@@ -323,11 +352,13 @@ export class FetchClient {
323
352
  if (timeoutId) {
324
353
  clearTimeout(timeoutId);
325
354
  }
355
+ // 用户手动取消请求
326
356
  if (error instanceof Error && error.name === 'AbortError') {
327
- const requestError = createError('Request timeout', requestConfig, 'ECONNABORTED', undefined);
328
- throw _this.applyResponseErrorInterceptors(requestError);
357
+ console.warn('User canceled request', error.message);
358
+ return;
329
359
  }
330
360
  const requestError = error.isAxiosError === true ? error : createError(error.message, requestConfig, error.code, undefined);
361
+ _this.dispatchError(requestError);
331
362
  throw _this.applyResponseErrorInterceptors(requestError);
332
363
  }
333
364
  })();
@@ -340,13 +371,31 @@ export class FetchClient {
340
371
  streamRequest(config) {
341
372
  var _this = this;
342
373
  return _async_to_generator(function*() {
343
- const { url, fetchConfig, requestConfig } = _this.prepareRequest(config, true);
374
+ // 错误去重:标记是否已调用过全局错误处理器
375
+ let errorDispatched = false;
376
+ // 包装 onError 回调,统一调用全局错误处理器
377
+ const originalOnError = config.onError;
378
+ const wrappedOnError = (error)=>{
379
+ if (!errorDispatched) {
380
+ // 将 requestConfig 附加到错误对象,以便 shouldIgnoreError 能匹配 URL
381
+ const errorWithConfig = error;
382
+ if (!errorWithConfig.config) {
383
+ errorWithConfig.config = requestConfig;
384
+ }
385
+ _this.dispatchError(errorWithConfig);
386
+ errorDispatched = true;
387
+ }
388
+ originalOnError === null || originalOnError === void 0 ? void 0 : originalOnError(error);
389
+ };
390
+ const wrappedConfig = _object_spread_props(_object_spread({}, config), {
391
+ onError: wrappedOnError
392
+ });
393
+ const { url, fetchConfig, requestConfig } = _this.prepareRequest(wrappedConfig, true);
344
394
  try {
345
- var _config_onStart, _fetchResponse_body;
395
+ var _wrappedConfig_onStart, _fetchResponse_body;
346
396
  const fetchResponse = yield fetch(url, fetchConfig);
347
397
  const validateStatus = requestConfig.validateStatus || _this.defaults.validateStatus;
348
398
  if (!validateStatus(fetchResponse.status)) {
349
- var _config_onError;
350
399
  let message = `Request failed with status code ${fetchResponse.status}`;
351
400
  try {
352
401
  var _errorData_error;
@@ -358,15 +407,14 @@ export class FetchClient {
358
407
  message = `Request failed with status code ${fetchResponse.status}`;
359
408
  }
360
409
  const error = createError(message, requestConfig, `ERR_BAD_RESPONSE`, undefined);
361
- (_config_onError = config.onError) === null || _config_onError === void 0 ? void 0 : _config_onError.call(config, error);
410
+ wrappedOnError(error);
362
411
  return;
363
412
  }
364
- (_config_onStart = config.onStart) === null || _config_onStart === void 0 ? void 0 : _config_onStart.call(config);
413
+ (_wrappedConfig_onStart = wrappedConfig.onStart) === null || _wrappedConfig_onStart === void 0 ? void 0 : _wrappedConfig_onStart.call(wrappedConfig);
365
414
  const reader = (_fetchResponse_body = fetchResponse.body) === null || _fetchResponse_body === void 0 ? void 0 : _fetchResponse_body.pipeThrough(new window.TextDecoderStream()).getReader();
366
415
  if (!reader) {
367
- var _config_onError1;
368
416
  const error = new Error('IResponse body is not readable');
369
- (_config_onError1 = config.onError) === null || _config_onError1 === void 0 ? void 0 : _config_onError1.call(config, error);
417
+ wrappedOnError(error);
370
418
  return;
371
419
  }
372
420
  // 缓存跨行分片数据
@@ -383,17 +431,17 @@ export class FetchClient {
383
431
  while(true){
384
432
  const { value, done } = yield reader.read();
385
433
  if (done) {
386
- var _config_onDone;
387
- (_config_onDone = config.onDone) === null || _config_onDone === void 0 ? void 0 : _config_onDone.call(config);
434
+ var _wrappedConfig_onDone;
435
+ (_wrappedConfig_onDone = wrappedConfig.onDone) === null || _wrappedConfig_onDone === void 0 ? void 0 : _wrappedConfig_onDone.call(wrappedConfig);
388
436
  break;
389
437
  }
390
438
  const values = (temp + value.toString()).split('\n');
391
439
  values.forEach((value)=>{
392
440
  const item = value.replace('data:', '').trim();
393
441
  if (isJson(item)) {
394
- var _config_onMessage;
442
+ var _wrappedConfig_onMessage;
395
443
  const json = JSON.parse(item);
396
- (_config_onMessage = config.onMessage) === null || _config_onMessage === void 0 ? void 0 : _config_onMessage.call(config, json);
444
+ (_wrappedConfig_onMessage = wrappedConfig.onMessage) === null || _wrappedConfig_onMessage === void 0 ? void 0 : _wrappedConfig_onMessage.call(wrappedConfig, json);
397
445
  temp = '';
398
446
  } else if (item) {
399
447
  temp = item;
@@ -403,12 +451,11 @@ export class FetchClient {
403
451
  } catch (error) {
404
452
  if (error instanceof Error) {
405
453
  if (error.name === 'AbortError') {
406
- var _config_onDone1;
407
- (_config_onDone1 = config.onDone) === null || _config_onDone1 === void 0 ? void 0 : _config_onDone1.call(config);
408
- } else {
409
- var _config_onError2;
410
- (_config_onError2 = config.onError) === null || _config_onError2 === void 0 ? void 0 : _config_onError2.call(config, error);
454
+ var _wrappedConfig_onDone1;
455
+ (_wrappedConfig_onDone1 = wrappedConfig.onDone) === null || _wrappedConfig_onDone1 === void 0 ? void 0 : _wrappedConfig_onDone1.call(wrappedConfig);
456
+ return;
411
457
  }
458
+ wrappedOnError(error);
412
459
  }
413
460
  throw error;
414
461
  }
@@ -417,6 +464,8 @@ export class FetchClient {
417
464
  constructor(config = {}){
418
465
  _define_property(this, "defaults", void 0);
419
466
  _define_property(this, "interceptors", void 0);
467
+ /** 全局错误处理器 */ _define_property(this, "globalErrorHandler", void 0);
468
+ /** 忽略的错误 URL 模式 */ _define_property(this, "ignoreErrorPatterns", []);
420
469
  this.defaults = mergeConfig({
421
470
  method: 'GET',
422
471
  headers: {