@zhin.js/client 1.0.17 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/client/app.ts +228 -0
  2. package/client/bootstrap/loadConsoleEntries.test.ts +32 -0
  3. package/client/bootstrap/loadConsoleEntries.ts +149 -0
  4. package/client/console-utils/cn.ts +6 -0
  5. package/client/console-utils/remoteApi.ts +48 -0
  6. package/client/index.ts +61 -14
  7. package/client/persistence/idb-store.ts +76 -0
  8. package/client/runtime/index.ts +12 -0
  9. package/client/store/createRegistryStore.ts +46 -0
  10. package/client/websocket/hooks.ts +218 -510
  11. package/client/websocket/index.ts +6 -43
  12. package/client/websocket/instance.ts +10 -32
  13. package/client/websocket/manager.ts +301 -385
  14. package/client/websocket/remote-settings.ts +25 -0
  15. package/client/websocket/types.ts +58 -222
  16. package/dist/app.d.ts +95 -0
  17. package/dist/app.d.ts.map +1 -0
  18. package/dist/app.js +147 -0
  19. package/dist/app.js.map +1 -0
  20. package/dist/bootstrap/loadConsoleEntries.d.ts +26 -0
  21. package/dist/bootstrap/loadConsoleEntries.d.ts.map +1 -0
  22. package/dist/bootstrap/loadConsoleEntries.js +111 -0
  23. package/dist/bootstrap/loadConsoleEntries.js.map +1 -0
  24. package/dist/bootstrap/loadConsoleEntries.test.d.ts +2 -0
  25. package/dist/bootstrap/loadConsoleEntries.test.d.ts.map +1 -0
  26. package/dist/bootstrap/loadConsoleEntries.test.js +27 -0
  27. package/dist/bootstrap/loadConsoleEntries.test.js.map +1 -0
  28. package/dist/console-utils/cn.d.ts +3 -0
  29. package/dist/console-utils/cn.d.ts.map +1 -0
  30. package/dist/console-utils/cn.js +6 -0
  31. package/dist/console-utils/cn.js.map +1 -0
  32. package/dist/console-utils/remoteApi.d.ts +7 -0
  33. package/dist/console-utils/remoteApi.d.ts.map +1 -0
  34. package/dist/console-utils/remoteApi.js +41 -0
  35. package/dist/console-utils/remoteApi.js.map +1 -0
  36. package/dist/index.d.ts +11 -7
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +13 -14
  39. package/dist/index.js.map +1 -1
  40. package/dist/persistence/idb-store.d.ts +15 -0
  41. package/dist/persistence/idb-store.d.ts.map +1 -0
  42. package/dist/persistence/idb-store.js +61 -0
  43. package/dist/persistence/idb-store.js.map +1 -0
  44. package/dist/runtime/index.d.ts +6 -0
  45. package/dist/runtime/index.d.ts.map +1 -0
  46. package/dist/runtime/index.js +10 -0
  47. package/dist/runtime/index.js.map +1 -0
  48. package/dist/store/createRegistryStore.d.ts +9 -0
  49. package/dist/store/createRegistryStore.d.ts.map +1 -0
  50. package/dist/store/createRegistryStore.js +38 -0
  51. package/dist/store/createRegistryStore.js.map +1 -0
  52. package/dist/websocket/hooks.d.ts +35 -58
  53. package/dist/websocket/hooks.d.ts.map +1 -1
  54. package/dist/websocket/hooks.js +134 -410
  55. package/dist/websocket/hooks.js.map +1 -1
  56. package/dist/websocket/index.d.ts +5 -10
  57. package/dist/websocket/index.d.ts.map +1 -1
  58. package/dist/websocket/index.js +5 -25
  59. package/dist/websocket/index.js.map +1 -1
  60. package/dist/websocket/instance.d.ts +1 -14
  61. package/dist/websocket/instance.d.ts.map +1 -1
  62. package/dist/websocket/instance.js +2 -20
  63. package/dist/websocket/instance.js.map +1 -1
  64. package/dist/websocket/manager.d.ts +28 -93
  65. package/dist/websocket/manager.d.ts.map +1 -1
  66. package/dist/websocket/manager.js +213 -221
  67. package/dist/websocket/manager.js.map +1 -1
  68. package/dist/websocket/remote-settings.d.ts +5 -0
  69. package/dist/websocket/remote-settings.d.ts.map +1 -0
  70. package/dist/websocket/remote-settings.js +23 -0
  71. package/dist/websocket/remote-settings.js.map +1 -0
  72. package/dist/websocket/types.d.ts +35 -122
  73. package/dist/websocket/types.d.ts.map +1 -1
  74. package/dist/websocket/types.js +4 -13
  75. package/dist/websocket/types.js.map +1 -1
  76. package/package.json +13 -11
  77. package/client/router/index.tsx +0 -54
  78. package/client/store/index.ts +0 -132
  79. package/client/store/reducers/config.ts +0 -135
  80. package/client/store/reducers/index.ts +0 -19
  81. package/client/store/reducers/route.ts +0 -126
  82. package/client/store/reducers/script.ts +0 -109
  83. package/client/store/reducers/ui.ts +0 -31
  84. package/client/websocket/messageHandler.ts +0 -208
  85. package/dist/router/index.d.ts +0 -25
  86. package/dist/router/index.d.ts.map +0 -1
  87. package/dist/router/index.js +0 -48
  88. package/dist/router/index.js.map +0 -1
  89. package/dist/store/index.d.ts +0 -19
  90. package/dist/store/index.d.ts.map +0 -1
  91. package/dist/store/index.js +0 -67
  92. package/dist/store/index.js.map +0 -1
  93. package/dist/store/reducers/config.d.ts +0 -54
  94. package/dist/store/reducers/config.d.ts.map +0 -1
  95. package/dist/store/reducers/config.js +0 -78
  96. package/dist/store/reducers/config.js.map +0 -1
  97. package/dist/store/reducers/index.d.ts +0 -13
  98. package/dist/store/reducers/index.d.ts.map +0 -1
  99. package/dist/store/reducers/index.js +0 -11
  100. package/dist/store/reducers/index.js.map +0 -1
  101. package/dist/store/reducers/route.d.ts +0 -30
  102. package/dist/store/reducers/route.d.ts.map +0 -1
  103. package/dist/store/reducers/route.js +0 -85
  104. package/dist/store/reducers/route.js.map +0 -1
  105. package/dist/store/reducers/script.d.ts +0 -13
  106. package/dist/store/reducers/script.d.ts.map +0 -1
  107. package/dist/store/reducers/script.js +0 -78
  108. package/dist/store/reducers/script.js.map +0 -1
  109. package/dist/store/reducers/ui.d.ts +0 -8
  110. package/dist/store/reducers/ui.d.ts.map +0 -1
  111. package/dist/store/reducers/ui.js +0 -23
  112. package/dist/store/reducers/ui.js.map +0 -1
  113. package/dist/websocket/messageHandler.d.ts +0 -58
  114. package/dist/websocket/messageHandler.d.ts.map +0 -1
  115. package/dist/websocket/messageHandler.js +0 -174
  116. package/dist/websocket/messageHandler.js.map +0 -1
@@ -1,411 +1,403 @@
1
- /**
2
- * WebSocket 核心管理器
3
- * 负责 WebSocket 连接管理、消息发送、重连逻辑等核心功能
4
- */
5
- import { store, setConnected, updateConfigs, updateSchemas } from '../store';
6
- import { MessageHandler } from './messageHandler';
7
- import { ConnectionState, WebSocketError, ConnectionError, MessageError, RequestTimeoutError } from './types';
1
+ import { ConnectionState, WebSocketError, ConnectionError, MessageError, RequestTimeoutError, } from "./types";
2
+ import { getStoredToken, resolveApiUrl } from "./remote-settings.js";
3
+ import { applyConsoleEvent } from "../persistence/idb-store.js";
8
4
  export class WebSocketManager {
9
- // ============================================================================
10
- // 私有属性
11
- // ============================================================================
12
5
  ws = null;
13
6
  config;
14
7
  callbacks;
15
8
  state = ConnectionState.DISCONNECTED;
16
- // 重连相关
17
9
  reconnectAttempts = 0;
18
10
  reconnectTimer = null;
19
- // 请求管理
20
11
  requestId = 0;
21
12
  pendingRequests = new Map();
22
- // ============================================================================
23
- // 构造函数
24
- // ============================================================================
13
+ connectedListeners = new Set();
14
+ sseAbort = null;
15
+ useRestTransport = true;
25
16
  constructor(config = {}, callbacks = {}) {
26
17
  this.config = {
27
18
  url: this.buildWebSocketUrl(config.url),
28
19
  reconnectInterval: config.reconnectInterval ?? 3000,
29
20
  maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
30
- requestTimeout: config.requestTimeout ?? 10000
21
+ requestTimeout: config.requestTimeout ?? 10000,
31
22
  };
32
23
  this.callbacks = callbacks;
33
24
  }
34
- // ============================================================================
35
- // 公共 API
36
- // ============================================================================
37
- /**
38
- * 建立 WebSocket 连接
39
- */
25
+ onConnectionChange(listener) {
26
+ this.connectedListeners.add(listener);
27
+ return () => this.connectedListeners.delete(listener);
28
+ }
29
+ notifyConnection(connected) {
30
+ for (const l of this.connectedListeners)
31
+ l(connected);
32
+ }
40
33
  connect() {
41
- if (this.state === ConnectionState.CONNECTED || this.state === ConnectionState.CONNECTING) {
34
+ if (this.useRestTransport) {
35
+ void this.connectRestSse();
42
36
  return;
43
37
  }
38
+ if (this.state === ConnectionState.CONNECTED || this.state === ConnectionState.CONNECTING)
39
+ return;
44
40
  this.setState(ConnectionState.CONNECTING);
45
41
  try {
46
42
  this.ws = new WebSocket(this.config.url);
47
43
  this.attachEventHandlers();
48
44
  }
49
45
  catch (error) {
50
- this.handleConnectionError(new ConnectionError('Failed to create WebSocket', error));
46
+ this.handleConnectionError(new ConnectionError("Failed to create WebSocket", error));
47
+ }
48
+ }
49
+ async connectRestSse() {
50
+ const token = getStoredToken();
51
+ if (!token) {
52
+ this.setState(ConnectionState.DISCONNECTED);
53
+ this.notifyConnection(false);
54
+ return;
55
+ }
56
+ this.setState(ConnectionState.CONNECTING);
57
+ this.sseAbort?.abort();
58
+ this.sseAbort = new AbortController();
59
+ try {
60
+ const res = await fetch(resolveApiUrl("/api/events"), {
61
+ headers: {
62
+ Authorization: `Bearer ${token}`,
63
+ Accept: "text/event-stream",
64
+ },
65
+ signal: this.sseAbort.signal,
66
+ });
67
+ if (!res.ok || !res.body) {
68
+ throw new ConnectionError(`SSE failed: HTTP ${res.status}`);
69
+ }
70
+ this.setState(ConnectionState.CONNECTED);
71
+ this.reconnectAttempts = 0;
72
+ this.notifyConnection(true);
73
+ this.callbacks.onConnect?.();
74
+ void this.pumpSse(res.body);
75
+ }
76
+ catch (error) {
77
+ if (error.name === "AbortError")
78
+ return;
79
+ this.handleConnectionError(error instanceof ConnectionError ? error : new ConnectionError("SSE connect failed", error));
80
+ this.scheduleReconnect();
81
+ }
82
+ }
83
+ async pumpSse(body) {
84
+ const reader = body.getReader();
85
+ const decoder = new TextDecoder();
86
+ let buffer = "";
87
+ try {
88
+ while (true) {
89
+ const { done, value } = await reader.read();
90
+ if (done)
91
+ break;
92
+ buffer += decoder.decode(value, { stream: true });
93
+ const parts = buffer.split("\n\n");
94
+ buffer = parts.pop() ?? "";
95
+ for (const part of parts) {
96
+ const line = part.split("\n").find((l) => l.startsWith("data: "));
97
+ if (!line)
98
+ continue;
99
+ const json = line.slice(6);
100
+ try {
101
+ const message = JSON.parse(json);
102
+ void applyConsoleEvent(message);
103
+ this.handleBroadcast(message);
104
+ this.callbacks.onMessage?.(message);
105
+ }
106
+ catch {
107
+ /* skip */
108
+ }
109
+ }
110
+ }
111
+ }
112
+ catch {
113
+ /* closed */
114
+ }
115
+ finally {
116
+ if (this.state === ConnectionState.CONNECTED) {
117
+ this.notifyConnection(false);
118
+ this.setState(ConnectionState.RECONNECTING);
119
+ this.scheduleReconnect();
120
+ }
51
121
  }
52
122
  }
53
- /**
54
- * 断开 WebSocket 连接
55
- */
56
123
  disconnect() {
57
124
  this.clearReconnectTimer();
58
125
  this.clearPendingRequests();
126
+ this.sseAbort?.abort();
127
+ this.sseAbort = null;
59
128
  if (this.ws) {
60
129
  this.ws.close();
61
130
  this.ws = null;
62
131
  }
63
132
  this.setState(ConnectionState.DISCONNECTED);
64
- store.dispatch(setConnected(false));
133
+ this.notifyConnection(false);
65
134
  }
66
- /**
67
- * 发送普通消息
68
- */
69
135
  send(message) {
70
- if (!this.isConnected()) {
71
- throw new WebSocketError('WebSocket is not connected', 'NOT_CONNECTED');
136
+ if (this.useRestTransport) {
137
+ void this.sendRequest(message);
138
+ return;
72
139
  }
140
+ if (!this.isConnected())
141
+ throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
73
142
  try {
74
143
  this.ws.send(JSON.stringify(message));
75
144
  }
76
145
  catch (error) {
77
- throw new MessageError('Failed to send message', error);
146
+ throw new MessageError("Failed to send message", error);
78
147
  }
79
148
  }
80
- /**
81
- * 发送请求并等待响应
82
- */
83
149
  async sendRequest(message) {
84
- if (!this.isConnected()) {
85
- throw new WebSocketError('WebSocket is not connected', 'NOT_CONNECTED');
150
+ if (this.useRestTransport) {
151
+ return this.sendRestRequest(message);
86
152
  }
153
+ if (!this.isConnected())
154
+ throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
87
155
  return new Promise((resolve, reject) => {
88
156
  const requestId = ++this.requestId;
89
157
  const messageWithId = { ...message, requestId };
90
- // 设置超时计时器
91
158
  const timer = setTimeout(() => {
92
159
  this.pendingRequests.delete(requestId);
93
160
  reject(new RequestTimeoutError(requestId));
94
161
  }, this.config.requestTimeout);
95
- // 存储请求信息
96
- this.pendingRequests.set(requestId, { resolve, reject, timer });
162
+ this.pendingRequests.set(requestId, { resolve: resolve, reject, timer });
97
163
  try {
98
164
  this.ws.send(JSON.stringify(messageWithId));
99
165
  }
100
166
  catch (error) {
101
167
  this.pendingRequests.delete(requestId);
102
168
  clearTimeout(timer);
103
- reject(new MessageError('Failed to send request', error));
169
+ reject(new MessageError("Failed to send request", error));
104
170
  }
105
171
  });
106
172
  }
107
- /**
108
- * 检查连接状态
109
- */
173
+ async sendRestRequest(message) {
174
+ const token = getStoredToken();
175
+ if (!token)
176
+ throw new WebSocketError("Not authenticated", "NOT_CONNECTED");
177
+ const requestId = ++this.requestId;
178
+ const body = { ...message, requestId };
179
+ const timer = setTimeout(() => {
180
+ /* handled below */
181
+ }, this.config.requestTimeout);
182
+ try {
183
+ const res = await fetch(resolveApiUrl("/api/console/request"), {
184
+ method: "POST",
185
+ headers: {
186
+ Authorization: `Bearer ${token}`,
187
+ "Content-Type": "application/json",
188
+ },
189
+ body: JSON.stringify(body),
190
+ signal: AbortSignal.timeout(this.config.requestTimeout),
191
+ });
192
+ clearTimeout(timer);
193
+ const json = (await res.json());
194
+ if (!res.ok || json.success === false) {
195
+ throw new WebSocketError(json.error ?? `HTTP ${res.status}`, "SERVER_ERROR");
196
+ }
197
+ return json.data;
198
+ }
199
+ catch (error) {
200
+ clearTimeout(timer);
201
+ if (error instanceof WebSocketError)
202
+ throw error;
203
+ throw new MessageError("REST request failed", error);
204
+ }
205
+ }
110
206
  isConnected() {
207
+ if (this.useRestTransport) {
208
+ return this.state === ConnectionState.CONNECTED;
209
+ }
111
210
  return this.ws?.readyState === WebSocket.OPEN;
112
211
  }
113
- /**
114
- * 获取当前状态
115
- */
116
212
  getState() {
117
213
  return this.state;
118
214
  }
119
- // ============================================================================
120
- // 配置管理 API
121
- // ============================================================================
122
- /**
123
- * 获取插件配置
124
- */
125
215
  async getConfig(pluginName) {
126
- return this.sendRequest({
127
- type: 'config:get',
128
- pluginName
129
- });
216
+ return this.sendRequest({ type: "config:get", pluginName });
130
217
  }
131
- /**
132
- * 设置插件配置
133
- */
134
218
  async setConfig(pluginName, config) {
135
219
  return this.sendRequest({
136
- type: 'config:set',
220
+ type: "config:set",
137
221
  pluginName,
138
- data: config
222
+ data: config,
139
223
  });
140
224
  }
141
- /**
142
- * 获取插件 Schema
143
- */
144
225
  async getSchema(pluginName) {
145
- return this.sendRequest({
146
- type: 'schema:get',
147
- pluginName
148
- });
226
+ return this.sendRequest({ type: "schema:get", pluginName });
149
227
  }
150
- /**
151
- * 获取所有配置
152
- */
153
228
  async getAllConfigs() {
154
- return this.sendRequest({
155
- type: 'config:get-all'
156
- });
229
+ return this.sendRequest({ type: "config:get-all" });
157
230
  }
158
- /**
159
- * 获取所有 Schema
160
- */
161
231
  async getAllSchemas() {
162
- return this.sendRequest({
163
- type: 'schema:get-all'
164
- });
232
+ return this.sendRequest({ type: "schema:get-all" });
165
233
  }
166
- // ============================================================================
167
- // 配置文件原始 YAML 读写
168
- // ============================================================================
169
234
  async getConfigYaml() {
170
- return this.sendRequest({ type: 'config:get-yaml' });
235
+ return this.sendRequest({ type: "config:get-yaml" });
171
236
  }
172
237
  async saveConfigYaml(yaml) {
173
- return this.sendRequest({ type: 'config:save-yaml', yaml });
238
+ return this.sendRequest({ type: "config:save-yaml", yaml });
174
239
  }
175
- // ============================================================================
176
- // 环境变量文件管理
177
- // ============================================================================
178
240
  async getEnvList() {
179
- return this.sendRequest({ type: 'env:list' });
241
+ return this.sendRequest({ type: "env:list" });
180
242
  }
181
243
  async getEnvFile(filename) {
182
- return this.sendRequest({ type: 'env:get', filename });
244
+ return this.sendRequest({ type: "env:get", filename });
183
245
  }
184
246
  async saveEnvFile(filename, content) {
185
- return this.sendRequest({ type: 'env:save', filename, content });
247
+ return this.sendRequest({ type: "env:save", filename, content });
186
248
  }
187
- // ============================================================================
188
- // 文件管理
189
- // ============================================================================
190
249
  async getFileTree() {
191
- return this.sendRequest({ type: 'files:tree' });
250
+ return this.sendRequest({ type: "files:tree" });
192
251
  }
193
252
  async readFile(filePath) {
194
- return this.sendRequest({ type: 'files:read', filePath });
253
+ return this.sendRequest({ type: "files:read", filePath });
195
254
  }
196
255
  async saveFile(filePath, content) {
197
- return this.sendRequest({ type: 'files:save', filePath, content });
256
+ return this.sendRequest({ type: "files:save", filePath, content });
198
257
  }
199
- // ============================================================================
200
- // 数据库管理
201
- // ============================================================================
202
258
  async getDbInfo() {
203
- return this.sendRequest({ type: 'db:info' });
259
+ return this.sendRequest({ type: "db:info" });
204
260
  }
205
261
  async getDbTables() {
206
- return this.sendRequest({ type: 'db:tables' });
262
+ return this.sendRequest({ type: "db:tables" });
207
263
  }
208
264
  async dbSelect(table, page, pageSize, where) {
209
- return this.sendRequest({ type: 'db:select', table, page, pageSize, where });
265
+ return this.sendRequest({ type: "db:select", table, page, pageSize, where });
210
266
  }
211
267
  async dbInsert(table, row) {
212
- return this.sendRequest({ type: 'db:insert', table, row });
268
+ return this.sendRequest({ type: "db:insert", table, row });
213
269
  }
214
270
  async dbUpdate(table, row, where) {
215
- return this.sendRequest({ type: 'db:update', table, row, where });
271
+ return this.sendRequest({ type: "db:update", table, row, where });
216
272
  }
217
273
  async dbDelete(table, where) {
218
- return this.sendRequest({ type: 'db:delete', table, where });
274
+ return this.sendRequest({ type: "db:delete", table, where });
219
275
  }
220
276
  async dbDropTable(table) {
221
- return this.sendRequest({ type: 'db:drop-table', table });
277
+ return this.sendRequest({ type: "db:drop-table", table });
222
278
  }
223
279
  async kvGet(table, key) {
224
- return this.sendRequest({ type: 'db:kv:get', table, key });
280
+ return this.sendRequest({ type: "db:kv:get", table, key });
225
281
  }
226
282
  async kvSet(table, key, value, ttl) {
227
- return this.sendRequest({ type: 'db:kv:set', table, key, value, ttl });
283
+ return this.sendRequest({ type: "db:kv:set", table, key, value, ttl });
228
284
  }
229
285
  async kvDelete(table, key) {
230
- return this.sendRequest({ type: 'db:kv:delete', table, key });
286
+ return this.sendRequest({ type: "db:kv:delete", table, key });
231
287
  }
232
288
  async kvGetEntries(table) {
233
- return this.sendRequest({ type: 'db:kv:entries', table });
234
- }
235
- // ============================================================================
236
- // 私有方法
237
- // ============================================================================
238
- /**
239
- * 构建 WebSocket URL
240
- */
289
+ return this.sendRequest({ type: "db:kv:entries", table });
290
+ }
241
291
  buildWebSocketUrl(customUrl) {
242
- if (customUrl) {
292
+ if (customUrl)
243
293
  return customUrl;
244
- }
245
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
246
- const host = window.location.host;
247
- return `${protocol}//${host}/server`;
294
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
295
+ return `${protocol}//${window.location.host}/server`;
248
296
  }
249
- /**
250
- * 设置连接状态
251
- */
252
297
  setState(newState) {
253
- const oldState = this.state;
254
298
  this.state = newState;
255
- console.debug(`[WebSocket] State changed: ${oldState} -> ${newState}`);
256
299
  }
257
- /**
258
- * 绑定事件处理器
259
- */
260
300
  attachEventHandlers() {
261
301
  if (!this.ws)
262
302
  return;
263
- this.ws.onopen = () => {
264
- this.handleConnectionOpen();
265
- };
266
- this.ws.onmessage = (event) => {
267
- this.handleMessage(event);
268
- };
269
- this.ws.onclose = (event) => {
270
- this.handleConnectionClose(event);
271
- };
272
- this.ws.onerror = (event) => {
273
- this.handleConnectionError(new ConnectionError('WebSocket error', event));
274
- };
303
+ this.ws.onopen = () => this.handleConnectionOpen();
304
+ this.ws.onmessage = (event) => this.handleMessage(event);
305
+ this.ws.onclose = () => this.handleConnectionClose();
306
+ this.ws.onerror = (event) => this.handleConnectionError(new ConnectionError("WebSocket error", event));
275
307
  }
276
- /**
277
- * 处理连接打开
278
- */
279
308
  handleConnectionOpen() {
280
309
  this.setState(ConnectionState.CONNECTED);
281
310
  this.reconnectAttempts = 0;
282
- // 更新 Redux 状态
283
- store.dispatch(setConnected(true));
284
- // 初始化数据
285
- this.initializeData();
286
- // 触发回调
311
+ this.notifyConnection(true);
287
312
  this.callbacks.onConnect?.();
288
313
  }
289
- /**
290
- * 处理消息接收
291
- */
292
314
  handleMessage(event) {
293
315
  try {
294
316
  const message = JSON.parse(event.data);
295
- // 处理请求响应
296
317
  if (message.requestId && this.pendingRequests.has(message.requestId)) {
297
318
  this.handleRequestResponse(message);
298
319
  return;
299
320
  }
300
- // 处理广播消息
301
- MessageHandler.handle(message);
321
+ void applyConsoleEvent(message);
322
+ this.handleBroadcast(message);
302
323
  this.callbacks.onMessage?.(message);
303
324
  }
304
325
  catch (error) {
305
- console.error('[WebSocket] Message parsing error:', error);
326
+ console.error("[WebSocket] Message parsing error:", error);
327
+ }
328
+ }
329
+ handleBroadcast(message) {
330
+ const t = message.type;
331
+ if (t === "bot:request" || t === "bot:notice" || t === "bot:message") {
332
+ if (typeof window !== "undefined") {
333
+ window.dispatchEvent(new CustomEvent("zhin-console-bot-push", { detail: message }));
334
+ }
335
+ return;
336
+ }
337
+ if (t === "hmr:reload") {
338
+ console.info(`[HMR] File changed, reloading...`);
339
+ window.location.reload();
340
+ return;
341
+ }
342
+ if (t === "system:restarting") {
343
+ console.info("[System] Server restarting, will reload shortly...");
344
+ setTimeout(() => window.location.reload(), 3000);
345
+ return;
306
346
  }
307
347
  }
308
- /**
309
- * 处理请求响应
310
- */
311
348
  handleRequestResponse(message) {
312
349
  const { requestId } = message;
313
- const pendingRequest = this.pendingRequests.get(requestId);
314
- if (!pendingRequest) {
350
+ const pending = this.pendingRequests.get(requestId);
351
+ if (!pending)
315
352
  return;
316
- }
317
353
  this.pendingRequests.delete(requestId);
318
- clearTimeout(pendingRequest.timer);
354
+ clearTimeout(pending.timer);
319
355
  if (message.error) {
320
- pendingRequest.reject(new WebSocketError(message.error, 'SERVER_ERROR'));
356
+ pending.reject(new WebSocketError(message.error, "SERVER_ERROR"));
321
357
  }
322
358
  else {
323
- pendingRequest.resolve(message.data);
359
+ pending.resolve(message.data);
324
360
  }
325
361
  }
326
- /**
327
- * 处理连接关闭
328
- */
329
- handleConnectionClose(event) {
362
+ handleConnectionClose() {
330
363
  this.ws = null;
331
- store.dispatch(setConnected(false));
332
- if (this.state === ConnectionState.DISCONNECTED) {
333
- // 主动断开,不重连
364
+ this.notifyConnection(false);
365
+ if (this.state === ConnectionState.DISCONNECTED)
334
366
  return;
335
- }
336
367
  this.setState(ConnectionState.RECONNECTING);
337
368
  this.callbacks.onDisconnect?.();
338
369
  this.scheduleReconnect();
339
370
  }
340
- /**
341
- * 处理连接错误
342
- */
343
371
  handleConnectionError(error) {
344
372
  this.clearReconnectTimer();
345
373
  this.setState(ConnectionState.ERROR);
346
- console.error('[WebSocket] Connection error:', error);
374
+ console.error("[Console transport] Connection error:", error);
347
375
  this.callbacks.onError?.(error);
348
376
  }
349
- /**
350
- * 安排重连
351
- */
352
377
  scheduleReconnect() {
353
378
  if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
354
- console.error('[WebSocket] Max reconnect attempts reached');
355
379
  this.setState(ConnectionState.ERROR);
356
380
  return;
357
381
  }
358
382
  this.reconnectAttempts++;
359
383
  const delay = this.config.reconnectInterval * this.reconnectAttempts;
360
- console.debug(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
361
384
  this.reconnectTimer = setTimeout(() => {
362
- if (this.state === ConnectionState.RECONNECTING) {
385
+ if (this.state === ConnectionState.RECONNECTING)
363
386
  this.connect();
364
- }
365
387
  }, delay);
366
388
  }
367
- /**
368
- * 清除重连计时器
369
- */
370
389
  clearReconnectTimer() {
371
390
  if (this.reconnectTimer) {
372
391
  clearTimeout(this.reconnectTimer);
373
392
  this.reconnectTimer = null;
374
393
  }
375
394
  }
376
- /**
377
- * 清除所有待处理请求
378
- */
379
395
  clearPendingRequests() {
380
- for (const [requestId, { reject, timer }] of this.pendingRequests) {
396
+ for (const [, { reject, timer }] of this.pendingRequests) {
381
397
  clearTimeout(timer);
382
- reject(new WebSocketError('Connection closed', 'CONNECTION_CLOSED'));
398
+ reject(new WebSocketError("Connection closed", "CONNECTION_CLOSED"));
383
399
  }
384
400
  this.pendingRequests.clear();
385
401
  }
386
- /**
387
- * 初始化数据
388
- */
389
- async initializeData() {
390
- try {
391
- // 并行获取所有配置和 Schema
392
- const [configs, schemas] = await Promise.all([
393
- this.getAllConfigs().catch(error => {
394
- console.warn('[WebSocket] Failed to load configs:', error);
395
- return {};
396
- }),
397
- this.getAllSchemas().catch(error => {
398
- console.warn('[WebSocket] Failed to load schemas:', error);
399
- return {};
400
- })
401
- ]);
402
- // 更新 Redux 状态
403
- store.dispatch(updateConfigs(configs));
404
- store.dispatch(updateSchemas(schemas));
405
- }
406
- catch (error) {
407
- console.error('[WebSocket] Data initialization failed:', error);
408
- }
409
- }
410
402
  }
411
403
  //# sourceMappingURL=manager.js.map