@64mb/freelens-power 1.0.2 → 1.1.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.
Files changed (47) hide show
  1. package/README.md +5 -1
  2. package/out/main/common/monitoring-history.js +52 -0
  3. package/out/main/common/monitoring-history.js.map +1 -0
  4. package/out/main/common/store/monitoring-preferences-store.js +25 -2
  5. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  6. package/out/main/dashboard-views.js +132 -28
  7. package/out/main/dashboard-views.js.map +1 -1
  8. package/out/main/index.js +2 -0
  9. package/out/main/index.js.map +1 -1
  10. package/out/main/monitoring-history-database.js +103 -0
  11. package/out/main/monitoring-history-database.js.map +1 -0
  12. package/out/main/monitoring-history-worker.js +61 -0
  13. package/out/main/monitoring-history-worker.js.map +1 -0
  14. package/out/main/monitoring-history.js +100 -0
  15. package/out/main/monitoring-history.js.map +1 -0
  16. package/out/renderer/_virtual/_global-external_mobx.js +2 -1
  17. package/out/renderer/diagnostics.js +647 -0
  18. package/out/renderer/diagnostics.js.map +1 -0
  19. package/out/renderer/index.js +28 -1
  20. package/out/renderer/index.js.map +1 -1
  21. package/out/renderer/monitoring-collector.js +202 -0
  22. package/out/renderer/monitoring-collector.js.map +1 -0
  23. package/out/renderer/monitoring-history-collection.js +184 -0
  24. package/out/renderer/monitoring-history-collection.js.map +1 -0
  25. package/out/renderer/monitoring-history-view.js +150 -0
  26. package/out/renderer/monitoring-history-view.js.map +1 -0
  27. package/out/renderer/monitoring-history.js +19 -0
  28. package/out/renderer/monitoring-history.js.map +1 -0
  29. package/out/renderer/monitoring.js +104 -130
  30. package/out/renderer/monitoring.js.map +1 -1
  31. package/out/renderer/node-monitoring.js +116 -0
  32. package/out/renderer/node-monitoring.js.map +1 -0
  33. package/out/renderer/pod-monitoring.js +107 -8
  34. package/out/renderer/pod-monitoring.js.map +1 -1
  35. package/out/renderer/preferences/monitoring-preferences.js +55 -10
  36. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  37. package/out/renderer/src/common/monitoring-history.js +15 -0
  38. package/out/renderer/src/common/monitoring-history.js.map +1 -0
  39. package/out/renderer/src/common/monitoring-math.js +98 -0
  40. package/out/renderer/src/common/monitoring-math.js.map +1 -0
  41. package/out/renderer/src/common/resource-diagnostics.js +227 -0
  42. package/out/renderer/src/common/resource-diagnostics.js.map +1 -0
  43. package/out/renderer/src/common/store/monitoring-preferences-store.js +25 -2
  44. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  45. package/out/renderer/welcome.js +3 -1
  46. package/out/renderer/welcome.js.map +1 -1
  47. package/package.json +1 -1
@@ -0,0 +1,103 @@
1
+ //#region src/main/monitoring-history-database.ts
2
+ var MonitoringHistoryDatabase = class {
3
+ path;
4
+ maxBytes;
5
+ db;
6
+ constructor(path, maxBytes = 100 * 1024 * 1024) {
7
+ this.path = path;
8
+ this.maxBytes = maxBytes;
9
+ const { DatabaseSync } = require("node:sqlite");
10
+ this.db = new DatabaseSync(path);
11
+ this.db.exec(`PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL; PRAGMA temp_store=MEMORY; PRAGMA cache_size=-2048; PRAGMA auto_vacuum=FULL;
12
+ CREATE TABLE IF NOT EXISTS points (cluster TEXT, scope TEXT, uid TEXT, metric TEXT, resolution INTEGER, time INTEGER, min REAL, max REAL, total REAL, count INTEGER, first INTEGER, last INTEGER, PRIMARY KEY(cluster,scope,uid,metric,resolution,time)) WITHOUT ROWID;`);
13
+ this.db.exec(`PRAGMA max_page_count=${Math.max(8, Math.floor(this.maxBytes / 3 / 4096))}`);
14
+ this.prune();
15
+ }
16
+ append(samples, now = Date.now()) {
17
+ const put = this.db.prepare(`INSERT INTO points VALUES(?,?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT(cluster,scope,uid,metric,resolution,time) DO UPDATE SET min=MIN(min,excluded.min), max=MAX(max,excluded.max),total=total+excluded.total,count=count+1,first=MIN(first,excluded.first),last=MAX(last,excluded.last) WHERE excluded.last > last`);
18
+ this.prune(now);
19
+ for (const sample of samples) {
20
+ let saved = false;
21
+ for (let attempt = 0; attempt < 8 && !saved; attempt++) {
22
+ this.db.exec("BEGIN");
23
+ try {
24
+ for (const [metric, value] of Object.entries(sample.values)) for (const resolution of [
25
+ 3e4,
26
+ 3e5,
27
+ 18e5
28
+ ]) put.run(sample.clusterId, sample.scope, sample.uid, metric, resolution, Math.floor(sample.time / resolution) * resolution, value, value, value, 1, sample.time, sample.time);
29
+ this.db.exec("COMMIT");
30
+ saved = true;
31
+ } catch (error) {
32
+ try {
33
+ this.db.exec("ROLLBACK");
34
+ } catch {}
35
+ if (error.errcode !== 13) throw error;
36
+ if (!this.evictOldest()) throw new Error("History storage cannot fit a numeric sample");
37
+ }
38
+ }
39
+ if (!saved) throw new Error("History storage is full");
40
+ }
41
+ this.prune(now);
42
+ }
43
+ prune(now = Date.now()) {
44
+ this.db.prepare("DELETE FROM points WHERE time < ? OR (resolution=30000 AND time < ?) OR (resolution=300000 AND time < ?)").run(now - 6048e5, now - 72e5, now - 864e5);
45
+ while (this.occupiedBytes() > this.maxBytes / 4) if (!this.evictOldest()) break;
46
+ }
47
+ occupiedBytes() {
48
+ return (Number(this.db.prepare("PRAGMA page_count").get()?.page_count ?? 0) - Number(this.db.prepare("PRAGMA freelist_count").get()?.freelist_count ?? 0)) * Number(this.db.prepare("PRAGMA page_size").get()?.page_size ?? 4096);
49
+ }
50
+ evictOldest() {
51
+ const count = Number(this.db.prepare("SELECT count(*) AS count FROM points").get()?.count ?? 0);
52
+ if (!count) return false;
53
+ const limit = Math.min(256, Math.max(1, Math.ceil(count / 8)));
54
+ this.db.prepare("DELETE FROM points WHERE (cluster,scope,uid,metric,resolution,time) IN (SELECT cluster,scope,uid,metric,resolution,time FROM points ORDER BY time LIMIT ?)").run(limit);
55
+ const pages = Number(this.db.prepare("PRAGMA page_count").get()?.page_count ?? 0);
56
+ if (Number(this.db.prepare("PRAGMA freelist_count").get()?.freelist_count ?? 0) > pages / 4) this.db.exec("VACUUM");
57
+ return true;
58
+ }
59
+ query(query, now = Date.now()) {
60
+ this.prune(now);
61
+ return this.db.prepare(`SELECT time,resolution,metric,min,max,total/count AS avg,count,first,last FROM points WHERE cluster=? AND scope=? AND uid=? AND (CASE WHEN resolution=30000 THEN last ELSE time+resolution-1 END)>=? AND (CASE WHEN resolution=30000 THEN last ELSE time END)<=? AND ((time>=? AND resolution=30000) OR (time<? AND time>=? AND resolution=300000) OR (time<? AND resolution=1800000)) ORDER BY time LIMIT 20000`).all(query.clusterId, query.scope, query.uid, query.from, query.to, now - 72e5, now - 72e5, now - 864e5, now - 864e5).map((row) => ({
62
+ time: Number(row.resolution) === 3e4 ? Number(row.last) : Number(row.time),
63
+ resolution: Number(row.resolution),
64
+ metric: row.metric,
65
+ min: Number(row.min),
66
+ max: Number(row.max),
67
+ avg: Number(row.avg),
68
+ count: Number(row.count),
69
+ gap: Number(row.last) - Number(row.first) > Number(row.count) * 3e4 || Number(row.count) * 3e4 < Number(row.resolution)
70
+ }));
71
+ }
72
+ bytes() {
73
+ return [
74
+ this.path,
75
+ `${this.path}-journal`,
76
+ `${this.path}-wal`,
77
+ `${this.path}-shm`
78
+ ].reduce((sum, path) => {
79
+ try {
80
+ return sum + require("node:fs").statSync(path).size;
81
+ } catch {
82
+ return sum;
83
+ }
84
+ }, 0);
85
+ }
86
+ status() {
87
+ this.prune();
88
+ return {
89
+ bytes: this.bytes(),
90
+ samples: Number(this.db.prepare("SELECT count(*) AS count FROM points").get()?.count ?? 0)
91
+ };
92
+ }
93
+ clear() {
94
+ this.db.exec("DELETE FROM points; VACUUM");
95
+ }
96
+ close() {
97
+ this.db.close();
98
+ }
99
+ };
100
+ //#endregion
101
+ exports.MonitoringHistoryDatabase = MonitoringHistoryDatabase;
102
+
103
+ //# sourceMappingURL=monitoring-history-database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring-history-database.js","names":[],"sources":["../../src/main/monitoring-history-database.ts"],"sourcesContent":["import { type HistoryPoint, type HistoryQuery, type HistorySample } from \"../common/monitoring-history\";\n\nexport class MonitoringHistoryDatabase {\n private db: import(\"node:sqlite\").DatabaseSync;\n constructor(\n private path: string,\n private maxBytes = 100 * 1024 * 1024,\n ) {\n const { DatabaseSync } = require(\"node:sqlite\") as typeof import(\"node:sqlite\");\n this.db = new DatabaseSync(path);\n this.db.exec(`PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL; PRAGMA temp_store=MEMORY; PRAGMA cache_size=-2048; PRAGMA auto_vacuum=FULL;\n CREATE TABLE IF NOT EXISTS points (cluster TEXT, scope TEXT, uid TEXT, metric TEXT, resolution INTEGER, time INTEGER, min REAL, max REAL, total REAL, count INTEGER, first INTEGER, last INTEGER, PRIMARY KEY(cluster,scope,uid,metric,resolution,time)) WITHOUT ROWID;`);\n this.db.exec(`PRAGMA max_page_count=${Math.max(8, Math.floor(this.maxBytes / 3 / 4096))}`);\n this.prune();\n }\n append(samples: HistorySample[], now = Date.now()): void {\n const put = this.db.prepare(\n `INSERT INTO points VALUES(?,?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT(cluster,scope,uid,metric,resolution,time) DO UPDATE SET min=MIN(min,excluded.min), max=MAX(max,excluded.max),total=total+excluded.total,count=count+1,first=MIN(first,excluded.first),last=MAX(last,excluded.last) WHERE excluded.last > last`,\n );\n this.prune(now);\n for (const sample of samples) {\n let saved = false;\n for (let attempt = 0; attempt < 8 && !saved; attempt++) {\n this.db.exec(\"BEGIN\");\n try {\n for (const [metric, value] of Object.entries(sample.values))\n for (const resolution of [30_000, 300_000, 1_800_000]) {\n put.run(\n sample.clusterId,\n sample.scope,\n sample.uid,\n metric,\n resolution,\n Math.floor(sample.time / resolution) * resolution,\n value,\n value,\n value,\n 1,\n sample.time,\n sample.time,\n );\n }\n this.db.exec(\"COMMIT\");\n saved = true;\n } catch (error) {\n try {\n this.db.exec(\"ROLLBACK\");\n } catch {\n /* sqlite can roll back a full transaction automatically */\n }\n if ((error as { errcode?: number }).errcode !== 13) throw error;\n if (!this.evictOldest()) throw new Error(\"History storage cannot fit a numeric sample\");\n }\n }\n if (!saved) throw new Error(\"History storage is full\");\n }\n this.prune(now);\n }\n prune(now = Date.now()): void {\n this.db\n .prepare(\n \"DELETE FROM points WHERE time < ? OR (resolution=30000 AND time < ?) OR (resolution=300000 AND time < ?)\",\n )\n .run(now - 604_800_000, now - 7_200_000, now - 86_400_000);\n // reserve room for rollback journals and compaction without dropping all history\n while (this.occupiedBytes() > this.maxBytes / 4) {\n if (!this.evictOldest()) break;\n }\n }\n private occupiedBytes(): number {\n const pages = Number(this.db.prepare(\"PRAGMA page_count\").get()?.page_count ?? 0);\n const free = Number(this.db.prepare(\"PRAGMA freelist_count\").get()?.freelist_count ?? 0);\n return (pages - free) * Number(this.db.prepare(\"PRAGMA page_size\").get()?.page_size ?? 4096);\n }\n private evictOldest(): boolean {\n const count = Number(this.db.prepare(\"SELECT count(*) AS count FROM points\").get()?.count ?? 0);\n if (!count) return false;\n const limit = Math.min(256, Math.max(1, Math.ceil(count / 8)));\n this.db\n .prepare(\n \"DELETE FROM points WHERE (cluster,scope,uid,metric,resolution,time) IN (SELECT cluster,scope,uid,metric,resolution,time FROM points ORDER BY time LIMIT ?)\",\n )\n .run(limit);\n const pages = Number(this.db.prepare(\"PRAGMA page_count\").get()?.page_count ?? 0);\n const free = Number(this.db.prepare(\"PRAGMA freelist_count\").get()?.freelist_count ?? 0);\n if (free > pages / 4) this.db.exec(\"VACUUM\");\n return true;\n }\n\n query(query: HistoryQuery, now = Date.now()): HistoryPoint[] {\n this.prune(now);\n const rows = this.db\n .prepare(\n `SELECT time,resolution,metric,min,max,total/count AS avg,count,first,last FROM points WHERE cluster=? AND scope=? AND uid=? AND (CASE WHEN resolution=30000 THEN last ELSE time+resolution-1 END)>=? AND (CASE WHEN resolution=30000 THEN last ELSE time END)<=? AND ((time>=? AND resolution=30000) OR (time<? AND time>=? AND resolution=300000) OR (time<? AND resolution=1800000)) ORDER BY time LIMIT 20000`,\n )\n .all(\n query.clusterId,\n query.scope,\n query.uid,\n query.from,\n query.to,\n now - 7_200_000,\n now - 7_200_000,\n now - 86_400_000,\n now - 86_400_000,\n );\n return rows.map((row) => ({\n time: Number(row.resolution) === 30_000 ? Number(row.last) : Number(row.time),\n resolution: Number(row.resolution),\n metric: row.metric as HistoryPoint[\"metric\"],\n min: Number(row.min),\n max: Number(row.max),\n avg: Number(row.avg),\n count: Number(row.count),\n gap:\n Number(row.last) - Number(row.first) > Number(row.count) * 30_000 ||\n Number(row.count) * 30_000 < Number(row.resolution),\n }));\n }\n bytes(): number {\n return [this.path, `${this.path}-journal`, `${this.path}-wal`, `${this.path}-shm`].reduce((sum, path) => {\n try {\n return sum + require(\"node:fs\").statSync(path).size;\n } catch {\n return sum;\n }\n }, 0);\n }\n status() {\n this.prune();\n return {\n bytes: this.bytes(),\n samples: Number(this.db.prepare(\"SELECT count(*) AS count FROM points\").get()?.count ?? 0),\n };\n }\n clear(): void {\n this.db.exec(\"DELETE FROM points; VACUUM\");\n }\n close(): void {\n this.db.close();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ const require_monitoring_history_database = require("./monitoring-history-database.js");
2
+ let node_worker_threads = require("node:worker_threads");
3
+ //#region src/main/monitoring-history-worker.ts
4
+ var MonitoringHistoryWorker = class {
5
+ worker;
6
+ sequence = 0;
7
+ pending = /* @__PURE__ */ new Map();
8
+ failure;
9
+ constructor(path) {
10
+ this.worker = new node_worker_threads.Worker(`const { parentPort, workerData } = require('node:worker_threads');
11
+ const Engine = ${require_monitoring_history_database.MonitoringHistoryDatabase.toString()};
12
+ let engine;
13
+ parentPort.on('message', ({id, operation, value}) => {
14
+ try {
15
+ engine ??= new Engine(workerData);
16
+ const result = operation === 'append' ? engine.append(value) : operation === 'query' ? engine.query(value) : operation === 'clear' ? engine.clear() : engine.status();
17
+ parentPort.postMessage({ id, result });
18
+ } catch { parentPort.postMessage({ id, error: true }); }
19
+ });`, {
20
+ eval: true,
21
+ workerData: path
22
+ });
23
+ this.worker.on("message", ({ id, result, error }) => {
24
+ const pending = this.pending.get(id);
25
+ this.pending.delete(id);
26
+ if (error) pending?.reject(/* @__PURE__ */ new Error("Local SQLite history is unavailable"));
27
+ else pending?.resolve(result);
28
+ });
29
+ this.worker.on("error", () => this.fail());
30
+ this.worker.on("exit", () => this.fail());
31
+ }
32
+ fail() {
33
+ this.failure = /* @__PURE__ */ new Error("Local SQLite history is unavailable");
34
+ for (const pending of this.pending.values()) pending.reject(this.failure);
35
+ this.pending.clear();
36
+ }
37
+ call(operation, value) {
38
+ if (this.failure) return Promise.reject(this.failure);
39
+ if (this.pending.size >= 8) return Promise.reject(/* @__PURE__ */ new Error("History is busy"));
40
+ return new Promise((resolve, reject) => {
41
+ const id = ++this.sequence;
42
+ this.pending.set(id, {
43
+ resolve,
44
+ reject
45
+ });
46
+ this.worker.postMessage({
47
+ id,
48
+ operation,
49
+ value
50
+ });
51
+ });
52
+ }
53
+ close() {
54
+ this.fail();
55
+ this.worker.terminate();
56
+ }
57
+ };
58
+ //#endregion
59
+ exports.MonitoringHistoryWorker = MonitoringHistoryWorker;
60
+
61
+ //# sourceMappingURL=monitoring-history-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring-history-worker.js","names":[],"sources":["../../src/main/monitoring-history-worker.ts"],"sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport { MonitoringHistoryDatabase } from \"./monitoring-history-database\";\n\nexport class MonitoringHistoryWorker {\n private worker: Worker;\n private sequence = 0;\n private pending = new Map<number, { resolve: (value: unknown) => void; reject: (error: Error) => void }>();\n private failure?: Error;\n constructor(path: string) {\n // the serialized engine has no module-level runtime dependencies and survives bundled builds\n this.worker = new Worker(\n `const { parentPort, workerData } = require('node:worker_threads');\n const Engine = ${MonitoringHistoryDatabase.toString()};\n let engine;\n parentPort.on('message', ({id, operation, value}) => {\n try {\n engine ??= new Engine(workerData);\n const result = operation === 'append' ? engine.append(value) : operation === 'query' ? engine.query(value) : operation === 'clear' ? engine.clear() : engine.status();\n parentPort.postMessage({ id, result });\n } catch { parentPort.postMessage({ id, error: true }); }\n });`,\n { eval: true, workerData: path },\n );\n this.worker.on(\"message\", ({ id, result, error }) => {\n const pending = this.pending.get(id);\n this.pending.delete(id);\n if (error) pending?.reject(new Error(\"Local SQLite history is unavailable\"));\n else pending?.resolve(result);\n });\n this.worker.on(\"error\", () => this.fail());\n this.worker.on(\"exit\", () => this.fail());\n }\n private fail(): void {\n this.failure = new Error(\"Local SQLite history is unavailable\");\n for (const pending of this.pending.values()) pending.reject(this.failure);\n this.pending.clear();\n }\n call(operation: \"append\" | \"query\" | \"clear\" | \"status\", value?: unknown): Promise<unknown> {\n if (this.failure) return Promise.reject(this.failure);\n if (this.pending.size >= 8) return Promise.reject(new Error(\"History is busy\"));\n return new Promise((resolve, reject) => {\n const id = ++this.sequence;\n this.pending.set(id, { resolve, reject });\n this.worker.postMessage({ id, operation, value });\n });\n }\n close(): void {\n this.fail();\n void this.worker.terminate();\n }\n}\n"],"mappings":";;;AAGA,IAAa,0BAAb,MAAqC;CACnC;CACA,WAAmB;CACnB,0BAAkB,IAAI,IAAmF;CACzG;CACA,YAAY,MAAc;EAExB,KAAK,SAAS,IAAI,oBAAA,OAChB;uBACiB,oCAAA,0BAA0B,SAAS,EAAE;;;;;;;;YAStD;GAAE,MAAM;GAAM,YAAY;EAAK,CACjC;EACA,KAAK,OAAO,GAAG,YAAY,EAAE,IAAI,QAAQ,YAAY;GACnD,MAAM,UAAU,KAAK,QAAQ,IAAI,EAAE;GACnC,KAAK,QAAQ,OAAO,EAAE;GACtB,IAAI,OAAO,SAAS,uBAAO,IAAI,MAAM,qCAAqC,CAAC;QACtE,SAAS,QAAQ,MAAM;EAC9B,CAAC;EACD,KAAK,OAAO,GAAG,eAAe,KAAK,KAAK,CAAC;EACzC,KAAK,OAAO,GAAG,cAAc,KAAK,KAAK,CAAC;CAC1C;CACA,OAAqB;EACnB,KAAK,0BAAU,IAAI,MAAM,qCAAqC;EAC9D,KAAK,MAAM,WAAW,KAAK,QAAQ,OAAO,GAAG,QAAQ,OAAO,KAAK,OAAO;EACxE,KAAK,QAAQ,MAAM;CACrB;CACA,KAAK,WAAoD,OAAmC;EAC1F,IAAI,KAAK,SAAS,OAAO,QAAQ,OAAO,KAAK,OAAO;EACpD,IAAI,KAAK,QAAQ,QAAQ,GAAG,OAAO,QAAQ,uBAAO,IAAI,MAAM,iBAAiB,CAAC;EAC9E,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,KAAK,EAAE,KAAK;GAClB,KAAK,QAAQ,IAAI,IAAI;IAAE;IAAS;GAAO,CAAC;GACxC,KAAK,OAAO,YAAY;IAAE;IAAI;IAAW;GAAM,CAAC;EAClD,CAAC;CACH;CACA,QAAc;EACZ,KAAK,KAAK;EACV,KAAU,OAAO,UAAU;CAC7B;AACF"}
@@ -0,0 +1,100 @@
1
+ const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
+ const require_monitoring_history = require("./common/monitoring-history.js");
3
+ const require_monitoring_preferences_store = require("./common/store/monitoring-preferences-store.js");
4
+ const require_monitoring_history_worker = require("./monitoring-history-worker.js");
5
+ let electron = require("electron");
6
+ let node_path = require("node:path");
7
+ let node_fs = require("node:fs");
8
+ //#region src/main/monitoring-history.ts
9
+ function trusted(event) {
10
+ const window = electron.BrowserWindow.fromWebContents(event.sender);
11
+ if (!window || window.isDestroyed() || event.senderFrame !== event.sender.mainFrame) return false;
12
+ try {
13
+ const url = new URL(event.senderFrame?.url ?? "");
14
+ const senderUrl = new URL(event.sender.getURL());
15
+ return url.protocol === "https:" && senderUrl.origin === url.origin && (url.hostname === "renderer.freelens.app" || url.hostname.endsWith(".renderer.freelens.app"));
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+ function startMonitoringHistory(_extension) {
21
+ let active = true;
22
+ let database;
23
+ let error;
24
+ const databasePath = (0, node_path.join)(electron.app.getPath("userData"), "freelens-power-history", "monitoring.sqlite");
25
+ const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
26
+ const open = () => {
27
+ if (!database && !error) try {
28
+ (0, node_fs.mkdirSync)((0, node_path.join)(electron.app.getPath("userData"), "freelens-power-history"), {
29
+ recursive: true,
30
+ mode: 448
31
+ });
32
+ database = new require_monitoring_history_worker.MonitoringHistoryWorker(databasePath);
33
+ } catch {
34
+ error = "Local SQLite history is unavailable in this Freelens runtime";
35
+ }
36
+ return database;
37
+ };
38
+ electron.ipcMain.handle(require_monitoring_history.MONITORING_HISTORY_CHANNEL, async (event, request) => {
39
+ if (!active || !trusted(event)) throw new Error("History unavailable");
40
+ if (!request || ![
41
+ "status",
42
+ "clear",
43
+ "query",
44
+ "append"
45
+ ].includes(request.operation)) throw new Error("Invalid history operation");
46
+ if (request.operation === "clear") {
47
+ await open()?.call("clear");
48
+ return true;
49
+ }
50
+ if (request.operation === "status") {
51
+ const db = preferences.historyEnabled || (0, node_fs.existsSync)(databasePath) ? open() : database;
52
+ let stats = {
53
+ bytes: 0,
54
+ samples: 0
55
+ };
56
+ if (db) try {
57
+ stats = await db.call("status");
58
+ } catch {
59
+ error = "Local SQLite history is unavailable in this Freelens runtime";
60
+ }
61
+ return {
62
+ available: !error,
63
+ enabled: preferences.historyEnabled,
64
+ ...stats,
65
+ error
66
+ };
67
+ }
68
+ if (!preferences.historyEnabled) return request.operation === "query" ? [] : false;
69
+ const db = open();
70
+ if (!db) throw new Error(error);
71
+ if (request.operation === "append") {
72
+ if (!preferences.enabled) return false;
73
+ const samples = require_monitoring_history.projectHistorySamples(request.samples);
74
+ if (samples.some((sample) => !require_extensions.Main.Catalog.getClusterById(sample.clusterId))) throw new Error("Unknown cluster");
75
+ await db.call("append", samples);
76
+ return true;
77
+ }
78
+ if (request.operation !== "query") throw new Error("Invalid history operation");
79
+ const query = request.query;
80
+ const now = Date.now();
81
+ if (!require_monitoring_history.validHistoryIdentity(query) || !Number.isSafeInteger(query.from) || !Number.isSafeInteger(query.to) || query.from > query.to || query.from < now - 6048e5 || query.to > now + 3e4 || !require_extensions.Main.Catalog.getClusterById(query.clusterId)) throw new Error("Invalid history query");
82
+ return db.call("query", {
83
+ clusterId: query.clusterId,
84
+ scope: query.scope,
85
+ uid: query.uid,
86
+ from: query.from,
87
+ to: query.to
88
+ });
89
+ });
90
+ return () => {
91
+ active = false;
92
+ electron.ipcMain.removeHandler(require_monitoring_history.MONITORING_HISTORY_CHANNEL);
93
+ database?.close();
94
+ database = void 0;
95
+ };
96
+ }
97
+ //#endregion
98
+ exports.startMonitoringHistory = startMonitoringHistory;
99
+
100
+ //# sourceMappingURL=monitoring-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitoring-history.js","names":[],"sources":["../../src/main/monitoring-history.ts"],"sourcesContent":["import { existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { Main } from \"@freelensapp/extensions\";\nimport { app, BrowserWindow, type IpcMainInvokeEvent, ipcMain } from \"electron\";\nimport {\n HISTORY_RETENTION,\n type HistoryRequest,\n type HistoryStatus,\n MONITORING_HISTORY_CHANNEL,\n projectHistorySamples,\n validHistoryIdentity,\n} from \"../common/monitoring-history\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { MonitoringHistoryWorker } from \"./monitoring-history-worker\";\n\nfunction trusted(event: IpcMainInvokeEvent): boolean {\n const window = BrowserWindow.fromWebContents(event.sender);\n if (!window || window.isDestroyed() || event.senderFrame !== event.sender.mainFrame) return false;\n try {\n const url = new URL(event.senderFrame?.url ?? \"\");\n const senderUrl = new URL(event.sender.getURL());\n return (\n url.protocol === \"https:\" &&\n senderUrl.origin === url.origin &&\n (url.hostname === \"renderer.freelens.app\" || url.hostname.endsWith(\".renderer.freelens.app\"))\n );\n } catch {\n return false;\n }\n}\nexport function startMonitoringHistory(_extension: Main.LensExtension): () => void {\n let active = true;\n let database: MonitoringHistoryWorker | undefined;\n let error: string | undefined;\n const databasePath = join(app.getPath(\"userData\"), \"freelens-power-history\", \"monitoring.sqlite\");\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n const open = () => {\n if (!database && !error) {\n try {\n const directory = join(app.getPath(\"userData\"), \"freelens-power-history\");\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n database = new MonitoringHistoryWorker(databasePath);\n } catch {\n error = \"Local SQLite history is unavailable in this Freelens runtime\";\n }\n }\n return database;\n };\n ipcMain.handle(MONITORING_HISTORY_CHANNEL, async (event, request: HistoryRequest) => {\n if (!active || !trusted(event)) throw new Error(\"History unavailable\");\n if (!request || ![\"status\", \"clear\", \"query\", \"append\"].includes(request.operation))\n throw new Error(\"Invalid history operation\");\n if (request.operation === \"clear\") {\n await open()?.call(\"clear\");\n return true;\n }\n if (request.operation === \"status\") {\n const db = preferences.historyEnabled || existsSync(databasePath) ? open() : database;\n let stats = { bytes: 0, samples: 0 };\n if (db) {\n try {\n stats = (await db.call(\"status\")) as typeof stats;\n } catch {\n error = \"Local SQLite history is unavailable in this Freelens runtime\";\n }\n }\n return { available: !error, enabled: preferences.historyEnabled, ...stats, error } satisfies HistoryStatus;\n }\n if (!preferences.historyEnabled) return request.operation === \"query\" ? [] : false;\n const db = open();\n if (!db) throw new Error(error);\n if (request.operation === \"append\") {\n if (!preferences.enabled) return false;\n const samples = projectHistorySamples(request.samples);\n if (samples.some((sample) => !Main.Catalog.getClusterById(sample.clusterId))) throw new Error(\"Unknown cluster\");\n await db.call(\"append\", samples);\n return true;\n }\n if (request.operation !== \"query\") throw new Error(\"Invalid history operation\");\n const query = request.query;\n const now = Date.now();\n if (\n !validHistoryIdentity(query) ||\n !Number.isSafeInteger(query.from) ||\n !Number.isSafeInteger(query.to) ||\n query.from > query.to ||\n query.from < now - HISTORY_RETENTION ||\n query.to > now + 30_000 ||\n !Main.Catalog.getClusterById(query.clusterId)\n )\n throw new Error(\"Invalid history query\");\n return db.call(\"query\", {\n clusterId: query.clusterId,\n scope: query.scope,\n uid: query.uid,\n from: query.from,\n to: query.to,\n });\n });\n return () => {\n active = false;\n ipcMain.removeHandler(MONITORING_HISTORY_CHANNEL);\n database?.close();\n database = undefined;\n };\n}\n"],"mappings":"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"}
@@ -20,7 +20,7 @@ __m._startAction;
20
20
  var action = __m.action;
21
21
  var autorun = __m.autorun;
22
22
  __m.comparer;
23
- __m.computed;
23
+ var computed = __m.computed;
24
24
  __m.configure;
25
25
  __m.createAtom;
26
26
  __m.defineProperty;
@@ -71,6 +71,7 @@ __m.when;
71
71
  //#endregion
72
72
  exports.action = action;
73
73
  exports.autorun = autorun;
74
+ exports.computed = computed;
74
75
  exports.default = __m;
75
76
  exports.makeObservable = makeObservable;
76
77
  exports.observable = observable;