@electron-memory/monitor 0.1.0 → 0.2.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.
package/dist/index.d.mts CHANGED
@@ -481,6 +481,12 @@ declare class ElectronMemoryMonitor extends EventEmitter {
481
481
  private mergeConfig;
482
482
  }
483
483
 
484
+ /**
485
+ * 必须在 app.on('ready') 之前调用(与 new ElectronMemoryMonitor 同文件顶部或更早)。
486
+ * 由 ElectronMemoryMonitor 在启用时于构造函数内调用。
487
+ */
488
+ declare function registerDashboardSchemePrivileged(): void;
489
+
484
490
  /**
485
491
  * IPC 通道常量定义
486
492
  * 所有通道以 'emm:' 为前缀,避免与业务 IPC 冲突
@@ -507,4 +513,4 @@ declare const IPC_CHANNELS: {
507
513
  readonly RENDERER_REQUEST: "emm:renderer:request";
508
514
  };
509
515
 
510
- export { type AnomalyCategory, type AnomalyEvent, type AnomalyRule, type AnomalySeverity, type CompareReport, ElectronMemoryMonitor, type EventMark, type GCResult, IPC_CHANNELS, type Improvement, type MemorySnapshot, type MetricDiff, type MetricSummary, type MonitorConfig, type ProcessMemoryInfo, type Regression, type RendererV8Detail, type SessionIndex, type SessionReport, type Suggestion, type SystemMemoryInfo, type TestSession, type TrendInfo, type V8HeapDetailStats, type V8HeapSpaceInfo, type V8HeapStats };
516
+ export { type AnomalyCategory, type AnomalyEvent, type AnomalyRule, type AnomalySeverity, type CompareReport, ElectronMemoryMonitor, type EventMark, type GCResult, IPC_CHANNELS, type Improvement, type MemorySnapshot, type MetricDiff, type MetricSummary, type MonitorConfig, type ProcessMemoryInfo, type Regression, type RendererV8Detail, type SessionIndex, type SessionReport, type Suggestion, type SystemMemoryInfo, type TestSession, type TrendInfo, type V8HeapDetailStats, type V8HeapSpaceInfo, type V8HeapStats, registerDashboardSchemePrivileged };
package/dist/index.d.ts CHANGED
@@ -481,6 +481,12 @@ declare class ElectronMemoryMonitor extends EventEmitter {
481
481
  private mergeConfig;
482
482
  }
483
483
 
484
+ /**
485
+ * 必须在 app.on('ready') 之前调用(与 new ElectronMemoryMonitor 同文件顶部或更早)。
486
+ * 由 ElectronMemoryMonitor 在启用时于构造函数内调用。
487
+ */
488
+ declare function registerDashboardSchemePrivileged(): void;
489
+
484
490
  /**
485
491
  * IPC 通道常量定义
486
492
  * 所有通道以 'emm:' 为前缀,避免与业务 IPC 冲突
@@ -507,4 +513,4 @@ declare const IPC_CHANNELS: {
507
513
  readonly RENDERER_REQUEST: "emm:renderer:request";
508
514
  };
509
515
 
510
- export { type AnomalyCategory, type AnomalyEvent, type AnomalyRule, type AnomalySeverity, type CompareReport, ElectronMemoryMonitor, type EventMark, type GCResult, IPC_CHANNELS, type Improvement, type MemorySnapshot, type MetricDiff, type MetricSummary, type MonitorConfig, type ProcessMemoryInfo, type Regression, type RendererV8Detail, type SessionIndex, type SessionReport, type Suggestion, type SystemMemoryInfo, type TestSession, type TrendInfo, type V8HeapDetailStats, type V8HeapSpaceInfo, type V8HeapStats };
516
+ export { type AnomalyCategory, type AnomalyEvent, type AnomalyRule, type AnomalySeverity, type CompareReport, ElectronMemoryMonitor, type EventMark, type GCResult, IPC_CHANNELS, type Improvement, type MemorySnapshot, type MetricDiff, type MetricSummary, type MonitorConfig, type ProcessMemoryInfo, type Regression, type RendererV8Detail, type SessionIndex, type SessionReport, type Suggestion, type SystemMemoryInfo, type TestSession, type TrendInfo, type V8HeapDetailStats, type V8HeapSpaceInfo, type V8HeapStats, registerDashboardSchemePrivileged };
package/dist/index.js CHANGED
@@ -31,13 +31,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  ElectronMemoryMonitor: () => ElectronMemoryMonitor,
34
- IPC_CHANNELS: () => IPC_CHANNELS
34
+ IPC_CHANNELS: () => IPC_CHANNELS,
35
+ registerDashboardSchemePrivileged: () => registerDashboardSchemePrivileged
35
36
  });
36
37
  module.exports = __toCommonJS(src_exports);
37
38
 
38
39
  // src/core/monitor.ts
39
- var import_electron4 = require("electron");
40
- var path3 = __toESM(require("path"));
40
+ var import_electron5 = require("electron");
41
+ var path4 = __toESM(require("path"));
41
42
  var v82 = __toESM(require("v8"));
42
43
  var import_events3 = require("events");
43
44
 
@@ -1106,8 +1107,76 @@ var Analyzer = class {
1106
1107
  };
1107
1108
 
1108
1109
  // src/core/dashboard.ts
1110
+ var import_electron3 = require("electron");
1111
+ var path3 = __toESM(require("path"));
1112
+
1113
+ // src/core/dashboard-protocol.ts
1109
1114
  var import_electron2 = require("electron");
1110
1115
  var path2 = __toESM(require("path"));
1116
+ var import_node_url = require("url");
1117
+ var SCHEME = "emm-dashboard";
1118
+ var privilegedRegistered = false;
1119
+ var handlerRegistered = false;
1120
+ function isPathInsideRoot(filePath, root) {
1121
+ const f = path2.resolve(filePath);
1122
+ const r = path2.resolve(root);
1123
+ if (f === r) {
1124
+ return true;
1125
+ }
1126
+ const sep2 = r.endsWith(path2.sep) ? r : `${r}${path2.sep}`;
1127
+ return f.startsWith(sep2);
1128
+ }
1129
+ function registerDashboardSchemePrivileged() {
1130
+ if (privilegedRegistered) {
1131
+ return;
1132
+ }
1133
+ privilegedRegistered = true;
1134
+ import_electron2.protocol.registerSchemesAsPrivileged([
1135
+ {
1136
+ scheme: SCHEME,
1137
+ privileges: {
1138
+ standard: true,
1139
+ secure: true,
1140
+ supportFetchAPI: true,
1141
+ corsEnabled: true,
1142
+ stream: true
1143
+ }
1144
+ }
1145
+ ]);
1146
+ }
1147
+ function ensureDashboardProtocolHandler(uiRoot) {
1148
+ if (handlerRegistered) {
1149
+ return;
1150
+ }
1151
+ if (!import_electron2.app.isReady()) {
1152
+ throw new Error(
1153
+ "[@electron-memory/monitor] ensureDashboardProtocolHandler: app must be ready before opening dashboard"
1154
+ );
1155
+ }
1156
+ handlerRegistered = true;
1157
+ const base = path2.resolve(uiRoot);
1158
+ import_electron2.protocol.handle(SCHEME, (request) => {
1159
+ try {
1160
+ const { pathname } = new URL(request.url);
1161
+ let rel = pathname.startsWith("/") ? pathname.slice(1) : pathname;
1162
+ if (!rel) {
1163
+ rel = "index.html";
1164
+ }
1165
+ const filePath = path2.resolve(path2.join(base, rel));
1166
+ if (!isPathInsideRoot(filePath, base)) {
1167
+ return new Response("Forbidden", { status: 403 });
1168
+ }
1169
+ return import_electron2.net.fetch((0, import_node_url.pathToFileURL)(filePath).href);
1170
+ } catch {
1171
+ return new Response("Bad Request", { status: 400 });
1172
+ }
1173
+ });
1174
+ }
1175
+ function getDashboardPageURL() {
1176
+ return `${SCHEME}://electron/index.html`;
1177
+ }
1178
+
1179
+ // src/core/dashboard.ts
1111
1180
  var DashboardManager = class {
1112
1181
  constructor(config) {
1113
1182
  this.window = null;
@@ -1130,8 +1199,8 @@ var DashboardManager = class {
1130
1199
  this.window.focus();
1131
1200
  return;
1132
1201
  }
1133
- const preloadPath = path2.join(__dirname, "dashboard-preload.js");
1134
- this.window = new import_electron2.BrowserWindow({
1202
+ const preloadPath = path3.join(__dirname, "dashboard-preload.js");
1203
+ this.window = new import_electron3.BrowserWindow({
1135
1204
  width: this.config.dashboard.width,
1136
1205
  height: this.config.dashboard.height,
1137
1206
  title: "Electron Memory Monitor",
@@ -1142,8 +1211,9 @@ var DashboardManager = class {
1142
1211
  nodeIntegration: false
1143
1212
  }
1144
1213
  });
1145
- const uiPath = path2.join(__dirname, "ui", "index.html");
1146
- this.window.loadFile(uiPath);
1214
+ const uiRoot = path3.join(__dirname, "ui");
1215
+ ensureDashboardProtocolHandler(uiRoot);
1216
+ this.window.loadURL(getDashboardPageURL());
1147
1217
  this.window.on("closed", () => {
1148
1218
  this.window = null;
1149
1219
  });
@@ -1165,7 +1235,7 @@ var DashboardManager = class {
1165
1235
  };
1166
1236
 
1167
1237
  // src/ipc/main-handler.ts
1168
- var import_electron3 = require("electron");
1238
+ var import_electron4 = require("electron");
1169
1239
 
1170
1240
  // src/ipc/channels.ts
1171
1241
  var IPC_CHANNELS = {
@@ -1203,49 +1273,49 @@ var IPCMainHandler = class {
1203
1273
  }
1204
1274
  /** 注册所有 IPC handlers */
1205
1275
  register() {
1206
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_START, (_event, args) => {
1276
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_START, (_event, args) => {
1207
1277
  return this.monitor.startSession(args.label, args.description);
1208
1278
  });
1209
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_STOP, async () => {
1279
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_STOP, async () => {
1210
1280
  return this.monitor.stopSession();
1211
1281
  });
1212
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_LIST, async () => {
1282
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_LIST, async () => {
1213
1283
  return this.monitor.getSessions();
1214
1284
  });
1215
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_REPORT, async (_event, sessionId) => {
1285
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_REPORT, async (_event, sessionId) => {
1216
1286
  return this.monitor.getSessionReport(sessionId);
1217
1287
  });
1218
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_COMPARE, async (_event, args) => {
1288
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_COMPARE, async (_event, args) => {
1219
1289
  return this.monitor.compareSessions(args.baseId, args.targetId);
1220
1290
  });
1221
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_SNAPSHOTS, async (_event, args) => {
1291
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_SNAPSHOTS, async (_event, args) => {
1222
1292
  return this.monitor.getSessionSnapshots(args.sessionId, args.startTime, args.endTime, args.maxPoints);
1223
1293
  });
1224
- import_electron3.ipcMain.handle(IPC_CHANNELS.TRIGGER_GC, async () => {
1294
+ import_electron4.ipcMain.handle(IPC_CHANNELS.TRIGGER_GC, async () => {
1225
1295
  return this.monitor.triggerGC();
1226
1296
  });
1227
- import_electron3.ipcMain.handle(IPC_CHANNELS.HEAP_SNAPSHOT, async (_event, filePath) => {
1297
+ import_electron4.ipcMain.handle(IPC_CHANNELS.HEAP_SNAPSHOT, async (_event, filePath) => {
1228
1298
  return this.monitor.takeHeapSnapshot(filePath);
1229
1299
  });
1230
- import_electron3.ipcMain.handle(IPC_CHANNELS.MARK, (_event, args) => {
1300
+ import_electron4.ipcMain.handle(IPC_CHANNELS.MARK, (_event, args) => {
1231
1301
  this.monitor.mark(args.label, args.metadata);
1232
1302
  });
1233
- import_electron3.ipcMain.handle(IPC_CHANNELS.GET_CONFIG, () => {
1303
+ import_electron4.ipcMain.handle(IPC_CHANNELS.GET_CONFIG, () => {
1234
1304
  return this.monitor.getConfig();
1235
1305
  });
1236
- import_electron3.ipcMain.handle(IPC_CHANNELS.GET_SESSIONS, async () => {
1306
+ import_electron4.ipcMain.handle(IPC_CHANNELS.GET_SESSIONS, async () => {
1237
1307
  return this.monitor.getSessions();
1238
1308
  });
1239
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_EXPORT, async (_event, sessionId) => {
1309
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_EXPORT, async (_event, sessionId) => {
1240
1310
  return this.monitor.exportSession(sessionId);
1241
1311
  });
1242
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_IMPORT, async () => {
1312
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_IMPORT, async () => {
1243
1313
  return this.monitor.importSession();
1244
1314
  });
1245
- import_electron3.ipcMain.handle(IPC_CHANNELS.SESSION_DELETE, async (_event, sessionId) => {
1315
+ import_electron4.ipcMain.handle(IPC_CHANNELS.SESSION_DELETE, async (_event, sessionId) => {
1246
1316
  return this.monitor.deleteSession(sessionId);
1247
1317
  });
1248
- import_electron3.ipcMain.on(IPC_CHANNELS.RENDERER_REPORT, (_event, detail) => {
1318
+ import_electron4.ipcMain.on(IPC_CHANNELS.RENDERER_REPORT, (_event, detail) => {
1249
1319
  this.monitor.updateRendererDetail(detail);
1250
1320
  });
1251
1321
  }
@@ -1265,8 +1335,8 @@ var IPCMainHandler = class {
1265
1335
  unregister() {
1266
1336
  const channels = Object.values(IPC_CHANNELS);
1267
1337
  for (const channel of channels) {
1268
- import_electron3.ipcMain.removeHandler(channel);
1269
- import_electron3.ipcMain.removeAllListeners(channel);
1338
+ import_electron4.ipcMain.removeHandler(channel);
1339
+ import_electron4.ipcMain.removeAllListeners(channel);
1270
1340
  }
1271
1341
  }
1272
1342
  };
@@ -1313,6 +1383,7 @@ var ElectronMemoryMonitor = class extends import_events3.EventEmitter {
1313
1383
  this.dashboard = null;
1314
1384
  return;
1315
1385
  }
1386
+ registerDashboardSchemePrivileged();
1316
1387
  this.collector = new MemoryCollector(this.config);
1317
1388
  this.anomalyDetector = new AnomalyDetector(this.config);
1318
1389
  this.analyzer = new Analyzer();
@@ -1325,10 +1396,10 @@ var ElectronMemoryMonitor = class extends import_events3.EventEmitter {
1325
1396
  /** 启动监控 */
1326
1397
  async start() {
1327
1398
  if (!this.config.enabled || this.started) return;
1328
- if (!import_electron4.app.isReady()) {
1329
- await import_electron4.app.whenReady();
1399
+ if (!import_electron5.app.isReady()) {
1400
+ await import_electron5.app.whenReady();
1330
1401
  }
1331
- const storageDir = this.config.storage.directory || path3.join(import_electron4.app.getPath("userData"), "memory-monitor");
1402
+ const storageDir = this.config.storage.directory || path4.join(import_electron5.app.getPath("userData"), "memory-monitor");
1332
1403
  this.persister = new DataPersister(this.config, storageDir);
1333
1404
  this.sessionManager = new SessionManager(this.persister);
1334
1405
  this.ipcHandler = new IPCMainHandler(this);
@@ -1400,7 +1471,7 @@ var ElectronMemoryMonitor = class extends import_events3.EventEmitter {
1400
1471
  anomalies,
1401
1472
  completedSession.dataFile
1402
1473
  );
1403
- const reportPath = path3.join(this.persister.getStorageDir(), completedSession.id, "report.json");
1474
+ const reportPath = path4.join(this.persister.getStorageDir(), completedSession.id, "report.json");
1404
1475
  const fs2 = await import("fs");
1405
1476
  fs2.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf-8");
1406
1477
  this.emit("session-end", report);
@@ -1430,7 +1501,7 @@ var ElectronMemoryMonitor = class extends import_events3.EventEmitter {
1430
1501
  /** 获取指定会话报告 */
1431
1502
  async getSessionReport(sessionId) {
1432
1503
  const fs2 = await import("fs");
1433
- const reportPath = path3.join(this.persister.getStorageDir(), sessionId, "report.json");
1504
+ const reportPath = path4.join(this.persister.getStorageDir(), sessionId, "report.json");
1434
1505
  try {
1435
1506
  const content = fs2.readFileSync(reportPath, "utf-8");
1436
1507
  return JSON.parse(content);
@@ -1561,7 +1632,7 @@ var ElectronMemoryMonitor = class extends import_events3.EventEmitter {
1561
1632
  } catch {
1562
1633
  }
1563
1634
  }
1564
- await new Promise((resolve) => setTimeout(resolve, 100));
1635
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
1565
1636
  const afterMem = process.memoryUsage();
1566
1637
  const freed = beforeMem.heapUsed - afterMem.heapUsed;
1567
1638
  return {
@@ -1574,7 +1645,7 @@ var ElectronMemoryMonitor = class extends import_events3.EventEmitter {
1574
1645
  }
1575
1646
  /** 导出堆快照 */
1576
1647
  async takeHeapSnapshot(filePath) {
1577
- const snapshotPath = filePath || path3.join(
1648
+ const snapshotPath = filePath || path4.join(
1578
1649
  this.persister.getStorageDir(),
1579
1650
  `heap-${Date.now()}.heapsnapshot`
1580
1651
  );
@@ -1635,6 +1706,7 @@ var ElectronMemoryMonitor = class extends import_events3.EventEmitter {
1635
1706
  // Annotate the CommonJS export names for ESM import in node:
1636
1707
  0 && (module.exports = {
1637
1708
  ElectronMemoryMonitor,
1638
- IPC_CHANNELS
1709
+ IPC_CHANNELS,
1710
+ registerDashboardSchemePrivileged
1639
1711
  });
1640
1712
  //# sourceMappingURL=index.js.map