@electron-memory/monitor 0.2.0 → 0.2.2

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
@@ -1073,20 +1073,49 @@ import { BrowserWindow as BrowserWindow2 } from "electron";
1073
1073
  import * as path3 from "path";
1074
1074
 
1075
1075
  // src/core/dashboard-protocol.ts
1076
- import { app as app2, net, protocol } from "electron";
1076
+ import { app as app2, protocol } from "electron";
1077
+ import { readFile } from "fs/promises";
1077
1078
  import * as path2 from "path";
1078
- import { pathToFileURL } from "url";
1079
1079
  var SCHEME = "emm-dashboard";
1080
1080
  var privilegedRegistered = false;
1081
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
+ };
1082
1101
  function isPathInsideRoot(filePath, root) {
1083
1102
  const f = path2.resolve(filePath);
1084
1103
  const r = path2.resolve(root);
1085
1104
  if (f === r) {
1086
1105
  return true;
1087
1106
  }
1088
- const sep2 = r.endsWith(path2.sep) ? r : `${r}${path2.sep}`;
1089
- return f.startsWith(sep2);
1107
+ const rel = path2.relative(r, f);
1108
+ if (!rel || rel.startsWith("..") || path2.isAbsolute(rel)) {
1109
+ return false;
1110
+ }
1111
+ return true;
1112
+ }
1113
+ function corsHeaders() {
1114
+ return {
1115
+ "Access-Control-Allow-Origin": "*",
1116
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
1117
+ "Access-Control-Allow-Headers": "*"
1118
+ };
1090
1119
  }
1091
1120
  function registerDashboardSchemePrivileged() {
1092
1121
  if (privilegedRegistered) {
@@ -1117,20 +1146,39 @@ function ensureDashboardProtocolHandler(uiRoot) {
1117
1146
  }
1118
1147
  handlerRegistered = true;
1119
1148
  const base = path2.resolve(uiRoot);
1120
- protocol.handle(SCHEME, (request) => {
1149
+ protocol.handle(SCHEME, async (request) => {
1150
+ if (request.method === "OPTIONS") {
1151
+ return new Response(null, { status: 204, headers: corsHeaders() });
1152
+ }
1121
1153
  try {
1122
- const { pathname } = new URL(request.url);
1123
- let rel = pathname.startsWith("/") ? pathname.slice(1) : pathname;
1154
+ let pathname;
1155
+ try {
1156
+ pathname = decodeURIComponent(new URL(request.url).pathname);
1157
+ } catch {
1158
+ return new Response("Bad URL", { status: 400, headers: corsHeaders() });
1159
+ }
1160
+ let rel = pathname.replace(/^\/+/, "");
1124
1161
  if (!rel) {
1125
1162
  rel = "index.html";
1126
1163
  }
1127
1164
  const filePath = path2.resolve(path2.join(base, rel));
1128
1165
  if (!isPathInsideRoot(filePath, base)) {
1129
- return new Response("Forbidden", { status: 403 });
1166
+ return new Response("Forbidden", { status: 403, headers: corsHeaders() });
1130
1167
  }
1131
- return net.fetch(pathToFileURL(filePath).href);
1132
- } catch {
1133
- return new Response("Bad Request", { status: 400 });
1168
+ const body = await readFile(filePath);
1169
+ const ext = path2.extname(filePath).toLowerCase();
1170
+ const mime = MIME_BY_EXT[ext] || "application/octet-stream";
1171
+ return new Response(body, {
1172
+ status: 200,
1173
+ headers: {
1174
+ "Content-Type": mime,
1175
+ "Cache-Control": "no-store",
1176
+ ...corsHeaders()
1177
+ }
1178
+ });
1179
+ } catch (err) {
1180
+ const msg = err instanceof Error ? err.message : String(err);
1181
+ return new Response(msg, { status: 404, headers: corsHeaders() });
1134
1182
  }
1135
1183
  });
1136
1184
  }
@@ -1138,6 +1186,51 @@ function getDashboardPageURL() {
1138
1186
  return `${SCHEME}://electron/index.html`;
1139
1187
  }
1140
1188
 
1189
+ // src/core/dashboard-window-hooks.ts
1190
+ function shouldOpenDevToolsShortcut(input) {
1191
+ if (input.type !== "keyDown") {
1192
+ return false;
1193
+ }
1194
+ if (input.key === "F12") {
1195
+ return true;
1196
+ }
1197
+ const k = input.key.toLowerCase();
1198
+ if (k !== "i") {
1199
+ return false;
1200
+ }
1201
+ if (input.control && input.shift) {
1202
+ return true;
1203
+ }
1204
+ if (process.platform === "darwin" && input.meta && input.alt) {
1205
+ return true;
1206
+ }
1207
+ return false;
1208
+ }
1209
+ function attachDashboardWindowHooks(win, options) {
1210
+ const wc = win.webContents;
1211
+ wc.on("before-input-event", (event, input) => {
1212
+ if (!shouldOpenDevToolsShortcut(input)) {
1213
+ return;
1214
+ }
1215
+ event.preventDefault();
1216
+ if (wc.isDevToolsOpened()) {
1217
+ wc.closeDevTools();
1218
+ } else {
1219
+ wc.openDevTools({ mode: "detach" });
1220
+ }
1221
+ });
1222
+ wc.on("did-fail-load", (_e, code, desc, url) => {
1223
+ console.error("[@electron-memory/monitor] dashboard did-fail-load", code, desc, url);
1224
+ });
1225
+ let autoOpened = false;
1226
+ wc.on("did-finish-load", () => {
1227
+ if (options.openDevToolsOnStart && !autoOpened) {
1228
+ autoOpened = true;
1229
+ wc.openDevTools({ mode: "detach" });
1230
+ }
1231
+ });
1232
+ }
1233
+
1141
1234
  // src/core/dashboard.ts
1142
1235
  var DashboardManager = class {
1143
1236
  constructor(config) {
@@ -1170,9 +1263,13 @@ var DashboardManager = class {
1170
1263
  webPreferences: {
1171
1264
  preload: preloadPath,
1172
1265
  contextIsolation: true,
1173
- nodeIntegration: false
1266
+ nodeIntegration: false,
1267
+ devTools: true
1174
1268
  }
1175
1269
  });
1270
+ attachDashboardWindowHooks(this.window, {
1271
+ openDevToolsOnStart: this.config.dashboard.openDevToolsOnStart
1272
+ });
1176
1273
  const uiRoot = path3.join(__dirname, "ui");
1177
1274
  ensureDashboardProtocolHandler(uiRoot);
1178
1275
  this.window.loadURL(getDashboardPageURL());
@@ -1326,7 +1423,8 @@ var DEFAULT_CONFIG = {
1326
1423
  dashboard: {
1327
1424
  width: 1400,
1328
1425
  height: 900,
1329
- alwaysOnTop: false
1426
+ alwaysOnTop: false,
1427
+ openDevToolsOnStart: false
1330
1428
  },
1331
1429
  processLabels: {}
1332
1430
  };
@@ -1642,7 +1740,15 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1642
1740
  this.emit("snapshot", snapshot);
1643
1741
  }
1644
1742
  mergeConfig(userConfig) {
1645
- if (!userConfig) return { ...DEFAULT_CONFIG };
1743
+ if (!userConfig) {
1744
+ return {
1745
+ ...DEFAULT_CONFIG,
1746
+ dashboard: {
1747
+ ...DEFAULT_CONFIG.dashboard,
1748
+ openDevToolsOnStart: process.env.LAUNCHER_MEMORY_MONITOR_DEVTOOLS === "1"
1749
+ }
1750
+ };
1751
+ }
1646
1752
  return {
1647
1753
  ...DEFAULT_CONFIG,
1648
1754
  ...userConfig,
@@ -1656,7 +1762,8 @@ var ElectronMemoryMonitor = class extends EventEmitter3 {
1656
1762
  },
1657
1763
  dashboard: {
1658
1764
  ...DEFAULT_CONFIG.dashboard,
1659
- ...userConfig.dashboard || {}
1765
+ ...userConfig.dashboard || {},
1766
+ openDevToolsOnStart: userConfig.dashboard?.openDevToolsOnStart !== void 0 ? userConfig.dashboard.openDevToolsOnStart : process.env.LAUNCHER_MEMORY_MONITOR_DEVTOOLS === "1"
1660
1767
  },
1661
1768
  processLabels: {
1662
1769
  ...DEFAULT_CONFIG.processLabels,