@electron-memory/monitor 0.1.0 → 0.2.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/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/core/monitor.ts
2
- import { app as app2 } from "electron";
3
- import * as path3 from "path";
2
+ import { app as app3 } from "electron";
3
+ import * as path4 from "path";
4
4
  import * as v82 from "v8";
5
5
  import { EventEmitter as EventEmitter3 } from "events";
6
6
 
@@ -1070,7 +1070,149 @@ var Analyzer = class {
1070
1070
 
1071
1071
  // src/core/dashboard.ts
1072
1072
  import { BrowserWindow as BrowserWindow2 } from "electron";
1073
+ import * as path3 from "path";
1074
+
1075
+ // src/core/dashboard-protocol.ts
1076
+ import { app as app2, protocol } from "electron";
1077
+ import { readFile } from "fs/promises";
1073
1078
  import * as path2 from "path";
1079
+ var SCHEME = "emm-dashboard";
1080
+ var privilegedRegistered = false;
1081
+ var handlerRegistered = false;
1082
+ var MIME_BY_EXT = {
1083
+ ".html": "text/html; charset=utf-8",
1084
+ ".js": "text/javascript; charset=utf-8",
1085
+ ".mjs": "text/javascript; charset=utf-8",
1086
+ ".css": "text/css; charset=utf-8",
1087
+ ".json": "application/json",
1088
+ ".map": "application/json",
1089
+ ".svg": "image/svg+xml",
1090
+ ".png": "image/png",
1091
+ ".jpg": "image/jpeg",
1092
+ ".jpeg": "image/jpeg",
1093
+ ".gif": "image/gif",
1094
+ ".webp": "image/webp",
1095
+ ".ico": "image/x-icon",
1096
+ ".woff": "font/woff",
1097
+ ".woff2": "font/woff2",
1098
+ ".ttf": "font/ttf",
1099
+ ".txt": "text/plain; charset=utf-8"
1100
+ };
1101
+ function isPathInsideRoot(filePath, root) {
1102
+ const f = path2.resolve(filePath);
1103
+ const r = path2.resolve(root);
1104
+ if (f === r) {
1105
+ return true;
1106
+ }
1107
+ const sep2 = r.endsWith(path2.sep) ? r : `${r}${path2.sep}`;
1108
+ return f.startsWith(sep2);
1109
+ }
1110
+ function registerDashboardSchemePrivileged() {
1111
+ if (privilegedRegistered) {
1112
+ return;
1113
+ }
1114
+ privilegedRegistered = true;
1115
+ protocol.registerSchemesAsPrivileged([
1116
+ {
1117
+ scheme: SCHEME,
1118
+ privileges: {
1119
+ standard: true,
1120
+ secure: true,
1121
+ supportFetchAPI: true,
1122
+ corsEnabled: true,
1123
+ stream: true
1124
+ }
1125
+ }
1126
+ ]);
1127
+ }
1128
+ function ensureDashboardProtocolHandler(uiRoot) {
1129
+ if (handlerRegistered) {
1130
+ return;
1131
+ }
1132
+ if (!app2.isReady()) {
1133
+ throw new Error(
1134
+ "[@electron-memory/monitor] ensureDashboardProtocolHandler: app must be ready before opening dashboard"
1135
+ );
1136
+ }
1137
+ handlerRegistered = true;
1138
+ const base = path2.resolve(uiRoot);
1139
+ protocol.handle(SCHEME, async (request) => {
1140
+ try {
1141
+ const { pathname } = new URL(request.url);
1142
+ let rel = pathname.startsWith("/") ? pathname.slice(1) : pathname;
1143
+ if (!rel) {
1144
+ rel = "index.html";
1145
+ }
1146
+ const filePath = path2.resolve(path2.join(base, rel));
1147
+ if (!isPathInsideRoot(filePath, base)) {
1148
+ return new Response("Forbidden", { status: 403 });
1149
+ }
1150
+ const body = await readFile(filePath);
1151
+ const ext = path2.extname(filePath).toLowerCase();
1152
+ const mime = MIME_BY_EXT[ext] || "application/octet-stream";
1153
+ return new Response(body, {
1154
+ status: 200,
1155
+ headers: {
1156
+ "Content-Type": mime,
1157
+ "Cache-Control": "no-store"
1158
+ }
1159
+ });
1160
+ } catch (err) {
1161
+ const msg = err instanceof Error ? err.message : String(err);
1162
+ return new Response(msg, { status: 404 });
1163
+ }
1164
+ });
1165
+ }
1166
+ function getDashboardPageURL() {
1167
+ return `${SCHEME}://electron/index.html`;
1168
+ }
1169
+
1170
+ // src/core/dashboard-window-hooks.ts
1171
+ function shouldOpenDevToolsShortcut(input) {
1172
+ if (input.type !== "keyDown") {
1173
+ return false;
1174
+ }
1175
+ if (input.key === "F12") {
1176
+ return true;
1177
+ }
1178
+ const k = input.key.toLowerCase();
1179
+ if (k !== "i") {
1180
+ return false;
1181
+ }
1182
+ if (input.control && input.shift) {
1183
+ return true;
1184
+ }
1185
+ if (process.platform === "darwin" && input.meta && input.alt) {
1186
+ return true;
1187
+ }
1188
+ return false;
1189
+ }
1190
+ function attachDashboardWindowHooks(win, options) {
1191
+ const wc = win.webContents;
1192
+ wc.on("before-input-event", (event, input) => {
1193
+ if (!shouldOpenDevToolsShortcut(input)) {
1194
+ return;
1195
+ }
1196
+ event.preventDefault();
1197
+ if (wc.isDevToolsOpened()) {
1198
+ wc.closeDevTools();
1199
+ } else {
1200
+ wc.openDevTools({ mode: "detach" });
1201
+ }
1202
+ });
1203
+ wc.on("did-fail-load", (_e, code, desc, url) => {
1204
+ console.error("[@electron-memory/monitor] dashboard did-fail-load", code, desc, url);
1205
+ });
1206
+ let autoOpened = false;
1207
+ wc.on("did-finish-load", () => {
1208
+ if (options.openDevToolsOnStart && !autoOpened) {
1209
+ autoOpened = true;
1210
+ wc.openDevTools({ mode: "detach" });
1211
+ }
1212
+ });
1213
+ }
1214
+
1215
+ // src/core/dashboard.ts
1074
1216
  var DashboardManager = class {
1075
1217
  constructor(config) {
1076
1218
  this.window = null;
@@ -1093,7 +1235,7 @@ var DashboardManager = class {
1093
1235
  this.window.focus();
1094
1236
  return;
1095
1237
  }
1096
- const preloadPath = path2.join(__dirname, "dashboard-preload.js");
1238
+ const preloadPath = path3.join(__dirname, "dashboard-preload.js");
1097
1239
  this.window = new BrowserWindow2({
1098
1240
  width: this.config.dashboard.width,
1099
1241
  height: this.config.dashboard.height,
@@ -1102,11 +1244,16 @@ var DashboardManager = class {
1102
1244
  webPreferences: {
1103
1245
  preload: preloadPath,
1104
1246
  contextIsolation: true,
1105
- nodeIntegration: false
1247
+ nodeIntegration: false,
1248
+ devTools: true
1106
1249
  }
1107
1250
  });
1108
- const uiPath = path2.join(__dirname, "ui", "index.html");
1109
- this.window.loadFile(uiPath);
1251
+ attachDashboardWindowHooks(this.window, {
1252
+ openDevToolsOnStart: this.config.dashboard.openDevToolsOnStart
1253
+ });
1254
+ const uiRoot = path3.join(__dirname, "ui");
1255
+ ensureDashboardProtocolHandler(uiRoot);
1256
+ this.window.loadURL(getDashboardPageURL());
1110
1257
  this.window.on("closed", () => {
1111
1258
  this.window = null;
1112
1259
  });
@@ -1257,7 +1404,8 @@ var DEFAULT_CONFIG = {
1257
1404
  dashboard: {
1258
1405
  width: 1400,
1259
1406
  height: 900,
1260
- alwaysOnTop: false
1407
+ alwaysOnTop: false,
1408
+ openDevToolsOnStart: false
1261
1409
  },
1262
1410
  processLabels: {}
1263
1411
  };
@@ -1276,6 +1424,7 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1276
1424
  this.dashboard = null;
1277
1425
  return;
1278
1426
  }
1427
+ registerDashboardSchemePrivileged();
1279
1428
  this.collector = new MemoryCollector(this.config);
1280
1429
  this.anomalyDetector = new AnomalyDetector(this.config);
1281
1430
  this.analyzer = new Analyzer();
@@ -1288,10 +1437,10 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1288
1437
  /** 启动监控 */
1289
1438
  async start() {
1290
1439
  if (!this.config.enabled || this.started) return;
1291
- if (!app2.isReady()) {
1292
- await app2.whenReady();
1440
+ if (!app3.isReady()) {
1441
+ await app3.whenReady();
1293
1442
  }
1294
- const storageDir = this.config.storage.directory || path3.join(app2.getPath("userData"), "memory-monitor");
1443
+ const storageDir = this.config.storage.directory || path4.join(app3.getPath("userData"), "memory-monitor");
1295
1444
  this.persister = new DataPersister(this.config, storageDir);
1296
1445
  this.sessionManager = new SessionManager(this.persister);
1297
1446
  this.ipcHandler = new IPCMainHandler(this);
@@ -1363,7 +1512,7 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1363
1512
  anomalies,
1364
1513
  completedSession.dataFile
1365
1514
  );
1366
- const reportPath = path3.join(this.persister.getStorageDir(), completedSession.id, "report.json");
1515
+ const reportPath = path4.join(this.persister.getStorageDir(), completedSession.id, "report.json");
1367
1516
  const fs2 = await import("fs");
1368
1517
  fs2.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf-8");
1369
1518
  this.emit("session-end", report);
@@ -1393,7 +1542,7 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1393
1542
  /** 获取指定会话报告 */
1394
1543
  async getSessionReport(sessionId) {
1395
1544
  const fs2 = await import("fs");
1396
- const reportPath = path3.join(this.persister.getStorageDir(), sessionId, "report.json");
1545
+ const reportPath = path4.join(this.persister.getStorageDir(), sessionId, "report.json");
1397
1546
  try {
1398
1547
  const content = fs2.readFileSync(reportPath, "utf-8");
1399
1548
  return JSON.parse(content);
@@ -1524,7 +1673,7 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1524
1673
  } catch {
1525
1674
  }
1526
1675
  }
1527
- await new Promise((resolve) => setTimeout(resolve, 100));
1676
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
1528
1677
  const afterMem = process.memoryUsage();
1529
1678
  const freed = beforeMem.heapUsed - afterMem.heapUsed;
1530
1679
  return {
@@ -1537,7 +1686,7 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1537
1686
  }
1538
1687
  /** 导出堆快照 */
1539
1688
  async takeHeapSnapshot(filePath) {
1540
- const snapshotPath = filePath || path3.join(
1689
+ const snapshotPath = filePath || path4.join(
1541
1690
  this.persister.getStorageDir(),
1542
1691
  `heap-${Date.now()}.heapsnapshot`
1543
1692
  );
@@ -1572,7 +1721,15 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1572
1721
  this.emit("snapshot", snapshot);
1573
1722
  }
1574
1723
  mergeConfig(userConfig) {
1575
- if (!userConfig) return { ...DEFAULT_CONFIG };
1724
+ if (!userConfig) {
1725
+ return {
1726
+ ...DEFAULT_CONFIG,
1727
+ dashboard: {
1728
+ ...DEFAULT_CONFIG.dashboard,
1729
+ openDevToolsOnStart: process.env.LAUNCHER_MEMORY_MONITOR_DEVTOOLS === "1"
1730
+ }
1731
+ };
1732
+ }
1576
1733
  return {
1577
1734
  ...DEFAULT_CONFIG,
1578
1735
  ...userConfig,
@@ -1586,7 +1743,8 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1586
1743
  },
1587
1744
  dashboard: {
1588
1745
  ...DEFAULT_CONFIG.dashboard,
1589
- ...userConfig.dashboard || {}
1746
+ ...userConfig.dashboard || {},
1747
+ openDevToolsOnStart: userConfig.dashboard?.openDevToolsOnStart !== void 0 ? userConfig.dashboard.openDevToolsOnStart : process.env.LAUNCHER_MEMORY_MONITOR_DEVTOOLS === "1"
1590
1748
  },
1591
1749
  processLabels: {
1592
1750
  ...DEFAULT_CONFIG.processLabels,
@@ -1597,6 +1755,7 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1597
1755
  };
1598
1756
  export {
1599
1757
  ElectronMemoryMonitor,
1600
- IPC_CHANNELS
1758
+ IPC_CHANNELS,
1759
+ registerDashboardSchemePrivileged
1601
1760
  };
1602
1761
  //# sourceMappingURL=index.mjs.map