@zhin.js/client 1.0.16 → 1.0.18

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.
Files changed (76) hide show
  1. package/client/app.ts +228 -0
  2. package/client/index.ts +42 -14
  3. package/client/websocket/hooks.ts +218 -510
  4. package/client/websocket/index.ts +6 -43
  5. package/client/websocket/instance.ts +10 -32
  6. package/client/websocket/manager.ts +173 -390
  7. package/client/websocket/types.ts +58 -222
  8. package/dist/app.d.ts +95 -0
  9. package/dist/app.d.ts.map +1 -0
  10. package/dist/app.js +147 -0
  11. package/dist/app.js.map +1 -0
  12. package/dist/index.d.ts +7 -7
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +10 -14
  15. package/dist/index.js.map +1 -1
  16. package/dist/websocket/hooks.d.ts +35 -58
  17. package/dist/websocket/hooks.d.ts.map +1 -1
  18. package/dist/websocket/hooks.js +134 -410
  19. package/dist/websocket/hooks.js.map +1 -1
  20. package/dist/websocket/index.d.ts +5 -10
  21. package/dist/websocket/index.d.ts.map +1 -1
  22. package/dist/websocket/index.js +5 -25
  23. package/dist/websocket/index.js.map +1 -1
  24. package/dist/websocket/instance.d.ts +1 -14
  25. package/dist/websocket/instance.d.ts.map +1 -1
  26. package/dist/websocket/instance.js +2 -20
  27. package/dist/websocket/instance.js.map +1 -1
  28. package/dist/websocket/manager.d.ts +23 -93
  29. package/dist/websocket/manager.d.ts.map +1 -1
  30. package/dist/websocket/manager.js +87 -227
  31. package/dist/websocket/manager.js.map +1 -1
  32. package/dist/websocket/types.d.ts +35 -122
  33. package/dist/websocket/types.d.ts.map +1 -1
  34. package/dist/websocket/types.js +4 -13
  35. package/dist/websocket/types.js.map +1 -1
  36. package/package.json +12 -11
  37. package/client/router/index.tsx +0 -54
  38. package/client/store/index.ts +0 -132
  39. package/client/store/reducers/config.ts +0 -135
  40. package/client/store/reducers/index.ts +0 -19
  41. package/client/store/reducers/route.ts +0 -126
  42. package/client/store/reducers/script.ts +0 -109
  43. package/client/store/reducers/ui.ts +0 -31
  44. package/client/websocket/messageHandler.ts +0 -208
  45. package/dist/router/index.d.ts +0 -25
  46. package/dist/router/index.d.ts.map +0 -1
  47. package/dist/router/index.js +0 -48
  48. package/dist/router/index.js.map +0 -1
  49. package/dist/store/index.d.ts +0 -19
  50. package/dist/store/index.d.ts.map +0 -1
  51. package/dist/store/index.js +0 -67
  52. package/dist/store/index.js.map +0 -1
  53. package/dist/store/reducers/config.d.ts +0 -54
  54. package/dist/store/reducers/config.d.ts.map +0 -1
  55. package/dist/store/reducers/config.js +0 -78
  56. package/dist/store/reducers/config.js.map +0 -1
  57. package/dist/store/reducers/index.d.ts +0 -13
  58. package/dist/store/reducers/index.d.ts.map +0 -1
  59. package/dist/store/reducers/index.js +0 -11
  60. package/dist/store/reducers/index.js.map +0 -1
  61. package/dist/store/reducers/route.d.ts +0 -30
  62. package/dist/store/reducers/route.d.ts.map +0 -1
  63. package/dist/store/reducers/route.js +0 -85
  64. package/dist/store/reducers/route.js.map +0 -1
  65. package/dist/store/reducers/script.d.ts +0 -13
  66. package/dist/store/reducers/script.d.ts.map +0 -1
  67. package/dist/store/reducers/script.js +0 -78
  68. package/dist/store/reducers/script.js.map +0 -1
  69. package/dist/store/reducers/ui.d.ts +0 -8
  70. package/dist/store/reducers/ui.d.ts.map +0 -1
  71. package/dist/store/reducers/ui.js +0 -23
  72. package/dist/store/reducers/ui.js.map +0 -1
  73. package/dist/websocket/messageHandler.d.ts +0 -58
  74. package/dist/websocket/messageHandler.d.ts.map +0 -1
  75. package/dist/websocket/messageHandler.js +0 -174
  76. package/dist/websocket/messageHandler.js.map +0 -1
@@ -1,510 +1,293 @@
1
- /**
2
- * WebSocket 核心管理器
3
- * 负责 WebSocket 连接管理、消息发送、重连逻辑等核心功能
4
- */
5
-
6
- import { store, setConnected, updateConfigs, updateSchemas } from '../store'
7
- import { MessageHandler } from './messageHandler'
8
1
  import type {
9
2
  WebSocketMessage,
10
3
  WebSocketConfig,
11
4
  WebSocketCallbacks,
12
- FileTreeNode,
13
- DatabaseInfo,
14
- TableInfo,
15
- SelectResult,
16
- KvEntry
17
- } from './types'
5
+ } from "./types";
18
6
  import {
19
7
  ConnectionState,
20
8
  WebSocketError,
21
9
  ConnectionError,
22
10
  MessageError,
23
- RequestTimeoutError
24
- } from './types'
11
+ RequestTimeoutError,
12
+ } from "./types";
25
13
 
26
14
  export class WebSocketManager {
27
- // ============================================================================
28
- // 私有属性
29
- // ============================================================================
30
-
31
- private ws: WebSocket | null = null
32
- private config: Required<WebSocketConfig>
33
- private callbacks: WebSocketCallbacks
34
- private state: ConnectionState = ConnectionState.DISCONNECTED
35
-
36
- // 重连相关
37
- private reconnectAttempts = 0
38
- private reconnectTimer: NodeJS.Timeout | null = null
39
-
40
- // 请求管理
41
- private requestId = 0
42
- private pendingRequests = new Map<number, {
43
- resolve: (value: any) => void
44
- reject: (error: Error) => void
45
- timer: NodeJS.Timeout
46
- }>()
47
-
48
- // ============================================================================
49
- // 构造函数
50
- // ============================================================================
15
+ private ws: WebSocket | null = null;
16
+ private config: Required<WebSocketConfig>;
17
+ private callbacks: WebSocketCallbacks;
18
+ private state: ConnectionState = ConnectionState.DISCONNECTED;
19
+ private reconnectAttempts = 0;
20
+ private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
21
+ private requestId = 0;
22
+ private pendingRequests = new Map<
23
+ number,
24
+ { resolve: (value: unknown) => void; reject: (error: Error) => void; timer: ReturnType<typeof setTimeout> }
25
+ >();
26
+ private connectedListeners = new Set<(connected: boolean) => void>();
51
27
 
52
28
  constructor(config: WebSocketConfig = {}, callbacks: WebSocketCallbacks = {}) {
53
29
  this.config = {
54
30
  url: this.buildWebSocketUrl(config.url),
55
31
  reconnectInterval: config.reconnectInterval ?? 3000,
56
32
  maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
57
- requestTimeout: config.requestTimeout ?? 10000
58
- }
59
- this.callbacks = callbacks
33
+ requestTimeout: config.requestTimeout ?? 10000,
34
+ };
35
+ this.callbacks = callbacks;
60
36
  }
61
37
 
62
- // ============================================================================
63
- // 公共 API
64
- // ============================================================================
38
+ onConnectionChange(listener: (connected: boolean) => void): () => void {
39
+ this.connectedListeners.add(listener);
40
+ return () => this.connectedListeners.delete(listener);
41
+ }
65
42
 
66
- /**
67
- * 建立 WebSocket 连接
68
- */
69
- connect(): void {
70
- if (this.state === ConnectionState.CONNECTED || this.state === ConnectionState.CONNECTING) {
71
- return
72
- }
43
+ private notifyConnection(connected: boolean) {
44
+ for (const l of this.connectedListeners) l(connected);
45
+ }
73
46
 
74
- this.setState(ConnectionState.CONNECTING)
75
-
47
+ connect(): void {
48
+ if (this.state === ConnectionState.CONNECTED || this.state === ConnectionState.CONNECTING) return;
49
+ this.setState(ConnectionState.CONNECTING);
76
50
  try {
77
- this.ws = new WebSocket(this.config.url)
78
- this.attachEventHandlers()
51
+ this.ws = new WebSocket(this.config.url);
52
+ this.attachEventHandlers();
79
53
  } catch (error) {
80
- this.handleConnectionError(new ConnectionError('Failed to create WebSocket', error as Error))
54
+ this.handleConnectionError(new ConnectionError("Failed to create WebSocket", error as Error));
81
55
  }
82
56
  }
83
57
 
84
- /**
85
- * 断开 WebSocket 连接
86
- */
87
58
  disconnect(): void {
88
- this.clearReconnectTimer()
89
- this.clearPendingRequests()
90
-
59
+ this.clearReconnectTimer();
60
+ this.clearPendingRequests();
91
61
  if (this.ws) {
92
- this.ws.close()
93
- this.ws = null
62
+ this.ws.close();
63
+ this.ws = null;
94
64
  }
95
-
96
- this.setState(ConnectionState.DISCONNECTED)
97
- store.dispatch(setConnected(false))
65
+ this.setState(ConnectionState.DISCONNECTED);
66
+ this.notifyConnection(false);
98
67
  }
99
68
 
100
- /**
101
- * 发送普通消息
102
- */
103
- send(message: any): void {
104
- if (!this.isConnected()) {
105
- throw new WebSocketError('WebSocket is not connected', 'NOT_CONNECTED')
106
- }
107
-
69
+ send(message: unknown): void {
70
+ if (!this.isConnected()) throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
108
71
  try {
109
- this.ws!.send(JSON.stringify(message))
72
+ this.ws!.send(JSON.stringify(message));
110
73
  } catch (error) {
111
- throw new MessageError('Failed to send message', error as Error)
74
+ throw new MessageError("Failed to send message", error as Error);
112
75
  }
113
76
  }
114
77
 
115
- /**
116
- * 发送请求并等待响应
117
- */
118
- async sendRequest<T = any>(message: any): Promise<T> {
119
- if (!this.isConnected()) {
120
- throw new WebSocketError('WebSocket is not connected', 'NOT_CONNECTED')
121
- }
122
-
78
+ async sendRequest<T = unknown>(message: unknown): Promise<T> {
79
+ if (!this.isConnected()) throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
123
80
  return new Promise((resolve, reject) => {
124
- const requestId = ++this.requestId
125
- const messageWithId = { ...message, requestId }
126
-
127
- // 设置超时计时器
81
+ const requestId = ++this.requestId;
82
+ const messageWithId = { ...(message as Record<string, unknown>), requestId };
128
83
  const timer = setTimeout(() => {
129
- this.pendingRequests.delete(requestId)
130
- reject(new RequestTimeoutError(requestId))
131
- }, this.config.requestTimeout)
132
-
133
- // 存储请求信息
134
- this.pendingRequests.set(requestId, { resolve, reject, timer })
135
-
84
+ this.pendingRequests.delete(requestId);
85
+ reject(new RequestTimeoutError(requestId));
86
+ }, this.config.requestTimeout);
87
+ this.pendingRequests.set(requestId, { resolve: resolve as (v: unknown) => void, reject, timer });
136
88
  try {
137
- this.ws!.send(JSON.stringify(messageWithId))
89
+ this.ws!.send(JSON.stringify(messageWithId));
138
90
  } catch (error) {
139
- this.pendingRequests.delete(requestId)
140
- clearTimeout(timer)
141
- reject(new MessageError('Failed to send request', error as Error))
91
+ this.pendingRequests.delete(requestId);
92
+ clearTimeout(timer);
93
+ reject(new MessageError("Failed to send request", error as Error));
142
94
  }
143
- })
95
+ });
144
96
  }
145
97
 
146
- /**
147
- * 检查连接状态
148
- */
149
98
  isConnected(): boolean {
150
- return this.ws?.readyState === WebSocket.OPEN
99
+ return this.ws?.readyState === WebSocket.OPEN;
151
100
  }
152
101
 
153
- /**
154
- * 获取当前状态
155
- */
156
102
  getState(): ConnectionState {
157
- return this.state
103
+ return this.state;
158
104
  }
159
105
 
160
- // ============================================================================
161
- // 配置管理 API
162
- // ============================================================================
163
-
164
- /**
165
- * 获取插件配置
166
- */
167
- async getConfig(pluginName: string): Promise<any> {
168
- return this.sendRequest({
169
- type: 'config:get',
170
- pluginName
171
- })
106
+ async getConfig(pluginName: string) {
107
+ return this.sendRequest<unknown>({ type: "config:get", pluginName });
172
108
  }
173
-
174
- /**
175
- * 设置插件配置
176
- */
177
- async setConfig(pluginName: string, config: any): Promise<{ success?: boolean; reloaded?: boolean; message?: string }> {
178
- return this.sendRequest({
179
- type: 'config:set',
180
- pluginName,
181
- data: config
182
- })
109
+ async setConfig(pluginName: string, config: unknown) {
110
+ return this.sendRequest<{ success?: boolean; reloaded?: boolean; message?: string }>({ type: "config:set", pluginName, data: config });
183
111
  }
184
-
185
- /**
186
- * 获取插件 Schema
187
- */
188
- async getSchema(pluginName: string): Promise<any> {
189
- return this.sendRequest({
190
- type: 'schema:get',
191
- pluginName
192
- })
112
+ async getSchema(pluginName: string) {
113
+ return this.sendRequest<unknown>({ type: "schema:get", pluginName });
193
114
  }
194
-
195
- /**
196
- * 获取所有配置
197
- */
198
- async getAllConfigs(): Promise<Record<string, any>> {
199
- return this.sendRequest({
200
- type: 'config:get-all'
201
- })
115
+ async getAllConfigs() {
116
+ return this.sendRequest<Record<string, unknown>>({ type: "config:get-all" });
202
117
  }
203
-
204
- /**
205
- * 获取所有 Schema
206
- */
207
- async getAllSchemas(): Promise<Record<string, any>> {
208
- return this.sendRequest({
209
- type: 'schema:get-all'
210
- })
118
+ async getAllSchemas() {
119
+ return this.sendRequest<Record<string, unknown>>({ type: "schema:get-all" });
211
120
  }
212
-
213
- // ============================================================================
214
- // 配置文件原始 YAML 读写
215
- // ============================================================================
216
-
217
- async getConfigYaml(): Promise<{ yaml: string; pluginKeys: string[] }> {
218
- return this.sendRequest({ type: 'config:get-yaml' })
121
+ async getConfigYaml() {
122
+ return this.sendRequest<{ yaml: string; pluginKeys: string[] }>({ type: "config:get-yaml" });
219
123
  }
220
-
221
- async saveConfigYaml(yaml: string): Promise<{ success: boolean; message?: string }> {
222
- return this.sendRequest({ type: 'config:save-yaml', yaml })
124
+ async saveConfigYaml(yaml: string) {
125
+ return this.sendRequest<{ success: boolean; message?: string }>({ type: "config:save-yaml", yaml });
223
126
  }
224
-
225
- // ============================================================================
226
- // 环境变量文件管理
227
- // ============================================================================
228
-
229
- async getEnvList(): Promise<{ files: Array<{ name: string; exists: boolean }> }> {
230
- return this.sendRequest({ type: 'env:list' })
127
+ async getEnvList() {
128
+ return this.sendRequest<{ files: Array<{ name: string; exists: boolean }> }>({ type: "env:list" });
231
129
  }
232
-
233
- async getEnvFile(filename: string): Promise<{ content: string }> {
234
- return this.sendRequest({ type: 'env:get', filename })
130
+ async getEnvFile(filename: string) {
131
+ return this.sendRequest<{ content: string }>({ type: "env:get", filename });
235
132
  }
236
-
237
- async saveEnvFile(filename: string, content: string): Promise<{ success: boolean; message?: string }> {
238
- return this.sendRequest({ type: 'env:save', filename, content })
133
+ async saveEnvFile(filename: string, content: string) {
134
+ return this.sendRequest<{ success: boolean; message?: string }>({ type: "env:save", filename, content });
239
135
  }
240
-
241
- // ============================================================================
242
- // 文件管理
243
- // ============================================================================
244
-
245
- async getFileTree(): Promise<{ tree: FileTreeNode[] }> {
246
- return this.sendRequest({ type: 'files:tree' })
136
+ async getFileTree() {
137
+ return this.sendRequest<{ tree: import("./types").FileTreeNode[] }>({ type: "files:tree" });
247
138
  }
248
-
249
- async readFile(filePath: string): Promise<{ content: string; size: number }> {
250
- return this.sendRequest({ type: 'files:read', filePath })
139
+ async readFile(filePath: string) {
140
+ return this.sendRequest<{ content: string; size: number }>({ type: "files:read", filePath });
251
141
  }
252
-
253
- async saveFile(filePath: string, content: string): Promise<{ success: boolean; message?: string }> {
254
- return this.sendRequest({ type: 'files:save', filePath, content })
142
+ async saveFile(filePath: string, content: string) {
143
+ return this.sendRequest<{ success: boolean; message?: string }>({ type: "files:save", filePath, content });
255
144
  }
256
-
257
- // ============================================================================
258
- // 数据库管理
259
- // ============================================================================
260
-
261
- async getDbInfo(): Promise<DatabaseInfo> {
262
- return this.sendRequest({ type: 'db:info' })
145
+ async getDbInfo() {
146
+ return this.sendRequest<import("./types").DatabaseInfo>({ type: "db:info" });
263
147
  }
264
-
265
- async getDbTables(): Promise<{ tables: TableInfo[] }> {
266
- return this.sendRequest({ type: 'db:tables' })
148
+ async getDbTables() {
149
+ return this.sendRequest<{ tables: import("./types").TableInfo[] }>({ type: "db:tables" });
267
150
  }
268
-
269
- async dbSelect(table: string, page?: number, pageSize?: number, where?: any): Promise<SelectResult> {
270
- return this.sendRequest({ type: 'db:select', table, page, pageSize, where })
151
+ async dbSelect(table: string, page?: number, pageSize?: number, where?: unknown) {
152
+ return this.sendRequest<import("./types").SelectResult>({ type: "db:select", table, page, pageSize, where });
271
153
  }
272
-
273
- async dbInsert(table: string, row: any): Promise<{ success: boolean }> {
274
- return this.sendRequest({ type: 'db:insert', table, row })
154
+ async dbInsert(table: string, row: unknown) {
155
+ return this.sendRequest<{ success: boolean }>({ type: "db:insert", table, row });
275
156
  }
276
-
277
- async dbUpdate(table: string, row: any, where: any): Promise<{ success: boolean; affected: number }> {
278
- return this.sendRequest({ type: 'db:update', table, row, where })
157
+ async dbUpdate(table: string, row: unknown, where: unknown) {
158
+ return this.sendRequest<{ success: boolean; affected: number }>({ type: "db:update", table, row, where });
279
159
  }
280
-
281
- async dbDelete(table: string, where: any): Promise<{ success: boolean; deleted: number }> {
282
- return this.sendRequest({ type: 'db:delete', table, where })
160
+ async dbDelete(table: string, where: unknown) {
161
+ return this.sendRequest<{ success: boolean; deleted: number }>({ type: "db:delete", table, where });
283
162
  }
284
-
285
- async dbDropTable(table: string): Promise<{ success: boolean }> {
286
- return this.sendRequest({ type: 'db:drop-table', table })
163
+ async dbDropTable(table: string) {
164
+ return this.sendRequest<{ success: boolean }>({ type: "db:drop-table", table });
287
165
  }
288
-
289
- async kvGet(table: string, key: string): Promise<{ key: string; value: any }> {
290
- return this.sendRequest({ type: 'db:kv:get', table, key })
166
+ async kvGet(table: string, key: string) {
167
+ return this.sendRequest<{ key: string; value: unknown }>({ type: "db:kv:get", table, key });
291
168
  }
292
-
293
- async kvSet(table: string, key: string, value: any, ttl?: number): Promise<{ success: boolean }> {
294
- return this.sendRequest({ type: 'db:kv:set', table, key, value, ttl })
169
+ async kvSet(table: string, key: string, value: unknown, ttl?: number) {
170
+ return this.sendRequest<{ success: boolean }>({ type: "db:kv:set", table, key, value, ttl });
295
171
  }
296
-
297
- async kvDelete(table: string, key: string): Promise<{ success: boolean }> {
298
- return this.sendRequest({ type: 'db:kv:delete', table, key })
172
+ async kvDelete(table: string, key: string) {
173
+ return this.sendRequest<{ success: boolean }>({ type: "db:kv:delete", table, key });
299
174
  }
300
-
301
- async kvGetEntries(table: string): Promise<{ entries: KvEntry[] }> {
302
- return this.sendRequest({ type: 'db:kv:entries', table })
175
+ async kvGetEntries(table: string) {
176
+ return this.sendRequest<{ entries: import("./types").KvEntry[] }>({ type: "db:kv:entries", table });
303
177
  }
304
178
 
305
- // ============================================================================
306
- // 私有方法
307
- // ============================================================================
308
-
309
- /**
310
- * 构建 WebSocket URL
311
- */
312
179
  private buildWebSocketUrl(customUrl?: string): string {
313
- if (customUrl) {
314
- return customUrl
315
- }
316
-
317
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
318
- const host = window.location.host
319
- return `${protocol}//${host}/server`
180
+ if (customUrl) return customUrl;
181
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
182
+ return `${protocol}//${window.location.host}/server`;
320
183
  }
321
184
 
322
- /**
323
- * 设置连接状态
324
- */
325
185
  private setState(newState: ConnectionState): void {
326
- const oldState = this.state
327
- this.state = newState
328
-
329
- console.debug(`[WebSocket] State changed: ${oldState} -> ${newState}`)
186
+ this.state = newState;
330
187
  }
331
188
 
332
- /**
333
- * 绑定事件处理器
334
- */
335
189
  private attachEventHandlers(): void {
336
- if (!this.ws) return
337
-
338
- this.ws.onopen = () => {
339
- this.handleConnectionOpen()
340
- }
341
-
342
- this.ws.onmessage = (event) => {
343
- this.handleMessage(event)
344
- }
345
-
346
- this.ws.onclose = (event) => {
347
- this.handleConnectionClose(event)
348
- }
349
-
350
- this.ws.onerror = (event) => {
351
- this.handleConnectionError(new ConnectionError('WebSocket error', event as any))
352
- }
190
+ if (!this.ws) return;
191
+ this.ws.onopen = () => this.handleConnectionOpen();
192
+ this.ws.onmessage = (event) => this.handleMessage(event);
193
+ this.ws.onclose = () => this.handleConnectionClose();
194
+ this.ws.onerror = (event) => this.handleConnectionError(new ConnectionError("WebSocket error", event as unknown as Error));
353
195
  }
354
196
 
355
- /**
356
- * 处理连接打开
357
- */
358
197
  private handleConnectionOpen(): void {
359
- this.setState(ConnectionState.CONNECTED)
360
- this.reconnectAttempts = 0
361
-
362
- // 更新 Redux 状态
363
- store.dispatch(setConnected(true))
364
-
365
- // 初始化数据
366
- this.initializeData()
367
-
368
- // 触发回调
369
- this.callbacks.onConnect?.()
198
+ this.setState(ConnectionState.CONNECTED);
199
+ this.reconnectAttempts = 0;
200
+ this.notifyConnection(true);
201
+ this.callbacks.onConnect?.();
370
202
  }
371
203
 
372
- /**
373
- * 处理消息接收
374
- */
375
204
  private handleMessage(event: MessageEvent): void {
376
205
  try {
377
- const message: WebSocketMessage = JSON.parse(event.data)
378
-
379
- // 处理请求响应
206
+ const message = JSON.parse(event.data) as WebSocketMessage;
380
207
  if (message.requestId && this.pendingRequests.has(message.requestId)) {
381
- this.handleRequestResponse(message)
382
- return
208
+ this.handleRequestResponse(message);
209
+ return;
383
210
  }
384
-
385
- // 处理广播消息
386
- MessageHandler.handle(message)
387
- this.callbacks.onMessage?.(message)
388
-
211
+ this.handleBroadcast(message);
212
+ this.callbacks.onMessage?.(message);
389
213
  } catch (error) {
390
- console.error('[WebSocket] Message parsing error:', error)
214
+ console.error("[WebSocket] Message parsing error:", error);
391
215
  }
392
216
  }
393
217
 
394
- /**
395
- * 处理请求响应
396
- */
397
- private handleRequestResponse(message: any): void {
398
- const { requestId } = message
399
- const pendingRequest = this.pendingRequests.get(requestId)
400
-
401
- if (!pendingRequest) {
402
- return
218
+ private handleBroadcast(message: WebSocketMessage): void {
219
+ const t = message.type;
220
+ if (t === "bot:request" || t === "bot:notice" || t === "bot:message") {
221
+ if (typeof window !== "undefined") {
222
+ window.dispatchEvent(new CustomEvent("zhin-console-bot-push", { detail: message }));
223
+ }
224
+ return;
403
225
  }
226
+ if (t === "hmr:reload") {
227
+ console.info(`[HMR] File changed, reloading...`);
228
+ window.location.reload();
229
+ return;
230
+ }
231
+ if (t === "system:restarting") {
232
+ console.info("[System] Server restarting, will reload shortly...");
233
+ setTimeout(() => window.location.reload(), 3000);
234
+ return;
235
+ }
236
+ }
404
237
 
405
- this.pendingRequests.delete(requestId)
406
- clearTimeout(pendingRequest.timer)
407
-
238
+ private handleRequestResponse(message: WebSocketMessage): void {
239
+ const { requestId } = message;
240
+ const pending = this.pendingRequests.get(requestId!);
241
+ if (!pending) return;
242
+ this.pendingRequests.delete(requestId!);
243
+ clearTimeout(pending.timer);
408
244
  if (message.error) {
409
- pendingRequest.reject(new WebSocketError(message.error, 'SERVER_ERROR'))
245
+ pending.reject(new WebSocketError(message.error, "SERVER_ERROR"));
410
246
  } else {
411
- pendingRequest.resolve(message.data)
247
+ pending.resolve(message.data);
412
248
  }
413
249
  }
414
250
 
415
- /**
416
- * 处理连接关闭
417
- */
418
- private handleConnectionClose(event: CloseEvent): void {
419
- this.ws = null
420
- store.dispatch(setConnected(false))
421
-
422
- if (this.state === ConnectionState.DISCONNECTED) {
423
- // 主动断开,不重连
424
- return
425
- }
426
-
427
- this.setState(ConnectionState.RECONNECTING)
428
- this.callbacks.onDisconnect?.()
429
- this.scheduleReconnect()
251
+ private handleConnectionClose(): void {
252
+ this.ws = null;
253
+ this.notifyConnection(false);
254
+ if (this.state === ConnectionState.DISCONNECTED) return;
255
+ this.setState(ConnectionState.RECONNECTING);
256
+ this.callbacks.onDisconnect?.();
257
+ this.scheduleReconnect();
430
258
  }
431
259
 
432
- /**
433
- * 处理连接错误
434
- */
435
260
  private handleConnectionError(error: Error): void {
436
- this.clearReconnectTimer()
437
- this.setState(ConnectionState.ERROR)
438
- console.error('[WebSocket] Connection error:', error)
439
- this.callbacks.onError?.(error as any)
261
+ this.clearReconnectTimer();
262
+ this.setState(ConnectionState.ERROR);
263
+ console.error("[WebSocket] Connection error:", error);
264
+ this.callbacks.onError?.(error as unknown as Event);
440
265
  }
441
266
 
442
- /**
443
- * 安排重连
444
- */
445
267
  private scheduleReconnect(): void {
446
268
  if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
447
- console.error('[WebSocket] Max reconnect attempts reached')
448
- this.setState(ConnectionState.ERROR)
449
- return
269
+ this.setState(ConnectionState.ERROR);
270
+ return;
450
271
  }
451
-
452
- this.reconnectAttempts++
453
- const delay = this.config.reconnectInterval * this.reconnectAttempts
454
-
455
- console.debug(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`)
456
-
272
+ this.reconnectAttempts++;
273
+ const delay = this.config.reconnectInterval * this.reconnectAttempts;
457
274
  this.reconnectTimer = setTimeout(() => {
458
- if (this.state === ConnectionState.RECONNECTING) {
459
- this.connect()
460
- }
461
- }, delay)
275
+ if (this.state === ConnectionState.RECONNECTING) this.connect();
276
+ }, delay);
462
277
  }
463
278
 
464
- /**
465
- * 清除重连计时器
466
- */
467
279
  private clearReconnectTimer(): void {
468
280
  if (this.reconnectTimer) {
469
- clearTimeout(this.reconnectTimer)
470
- this.reconnectTimer = null
281
+ clearTimeout(this.reconnectTimer);
282
+ this.reconnectTimer = null;
471
283
  }
472
284
  }
473
285
 
474
- /**
475
- * 清除所有待处理请求
476
- */
477
286
  private clearPendingRequests(): void {
478
- for (const [requestId, { reject, timer }] of this.pendingRequests) {
479
- clearTimeout(timer)
480
- reject(new WebSocketError('Connection closed', 'CONNECTION_CLOSED'))
481
- }
482
- this.pendingRequests.clear()
483
- }
484
-
485
- /**
486
- * 初始化数据
487
- */
488
- private async initializeData(): Promise<void> {
489
- try {
490
- // 并行获取所有配置和 Schema
491
- const [configs, schemas] = await Promise.all([
492
- this.getAllConfigs().catch(error => {
493
- console.warn('[WebSocket] Failed to load configs:', error)
494
- return {}
495
- }),
496
- this.getAllSchemas().catch(error => {
497
- console.warn('[WebSocket] Failed to load schemas:', error)
498
- return {}
499
- })
500
- ])
501
-
502
- // 更新 Redux 状态
503
- store.dispatch(updateConfigs(configs))
504
- store.dispatch(updateSchemas(schemas))
505
-
506
- } catch (error) {
507
- console.error('[WebSocket] Data initialization failed:', error)
287
+ for (const [, { reject, timer }] of this.pendingRequests) {
288
+ clearTimeout(timer);
289
+ reject(new WebSocketError("Connection closed", "CONNECTION_CLOSED"));
508
290
  }
291
+ this.pendingRequests.clear();
509
292
  }
510
- }
293
+ }