@hangox/mg-cli 1.0.0 → 1.0.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/cli.js +1574 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon-runner.js +794 -0
- package/dist/daemon-runner.js.map +1 -0
- package/dist/index-DNrszrq9.d.ts +568 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.js +950 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +689 -0
- package/dist/server.js.map +1 -0
- package/package.json +5 -1
- package/.eslintrc.cjs +0 -26
- package/CLAUDE.md +0 -43
- package/src/cli/client.ts +0 -266
- package/src/cli/commands/execute-code.ts +0 -59
- package/src/cli/commands/export-image.ts +0 -193
- package/src/cli/commands/get-all-nodes.ts +0 -81
- package/src/cli/commands/get-all-pages.ts +0 -118
- package/src/cli/commands/get-node-by-id.ts +0 -83
- package/src/cli/commands/get-node-by-link.ts +0 -105
- package/src/cli/commands/server.ts +0 -130
- package/src/cli/index.ts +0 -33
- package/src/index.ts +0 -9
- package/src/server/connection-manager.ts +0 -211
- package/src/server/daemon-runner.ts +0 -22
- package/src/server/daemon.ts +0 -211
- package/src/server/index.ts +0 -8
- package/src/server/logger.ts +0 -117
- package/src/server/request-handler.ts +0 -192
- package/src/server/websocket-server.ts +0 -297
- package/src/shared/constants.ts +0 -90
- package/src/shared/errors.ts +0 -131
- package/src/shared/index.ts +0 -8
- package/src/shared/types.ts +0 -227
- package/src/shared/utils.ts +0 -352
- package/tests/unit/shared/constants.test.ts +0 -66
- package/tests/unit/shared/errors.test.ts +0 -82
- package/tests/unit/shared/utils.test.ts +0 -208
- package/tsconfig.json +0 -22
- package/tsup.config.ts +0 -33
- package/vitest.config.ts +0 -22
package/dist/index.js
ADDED
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
// src/shared/constants.ts
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
var DEFAULT_PORT = 9527;
|
|
5
|
+
var PORT_RANGE_START = 9527;
|
|
6
|
+
var PORT_RANGE_END = 9536;
|
|
7
|
+
var MAX_PORT_ATTEMPTS = 10;
|
|
8
|
+
var PORT_SCAN_TIMEOUT = 500;
|
|
9
|
+
var CONFIG_DIR = join(homedir(), ".mg-plugin");
|
|
10
|
+
var SERVER_INFO_FILE = join(CONFIG_DIR, "server.json");
|
|
11
|
+
var LOG_DIR = join(CONFIG_DIR, "logs");
|
|
12
|
+
var SERVER_LOG_FILE = join(LOG_DIR, "server.log");
|
|
13
|
+
var HEARTBEAT_INTERVAL = 3e4;
|
|
14
|
+
var HEARTBEAT_TIMEOUT = 9e4;
|
|
15
|
+
var REQUEST_TIMEOUT = 3e4;
|
|
16
|
+
var SERVER_START_TIMEOUT = 5e3;
|
|
17
|
+
var RETRY_INTERVALS = [1e3, 2e3, 4e3];
|
|
18
|
+
var MAX_RETRY_COUNT = 3;
|
|
19
|
+
var ConnectionType = /* @__PURE__ */ ((ConnectionType2) => {
|
|
20
|
+
ConnectionType2["CONSUMER"] = "consumer";
|
|
21
|
+
ConnectionType2["PROVIDER"] = "provider";
|
|
22
|
+
return ConnectionType2;
|
|
23
|
+
})(ConnectionType || {});
|
|
24
|
+
var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
|
25
|
+
MessageType2["PING"] = "ping";
|
|
26
|
+
MessageType2["PONG"] = "pong";
|
|
27
|
+
MessageType2["REGISTER"] = "register";
|
|
28
|
+
MessageType2["REGISTER_ACK"] = "register_ack";
|
|
29
|
+
MessageType2["GET_NODE_BY_ID"] = "get_node_by_id";
|
|
30
|
+
MessageType2["GET_ALL_NODES"] = "get_all_nodes";
|
|
31
|
+
MessageType2["GET_SELECTION"] = "get_selection";
|
|
32
|
+
MessageType2["EXPORT_IMAGE"] = "export_image";
|
|
33
|
+
MessageType2["EXECUTE_CODE"] = "execute_code";
|
|
34
|
+
MessageType2["GET_ALL_PAGES"] = "get_all_pages";
|
|
35
|
+
MessageType2["RESPONSE"] = "response";
|
|
36
|
+
MessageType2["ERROR"] = "error";
|
|
37
|
+
return MessageType2;
|
|
38
|
+
})(MessageType || {});
|
|
39
|
+
|
|
40
|
+
// src/shared/errors.ts
|
|
41
|
+
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
42
|
+
ErrorCode2["CONNECTION_FAILED"] = "E001";
|
|
43
|
+
ErrorCode2["CONNECTION_TIMEOUT"] = "E002";
|
|
44
|
+
ErrorCode2["NO_PAGE_CONNECTED"] = "E003";
|
|
45
|
+
ErrorCode2["PAGE_NOT_FOUND"] = "E004";
|
|
46
|
+
ErrorCode2["NODE_NOT_FOUND"] = "E005";
|
|
47
|
+
ErrorCode2["NO_SELECTION"] = "E006";
|
|
48
|
+
ErrorCode2["MG_UNAVAILABLE"] = "E007";
|
|
49
|
+
ErrorCode2["EXPORT_FAILED"] = "E008";
|
|
50
|
+
ErrorCode2["FILE_WRITE_FAILED"] = "E009";
|
|
51
|
+
ErrorCode2["INVALID_LINK"] = "E010";
|
|
52
|
+
ErrorCode2["INVALID_PARAMS"] = "E011";
|
|
53
|
+
ErrorCode2["REQUEST_TIMEOUT"] = "E012";
|
|
54
|
+
ErrorCode2["PORT_EXHAUSTED"] = "E013";
|
|
55
|
+
ErrorCode2["SERVER_DISCOVERY_FAILED"] = "E014";
|
|
56
|
+
ErrorCode2["SERVER_START_FAILED"] = "E015";
|
|
57
|
+
ErrorCode2["SERVER_ALREADY_RUNNING"] = "E016";
|
|
58
|
+
ErrorCode2["CONNECTION_LOST"] = "E017";
|
|
59
|
+
ErrorCode2["UNKNOWN_ERROR"] = "E099";
|
|
60
|
+
return ErrorCode2;
|
|
61
|
+
})(ErrorCode || {});
|
|
62
|
+
var ErrorNames = {
|
|
63
|
+
["E001" /* CONNECTION_FAILED */]: "CONNECTION_FAILED",
|
|
64
|
+
["E002" /* CONNECTION_TIMEOUT */]: "CONNECTION_TIMEOUT",
|
|
65
|
+
["E003" /* NO_PAGE_CONNECTED */]: "NO_PAGE_CONNECTED",
|
|
66
|
+
["E004" /* PAGE_NOT_FOUND */]: "PAGE_NOT_FOUND",
|
|
67
|
+
["E005" /* NODE_NOT_FOUND */]: "NODE_NOT_FOUND",
|
|
68
|
+
["E006" /* NO_SELECTION */]: "NO_SELECTION",
|
|
69
|
+
["E007" /* MG_UNAVAILABLE */]: "MG_UNAVAILABLE",
|
|
70
|
+
["E008" /* EXPORT_FAILED */]: "EXPORT_FAILED",
|
|
71
|
+
["E009" /* FILE_WRITE_FAILED */]: "FILE_WRITE_FAILED",
|
|
72
|
+
["E010" /* INVALID_LINK */]: "INVALID_LINK",
|
|
73
|
+
["E011" /* INVALID_PARAMS */]: "INVALID_PARAMS",
|
|
74
|
+
["E012" /* REQUEST_TIMEOUT */]: "REQUEST_TIMEOUT",
|
|
75
|
+
["E013" /* PORT_EXHAUSTED */]: "PORT_EXHAUSTED",
|
|
76
|
+
["E014" /* SERVER_DISCOVERY_FAILED */]: "SERVER_DISCOVERY_FAILED",
|
|
77
|
+
["E015" /* SERVER_START_FAILED */]: "SERVER_START_FAILED",
|
|
78
|
+
["E016" /* SERVER_ALREADY_RUNNING */]: "SERVER_ALREADY_RUNNING",
|
|
79
|
+
["E017" /* CONNECTION_LOST */]: "CONNECTION_LOST",
|
|
80
|
+
["E099" /* UNKNOWN_ERROR */]: "UNKNOWN_ERROR"
|
|
81
|
+
};
|
|
82
|
+
var ErrorMessages = {
|
|
83
|
+
["E001" /* CONNECTION_FAILED */]: "\u65E0\u6CD5\u8FDE\u63A5\u5230 MG Server",
|
|
84
|
+
["E002" /* CONNECTION_TIMEOUT */]: "\u8FDE\u63A5\u8D85\u65F6",
|
|
85
|
+
["E003" /* NO_PAGE_CONNECTED */]: "\u6CA1\u6709 MasterGo \u9875\u9762\u8FDE\u63A5\u5230 Server",
|
|
86
|
+
["E004" /* PAGE_NOT_FOUND */]: "\u672A\u627E\u5230\u5339\u914D\u7684\u9875\u9762",
|
|
87
|
+
["E005" /* NODE_NOT_FOUND */]: "\u8282\u70B9\u4E0D\u5B58\u5728",
|
|
88
|
+
["E006" /* NO_SELECTION */]: "\u6CA1\u6709\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9",
|
|
89
|
+
["E007" /* MG_UNAVAILABLE */]: "mg \u5BF9\u8C61\u4E0D\u53EF\u7528",
|
|
90
|
+
["E008" /* EXPORT_FAILED */]: "\u5BFC\u51FA\u56FE\u7247\u5931\u8D25",
|
|
91
|
+
["E009" /* FILE_WRITE_FAILED */]: "\u6587\u4EF6\u5199\u5165\u5931\u8D25",
|
|
92
|
+
["E010" /* INVALID_LINK */]: "\u65E0\u6548\u7684 mgp:// \u94FE\u63A5\u683C\u5F0F",
|
|
93
|
+
["E011" /* INVALID_PARAMS */]: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25",
|
|
94
|
+
["E012" /* REQUEST_TIMEOUT */]: "\u8BF7\u6C42\u8D85\u65F6",
|
|
95
|
+
["E013" /* PORT_EXHAUSTED */]: "\u6240\u6709\u5907\u9009\u7AEF\u53E3\u5747\u88AB\u5360\u7528",
|
|
96
|
+
["E014" /* SERVER_DISCOVERY_FAILED */]: "\u65E0\u6CD5\u53D1\u73B0 Server (\u7AEF\u53E3\u626B\u63CF\u5931\u8D25)",
|
|
97
|
+
["E015" /* SERVER_START_FAILED */]: "\u81EA\u52A8\u542F\u52A8 Server \u5931\u8D25",
|
|
98
|
+
["E016" /* SERVER_ALREADY_RUNNING */]: "Server \u5DF2\u5728\u8FD0\u884C\u4E2D",
|
|
99
|
+
["E017" /* CONNECTION_LOST */]: "\u8FDE\u63A5\u65AD\u5F00",
|
|
100
|
+
["E099" /* UNKNOWN_ERROR */]: "\u672A\u77E5\u9519\u8BEF"
|
|
101
|
+
};
|
|
102
|
+
var MGError = class extends Error {
|
|
103
|
+
/** 错误码 */
|
|
104
|
+
code;
|
|
105
|
+
/** 错误名称 */
|
|
106
|
+
errorName;
|
|
107
|
+
/** 额外详情 */
|
|
108
|
+
details;
|
|
109
|
+
constructor(code, message, details) {
|
|
110
|
+
super(message || ErrorMessages[code]);
|
|
111
|
+
this.name = "MGError";
|
|
112
|
+
this.code = code;
|
|
113
|
+
this.errorName = ErrorNames[code];
|
|
114
|
+
this.details = details;
|
|
115
|
+
}
|
|
116
|
+
/** 转换为 JSON 格式 */
|
|
117
|
+
toJSON() {
|
|
118
|
+
return {
|
|
119
|
+
code: this.code,
|
|
120
|
+
name: this.errorName,
|
|
121
|
+
message: this.message,
|
|
122
|
+
details: this.details
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** 格式化输出 */
|
|
126
|
+
toString() {
|
|
127
|
+
return `\u9519\u8BEF [${this.code}]: ${this.message}`;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
function createError(code, message, details) {
|
|
131
|
+
return new MGError(code, message, details);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/shared/utils.ts
|
|
135
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
136
|
+
import { dirname, resolve, isAbsolute } from "path";
|
|
137
|
+
function ensureDir(dir) {
|
|
138
|
+
if (!existsSync(dir)) {
|
|
139
|
+
mkdirSync(dir, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function ensureConfigDir() {
|
|
143
|
+
ensureDir(CONFIG_DIR);
|
|
144
|
+
ensureDir(LOG_DIR);
|
|
145
|
+
}
|
|
146
|
+
function readServerInfo() {
|
|
147
|
+
try {
|
|
148
|
+
if (!existsSync(SERVER_INFO_FILE)) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
const content = readFileSync(SERVER_INFO_FILE, "utf-8");
|
|
152
|
+
return JSON.parse(content);
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function writeServerInfo(info) {
|
|
158
|
+
ensureConfigDir();
|
|
159
|
+
writeFileSync(SERVER_INFO_FILE, JSON.stringify(info, null, 2), "utf-8");
|
|
160
|
+
}
|
|
161
|
+
function deleteServerInfo() {
|
|
162
|
+
try {
|
|
163
|
+
if (existsSync(SERVER_INFO_FILE)) {
|
|
164
|
+
unlinkSync(SERVER_INFO_FILE);
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function resolveOutputPath(outputPath) {
|
|
170
|
+
if (isAbsolute(outputPath)) {
|
|
171
|
+
return outputPath;
|
|
172
|
+
}
|
|
173
|
+
return resolve(process.cwd(), outputPath);
|
|
174
|
+
}
|
|
175
|
+
function ensureOutputDir(outputPath) {
|
|
176
|
+
const dir = dirname(outputPath);
|
|
177
|
+
ensureDir(dir);
|
|
178
|
+
}
|
|
179
|
+
function isProcessRunning(pid) {
|
|
180
|
+
try {
|
|
181
|
+
process.kill(pid, 0);
|
|
182
|
+
return true;
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function killProcess(pid) {
|
|
188
|
+
try {
|
|
189
|
+
process.kill(pid, "SIGTERM");
|
|
190
|
+
return true;
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function normalizePageUrl(url) {
|
|
196
|
+
try {
|
|
197
|
+
const urlObj = new URL(url);
|
|
198
|
+
return urlObj.host + urlObj.pathname;
|
|
199
|
+
} catch {
|
|
200
|
+
return url;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function isDesignPageUrl(url) {
|
|
204
|
+
return /\/file\/\d+/.test(url);
|
|
205
|
+
}
|
|
206
|
+
function parseMgpLink(link) {
|
|
207
|
+
if (!link.startsWith("mgp://")) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const urlPart = link.slice(6);
|
|
212
|
+
const questionMarkIndex = urlPart.indexOf("?");
|
|
213
|
+
if (questionMarkIndex === -1) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const pageUrl = urlPart.slice(0, questionMarkIndex);
|
|
217
|
+
const queryString = urlPart.slice(questionMarkIndex + 1);
|
|
218
|
+
const params = new URLSearchParams(queryString);
|
|
219
|
+
const encodedNodeId = params.get("nodeId");
|
|
220
|
+
if (!encodedNodeId) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const nodeId = decodeURIComponent(encodedNodeId);
|
|
224
|
+
if (!/^(\d+:\d+)(\/\d+:\d+)*$/.test(nodeId)) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const encodedNodePath = params.get("nodePath");
|
|
228
|
+
let nodePath;
|
|
229
|
+
if (encodedNodePath) {
|
|
230
|
+
const decodedNodePath = decodeURIComponent(encodedNodePath);
|
|
231
|
+
nodePath = decodedNodePath.split("/").filter(Boolean);
|
|
232
|
+
if (!nodePath.every((segment) => /^\d+:\d+$/.test(segment))) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
pageUrl,
|
|
238
|
+
nodeId,
|
|
239
|
+
nodePath
|
|
240
|
+
};
|
|
241
|
+
} catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function generateMgpLink(pageUrl, nodeId, nodePath) {
|
|
246
|
+
const normalizedUrl = normalizePageUrl(pageUrl);
|
|
247
|
+
const encodedNodeId = encodeURIComponent(nodeId);
|
|
248
|
+
let link = `mgp://${normalizedUrl}?nodeId=${encodedNodeId}`;
|
|
249
|
+
if (nodePath && nodePath.length > 0) {
|
|
250
|
+
const encodedNodePath = encodeURIComponent(nodePath.join("/"));
|
|
251
|
+
link += `&nodePath=${encodedNodePath}`;
|
|
252
|
+
}
|
|
253
|
+
return link;
|
|
254
|
+
}
|
|
255
|
+
function formatFileSize(bytes) {
|
|
256
|
+
if (bytes < 1024) {
|
|
257
|
+
return `${bytes} \u5B57\u8282`;
|
|
258
|
+
}
|
|
259
|
+
const kb = bytes / 1024;
|
|
260
|
+
if (kb < 1024) {
|
|
261
|
+
return `${kb.toFixed(2)} KB`;
|
|
262
|
+
}
|
|
263
|
+
const mb = kb / 1024;
|
|
264
|
+
return `${mb.toFixed(2)} MB`;
|
|
265
|
+
}
|
|
266
|
+
function formatDuration(ms) {
|
|
267
|
+
const seconds = Math.floor(ms / 1e3);
|
|
268
|
+
const minutes = Math.floor(seconds / 60);
|
|
269
|
+
const hours = Math.floor(minutes / 60);
|
|
270
|
+
const days = Math.floor(hours / 24);
|
|
271
|
+
if (days > 0) {
|
|
272
|
+
return `${days} \u5929 ${hours % 24} \u5C0F\u65F6`;
|
|
273
|
+
}
|
|
274
|
+
if (hours > 0) {
|
|
275
|
+
return `${hours} \u5C0F\u65F6 ${minutes % 60} \u5206\u949F`;
|
|
276
|
+
}
|
|
277
|
+
if (minutes > 0) {
|
|
278
|
+
return `${minutes} \u5206\u949F ${seconds % 60} \u79D2`;
|
|
279
|
+
}
|
|
280
|
+
return `${seconds} \u79D2`;
|
|
281
|
+
}
|
|
282
|
+
function generateId() {
|
|
283
|
+
return `${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
284
|
+
}
|
|
285
|
+
function getCurrentISOTime() {
|
|
286
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
287
|
+
}
|
|
288
|
+
function formatLogTime(date = /* @__PURE__ */ new Date()) {
|
|
289
|
+
const year = date.getFullYear();
|
|
290
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
291
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
292
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
293
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
294
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
295
|
+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
296
|
+
}
|
|
297
|
+
function extractFileIdFromUrl(url) {
|
|
298
|
+
const match = url.match(/\/file\/(\d+)/);
|
|
299
|
+
return match ? match[1] : null;
|
|
300
|
+
}
|
|
301
|
+
function extractFileIdFromMgpLink(link) {
|
|
302
|
+
if (!link.startsWith("mgp://")) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
return extractFileIdFromUrl(link);
|
|
306
|
+
}
|
|
307
|
+
function extractFileId(input) {
|
|
308
|
+
const trimmed = input.trim();
|
|
309
|
+
if (/^\d+$/.test(trimmed)) {
|
|
310
|
+
return trimmed;
|
|
311
|
+
}
|
|
312
|
+
if (trimmed.startsWith("mgp://")) {
|
|
313
|
+
return extractFileIdFromMgpLink(trimmed);
|
|
314
|
+
}
|
|
315
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
316
|
+
return extractFileIdFromUrl(trimmed);
|
|
317
|
+
}
|
|
318
|
+
return extractFileIdFromUrl(trimmed);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/server/websocket-server.ts
|
|
322
|
+
import { WebSocketServer } from "ws";
|
|
323
|
+
|
|
324
|
+
// src/server/logger.ts
|
|
325
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
326
|
+
import { dirname as dirname2 } from "path";
|
|
327
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
328
|
+
LogLevel2["DEBUG"] = "DEBUG";
|
|
329
|
+
LogLevel2["INFO"] = "INFO";
|
|
330
|
+
LogLevel2["WARN"] = "WARN";
|
|
331
|
+
LogLevel2["ERROR"] = "ERROR";
|
|
332
|
+
return LogLevel2;
|
|
333
|
+
})(LogLevel || {});
|
|
334
|
+
var levelPriority = {
|
|
335
|
+
["DEBUG" /* DEBUG */]: 0,
|
|
336
|
+
["INFO" /* INFO */]: 1,
|
|
337
|
+
["WARN" /* WARN */]: 2,
|
|
338
|
+
["ERROR" /* ERROR */]: 3
|
|
339
|
+
};
|
|
340
|
+
var Logger = class {
|
|
341
|
+
options;
|
|
342
|
+
constructor(options = {}) {
|
|
343
|
+
this.options = {
|
|
344
|
+
console: options.console ?? true,
|
|
345
|
+
file: options.file ?? false,
|
|
346
|
+
filePath: options.filePath ?? SERVER_LOG_FILE,
|
|
347
|
+
minLevel: options.minLevel ?? "INFO" /* INFO */
|
|
348
|
+
};
|
|
349
|
+
if (this.options.file) {
|
|
350
|
+
const dir = dirname2(this.options.filePath);
|
|
351
|
+
if (!existsSync2(dir)) {
|
|
352
|
+
mkdirSync2(dir, { recursive: true });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 记录日志
|
|
358
|
+
*/
|
|
359
|
+
log(level, message, ...args) {
|
|
360
|
+
if (levelPriority[level] < levelPriority[this.options.minLevel]) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const timestamp = formatLogTime();
|
|
364
|
+
const formattedMessage = `[${timestamp}] [${level}] ${message}`;
|
|
365
|
+
if (this.options.console) {
|
|
366
|
+
const consoleMethod = level === "ERROR" /* ERROR */ ? console.error : console.log;
|
|
367
|
+
if (args.length > 0) {
|
|
368
|
+
consoleMethod(formattedMessage, ...args);
|
|
369
|
+
} else {
|
|
370
|
+
consoleMethod(formattedMessage);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (this.options.file) {
|
|
374
|
+
try {
|
|
375
|
+
const fileMessage = args.length > 0 ? `${formattedMessage} ${JSON.stringify(args)}
|
|
376
|
+
` : `${formattedMessage}
|
|
377
|
+
`;
|
|
378
|
+
appendFileSync(this.options.filePath, fileMessage);
|
|
379
|
+
} catch (error) {
|
|
380
|
+
if (this.options.console) {
|
|
381
|
+
console.error("\u65E5\u5FD7\u6587\u4EF6\u5199\u5165\u5931\u8D25:", error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
debug(message, ...args) {
|
|
387
|
+
this.log("DEBUG" /* DEBUG */, message, ...args);
|
|
388
|
+
}
|
|
389
|
+
info(message, ...args) {
|
|
390
|
+
this.log("INFO" /* INFO */, message, ...args);
|
|
391
|
+
}
|
|
392
|
+
warn(message, ...args) {
|
|
393
|
+
this.log("WARN" /* WARN */, message, ...args);
|
|
394
|
+
}
|
|
395
|
+
error(message, ...args) {
|
|
396
|
+
this.log("ERROR" /* ERROR */, message, ...args);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
function createLogger(options) {
|
|
400
|
+
return new Logger(options);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/server/connection-manager.ts
|
|
404
|
+
var ConnectionManager = class {
|
|
405
|
+
logger;
|
|
406
|
+
/** Provider 连接(按页面 URL 索引) */
|
|
407
|
+
providers = /* @__PURE__ */ new Map();
|
|
408
|
+
/** Consumer 连接 */
|
|
409
|
+
consumers = /* @__PURE__ */ new Map();
|
|
410
|
+
/** 所有连接(按 ID 索引) */
|
|
411
|
+
allConnections = /* @__PURE__ */ new Map();
|
|
412
|
+
/** 心跳检查定时器 */
|
|
413
|
+
heartbeatTimer = null;
|
|
414
|
+
constructor(logger) {
|
|
415
|
+
this.logger = logger;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* 启动心跳检查
|
|
419
|
+
*/
|
|
420
|
+
startHeartbeatCheck(interval = 3e4) {
|
|
421
|
+
if (this.heartbeatTimer) {
|
|
422
|
+
clearInterval(this.heartbeatTimer);
|
|
423
|
+
}
|
|
424
|
+
this.heartbeatTimer = setInterval(() => {
|
|
425
|
+
this.checkHeartbeats();
|
|
426
|
+
}, interval);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* 停止心跳检查
|
|
430
|
+
*/
|
|
431
|
+
stopHeartbeatCheck() {
|
|
432
|
+
if (this.heartbeatTimer) {
|
|
433
|
+
clearInterval(this.heartbeatTimer);
|
|
434
|
+
this.heartbeatTimer = null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* 检查所有连接的心跳
|
|
439
|
+
*/
|
|
440
|
+
checkHeartbeats() {
|
|
441
|
+
const now = Date.now();
|
|
442
|
+
for (const [id, ws] of this.allConnections) {
|
|
443
|
+
const lastActive = ws.connectionInfo.lastActiveAt.getTime();
|
|
444
|
+
const elapsed = now - lastActive;
|
|
445
|
+
if (elapsed > HEARTBEAT_TIMEOUT) {
|
|
446
|
+
this.logger.warn(`\u8FDE\u63A5 ${id} \u5FC3\u8DF3\u8D85\u65F6\uFF0C\u5173\u95ED\u8FDE\u63A5`);
|
|
447
|
+
this.removeConnection(ws);
|
|
448
|
+
ws.terminate();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* 添加连接
|
|
454
|
+
*/
|
|
455
|
+
addConnection(ws, type, pageUrl, pageId) {
|
|
456
|
+
const connectionId = generateId();
|
|
457
|
+
const now = /* @__PURE__ */ new Date();
|
|
458
|
+
const connectionInfo = {
|
|
459
|
+
id: connectionId,
|
|
460
|
+
type,
|
|
461
|
+
pageUrl,
|
|
462
|
+
pageId,
|
|
463
|
+
connectedAt: now,
|
|
464
|
+
lastActiveAt: now
|
|
465
|
+
};
|
|
466
|
+
const managedWs = ws;
|
|
467
|
+
managedWs.connectionId = connectionId;
|
|
468
|
+
managedWs.connectionInfo = connectionInfo;
|
|
469
|
+
managedWs.isAlive = true;
|
|
470
|
+
this.allConnections.set(connectionId, managedWs);
|
|
471
|
+
if (type === "provider" /* PROVIDER */ && pageUrl) {
|
|
472
|
+
const existing = this.providers.get(pageUrl);
|
|
473
|
+
if (existing) {
|
|
474
|
+
this.logger.info(`\u9875\u9762 ${pageUrl} \u5DF2\u6709\u8FDE\u63A5\uFF0C\u66FF\u6362\u4E3A\u65B0\u8FDE\u63A5`);
|
|
475
|
+
this.removeConnection(existing);
|
|
476
|
+
existing.close();
|
|
477
|
+
}
|
|
478
|
+
this.providers.set(pageUrl, managedWs);
|
|
479
|
+
this.logger.info(`Provider \u8FDE\u63A5: ${pageUrl}`);
|
|
480
|
+
} else if (type === "consumer" /* CONSUMER */) {
|
|
481
|
+
this.consumers.set(connectionId, managedWs);
|
|
482
|
+
this.logger.info(`Consumer \u8FDE\u63A5: ${connectionId}`);
|
|
483
|
+
}
|
|
484
|
+
return managedWs;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* 移除连接
|
|
488
|
+
*/
|
|
489
|
+
removeConnection(ws) {
|
|
490
|
+
const { connectionId, connectionInfo } = ws;
|
|
491
|
+
this.allConnections.delete(connectionId);
|
|
492
|
+
if (connectionInfo.type === "provider" /* PROVIDER */ && connectionInfo.pageUrl) {
|
|
493
|
+
this.providers.delete(connectionInfo.pageUrl);
|
|
494
|
+
this.logger.info(`Provider \u65AD\u5F00: ${connectionInfo.pageUrl}`);
|
|
495
|
+
} else if (connectionInfo.type === "consumer" /* CONSUMER */) {
|
|
496
|
+
this.consumers.delete(connectionId);
|
|
497
|
+
this.logger.info(`Consumer \u65AD\u5F00: ${connectionId}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* 更新连接活跃时间
|
|
502
|
+
*/
|
|
503
|
+
updateLastActive(ws) {
|
|
504
|
+
ws.connectionInfo.lastActiveAt = /* @__PURE__ */ new Date();
|
|
505
|
+
ws.isAlive = true;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* 根据页面 URL 查找 Provider
|
|
509
|
+
*/
|
|
510
|
+
findProviderByPageUrl(pageUrl) {
|
|
511
|
+
return this.providers.get(pageUrl);
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* 获取第一个可用的 Provider
|
|
515
|
+
*/
|
|
516
|
+
getFirstProvider() {
|
|
517
|
+
const iterator = this.providers.values();
|
|
518
|
+
const first = iterator.next();
|
|
519
|
+
return first.value;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* 获取所有 Provider 信息
|
|
523
|
+
*/
|
|
524
|
+
getAllProviders() {
|
|
525
|
+
return Array.from(this.providers.values()).map((ws) => ws.connectionInfo);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* 获取连接统计
|
|
529
|
+
*/
|
|
530
|
+
getStats() {
|
|
531
|
+
return {
|
|
532
|
+
providers: this.providers.size,
|
|
533
|
+
consumers: this.consumers.size,
|
|
534
|
+
total: this.allConnections.size
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* 获取所有已连接的页面 URL
|
|
539
|
+
*/
|
|
540
|
+
getConnectedPageUrls() {
|
|
541
|
+
return Array.from(this.providers.keys());
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* 关闭所有连接
|
|
545
|
+
*/
|
|
546
|
+
closeAll() {
|
|
547
|
+
this.stopHeartbeatCheck();
|
|
548
|
+
for (const ws of this.allConnections.values()) {
|
|
549
|
+
ws.close();
|
|
550
|
+
}
|
|
551
|
+
this.providers.clear();
|
|
552
|
+
this.consumers.clear();
|
|
553
|
+
this.allConnections.clear();
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// src/server/request-handler.ts
|
|
558
|
+
var RequestHandler = class {
|
|
559
|
+
logger;
|
|
560
|
+
connectionManager;
|
|
561
|
+
/** 待处理的请求 */
|
|
562
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
563
|
+
constructor(connectionManager, logger) {
|
|
564
|
+
this.connectionManager = connectionManager;
|
|
565
|
+
this.logger = logger;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* 处理 Consumer 请求
|
|
569
|
+
*/
|
|
570
|
+
async handleRequest(consumer, message) {
|
|
571
|
+
const requestId = message.id || generateId();
|
|
572
|
+
const { type, pageUrl, params } = message;
|
|
573
|
+
this.logger.info(`\u6536\u5230\u8BF7\u6C42: ${type} (${requestId})`, { pageUrl });
|
|
574
|
+
let provider;
|
|
575
|
+
if (pageUrl) {
|
|
576
|
+
provider = this.connectionManager.findProviderByPageUrl(pageUrl);
|
|
577
|
+
if (!provider) {
|
|
578
|
+
this.sendError(consumer, requestId, "E004" /* PAGE_NOT_FOUND */, `\u672A\u627E\u5230\u9875\u9762: ${pageUrl}`);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
provider = this.connectionManager.getFirstProvider();
|
|
583
|
+
if (!provider) {
|
|
584
|
+
this.sendError(consumer, requestId, "E003" /* NO_PAGE_CONNECTED */, "\u6CA1\u6709\u9875\u9762\u8FDE\u63A5\u5230 Server");
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const timer = setTimeout(() => {
|
|
589
|
+
this.handleTimeout(requestId);
|
|
590
|
+
}, REQUEST_TIMEOUT);
|
|
591
|
+
this.pendingRequests.set(requestId, {
|
|
592
|
+
id: requestId,
|
|
593
|
+
consumer,
|
|
594
|
+
timer,
|
|
595
|
+
timestamp: Date.now()
|
|
596
|
+
});
|
|
597
|
+
const forwardMessage = {
|
|
598
|
+
id: requestId,
|
|
599
|
+
type,
|
|
600
|
+
pageUrl: pageUrl || provider.connectionInfo.pageUrl,
|
|
601
|
+
params,
|
|
602
|
+
timestamp: Date.now()
|
|
603
|
+
};
|
|
604
|
+
try {
|
|
605
|
+
provider.send(JSON.stringify(forwardMessage));
|
|
606
|
+
this.logger.info(`\u8BF7\u6C42\u8F6C\u53D1: ${type} -> ${provider.connectionInfo.pageUrl}`);
|
|
607
|
+
} catch {
|
|
608
|
+
this.cleanupRequest(requestId);
|
|
609
|
+
this.sendError(consumer, requestId, "E001" /* CONNECTION_FAILED */, "\u8F6C\u53D1\u8BF7\u6C42\u5931\u8D25");
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* 处理 Provider 响应
|
|
614
|
+
*/
|
|
615
|
+
handleResponse(response) {
|
|
616
|
+
const { id } = response;
|
|
617
|
+
const pending = this.pendingRequests.get(id);
|
|
618
|
+
if (!pending) {
|
|
619
|
+
this.logger.warn(`\u6536\u5230\u672A\u77E5\u8BF7\u6C42\u7684\u54CD\u5E94: ${id}`);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
this.cleanupRequest(id);
|
|
623
|
+
try {
|
|
624
|
+
pending.consumer.send(JSON.stringify(response));
|
|
625
|
+
this.logger.info(
|
|
626
|
+
`\u54CD\u5E94\u8FD4\u56DE: ${id} (${response.success ? "\u6210\u529F" : "\u5931\u8D25"})`
|
|
627
|
+
);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
this.logger.error(`\u54CD\u5E94\u8F6C\u53D1\u5931\u8D25: ${id}`, error);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* 处理请求超时
|
|
634
|
+
*/
|
|
635
|
+
handleTimeout(requestId) {
|
|
636
|
+
const pending = this.pendingRequests.get(requestId);
|
|
637
|
+
if (!pending) return;
|
|
638
|
+
this.logger.warn(`\u8BF7\u6C42\u8D85\u65F6: ${requestId}`);
|
|
639
|
+
this.cleanupRequest(requestId);
|
|
640
|
+
this.sendError(pending.consumer, requestId, "E012" /* REQUEST_TIMEOUT */, "\u8BF7\u6C42\u8D85\u65F6");
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* 清理请求
|
|
644
|
+
*/
|
|
645
|
+
cleanupRequest(requestId) {
|
|
646
|
+
const pending = this.pendingRequests.get(requestId);
|
|
647
|
+
if (pending) {
|
|
648
|
+
clearTimeout(pending.timer);
|
|
649
|
+
this.pendingRequests.delete(requestId);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* 发送错误响应
|
|
654
|
+
*/
|
|
655
|
+
sendError(consumer, requestId, code, message) {
|
|
656
|
+
const error = new MGError(code, message);
|
|
657
|
+
const response = {
|
|
658
|
+
id: requestId,
|
|
659
|
+
type: "error" /* ERROR */,
|
|
660
|
+
success: false,
|
|
661
|
+
data: null,
|
|
662
|
+
error: error.toJSON()
|
|
663
|
+
};
|
|
664
|
+
try {
|
|
665
|
+
consumer.send(JSON.stringify(response));
|
|
666
|
+
} catch (err) {
|
|
667
|
+
this.logger.error(`\u53D1\u9001\u9519\u8BEF\u54CD\u5E94\u5931\u8D25: ${requestId}`, err);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* 清理特定连接的所有待处理请求
|
|
672
|
+
*/
|
|
673
|
+
cleanupConnectionRequests(connectionId) {
|
|
674
|
+
for (const [requestId, pending] of this.pendingRequests) {
|
|
675
|
+
if (pending.consumer.connectionId === connectionId) {
|
|
676
|
+
this.cleanupRequest(requestId);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* 清理所有待处理请求
|
|
682
|
+
*/
|
|
683
|
+
cleanupAll() {
|
|
684
|
+
for (const [requestId] of this.pendingRequests) {
|
|
685
|
+
this.cleanupRequest(requestId);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
// src/server/websocket-server.ts
|
|
691
|
+
var MGServer = class {
|
|
692
|
+
wss = null;
|
|
693
|
+
logger;
|
|
694
|
+
connectionManager;
|
|
695
|
+
requestHandler;
|
|
696
|
+
port;
|
|
697
|
+
isRunning = false;
|
|
698
|
+
constructor(options = {}) {
|
|
699
|
+
this.port = options.port || DEFAULT_PORT;
|
|
700
|
+
this.logger = options.logger || createLogger();
|
|
701
|
+
this.connectionManager = new ConnectionManager(this.logger);
|
|
702
|
+
this.requestHandler = new RequestHandler(this.connectionManager, this.logger);
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* 启动服务器
|
|
706
|
+
*/
|
|
707
|
+
async start() {
|
|
708
|
+
if (this.isRunning) {
|
|
709
|
+
throw new MGError("E016" /* SERVER_ALREADY_RUNNING */, "Server \u5DF2\u5728\u8FD0\u884C\u4E2D");
|
|
710
|
+
}
|
|
711
|
+
const port = await this.findAvailablePort();
|
|
712
|
+
return new Promise((resolve2, reject) => {
|
|
713
|
+
this.wss = new WebSocketServer({ port });
|
|
714
|
+
this.wss.on("listening", () => {
|
|
715
|
+
this.port = port;
|
|
716
|
+
this.isRunning = true;
|
|
717
|
+
this.logger.info(`Server \u542F\u52A8\u6210\u529F\uFF0C\u76D1\u542C\u7AEF\u53E3: ${port}`);
|
|
718
|
+
this.connectionManager.startHeartbeatCheck(HEARTBEAT_INTERVAL);
|
|
719
|
+
resolve2(port);
|
|
720
|
+
});
|
|
721
|
+
this.wss.on("error", (error) => {
|
|
722
|
+
this.logger.error("Server \u9519\u8BEF:", error);
|
|
723
|
+
reject(error);
|
|
724
|
+
});
|
|
725
|
+
this.wss.on("connection", (ws, request) => {
|
|
726
|
+
this.handleConnection(ws, request);
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* 查找可用端口
|
|
732
|
+
*/
|
|
733
|
+
async findAvailablePort() {
|
|
734
|
+
for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {
|
|
735
|
+
const available = await this.isPortAvailable(port);
|
|
736
|
+
if (available) {
|
|
737
|
+
return port;
|
|
738
|
+
}
|
|
739
|
+
this.logger.debug(`\u7AEF\u53E3 ${port} \u88AB\u5360\u7528\uFF0C\u5C1D\u8BD5\u4E0B\u4E00\u4E2A`);
|
|
740
|
+
}
|
|
741
|
+
throw new MGError(
|
|
742
|
+
"E013" /* PORT_EXHAUSTED */,
|
|
743
|
+
`\u7AEF\u53E3 ${PORT_RANGE_START}-${PORT_RANGE_END} \u5747\u88AB\u5360\u7528`
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* 检查端口是否可用
|
|
748
|
+
*/
|
|
749
|
+
isPortAvailable(port) {
|
|
750
|
+
return new Promise((resolve2) => {
|
|
751
|
+
const testServer = new WebSocketServer({ port });
|
|
752
|
+
testServer.on("listening", () => {
|
|
753
|
+
testServer.close();
|
|
754
|
+
resolve2(true);
|
|
755
|
+
});
|
|
756
|
+
testServer.on("error", () => {
|
|
757
|
+
resolve2(false);
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* 处理新连接
|
|
763
|
+
*/
|
|
764
|
+
handleConnection(ws, _request) {
|
|
765
|
+
this.logger.info("\u65B0\u8FDE\u63A5\u5EFA\u7ACB");
|
|
766
|
+
const registerTimeout = setTimeout(() => {
|
|
767
|
+
this.logger.warn("\u8FDE\u63A5\u6CE8\u518C\u8D85\u65F6\uFF0C\u5173\u95ED\u8FDE\u63A5");
|
|
768
|
+
ws.close();
|
|
769
|
+
}, 5e3);
|
|
770
|
+
ws.on("message", (data) => {
|
|
771
|
+
try {
|
|
772
|
+
const message = JSON.parse(data.toString());
|
|
773
|
+
if (message.type === "register" /* REGISTER */) {
|
|
774
|
+
clearTimeout(registerTimeout);
|
|
775
|
+
this.handleRegister(ws, message);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const managedWs = ws;
|
|
779
|
+
if (!managedWs.connectionId) {
|
|
780
|
+
this.logger.warn("\u672A\u6CE8\u518C\u7684\u8FDE\u63A5\u53D1\u9001\u6D88\u606F\uFF0C\u5FFD\u7565");
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
this.handleMessage(managedWs, message);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
this.logger.error("\u6D88\u606F\u89E3\u6790\u5931\u8D25:", error);
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
ws.on("close", () => {
|
|
789
|
+
clearTimeout(registerTimeout);
|
|
790
|
+
const managedWs = ws;
|
|
791
|
+
if (managedWs.connectionId) {
|
|
792
|
+
this.requestHandler.cleanupConnectionRequests(managedWs.connectionId);
|
|
793
|
+
this.connectionManager.removeConnection(managedWs);
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
ws.on("error", (error) => {
|
|
797
|
+
this.logger.error("WebSocket \u9519\u8BEF:", error);
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* 处理注册消息
|
|
802
|
+
*/
|
|
803
|
+
handleRegister(ws, message) {
|
|
804
|
+
const { connectionType, pageUrl, pageId } = message.data;
|
|
805
|
+
const managedWs = this.connectionManager.addConnection(
|
|
806
|
+
ws,
|
|
807
|
+
connectionType,
|
|
808
|
+
pageUrl,
|
|
809
|
+
pageId
|
|
810
|
+
);
|
|
811
|
+
const ack = {
|
|
812
|
+
id: message.id || "",
|
|
813
|
+
type: "register_ack" /* REGISTER_ACK */,
|
|
814
|
+
success: true,
|
|
815
|
+
data: {
|
|
816
|
+
connectionId: managedWs.connectionId,
|
|
817
|
+
pageUrl
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
ws.send(JSON.stringify(ack));
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* 处理消息
|
|
824
|
+
*/
|
|
825
|
+
handleMessage(ws, message) {
|
|
826
|
+
this.connectionManager.updateLastActive(ws);
|
|
827
|
+
switch (message.type) {
|
|
828
|
+
case "ping" /* PING */:
|
|
829
|
+
this.handlePing(ws, message);
|
|
830
|
+
break;
|
|
831
|
+
case "response" /* RESPONSE */:
|
|
832
|
+
case "error" /* ERROR */:
|
|
833
|
+
this.requestHandler.handleResponse(message);
|
|
834
|
+
break;
|
|
835
|
+
default:
|
|
836
|
+
if (ws.connectionInfo.type === "consumer" /* CONSUMER */) {
|
|
837
|
+
this.requestHandler.handleRequest(ws, message);
|
|
838
|
+
}
|
|
839
|
+
break;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* 处理心跳
|
|
844
|
+
*/
|
|
845
|
+
handlePing(ws, message) {
|
|
846
|
+
const pong = {
|
|
847
|
+
type: "pong" /* PONG */,
|
|
848
|
+
timestamp: message.timestamp || Date.now()
|
|
849
|
+
};
|
|
850
|
+
ws.send(JSON.stringify(pong));
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* 停止服务器
|
|
854
|
+
*/
|
|
855
|
+
async stop() {
|
|
856
|
+
if (!this.isRunning || !this.wss) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
this.logger.info("\u6B63\u5728\u505C\u6B62 Server...");
|
|
860
|
+
this.requestHandler.cleanupAll();
|
|
861
|
+
this.connectionManager.closeAll();
|
|
862
|
+
return new Promise((resolve2) => {
|
|
863
|
+
this.wss.close(() => {
|
|
864
|
+
this.isRunning = false;
|
|
865
|
+
this.wss = null;
|
|
866
|
+
this.logger.info("Server \u5DF2\u505C\u6B62");
|
|
867
|
+
resolve2();
|
|
868
|
+
});
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* 获取运行状态
|
|
873
|
+
*/
|
|
874
|
+
getStatus() {
|
|
875
|
+
return {
|
|
876
|
+
running: this.isRunning,
|
|
877
|
+
port: this.port,
|
|
878
|
+
stats: this.connectionManager.getStats(),
|
|
879
|
+
connectedPages: this.connectionManager.getConnectedPageUrls()
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* 获取端口
|
|
884
|
+
*/
|
|
885
|
+
getPort() {
|
|
886
|
+
return this.port;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* 是否运行中
|
|
890
|
+
*/
|
|
891
|
+
isServerRunning() {
|
|
892
|
+
return this.isRunning;
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
function createServer(options) {
|
|
896
|
+
return new MGServer(options);
|
|
897
|
+
}
|
|
898
|
+
export {
|
|
899
|
+
CONFIG_DIR,
|
|
900
|
+
ConnectionManager,
|
|
901
|
+
ConnectionType,
|
|
902
|
+
DEFAULT_PORT,
|
|
903
|
+
ErrorCode,
|
|
904
|
+
ErrorMessages,
|
|
905
|
+
ErrorNames,
|
|
906
|
+
HEARTBEAT_INTERVAL,
|
|
907
|
+
HEARTBEAT_TIMEOUT,
|
|
908
|
+
LOG_DIR,
|
|
909
|
+
LogLevel,
|
|
910
|
+
Logger,
|
|
911
|
+
MAX_PORT_ATTEMPTS,
|
|
912
|
+
MAX_RETRY_COUNT,
|
|
913
|
+
MGError,
|
|
914
|
+
MGServer,
|
|
915
|
+
MessageType,
|
|
916
|
+
PORT_RANGE_END,
|
|
917
|
+
PORT_RANGE_START,
|
|
918
|
+
PORT_SCAN_TIMEOUT,
|
|
919
|
+
REQUEST_TIMEOUT,
|
|
920
|
+
RETRY_INTERVALS,
|
|
921
|
+
RequestHandler,
|
|
922
|
+
SERVER_INFO_FILE,
|
|
923
|
+
SERVER_LOG_FILE,
|
|
924
|
+
SERVER_START_TIMEOUT,
|
|
925
|
+
createError,
|
|
926
|
+
createLogger,
|
|
927
|
+
createServer,
|
|
928
|
+
deleteServerInfo,
|
|
929
|
+
ensureConfigDir,
|
|
930
|
+
ensureDir,
|
|
931
|
+
ensureOutputDir,
|
|
932
|
+
extractFileId,
|
|
933
|
+
extractFileIdFromMgpLink,
|
|
934
|
+
extractFileIdFromUrl,
|
|
935
|
+
formatDuration,
|
|
936
|
+
formatFileSize,
|
|
937
|
+
formatLogTime,
|
|
938
|
+
generateId,
|
|
939
|
+
generateMgpLink,
|
|
940
|
+
getCurrentISOTime,
|
|
941
|
+
isDesignPageUrl,
|
|
942
|
+
isProcessRunning,
|
|
943
|
+
killProcess,
|
|
944
|
+
normalizePageUrl,
|
|
945
|
+
parseMgpLink,
|
|
946
|
+
readServerInfo,
|
|
947
|
+
resolveOutputPath,
|
|
948
|
+
writeServerInfo
|
|
949
|
+
};
|
|
950
|
+
//# sourceMappingURL=index.js.map
|