@hangox/mg-cli 1.0.6 → 1.0.7
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 +170 -81
- package/dist/cli.js.map +1 -1
- package/dist/daemon-runner.js +46 -35
- package/dist/daemon-runner.js.map +1 -1
- package/dist/{index-BCd-mD-X.d.ts → index-DUQN5gSR.d.ts} +149 -5
- package/dist/index.d.ts +15 -5
- package/dist/index.js +84 -49
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +46 -35
- package/dist/server.js.map +1 -1
- package/package.json +2 -3
- package/VERSION +0 -1
|
@@ -61,6 +61,7 @@ declare enum MessageType {
|
|
|
61
61
|
REGISTER = "register",
|
|
62
62
|
REGISTER_ACK = "register_ack",
|
|
63
63
|
GET_NODE_BY_ID = "get_node_by_id",
|
|
64
|
+
GET_PAGE_BY_ID = "get_page_by_id",
|
|
64
65
|
GET_ALL_NODES = "get_all_nodes",
|
|
65
66
|
GET_SELECTION = "get_selection",
|
|
66
67
|
EXPORT_IMAGE = "export_image",
|
|
@@ -230,7 +231,139 @@ interface ConnectionInfo {
|
|
|
230
231
|
/** 最后活跃时间 */
|
|
231
232
|
lastActiveAt: Date;
|
|
232
233
|
}
|
|
233
|
-
/**
|
|
234
|
+
/**
|
|
235
|
+
* 节点信息
|
|
236
|
+
*
|
|
237
|
+
* 包含 MasterGo 节点的所有属性,根据节点类型不同,可用属性也不同。
|
|
238
|
+
*
|
|
239
|
+
* ## 通用属性 (所有节点)
|
|
240
|
+
* - `id`: 节点唯一标识
|
|
241
|
+
* - `name`: 节点名称
|
|
242
|
+
* - `type`: 节点类型 (FRAME, TEXT, RECTANGLE, ELLIPSE, POLYGON, STAR, LINE, PEN, COMPONENT, COMPONENT_SET, INSTANCE, GROUP, BOOLEAN_OPERATION, SLICE, CONNECTOR, SECTION)
|
|
243
|
+
* - `visible`: 是否可见
|
|
244
|
+
* - `isLocked`: 是否锁定
|
|
245
|
+
* - `slotInfo`: 插槽信息
|
|
246
|
+
* - `componentPropertyReferences`: 组件属性引用
|
|
247
|
+
* - `attachedConnectorIds`: 连接的连接线 ID 数组
|
|
248
|
+
*
|
|
249
|
+
* ## 布局属性 (LayoutMixin)
|
|
250
|
+
* - `x`, `y`: 坐标
|
|
251
|
+
* - `width`, `height`: 尺寸
|
|
252
|
+
* - `rotation`: 旋转角度
|
|
253
|
+
* - `absoluteTransform`: 绝对变换矩阵
|
|
254
|
+
* - `relativeTransform`: 相对变换矩阵
|
|
255
|
+
* - `absoluteRenderBounds`: 渲染边界(含阴影/外描边)
|
|
256
|
+
* - `absoluteBoundingBox`: 绝对边界框
|
|
257
|
+
* - `bound`: 边界
|
|
258
|
+
* - `minWidth`, `maxWidth`, `minHeight`, `maxHeight`: 尺寸约束
|
|
259
|
+
* - `constrainProportions`: 锁定宽高比
|
|
260
|
+
* - `layoutPositioning`: 布局定位方式 (AUTO | ABSOLUTE)
|
|
261
|
+
* - `alignSelf`: 交叉轴对齐 (STRETCH | INHERIT)
|
|
262
|
+
* - `flexGrow`: 弹性增长 (0 | 1)
|
|
263
|
+
* - `scaleFactor`: 缩放因子
|
|
264
|
+
*
|
|
265
|
+
* ## 混合属性 (BlendMixin)
|
|
266
|
+
* - `opacity`: 不透明度
|
|
267
|
+
* - `blendMode`: 混合模式
|
|
268
|
+
* - `isMask`: 是否蒙版
|
|
269
|
+
* - `isMaskOutline`: 轮廓蒙版
|
|
270
|
+
* - `isMaskVisible`: 蒙版可见
|
|
271
|
+
* - `effects`: 效果数组(阴影、模糊等)
|
|
272
|
+
* - `effectStyleId`: 效果样式 ID
|
|
273
|
+
*
|
|
274
|
+
* ## 几何属性 (GeometryMixin)
|
|
275
|
+
* - `fills`: 填充数组
|
|
276
|
+
* - `strokes`: 描边数组
|
|
277
|
+
* - `strokeWeight`: 描边粗细
|
|
278
|
+
* - `strokeAlign`: 描边对齐 (CENTER | INSIDE | OUTSIDE)
|
|
279
|
+
* - `strokeCap`: 端点装饰
|
|
280
|
+
* - `strokeJoin`: 拐角装饰
|
|
281
|
+
* - `strokeStyle`: 描边样式 (SOLID | DASH | CUSTOM)
|
|
282
|
+
* - `dashCap`: 虚线端点
|
|
283
|
+
* - `strokeDashes`: 虚线数组
|
|
284
|
+
* - `fillStyleId`: 填充样式 ID
|
|
285
|
+
* - `strokeFillStyleId`: 描边填充样式 ID
|
|
286
|
+
* - `strokeWidthStyleId`: 描边宽度样式 ID
|
|
287
|
+
* - `paddingStyleId`: 内边距样式 ID
|
|
288
|
+
* - `spacingStyleId`: 间距样式 ID
|
|
289
|
+
* - `cornerRadiusStyleId`: 圆角样式 ID
|
|
290
|
+
*
|
|
291
|
+
* ## 矩形描边属性 (RectangleStrokeWeightMixin)
|
|
292
|
+
* - `strokeTopWeight`, `strokeLeftWeight`, `strokeBottomWeight`, `strokeRightWeight`: 各边描边粗细
|
|
293
|
+
*
|
|
294
|
+
* ## 圆角属性 (CornerMixin)
|
|
295
|
+
* - `cornerSmooth`: 角平滑度
|
|
296
|
+
* - `cornerRadius`: 统一圆角
|
|
297
|
+
* - `topLeftRadius`, `topRightRadius`, `bottomLeftRadius`, `bottomRightRadius`: 各角圆角
|
|
298
|
+
*
|
|
299
|
+
* ## 约束属性 (ConstraintMixin)
|
|
300
|
+
* - `constraints`: 约束设置 { horizontal, vertical }
|
|
301
|
+
*
|
|
302
|
+
* ## 容器属性 (ContainerMixin)
|
|
303
|
+
* - `expanded`: 是否展开
|
|
304
|
+
*
|
|
305
|
+
* ## 自动布局属性 (FrameContainerMixin)
|
|
306
|
+
* - `flexMode`: 自动布局方向 (NONE | HORIZONTAL | VERTICAL)
|
|
307
|
+
* - `flexWrap`: 换行设置 (WRAP | NO_WRAP)
|
|
308
|
+
* - `itemSpacing`: 主轴间距
|
|
309
|
+
* - `crossAxisSpacing`: 交叉轴间距
|
|
310
|
+
* - `mainAxisAlignItems`: 主轴对齐
|
|
311
|
+
* - `crossAxisAlignItems`: 交叉轴对齐
|
|
312
|
+
* - `mainAxisSizingMode`: 主轴尺寸模式 (FIXED | AUTO)
|
|
313
|
+
* - `crossAxisSizingMode`: 交叉轴尺寸模式 (FIXED | AUTO)
|
|
314
|
+
* - `crossAxisAlignContent`: 多行对齐
|
|
315
|
+
* - `strokesIncludedInLayout`: 描边计入布局
|
|
316
|
+
* - `itemReverseZIndex`: 反转堆叠
|
|
317
|
+
* - `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft`: 内边距
|
|
318
|
+
* - `clipsContent`: 裁剪内容
|
|
319
|
+
* - `layoutGrids`: 布局网格
|
|
320
|
+
* - `gridStyleId`: 网格样式 ID
|
|
321
|
+
* - `overflowDirection`: 溢出方向
|
|
322
|
+
*
|
|
323
|
+
* ## 原型交互属性 (ReactionMixin)
|
|
324
|
+
* - `reactions`: 原型交互数组
|
|
325
|
+
*
|
|
326
|
+
* ## 导出属性 (ExportMixin)
|
|
327
|
+
* - `exportSettings`: 导出设置数组
|
|
328
|
+
*
|
|
329
|
+
* ## TEXT 节点特有属性
|
|
330
|
+
* - `characters`: 文本内容
|
|
331
|
+
* - `hasMissingFont`: 缺失字体
|
|
332
|
+
* - `hyperlinks`: 超链接数组
|
|
333
|
+
* - `textAlignHorizontal`: 水平对齐
|
|
334
|
+
* - `textAlignVertical`: 垂直对齐
|
|
335
|
+
* - `textAutoResize`: 自动调整
|
|
336
|
+
* - `paragraphSpacing`: 段落间距
|
|
337
|
+
* - `textStyles`: 文本样式数组
|
|
338
|
+
* - `listStyles`: 列表样式数组
|
|
339
|
+
*
|
|
340
|
+
* ## COMPONENT/COMPONENT_SET 节点特有属性
|
|
341
|
+
* - `variantProperties`: 变体属性
|
|
342
|
+
* - `componentPropertyValues`: 组件属性值
|
|
343
|
+
* - `description`: 描述
|
|
344
|
+
* - `alias`: 别名
|
|
345
|
+
* - `documentationLinks`: 文档链接
|
|
346
|
+
* - `isExternal`: 是否团队库组件
|
|
347
|
+
* - `ukey`: 唯一键
|
|
348
|
+
* - `publishStatus`: 发布状态
|
|
349
|
+
*
|
|
350
|
+
* ## INSTANCE 节点特有属性
|
|
351
|
+
* - `componentProperties`: 组件属性
|
|
352
|
+
* - `exposedInstances`: 暴露的实例 ID 数组
|
|
353
|
+
* - `isExposedInstance`: 是否暴露的实例
|
|
354
|
+
* - `mainComponentId`: 主组件 ID
|
|
355
|
+
* - `mainComponentName`: 主组件名称
|
|
356
|
+
*
|
|
357
|
+
* ## 其他节点特有属性
|
|
358
|
+
* - ELLIPSE: `arcData`
|
|
359
|
+
* - POLYGON/STAR: `pointCount`
|
|
360
|
+
* - STAR: `innerRadius`
|
|
361
|
+
* - LINE: `leftStrokeCap`, `rightStrokeCap`
|
|
362
|
+
* - PEN: `penNetwork`, `penPaths`
|
|
363
|
+
* - BOOLEAN_OPERATION: `booleanOperation`
|
|
364
|
+
* - CONNECTOR: `connectorStart`, `connectorEnd`, `connectorStartStrokeCap`, `connectorEndStrokeCap`, `connectorText`
|
|
365
|
+
* - SLICE: `isPreserveRatio`
|
|
366
|
+
*/
|
|
234
367
|
interface NodeInfo {
|
|
235
368
|
/** 节点 ID */
|
|
236
369
|
id: string;
|
|
@@ -250,7 +383,7 @@ interface NodeInfo {
|
|
|
250
383
|
height?: number;
|
|
251
384
|
/** 子节点 */
|
|
252
385
|
children?: NodeInfo[];
|
|
253
|
-
/**
|
|
386
|
+
/** 其他属性(根据节点类型不同而不同) */
|
|
254
387
|
[key: string]: unknown;
|
|
255
388
|
}
|
|
256
389
|
/** 空间节点信息(仅包含位置和尺寸,用于 AI 理解空间布局) */
|
|
@@ -281,6 +414,17 @@ interface GetNodeParams {
|
|
|
281
414
|
/** 索引签名 */
|
|
282
415
|
[key: string]: unknown;
|
|
283
416
|
}
|
|
417
|
+
/** 获取页面参数 */
|
|
418
|
+
interface GetPageParams {
|
|
419
|
+
/** 页面 ID */
|
|
420
|
+
pageId: string;
|
|
421
|
+
/** 遍历深度 */
|
|
422
|
+
maxDepth?: number;
|
|
423
|
+
/** 是否包含不可见节点 */
|
|
424
|
+
includeInvisible?: boolean;
|
|
425
|
+
/** 索引签名 */
|
|
426
|
+
[key: string]: unknown;
|
|
427
|
+
}
|
|
284
428
|
/** 获取所有节点参数 */
|
|
285
429
|
interface GetAllNodesParams {
|
|
286
430
|
/** 遍历深度 */
|
|
@@ -532,7 +676,7 @@ interface ManagedWebSocket extends WebSocket {
|
|
|
532
676
|
*/
|
|
533
677
|
declare class ConnectionManager {
|
|
534
678
|
private logger;
|
|
535
|
-
/** Provider 连接(按页面 URL
|
|
679
|
+
/** Provider 连接(按页面 URL 索引,支持同 URL 多个连接) */
|
|
536
680
|
private providers;
|
|
537
681
|
/** Consumer 连接 */
|
|
538
682
|
private consumers;
|
|
@@ -566,7 +710,7 @@ declare class ConnectionManager {
|
|
|
566
710
|
*/
|
|
567
711
|
updateLastActive(ws: ManagedWebSocket): void;
|
|
568
712
|
/**
|
|
569
|
-
* 根据页面 URL 查找 Provider
|
|
713
|
+
* 根据页面 URL 查找 Provider(返回第一个可用的连接)
|
|
570
714
|
*/
|
|
571
715
|
findProviderByPageUrl(pageUrl: string): ManagedWebSocket | undefined;
|
|
572
716
|
/**
|
|
@@ -639,4 +783,4 @@ declare class RequestHandler {
|
|
|
639
783
|
cleanupAll(): void;
|
|
640
784
|
}
|
|
641
785
|
|
|
642
|
-
export {
|
|
786
|
+
export { type ServerOptions as $, type PongMessage as A, type BaseMessage as B, CONFIG_DIR as C, DEV_DEFAULT_PORT as D, ErrorCode as E, type ConnectionInfo as F, type GetNodeParams as G, HEARTBEAT_INTERVAL as H, IS_DEV_MODE as I, type GetPageParams as J, type GetAllNodesParams as K, LOG_DIR as L, MAX_PORT_ATTEMPTS as M, type NodeInfo as N, type ExportImageParams as O, PROD_DEFAULT_PORT as P, type OutputFormatter as Q, REQUEST_TIMEOUT as R, type ServerInfo as S, type CliOptions as T, type RGBA as U, type MgPageInfo as V, type AllPagesInfo as W, type ConnectedPageInfo as X, type ServerStatusResponse as Y, MGServer as Z, createServer as _, type SpaceNodeInfo as a, ConnectionManager as a0, type ManagedWebSocket as a1, RequestHandler as a2, Logger as a3, createLogger as a4, LogLevel as a5, type LoggerOptions as a6, PROD_PORT_RANGE_START as b, PROD_PORT_RANGE_END as c, DEV_PORT_RANGE_START as d, DEV_PORT_RANGE_END as e, DEFAULT_PORT as f, PORT_RANGE_START as g, PORT_RANGE_END as h, PORT_SCAN_TIMEOUT as i, SERVER_INFO_FILE as j, SERVER_LOG_FILE as k, HEARTBEAT_TIMEOUT as l, SERVER_START_TIMEOUT as m, RETRY_INTERVALS as n, MAX_RETRY_COUNT as o, ConnectionType as p, MessageType as q, ErrorNames as r, ErrorMessages as s, MGError as t, createError as u, type RequestMessage as v, type ResponseMessage as w, type ErrorInfo as x, type RegisterMessage as y, type PingMessage as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ServerInfo, N as NodeInfo, a as SpaceNodeInfo } from './index-
|
|
2
|
-
export {
|
|
1
|
+
import { S as ServerInfo, N as NodeInfo, a as SpaceNodeInfo } from './index-DUQN5gSR.js';
|
|
2
|
+
export { W as AllPagesInfo, B as BaseMessage, C as CONFIG_DIR, T as CliOptions, X as ConnectedPageInfo, F as ConnectionInfo, a0 as ConnectionManager, p as ConnectionType, f as DEFAULT_PORT, D as DEV_DEFAULT_PORT, e as DEV_PORT_RANGE_END, d as DEV_PORT_RANGE_START, E as ErrorCode, x as ErrorInfo, s as ErrorMessages, r as ErrorNames, O as ExportImageParams, K as GetAllNodesParams, G as GetNodeParams, J as GetPageParams, H as HEARTBEAT_INTERVAL, l as HEARTBEAT_TIMEOUT, I as IS_DEV_MODE, L as LOG_DIR, a5 as LogLevel, a3 as Logger, a6 as LoggerOptions, M as MAX_PORT_ATTEMPTS, o as MAX_RETRY_COUNT, t as MGError, Z as MGServer, a1 as ManagedWebSocket, q as MessageType, V as MgPageInfo, Q as OutputFormatter, h as PORT_RANGE_END, g as PORT_RANGE_START, i as PORT_SCAN_TIMEOUT, P as PROD_DEFAULT_PORT, c as PROD_PORT_RANGE_END, b as PROD_PORT_RANGE_START, z as PingMessage, A as PongMessage, R as REQUEST_TIMEOUT, n as RETRY_INTERVALS, U as RGBA, y as RegisterMessage, a2 as RequestHandler, v as RequestMessage, w as ResponseMessage, j as SERVER_INFO_FILE, k as SERVER_LOG_FILE, m as SERVER_START_TIMEOUT, $ as ServerOptions, Y as ServerStatusResponse, u as createError, a4 as createLogger, _ as createServer } from './index-DUQN5gSR.js';
|
|
3
3
|
import 'ws';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -56,15 +56,18 @@ declare function isDesignPageUrl(url: string): boolean;
|
|
|
56
56
|
/**
|
|
57
57
|
* 解析 mgp:// 链接
|
|
58
58
|
*
|
|
59
|
-
* 支持的格式 (queryParams 格式,nodeId 需要 URL 编码):
|
|
59
|
+
* 支持的格式 (queryParams 格式,nodeId/pageId 需要 URL 编码):
|
|
60
60
|
* - mgp://mastergo.netease.com/file/174135798361888?nodeId=123%3A456 (单个节点)
|
|
61
61
|
* - mgp://mastergo.netease.com/file/174135798361888?nodeId=0%3A8633&nodePath=314%3A13190%2F0%3A8633 (带父节点路径)
|
|
62
|
+
* - mgp://mastergo.netease.com/file/174135798361888?pageId=0%3A1 (页面链接)
|
|
62
63
|
*
|
|
63
64
|
* 输出: { pageUrl: 'mastergo.netease.com/file/174135798361888', nodeId: '0:8633', nodePath: ['314:13190', '0:8633'] }
|
|
65
|
+
* 或 { pageUrl: 'mastergo.netease.com/file/174135798361888', pageId: '0:1' }
|
|
64
66
|
*/
|
|
65
67
|
declare function parseMgpLink(link: string): {
|
|
66
68
|
pageUrl: string;
|
|
67
|
-
nodeId
|
|
69
|
+
nodeId?: string;
|
|
70
|
+
pageId?: string;
|
|
68
71
|
nodePath?: string[];
|
|
69
72
|
} | null;
|
|
70
73
|
/**
|
|
@@ -75,6 +78,13 @@ declare function parseMgpLink(link: string): {
|
|
|
75
78
|
* @param nodePath 可选的父节点路径(会被 URL 编码)
|
|
76
79
|
*/
|
|
77
80
|
declare function generateMgpLink(pageUrl: string, nodeId: string, nodePath?: string[]): string;
|
|
81
|
+
/**
|
|
82
|
+
* 生成 mgp:// 页面链接
|
|
83
|
+
*
|
|
84
|
+
* @param pageUrl 页面 URL(会被标准化)
|
|
85
|
+
* @param pageId 页面 ID(会被 URL 编码)
|
|
86
|
+
*/
|
|
87
|
+
declare function generatePageLink(pageUrl: string, pageId: string): string;
|
|
78
88
|
/**
|
|
79
89
|
* 格式化文件大小
|
|
80
90
|
*/
|
|
@@ -153,4 +163,4 @@ declare function trimNodeDefaults<T extends Record<string, unknown>>(node: T): T
|
|
|
153
163
|
*/
|
|
154
164
|
declare function extractSpaceInfo(node: NodeInfo): SpaceNodeInfo;
|
|
155
165
|
|
|
156
|
-
export { NODE_DEFAULTS, NodeInfo, ServerInfo, SpaceNodeInfo, deleteServerInfo, ensureConfigDir, ensureDir, ensureOutputDir, extractFileId, extractFileIdFromMgpLink, extractFileIdFromUrl, extractSpaceInfo, formatDuration, formatFileSize, formatLogTime, generateId, generateMgpLink, getCurrentISOTime, isDesignPageUrl, isProcessRunning, killProcess, normalizePageUrl, parseMgpLink, readServerInfo, resolveOutputPath, roundToOneDecimal, trimNodeDefaults, writeServerInfo };
|
|
166
|
+
export { NODE_DEFAULTS, NodeInfo, ServerInfo, SpaceNodeInfo, deleteServerInfo, ensureConfigDir, ensureDir, ensureOutputDir, extractFileId, extractFileIdFromMgpLink, extractFileIdFromUrl, extractSpaceInfo, formatDuration, formatFileSize, formatLogTime, generateId, generateMgpLink, generatePageLink, getCurrentISOTime, isDesignPageUrl, isProcessRunning, killProcess, normalizePageUrl, parseMgpLink, readServerInfo, resolveOutputPath, roundToOneDecimal, trimNodeDefaults, writeServerInfo };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
|
|
34
34
|
MessageType2["REGISTER"] = "register";
|
|
35
35
|
MessageType2["REGISTER_ACK"] = "register_ack";
|
|
36
36
|
MessageType2["GET_NODE_BY_ID"] = "get_node_by_id";
|
|
37
|
+
MessageType2["GET_PAGE_BY_ID"] = "get_page_by_id";
|
|
37
38
|
MessageType2["GET_ALL_NODES"] = "get_all_nodes";
|
|
38
39
|
MessageType2["GET_SELECTION"] = "get_selection";
|
|
39
40
|
MessageType2["EXPORT_IMAGE"] = "export_image";
|
|
@@ -225,27 +226,44 @@ function parseMgpLink(link) {
|
|
|
225
226
|
const queryString = urlPart.slice(questionMarkIndex + 1);
|
|
226
227
|
const params = new URLSearchParams(queryString);
|
|
227
228
|
const encodedNodeId = params.get("nodeId");
|
|
228
|
-
|
|
229
|
+
const encodedPageId = params.get("pageId");
|
|
230
|
+
if (!encodedNodeId && !encodedPageId) {
|
|
229
231
|
return null;
|
|
230
232
|
}
|
|
231
|
-
|
|
232
|
-
if (!/^(\d+:\d+)(\/\d+:\d+)*$/.test(nodeId)) {
|
|
233
|
+
if (encodedNodeId && encodedPageId) {
|
|
233
234
|
return null;
|
|
234
235
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const decodedNodePath = decodeURIComponent(encodedNodePath);
|
|
239
|
-
nodePath = decodedNodePath.split("/").filter(Boolean);
|
|
240
|
-
if (!nodePath.every((segment) => /^\d+:\d+$/.test(segment))) {
|
|
236
|
+
if (encodedNodeId) {
|
|
237
|
+
const nodeId = decodeURIComponent(encodedNodeId);
|
|
238
|
+
if (!/^(\d+:\d+)(\/\d+:\d+)*$/.test(nodeId)) {
|
|
241
239
|
return null;
|
|
242
240
|
}
|
|
241
|
+
const encodedNodePath = params.get("nodePath");
|
|
242
|
+
let nodePath;
|
|
243
|
+
if (encodedNodePath) {
|
|
244
|
+
const decodedNodePath = decodeURIComponent(encodedNodePath);
|
|
245
|
+
nodePath = decodedNodePath.split("/").filter(Boolean);
|
|
246
|
+
if (!nodePath.every((segment) => /^\d+:\d+$/.test(segment))) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
pageUrl,
|
|
252
|
+
nodeId,
|
|
253
|
+
nodePath
|
|
254
|
+
};
|
|
243
255
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
256
|
+
if (encodedPageId) {
|
|
257
|
+
const pageId = decodeURIComponent(encodedPageId);
|
|
258
|
+
if (!/^\d+:\d+$/.test(pageId)) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
pageUrl,
|
|
263
|
+
pageId
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
249
267
|
} catch {
|
|
250
268
|
return null;
|
|
251
269
|
}
|
|
@@ -260,6 +278,11 @@ function generateMgpLink(pageUrl, nodeId, nodePath) {
|
|
|
260
278
|
}
|
|
261
279
|
return link;
|
|
262
280
|
}
|
|
281
|
+
function generatePageLink(pageUrl, pageId) {
|
|
282
|
+
const normalizedUrl = normalizePageUrl(pageUrl);
|
|
283
|
+
const encodedPageId = encodeURIComponent(pageId);
|
|
284
|
+
return `mgp://${normalizedUrl}?pageId=${encodedPageId}`;
|
|
285
|
+
}
|
|
263
286
|
function formatFileSize(bytes) {
|
|
264
287
|
if (bytes < 1024) {
|
|
265
288
|
return `${bytes} \u5B57\u8282`;
|
|
@@ -557,7 +580,7 @@ function createLogger(options) {
|
|
|
557
580
|
// src/server/connection-manager.ts
|
|
558
581
|
var ConnectionManager = class {
|
|
559
582
|
logger;
|
|
560
|
-
/** Provider 连接(按页面 URL
|
|
583
|
+
/** Provider 连接(按页面 URL 索引,支持同 URL 多个连接) */
|
|
561
584
|
providers = /* @__PURE__ */ new Map();
|
|
562
585
|
/** Consumer 连接 */
|
|
563
586
|
consumers = /* @__PURE__ */ new Map();
|
|
@@ -593,7 +616,8 @@ var ConnectionManager = class {
|
|
|
593
616
|
*/
|
|
594
617
|
checkHeartbeats() {
|
|
595
618
|
const now = Date.now();
|
|
596
|
-
|
|
619
|
+
const entries = Array.from(this.allConnections.entries());
|
|
620
|
+
for (const [id, ws] of entries) {
|
|
597
621
|
const lastActive = ws.connectionInfo.lastActiveAt.getTime();
|
|
598
622
|
const elapsed = now - lastActive;
|
|
599
623
|
if (elapsed > HEARTBEAT_TIMEOUT) {
|
|
@@ -623,14 +647,10 @@ var ConnectionManager = class {
|
|
|
623
647
|
managedWs.isAlive = true;
|
|
624
648
|
this.allConnections.set(connectionId, managedWs);
|
|
625
649
|
if (type === "provider" /* PROVIDER */ && pageUrl) {
|
|
626
|
-
const existing = this.providers.get(pageUrl);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
existing.close();
|
|
631
|
-
}
|
|
632
|
-
this.providers.set(pageUrl, managedWs);
|
|
633
|
-
this.logger.info(`Provider \u8FDE\u63A5: ${pageUrl}`);
|
|
650
|
+
const existing = this.providers.get(pageUrl) || [];
|
|
651
|
+
existing.push(managedWs);
|
|
652
|
+
this.providers.set(pageUrl, existing);
|
|
653
|
+
this.logger.info(`Provider \u8FDE\u63A5: ${pageUrl} (\u5F53\u524D\u8BE5\u9875\u9762\u8FDE\u63A5\u6570: ${existing.length})`);
|
|
634
654
|
} else if (type === "consumer" /* CONSUMER */) {
|
|
635
655
|
this.consumers.set(connectionId, managedWs);
|
|
636
656
|
this.logger.info(`Consumer \u8FDE\u63A5: ${connectionId}`);
|
|
@@ -644,8 +664,17 @@ var ConnectionManager = class {
|
|
|
644
664
|
const { connectionId, connectionInfo } = ws;
|
|
645
665
|
this.allConnections.delete(connectionId);
|
|
646
666
|
if (connectionInfo.type === "provider" /* PROVIDER */ && connectionInfo.pageUrl) {
|
|
647
|
-
this.providers.
|
|
648
|
-
|
|
667
|
+
const connections = this.providers.get(connectionInfo.pageUrl);
|
|
668
|
+
if (connections) {
|
|
669
|
+
const index = connections.findIndex((c) => c.connectionId === connectionId);
|
|
670
|
+
if (index !== -1) {
|
|
671
|
+
connections.splice(index, 1);
|
|
672
|
+
if (connections.length === 0) {
|
|
673
|
+
this.providers.delete(connectionInfo.pageUrl);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
this.logger.info(`Provider \u65AD\u5F00: ${connectionInfo.pageUrl} (\u8FDE\u63A5ID: ${connectionId})`);
|
|
649
678
|
} else if (connectionInfo.type === "consumer" /* CONSUMER */) {
|
|
650
679
|
this.consumers.delete(connectionId);
|
|
651
680
|
this.logger.info(`Consumer \u65AD\u5F00: ${connectionId}`);
|
|
@@ -659,31 +688,48 @@ var ConnectionManager = class {
|
|
|
659
688
|
ws.isAlive = true;
|
|
660
689
|
}
|
|
661
690
|
/**
|
|
662
|
-
* 根据页面 URL 查找 Provider
|
|
691
|
+
* 根据页面 URL 查找 Provider(返回第一个可用的连接)
|
|
663
692
|
*/
|
|
664
693
|
findProviderByPageUrl(pageUrl) {
|
|
665
|
-
|
|
694
|
+
const connections = this.providers.get(pageUrl);
|
|
695
|
+
return connections?.[0];
|
|
666
696
|
}
|
|
667
697
|
/**
|
|
668
698
|
* 获取第一个可用的 Provider
|
|
669
699
|
*/
|
|
670
700
|
getFirstProvider() {
|
|
671
|
-
const
|
|
672
|
-
const
|
|
673
|
-
|
|
701
|
+
const allConnections = Array.from(this.providers.values());
|
|
702
|
+
for (const connections of allConnections) {
|
|
703
|
+
if (connections.length > 0) {
|
|
704
|
+
return connections[0];
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return void 0;
|
|
674
708
|
}
|
|
675
709
|
/**
|
|
676
710
|
* 获取所有 Provider 信息
|
|
677
711
|
*/
|
|
678
712
|
getAllProviders() {
|
|
679
|
-
|
|
713
|
+
const result = [];
|
|
714
|
+
const allConnections = Array.from(this.providers.values());
|
|
715
|
+
for (const connections of allConnections) {
|
|
716
|
+
for (const ws of connections) {
|
|
717
|
+
result.push(ws.connectionInfo);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return result;
|
|
680
721
|
}
|
|
681
722
|
/**
|
|
682
723
|
* 获取连接统计
|
|
683
724
|
*/
|
|
684
725
|
getStats() {
|
|
726
|
+
let providerCount = 0;
|
|
727
|
+
const allConnections = Array.from(this.providers.values());
|
|
728
|
+
for (const connections of allConnections) {
|
|
729
|
+
providerCount += connections.length;
|
|
730
|
+
}
|
|
685
731
|
return {
|
|
686
|
-
providers:
|
|
732
|
+
providers: providerCount,
|
|
687
733
|
consumers: this.consumers.size,
|
|
688
734
|
total: this.allConnections.size
|
|
689
735
|
};
|
|
@@ -699,7 +745,8 @@ var ConnectionManager = class {
|
|
|
699
745
|
*/
|
|
700
746
|
closeAll() {
|
|
701
747
|
this.stopHeartbeatCheck();
|
|
702
|
-
|
|
748
|
+
const allWs = Array.from(this.allConnections.values());
|
|
749
|
+
for (const ws of allWs) {
|
|
703
750
|
ws.close();
|
|
704
751
|
}
|
|
705
752
|
this.providers.clear();
|
|
@@ -853,24 +900,11 @@ function getVersion() {
|
|
|
853
900
|
try {
|
|
854
901
|
const currentFile = fileURLToPath(import.meta.url);
|
|
855
902
|
const currentDir = dirname3(currentFile);
|
|
856
|
-
const versionFilePaths = [
|
|
857
|
-
join2(currentDir, "..", "VERSION"),
|
|
858
|
-
// dist/xxx.js -> ../VERSION
|
|
859
|
-
join2(currentDir, "..", "..", "VERSION")
|
|
860
|
-
// src/shared/version.ts -> ../../VERSION
|
|
861
|
-
];
|
|
862
|
-
for (const versionFilePath of versionFilePaths) {
|
|
863
|
-
if (existsSync3(versionFilePath)) {
|
|
864
|
-
const version = readFileSync2(versionFilePath, "utf-8").trim();
|
|
865
|
-
if (version) {
|
|
866
|
-
cachedVersion = version;
|
|
867
|
-
return cachedVersion;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
903
|
const packageJsonPaths = [
|
|
872
904
|
join2(currentDir, "..", "package.json"),
|
|
905
|
+
// dist/xxx.js -> ../package.json
|
|
873
906
|
join2(currentDir, "..", "..", "package.json")
|
|
907
|
+
// src/shared/version.ts -> ../../package.json
|
|
874
908
|
];
|
|
875
909
|
for (const packageJsonPath of packageJsonPaths) {
|
|
876
910
|
if (existsSync3(packageJsonPath)) {
|
|
@@ -1185,6 +1219,7 @@ export {
|
|
|
1185
1219
|
formatLogTime,
|
|
1186
1220
|
generateId,
|
|
1187
1221
|
generateMgpLink,
|
|
1222
|
+
generatePageLink,
|
|
1188
1223
|
getCurrentISOTime,
|
|
1189
1224
|
isDesignPageUrl,
|
|
1190
1225
|
isProcessRunning,
|