@dimina-kit/devtools 0.1.0 → 0.2.0-dev.20260424031451

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 (190) hide show
  1. package/dist/main/api.d.ts +3 -1
  2. package/dist/main/api.js +2 -0
  3. package/dist/main/app/app.d.ts +2 -0
  4. package/dist/main/app/app.js +29 -7
  5. package/dist/main/index.bundle.js +2716 -0
  6. package/dist/main/services/automation/index.d.ts +2 -1
  7. package/dist/main/services/automation/index.js +28 -4
  8. package/dist/main/services/projects/project-repository.js +6 -0
  9. package/dist/main/services/simulator-storage/index.d.ts +20 -0
  10. package/dist/main/services/simulator-storage/index.js +136 -0
  11. package/dist/main/services/update/github-release-checker.d.ts +49 -0
  12. package/dist/main/services/update/github-release-checker.js +248 -0
  13. package/dist/main/services/update/index.d.ts +5 -0
  14. package/dist/main/services/update/index.js +3 -0
  15. package/dist/main/services/update/update-manager.d.ts +37 -0
  16. package/dist/main/services/update/update-manager.js +90 -0
  17. package/dist/main/services/workspace/workspace-service.js +12 -0
  18. package/dist/preload/instrumentation/index.d.ts +0 -1
  19. package/dist/preload/instrumentation/index.js +0 -1
  20. package/dist/preload/windows/main.d.ts +0 -1
  21. package/dist/preload/windows/main.js +0 -1
  22. package/dist/preload/windows/simulator.js +1 -109
  23. package/dist/renderer/assets/index-D_HFRqZA.js +46 -0
  24. package/dist/renderer/assets/{input-B7sz4y1r.js → input-VLotwer2.js} +2 -2
  25. package/dist/renderer/assets/ipc-transport-6Gy3FXIL.css +1 -0
  26. package/dist/renderer/assets/{ipc-transport-L_WD9x12.js → ipc-transport-COKIC6Ny.js} +6 -6
  27. package/dist/renderer/assets/{popover-CzMIs2j4.js → popover-Dn0RwMLI.js} +2 -2
  28. package/dist/renderer/assets/{select-BBXJVe8R.js → select-DzIsLaKX.js} +2 -2
  29. package/dist/renderer/assets/{settings-Db1pvrBJ.js → settings-CD-dacWB.js} +2 -2
  30. package/dist/renderer/assets/settings-api-BV9MPckG.js +2 -0
  31. package/dist/renderer/assets/{workbenchSettings-BnQsvWkQ.js → workbenchSettings-ClwgsvJL.js} +2 -2
  32. package/dist/renderer/entries/main/index.html +5 -5
  33. package/dist/renderer/entries/popover/index.html +5 -5
  34. package/dist/renderer/entries/settings/index.html +5 -5
  35. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  36. package/dist/shared/ipc-channels.d.ts +26 -3
  37. package/dist/shared/ipc-channels.js +10 -3
  38. package/dist/shared/types.d.ts +27 -0
  39. package/package.json +6 -4
  40. package/dist/main/api.d.ts.map +0 -1
  41. package/dist/main/api.js.map +0 -1
  42. package/dist/main/app/app.d.ts.map +0 -1
  43. package/dist/main/app/app.js.map +0 -1
  44. package/dist/main/app/bootstrap.d.ts.map +0 -1
  45. package/dist/main/app/bootstrap.js.map +0 -1
  46. package/dist/main/app/launch.d.ts.map +0 -1
  47. package/dist/main/app/launch.js.map +0 -1
  48. package/dist/main/app/lifecycle.d.ts.map +0 -1
  49. package/dist/main/app/lifecycle.js.map +0 -1
  50. package/dist/main/index.d.ts.map +0 -1
  51. package/dist/main/index.js.map +0 -1
  52. package/dist/main/ipc/app.d.ts.map +0 -1
  53. package/dist/main/ipc/app.js.map +0 -1
  54. package/dist/main/ipc/index.d.ts.map +0 -1
  55. package/dist/main/ipc/index.js.map +0 -1
  56. package/dist/main/ipc/panels.d.ts.map +0 -1
  57. package/dist/main/ipc/panels.js.map +0 -1
  58. package/dist/main/ipc/popover.d.ts.map +0 -1
  59. package/dist/main/ipc/popover.js.map +0 -1
  60. package/dist/main/ipc/projects.d.ts.map +0 -1
  61. package/dist/main/ipc/projects.js.map +0 -1
  62. package/dist/main/ipc/session.d.ts.map +0 -1
  63. package/dist/main/ipc/session.js.map +0 -1
  64. package/dist/main/ipc/settings.d.ts.map +0 -1
  65. package/dist/main/ipc/settings.js.map +0 -1
  66. package/dist/main/ipc/simulator.d.ts.map +0 -1
  67. package/dist/main/ipc/simulator.js.map +0 -1
  68. package/dist/main/ipc/toolbar.d.ts.map +0 -1
  69. package/dist/main/ipc/toolbar.js.map +0 -1
  70. package/dist/main/menu/index.d.ts.map +0 -1
  71. package/dist/main/menu/index.js.map +0 -1
  72. package/dist/main/services/automation/exec.d.ts.map +0 -1
  73. package/dist/main/services/automation/exec.js.map +0 -1
  74. package/dist/main/services/automation/handlers/app.d.ts.map +0 -1
  75. package/dist/main/services/automation/handlers/app.js.map +0 -1
  76. package/dist/main/services/automation/handlers/element.d.ts.map +0 -1
  77. package/dist/main/services/automation/handlers/element.js.map +0 -1
  78. package/dist/main/services/automation/handlers/page.d.ts.map +0 -1
  79. package/dist/main/services/automation/handlers/page.js.map +0 -1
  80. package/dist/main/services/automation/handlers/tool.d.ts.map +0 -1
  81. package/dist/main/services/automation/handlers/tool.js.map +0 -1
  82. package/dist/main/services/automation/index.d.ts.map +0 -1
  83. package/dist/main/services/automation/index.js.map +0 -1
  84. package/dist/main/services/automation/registry.d.ts.map +0 -1
  85. package/dist/main/services/automation/registry.js.map +0 -1
  86. package/dist/main/services/automation/shared.d.ts.map +0 -1
  87. package/dist/main/services/automation/shared.js.map +0 -1
  88. package/dist/main/services/default-adapter.d.ts.map +0 -1
  89. package/dist/main/services/default-adapter.js.map +0 -1
  90. package/dist/main/services/layout/index.d.ts.map +0 -1
  91. package/dist/main/services/layout/index.js.map +0 -1
  92. package/dist/main/services/mcp/index.d.ts.map +0 -1
  93. package/dist/main/services/mcp/index.js.map +0 -1
  94. package/dist/main/services/mcp/server.d.ts.map +0 -1
  95. package/dist/main/services/mcp/server.js.map +0 -1
  96. package/dist/main/services/mcp/target-manager.d.ts.map +0 -1
  97. package/dist/main/services/mcp/target-manager.js.map +0 -1
  98. package/dist/main/services/mcp/tool-registry.d.ts.map +0 -1
  99. package/dist/main/services/mcp/tool-registry.js.map +0 -1
  100. package/dist/main/services/mcp/tools/context-tools.d.ts.map +0 -1
  101. package/dist/main/services/mcp/tools/context-tools.js.map +0 -1
  102. package/dist/main/services/mcp/tools/simulator-tools.d.ts.map +0 -1
  103. package/dist/main/services/mcp/tools/simulator-tools.js.map +0 -1
  104. package/dist/main/services/mcp/tools/workbench-tools.d.ts.map +0 -1
  105. package/dist/main/services/mcp/tools/workbench-tools.js.map +0 -1
  106. package/dist/main/services/notifications/renderer-notifier.d.ts.map +0 -1
  107. package/dist/main/services/notifications/renderer-notifier.js.map +0 -1
  108. package/dist/main/services/projects/index.d.ts.map +0 -1
  109. package/dist/main/services/projects/index.js.map +0 -1
  110. package/dist/main/services/projects/project-repository.d.ts.map +0 -1
  111. package/dist/main/services/projects/project-repository.js.map +0 -1
  112. package/dist/main/services/settings/index.d.ts.map +0 -1
  113. package/dist/main/services/settings/index.js.map +0 -1
  114. package/dist/main/services/simulator/dir.d.ts.map +0 -1
  115. package/dist/main/services/simulator/dir.js.map +0 -1
  116. package/dist/main/services/simulator/referer.d.ts.map +0 -1
  117. package/dist/main/services/simulator/referer.js.map +0 -1
  118. package/dist/main/services/views/view-manager.d.ts.map +0 -1
  119. package/dist/main/services/views/view-manager.js.map +0 -1
  120. package/dist/main/services/workbench-context.d.ts.map +0 -1
  121. package/dist/main/services/workbench-context.js.map +0 -1
  122. package/dist/main/services/workspace/index.d.ts.map +0 -1
  123. package/dist/main/services/workspace/index.js.map +0 -1
  124. package/dist/main/services/workspace/workspace-service.d.ts.map +0 -1
  125. package/dist/main/services/workspace/workspace-service.js.map +0 -1
  126. package/dist/main/utils/logger.d.ts.map +0 -1
  127. package/dist/main/utils/logger.js.map +0 -1
  128. package/dist/main/utils/paths.d.ts.map +0 -1
  129. package/dist/main/utils/paths.js.map +0 -1
  130. package/dist/main/utils/theme.d.ts.map +0 -1
  131. package/dist/main/utils/theme.js.map +0 -1
  132. package/dist/main/windows/main-window/create.d.ts.map +0 -1
  133. package/dist/main/windows/main-window/create.js.map +0 -1
  134. package/dist/main/windows/main-window/events.d.ts.map +0 -1
  135. package/dist/main/windows/main-window/events.js.map +0 -1
  136. package/dist/main/windows/main-window/index.d.ts.map +0 -1
  137. package/dist/main/windows/main-window/index.js.map +0 -1
  138. package/dist/main/windows/settings-window/create.d.ts.map +0 -1
  139. package/dist/main/windows/settings-window/create.js.map +0 -1
  140. package/dist/main/windows/settings-window/events.d.ts.map +0 -1
  141. package/dist/main/windows/settings-window/events.js.map +0 -1
  142. package/dist/main/windows/settings-window/index.d.ts.map +0 -1
  143. package/dist/main/windows/settings-window/index.js.map +0 -1
  144. package/dist/preload/instrumentation/app-data.d.ts.map +0 -1
  145. package/dist/preload/instrumentation/app-data.js.map +0 -1
  146. package/dist/preload/instrumentation/console.d.ts.map +0 -1
  147. package/dist/preload/instrumentation/console.js.map +0 -1
  148. package/dist/preload/instrumentation/disposable.d.ts.map +0 -1
  149. package/dist/preload/instrumentation/disposable.js.map +0 -1
  150. package/dist/preload/instrumentation/index.d.ts.map +0 -1
  151. package/dist/preload/instrumentation/index.js.map +0 -1
  152. package/dist/preload/instrumentation/storage.d.ts +0 -2
  153. package/dist/preload/instrumentation/storage.d.ts.map +0 -1
  154. package/dist/preload/instrumentation/storage.js +0 -106
  155. package/dist/preload/instrumentation/storage.js.map +0 -1
  156. package/dist/preload/instrumentation/wxml-extract.d.ts.map +0 -1
  157. package/dist/preload/instrumentation/wxml-extract.js.map +0 -1
  158. package/dist/preload/instrumentation/wxml.d.ts.map +0 -1
  159. package/dist/preload/instrumentation/wxml.js.map +0 -1
  160. package/dist/preload/runtime/bridge.d.ts.map +0 -1
  161. package/dist/preload/runtime/bridge.js.map +0 -1
  162. package/dist/preload/runtime/host.d.ts.map +0 -1
  163. package/dist/preload/runtime/host.js.map +0 -1
  164. package/dist/preload/shared/api-compat.d.ts.map +0 -1
  165. package/dist/preload/shared/api-compat.js.map +0 -1
  166. package/dist/preload/shared/constants.d.ts.map +0 -1
  167. package/dist/preload/shared/constants.js.map +0 -1
  168. package/dist/preload/shared/types.d.ts.map +0 -1
  169. package/dist/preload/shared/types.js.map +0 -1
  170. package/dist/preload/windows/main.d.ts.map +0 -1
  171. package/dist/preload/windows/main.js.map +0 -1
  172. package/dist/preload/windows/simulator.js.map +0 -7
  173. package/dist/renderer/assets/index-kcmIQPCg.js +0 -2
  174. package/dist/renderer/assets/index-kcmIQPCg.js.map +0 -1
  175. package/dist/renderer/assets/input-B7sz4y1r.js.map +0 -1
  176. package/dist/renderer/assets/ipc-transport-C9-PPTGn.css +0 -1
  177. package/dist/renderer/assets/ipc-transport-L_WD9x12.js.map +0 -1
  178. package/dist/renderer/assets/popover-CzMIs2j4.js.map +0 -1
  179. package/dist/renderer/assets/select-BBXJVe8R.js.map +0 -1
  180. package/dist/renderer/assets/settings-Db1pvrBJ.js.map +0 -1
  181. package/dist/renderer/assets/settings-api-DJ1M-CNt.js +0 -2
  182. package/dist/renderer/assets/settings-api-DJ1M-CNt.js.map +0 -1
  183. package/dist/renderer/assets/workbenchSettings-BnQsvWkQ.js.map +0 -1
  184. package/dist/shared/constants.d.ts.map +0 -1
  185. package/dist/shared/constants.js.map +0 -1
  186. package/dist/shared/ipc-channels.d.ts.map +0 -1
  187. package/dist/shared/ipc-channels.js.map +0 -1
  188. package/dist/shared/types.d.ts.map +0 -1
  189. package/dist/shared/types.js.map +0 -1
  190. package/dist/simulator/assets/simulator-C1eHXnuq.js.map +0 -1
@@ -14,5 +14,6 @@ export interface AutomationServer {
14
14
  close: () => void;
15
15
  port: number;
16
16
  }
17
- export declare function startAutomationServer(ctx: WorkbenchContext, port: number): AutomationServer;
17
+ export declare function getAutomationPort(): number | null;
18
+ export declare function startAutomationServer(ctx: WorkbenchContext, port?: number): Promise<AutomationServer>;
18
19
  //# sourceMappingURL=index.d.ts.map
@@ -9,9 +9,9 @@
9
9
  * This server handles these messages by delegating to the workbench context
10
10
  * (simulator webContents, IPC handlers, etc.) without modifying dimina upstream.
11
11
  */
12
- /// <reference types="electron" />
12
+ import { ipcMain } from 'electron';
13
13
  import { WebSocketServer } from 'ws';
14
- import { SimulatorChannel } from '../../../shared/ipc-channels.js';
14
+ import { AutomationChannel, SimulatorChannel } from '../../../shared/ipc-channels.js';
15
15
  import { getSimulator } from './exec.js';
16
16
  import { toolHandlers } from './handlers/tool.js';
17
17
  import { appHandlers } from './handlers/app.js';
@@ -24,9 +24,26 @@ const handlers = {
24
24
  ...pageHandlers,
25
25
  ...elementHandlers,
26
26
  };
27
- export function startAutomationServer(ctx, port) {
27
+ let currentPort = null;
28
+ let portIpcRegistered = false;
29
+ export function getAutomationPort() {
30
+ return currentPort;
31
+ }
32
+ export async function startAutomationServer(ctx, port = 0) {
28
33
  const wss = new WebSocketServer({ port });
29
34
  const clients = new Set();
35
+ await new Promise((resolve, reject) => {
36
+ wss.once('listening', () => resolve());
37
+ wss.once('error', (err) => reject(err));
38
+ });
39
+ const addr = wss.address();
40
+ // ws always binds via an http.Server, so address() is AddressInfo here.
41
+ const resolvedPort = typeof addr === 'object' && addr ? addr.port : port;
42
+ currentPort = resolvedPort;
43
+ if (!portIpcRegistered) {
44
+ portIpcRegistered = true;
45
+ ipcMain.handle(AutomationChannel.GetPort, () => currentPort);
46
+ }
30
47
  function broadcast(event) {
31
48
  const msg = JSON.stringify(event);
32
49
  for (const ws of clients) {
@@ -90,6 +107,13 @@ export function startAutomationServer(ctx, port) {
90
107
  });
91
108
  ws.on('close', () => clients.delete(ws));
92
109
  });
93
- return { close: () => wss.close(), port };
110
+ return {
111
+ close: () => {
112
+ if (currentPort === resolvedPort)
113
+ currentPort = null;
114
+ wss.close();
115
+ },
116
+ port: resolvedPort,
117
+ };
94
118
  }
95
119
  //# sourceMappingURL=index.js.map
@@ -22,6 +22,12 @@ export function listProjects() {
22
22
  return load();
23
23
  }
24
24
  export function validateProjectDir(dirPath) {
25
+ if (!dirPath) {
26
+ return '小程序目录路径为空,请选择包含小程序源码的目录';
27
+ }
28
+ if (!fs.existsSync(dirPath)) {
29
+ return `小程序目录不存在:${dirPath}`;
30
+ }
25
31
  if (!fs.existsSync(path.join(dirPath, 'app.json'))) {
26
32
  const configPath = path.join(dirPath, 'project.config.json');
27
33
  if (fs.existsSync(configPath)) {
@@ -0,0 +1,20 @@
1
+ /**
2
+ * SimulatorStorageWatcher
3
+ *
4
+ * Attaches the Chrome DevTools Protocol debugger to the simulator <webview>
5
+ * and forwards `DOMStorage.*` events to the renderer host. Replaces the
6
+ * preload-side localStorage.setItem hook that used to push storage changes
7
+ * via SimulatorChannel.Storage / StorageAll.
8
+ *
9
+ * Trade-offs vs the preload approach:
10
+ * + Uses standard browser protocol; no preload injection
11
+ * + Captures every change including ones bypassing wx (api-compat fallback,
12
+ * direct localStorage.setItem from devtools, etc.)
13
+ * + Decouples panel UI from dimina runtime's wx implementation
14
+ * - debugger.attach is mutually exclusive with Chrome DevTools (F12).
15
+ * If a user opens DevTools on the simulator the debugger detaches and
16
+ * events stop flowing until they close it.
17
+ */
18
+ import { type WebContents } from 'electron';
19
+ export declare function setupSimulatorStorage(host: WebContents): void;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,136 @@
1
+ /**
2
+ * SimulatorStorageWatcher
3
+ *
4
+ * Attaches the Chrome DevTools Protocol debugger to the simulator <webview>
5
+ * and forwards `DOMStorage.*` events to the renderer host. Replaces the
6
+ * preload-side localStorage.setItem hook that used to push storage changes
7
+ * via SimulatorChannel.Storage / StorageAll.
8
+ *
9
+ * Trade-offs vs the preload approach:
10
+ * + Uses standard browser protocol; no preload injection
11
+ * + Captures every change including ones bypassing wx (api-compat fallback,
12
+ * direct localStorage.setItem from devtools, etc.)
13
+ * + Decouples panel UI from dimina runtime's wx implementation
14
+ * - debugger.attach is mutually exclusive with Chrome DevTools (F12).
15
+ * If a user opens DevTools on the simulator the debugger detaches and
16
+ * events stop flowing until they close it.
17
+ */
18
+ import { app, ipcMain, webContents as wcStatic } from 'electron';
19
+ import { SimulatorStorageChannel, } from '../../../shared/ipc-channels.js';
20
+ let attachedWc = null;
21
+ let hostWc = null;
22
+ function isSimulatorWebview(wc) {
23
+ if (wc.isDestroyed())
24
+ return false;
25
+ if (wc.getType() !== 'webview')
26
+ return false;
27
+ return wc.getURL().includes('simulator.html');
28
+ }
29
+ function detach() {
30
+ if (!attachedWc)
31
+ return;
32
+ const wc = attachedWc;
33
+ attachedWc = null;
34
+ try {
35
+ if (!wc.isDestroyed() && wc.debugger.isAttached()) {
36
+ wc.debugger.detach();
37
+ }
38
+ }
39
+ catch {
40
+ // best-effort
41
+ }
42
+ }
43
+ async function attachToSim(wc) {
44
+ if (attachedWc === wc)
45
+ return;
46
+ if (wc.isDestroyed())
47
+ return;
48
+ detach();
49
+ try {
50
+ if (!wc.debugger.isAttached()) {
51
+ wc.debugger.attach('1.3');
52
+ }
53
+ await wc.debugger.sendCommand('DOMStorage.enable');
54
+ wc.debugger.on('message', onCdpMessage);
55
+ wc.debugger.on('detach', () => {
56
+ if (attachedWc === wc)
57
+ attachedWc = null;
58
+ });
59
+ wc.once('destroyed', () => {
60
+ if (attachedWc === wc)
61
+ attachedWc = null;
62
+ });
63
+ attachedWc = wc;
64
+ }
65
+ catch (e) {
66
+ console.warn('[storage-watcher] attach failed:', e.message);
67
+ }
68
+ }
69
+ function onCdpMessage(_event, method, params) {
70
+ if (!hostWc || hostWc.isDestroyed())
71
+ return;
72
+ const p = params;
73
+ let evt;
74
+ switch (method) {
75
+ case 'DOMStorage.domStorageItemAdded':
76
+ evt = { type: 'added', key: String(p.key), newValue: String(p.newValue ?? '') };
77
+ break;
78
+ case 'DOMStorage.domStorageItemUpdated':
79
+ evt = {
80
+ type: 'updated',
81
+ key: String(p.key),
82
+ oldValue: String(p.oldValue ?? ''),
83
+ newValue: String(p.newValue ?? ''),
84
+ };
85
+ break;
86
+ case 'DOMStorage.domStorageItemRemoved':
87
+ evt = { type: 'removed', key: String(p.key) };
88
+ break;
89
+ case 'DOMStorage.domStorageItemsCleared':
90
+ evt = { type: 'cleared' };
91
+ break;
92
+ default:
93
+ return;
94
+ }
95
+ hostWc.send(SimulatorStorageChannel.Event, evt);
96
+ }
97
+ async function getSnapshot() {
98
+ if (!attachedWc || attachedWc.isDestroyed())
99
+ return [];
100
+ try {
101
+ const origin = await attachedWc.executeJavaScript('location.origin');
102
+ const result = (await attachedWc.debugger.sendCommand('DOMStorage.getDOMStorageItems', {
103
+ storageId: { securityOrigin: origin, isLocalStorage: true },
104
+ }));
105
+ return result.entries.map(([key, value]) => ({ key, value }));
106
+ }
107
+ catch {
108
+ return [];
109
+ }
110
+ }
111
+ export function setupSimulatorStorage(host) {
112
+ hostWc = host;
113
+ // Attach to any simulator webview that already exists (project may have
114
+ // been opened before this function runs).
115
+ for (const wc of wcStatic.getAllWebContents()) {
116
+ if (isSimulatorWebview(wc)) {
117
+ void attachToSim(wc);
118
+ break;
119
+ }
120
+ }
121
+ // Catch new simulator webviews on creation. did-finish-load is the right
122
+ // moment because getURL() may still be 'about:blank' at creation.
123
+ app.on('web-contents-created', (_event, wc) => {
124
+ wc.on('did-finish-load', () => {
125
+ if (isSimulatorWebview(wc)) {
126
+ void attachToSim(wc);
127
+ }
128
+ });
129
+ wc.on('did-navigate-in-page', () => {
130
+ // Project switched but same webview — attach already correct, debugger
131
+ // session survives in-page navigations. No-op.
132
+ });
133
+ });
134
+ ipcMain.handle(SimulatorStorageChannel.GetSnapshot, getSnapshot);
135
+ }
136
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,49 @@
1
+ import type { UpdateChecker } from '../../../shared/types.js';
2
+ interface GitHubReleaseAsset {
3
+ name: string;
4
+ browser_download_url: string;
5
+ size: number;
6
+ }
7
+ interface GitHubRelease {
8
+ tag_name: string;
9
+ name?: string;
10
+ body?: string;
11
+ prerelease: boolean;
12
+ draft: boolean;
13
+ assets: GitHubReleaseAsset[];
14
+ }
15
+ export type VersionScheme = 'semver' | 'trailing-number';
16
+ export interface GitHubReleaseCheckerOptions {
17
+ owner: string;
18
+ repo: string;
19
+ /** Optional GitHub token (required for private repos; recommended to avoid rate limits). */
20
+ token?: string;
21
+ /** Include prereleases when resolving the latest version. Default: false. */
22
+ includePrereleases?: boolean;
23
+ /** Mark every delivered update as mandatory. Default: false. */
24
+ mandatory?: boolean;
25
+ /**
26
+ * Built-in version scheme. Ignored when `parseVersion` is supplied.
27
+ * - `'semver'` (default): extract `x.y.z` from `tag_name` → release name → first asset name.
28
+ * - `'trailing-number'`: treat the trailing `-<N>` in `tag_name` as the version
29
+ * (e.g. `release-20260422-1` → `'1'`). Compared as an integer.
30
+ */
31
+ versionScheme?: VersionScheme;
32
+ /**
33
+ * Custom asset picker. Receives the release assets plus the current
34
+ * process platform/arch. If omitted, a built-in picker is used that
35
+ * matches by file extension and arch suffix.
36
+ */
37
+ pickAsset?: (assets: GitHubReleaseAsset[], ctx: PickAssetContext) => GitHubReleaseAsset | undefined;
38
+ /**
39
+ * Extract the version string from a release. Overrides `versionScheme`.
40
+ */
41
+ parseVersion?: (release: GitHubRelease) => string | null;
42
+ }
43
+ export interface PickAssetContext {
44
+ platform: NodeJS.Platform;
45
+ arch: string;
46
+ }
47
+ export declare function createGitHubReleaseChecker(opts: GitHubReleaseCheckerOptions): UpdateChecker;
48
+ export {};
49
+ //# sourceMappingURL=github-release-checker.d.ts.map
@@ -0,0 +1,248 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import https from 'https';
4
+ import { app } from 'electron';
5
+ export function createGitHubReleaseChecker(opts) {
6
+ const releaseUrl = opts.includePrereleases
7
+ ? `https://api.github.com/repos/${opts.owner}/${opts.repo}/releases`
8
+ : `https://api.github.com/repos/${opts.owner}/${opts.repo}/releases/latest`;
9
+ const parseVersion = opts.parseVersion ?? schemeParser(opts.versionScheme ?? 'semver');
10
+ return {
11
+ async checkForUpdates(currentVersion) {
12
+ const release = await fetchRelease(releaseUrl, opts);
13
+ if (!release)
14
+ return null;
15
+ const latestVersion = parseVersion(release);
16
+ if (!latestVersion)
17
+ return null;
18
+ if (compareSemver(latestVersion, stripV(currentVersion)) <= 0)
19
+ return null;
20
+ const asset = (opts.pickAsset ?? defaultPickAsset)(release.assets, {
21
+ platform: process.platform,
22
+ arch: process.arch,
23
+ });
24
+ if (!asset)
25
+ return null;
26
+ return {
27
+ version: latestVersion,
28
+ downloadUrl: asset.browser_download_url,
29
+ releaseNotes: release.body,
30
+ mandatory: opts.mandatory,
31
+ };
32
+ },
33
+ async downloadUpdate(info, onProgress) {
34
+ const tmpDir = path.join(app.getPath('temp'), `${opts.repo}-update`);
35
+ if (!fs.existsSync(tmpDir))
36
+ fs.mkdirSync(tmpDir, { recursive: true });
37
+ const fileName = path.basename(new URL(info.downloadUrl).pathname);
38
+ const filePath = path.join(tmpDir, fileName);
39
+ await downloadFile(info.downloadUrl, filePath, opts.token, onProgress);
40
+ return filePath;
41
+ },
42
+ };
43
+ }
44
+ // ── Default pickers ───────────────────────────────────────────────────────
45
+ function defaultPickAsset(assets, ctx) {
46
+ const candidates = platformMatchers(ctx.platform, ctx.arch);
47
+ for (const match of candidates) {
48
+ const hit = assets.find((a) => match(a.name));
49
+ if (hit)
50
+ return hit;
51
+ }
52
+ return undefined;
53
+ }
54
+ function platformMatchers(platform, arch) {
55
+ const lc = (s) => s.toLowerCase();
56
+ const archTags = archAliases(arch);
57
+ const hasArch = (name) => archTags.some((tag) => lc(name).includes(tag));
58
+ const endsWith = (name, ext) => lc(name).endsWith(ext);
59
+ switch (platform) {
60
+ case 'darwin':
61
+ return [
62
+ (n) => endsWith(n, '.dmg') && hasArch(n),
63
+ (n) => endsWith(n, '.dmg'), // mac builds are often universal / unarch-tagged
64
+ (n) => endsWith(n, '.zip') && (lc(n).includes('mac') || lc(n).includes('darwin')),
65
+ ];
66
+ case 'win32':
67
+ return [
68
+ (n) => endsWith(n, '.exe') && hasArch(n),
69
+ (n) => endsWith(n, '.zip') && (lc(n).includes('win') || lc(n).includes('windows')) && hasArch(n),
70
+ (n) => endsWith(n, '.exe'),
71
+ (n) => endsWith(n, '.zip') && (lc(n).includes('win') || lc(n).includes('windows')),
72
+ ];
73
+ case 'linux':
74
+ return [
75
+ (n) => endsWith(n, '.appimage') && hasArch(n),
76
+ (n) => endsWith(n, '.tar.gz') && lc(n).includes('linux') && hasArch(n),
77
+ (n) => endsWith(n, '.appimage'),
78
+ (n) => endsWith(n, '.tar.gz') && lc(n).includes('linux'),
79
+ (n) => endsWith(n, '.deb') && hasArch(n),
80
+ ];
81
+ default:
82
+ return [];
83
+ }
84
+ }
85
+ function archAliases(arch) {
86
+ switch (arch) {
87
+ case 'x64':
88
+ return ['x64', 'x86_64', 'amd64'];
89
+ case 'arm64':
90
+ return ['arm64', 'aarch64'];
91
+ case 'ia32':
92
+ return ['ia32', 'x86', 'i386'];
93
+ default:
94
+ return [arch];
95
+ }
96
+ }
97
+ // ── Version parsing & comparison ──────────────────────────────────────────
98
+ const SEMVER_RE = /(\d+)\.(\d+)\.(\d+)(?:[-+]([0-9A-Za-z.-]+))?/;
99
+ const TRAILING_NUMBER_RE = /-(\d+)$/;
100
+ function schemeParser(scheme) {
101
+ if (scheme === 'trailing-number') {
102
+ return (release) => {
103
+ const m = release.tag_name.match(TRAILING_NUMBER_RE);
104
+ return m ? m[1] : null;
105
+ };
106
+ }
107
+ return defaultParseVersion;
108
+ }
109
+ function defaultParseVersion(release) {
110
+ const fromTag = extractSemver(release.tag_name);
111
+ if (fromTag)
112
+ return fromTag;
113
+ if (release.name) {
114
+ const fromName = extractSemver(release.name);
115
+ if (fromName)
116
+ return fromName;
117
+ }
118
+ for (const asset of release.assets) {
119
+ const hit = extractSemver(asset.name);
120
+ if (hit)
121
+ return hit;
122
+ }
123
+ return null;
124
+ }
125
+ function extractSemver(input) {
126
+ const m = input.match(SEMVER_RE);
127
+ if (!m)
128
+ return null;
129
+ const [, major, minor, patch, pre] = m;
130
+ return pre ? `${major}.${minor}.${patch}-${pre}` : `${major}.${minor}.${patch}`;
131
+ }
132
+ function stripV(version) {
133
+ return version.replace(/^v/i, '');
134
+ }
135
+ function compareSemver(a, b) {
136
+ const [aCore, aPre] = splitPre(a);
137
+ const [bCore, bPre] = splitPre(b);
138
+ const [a1, a2, a3] = parseCore(aCore);
139
+ const [b1, b2, b3] = parseCore(bCore);
140
+ if (a1 !== b1)
141
+ return a1 - b1;
142
+ if (a2 !== b2)
143
+ return a2 - b2;
144
+ if (a3 !== b3)
145
+ return a3 - b3;
146
+ // Release > prerelease of same core.
147
+ if (!aPre && bPre)
148
+ return 1;
149
+ if (aPre && !bPre)
150
+ return -1;
151
+ if (!aPre && !bPre)
152
+ return 0;
153
+ return aPre.localeCompare(bPre);
154
+ }
155
+ function splitPre(v) {
156
+ const i = v.indexOf('-');
157
+ return i < 0 ? [v, null] : [v.slice(0, i), v.slice(i + 1)];
158
+ }
159
+ function parseCore(core) {
160
+ const parts = core.split('.').map((s) => parseInt(s, 10) || 0);
161
+ return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
162
+ }
163
+ // ── HTTP helpers ──────────────────────────────────────────────────────────
164
+ function ghHeaders(token) {
165
+ const headers = {
166
+ 'User-Agent': 'dimina-kit-updater',
167
+ 'Accept': 'application/vnd.github+json',
168
+ 'X-GitHub-Api-Version': '2022-11-28',
169
+ };
170
+ if (token)
171
+ headers.Authorization = `Bearer ${token}`;
172
+ return headers;
173
+ }
174
+ async function fetchRelease(url, opts) {
175
+ const body = await httpGetText(url, { headers: ghHeaders(opts.token) });
176
+ if (!body)
177
+ return null;
178
+ if (opts.includePrereleases) {
179
+ const list = JSON.parse(body);
180
+ const first = list.find((r) => !r.draft);
181
+ return first ?? null;
182
+ }
183
+ const release = JSON.parse(body);
184
+ return release.draft ? null : release;
185
+ }
186
+ function httpGetText(url, init) {
187
+ return new Promise((resolve, reject) => {
188
+ const req = https.get(url, { headers: init.headers }, (res) => {
189
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
190
+ res.resume();
191
+ resolve(httpGetText(res.headers.location, init));
192
+ return;
193
+ }
194
+ if (res.statusCode !== 200) {
195
+ res.resume();
196
+ resolve(null);
197
+ return;
198
+ }
199
+ let body = '';
200
+ res.setEncoding('utf8');
201
+ res.on('data', (chunk) => { body += chunk; });
202
+ res.on('end', () => resolve(body));
203
+ });
204
+ req.on('error', reject);
205
+ });
206
+ }
207
+ function downloadFile(url, dest, token, onProgress) {
208
+ return new Promise((resolve, reject) => {
209
+ const headers = ghHeaders(token);
210
+ // GitHub asset downloads redirect to signed S3 URLs; keep following them.
211
+ const attempt = (target, redirectsLeft) => {
212
+ const req = https.get(target, { headers }, (res) => {
213
+ if (res.statusCode &&
214
+ res.statusCode >= 300 &&
215
+ res.statusCode < 400 &&
216
+ res.headers.location) {
217
+ res.resume();
218
+ if (redirectsLeft <= 0) {
219
+ reject(new Error('Too many redirects'));
220
+ return;
221
+ }
222
+ attempt(res.headers.location, redirectsLeft - 1);
223
+ return;
224
+ }
225
+ if (res.statusCode !== 200) {
226
+ res.resume();
227
+ reject(new Error(`HTTP ${res.statusCode} while downloading update`));
228
+ return;
229
+ }
230
+ const total = parseInt(res.headers['content-length'] || '0', 10);
231
+ let received = 0;
232
+ const out = fs.createWriteStream(dest);
233
+ res.on('data', (chunk) => {
234
+ received += chunk.length;
235
+ if (total > 0 && onProgress) {
236
+ onProgress((received / total) * 100);
237
+ }
238
+ });
239
+ res.pipe(out);
240
+ out.on('finish', () => { out.close(); resolve(); });
241
+ out.on('error', (err) => { fs.unlink(dest, () => { }); reject(err); });
242
+ });
243
+ req.on('error', (err) => { fs.unlink(dest, () => { }); reject(err); });
244
+ };
245
+ attempt(url, 5);
246
+ });
247
+ }
248
+ //# sourceMappingURL=github-release-checker.js.map
@@ -0,0 +1,5 @@
1
+ export { UpdateManager } from './update-manager.js';
2
+ export type { UpdateManagerOptions } from './update-manager.js';
3
+ export { createGitHubReleaseChecker } from './github-release-checker.js';
4
+ export type { GitHubReleaseCheckerOptions, PickAssetContext, VersionScheme, } from './github-release-checker.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { UpdateManager } from './update-manager.js';
2
+ export { createGitHubReleaseChecker } from './github-release-checker.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,37 @@
1
+ import { BrowserWindow } from 'electron';
2
+ import type { UpdateChecker, UpdateInfo } from '../../../shared/types.js';
3
+ export interface UpdateManagerOptions {
4
+ checker: UpdateChecker;
5
+ mainWindow: BrowserWindow;
6
+ /** Check interval in milliseconds. Default: 1 hour */
7
+ checkInterval?: number;
8
+ /** Delay before the first check after startup in ms. Default: 5000 */
9
+ initialDelay?: number;
10
+ /** Override the version string passed to the checker. Default: app.getVersion() */
11
+ getCurrentVersion?: () => string;
12
+ }
13
+ export declare class UpdateManager {
14
+ private checker;
15
+ private mainWindow;
16
+ private getCurrentVersion;
17
+ private latestUpdate;
18
+ private downloadedPath;
19
+ private timer;
20
+ private initialTimer;
21
+ constructor(opts: UpdateManagerOptions);
22
+ private registerIpc;
23
+ check(): Promise<{
24
+ hasUpdate: boolean;
25
+ info?: UpdateInfo;
26
+ }>;
27
+ download(): Promise<{
28
+ success: boolean;
29
+ filePath?: string;
30
+ error?: string;
31
+ }>;
32
+ install(): void;
33
+ dispose(): void;
34
+ private startPeriodicCheck;
35
+ private checkAndNotify;
36
+ }
37
+ //# sourceMappingURL=update-manager.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { app, ipcMain, shell } from 'electron';
2
+ export class UpdateManager {
3
+ checker;
4
+ mainWindow;
5
+ getCurrentVersion;
6
+ latestUpdate = null;
7
+ downloadedPath = null;
8
+ timer = null;
9
+ initialTimer = null;
10
+ constructor(opts) {
11
+ this.checker = opts.checker;
12
+ this.mainWindow = opts.mainWindow;
13
+ this.getCurrentVersion = opts.getCurrentVersion ?? (() => app.getVersion());
14
+ this.registerIpc();
15
+ this.startPeriodicCheck(opts.checkInterval ?? 60 * 60 * 1000, opts.initialDelay ?? 5000);
16
+ }
17
+ registerIpc() {
18
+ ipcMain.handle('updates:check', async () => {
19
+ return this.check();
20
+ });
21
+ ipcMain.handle('updates:download', async () => {
22
+ return this.download();
23
+ });
24
+ ipcMain.handle('updates:install', async () => {
25
+ this.install();
26
+ });
27
+ }
28
+ async check() {
29
+ try {
30
+ const currentVersion = this.getCurrentVersion();
31
+ const info = await this.checker.checkForUpdates(currentVersion);
32
+ if (info) {
33
+ this.latestUpdate = info;
34
+ this.downloadedPath = null;
35
+ return { hasUpdate: true, info };
36
+ }
37
+ return { hasUpdate: false };
38
+ }
39
+ catch (err) {
40
+ console.warn('[UpdateManager] check failed:', err);
41
+ return { hasUpdate: false };
42
+ }
43
+ }
44
+ async download() {
45
+ if (!this.latestUpdate) {
46
+ return { success: false, error: 'No update available' };
47
+ }
48
+ try {
49
+ const filePath = await this.checker.downloadUpdate(this.latestUpdate, (percent) => {
50
+ this.mainWindow.webContents.send('updates:downloadProgress', { percent });
51
+ });
52
+ this.downloadedPath = filePath;
53
+ return { success: true, filePath };
54
+ }
55
+ catch (err) {
56
+ console.warn('[UpdateManager] download failed:', err);
57
+ return { success: false, error: String(err) };
58
+ }
59
+ }
60
+ install() {
61
+ if (this.downloadedPath) {
62
+ shell.openPath(this.downloadedPath);
63
+ app.quit();
64
+ }
65
+ }
66
+ dispose() {
67
+ if (this.timer) {
68
+ clearInterval(this.timer);
69
+ this.timer = null;
70
+ }
71
+ if (this.initialTimer) {
72
+ clearTimeout(this.initialTimer);
73
+ this.initialTimer = null;
74
+ }
75
+ ipcMain.removeHandler('updates:check');
76
+ ipcMain.removeHandler('updates:download');
77
+ ipcMain.removeHandler('updates:install');
78
+ }
79
+ startPeriodicCheck(interval, initialDelay) {
80
+ this.initialTimer = setTimeout(() => void this.checkAndNotify(), initialDelay);
81
+ this.timer = setInterval(() => void this.checkAndNotify(), interval);
82
+ }
83
+ async checkAndNotify() {
84
+ const result = await this.check();
85
+ if (result.hasUpdate && result.info) {
86
+ this.mainWindow.webContents.send('updates:available', result.info);
87
+ }
88
+ }
89
+ }
90
+ //# sourceMappingURL=update-manager.js.map