@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.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +197 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +176 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/core/monitor.ts
|
|
2
|
-
import { app as
|
|
3
|
-
import * as
|
|
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 =
|
|
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
|
-
|
|
1109
|
-
|
|
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 (!
|
|
1292
|
-
await
|
|
1440
|
+
if (!app3.isReady()) {
|
|
1441
|
+
await app3.whenReady();
|
|
1293
1442
|
}
|
|
1294
|
-
const storageDir = this.config.storage.directory ||
|
|
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 =
|
|
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 =
|
|
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((
|
|
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 ||
|
|
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)
|
|
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
|