electron-infra-kit 0.0.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.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -0
  3. package/README.zh-CN.md +308 -0
  4. package/dist/core/error/WindowError.d.ts +56 -0
  5. package/dist/core/error/WindowError.js +71 -0
  6. package/dist/core/error/WindowError.js.map +1 -0
  7. package/dist/core/error/WindowError.mjs +71 -0
  8. package/dist/core/error/WindowError.mjs.map +1 -0
  9. package/dist/core/ipc/IpcHandler.d.ts +48 -0
  10. package/dist/core/ipc/IpcHandler.js +59 -0
  11. package/dist/core/ipc/IpcHandler.js.map +1 -0
  12. package/dist/core/ipc/IpcHandler.mjs +59 -0
  13. package/dist/core/ipc/IpcHandler.mjs.map +1 -0
  14. package/dist/core/ipc/IpcRouter.d.ts +70 -0
  15. package/dist/core/ipc/IpcRouter.js +143 -0
  16. package/dist/core/ipc/IpcRouter.js.map +1 -0
  17. package/dist/core/ipc/IpcRouter.mjs +143 -0
  18. package/dist/core/ipc/IpcRouter.mjs.map +1 -0
  19. package/dist/core/ipc/index.d.ts +3 -0
  20. package/dist/core/ipc/index.js +1 -0
  21. package/dist/core/ipc/index.js.map +1 -0
  22. package/dist/core/ipc/index.mjs +1 -0
  23. package/dist/core/ipc/index.mjs.map +1 -0
  24. package/dist/core/ipc/ipc-router.type.d.ts +73 -0
  25. package/dist/core/ipc/transport/index.d.ts +65 -0
  26. package/dist/core/ipc/transport/index.js +229 -0
  27. package/dist/core/ipc/transport/index.js.map +1 -0
  28. package/dist/core/ipc/transport/index.mjs +229 -0
  29. package/dist/core/ipc/transport/index.mjs.map +1 -0
  30. package/dist/core/ipc/transport/ipc.type.d.ts +36 -0
  31. package/dist/core/lifecycle/LifecycleManager.d.ts +66 -0
  32. package/dist/core/lifecycle/LifecycleManager.js +140 -0
  33. package/dist/core/lifecycle/LifecycleManager.js.map +1 -0
  34. package/dist/core/lifecycle/LifecycleManager.mjs +140 -0
  35. package/dist/core/lifecycle/LifecycleManager.mjs.map +1 -0
  36. package/dist/core/message-bus/MessageBus.d.ts +282 -0
  37. package/dist/core/message-bus/MessageBus.js +677 -0
  38. package/dist/core/message-bus/MessageBus.js.map +1 -0
  39. package/dist/core/message-bus/MessageBus.mjs +677 -0
  40. package/dist/core/message-bus/MessageBus.mjs.map +1 -0
  41. package/dist/core/message-bus/MessageBusClient.d.ts +100 -0
  42. package/dist/core/message-bus/MessageBusClient.js +280 -0
  43. package/dist/core/message-bus/MessageBusClient.js.map +1 -0
  44. package/dist/core/message-bus/MessageBusClient.mjs +280 -0
  45. package/dist/core/message-bus/MessageBusClient.mjs.map +1 -0
  46. package/dist/core/message-bus/core/DataStoreManager.d.ts +51 -0
  47. package/dist/core/message-bus/core/DataStoreManager.js +94 -0
  48. package/dist/core/message-bus/core/DataStoreManager.js.map +1 -0
  49. package/dist/core/message-bus/core/DataStoreManager.mjs +94 -0
  50. package/dist/core/message-bus/core/DataStoreManager.mjs.map +1 -0
  51. package/dist/core/message-bus/core/ManagedPort.d.ts +26 -0
  52. package/dist/core/message-bus/core/ManagedPort.js +55 -0
  53. package/dist/core/message-bus/core/ManagedPort.js.map +1 -0
  54. package/dist/core/message-bus/core/ManagedPort.mjs +55 -0
  55. package/dist/core/message-bus/core/ManagedPort.mjs.map +1 -0
  56. package/dist/core/message-bus/core/PortManager.d.ts +47 -0
  57. package/dist/core/message-bus/core/PortManager.js +114 -0
  58. package/dist/core/message-bus/core/PortManager.js.map +1 -0
  59. package/dist/core/message-bus/core/PortManager.mjs +114 -0
  60. package/dist/core/message-bus/core/PortManager.mjs.map +1 -0
  61. package/dist/core/message-bus/core/SubscriptionManager.d.ts +36 -0
  62. package/dist/core/message-bus/core/SubscriptionManager.js +78 -0
  63. package/dist/core/message-bus/core/SubscriptionManager.js.map +1 -0
  64. package/dist/core/message-bus/core/SubscriptionManager.mjs +78 -0
  65. package/dist/core/message-bus/core/SubscriptionManager.mjs.map +1 -0
  66. package/dist/core/message-bus/core/TransactionManager.d.ts +54 -0
  67. package/dist/core/message-bus/core/TransactionManager.js +95 -0
  68. package/dist/core/message-bus/core/TransactionManager.js.map +1 -0
  69. package/dist/core/message-bus/core/TransactionManager.mjs +95 -0
  70. package/dist/core/message-bus/core/TransactionManager.mjs.map +1 -0
  71. package/dist/core/message-bus/core/index.d.ts +5 -0
  72. package/dist/core/message-bus/index.d.ts +4 -0
  73. package/dist/core/message-bus/index.js +1 -0
  74. package/dist/core/message-bus/index.js.map +1 -0
  75. package/dist/core/message-bus/index.mjs +1 -0
  76. package/dist/core/message-bus/index.mjs.map +1 -0
  77. package/dist/core/message-bus/message-bus.type.d.ts +143 -0
  78. package/dist/core/message-bus/message-bus.type.js +26 -0
  79. package/dist/core/message-bus/message-bus.type.js.map +1 -0
  80. package/dist/core/message-bus/message-bus.type.mjs +26 -0
  81. package/dist/core/message-bus/message-bus.type.mjs.map +1 -0
  82. package/dist/core/message-bus/preload.d.ts +16 -0
  83. package/dist/core/message-bus/preload.js +27 -0
  84. package/dist/core/message-bus/preload.js.map +1 -0
  85. package/dist/core/message-bus/preload.mjs +27 -0
  86. package/dist/core/message-bus/preload.mjs.map +1 -0
  87. package/dist/core/message-bus/transport/ITransport.d.ts +40 -0
  88. package/dist/core/message-bus/transport/IpcTransport.d.ts +18 -0
  89. package/dist/core/message-bus/transport/IpcTransport.js +60 -0
  90. package/dist/core/message-bus/transport/IpcTransport.js.map +1 -0
  91. package/dist/core/message-bus/transport/IpcTransport.mjs +60 -0
  92. package/dist/core/message-bus/transport/IpcTransport.mjs.map +1 -0
  93. package/dist/core/message-bus/transport/MessagePortTransport.d.ts +15 -0
  94. package/dist/core/message-bus/transport/MessagePortTransport.js +35 -0
  95. package/dist/core/message-bus/transport/MessagePortTransport.js.map +1 -0
  96. package/dist/core/message-bus/transport/MessagePortTransport.mjs +35 -0
  97. package/dist/core/message-bus/transport/MessagePortTransport.mjs.map +1 -0
  98. package/dist/core/message-bus/transport/index.d.ts +3 -0
  99. package/dist/core/window/IpcSetup.d.ts +50 -0
  100. package/dist/core/window/IpcSetup.js +96 -0
  101. package/dist/core/window/IpcSetup.js.map +1 -0
  102. package/dist/core/window/IpcSetup.mjs +96 -0
  103. package/dist/core/window/IpcSetup.mjs.map +1 -0
  104. package/dist/core/window/WindowCreator.d.ts +66 -0
  105. package/dist/core/window/WindowCreator.js +168 -0
  106. package/dist/core/window/WindowCreator.js.map +1 -0
  107. package/dist/core/window/WindowCreator.mjs +168 -0
  108. package/dist/core/window/WindowCreator.mjs.map +1 -0
  109. package/dist/core/window/WindowManager.d.ts +214 -0
  110. package/dist/core/window/WindowManager.js +583 -0
  111. package/dist/core/window/WindowManager.js.map +1 -0
  112. package/dist/core/window/WindowManager.mjs +583 -0
  113. package/dist/core/window/WindowManager.mjs.map +1 -0
  114. package/dist/core/window/WindowStore.d.ts +136 -0
  115. package/dist/core/window/WindowStore.js +436 -0
  116. package/dist/core/window/WindowStore.js.map +1 -0
  117. package/dist/core/window/WindowStore.mjs +436 -0
  118. package/dist/core/window/WindowStore.mjs.map +1 -0
  119. package/dist/core/window/constants.d.ts +17 -0
  120. package/dist/core/window/constants.js +15 -0
  121. package/dist/core/window/constants.js.map +1 -0
  122. package/dist/core/window/constants.mjs +15 -0
  123. package/dist/core/window/constants.mjs.map +1 -0
  124. package/dist/core/window/core/MetricsManager.d.ts +14 -0
  125. package/dist/core/window/core/MetricsManager.js +27 -0
  126. package/dist/core/window/core/MetricsManager.js.map +1 -0
  127. package/dist/core/window/core/MetricsManager.mjs +27 -0
  128. package/dist/core/window/core/MetricsManager.mjs.map +1 -0
  129. package/dist/core/window/core/PluginExecutor.d.ts +22 -0
  130. package/dist/core/window/core/PluginExecutor.js +110 -0
  131. package/dist/core/window/core/PluginExecutor.js.map +1 -0
  132. package/dist/core/window/core/PluginExecutor.mjs +110 -0
  133. package/dist/core/window/core/PluginExecutor.mjs.map +1 -0
  134. package/dist/core/window/core/WindowContextManager.d.ts +26 -0
  135. package/dist/core/window/core/WindowContextManager.js +59 -0
  136. package/dist/core/window/core/WindowContextManager.js.map +1 -0
  137. package/dist/core/window/core/WindowContextManager.mjs +59 -0
  138. package/dist/core/window/core/WindowContextManager.mjs.map +1 -0
  139. package/dist/core/window/core/WindowLifecycle.d.ts +15 -0
  140. package/dist/core/window/core/WindowLifecycle.js +150 -0
  141. package/dist/core/window/core/WindowLifecycle.js.map +1 -0
  142. package/dist/core/window/core/WindowLifecycle.mjs +150 -0
  143. package/dist/core/window/core/WindowLifecycle.mjs.map +1 -0
  144. package/dist/core/window/core/WindowOperator.d.ts +90 -0
  145. package/dist/core/window/core/WindowOperator.js +154 -0
  146. package/dist/core/window/core/WindowOperator.js.map +1 -0
  147. package/dist/core/window/core/WindowOperator.mjs +154 -0
  148. package/dist/core/window/core/WindowOperator.mjs.map +1 -0
  149. package/dist/core/window/core/WindowRegistry.d.ts +168 -0
  150. package/dist/core/window/core/WindowRegistry.js +331 -0
  151. package/dist/core/window/core/WindowRegistry.js.map +1 -0
  152. package/dist/core/window/core/WindowRegistry.mjs +331 -0
  153. package/dist/core/window/core/WindowRegistry.mjs.map +1 -0
  154. package/dist/core/window/core/WindowStateManager.d.ts +40 -0
  155. package/dist/core/window/core/WindowStateManager.js +110 -0
  156. package/dist/core/window/core/WindowStateManager.js.map +1 -0
  157. package/dist/core/window/core/WindowStateManager.mjs +110 -0
  158. package/dist/core/window/core/WindowStateManager.mjs.map +1 -0
  159. package/dist/core/window/index.d.ts +7 -0
  160. package/dist/core/window/index.js +1 -0
  161. package/dist/core/window/index.js.map +1 -0
  162. package/dist/core/window/index.mjs +1 -0
  163. package/dist/core/window/index.mjs.map +1 -0
  164. package/dist/core/window/window-manager.schema.d.ts +50 -0
  165. package/dist/core/window/window-manager.schema.js +87 -0
  166. package/dist/core/window/window-manager.schema.js.map +1 -0
  167. package/dist/core/window/window-manager.schema.mjs +87 -0
  168. package/dist/core/window/window-manager.schema.mjs.map +1 -0
  169. package/dist/core/window/window-manager.type.d.ts +365 -0
  170. package/dist/index.d.ts +25 -0
  171. package/dist/index.js +33 -0
  172. package/dist/index.js.map +1 -0
  173. package/dist/index.mjs +33 -0
  174. package/dist/index.mjs.map +1 -0
  175. package/dist/index.umd.js +1 -0
  176. package/dist/infrastructure/config/ConfigManager.d.ts +133 -0
  177. package/dist/infrastructure/config/ConfigManager.js +218 -0
  178. package/dist/infrastructure/config/ConfigManager.js.map +1 -0
  179. package/dist/infrastructure/config/ConfigManager.mjs +218 -0
  180. package/dist/infrastructure/config/ConfigManager.mjs.map +1 -0
  181. package/dist/infrastructure/config/index.d.ts +1 -0
  182. package/dist/infrastructure/debug/EnhancedDebugHelper.d.ts +106 -0
  183. package/dist/infrastructure/debug/EnhancedDebugHelper.js +218 -0
  184. package/dist/infrastructure/debug/EnhancedDebugHelper.js.map +1 -0
  185. package/dist/infrastructure/debug/EnhancedDebugHelper.mjs +218 -0
  186. package/dist/infrastructure/debug/EnhancedDebugHelper.mjs.map +1 -0
  187. package/dist/infrastructure/debug/PerformanceMonitor.d.ts +45 -0
  188. package/dist/infrastructure/debug/PerformanceMonitor.js +67 -0
  189. package/dist/infrastructure/debug/PerformanceMonitor.js.map +1 -0
  190. package/dist/infrastructure/debug/PerformanceMonitor.mjs +67 -0
  191. package/dist/infrastructure/debug/PerformanceMonitor.mjs.map +1 -0
  192. package/dist/infrastructure/debug/index.d.ts +22 -0
  193. package/dist/infrastructure/debug/index.js +47 -0
  194. package/dist/infrastructure/debug/index.js.map +1 -0
  195. package/dist/infrastructure/debug/index.mjs +47 -0
  196. package/dist/infrastructure/debug/index.mjs.map +1 -0
  197. package/dist/infrastructure/errors/ErrorCodes.d.ts +74 -0
  198. package/dist/infrastructure/errors/ErrorCodes.js +78 -0
  199. package/dist/infrastructure/errors/ErrorCodes.js.map +1 -0
  200. package/dist/infrastructure/errors/ErrorCodes.mjs +78 -0
  201. package/dist/infrastructure/errors/ErrorCodes.mjs.map +1 -0
  202. package/dist/infrastructure/errors/StandardError.d.ts +61 -0
  203. package/dist/infrastructure/errors/StandardError.js +84 -0
  204. package/dist/infrastructure/errors/StandardError.js.map +1 -0
  205. package/dist/infrastructure/errors/StandardError.mjs +84 -0
  206. package/dist/infrastructure/errors/StandardError.mjs.map +1 -0
  207. package/dist/infrastructure/errors/index.d.ts +13 -0
  208. package/dist/infrastructure/errors/index.js +24 -0
  209. package/dist/infrastructure/errors/index.js.map +1 -0
  210. package/dist/infrastructure/errors/index.mjs +24 -0
  211. package/dist/infrastructure/errors/index.mjs.map +1 -0
  212. package/dist/infrastructure/logger/ElectronLogger.d.ts +39 -0
  213. package/dist/infrastructure/logger/ElectronLogger.js +65 -0
  214. package/dist/infrastructure/logger/ElectronLogger.js.map +1 -0
  215. package/dist/infrastructure/logger/ElectronLogger.mjs +65 -0
  216. package/dist/infrastructure/logger/ElectronLogger.mjs.map +1 -0
  217. package/dist/infrastructure/logger/index.d.ts +2 -0
  218. package/dist/infrastructure/logger/logger.type.d.ts +8 -0
  219. package/dist/internal/types/BrandedTypes.d.ts +64 -0
  220. package/dist/internal/types/BrandedTypes.js +54 -0
  221. package/dist/internal/types/BrandedTypes.js.map +1 -0
  222. package/dist/internal/types/BrandedTypes.mjs +54 -0
  223. package/dist/internal/types/BrandedTypes.mjs.map +1 -0
  224. package/dist/internal/types/PerformanceOptions.d.ts +87 -0
  225. package/dist/internal/types/branded.d.ts +42 -0
  226. package/dist/internal/utils/MessageDispatcher.d.ts +67 -0
  227. package/dist/internal/utils/MessageDispatcher.js +96 -0
  228. package/dist/internal/utils/MessageDispatcher.js.map +1 -0
  229. package/dist/internal/utils/MessageDispatcher.mjs +96 -0
  230. package/dist/internal/utils/MessageDispatcher.mjs.map +1 -0
  231. package/dist/internal/utils/RateLimiter.d.ts +41 -0
  232. package/dist/internal/utils/RateLimiter.js +83 -0
  233. package/dist/internal/utils/RateLimiter.js.map +1 -0
  234. package/dist/internal/utils/RateLimiter.mjs +83 -0
  235. package/dist/internal/utils/RateLimiter.mjs.map +1 -0
  236. package/dist/internal/utils/StateKeeper.d.ts +125 -0
  237. package/dist/internal/utils/StateKeeper.js +334 -0
  238. package/dist/internal/utils/StateKeeper.js.map +1 -0
  239. package/dist/internal/utils/StateKeeper.mjs +334 -0
  240. package/dist/internal/utils/StateKeeper.mjs.map +1 -0
  241. package/dist/internal/utils/branded-helpers.d.ts +33 -0
  242. package/dist/internal/utils/index.d.ts +5 -0
  243. package/dist/preload/index.d.ts +45 -0
  244. package/dist/preload/index.js +91 -0
  245. package/dist/preload/index.js.map +1 -0
  246. package/dist/preload/index.mjs +91 -0
  247. package/dist/preload/index.mjs.map +1 -0
  248. package/dist/preload/preload.type.d.ts +15 -0
  249. package/dist/types.d.ts +7 -0
  250. package/dist/types.js +1 -0
  251. package/dist/types.js.map +1 -0
  252. package/dist/types.mjs +1 -0
  253. package/dist/types.mjs.map +1 -0
  254. package/package.json +143 -0
@@ -0,0 +1,334 @@
1
+ import {app,screen}from'electron';import*as fs from'fs';import*as path from'path';import*as crypto from'crypto';import {ElectronLogger}from'../../infrastructure/logger/ElectronLogger.mjs';/**
2
+ * StateKeeper.ts
3
+ * 窗口状态管理器,负责持久化和恢复 Electron 窗口的位置、大小等状态
4
+ *
5
+ * 主要功能:
6
+ * - 保存窗口状态到文件系统
7
+ * - 从文件系统加载窗口状态
8
+ * - 验证窗口状态的有效性(确保窗口在可用显示器范围内)
9
+ * - 支持延迟保存机制,避免频繁 IO 操作
10
+ */
11
+ /**
12
+ * 简单的深度对比函数,用于检查窗口状态是否发生变化
13
+ */
14
+ function isStateEqual(a, b) {
15
+ if (a === b)
16
+ return true;
17
+ if (!a || !b)
18
+ return false;
19
+ // Check groups equality
20
+ const groupsA = a.groups || [];
21
+ const groupsB = b.groups || [];
22
+ const groupsEqual = groupsA.length === groupsB.length &&
23
+ [...groupsA].sort().every((val, index) => val === [...groupsB].sort()[index]);
24
+ return (a.x === b.x &&
25
+ a.y === b.y &&
26
+ a.width === b.width &&
27
+ a.height === b.height &&
28
+ a.isMaximized === b.isMaximized &&
29
+ a.isFullScreen === b.isFullScreen &&
30
+ groupsEqual &&
31
+ // 比较 displayBounds
32
+ ((!a.displayBounds && !b.displayBounds) ||
33
+ (!!a.displayBounds &&
34
+ !!b.displayBounds &&
35
+ a.displayBounds.x === b.displayBounds.x &&
36
+ a.displayBounds.y === b.displayBounds.y &&
37
+ a.displayBounds.width === b.displayBounds.width &&
38
+ a.displayBounds.height === b.displayBounds.height)));
39
+ }
40
+ /**
41
+ * 窗口状态管理器类
42
+ * 实现了窗口状态的持久化存储和加载功能
43
+ */
44
+ class StateKeeper {
45
+ stateFile; // 状态文件路径
46
+ state = {}; // 存储所有窗口的状态
47
+ logger; // 日志记录器
48
+ saveTimer = null; // 延迟保存计时器
49
+ throttleLastSave = 0; // 节流模式下上次保存时间
50
+ saveDelay; // 延迟保存时间(毫秒)
51
+ saveStrategy; // 保存策略
52
+ // Track active state files to prevent multiple instances writing to the same file
53
+ // 跟踪活动的 State 文件,防止多个实例写入同一个文件
54
+ static activeFiles = new Set();
55
+ hasLock = false;
56
+ isWriting = false;
57
+ pendingWrite = false;
58
+ lastSavedHash = '';
59
+ /**
60
+ * 构造函数
61
+ * @param options 配置选项
62
+ */
63
+ constructor(options = {}) {
64
+ // 初始化日志记录器
65
+ this.logger = options.logger || new ElectronLogger({ appName: 'StateKeeper' });
66
+ // 初始化配置
67
+ this.saveDelay = options.saveDelay ?? 500;
68
+ this.saveStrategy = options.saveStrategy ?? 'debounce';
69
+ // 获取应用程序的数据目录
70
+ const userDataDir = app.getPath('userData');
71
+ // 设置状态文件路径
72
+ this.stateFile = options.stateFilePath || path.join(userDataDir, 'window-state.json');
73
+ // Check if file is already being used by another instance
74
+ // 检查文件是否已被另一个实例使用
75
+ if (StateKeeper.activeFiles.has(this.stateFile)) {
76
+ this.logger.warn(`State file "${this.stateFile}" is already in use by another StateKeeper instance. This may lead to data conflicts.`);
77
+ this.hasLock = false;
78
+ }
79
+ else {
80
+ StateKeeper.activeFiles.add(this.stateFile);
81
+ this.hasLock = true;
82
+ }
83
+ // 加载已保存的窗口状态
84
+ this.loadState();
85
+ // 监听 app 退出事件,强制保存
86
+ app.on('before-quit', () => {
87
+ this.flushSync();
88
+ });
89
+ }
90
+ /**
91
+ * 从文件加载窗口状态
92
+ * 私有方法,仅在构造函数中调用
93
+ */
94
+ loadState() {
95
+ try {
96
+ // 检查状态文件是否存在
97
+ if (fs.existsSync(this.stateFile)) {
98
+ // 读取文件内容
99
+ const data = fs.readFileSync(this.stateFile, 'utf-8');
100
+ // 解析 JSON 数据到状态对象
101
+ this.state = JSON.parse(data);
102
+ // Calculate initial hash
103
+ this.lastSavedHash = crypto.createHash('md5').update(data).digest('hex');
104
+ }
105
+ }
106
+ catch (error) {
107
+ // 加载失败时记录错误并初始化空状态
108
+ this.logger.error(`Failed to load window state: ${error}`);
109
+ this.state = {};
110
+ }
111
+ }
112
+ /**
113
+ * 保存指定窗口的状态
114
+ * @param windowName 窗口名称,用于标识不同窗口的状态
115
+ * @param windowState 窗口状态对象
116
+ */
117
+ saveState(windowName, windowState) {
118
+ // 1. Dirty Check (脏检查)
119
+ // 如果新状态与当前保存的状态一致,则跳过保存
120
+ const currentState = this.state[windowName];
121
+ if (isStateEqual(currentState, windowState)) {
122
+ return;
123
+ }
124
+ // 更新内存中的窗口状态
125
+ this.state[windowName] = windowState;
126
+ // 触发持久化操作(带延迟)
127
+ this.persist();
128
+ }
129
+ /**
130
+ * 立即同步保存状态(不使用延迟)
131
+ */
132
+ flushSync() {
133
+ if (this.saveTimer) {
134
+ clearTimeout(this.saveTimer);
135
+ this.saveTimer = null;
136
+ }
137
+ try {
138
+ fs.writeFileSync(this.stateFile, JSON.stringify(this.state, null, 2));
139
+ }
140
+ catch (error) {
141
+ this.logger.error(`Failed to flush window state: ${error}`);
142
+ }
143
+ }
144
+ /**
145
+ * 将内存中的状态持久化到文件
146
+ * 私有方法,使用延迟保存机制避免频繁 IO 操作
147
+ */
148
+ persist() {
149
+ if (this.saveStrategy === 'throttle') {
150
+ // 节流模式:在指定时间内只保存一次
151
+ const now = Date.now();
152
+ const timeSinceLastSave = now - this.throttleLastSave;
153
+ if (timeSinceLastSave >= this.saveDelay) {
154
+ // 立即保存
155
+ this.performSave();
156
+ this.throttleLastSave = now;
157
+ }
158
+ else {
159
+ // 安排在剩余时间后保存
160
+ if (this.saveTimer) {
161
+ clearTimeout(this.saveTimer);
162
+ }
163
+ this.saveTimer = setTimeout(() => {
164
+ this.performSave();
165
+ this.throttleLastSave = Date.now();
166
+ }, this.saveDelay - timeSinceLastSave);
167
+ }
168
+ }
169
+ else {
170
+ // 防抖模式(默认):重置计时器
171
+ if (this.saveTimer) {
172
+ clearTimeout(this.saveTimer);
173
+ }
174
+ this.saveTimer = setTimeout(() => {
175
+ this.performSave();
176
+ }, this.saveDelay);
177
+ }
178
+ }
179
+ /**
180
+ * 执行实际的保存操作
181
+ * 私有方法
182
+ */
183
+ async performSave() {
184
+ // If already writing, mark pending and return
185
+ // 如果正在写入,标记为等待写入并返回
186
+ if (this.isWriting) {
187
+ this.pendingWrite = true;
188
+ return;
189
+ }
190
+ this.isWriting = true;
191
+ const tempFile = `${this.stateFile}.tmp`; // 临时文件路径
192
+ try {
193
+ const data = JSON.stringify(this.state, null, 2);
194
+ // Optimization: Check if data has actually changed using MD5 hash
195
+ // 优化:使用 MD5 哈希检查数据是否实际发生变化
196
+ const currentHash = crypto.createHash('md5').update(data).digest('hex');
197
+ if (currentHash === this.lastSavedHash) {
198
+ // No changes, skip write
199
+ this.isWriting = false;
200
+ return;
201
+ }
202
+ // 1. Write to temporary file first (Atomic Write Step 1)
203
+ // 先写入临时文件(原子写入步骤 1)
204
+ await fs.promises.writeFile(tempFile, data);
205
+ // 2. Rename temporary file to actual file (Atomic Write Step 2)
206
+ // 重命名临时文件为实际文件(原子写入步骤 2)
207
+ // This operation is atomic on most file systems
208
+ // 此操作在大多数文件系统上是原子的
209
+ await fs.promises.rename(tempFile, this.stateFile);
210
+ this.lastSavedHash = currentHash;
211
+ }
212
+ catch (error) {
213
+ this.logger.error(`Failed to save window state: ${error}`);
214
+ // Cleanup: Try to delete the temp file if it exists
215
+ // 清理:如果临时文件存在,尝试删除
216
+ try {
217
+ await fs.promises.unlink(tempFile);
218
+ }
219
+ catch (cleanupError) {
220
+ // Ignore cleanup errors (e.g. file not found)
221
+ // 忽略清理错误(例如文件未找到)
222
+ }
223
+ }
224
+ finally {
225
+ this.saveTimer = null;
226
+ this.isWriting = false;
227
+ // If a write was requested while writing, trigger another save immediately
228
+ // 如果在写入期间请求了写入,立即触发另一次保存
229
+ if (this.pendingWrite) {
230
+ this.pendingWrite = false;
231
+ // Use setImmediate to break the call stack and allow other events
232
+ // 使用 setImmediate 打断调用栈并允许处理其他事件
233
+ setImmediate(() => this.performSave());
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * 获取指定窗口的状态
239
+ * @param windowName 窗口名称
240
+ * @param defaultWidth 默认宽度,当没有保存状态或状态无效时使用
241
+ * @param defaultHeight 默认高度,当没有保存状态或状态无效时使用
242
+ * @returns 窗口状态对象
243
+ */
244
+ getWindowState(windowName, defaultWidth = 800, defaultHeight = 600) {
245
+ // 获取保存的窗口状态
246
+ const savedState = this.state[windowName];
247
+ // 如果没有保存的状态,返回默认状态
248
+ if (!savedState) {
249
+ return {
250
+ width: defaultWidth,
251
+ height: defaultHeight,
252
+ isMaximized: false,
253
+ isFullScreen: false,
254
+ };
255
+ }
256
+ // 验证保存的状态是否有效
257
+ if (!this.isValidState(savedState)) {
258
+ // 如果状态无效,返回默认状态
259
+ return {
260
+ width: defaultWidth,
261
+ height: defaultHeight,
262
+ isMaximized: false,
263
+ isFullScreen: false,
264
+ };
265
+ }
266
+ // 返回有效的保存状态
267
+ return savedState;
268
+ }
269
+ /**
270
+ * 验证窗口状态是否有效
271
+ * 主要检查窗口是否在当前可用显示器范围内
272
+ * @param state 要验证的窗口状态
273
+ * @returns 状态是否有效
274
+ */
275
+ isValidState(state) {
276
+ // 获取所有可用显示器
277
+ const displays = screen.getAllDisplays();
278
+ // 优先检查保存的显示器信息是否与当前显示器匹配
279
+ if (state.displayBounds) {
280
+ const displayMatch = displays.find((d) => {
281
+ // 精确匹配显示器边界
282
+ return (d.bounds.x === state.displayBounds?.x &&
283
+ d.bounds.y === state.displayBounds?.y &&
284
+ d.bounds.width === state.displayBounds?.width &&
285
+ d.bounds.height === state.displayBounds?.height);
286
+ });
287
+ if (displayMatch) {
288
+ // 如果找到完全匹配的显示器,并且窗口有坐标信息,则认为状态有效
289
+ if (state.x !== undefined && state.y !== undefined) {
290
+ return true;
291
+ }
292
+ }
293
+ }
294
+ // 降级验证:检查窗口是否与任何显示器相交
295
+ const isVisible = displays.some((display) => {
296
+ const { x, y, width, height } = display.bounds;
297
+ // 如果窗口没有坐标信息,使用默认值 0
298
+ const wx = state.x || 0;
299
+ const wy = state.y || 0;
300
+ const ww = state.width;
301
+ const wh = state.height;
302
+ // 简单的矩形相交检查
303
+ return wx < x + width && wx + ww > x && wy < y + height && wy + wh > y;
304
+ });
305
+ return isVisible;
306
+ }
307
+ /**
308
+ * 移除指定窗口的状态
309
+ * @param windowName 窗口名称
310
+ */
311
+ removeState(windowName) {
312
+ // 检查是否存在该窗口的状态
313
+ if (this.state[windowName]) {
314
+ // 从内存中删除窗口状态
315
+ delete this.state[windowName];
316
+ // 触发持久化操作
317
+ this.persist();
318
+ }
319
+ }
320
+ /**
321
+ * Dispose StateKeeper instance
322
+ * 释放 StateKeeper 实例
323
+ */
324
+ dispose() {
325
+ if (this.saveTimer) {
326
+ clearTimeout(this.saveTimer);
327
+ this.saveTimer = null;
328
+ }
329
+ // Only remove from activeFiles if this instance acquired the lock
330
+ if (this.hasLock) {
331
+ StateKeeper.activeFiles.delete(this.stateFile);
332
+ }
333
+ }
334
+ }export{StateKeeper as default};//# sourceMappingURL=StateKeeper.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateKeeper.mjs","sources":["../../../src/internal/utils/StateKeeper.ts"],"sourcesContent":["/**\n * StateKeeper.ts\n * 窗口状态管理器,负责持久化和恢复 Electron 窗口的位置、大小等状态\n *\n * 主要功能:\n * - 保存窗口状态到文件系统\n * - 从文件系统加载窗口状态\n * - 验证窗口状态的有效性(确保窗口在可用显示器范围内)\n * - 支持延迟保存机制,避免频繁 IO 操作\n */\n\nimport { app, screen } from 'electron';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport { Logger, ILogger } from '@/infrastructure/logger';\n\n/**\n * 窗口状态接口,定义了需要持久化的窗口属性\n */\nexport interface WindowState {\n x?: number; // 窗口左上角 X 坐标\n y?: number; // 窗口左上角 Y 坐标\n width: number; // 窗口宽度\n height: number; // 窗口高度\n isMaximized: boolean; // 是否最大化\n isFullScreen: boolean; // 是否全屏\n displayBounds?: Electron.Rectangle; // 显示该窗口的显示器边界信息\n groups?: string[]; // 窗口所属的组\n}\n\n/**\n * StateKeeper Configuration Options\n * StateKeeper 配置选项\n */\nexport interface StateKeeperOptions {\n /**\n * Delay before saving state to disk (in milliseconds)\n * 保存状态到磁盘前的延迟时间(毫秒)\n * @default 500\n */\n saveDelay?: number;\n\n /**\n * Save strategy: 'debounce' or 'throttle'\n * 保存策略:'debounce'(防抖)或 'throttle'(节流)\n * @default 'debounce'\n */\n saveStrategy?: 'debounce' | 'throttle';\n\n /**\n * Custom logger instance\n * 自定义日志实例\n */\n logger?: ILogger;\n\n /**\n * Custom state file path\n * 自定义状态文件路径\n */\n stateFilePath?: string;\n}\n\n/**\n * 简单的深度对比函数,用于检查窗口状态是否发生变化\n */\nfunction isStateEqual(a: WindowState | undefined, b: WindowState | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n\n // Check groups equality\n const groupsA = a.groups || [];\n const groupsB = b.groups || [];\n const groupsEqual =\n groupsA.length === groupsB.length &&\n [...groupsA].sort().every((val, index) => val === [...groupsB].sort()[index]);\n\n return (\n a.x === b.x &&\n a.y === b.y &&\n a.width === b.width &&\n a.height === b.height &&\n a.isMaximized === b.isMaximized &&\n a.isFullScreen === b.isFullScreen &&\n groupsEqual &&\n // 比较 displayBounds\n ((!a.displayBounds && !b.displayBounds) ||\n (!!a.displayBounds &&\n !!b.displayBounds &&\n a.displayBounds.x === b.displayBounds.x &&\n a.displayBounds.y === b.displayBounds.y &&\n a.displayBounds.width === b.displayBounds.width &&\n a.displayBounds.height === b.displayBounds.height))\n );\n}\n\n/**\n * 窗口状态管理器类\n * 实现了窗口状态的持久化存储和加载功能\n */\nexport default class StateKeeper {\n private stateFile: string; // 状态文件路径\n private state: Record<string, WindowState> = {}; // 存储所有窗口的状态\n private logger: ILogger; // 日志记录器\n\n private saveTimer: NodeJS.Timeout | null = null; // 延迟保存计时器\n private throttleLastSave: number = 0; // 节流模式下上次保存时间\n private readonly saveDelay: number; // 延迟保存时间(毫秒)\n private readonly saveStrategy: 'debounce' | 'throttle'; // 保存策略\n\n // Track active state files to prevent multiple instances writing to the same file\n // 跟踪活动的 State 文件,防止多个实例写入同一个文件\n private static activeFiles: Set<string> = new Set();\n private hasLock: boolean = false;\n private isWriting: boolean = false;\n private pendingWrite: boolean = false;\n private lastSavedHash: string = '';\n\n /**\n * 构造函数\n * @param options 配置选项\n */\n constructor(options: StateKeeperOptions = {}) {\n // 初始化日志记录器\n this.logger = options.logger || new Logger({ appName: 'StateKeeper' });\n\n // 初始化配置\n this.saveDelay = options.saveDelay ?? 500;\n this.saveStrategy = options.saveStrategy ?? 'debounce';\n\n // 获取应用程序的数据目录\n const userDataDir = app.getPath('userData');\n // 设置状态文件路径\n this.stateFile = options.stateFilePath || path.join(userDataDir, 'window-state.json');\n\n // Check if file is already being used by another instance\n // 检查文件是否已被另一个实例使用\n if (StateKeeper.activeFiles.has(this.stateFile)) {\n this.logger.warn(\n `State file \"${this.stateFile}\" is already in use by another StateKeeper instance. This may lead to data conflicts.`\n );\n this.hasLock = false;\n } else {\n StateKeeper.activeFiles.add(this.stateFile);\n this.hasLock = true;\n }\n\n // 加载已保存的窗口状态\n this.loadState();\n\n // 监听 app 退出事件,强制保存\n app.on('before-quit', () => {\n this.flushSync();\n });\n }\n\n /**\n * 从文件加载窗口状态\n * 私有方法,仅在构造函数中调用\n */\n private loadState(): void {\n try {\n // 检查状态文件是否存在\n if (fs.existsSync(this.stateFile)) {\n // 读取文件内容\n const data = fs.readFileSync(this.stateFile, 'utf-8');\n // 解析 JSON 数据到状态对象\n this.state = JSON.parse(data);\n // Calculate initial hash\n this.lastSavedHash = crypto.createHash('md5').update(data).digest('hex');\n }\n } catch (error) {\n // 加载失败时记录错误并初始化空状态\n this.logger.error(`Failed to load window state: ${error}`);\n this.state = {};\n }\n }\n\n /**\n * 保存指定窗口的状态\n * @param windowName 窗口名称,用于标识不同窗口的状态\n * @param windowState 窗口状态对象\n */\n public saveState(windowName: string, windowState: WindowState): void {\n // 1. Dirty Check (脏检查)\n // 如果新状态与当前保存的状态一致,则跳过保存\n const currentState = this.state[windowName];\n if (isStateEqual(currentState, windowState)) {\n return;\n }\n\n // 更新内存中的窗口状态\n this.state[windowName] = windowState;\n // 触发持久化操作(带延迟)\n this.persist();\n }\n\n /**\n * 立即同步保存状态(不使用延迟)\n */\n public flushSync(): void {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n\n try {\n fs.writeFileSync(this.stateFile, JSON.stringify(this.state, null, 2));\n } catch (error) {\n this.logger.error(`Failed to flush window state: ${error}`);\n }\n }\n\n /**\n * 将内存中的状态持久化到文件\n * 私有方法,使用延迟保存机制避免频繁 IO 操作\n */\n private persist(): void {\n if (this.saveStrategy === 'throttle') {\n // 节流模式:在指定时间内只保存一次\n const now = Date.now();\n const timeSinceLastSave = now - this.throttleLastSave;\n\n if (timeSinceLastSave >= this.saveDelay) {\n // 立即保存\n this.performSave();\n this.throttleLastSave = now;\n } else {\n // 安排在剩余时间后保存\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n }\n this.saveTimer = setTimeout(() => {\n this.performSave();\n this.throttleLastSave = Date.now();\n }, this.saveDelay - timeSinceLastSave);\n }\n } else {\n // 防抖模式(默认):重置计时器\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n }\n\n this.saveTimer = setTimeout(() => {\n this.performSave();\n }, this.saveDelay);\n }\n }\n\n /**\n * 执行实际的保存操作\n * 私有方法\n */\n private async performSave(): Promise<void> {\n // If already writing, mark pending and return\n // 如果正在写入,标记为等待写入并返回\n if (this.isWriting) {\n this.pendingWrite = true;\n return;\n }\n\n this.isWriting = true;\n const tempFile = `${this.stateFile}.tmp`; // 临时文件路径\n\n try {\n const data = JSON.stringify(this.state, null, 2);\n\n // Optimization: Check if data has actually changed using MD5 hash\n // 优化:使用 MD5 哈希检查数据是否实际发生变化\n const currentHash = crypto.createHash('md5').update(data).digest('hex');\n if (currentHash === this.lastSavedHash) {\n // No changes, skip write\n this.isWriting = false;\n return;\n }\n\n // 1. Write to temporary file first (Atomic Write Step 1)\n // 先写入临时文件(原子写入步骤 1)\n await fs.promises.writeFile(tempFile, data);\n\n // 2. Rename temporary file to actual file (Atomic Write Step 2)\n // 重命名临时文件为实际文件(原子写入步骤 2)\n // This operation is atomic on most file systems\n // 此操作在大多数文件系统上是原子的\n await fs.promises.rename(tempFile, this.stateFile);\n\n this.lastSavedHash = currentHash;\n } catch (error) {\n this.logger.error(`Failed to save window state: ${error}`);\n\n // Cleanup: Try to delete the temp file if it exists\n // 清理:如果临时文件存在,尝试删除\n try {\n await fs.promises.unlink(tempFile);\n } catch (cleanupError) {\n // Ignore cleanup errors (e.g. file not found)\n // 忽略清理错误(例如文件未找到)\n }\n } finally {\n this.saveTimer = null;\n this.isWriting = false;\n\n // If a write was requested while writing, trigger another save immediately\n // 如果在写入期间请求了写入,立即触发另一次保存\n if (this.pendingWrite) {\n this.pendingWrite = false;\n // Use setImmediate to break the call stack and allow other events\n // 使用 setImmediate 打断调用栈并允许处理其他事件\n setImmediate(() => this.performSave());\n }\n }\n }\n\n /**\n * 获取指定窗口的状态\n * @param windowName 窗口名称\n * @param defaultWidth 默认宽度,当没有保存状态或状态无效时使用\n * @param defaultHeight 默认高度,当没有保存状态或状态无效时使用\n * @returns 窗口状态对象\n */\n public getWindowState(\n windowName: string,\n defaultWidth: number = 800,\n defaultHeight: number = 600\n ): WindowState {\n // 获取保存的窗口状态\n const savedState = this.state[windowName];\n // 如果没有保存的状态,返回默认状态\n if (!savedState) {\n return {\n width: defaultWidth,\n height: defaultHeight,\n isMaximized: false,\n isFullScreen: false,\n };\n }\n\n // 验证保存的状态是否有效\n if (!this.isValidState(savedState)) {\n // 如果状态无效,返回默认状态\n return {\n width: defaultWidth,\n height: defaultHeight,\n isMaximized: false,\n isFullScreen: false,\n };\n }\n\n // 返回有效的保存状态\n return savedState;\n }\n\n /**\n * 验证窗口状态是否有效\n * 主要检查窗口是否在当前可用显示器范围内\n * @param state 要验证的窗口状态\n * @returns 状态是否有效\n */\n private isValidState(state: WindowState): boolean {\n // 获取所有可用显示器\n const displays = screen.getAllDisplays();\n\n // 优先检查保存的显示器信息是否与当前显示器匹配\n if (state.displayBounds) {\n const displayMatch = displays.find((d) => {\n // 精确匹配显示器边界\n return (\n d.bounds.x === state.displayBounds?.x &&\n d.bounds.y === state.displayBounds?.y &&\n d.bounds.width === state.displayBounds?.width &&\n d.bounds.height === state.displayBounds?.height\n );\n });\n\n if (displayMatch) {\n // 如果找到完全匹配的显示器,并且窗口有坐标信息,则认为状态有效\n if (state.x !== undefined && state.y !== undefined) {\n return true;\n }\n }\n }\n\n // 降级验证:检查窗口是否与任何显示器相交\n const isVisible = displays.some((display) => {\n const { x, y, width, height } = display.bounds;\n // 如果窗口没有坐标信息,使用默认值 0\n const wx = state.x || 0;\n const wy = state.y || 0;\n const ww = state.width;\n const wh = state.height;\n\n // 简单的矩形相交检查\n return wx < x + width && wx + ww > x && wy < y + height && wy + wh > y;\n });\n\n return isVisible;\n }\n\n /**\n * 移除指定窗口的状态\n * @param windowName 窗口名称\n */\n public removeState(windowName: string): void {\n // 检查是否存在该窗口的状态\n if (this.state[windowName]) {\n // 从内存中删除窗口状态\n delete this.state[windowName];\n // 触发持久化操作\n this.persist();\n }\n }\n\n /**\n * Dispose StateKeeper instance\n * 释放 StateKeeper 实例\n */\n public dispose(): void {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n // Only remove from activeFiles if this instance acquired the lock\n if (this.hasLock) {\n StateKeeper.activeFiles.delete(this.stateFile);\n }\n }\n}\n"],"names":["Logger"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Branded Type Helpers
3
+ * 品牌类型辅助函数
4
+ *
5
+ * Utility functions for creating and validating branded types
6
+ * 用于创建和验证品牌类型的工具函数
7
+ */
8
+ import type { WindowId, EventName, ChannelName, HandlerName, FieldKey } from '@/internal/types/branded';
9
+ /**
10
+ * Create a WindowId from a string with validation
11
+ * 从字符串创建 WindowId (带验证)
12
+ */
13
+ export declare function createWindowId(id: string): WindowId;
14
+ /**
15
+ * Create an EventName from a string with validation
16
+ * 从字符串创建 EventName (带验证)
17
+ */
18
+ export declare function createEventName(name: string): EventName;
19
+ /**
20
+ * Create a ChannelName from a string with validation
21
+ * 从字符串创建 ChannelName (带验证)
22
+ */
23
+ export declare function createChannelName(name: string): ChannelName;
24
+ /**
25
+ * Create a HandlerName from a string with validation
26
+ * 从字符串创建 HandlerName (带验证)
27
+ */
28
+ export declare function createHandlerName(name: string): HandlerName;
29
+ /**
30
+ * Create a FieldKey from a string with validation
31
+ * 从字符串创建 FieldKey (带验证)
32
+ */
33
+ export declare function createFieldKey(key: string): FieldKey;
@@ -0,0 +1,5 @@
1
+ export { default as StateKeeper } from './StateKeeper';
2
+ export { MessageDispatcher } from './MessageDispatcher';
3
+ export * from './branded-helpers';
4
+ export * from './RateLimiter';
5
+ export declare const delay: (time: number) => Promise<void>;
@@ -0,0 +1,45 @@
1
+ import { IpcRendererBindings, PreloadConfig } from './preload.type';
2
+ export * from './preload.type';
3
+ /**
4
+ * IpcRendererBridge - Secure IPC Bridge for Renderer Process
5
+ * IpcRendererBridge - 渲染进程的安全 IPC 桥接器
6
+ */
7
+ export declare class IpcRendererBridge {
8
+ private bindings;
9
+ private channel;
10
+ private syncChannel;
11
+ constructor();
12
+ /**
13
+ * Configure the IPC channels
14
+ * 配置 IPC 频道
15
+ * @param config - The configuration object
16
+ */
17
+ configure(config: PreloadConfig): void;
18
+ /**
19
+ * Send an asynchronous message to the main process
20
+ * 发送异步消息到主进程
21
+ * @param name - Handler name (处理器名称)
22
+ * @param payload - Data payload (数据负载)
23
+ */
24
+ private invoke;
25
+ /**
26
+ * Send a synchronous message to the main process
27
+ * 发送同步消息到主进程
28
+ * @param name - Handler name (处理器名称)
29
+ * @param payload - Data payload (数据负载)
30
+ */
31
+ private sendSync;
32
+ /**
33
+ * Get the bindings object
34
+ * 获取绑定对象
35
+ */
36
+ getBindings(): IpcRendererBindings;
37
+ /**
38
+ * Expose API to the main world using contextBridge
39
+ * 使用 contextBridge 将 API 暴露给主世界
40
+ * @param apiKey - The key to expose on window object (default: 'ipcApi')
41
+ */
42
+ exposeApi(apiKey?: string): void;
43
+ }
44
+ declare const ipcRendererBridge: IpcRendererBridge;
45
+ export { ipcRendererBridge };
@@ -0,0 +1,91 @@
1
+ 'use strict';var electron=require('electron'),constants=require('../core/window/constants.js');/**
2
+ * IpcRendererBridge - Secure IPC Bridge for Renderer Process
3
+ * IpcRendererBridge - 渲染进程的安全 IPC 桥接器
4
+ */
5
+ class IpcRendererBridge {
6
+ bindings;
7
+ channel = constants.IPC_CHANNELS.RENDERER_TO_MAIN;
8
+ syncChannel = constants.IPC_CHANNELS.RENDERER_TO_MAIN_SYNC;
9
+ constructor() {
10
+ this.bindings = {
11
+ invoke: this.invoke.bind(this),
12
+ sendSync: this.sendSync.bind(this),
13
+ };
14
+ }
15
+ /**
16
+ * Configure the IPC channels
17
+ * 配置 IPC 频道
18
+ * @param config - The configuration object
19
+ */
20
+ configure(config) {
21
+ if (config.channel) {
22
+ this.channel = config.channel;
23
+ }
24
+ if (config.syncChannel) {
25
+ this.syncChannel = config.syncChannel;
26
+ }
27
+ }
28
+ /**
29
+ * Send an asynchronous message to the main process
30
+ * 发送异步消息到主进程
31
+ * @param name - Handler name (处理器名称)
32
+ * @param payload - Data payload (数据负载)
33
+ */
34
+ invoke = async (name, payload) => {
35
+ const response = await electron.ipcRenderer.invoke(this.channel, {
36
+ name,
37
+ payload,
38
+ });
39
+ // Automatic unwrapping of standard response format
40
+ // 自动解包标准响应格式
41
+ if (response && typeof response === 'object' && 'code' in response) {
42
+ if (response.code === 0) {
43
+ return response.data;
44
+ }
45
+ throw new Error(response.message || `IPC Error: ${response.code}`);
46
+ }
47
+ return response;
48
+ };
49
+ /**
50
+ * Send a synchronous message to the main process
51
+ * 发送同步消息到主进程
52
+ * @param name - Handler name (处理器名称)
53
+ * @param payload - Data payload (数据负载)
54
+ */
55
+ sendSync = (name, payload) => {
56
+ const response = electron.ipcRenderer.sendSync(this.syncChannel, {
57
+ name,
58
+ payload,
59
+ });
60
+ // Automatic unwrapping of standard response format
61
+ // 自动解包标准响应格式
62
+ if (response && typeof response === 'object' && 'code' in response) {
63
+ if (response.code === 0) {
64
+ return response.data;
65
+ }
66
+ throw new Error(response.message || `IPC Error: ${response.code}`);
67
+ }
68
+ return response;
69
+ };
70
+ /**
71
+ * Get the bindings object
72
+ * 获取绑定对象
73
+ */
74
+ getBindings() {
75
+ return this.bindings;
76
+ }
77
+ /**
78
+ * Expose API to the main world using contextBridge
79
+ * 使用 contextBridge 将 API 暴露给主世界
80
+ * @param apiKey - The key to expose on window object (default: 'ipcApi')
81
+ */
82
+ exposeApi(apiKey = 'ipcApi') {
83
+ try {
84
+ electron.contextBridge.exposeInMainWorld(apiKey, this.bindings);
85
+ }
86
+ catch (error) {
87
+ console.error('[IpcRendererBridge] Failed to expose API:', error);
88
+ }
89
+ }
90
+ }
91
+ const ipcRendererBridge = new IpcRendererBridge();exports.IpcRendererBridge=IpcRendererBridge;exports.ipcRendererBridge=ipcRendererBridge;//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/preload/index.ts"],"sourcesContent":["import { ipcRenderer, contextBridge } from 'electron';\nimport { IpcRendererBindings, PreloadConfig } from './preload.type';\nimport { IPC_CHANNELS } from '@/core/window/constants';\nexport * from './preload.type';\n\n/**\n * IpcRendererBridge - Secure IPC Bridge for Renderer Process\n * IpcRendererBridge - 渲染进程的安全 IPC 桥接器\n */\nexport class IpcRendererBridge {\n private bindings: IpcRendererBindings;\n private channel: string = IPC_CHANNELS.RENDERER_TO_MAIN;\n private syncChannel: string = IPC_CHANNELS.RENDERER_TO_MAIN_SYNC;\n\n constructor() {\n this.bindings = {\n invoke: this.invoke.bind(this),\n sendSync: this.sendSync.bind(this),\n };\n }\n\n /**\n * Configure the IPC channels\n * 配置 IPC 频道\n * @param config - The configuration object\n */\n public configure(config: PreloadConfig): void {\n if (config.channel) {\n this.channel = config.channel;\n }\n if (config.syncChannel) {\n this.syncChannel = config.syncChannel;\n }\n }\n\n /**\n * Send an asynchronous message to the main process\n * 发送异步消息到主进程\n * @param name - Handler name (处理器名称)\n * @param payload - Data payload (数据负载)\n */\n private invoke = async <T = unknown>(name: string, payload?: unknown): Promise<T> => {\n const response = await ipcRenderer.invoke(this.channel, {\n name,\n payload,\n });\n\n // Automatic unwrapping of standard response format\n // 自动解包标准响应格式\n if (response && typeof response === 'object' && 'code' in response) {\n if (response.code === 0) {\n return response.data as T;\n }\n throw new Error(response.message || `IPC Error: ${response.code}`);\n }\n\n return response as T;\n };\n\n /**\n * Send a synchronous message to the main process\n * 发送同步消息到主进程\n * @param name - Handler name (处理器名称)\n * @param payload - Data payload (数据负载)\n */\n private sendSync = <T = unknown>(name: string, payload?: unknown): T => {\n const response = ipcRenderer.sendSync(this.syncChannel, {\n name,\n payload,\n });\n\n // Automatic unwrapping of standard response format\n // 自动解包标准响应格式\n if (response && typeof response === 'object' && 'code' in response) {\n if (response.code === 0) {\n return response.data as T;\n }\n throw new Error(response.message || `IPC Error: ${response.code}`);\n }\n\n return response as T;\n };\n\n /**\n * Get the bindings object\n * 获取绑定对象\n */\n public getBindings(): IpcRendererBindings {\n return this.bindings;\n }\n\n /**\n * Expose API to the main world using contextBridge\n * 使用 contextBridge 将 API 暴露给主世界\n * @param apiKey - The key to expose on window object (default: 'ipcApi')\n */\n public exposeApi(apiKey: string = 'ipcApi'): void {\n try {\n contextBridge.exposeInMainWorld(apiKey, this.bindings);\n } catch (error) {\n console.error('[IpcRendererBridge] Failed to expose API:', error);\n }\n }\n}\n\nconst ipcRendererBridge = new IpcRendererBridge();\nexport { ipcRendererBridge };\n"],"names":["IPC_CHANNELS","ipcRenderer","contextBridge"],"mappings":"+FAKA;;;AAGG;MACU,iBAAiB,CAAA;AACpB,IAAA,QAAQ;AACR,IAAA,OAAO,GAAWA,sBAAY,CAAC,gBAAgB;AAC/C,IAAA,WAAW,GAAWA,sBAAY,CAAC,qBAAqB;AAEhE,IAAA,WAAA,GAAA;QACE,IAAI,CAAC,QAAQ,GAAG;YACd,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9B,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;SACnC;IACH;AAEA;;;;AAIG;AACI,IAAA,SAAS,CAAC,MAAqB,EAAA;AACpC,QAAA,IAAI,MAAM,CAAC,OAAO,EAAE;AAClB,YAAA,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO;QAC/B;AACA,QAAA,IAAI,MAAM,CAAC,WAAW,EAAE;AACtB,YAAA,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW;QACvC;IACF;AAEA;;;;;AAKG;AACK,IAAA,MAAM,GAAG,OAAoB,IAAY,EAAE,OAAiB,KAAgB;QAClF,MAAM,QAAQ,GAAG,MAAMC,oBAAW,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YACtD,IAAI;YACJ,OAAO;AACR,SAAA,CAAC;;;QAIF,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,IAAI,QAAQ,EAAE;AAClE,YAAA,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE;gBACvB,OAAO,QAAQ,CAAC,IAAS;YAC3B;AACA,YAAA,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAA,WAAA,EAAc,QAAQ,CAAC,IAAI,CAAA,CAAE,CAAC;QACpE;AAEA,QAAA,OAAO,QAAa;AACtB,IAAA,CAAC;AAED;;;;;AAKG;AACK,IAAA,QAAQ,GAAG,CAAc,IAAY,EAAE,OAAiB,KAAO;QACrE,MAAM,QAAQ,GAAGA,oBAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE;YACtD,IAAI;YACJ,OAAO;AACR,SAAA,CAAC;;;QAIF,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,IAAI,QAAQ,EAAE;AAClE,YAAA,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE;gBACvB,OAAO,QAAQ,CAAC,IAAS;YAC3B;AACA,YAAA,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAA,WAAA,EAAc,QAAQ,CAAC,IAAI,CAAA,CAAE,CAAC;QACpE;AAEA,QAAA,OAAO,QAAa;AACtB,IAAA,CAAC;AAED;;;AAGG;IACI,WAAW,GAAA;QAChB,OAAO,IAAI,CAAC,QAAQ;IACtB;AAEA;;;;AAIG;IACI,SAAS,CAAC,SAAiB,QAAQ,EAAA;AACxC,QAAA,IAAI;YACFC,sBAAa,CAAC,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;QACxD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC;QACnE;IACF;AACD;AAED,MAAM,iBAAiB,GAAG,IAAI,iBAAiB"}
@@ -0,0 +1,91 @@
1
+ import {ipcRenderer,contextBridge}from'electron';import {IPC_CHANNELS}from'../core/window/constants.mjs';/**
2
+ * IpcRendererBridge - Secure IPC Bridge for Renderer Process
3
+ * IpcRendererBridge - 渲染进程的安全 IPC 桥接器
4
+ */
5
+ class IpcRendererBridge {
6
+ bindings;
7
+ channel = IPC_CHANNELS.RENDERER_TO_MAIN;
8
+ syncChannel = IPC_CHANNELS.RENDERER_TO_MAIN_SYNC;
9
+ constructor() {
10
+ this.bindings = {
11
+ invoke: this.invoke.bind(this),
12
+ sendSync: this.sendSync.bind(this),
13
+ };
14
+ }
15
+ /**
16
+ * Configure the IPC channels
17
+ * 配置 IPC 频道
18
+ * @param config - The configuration object
19
+ */
20
+ configure(config) {
21
+ if (config.channel) {
22
+ this.channel = config.channel;
23
+ }
24
+ if (config.syncChannel) {
25
+ this.syncChannel = config.syncChannel;
26
+ }
27
+ }
28
+ /**
29
+ * Send an asynchronous message to the main process
30
+ * 发送异步消息到主进程
31
+ * @param name - Handler name (处理器名称)
32
+ * @param payload - Data payload (数据负载)
33
+ */
34
+ invoke = async (name, payload) => {
35
+ const response = await ipcRenderer.invoke(this.channel, {
36
+ name,
37
+ payload,
38
+ });
39
+ // Automatic unwrapping of standard response format
40
+ // 自动解包标准响应格式
41
+ if (response && typeof response === 'object' && 'code' in response) {
42
+ if (response.code === 0) {
43
+ return response.data;
44
+ }
45
+ throw new Error(response.message || `IPC Error: ${response.code}`);
46
+ }
47
+ return response;
48
+ };
49
+ /**
50
+ * Send a synchronous message to the main process
51
+ * 发送同步消息到主进程
52
+ * @param name - Handler name (处理器名称)
53
+ * @param payload - Data payload (数据负载)
54
+ */
55
+ sendSync = (name, payload) => {
56
+ const response = ipcRenderer.sendSync(this.syncChannel, {
57
+ name,
58
+ payload,
59
+ });
60
+ // Automatic unwrapping of standard response format
61
+ // 自动解包标准响应格式
62
+ if (response && typeof response === 'object' && 'code' in response) {
63
+ if (response.code === 0) {
64
+ return response.data;
65
+ }
66
+ throw new Error(response.message || `IPC Error: ${response.code}`);
67
+ }
68
+ return response;
69
+ };
70
+ /**
71
+ * Get the bindings object
72
+ * 获取绑定对象
73
+ */
74
+ getBindings() {
75
+ return this.bindings;
76
+ }
77
+ /**
78
+ * Expose API to the main world using contextBridge
79
+ * 使用 contextBridge 将 API 暴露给主世界
80
+ * @param apiKey - The key to expose on window object (default: 'ipcApi')
81
+ */
82
+ exposeApi(apiKey = 'ipcApi') {
83
+ try {
84
+ contextBridge.exposeInMainWorld(apiKey, this.bindings);
85
+ }
86
+ catch (error) {
87
+ console.error('[IpcRendererBridge] Failed to expose API:', error);
88
+ }
89
+ }
90
+ }
91
+ const ipcRendererBridge = new IpcRendererBridge();export{IpcRendererBridge,ipcRendererBridge};//# sourceMappingURL=index.mjs.map