@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.
@@ -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 { ConnectionManager 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 GetAllNodesParams as J, type ExportImageParams as K, LOG_DIR as L, MAX_PORT_ATTEMPTS as M, type NodeInfo as N, type OutputFormatter as O, PROD_DEFAULT_PORT as P, type CliOptions as Q, REQUEST_TIMEOUT as R, type ServerInfo as S, type RGBA as T, type MgPageInfo as U, type AllPagesInfo as V, type ConnectedPageInfo as W, type ServerStatusResponse as X, MGServer as Y, createServer as Z, type ServerOptions as _, type SpaceNodeInfo as a, type ManagedWebSocket as a0, RequestHandler as a1, Logger as a2, createLogger as a3, LogLevel as a4, type LoggerOptions as a5, 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 };
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-BCd-mD-X.js';
2
- export { V as AllPagesInfo, B as BaseMessage, C as CONFIG_DIR, Q as CliOptions, W as ConnectedPageInfo, F as ConnectionInfo, $ 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, K as ExportImageParams, J as GetAllNodesParams, G as GetNodeParams, H as HEARTBEAT_INTERVAL, l as HEARTBEAT_TIMEOUT, I as IS_DEV_MODE, L as LOG_DIR, a4 as LogLevel, a2 as Logger, a5 as LoggerOptions, M as MAX_PORT_ATTEMPTS, o as MAX_RETRY_COUNT, t as MGError, Y as MGServer, a0 as ManagedWebSocket, q as MessageType, U as MgPageInfo, O 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, T as RGBA, y as RegisterMessage, a1 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, X as ServerStatusResponse, u as createError, a3 as createLogger, Z as createServer } from './index-BCd-mD-X.js';
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: string;
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
- if (!encodedNodeId) {
229
+ const encodedPageId = params.get("pageId");
230
+ if (!encodedNodeId && !encodedPageId) {
229
231
  return null;
230
232
  }
231
- const nodeId = decodeURIComponent(encodedNodeId);
232
- if (!/^(\d+:\d+)(\/\d+:\d+)*$/.test(nodeId)) {
233
+ if (encodedNodeId && encodedPageId) {
233
234
  return null;
234
235
  }
235
- const encodedNodePath = params.get("nodePath");
236
- let nodePath;
237
- if (encodedNodePath) {
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
- return {
245
- pageUrl,
246
- nodeId,
247
- nodePath
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
- for (const [id, ws] of this.allConnections) {
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
- if (existing) {
628
- this.logger.info(`\u9875\u9762 ${pageUrl} \u5DF2\u6709\u8FDE\u63A5\uFF0C\u66FF\u6362\u4E3A\u65B0\u8FDE\u63A5`);
629
- this.removeConnection(existing);
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.delete(connectionInfo.pageUrl);
648
- this.logger.info(`Provider \u65AD\u5F00: ${connectionInfo.pageUrl}`);
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
- return this.providers.get(pageUrl);
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 iterator = this.providers.values();
672
- const first = iterator.next();
673
- return first.value;
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
- return Array.from(this.providers.values()).map((ws) => ws.connectionInfo);
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: this.providers.size,
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
- for (const ws of this.allConnections.values()) {
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,