@64mb/freelens-power 1.4.0 → 1.6.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 (107) hide show
  1. package/README.md +7 -4
  2. package/out/main/ai-ipc.js +34 -3
  3. package/out/main/ai-ipc.js.map +1 -1
  4. package/out/main/common/power-logs-limits.js +227 -0
  5. package/out/main/common/power-logs-limits.js.map +1 -0
  6. package/out/main/common/power-logs.js +32 -0
  7. package/out/main/common/power-logs.js.map +1 -0
  8. package/out/main/common/resource-search.js +61 -0
  9. package/out/main/common/resource-search.js.map +1 -0
  10. package/out/main/common/store/logs-preferences-store.js +45 -0
  11. package/out/main/common/store/logs-preferences-store.js.map +1 -0
  12. package/out/main/common/store/monitoring-preferences-store.js +5 -0
  13. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  14. package/out/main/dashboard-icon-cache.js +1 -1
  15. package/out/main/index.js +6 -0
  16. package/out/main/index.js.map +1 -1
  17. package/out/main/power-logs-budget.js +126 -0
  18. package/out/main/power-logs-budget.js.map +1 -0
  19. package/out/main/power-logs-child.js +86 -0
  20. package/out/main/power-logs-child.js.map +1 -0
  21. package/out/main/power-logs-database.js +179 -0
  22. package/out/main/power-logs-database.js.map +1 -0
  23. package/out/main/power-logs-ipc.js +275 -0
  24. package/out/main/power-logs-ipc.js.map +1 -0
  25. package/out/main/power-logs-query-child.js +23 -0
  26. package/out/main/power-logs-query-child.js.map +1 -0
  27. package/out/main/power-logs-query.js +127 -0
  28. package/out/main/power-logs-query.js.map +1 -0
  29. package/out/main/power-logs-worker.js +215 -0
  30. package/out/main/power-logs-worker.js.map +1 -0
  31. package/out/main/resource-search-database.js +209 -0
  32. package/out/main/resource-search-database.js.map +1 -0
  33. package/out/main/resource-search-worker.js +56 -0
  34. package/out/main/resource-search-worker.js.map +1 -0
  35. package/out/main/resource-search.js +155 -0
  36. package/out/main/resource-search.js.map +1 -0
  37. package/out/renderer/ai-logs-sql.js +27 -0
  38. package/out/renderer/ai-logs-sql.js.map +1 -0
  39. package/out/renderer/ai-message.js +48 -27
  40. package/out/renderer/ai-message.js.map +1 -1
  41. package/out/renderer/ai-terminal-context.js +50 -40
  42. package/out/renderer/ai-terminal-context.js.map +1 -1
  43. package/out/renderer/ai.js +15 -2
  44. package/out/renderer/ai.js.map +1 -1
  45. package/out/renderer/appearance.js +2 -1
  46. package/out/renderer/appearance.js.map +1 -1
  47. package/out/renderer/gateway-relationships-client.js +107 -0
  48. package/out/renderer/gateway-relationships-client.js.map +1 -0
  49. package/out/renderer/index.js +67 -29
  50. package/out/renderer/index.js.map +1 -1
  51. package/out/renderer/power-log-json.js +29 -0
  52. package/out/renderer/power-log-json.js.map +1 -0
  53. package/out/renderer/power-logs-client.js +35 -0
  54. package/out/renderer/power-logs-client.js.map +1 -0
  55. package/out/renderer/power-logs-collector.js +316 -0
  56. package/out/renderer/power-logs-collector.js.map +1 -0
  57. package/out/renderer/power-logs-entrypoints.js +201 -0
  58. package/out/renderer/power-logs-entrypoints.js.map +1 -0
  59. package/out/renderer/power-logs-sql-row.js +52 -0
  60. package/out/renderer/power-logs-sql-row.js.map +1 -0
  61. package/out/renderer/power-logs-transport.js +122 -0
  62. package/out/renderer/power-logs-transport.js.map +1 -0
  63. package/out/renderer/power-logs.css.js +116 -0
  64. package/out/renderer/power-logs.css.js.map +1 -0
  65. package/out/renderer/power-logs.js +1352 -0
  66. package/out/renderer/power-logs.js.map +1 -0
  67. package/out/renderer/preferences/logs-preferences.js +93 -0
  68. package/out/renderer/preferences/logs-preferences.js.map +1 -0
  69. package/out/renderer/preferences/monitoring-preferences.js +10 -0
  70. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  71. package/out/renderer/preferences/preferences.js +24 -10
  72. package/out/renderer/preferences/preferences.js.map +1 -1
  73. package/out/renderer/resource-relationships.css.js +1 -1
  74. package/out/renderer/resource-relationships.css.js.map +1 -1
  75. package/out/renderer/resource-relationships.js +46 -8
  76. package/out/renderer/resource-relationships.js.map +1 -1
  77. package/out/renderer/resource-search-client.js +116 -0
  78. package/out/renderer/resource-search-client.js.map +1 -0
  79. package/out/renderer/resource-search-discovery.js +79 -0
  80. package/out/renderer/resource-search-discovery.js.map +1 -0
  81. package/out/renderer/resource-search-model.js +2 -1
  82. package/out/renderer/resource-search-model.js.map +1 -1
  83. package/out/renderer/resource-search-routing.js +78 -0
  84. package/out/renderer/resource-search-routing.js.map +1 -0
  85. package/out/renderer/resource-search-sync.js +199 -0
  86. package/out/renderer/resource-search-sync.js.map +1 -0
  87. package/out/renderer/resource-search.js +279 -54
  88. package/out/renderer/resource-search.js.map +1 -1
  89. package/out/renderer/src/common/gateway-relationships.js +122 -0
  90. package/out/renderer/src/common/gateway-relationships.js.map +1 -0
  91. package/out/renderer/src/common/power-log-parser.js +114 -0
  92. package/out/renderer/src/common/power-log-parser.js.map +1 -0
  93. package/out/renderer/src/common/power-logs-ai.js +56 -0
  94. package/out/renderer/src/common/power-logs-ai.js.map +1 -0
  95. package/out/renderer/src/common/power-logs-limits.js +227 -0
  96. package/out/renderer/src/common/power-logs-limits.js.map +1 -0
  97. package/out/renderer/src/common/power-logs.js +32 -0
  98. package/out/renderer/src/common/power-logs.js.map +1 -0
  99. package/out/renderer/src/common/resource-relationships.js +8 -0
  100. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  101. package/out/renderer/src/common/resource-search.js +6 -0
  102. package/out/renderer/src/common/resource-search.js.map +1 -0
  103. package/out/renderer/src/common/store/logs-preferences-store.js +45 -0
  104. package/out/renderer/src/common/store/logs-preferences-store.js.map +1 -0
  105. package/out/renderer/src/common/store/monitoring-preferences-store.js +5 -0
  106. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  107. package/package.json +8 -2
package/out/main/index.js CHANGED
@@ -10,6 +10,7 @@ const require_ai_shortcuts_store = require("./common/store/ai-shortcuts-store.js
10
10
  const require_appearance_preferences_store = require("./common/store/appearance-preferences-store.js");
11
11
  const require_cluster_icons_store = require("./common/store/cluster-icons-store.js");
12
12
  const require_dashboard_preferences_store = require("./common/store/dashboard-preferences-store.js");
13
+ const require_logs_preferences_store = require("./common/store/logs-preferences-store.js");
13
14
  const require_monitoring_preferences_store = require("./common/store/monitoring-preferences-store.js");
14
15
  const require_node_shell_preferences_store = require("./common/store/node-shell-preferences-store.js");
15
16
  const require_power_preferences_store = require("./common/store/power-preferences-store.js");
@@ -24,6 +25,8 @@ const require_dashboard_views = require("./dashboard-views.js");
24
25
  const require_diagnostics_menu_order = require("./diagnostics-menu-order.js");
25
26
  const require_dock_icon = require("./dock-icon.js");
26
27
  const require_monitoring_history = require("./monitoring-history.js");
28
+ const require_resource_search = require("./resource-search.js");
29
+ const require_power_logs_ipc = require("./power-logs-ipc.js");
27
30
  const require_settings_export = require("./settings-export.js");
28
31
  const require_splash_accent = require("./splash-accent.js");
29
32
  //#region src/main/index.ts
@@ -41,6 +44,7 @@ var FreelensPowerMain = class extends require_extensions.Main.LensExtension {
41
44
  onActivate() {
42
45
  const power = require_power_preferences_store.PowerPreferencesStore.getInstanceOrCreate();
43
46
  power.loadExtension(this);
47
+ require_logs_preferences_store.LogsPreferencesStore.getInstanceOrCreate().loadExtension(this);
44
48
  require_ai_preferences_store.AiPreferencesStore.getInstanceOrCreate().loadExtension(this);
45
49
  require_ai_shortcuts_store.AiShortcutsStore.getInstanceOrCreate().loadExtension(this);
46
50
  require_read_only_preferences_store.ReadOnlyPreferencesStore.getInstanceOrCreate().loadExtension(this);
@@ -56,6 +60,8 @@ var FreelensPowerMain = class extends require_extensions.Main.LensExtension {
56
60
  for (const dispose of disposers) dispose();
57
61
  disposers = enabled ? [
58
62
  require_monitoring_history.startMonitoringHistory(this),
63
+ require_resource_search.startResourceSearchIndex(),
64
+ require_power_logs_ipc.startPowerLogs(),
59
65
  require_diagnostics_menu_order.startDiagnosticsMenuOrder(),
60
66
  require_ai_ipc.startAiIpc(),
61
67
  require_ai_models.startAiModels(),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/main/index.ts"],"sourcesContent":["import { Main } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { EXTENSION_DISPLAY_NAME, EXTENSION_ID } from \"../common/extension-info\";\nimport { AiPreferencesStore } from \"../common/store/ai-preferences-store\";\nimport { AiShortcutsStore } from \"../common/store/ai-shortcuts-store\";\nimport { AppearancePreferencesStore } from \"../common/store/appearance-preferences-store\";\nimport { ClusterIconsStore } from \"../common/store/cluster-icons-store\";\nimport { DashboardPreferencesStore } from \"../common/store/dashboard-preferences-store\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { NodeShellPreferencesStore } from \"../common/store/node-shell-preferences-store\";\nimport { PowerPreferencesStore } from \"../common/store/power-preferences-store\";\nimport { ReadOnlyPreferencesStore } from \"../common/store/read-only-preferences-store\";\nimport { startAiIpc } from \"./ai-ipc\";\nimport { startAiModels } from \"./ai-models\";\nimport { startClusterIcons } from \"./cluster-icons\";\nimport { registerClusterIconsCatalog } from \"./cluster-icons-catalog\";\nimport { registerDashboardCatalog } from \"./dashboard-catalog\";\nimport { startDashboardPicker } from \"./dashboard-picker\";\nimport { startDashboardViews } from \"./dashboard-views\";\nimport { startDiagnosticsMenuOrder } from \"./diagnostics-menu-order\";\nimport { startDockIconOverride } from \"./dock-icon\";\nimport { startMonitoringHistory } from \"./monitoring-history\";\nimport { startSettingsExport } from \"./settings-export\";\nimport { startSplashAccentOverride } from \"./splash-accent\";\n\nexport default class FreelensPowerMain extends Main.LensExtension {\n private stopFeatures?: () => void;\n\n get name() {\n return EXTENSION_DISPLAY_NAME;\n }\n get sanitizedExtensionId() {\n return EXTENSION_ID;\n }\n get storeName() {\n return this.manifest.storeName || EXTENSION_ID;\n }\n\n onActivate() {\n const power = PowerPreferencesStore.getInstanceOrCreate<PowerPreferencesStore>();\n power.loadExtension(this);\n AiPreferencesStore.getInstanceOrCreate().loadExtension(this);\n AiShortcutsStore.getInstanceOrCreate().loadExtension(this);\n ReadOnlyPreferencesStore.getInstanceOrCreate().loadExtension(this);\n NodeShellPreferencesStore.getInstanceOrCreate().loadExtension(this);\n MonitoringPreferencesStore.getInstanceOrCreate().loadExtension(this);\n AppearancePreferencesStore.getInstanceOrCreate().loadExtension(this);\n DashboardPreferencesStore.getInstanceOrCreate().loadExtension(this);\n ClusterIconsStore.getInstanceOrCreate().loadExtension(this);\n this.stopFeatures?.();\n const stopExport = startSettingsExport();\n let disposers: (() => void)[] = [];\n const stop = reaction(\n () => power.enabled,\n (enabled) => {\n for (const dispose of disposers) dispose();\n disposers = enabled\n ? [\n startMonitoringHistory(this),\n startDiagnosticsMenuOrder(),\n startAiIpc(),\n startAiModels(),\n startDockIconOverride(),\n startSplashAccentOverride(),\n startDashboardViews(),\n startDashboardPicker(),\n registerDashboardCatalog(this),\n registerClusterIconsCatalog(this),\n startClusterIcons(),\n ]\n : [];\n },\n { fireImmediately: true },\n );\n this.stopFeatures = () => {\n stop();\n stopExport();\n for (const dispose of disposers) dispose();\n disposers = [];\n };\n }\n\n onDeactivate() {\n this.stopFeatures?.();\n this.stopFeatures = undefined;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAqB,oBAArB,cAA+C,mBAAA,KAAK,cAAc;CAChE;CAEA,IAAI,OAAO;EACT,OAAO,uBAAA;CACT;CACA,IAAI,uBAAuB;EACzB,OAAO,uBAAA;CACT;CACA,IAAI,YAAY;EACd,OAAO,KAAK,SAAS,aAAA;CACvB;CAEA,aAAa;EACX,MAAM,QAAQ,gCAAA,sBAAsB,oBAA2C;EAC/E,MAAM,cAAc,IAAI;EACxB,6BAAA,mBAAmB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAC3D,2BAAA,iBAAiB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACzD,oCAAA,yBAAyB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACjE,qCAAA,0BAA0B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAClE,qCAAA,2BAA2B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACnE,qCAAA,2BAA2B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACnE,oCAAA,0BAA0B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAClE,4BAAA,kBAAkB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAC1D,KAAK,eAAe;EACpB,MAAM,aAAa,wBAAA,oBAAoB;EACvC,IAAI,YAA4B,CAAC;EACjC,MAAM,OAAO,8BAAA,eACL,MAAM,UACX,YAAY;GACX,KAAK,MAAM,WAAW,WAAW,QAAQ;GACzC,YAAY,UACR;IACE,2BAAA,uBAAuB,IAAI;IAC3B,+BAAA,0BAA0B;IAC1B,eAAA,WAAW;IACX,kBAAA,cAAc;IACd,kBAAA,sBAAsB;IACtB,sBAAA,0BAA0B;IAC1B,wBAAA,oBAAoB;IACpB,yBAAA,qBAAqB;IACrB,0BAAA,yBAAyB,IAAI;IAC7B,8BAAA,4BAA4B,IAAI;IAChC,sBAAA,kBAAkB;GACpB,IACA,CAAC;EACP,GACA,EAAE,iBAAiB,KAAK,CAC1B;EACA,KAAK,qBAAqB;GACxB,KAAK;GACL,WAAW;GACX,KAAK,MAAM,WAAW,WAAW,QAAQ;GACzC,YAAY,CAAC;EACf;CACF;CAEA,eAAe;EACb,KAAK,eAAe;EACpB,KAAK,eAAe,KAAA;CACtB;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/main/index.ts"],"sourcesContent":["import { Main } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { EXTENSION_DISPLAY_NAME, EXTENSION_ID } from \"../common/extension-info\";\nimport { AiPreferencesStore } from \"../common/store/ai-preferences-store\";\nimport { AiShortcutsStore } from \"../common/store/ai-shortcuts-store\";\nimport { AppearancePreferencesStore } from \"../common/store/appearance-preferences-store\";\nimport { ClusterIconsStore } from \"../common/store/cluster-icons-store\";\nimport { DashboardPreferencesStore } from \"../common/store/dashboard-preferences-store\";\nimport { LogsPreferencesStore } from \"../common/store/logs-preferences-store\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { NodeShellPreferencesStore } from \"../common/store/node-shell-preferences-store\";\nimport { PowerPreferencesStore } from \"../common/store/power-preferences-store\";\nimport { ReadOnlyPreferencesStore } from \"../common/store/read-only-preferences-store\";\nimport { startAiIpc } from \"./ai-ipc\";\nimport { startAiModels } from \"./ai-models\";\nimport { startClusterIcons } from \"./cluster-icons\";\nimport { registerClusterIconsCatalog } from \"./cluster-icons-catalog\";\nimport { registerDashboardCatalog } from \"./dashboard-catalog\";\nimport { startDashboardPicker } from \"./dashboard-picker\";\nimport { startDashboardViews } from \"./dashboard-views\";\nimport { startDiagnosticsMenuOrder } from \"./diagnostics-menu-order\";\nimport { startDockIconOverride } from \"./dock-icon\";\nimport { startMonitoringHistory } from \"./monitoring-history\";\nimport { startPowerLogs } from \"./power-logs-ipc\";\nimport { startResourceSearchIndex } from \"./resource-search\";\nimport { startSettingsExport } from \"./settings-export\";\nimport { startSplashAccentOverride } from \"./splash-accent\";\n\nexport default class FreelensPowerMain extends Main.LensExtension {\n private stopFeatures?: () => void;\n\n get name() {\n return EXTENSION_DISPLAY_NAME;\n }\n get sanitizedExtensionId() {\n return EXTENSION_ID;\n }\n get storeName() {\n return this.manifest.storeName || EXTENSION_ID;\n }\n\n onActivate() {\n const power = PowerPreferencesStore.getInstanceOrCreate<PowerPreferencesStore>();\n power.loadExtension(this);\n LogsPreferencesStore.getInstanceOrCreate().loadExtension(this);\n AiPreferencesStore.getInstanceOrCreate().loadExtension(this);\n AiShortcutsStore.getInstanceOrCreate().loadExtension(this);\n ReadOnlyPreferencesStore.getInstanceOrCreate().loadExtension(this);\n NodeShellPreferencesStore.getInstanceOrCreate().loadExtension(this);\n MonitoringPreferencesStore.getInstanceOrCreate().loadExtension(this);\n AppearancePreferencesStore.getInstanceOrCreate().loadExtension(this);\n DashboardPreferencesStore.getInstanceOrCreate().loadExtension(this);\n ClusterIconsStore.getInstanceOrCreate().loadExtension(this);\n this.stopFeatures?.();\n const stopExport = startSettingsExport();\n let disposers: (() => void)[] = [];\n const stop = reaction(\n () => power.enabled,\n (enabled) => {\n for (const dispose of disposers) dispose();\n disposers = enabled\n ? [\n startMonitoringHistory(this),\n startResourceSearchIndex(),\n startPowerLogs(),\n startDiagnosticsMenuOrder(),\n startAiIpc(),\n startAiModels(),\n startDockIconOverride(),\n startSplashAccentOverride(),\n startDashboardViews(),\n startDashboardPicker(),\n registerDashboardCatalog(this),\n registerClusterIconsCatalog(this),\n startClusterIcons(),\n ]\n : [];\n },\n { fireImmediately: true },\n );\n this.stopFeatures = () => {\n stop();\n stopExport();\n for (const dispose of disposers) dispose();\n disposers = [];\n };\n }\n\n onDeactivate() {\n this.stopFeatures?.();\n this.stopFeatures = undefined;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAqB,oBAArB,cAA+C,mBAAA,KAAK,cAAc;CAChE;CAEA,IAAI,OAAO;EACT,OAAO,uBAAA;CACT;CACA,IAAI,uBAAuB;EACzB,OAAO,uBAAA;CACT;CACA,IAAI,YAAY;EACd,OAAO,KAAK,SAAS,aAAA;CACvB;CAEA,aAAa;EACX,MAAM,QAAQ,gCAAA,sBAAsB,oBAA2C;EAC/E,MAAM,cAAc,IAAI;EACxB,+BAAA,qBAAqB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAC7D,6BAAA,mBAAmB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAC3D,2BAAA,iBAAiB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACzD,oCAAA,yBAAyB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACjE,qCAAA,0BAA0B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAClE,qCAAA,2BAA2B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACnE,qCAAA,2BAA2B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EACnE,oCAAA,0BAA0B,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAClE,4BAAA,kBAAkB,oBAAoB,CAAC,CAAC,cAAc,IAAI;EAC1D,KAAK,eAAe;EACpB,MAAM,aAAa,wBAAA,oBAAoB;EACvC,IAAI,YAA4B,CAAC;EACjC,MAAM,OAAO,8BAAA,eACL,MAAM,UACX,YAAY;GACX,KAAK,MAAM,WAAW,WAAW,QAAQ;GACzC,YAAY,UACR;IACE,2BAAA,uBAAuB,IAAI;IAC3B,wBAAA,yBAAyB;IACzB,uBAAA,eAAe;IACf,+BAAA,0BAA0B;IAC1B,eAAA,WAAW;IACX,kBAAA,cAAc;IACd,kBAAA,sBAAsB;IACtB,sBAAA,0BAA0B;IAC1B,wBAAA,oBAAoB;IACpB,yBAAA,qBAAqB;IACrB,0BAAA,yBAAyB,IAAI;IAC7B,8BAAA,4BAA4B,IAAI;IAChC,sBAAA,kBAAkB;GACpB,IACA,CAAC;EACP,GACA,EAAE,iBAAiB,KAAK,CAC1B;EACA,KAAK,qBAAqB;GACxB,KAAK;GACL,WAAW;GACX,KAAK,MAAM,WAAW,WAAW,QAAQ;GACzC,YAAY,CAAC;EACf;CACF;CAEA,eAAe;EACb,KAAK,eAAe;EACpB,KAAK,eAAe,KAAA;CACtB;AACF"}
@@ -0,0 +1,126 @@
1
+ const require_power_logs = require("./common/power-logs.js");
2
+ //#region src/main/power-logs-budget.ts
3
+ var budgets = /* @__PURE__ */ new Map();
4
+ var active = false;
5
+ var POWER_LOG_STREAM_LEASE_MS = 3e4;
6
+ var validId = (value) => /^[a-zA-Z0-9_.:-]{1,253}$/.test(value);
7
+ var pendingCount = (budget) => budget.pending.size + [...budget.streams.values()].filter((stream) => !stream.active).length;
8
+ function getBudget(clusterId) {
9
+ if (!active) throw new Error("Log request canceled");
10
+ if (!validId(clusterId)) throw new Error("Invalid log budget");
11
+ const now = Date.now();
12
+ for (const [key, value] of budgets) if (!value.pending.size && !value.streams.size && Math.max(value.start, value.bytes) <= now) budgets.delete(key);
13
+ let budget = budgets.get(clusterId);
14
+ if (!budget) {
15
+ if (budgets.size >= 100) throw new Error("Log request queue is full");
16
+ budget = {
17
+ start: 0,
18
+ bytes: 0,
19
+ pending: /* @__PURE__ */ new Set(),
20
+ streams: /* @__PURE__ */ new Map()
21
+ };
22
+ budgets.set(clusterId, budget);
23
+ }
24
+ return budget;
25
+ }
26
+ function activateStreams(clusterId, budget) {
27
+ for (const [id, stream] of budget.streams) {
28
+ if (stream.active) continue;
29
+ if ([...budget.streams.values()].filter((other) => other.active && other.follow === stream.follow).length >= (stream.follow ? require_power_logs.POWER_LOGS_LIMITS.liveConcurrency : require_power_logs.POWER_LOGS_LIMITS.historyConcurrency)) continue;
30
+ clearTimeout(stream.timer);
31
+ stream.active = true;
32
+ stream.timer = setTimeout(() => releasePowerLogStream(clusterId, id), POWER_LOG_STREAM_LEASE_MS);
33
+ stream.resolve();
34
+ }
35
+ }
36
+ function startPowerLogBudget() {
37
+ active = true;
38
+ }
39
+ function cancelPowerLogPermits(stop = false) {
40
+ if (stop) active = false;
41
+ for (const budget of budgets.values()) {
42
+ for (const wait of budget.pending) {
43
+ clearTimeout(wait.timer);
44
+ wait.detach();
45
+ wait.reject(/* @__PURE__ */ new Error("Log request canceled"));
46
+ }
47
+ for (const stream of budget.streams.values()) {
48
+ clearTimeout(stream.timer);
49
+ stream.detach();
50
+ if (!stream.active) stream.reject(/* @__PURE__ */ new Error("Log request canceled"));
51
+ }
52
+ }
53
+ budgets.clear();
54
+ }
55
+ async function acquirePowerLogStream(clusterId, streamId, follow, signal) {
56
+ if (signal?.aborted || !validId(streamId) || typeof follow !== "boolean") throw new Error("Log request canceled");
57
+ const budget = getBudget(clusterId);
58
+ if (budget.streams.has(streamId)) throw new Error("Log stream already exists");
59
+ if (pendingCount(budget) >= 16) throw new Error("Log request queue is full");
60
+ await new Promise((resolve, reject) => {
61
+ const abort = () => releasePowerLogStream(clusterId, streamId);
62
+ const stream = {
63
+ follow,
64
+ active: false,
65
+ timer: setTimeout(abort, POWER_LOG_STREAM_LEASE_MS),
66
+ resolve,
67
+ reject,
68
+ detach: () => signal?.removeEventListener("abort", abort)
69
+ };
70
+ budget.streams.set(streamId, stream);
71
+ signal?.addEventListener("abort", abort, { once: true });
72
+ activateStreams(clusterId, budget);
73
+ });
74
+ }
75
+ function releasePowerLogStream(clusterId, streamId) {
76
+ const budget = budgets.get(clusterId), stream = budget?.streams.get(streamId);
77
+ if (!budget || !stream) return;
78
+ clearTimeout(stream.timer);
79
+ stream.detach();
80
+ budget.streams.delete(streamId);
81
+ if (!stream.active) stream.reject(/* @__PURE__ */ new Error("Log request canceled"));
82
+ activateStreams(clusterId, budget);
83
+ }
84
+ function renewPowerLogStream(clusterId, streamId) {
85
+ const budget = budgets.get(clusterId), stream = budget?.streams.get(streamId);
86
+ if (!budget || !stream?.active) throw new Error("Log stream lease expired");
87
+ clearTimeout(stream.timer);
88
+ stream.timer = setTimeout(() => releasePowerLogStream(clusterId, streamId), POWER_LOG_STREAM_LEASE_MS);
89
+ }
90
+ async function requestPowerLogPermit(clusterId, bytes, signal) {
91
+ if (signal?.aborted) throw new Error("Log request canceled");
92
+ if (bytes !== void 0 && (!Number.isSafeInteger(bytes) || bytes < 1 || bytes > require_power_logs.POWER_LOGS_LIMITS.batchBytes)) throw new Error("Invalid log budget");
93
+ const budget = getBudget(clusterId), now = Date.now();
94
+ if (pendingCount(budget) >= 16) throw new Error("Log request queue is full");
95
+ const lane = bytes === void 0 ? "start" : "bytes";
96
+ const start = Math.max(now, budget[lane]);
97
+ budget[lane] = start + (bytes === void 0 ? 1e3 / require_power_logs.POWER_LOGS_LIMITS.requestsPerSecond : bytes / require_power_logs.POWER_LOGS_LIMITS.bytesPerSecond * 1e3);
98
+ if (start <= now) return;
99
+ await new Promise((resolve, reject) => {
100
+ const finish = (error) => {
101
+ clearTimeout(wait.timer);
102
+ wait.detach();
103
+ budget.pending.delete(wait);
104
+ if (error) reject(error);
105
+ else resolve();
106
+ };
107
+ const abort = () => finish(/* @__PURE__ */ new Error("Log request canceled"));
108
+ const wait = {
109
+ timer: setTimeout(() => finish(), Math.ceil(start - now)),
110
+ reject,
111
+ detach: () => signal?.removeEventListener("abort", abort)
112
+ };
113
+ budget.pending.add(wait);
114
+ signal?.addEventListener("abort", abort, { once: true });
115
+ });
116
+ }
117
+ //#endregion
118
+ exports.POWER_LOG_STREAM_LEASE_MS = POWER_LOG_STREAM_LEASE_MS;
119
+ exports.acquirePowerLogStream = acquirePowerLogStream;
120
+ exports.cancelPowerLogPermits = cancelPowerLogPermits;
121
+ exports.releasePowerLogStream = releasePowerLogStream;
122
+ exports.renewPowerLogStream = renewPowerLogStream;
123
+ exports.requestPowerLogPermit = requestPowerLogPermit;
124
+ exports.startPowerLogBudget = startPowerLogBudget;
125
+
126
+ //# sourceMappingURL=power-logs-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"power-logs-budget.js","names":[],"sources":["../../src/main/power-logs-budget.ts"],"sourcesContent":["import { POWER_LOGS_LIMITS as L } from \"../common/power-logs\";\n\ntype Wait = { timer: ReturnType<typeof setTimeout>; reject: (error: Error) => void; detach: () => void };\ntype Stream = {\n follow: boolean;\n active: boolean;\n timer: ReturnType<typeof setTimeout>;\n resolve: () => void;\n reject: (error: Error) => void;\n detach: () => void;\n};\ntype Budget = { start: number; bytes: number; pending: Set<Wait>; streams: Map<string, Stream> };\nconst budgets = new Map<string, Budget>();\nlet active = false;\nexport const POWER_LOG_STREAM_LEASE_MS = 30000;\nconst validId = (value: string) => /^[a-zA-Z0-9_.:-]{1,253}$/.test(value);\nconst pendingCount = (budget: Budget) =>\n budget.pending.size + [...budget.streams.values()].filter((stream) => !stream.active).length;\nfunction getBudget(clusterId: string): Budget {\n if (!active) throw new Error(\"Log request canceled\");\n if (!validId(clusterId)) throw new Error(\"Invalid log budget\");\n const now = Date.now();\n for (const [key, value] of budgets)\n if (!value.pending.size && !value.streams.size && Math.max(value.start, value.bytes) <= now) budgets.delete(key);\n let budget = budgets.get(clusterId);\n if (!budget) {\n if (budgets.size >= 100) throw new Error(\"Log request queue is full\");\n budget = { start: 0, bytes: 0, pending: new Set(), streams: new Map() };\n budgets.set(clusterId, budget);\n }\n return budget;\n}\nfunction activateStreams(clusterId: string, budget: Budget): void {\n for (const [id, stream] of budget.streams) {\n if (stream.active) continue;\n const count = [...budget.streams.values()].filter((other) => other.active && other.follow === stream.follow).length;\n if (count >= (stream.follow ? L.liveConcurrency : L.historyConcurrency)) continue;\n clearTimeout(stream.timer);\n stream.active = true;\n stream.timer = setTimeout(() => releasePowerLogStream(clusterId, id), POWER_LOG_STREAM_LEASE_MS);\n stream.resolve();\n }\n}\nexport function startPowerLogBudget(): void {\n active = true;\n}\nexport function cancelPowerLogPermits(stop = false): void {\n if (stop) active = false;\n for (const budget of budgets.values()) {\n for (const wait of budget.pending) {\n clearTimeout(wait.timer);\n wait.detach();\n wait.reject(new Error(\"Log request canceled\"));\n }\n for (const stream of budget.streams.values()) {\n clearTimeout(stream.timer);\n stream.detach();\n if (!stream.active) stream.reject(new Error(\"Log request canceled\"));\n }\n }\n budgets.clear();\n}\nexport async function acquirePowerLogStream(\n clusterId: string,\n streamId: string,\n follow: boolean,\n signal?: AbortSignal,\n): Promise<void> {\n if (signal?.aborted || !validId(streamId) || typeof follow !== \"boolean\") throw new Error(\"Log request canceled\");\n const budget = getBudget(clusterId);\n if (budget.streams.has(streamId)) throw new Error(\"Log stream already exists\");\n if (pendingCount(budget) >= 16) throw new Error(\"Log request queue is full\");\n await new Promise<void>((resolve, reject) => {\n const abort = () => releasePowerLogStream(clusterId, streamId);\n const stream: Stream = {\n follow,\n active: false,\n timer: setTimeout(abort, POWER_LOG_STREAM_LEASE_MS),\n resolve,\n reject,\n detach: () => signal?.removeEventListener(\"abort\", abort),\n };\n budget.streams.set(streamId, stream);\n signal?.addEventListener(\"abort\", abort, { once: true });\n activateStreams(clusterId, budget);\n });\n}\nexport function releasePowerLogStream(clusterId: string, streamId: string): void {\n const budget = budgets.get(clusterId),\n stream = budget?.streams.get(streamId);\n if (!budget || !stream) return;\n clearTimeout(stream.timer);\n stream.detach();\n budget.streams.delete(streamId);\n if (!stream.active) stream.reject(new Error(\"Log request canceled\"));\n activateStreams(clusterId, budget);\n}\nexport function renewPowerLogStream(clusterId: string, streamId: string): void {\n const budget = budgets.get(clusterId),\n stream = budget?.streams.get(streamId);\n if (!budget || !stream?.active) throw new Error(\"Log stream lease expired\");\n clearTimeout(stream.timer);\n stream.timer = setTimeout(() => releasePowerLogStream(clusterId, streamId), POWER_LOG_STREAM_LEASE_MS);\n}\nexport async function requestPowerLogPermit(clusterId: string, bytes?: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) throw new Error(\"Log request canceled\");\n if (bytes !== undefined && (!Number.isSafeInteger(bytes) || bytes < 1 || bytes > L.batchBytes))\n throw new Error(\"Invalid log budget\");\n const budget = getBudget(clusterId),\n now = Date.now();\n if (pendingCount(budget) >= 16) throw new Error(\"Log request queue is full\");\n const lane = bytes === undefined ? \"start\" : \"bytes\";\n const start = Math.max(now, budget[lane]);\n budget[lane] = start + (bytes === undefined ? 1000 / L.requestsPerSecond : (bytes / L.bytesPerSecond) * 1000);\n if (start <= now) return;\n await new Promise<void>((resolve, reject) => {\n const finish = (error?: Error) => {\n clearTimeout(wait.timer);\n wait.detach();\n budget.pending.delete(wait);\n if (error) reject(error);\n else resolve();\n };\n const abort = () => finish(new Error(\"Log request canceled\"));\n const wait: Wait = {\n timer: setTimeout(() => finish(), Math.ceil(start - now)),\n reject,\n detach: () => signal?.removeEventListener(\"abort\", abort),\n };\n budget.pending.add(wait);\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_power_logs_limits = require("./common/power-logs-limits.js");
3
+ const require_power_logs_database = require("./power-logs-database.js");
4
+ let node_fs_promises = require("node:fs/promises");
5
+ //#region src/main/power-logs-child.ts
6
+ var port = process.parentPort;
7
+ if (!port) throw new Error("Log process requires parent");
8
+ var engine = new require_power_logs_database.PowerLogsDatabase(process.argv[2]);
9
+ var queries = /* @__PURE__ */ new Map();
10
+ var queryKey = (request) => JSON.stringify([
11
+ request.clusterId,
12
+ request.contextId,
13
+ request.queryId
14
+ ]);
15
+ var queue = Promise.resolve(), queuedCount = 0;
16
+ port.on("message", ({ data }) => {
17
+ if (data.request.operation === "cancel") {
18
+ const pending = queries.get(queryKey(data.request));
19
+ if (pending && !pending.started) {
20
+ if (!pending.canceled) port.postMessage({
21
+ id: pending.id,
22
+ error: "Log query canceled"
23
+ });
24
+ pending.canceled = true;
25
+ port.postMessage({
26
+ id: data.id,
27
+ result: true
28
+ });
29
+ return;
30
+ }
31
+ engine.run(data.request).then((result) => port.postMessage({
32
+ id: data.id,
33
+ result
34
+ }), () => port.postMessage({
35
+ id: data.id,
36
+ error: "Log operation failed or exceeded its limit"
37
+ }));
38
+ return;
39
+ }
40
+ const key = data.request.operation === "query" ? queryKey(data.request) : void 0;
41
+ if (key && queries.size) {
42
+ port.postMessage({
43
+ id: data.id,
44
+ error: "Log query is busy"
45
+ });
46
+ return;
47
+ }
48
+ if (queuedCount >= 16) {
49
+ port.postMessage({
50
+ id: data.id,
51
+ error: "Log operation queue is full"
52
+ });
53
+ return;
54
+ }
55
+ const pending = key ? {
56
+ id: data.id,
57
+ started: false,
58
+ canceled: false
59
+ } : void 0;
60
+ if (key && pending) queries.set(key, pending);
61
+ queuedCount++;
62
+ const run = async () => {
63
+ try {
64
+ if (pending?.canceled) return;
65
+ require_power_logs_limits.applyPowerLogsLimits(data.limits);
66
+ if (pending) pending.started = true;
67
+ const result = data.request.operation === "export" && data.destination ? await (0, node_fs_promises.copyFile)(engine.exportPath(data.request), data.destination).then(() => true) : await engine.run(data.request);
68
+ port.postMessage({
69
+ id: data.id,
70
+ result
71
+ });
72
+ } catch {
73
+ port.postMessage({
74
+ id: data.id,
75
+ error: "Log operation failed or exceeded its limit"
76
+ });
77
+ } finally {
78
+ queuedCount--;
79
+ if (key) queries.delete(key);
80
+ }
81
+ };
82
+ queue = queue.then(run);
83
+ });
84
+ //#endregion
85
+
86
+ //# sourceMappingURL=power-logs-child.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"power-logs-child.js","names":[],"sources":["../../src/main/power-logs-child.ts"],"sourcesContent":["import { copyFile } from \"node:fs/promises\";\nimport { applyPowerLogsLimits } from \"../common/power-logs-limits\";\nimport { PowerLogsDatabase } from \"./power-logs-database\";\n\nimport type { LogRequest } from \"../common/power-logs\";\n\nconst port = process.parentPort;\nif (!port) throw new Error(\"Log process requires parent\");\nconst engine = new PowerLogsDatabase(process.argv[2]);\nconst queries = new Map<string, { id: number; started: boolean; canceled: boolean }>();\nconst queryKey = (request: LogRequest & { queryId: string }) =>\n JSON.stringify([request.clusterId, request.contextId, request.queryId]);\nlet queue = Promise.resolve(),\n queuedCount = 0;\nport.on(\n \"message\",\n ({ data }: { data: { id: number; request: LogRequest; destination?: string; limits?: unknown } }) => {\n if (data.request.operation === \"cancel\") {\n const pending = queries.get(queryKey(data.request));\n if (pending && !pending.started) {\n if (!pending.canceled) port.postMessage({ id: pending.id, error: \"Log query canceled\" });\n pending.canceled = true;\n port.postMessage({ id: data.id, result: true });\n return;\n }\n void engine.run(data.request).then(\n (result) => port.postMessage({ id: data.id, result }),\n () => port.postMessage({ id: data.id, error: \"Log operation failed or exceeded its limit\" }),\n );\n return;\n }\n const key = data.request.operation === \"query\" ? queryKey(data.request) : undefined;\n if (key && queries.size) {\n port.postMessage({ id: data.id, error: \"Log query is busy\" });\n return;\n }\n if (queuedCount >= 16) {\n port.postMessage({ id: data.id, error: \"Log operation queue is full\" });\n return;\n }\n const pending = key ? { id: data.id, started: false, canceled: false } : undefined;\n if (key && pending) queries.set(key, pending);\n queuedCount++;\n const run = async () => {\n try {\n if (pending?.canceled) return;\n applyPowerLogsLimits(data.limits);\n if (pending) pending.started = true;\n const result =\n data.request.operation === \"export\" && data.destination\n ? await copyFile(engine.exportPath(data.request), data.destination).then(() => true)\n : await engine.run(data.request);\n port.postMessage({ id: data.id, result });\n } catch {\n port.postMessage({ id: data.id, error: \"Log operation failed or exceeded its limit\" });\n } finally {\n queuedCount--;\n if (key) queries.delete(key);\n }\n };\n queue = queue.then(run);\n },\n);\n"],"mappings":";;;;;AAMA,IAAM,OAAO,QAAQ;AACrB,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,6BAA6B;AACxD,IAAM,SAAS,IAAI,4BAAA,kBAAkB,QAAQ,KAAK,EAAE;AACpD,IAAM,0BAAU,IAAI,IAAiE;AACrF,IAAM,YAAY,YAChB,KAAK,UAAU;CAAC,QAAQ;CAAW,QAAQ;CAAW,QAAQ;AAAO,CAAC;AACxE,IAAI,QAAQ,QAAQ,QAAQ,GAC1B,cAAc;AAChB,KAAK,GACH,YACC,EAAE,WAAkG;CACnG,IAAI,KAAK,QAAQ,cAAc,UAAU;EACvC,MAAM,UAAU,QAAQ,IAAI,SAAS,KAAK,OAAO,CAAC;EAClD,IAAI,WAAW,CAAC,QAAQ,SAAS;GAC/B,IAAI,CAAC,QAAQ,UAAU,KAAK,YAAY;IAAE,IAAI,QAAQ;IAAI,OAAO;GAAqB,CAAC;GACvF,QAAQ,WAAW;GACnB,KAAK,YAAY;IAAE,IAAI,KAAK;IAAI,QAAQ;GAAK,CAAC;GAC9C;EACF;EACA,OAAY,IAAI,KAAK,OAAO,CAAC,CAAC,MAC3B,WAAW,KAAK,YAAY;GAAE,IAAI,KAAK;GAAI;EAAO,CAAC,SAC9C,KAAK,YAAY;GAAE,IAAI,KAAK;GAAI,OAAO;EAA6C,CAAC,CAC7F;EACA;CACF;CACA,MAAM,MAAM,KAAK,QAAQ,cAAc,UAAU,SAAS,KAAK,OAAO,IAAI,KAAA;CAC1E,IAAI,OAAO,QAAQ,MAAM;EACvB,KAAK,YAAY;GAAE,IAAI,KAAK;GAAI,OAAO;EAAoB,CAAC;EAC5D;CACF;CACA,IAAI,eAAe,IAAI;EACrB,KAAK,YAAY;GAAE,IAAI,KAAK;GAAI,OAAO;EAA8B,CAAC;EACtE;CACF;CACA,MAAM,UAAU,MAAM;EAAE,IAAI,KAAK;EAAI,SAAS;EAAO,UAAU;CAAM,IAAI,KAAA;CACzE,IAAI,OAAO,SAAS,QAAQ,IAAI,KAAK,OAAO;CAC5C;CACA,MAAM,MAAM,YAAY;EACtB,IAAI;GACF,IAAI,SAAS,UAAU;GACvB,0BAAA,qBAAqB,KAAK,MAAM;GAChC,IAAI,SAAS,QAAQ,UAAU;GAC/B,MAAM,SACJ,KAAK,QAAQ,cAAc,YAAY,KAAK,cACxC,OAAA,GAAA,iBAAA,SAAA,CAAe,OAAO,WAAW,KAAK,OAAO,GAAG,KAAK,WAAW,CAAC,CAAC,WAAW,IAAI,IACjF,MAAM,OAAO,IAAI,KAAK,OAAO;GACnC,KAAK,YAAY;IAAE,IAAI,KAAK;IAAI;GAAO,CAAC;EAC1C,QAAQ;GACN,KAAK,YAAY;IAAE,IAAI,KAAK;IAAI,OAAO;GAA6C,CAAC;EACvF,UAAU;GACR;GACA,IAAI,KAAK,QAAQ,OAAO,GAAG;EAC7B;CACF;CACA,QAAQ,MAAM,KAAK,GAAG;AACxB,CACF"}
@@ -0,0 +1,179 @@
1
+ const require_power_logs = require("./common/power-logs.js");
2
+ let node_crypto = require("node:crypto");
3
+ let node_path = require("node:path");
4
+ let node_fs = require("node:fs");
5
+ let node_sqlite = require("node:sqlite");
6
+ let node_readline = require("node:readline");
7
+ //#region src/main/power-logs-database.ts
8
+ var PowerLogsDatabase = class {
9
+ directory;
10
+ captures = /* @__PURE__ */ new Map();
11
+ closed = false;
12
+ timer;
13
+ constructor(directory) {
14
+ this.directory = directory;
15
+ (0, node_fs.mkdirSync)(directory, {
16
+ recursive: true,
17
+ mode: 448
18
+ });
19
+ (0, node_fs.chmodSync)(directory, 448);
20
+ this.timer = setInterval(() => this.expire(), 3e4);
21
+ this.timer.unref();
22
+ }
23
+ partition(request) {
24
+ return JSON.stringify([request.clusterId, request.contextId]);
25
+ }
26
+ expire() {
27
+ for (const [id, capture] of this.captures) {
28
+ if (capture.meta.expiresAt > Date.now()) continue;
29
+ try {
30
+ this.remove(id);
31
+ } catch {}
32
+ }
33
+ }
34
+ remove(id) {
35
+ const capture = this.captures.get(id);
36
+ if (!capture) return;
37
+ try {
38
+ (0, node_fs.rmSync)(capture.directory, {
39
+ recursive: true,
40
+ force: true
41
+ });
42
+ } catch {
43
+ throw new Error("Log capture storage is busy or unavailable");
44
+ }
45
+ this.captures.delete(id);
46
+ }
47
+ exportPath(request) {
48
+ return this.capture(request).spool;
49
+ }
50
+ capture(request) {
51
+ const capture = this.captures.get(request.captureId);
52
+ if (!capture || capture.partition !== this.partition(request) || capture.meta.expiresAt <= Date.now()) throw new Error("Capture unavailable");
53
+ return capture;
54
+ }
55
+ async run(request) {
56
+ if (this.closed) throw new Error("Log storage unavailable");
57
+ this.expire();
58
+ if (request.operation === "permit" || "streamId" in request) throw new Error("Log permits require main process");
59
+ const partition = this.partition(request);
60
+ if (request.operation === "cancel") return true;
61
+ if (request.operation === "list") return [...this.captures.values()].filter((c) => c.partition === partition && c.meta.expiresAt > Date.now()).map((c) => ({ ...c.meta }));
62
+ if (request.operation === "create") {
63
+ if (this.captures.size >= require_power_logs.POWER_LOGS_LIMITS.captures) throw new Error("Capture limit reached");
64
+ const id = (0, node_crypto.randomUUID)(), directory = (0, node_path.join)(this.directory, id), now = Date.now();
65
+ (0, node_fs.mkdirSync)(directory, { mode: 448 });
66
+ const spool = (0, node_path.join)(directory, "logs.ndjson");
67
+ (0, node_fs.writeFileSync)(spool, "", { mode: 384 });
68
+ const meta = {
69
+ id,
70
+ target: request.target,
71
+ createdAt: now,
72
+ expiresAt: now + require_power_logs.POWER_LOGS_LIMITS.ttlMs,
73
+ state: "collecting",
74
+ rows: 0,
75
+ bytes: 0,
76
+ notes: []
77
+ };
78
+ this.captures.set(id, {
79
+ meta,
80
+ partition,
81
+ directory,
82
+ spool,
83
+ database: (0, node_path.join)(directory, "logs.sqlite"),
84
+ limits: {
85
+ captureBytes: require_power_logs.POWER_LOGS_LIMITS.captureBytes,
86
+ captureRows: require_power_logs.POWER_LOGS_LIMITS.captureRows
87
+ }
88
+ });
89
+ return { ...meta };
90
+ }
91
+ const capture = this.capture(request);
92
+ if (request.operation === "delete") {
93
+ this.remove(request.captureId);
94
+ return true;
95
+ }
96
+ if (request.operation === "export") throw new Error("Export requires trusted destination");
97
+ if (request.operation === "append") {
98
+ if (capture.meta.state !== "collecting") throw new Error("Capture is complete");
99
+ if (request.records.length > require_power_logs.POWER_LOGS_LIMITS.batchRows) throw new Error("Log batch exceeds limit");
100
+ const data = request.records.map((record) => JSON.stringify({
101
+ ...record,
102
+ previous: record.previous === true
103
+ }) + "\n").join("");
104
+ const bytes = Buffer.byteLength(data);
105
+ if (bytes > require_power_logs.POWER_LOGS_LIMITS.batchBytes || capture.meta.bytes + bytes > capture.limits.captureBytes || capture.meta.rows + request.records.length > capture.limits.captureRows) throw new Error("Capture limit reached");
106
+ try {
107
+ (0, node_fs.appendFileSync)(capture.spool, data);
108
+ } catch {
109
+ (0, node_fs.truncateSync)(capture.spool, capture.meta.bytes);
110
+ throw new Error("Log capture storage unavailable");
111
+ }
112
+ capture.meta.rows += request.records.length;
113
+ capture.meta.bytes += bytes;
114
+ return { ...capture.meta };
115
+ }
116
+ if (request.operation === "finish") {
117
+ if (capture.meta.state !== "collecting") return { ...capture.meta };
118
+ let database;
119
+ try {
120
+ (0, node_fs.writeFileSync)(capture.database, "", { mode: 384 });
121
+ database = new node_sqlite.DatabaseSync(capture.database, { allowExtension: false });
122
+ database.exec("PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA cache_size=-16384; PRAGMA temp_store=MEMORY");
123
+ database.exec("CREATE TABLE logs(timestamp INTEGER, pod TEXT, podUid TEXT, container TEXT, level TEXT, message TEXT, fieldsJson TEXT, previous INTEGER CHECK(previous IN (0,1)))");
124
+ const insert = database.prepare("INSERT INTO logs VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
125
+ const input = (0, node_fs.createReadStream)(capture.spool);
126
+ const lines = (0, node_readline.createInterface)({
127
+ input,
128
+ crlfDelay: Infinity
129
+ });
130
+ let count = 0;
131
+ database.exec("BEGIN");
132
+ try {
133
+ for await (const line of lines) {
134
+ const record = JSON.parse(line);
135
+ insert.run(record.timestamp, record.pod, record.podUid, record.container, record.level, record.message, record.fieldsJson, record.previous ? 1 : 0);
136
+ if (++count % 5e3 === 0) {
137
+ database.exec("COMMIT; BEGIN");
138
+ await new Promise((resolve) => setImmediate(resolve));
139
+ }
140
+ }
141
+ database.exec("COMMIT");
142
+ } finally {
143
+ lines.close();
144
+ input.destroy();
145
+ }
146
+ database.exec("CREATE INDEX logs_timestamp ON logs(timestamp)");
147
+ } catch {
148
+ database?.close();
149
+ database = void 0;
150
+ (0, node_fs.rmSync)(capture.database, { force: true });
151
+ throw new Error("Log capture storage unavailable");
152
+ } finally {
153
+ database?.close();
154
+ }
155
+ capture.meta.state = request.partial ? "partial" : "ready";
156
+ capture.meta.notes = request.notes;
157
+ return { ...capture.meta };
158
+ }
159
+ if (capture.meta.state === "collecting") throw new Error("Finish capture before querying");
160
+ return {
161
+ database: capture.database,
162
+ captureId: capture.meta.id,
163
+ expiresAt: capture.meta.expiresAt
164
+ };
165
+ }
166
+ close() {
167
+ this.closed = true;
168
+ clearInterval(this.timer);
169
+ this.captures.clear();
170
+ (0, node_fs.rmSync)(this.directory, {
171
+ recursive: true,
172
+ force: true
173
+ });
174
+ }
175
+ };
176
+ //#endregion
177
+ exports.PowerLogsDatabase = PowerLogsDatabase;
178
+
179
+ //# sourceMappingURL=power-logs-database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"power-logs-database.js","names":[],"sources":["../../src/main/power-logs-database.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { appendFileSync, chmodSync, createReadStream, mkdirSync, rmSync, truncateSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { POWER_LOGS_LIMITS as L, type LogCapture, type LogRequest } from \"../common/power-logs\";\n\nimport type { PowerLogsQuerySnapshot } from \"./power-logs-query\";\n\ntype Capture = {\n meta: LogCapture;\n partition: string;\n directory: string;\n spool: string;\n database: string;\n limits: { captureBytes: number; captureRows: number };\n};\nexport class PowerLogsDatabase {\n private captures = new Map<string, Capture>();\n private closed = false;\n private timer: ReturnType<typeof setInterval>;\n constructor(private directory: string) {\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n chmodSync(directory, 0o700);\n this.timer = setInterval(() => this.expire(), 30000);\n this.timer.unref();\n }\n private partition(request: LogRequest): string {\n return JSON.stringify([request.clusterId, request.contextId]);\n }\n private expire(): void {\n for (const [id, capture] of this.captures) {\n if (capture.meta.expiresAt > Date.now()) continue;\n try {\n this.remove(id);\n } catch {\n // retain expired entries within the capture limit until a later sweep can remove locked files\n }\n }\n }\n private remove(id: string): void {\n const capture = this.captures.get(id);\n if (!capture) return;\n try {\n rmSync(capture.directory, { recursive: true, force: true });\n } catch {\n throw new Error(\"Log capture storage is busy or unavailable\");\n }\n this.captures.delete(id);\n }\n exportPath(request: LogRequest & { captureId: string }): string {\n return this.capture(request).spool;\n }\n private capture(request: LogRequest & { captureId: string }): Capture {\n const capture = this.captures.get(request.captureId);\n if (!capture || capture.partition !== this.partition(request) || capture.meta.expiresAt <= Date.now())\n throw new Error(\"Capture unavailable\");\n return capture;\n }\n async run(request: LogRequest): Promise<unknown> {\n if (this.closed) throw new Error(\"Log storage unavailable\");\n this.expire();\n if (request.operation === \"permit\" || \"streamId\" in request) throw new Error(\"Log permits require main process\");\n const partition = this.partition(request);\n if (request.operation === \"cancel\") return true;\n if (request.operation === \"list\")\n return [...this.captures.values()]\n .filter((c) => c.partition === partition && c.meta.expiresAt > Date.now())\n .map((c) => ({ ...c.meta }));\n if (request.operation === \"create\") {\n if (this.captures.size >= L.captures) throw new Error(\"Capture limit reached\");\n const id = randomUUID(),\n directory = join(this.directory, id),\n now = Date.now();\n mkdirSync(directory, { mode: 0o700 });\n const spool = join(directory, \"logs.ndjson\");\n writeFileSync(spool, \"\", { mode: 0o600 });\n const meta: LogCapture = {\n id,\n target: request.target,\n createdAt: now,\n expiresAt: now + L.ttlMs,\n state: \"collecting\",\n rows: 0,\n bytes: 0,\n notes: [],\n };\n this.captures.set(id, {\n meta,\n partition,\n directory,\n spool,\n database: join(directory, \"logs.sqlite\"),\n limits: { captureBytes: L.captureBytes, captureRows: L.captureRows },\n });\n return { ...meta };\n }\n const capture = this.capture(request);\n if (request.operation === \"delete\") {\n this.remove(request.captureId);\n return true;\n }\n if (request.operation === \"export\") throw new Error(\"Export requires trusted destination\");\n if (request.operation === \"append\") {\n if (capture.meta.state !== \"collecting\") throw new Error(\"Capture is complete\");\n if (request.records.length > L.batchRows) throw new Error(\"Log batch exceeds limit\");\n const data = request.records\n .map((record) => JSON.stringify({ ...record, previous: record.previous === true }) + \"\\n\")\n .join(\"\");\n const bytes = Buffer.byteLength(data);\n if (\n bytes > L.batchBytes ||\n capture.meta.bytes + bytes > capture.limits.captureBytes ||\n capture.meta.rows + request.records.length > capture.limits.captureRows\n )\n throw new Error(\"Capture limit reached\");\n try {\n appendFileSync(capture.spool, data);\n } catch {\n truncateSync(capture.spool, capture.meta.bytes);\n throw new Error(\"Log capture storage unavailable\");\n }\n capture.meta.rows += request.records.length;\n capture.meta.bytes += bytes;\n return { ...capture.meta };\n }\n if (request.operation === \"finish\") {\n if (capture.meta.state !== \"collecting\") return { ...capture.meta };\n let database: DatabaseSync | undefined;\n try {\n writeFileSync(capture.database, \"\", { mode: 0o600 });\n database = new DatabaseSync(capture.database, { allowExtension: false });\n database.exec(\n \"PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA cache_size=-16384; PRAGMA temp_store=MEMORY\",\n );\n database.exec(\n \"CREATE TABLE logs(timestamp INTEGER, pod TEXT, podUid TEXT, container TEXT, level TEXT, message TEXT, fieldsJson TEXT, previous INTEGER CHECK(previous IN (0,1)))\",\n );\n const insert = database.prepare(\"INSERT INTO logs VALUES (?, ?, ?, ?, ?, ?, ?, ?)\");\n const input = createReadStream(capture.spool);\n const lines = createInterface({ input, crlfDelay: Infinity });\n let count = 0;\n database.exec(\"BEGIN\");\n try {\n for await (const line of lines) {\n const record = JSON.parse(line);\n insert.run(\n record.timestamp,\n record.pod,\n record.podUid,\n record.container,\n record.level,\n record.message,\n record.fieldsJson,\n record.previous ? 1 : 0,\n );\n if (++count % 5000 === 0) {\n database.exec(\"COMMIT; BEGIN\");\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n database.exec(\"COMMIT\");\n } finally {\n lines.close();\n input.destroy();\n }\n database.exec(\"CREATE INDEX logs_timestamp ON logs(timestamp)\");\n } catch {\n database?.close();\n database = undefined;\n rmSync(capture.database, { force: true });\n throw new Error(\"Log capture storage unavailable\");\n } finally {\n database?.close();\n }\n capture.meta.state = request.partial ? \"partial\" : \"ready\";\n capture.meta.notes = request.notes;\n return { ...capture.meta };\n }\n if (capture.meta.state === \"collecting\") throw new Error(\"Finish capture before querying\");\n return {\n database: capture.database,\n captureId: capture.meta.id,\n expiresAt: capture.meta.expiresAt,\n } satisfies PowerLogsQuerySnapshot;\n }\n close(): void {\n this.closed = true;\n clearInterval(this.timer);\n this.captures.clear();\n rmSync(this.directory, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}