electron-buff 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/pathHelper/main/index.d.mts +20 -0
- package/dist/pathHelper/main/index.d.ts +20 -0
- package/dist/pathHelper/main/index.js +41 -0
- package/dist/pathHelper/main/index.js.map +1 -0
- package/dist/pathHelper/main/index.mjs +39 -0
- package/dist/pathHelper/main/index.mjs.map +1 -0
- package/dist/pathHelper/renderer/index.d.mts +23 -0
- package/dist/pathHelper/renderer/index.d.ts +23 -0
- package/dist/pathHelper/renderer/index.js +41 -0
- package/dist/pathHelper/renderer/index.js.map +1 -0
- package/dist/pathHelper/renderer/index.mjs +38 -0
- package/dist/pathHelper/renderer/index.mjs.map +1 -0
- package/dist/xpc/main/index.d.mts +80 -0
- package/dist/xpc/main/index.d.ts +80 -0
- package/dist/xpc/main/index.js +185 -0
- package/dist/xpc/main/index.js.map +1 -0
- package/dist/xpc/main/index.mjs +181 -0
- package/dist/xpc/main/index.mjs.map +1 -0
- package/dist/xpc/preload/index.d.mts +50 -0
- package/dist/xpc/preload/index.d.ts +50 -0
- package/dist/xpc/preload/index.js +88 -0
- package/dist/xpc/preload/index.js.map +1 -0
- package/dist/xpc/preload/index.mjs +85 -0
- package/dist/xpc/preload/index.mjs.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# electron-buff
|
|
2
|
+
|
|
3
|
+
Electron enhancement utilities for electron-vite projects.
|
|
4
|
+
|
|
5
|
+
## Modules
|
|
6
|
+
|
|
7
|
+
| Module | Description | Documentation |
|
|
8
|
+
|--------|-------------|---------------|
|
|
9
|
+
| **XPC** | Cross-process communication | [English](./doc/xpc.doc.md) \| [中文](./doc/xpc_cn.doc.md) |
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
yarn add electron-buff
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## TODO
|
|
18
|
+
|
|
19
|
+
1. 通过 Proxy 实现 XPC 调用:发送通过调用 class 的 function 实现,监听通过 class 的函数名在实例化过程中自动注册,无需手动调用 `handle`/`send` 并传入字符串句柄,可实现代码自动补全。
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Module Overview
|
|
23
|
+
|
|
24
|
+
### XPC — Cross-Process Communication
|
|
25
|
+
|
|
26
|
+
XPC is a bidirectional async/await RPC module for **Electron** applications. Unlike Electron's built-in `ipcRenderer.invoke` / `ipcMain.handle`, which only supports renderer-to-main request–response, XPC enables **any process** (renderer or main) to call handlers registered in **any other process** with full `async/await` semantics — including renderer-to-renderer, main-to-renderer, and main-to-main invocations.
|
|
27
|
+
|
|
28
|
+
**Advantages:**
|
|
29
|
+
|
|
30
|
+
1. **Offload work to renderer processes** — Heavy or blocking tasks can be delegated to a preload script running in a hidden renderer window, keeping the main process responsive and reducing its performance overhead.
|
|
31
|
+
2. **Unified async/await across all processes** — Since every inter-process call supports `async/await`, complex multi-step workflows that span multiple processes can be orchestrated with straightforward sequential logic, eliminating deeply nested callbacks or manual event coordination.
|
|
32
|
+
|
|
33
|
+
> Full documentation: [English](./doc/xpc.doc.md) | [中文](./doc/xpc_cn.doc.md)
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type PathName = 'home' | 'appData' | 'userData' | 'sessionData' | 'temp' | 'exe' | 'module' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'recent' | 'logs' | 'crashDumps';
|
|
2
|
+
type PathHelperApi = {
|
|
3
|
+
getAppPath: () => Promise<string>;
|
|
4
|
+
getPath: (name: PathName) => Promise<string>;
|
|
5
|
+
getUserDataPath: () => Promise<string>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
declare class PathMainHelper {
|
|
9
|
+
init(): void;
|
|
10
|
+
private setupListeners;
|
|
11
|
+
/** Get the app installation path */
|
|
12
|
+
getAppPath(): string;
|
|
13
|
+
/** Get a special directory or file path by name */
|
|
14
|
+
getPath(name: PathName): string;
|
|
15
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
16
|
+
getUserDataPath(): string;
|
|
17
|
+
}
|
|
18
|
+
declare const pathMainHelper: PathMainHelper;
|
|
19
|
+
|
|
20
|
+
export { type PathHelperApi, type PathName, pathMainHelper };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type PathName = 'home' | 'appData' | 'userData' | 'sessionData' | 'temp' | 'exe' | 'module' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'recent' | 'logs' | 'crashDumps';
|
|
2
|
+
type PathHelperApi = {
|
|
3
|
+
getAppPath: () => Promise<string>;
|
|
4
|
+
getPath: (name: PathName) => Promise<string>;
|
|
5
|
+
getUserDataPath: () => Promise<string>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
declare class PathMainHelper {
|
|
9
|
+
init(): void;
|
|
10
|
+
private setupListeners;
|
|
11
|
+
/** Get the app installation path */
|
|
12
|
+
getAppPath(): string;
|
|
13
|
+
/** Get a special directory or file path by name */
|
|
14
|
+
getPath(name: PathName): string;
|
|
15
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
16
|
+
getUserDataPath(): string;
|
|
17
|
+
}
|
|
18
|
+
declare const pathMainHelper: PathMainHelper;
|
|
19
|
+
|
|
20
|
+
export { type PathHelperApi, type PathName, pathMainHelper };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var electron = require('electron');
|
|
4
|
+
|
|
5
|
+
// src/pathHelper/main/pathMain.helper.ts
|
|
6
|
+
var IPC_GET_APP_PATH = "__buff_path_getAppPath__";
|
|
7
|
+
var IPC_GET_PATH = "__buff_path_getPath__";
|
|
8
|
+
var IPC_GET_USER_DATA_PATH = "__buff_path_getUserDataPath__";
|
|
9
|
+
var PathMainHelper = class {
|
|
10
|
+
init() {
|
|
11
|
+
this.setupListeners();
|
|
12
|
+
}
|
|
13
|
+
setupListeners() {
|
|
14
|
+
electron.ipcMain.handle(IPC_GET_APP_PATH, () => {
|
|
15
|
+
return electron.app.getAppPath();
|
|
16
|
+
});
|
|
17
|
+
electron.ipcMain.handle(IPC_GET_PATH, (_event, name) => {
|
|
18
|
+
return electron.app.getPath(name);
|
|
19
|
+
});
|
|
20
|
+
electron.ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {
|
|
21
|
+
return this.getUserDataPath();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/** Get the app installation path */
|
|
25
|
+
getAppPath() {
|
|
26
|
+
return electron.app.getAppPath();
|
|
27
|
+
}
|
|
28
|
+
/** Get a special directory or file path by name */
|
|
29
|
+
getPath(name) {
|
|
30
|
+
return electron.app.getPath(name);
|
|
31
|
+
}
|
|
32
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
33
|
+
getUserDataPath() {
|
|
34
|
+
return electron.app.getPath("userData");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var pathMainHelper = new PathMainHelper();
|
|
38
|
+
|
|
39
|
+
exports.pathMainHelper = pathMainHelper;
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/pathHelper/main/pathMain.helper.ts"],"names":["ipcMain","app"],"mappings":";;;;;AAGA,IAAM,gBAAA,GAAmB,0BAAA;AACzB,IAAM,YAAA,GAAe,uBAAA;AACrB,IAAM,sBAAA,GAAyB,+BAAA;AAE/B,IAAM,iBAAN,MAAqB;AAAA,EACnB,IAAA,GAAa;AACX,IAAA,IAAA,CAAK,cAAA,EAAe;AAAA,EACtB;AAAA,EAEQ,cAAA,GAAuB;AAC7B,IAAAA,gBAAA,CAAQ,MAAA,CAAO,kBAAkB,MAAM;AACrC,MAAA,OAAOC,aAAI,UAAA,EAAW;AAAA,IACxB,CAAC,CAAA;AAED,IAAAD,gBAAA,CAAQ,MAAA,CAAO,YAAA,EAAc,CAAC,MAAA,EAAQ,IAAA,KAAmB;AACvD,MAAA,OAAOC,YAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,IACzB,CAAC,CAAA;AAED,IAAAD,gBAAA,CAAQ,MAAA,CAAO,wBAAwB,MAAM;AAC3C,MAAA,OAAO,KAAK,eAAA,EAAgB;AAAA,IAC9B,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,UAAA,GAAqB;AACnB,IAAA,OAAOC,aAAI,UAAA,EAAW;AAAA,EACxB;AAAA;AAAA,EAGA,QAAQ,IAAA,EAAwB;AAC9B,IAAA,OAAOA,YAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,EACzB;AAAA;AAAA,EAGA,eAAA,GAA0B;AACxB,IAAA,OAAOA,YAAA,CAAI,QAAQ,UAAU,CAAA;AAAA,EAC/B;AAEF,CAAA;AAEO,IAAM,cAAA,GAAiB,IAAI,cAAA","file":"index.js","sourcesContent":["import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ipcMain, app } from 'electron';
|
|
2
|
+
|
|
3
|
+
// src/pathHelper/main/pathMain.helper.ts
|
|
4
|
+
var IPC_GET_APP_PATH = "__buff_path_getAppPath__";
|
|
5
|
+
var IPC_GET_PATH = "__buff_path_getPath__";
|
|
6
|
+
var IPC_GET_USER_DATA_PATH = "__buff_path_getUserDataPath__";
|
|
7
|
+
var PathMainHelper = class {
|
|
8
|
+
init() {
|
|
9
|
+
this.setupListeners();
|
|
10
|
+
}
|
|
11
|
+
setupListeners() {
|
|
12
|
+
ipcMain.handle(IPC_GET_APP_PATH, () => {
|
|
13
|
+
return app.getAppPath();
|
|
14
|
+
});
|
|
15
|
+
ipcMain.handle(IPC_GET_PATH, (_event, name) => {
|
|
16
|
+
return app.getPath(name);
|
|
17
|
+
});
|
|
18
|
+
ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {
|
|
19
|
+
return this.getUserDataPath();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Get the app installation path */
|
|
23
|
+
getAppPath() {
|
|
24
|
+
return app.getAppPath();
|
|
25
|
+
}
|
|
26
|
+
/** Get a special directory or file path by name */
|
|
27
|
+
getPath(name) {
|
|
28
|
+
return app.getPath(name);
|
|
29
|
+
}
|
|
30
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
31
|
+
getUserDataPath() {
|
|
32
|
+
return app.getPath("userData");
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var pathMainHelper = new PathMainHelper();
|
|
36
|
+
|
|
37
|
+
export { pathMainHelper };
|
|
38
|
+
//# sourceMappingURL=index.mjs.map
|
|
39
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/pathHelper/main/pathMain.helper.ts"],"names":[],"mappings":";;;AAGA,IAAM,gBAAA,GAAmB,0BAAA;AACzB,IAAM,YAAA,GAAe,uBAAA;AACrB,IAAM,sBAAA,GAAyB,+BAAA;AAE/B,IAAM,iBAAN,MAAqB;AAAA,EACnB,IAAA,GAAa;AACX,IAAA,IAAA,CAAK,cAAA,EAAe;AAAA,EACtB;AAAA,EAEQ,cAAA,GAAuB;AAC7B,IAAA,OAAA,CAAQ,MAAA,CAAO,kBAAkB,MAAM;AACrC,MAAA,OAAO,IAAI,UAAA,EAAW;AAAA,IACxB,CAAC,CAAA;AAED,IAAA,OAAA,CAAQ,MAAA,CAAO,YAAA,EAAc,CAAC,MAAA,EAAQ,IAAA,KAAmB;AACvD,MAAA,OAAO,GAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,IACzB,CAAC,CAAA;AAED,IAAA,OAAA,CAAQ,MAAA,CAAO,wBAAwB,MAAM;AAC3C,MAAA,OAAO,KAAK,eAAA,EAAgB;AAAA,IAC9B,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,UAAA,GAAqB;AACnB,IAAA,OAAO,IAAI,UAAA,EAAW;AAAA,EACxB;AAAA;AAAA,EAGA,QAAQ,IAAA,EAAwB;AAC9B,IAAA,OAAO,GAAA,CAAI,QAAQ,IAAI,CAAA;AAAA,EACzB;AAAA;AAAA,EAGA,eAAA,GAA0B;AACxB,IAAA,OAAO,GAAA,CAAI,QAAQ,UAAU,CAAA;AAAA,EAC/B;AAEF,CAAA;AAEO,IAAM,cAAA,GAAiB,IAAI,cAAA","file":"index.mjs","sourcesContent":["import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type PathName = 'home' | 'appData' | 'userData' | 'sessionData' | 'temp' | 'exe' | 'module' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'recent' | 'logs' | 'crashDumps';
|
|
2
|
+
type PathHelperApi = {
|
|
3
|
+
getAppPath: () => Promise<string>;
|
|
4
|
+
getPath: (name: PathName) => Promise<string>;
|
|
5
|
+
getUserDataPath: () => Promise<string>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
declare class PathRendererHelper {
|
|
9
|
+
/** Get the app installation path */
|
|
10
|
+
getAppPath(): Promise<string>;
|
|
11
|
+
/** Get a special directory or file path by name */
|
|
12
|
+
getPath(name: PathName): Promise<string>;
|
|
13
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
14
|
+
getUserDataPath(): Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
declare const pathRendererHelper: PathRendererHelper;
|
|
17
|
+
/**
|
|
18
|
+
* Returns a contextBridge-safe object for exposeInMainWorld.
|
|
19
|
+
* Usage: contextBridge.exposeInMainWorld('pathHelper', exposePathHelper())
|
|
20
|
+
*/
|
|
21
|
+
declare const exposePathHelper: () => PathHelperApi;
|
|
22
|
+
|
|
23
|
+
export { type PathHelperApi, type PathName, exposePathHelper, pathRendererHelper };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type PathName = 'home' | 'appData' | 'userData' | 'sessionData' | 'temp' | 'exe' | 'module' | 'desktop' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos' | 'recent' | 'logs' | 'crashDumps';
|
|
2
|
+
type PathHelperApi = {
|
|
3
|
+
getAppPath: () => Promise<string>;
|
|
4
|
+
getPath: (name: PathName) => Promise<string>;
|
|
5
|
+
getUserDataPath: () => Promise<string>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
declare class PathRendererHelper {
|
|
9
|
+
/** Get the app installation path */
|
|
10
|
+
getAppPath(): Promise<string>;
|
|
11
|
+
/** Get a special directory or file path by name */
|
|
12
|
+
getPath(name: PathName): Promise<string>;
|
|
13
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
14
|
+
getUserDataPath(): Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
declare const pathRendererHelper: PathRendererHelper;
|
|
17
|
+
/**
|
|
18
|
+
* Returns a contextBridge-safe object for exposeInMainWorld.
|
|
19
|
+
* Usage: contextBridge.exposeInMainWorld('pathHelper', exposePathHelper())
|
|
20
|
+
*/
|
|
21
|
+
declare const exposePathHelper: () => PathHelperApi;
|
|
22
|
+
|
|
23
|
+
export { type PathHelperApi, type PathName, exposePathHelper, pathRendererHelper };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var electron = require('electron');
|
|
4
|
+
|
|
5
|
+
// src/pathHelper/renderer/pathRenderer.helper.ts
|
|
6
|
+
var IPC_GET_APP_PATH = "__buff_path_getAppPath__";
|
|
7
|
+
var IPC_GET_PATH = "__buff_path_getPath__";
|
|
8
|
+
var IPC_GET_USER_DATA_PATH = "__buff_path_getUserDataPath__";
|
|
9
|
+
var PathRendererHelper = class {
|
|
10
|
+
/** Get the app installation path */
|
|
11
|
+
async getAppPath() {
|
|
12
|
+
return await electron.ipcRenderer.invoke(IPC_GET_APP_PATH);
|
|
13
|
+
}
|
|
14
|
+
/** Get a special directory or file path by name */
|
|
15
|
+
async getPath(name) {
|
|
16
|
+
return await electron.ipcRenderer.invoke(IPC_GET_PATH, name);
|
|
17
|
+
}
|
|
18
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
19
|
+
async getUserDataPath() {
|
|
20
|
+
return await electron.ipcRenderer.invoke(IPC_GET_USER_DATA_PATH);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var pathRendererHelper = new PathRendererHelper();
|
|
24
|
+
var exposePathHelper = () => {
|
|
25
|
+
return {
|
|
26
|
+
getAppPath: () => {
|
|
27
|
+
return pathRendererHelper.getAppPath();
|
|
28
|
+
},
|
|
29
|
+
getPath: (name) => {
|
|
30
|
+
return pathRendererHelper.getPath(name);
|
|
31
|
+
},
|
|
32
|
+
getUserDataPath: () => {
|
|
33
|
+
return pathRendererHelper.getUserDataPath();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
exports.exposePathHelper = exposePathHelper;
|
|
39
|
+
exports.pathRendererHelper = pathRendererHelper;
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/pathHelper/renderer/pathRenderer.helper.ts"],"names":["ipcRenderer"],"mappings":";;;;;AAGA,IAAM,gBAAA,GAAmB,0BAAA;AACzB,IAAM,YAAA,GAAe,uBAAA;AACrB,IAAM,sBAAA,GAAyB,+BAAA;AAE/B,IAAM,qBAAN,MAAyB;AAAA;AAAA,EAEvB,MAAM,UAAA,GAA8B;AAClC,IAAA,OAAO,MAAMA,oBAAA,CAAY,MAAA,CAAO,gBAAgB,CAAA;AAAA,EAClD;AAAA;AAAA,EAGA,MAAM,QAAQ,IAAA,EAAiC;AAC7C,IAAA,OAAO,MAAMA,oBAAA,CAAY,MAAA,CAAO,YAAA,EAAc,IAAI,CAAA;AAAA,EACpD;AAAA;AAAA,EAGA,MAAM,eAAA,GAAmC;AACvC,IAAA,OAAO,MAAMA,oBAAA,CAAY,MAAA,CAAO,sBAAsB,CAAA;AAAA,EACxD;AACF,CAAA;AAEO,IAAM,kBAAA,GAAqB,IAAI,kBAAA;AAM/B,IAAM,mBAAmB,MAAqB;AACnD,EAAA,OAAO;AAAA,IACL,YAAY,MAAuB;AACjC,MAAA,OAAO,mBAAmB,UAAA,EAAW;AAAA,IACvC,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,IAAA,KAAoC;AAC5C,MAAA,OAAO,kBAAA,CAAmB,QAAQ,IAAI,CAAA;AAAA,IACxC,CAAA;AAAA,IACA,iBAAiB,MAAuB;AACtC,MAAA,OAAO,mBAAmB,eAAA,EAAgB;AAAA,IAC5C;AAAA,GACF;AACF","file":"index.js","sourcesContent":["import { ipcRenderer } from 'electron';\nimport type { PathName, PathHelperApi } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathRendererHelper {\n /** Get the app installation path */\n async getAppPath(): Promise<string> {\n return await ipcRenderer.invoke(IPC_GET_APP_PATH);\n }\n\n /** Get a special directory or file path by name */\n async getPath(name: PathName): Promise<string> {\n return await ipcRenderer.invoke(IPC_GET_PATH, name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n async getUserDataPath(): Promise<string> {\n return await ipcRenderer.invoke(IPC_GET_USER_DATA_PATH);\n }\n}\n\nexport const pathRendererHelper = new PathRendererHelper();\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n * Usage: contextBridge.exposeInMainWorld('pathHelper', exposePathHelper())\n */\nexport const exposePathHelper = (): PathHelperApi => {\n return {\n getAppPath: (): Promise<string> => {\n return pathRendererHelper.getAppPath();\n },\n getPath: (name: PathName): Promise<string> => {\n return pathRendererHelper.getPath(name);\n },\n getUserDataPath: (): Promise<string> => {\n return pathRendererHelper.getUserDataPath();\n },\n };\n};\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ipcRenderer } from 'electron';
|
|
2
|
+
|
|
3
|
+
// src/pathHelper/renderer/pathRenderer.helper.ts
|
|
4
|
+
var IPC_GET_APP_PATH = "__buff_path_getAppPath__";
|
|
5
|
+
var IPC_GET_PATH = "__buff_path_getPath__";
|
|
6
|
+
var IPC_GET_USER_DATA_PATH = "__buff_path_getUserDataPath__";
|
|
7
|
+
var PathRendererHelper = class {
|
|
8
|
+
/** Get the app installation path */
|
|
9
|
+
async getAppPath() {
|
|
10
|
+
return await ipcRenderer.invoke(IPC_GET_APP_PATH);
|
|
11
|
+
}
|
|
12
|
+
/** Get a special directory or file path by name */
|
|
13
|
+
async getPath(name) {
|
|
14
|
+
return await ipcRenderer.invoke(IPC_GET_PATH, name);
|
|
15
|
+
}
|
|
16
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
17
|
+
async getUserDataPath() {
|
|
18
|
+
return await ipcRenderer.invoke(IPC_GET_USER_DATA_PATH);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var pathRendererHelper = new PathRendererHelper();
|
|
22
|
+
var exposePathHelper = () => {
|
|
23
|
+
return {
|
|
24
|
+
getAppPath: () => {
|
|
25
|
+
return pathRendererHelper.getAppPath();
|
|
26
|
+
},
|
|
27
|
+
getPath: (name) => {
|
|
28
|
+
return pathRendererHelper.getPath(name);
|
|
29
|
+
},
|
|
30
|
+
getUserDataPath: () => {
|
|
31
|
+
return pathRendererHelper.getUserDataPath();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { exposePathHelper, pathRendererHelper };
|
|
37
|
+
//# sourceMappingURL=index.mjs.map
|
|
38
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/pathHelper/renderer/pathRenderer.helper.ts"],"names":[],"mappings":";;;AAGA,IAAM,gBAAA,GAAmB,0BAAA;AACzB,IAAM,YAAA,GAAe,uBAAA;AACrB,IAAM,sBAAA,GAAyB,+BAAA;AAE/B,IAAM,qBAAN,MAAyB;AAAA;AAAA,EAEvB,MAAM,UAAA,GAA8B;AAClC,IAAA,OAAO,MAAM,WAAA,CAAY,MAAA,CAAO,gBAAgB,CAAA;AAAA,EAClD;AAAA;AAAA,EAGA,MAAM,QAAQ,IAAA,EAAiC;AAC7C,IAAA,OAAO,MAAM,WAAA,CAAY,MAAA,CAAO,YAAA,EAAc,IAAI,CAAA;AAAA,EACpD;AAAA;AAAA,EAGA,MAAM,eAAA,GAAmC;AACvC,IAAA,OAAO,MAAM,WAAA,CAAY,MAAA,CAAO,sBAAsB,CAAA;AAAA,EACxD;AACF,CAAA;AAEO,IAAM,kBAAA,GAAqB,IAAI,kBAAA;AAM/B,IAAM,mBAAmB,MAAqB;AACnD,EAAA,OAAO;AAAA,IACL,YAAY,MAAuB;AACjC,MAAA,OAAO,mBAAmB,UAAA,EAAW;AAAA,IACvC,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,IAAA,KAAoC;AAC5C,MAAA,OAAO,kBAAA,CAAmB,QAAQ,IAAI,CAAA;AAAA,IACxC,CAAA;AAAA,IACA,iBAAiB,MAAuB;AACtC,MAAA,OAAO,mBAAmB,eAAA,EAAgB;AAAA,IAC5C;AAAA,GACF;AACF","file":"index.mjs","sourcesContent":["import { ipcRenderer } from 'electron';\nimport type { PathName, PathHelperApi } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathRendererHelper {\n /** Get the app installation path */\n async getAppPath(): Promise<string> {\n return await ipcRenderer.invoke(IPC_GET_APP_PATH);\n }\n\n /** Get a special directory or file path by name */\n async getPath(name: PathName): Promise<string> {\n return await ipcRenderer.invoke(IPC_GET_PATH, name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n async getUserDataPath(): Promise<string> {\n return await ipcRenderer.invoke(IPC_GET_USER_DATA_PATH);\n }\n}\n\nexport const pathRendererHelper = new PathRendererHelper();\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n * Usage: contextBridge.exposeInMainWorld('pathHelper', exposePathHelper())\n */\nexport const exposePathHelper = (): PathHelperApi => {\n return {\n getAppPath: (): Promise<string> => {\n return pathRendererHelper.getAppPath();\n },\n getPath: (name: PathName): Promise<string> => {\n return pathRendererHelper.getPath(name);\n },\n getUserDataPath: (): Promise<string> => {\n return pathRendererHelper.getUserDataPath();\n },\n };\n};\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XpcCenter: runs in the main process.
|
|
3
|
+
* - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}
|
|
4
|
+
* - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,
|
|
5
|
+
* blocks via semaphore until __xpc_finish__ is received, then returns result.
|
|
6
|
+
* - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.
|
|
7
|
+
*/
|
|
8
|
+
declare class XpcCenter {
|
|
9
|
+
/** handleName → webContentsId */
|
|
10
|
+
private registry;
|
|
11
|
+
/** task.id → XpcTask (with semaphore block/unblock) */
|
|
12
|
+
private pendingTasks;
|
|
13
|
+
init(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Register a main-process handleName in the registry with webContentsId = 0.
|
|
16
|
+
*/
|
|
17
|
+
registerMainHandler(handleName: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Execute a handleName: if main-process handler, call directly;
|
|
20
|
+
* otherwise forward to target renderer, block until __xpc_finish__.
|
|
21
|
+
* Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().
|
|
22
|
+
*/
|
|
23
|
+
exec(handleName: string, params?: any): Promise<any>;
|
|
24
|
+
private setupListeners;
|
|
25
|
+
}
|
|
26
|
+
declare const xpcCenter: XpcCenter;
|
|
27
|
+
|
|
28
|
+
type XpcPayload = {
|
|
29
|
+
/** Unique task ID, guaranteed unique within process lifetime */
|
|
30
|
+
id: string;
|
|
31
|
+
/** Event handle name */
|
|
32
|
+
handleName: string;
|
|
33
|
+
/** Parameters, nullable */
|
|
34
|
+
params?: any;
|
|
35
|
+
/** Return data from target process, nullable, defaults to null */
|
|
36
|
+
ret?: any;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type XpcHandler = (payload: XpcPayload) => Promise<any>;
|
|
40
|
+
/**
|
|
41
|
+
* XpcMain: runs in the main process.
|
|
42
|
+
* - handle(): register a handler callable by renderers or other main-process code.
|
|
43
|
+
* - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.
|
|
44
|
+
*/
|
|
45
|
+
declare class XpcMain {
|
|
46
|
+
private handlers;
|
|
47
|
+
/**
|
|
48
|
+
* Register a handler in the main process.
|
|
49
|
+
* When another renderer calls send() with this handleName, xpcCenter will
|
|
50
|
+
* invoke this handler directly (webContentsId = 0) without forwarding to a renderer.
|
|
51
|
+
*/
|
|
52
|
+
handle(handleName: string, handler: XpcHandler): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get the registered handler for a given handleName.
|
|
55
|
+
*/
|
|
56
|
+
getHandler(handleName: string): XpcHandler | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Send a message to a registered handler by handleName.
|
|
59
|
+
* Delegates to xpcCenter.exec() which handles both main-process and renderer targets.
|
|
60
|
+
*/
|
|
61
|
+
send(handleName: string, params?: any): Promise<any>;
|
|
62
|
+
}
|
|
63
|
+
declare const xpcMain: XpcMain;
|
|
64
|
+
|
|
65
|
+
declare class XpcTask implements XpcPayload {
|
|
66
|
+
id: string;
|
|
67
|
+
handleName: string;
|
|
68
|
+
params?: any;
|
|
69
|
+
ret?: any;
|
|
70
|
+
private semaphore;
|
|
71
|
+
constructor(payload: XpcPayload);
|
|
72
|
+
/** Block until unblock() is called */
|
|
73
|
+
block(): Promise<void>;
|
|
74
|
+
/** Release the semaphore, unblocking the waiting block() call */
|
|
75
|
+
unblock(): void;
|
|
76
|
+
/** Convert to a plain XpcPayload (serializable for IPC) */
|
|
77
|
+
toPayload(): XpcPayload;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { type XpcPayload, XpcTask, xpcCenter, xpcMain };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XpcCenter: runs in the main process.
|
|
3
|
+
* - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}
|
|
4
|
+
* - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,
|
|
5
|
+
* blocks via semaphore until __xpc_finish__ is received, then returns result.
|
|
6
|
+
* - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.
|
|
7
|
+
*/
|
|
8
|
+
declare class XpcCenter {
|
|
9
|
+
/** handleName → webContentsId */
|
|
10
|
+
private registry;
|
|
11
|
+
/** task.id → XpcTask (with semaphore block/unblock) */
|
|
12
|
+
private pendingTasks;
|
|
13
|
+
init(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Register a main-process handleName in the registry with webContentsId = 0.
|
|
16
|
+
*/
|
|
17
|
+
registerMainHandler(handleName: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Execute a handleName: if main-process handler, call directly;
|
|
20
|
+
* otherwise forward to target renderer, block until __xpc_finish__.
|
|
21
|
+
* Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().
|
|
22
|
+
*/
|
|
23
|
+
exec(handleName: string, params?: any): Promise<any>;
|
|
24
|
+
private setupListeners;
|
|
25
|
+
}
|
|
26
|
+
declare const xpcCenter: XpcCenter;
|
|
27
|
+
|
|
28
|
+
type XpcPayload = {
|
|
29
|
+
/** Unique task ID, guaranteed unique within process lifetime */
|
|
30
|
+
id: string;
|
|
31
|
+
/** Event handle name */
|
|
32
|
+
handleName: string;
|
|
33
|
+
/** Parameters, nullable */
|
|
34
|
+
params?: any;
|
|
35
|
+
/** Return data from target process, nullable, defaults to null */
|
|
36
|
+
ret?: any;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type XpcHandler = (payload: XpcPayload) => Promise<any>;
|
|
40
|
+
/**
|
|
41
|
+
* XpcMain: runs in the main process.
|
|
42
|
+
* - handle(): register a handler callable by renderers or other main-process code.
|
|
43
|
+
* - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.
|
|
44
|
+
*/
|
|
45
|
+
declare class XpcMain {
|
|
46
|
+
private handlers;
|
|
47
|
+
/**
|
|
48
|
+
* Register a handler in the main process.
|
|
49
|
+
* When another renderer calls send() with this handleName, xpcCenter will
|
|
50
|
+
* invoke this handler directly (webContentsId = 0) without forwarding to a renderer.
|
|
51
|
+
*/
|
|
52
|
+
handle(handleName: string, handler: XpcHandler): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get the registered handler for a given handleName.
|
|
55
|
+
*/
|
|
56
|
+
getHandler(handleName: string): XpcHandler | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Send a message to a registered handler by handleName.
|
|
59
|
+
* Delegates to xpcCenter.exec() which handles both main-process and renderer targets.
|
|
60
|
+
*/
|
|
61
|
+
send(handleName: string, params?: any): Promise<any>;
|
|
62
|
+
}
|
|
63
|
+
declare const xpcMain: XpcMain;
|
|
64
|
+
|
|
65
|
+
declare class XpcTask implements XpcPayload {
|
|
66
|
+
id: string;
|
|
67
|
+
handleName: string;
|
|
68
|
+
params?: any;
|
|
69
|
+
ret?: any;
|
|
70
|
+
private semaphore;
|
|
71
|
+
constructor(payload: XpcPayload);
|
|
72
|
+
/** Block until unblock() is called */
|
|
73
|
+
block(): Promise<void>;
|
|
74
|
+
/** Release the semaphore, unblocking the waiting block() call */
|
|
75
|
+
unblock(): void;
|
|
76
|
+
/** Convert to a plain XpcPayload (serializable for IPC) */
|
|
77
|
+
toPayload(): XpcPayload;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { type XpcPayload, XpcTask, xpcCenter, xpcMain };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var electron = require('electron');
|
|
4
|
+
var rigFoundation = require('rig-foundation');
|
|
5
|
+
|
|
6
|
+
// src/xpc/main/xpc-center.helper.ts
|
|
7
|
+
var XpcTask = class {
|
|
8
|
+
constructor(payload) {
|
|
9
|
+
this.id = payload.id;
|
|
10
|
+
this.handleName = payload.handleName;
|
|
11
|
+
this.params = payload.params;
|
|
12
|
+
this.ret = payload.ret ?? null;
|
|
13
|
+
this.semaphore = new rigFoundation.Semaphore(1);
|
|
14
|
+
this.semaphore.take(() => {
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** Block until unblock() is called */
|
|
18
|
+
block() {
|
|
19
|
+
return this.semaphore.takeAsync();
|
|
20
|
+
}
|
|
21
|
+
/** Release the semaphore, unblocking the waiting block() call */
|
|
22
|
+
unblock() {
|
|
23
|
+
this.semaphore.leave();
|
|
24
|
+
}
|
|
25
|
+
/** Convert to a plain XpcPayload (serializable for IPC) */
|
|
26
|
+
toPayload() {
|
|
27
|
+
return {
|
|
28
|
+
id: this.id,
|
|
29
|
+
handleName: this.handleName,
|
|
30
|
+
params: this.params,
|
|
31
|
+
ret: this.ret
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/xpc/main/xpc-id.helper.ts
|
|
37
|
+
var prefix = Math.random().toString(36).slice(2, 8);
|
|
38
|
+
var counter = 0;
|
|
39
|
+
var generateXpcId = () => {
|
|
40
|
+
return `${prefix}-${(++counter).toString(36)}`;
|
|
41
|
+
};
|
|
42
|
+
var IPC_GET_APP_PATH = "__buff_path_getAppPath__";
|
|
43
|
+
var IPC_GET_PATH = "__buff_path_getPath__";
|
|
44
|
+
var IPC_GET_USER_DATA_PATH = "__buff_path_getUserDataPath__";
|
|
45
|
+
var PathMainHelper = class {
|
|
46
|
+
init() {
|
|
47
|
+
this.setupListeners();
|
|
48
|
+
}
|
|
49
|
+
setupListeners() {
|
|
50
|
+
electron.ipcMain.handle(IPC_GET_APP_PATH, () => {
|
|
51
|
+
return electron.app.getAppPath();
|
|
52
|
+
});
|
|
53
|
+
electron.ipcMain.handle(IPC_GET_PATH, (_event, name) => {
|
|
54
|
+
return electron.app.getPath(name);
|
|
55
|
+
});
|
|
56
|
+
electron.ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {
|
|
57
|
+
return this.getUserDataPath();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** Get the app installation path */
|
|
61
|
+
getAppPath() {
|
|
62
|
+
return electron.app.getAppPath();
|
|
63
|
+
}
|
|
64
|
+
/** Get a special directory or file path by name */
|
|
65
|
+
getPath(name) {
|
|
66
|
+
return electron.app.getPath(name);
|
|
67
|
+
}
|
|
68
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
69
|
+
getUserDataPath() {
|
|
70
|
+
return electron.app.getPath("userData");
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var pathMainHelper = new PathMainHelper();
|
|
74
|
+
|
|
75
|
+
// src/xpc/main/xpc-main.helper.ts
|
|
76
|
+
var XpcMain = class {
|
|
77
|
+
constructor() {
|
|
78
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Register a handler in the main process.
|
|
82
|
+
* When another renderer calls send() with this handleName, xpcCenter will
|
|
83
|
+
* invoke this handler directly (webContentsId = 0) without forwarding to a renderer.
|
|
84
|
+
*/
|
|
85
|
+
handle(handleName, handler) {
|
|
86
|
+
this.handlers.set(handleName, handler);
|
|
87
|
+
xpcCenter.registerMainHandler(handleName);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get the registered handler for a given handleName.
|
|
91
|
+
*/
|
|
92
|
+
getHandler(handleName) {
|
|
93
|
+
return this.handlers.get(handleName);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Send a message to a registered handler by handleName.
|
|
97
|
+
* Delegates to xpcCenter.exec() which handles both main-process and renderer targets.
|
|
98
|
+
*/
|
|
99
|
+
async send(handleName, params) {
|
|
100
|
+
return xpcCenter.exec(handleName, params);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var xpcMain = new XpcMain();
|
|
104
|
+
|
|
105
|
+
// src/xpc/main/xpc-center.helper.ts
|
|
106
|
+
var XPC_REGISTER = "__xpc_register__";
|
|
107
|
+
var XPC_EXEC = "__xpc_exec__";
|
|
108
|
+
var XPC_FINISH = "__xpc_finish__";
|
|
109
|
+
var XpcCenter = class {
|
|
110
|
+
constructor() {
|
|
111
|
+
/** handleName → webContentsId */
|
|
112
|
+
this.registry = /* @__PURE__ */ new Map();
|
|
113
|
+
/** task.id → XpcTask (with semaphore block/unblock) */
|
|
114
|
+
this.pendingTasks = /* @__PURE__ */ new Map();
|
|
115
|
+
}
|
|
116
|
+
init() {
|
|
117
|
+
pathMainHelper.init();
|
|
118
|
+
this.setupListeners();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Register a main-process handleName in the registry with webContentsId = 0.
|
|
122
|
+
*/
|
|
123
|
+
registerMainHandler(handleName) {
|
|
124
|
+
this.registry.set(handleName, 0);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Execute a handleName: if main-process handler, call directly;
|
|
128
|
+
* otherwise forward to target renderer, block until __xpc_finish__.
|
|
129
|
+
* Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().
|
|
130
|
+
*/
|
|
131
|
+
async exec(handleName, params) {
|
|
132
|
+
const targetId = this.registry.get(handleName);
|
|
133
|
+
if (targetId == null) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
const payload = {
|
|
137
|
+
id: generateXpcId(),
|
|
138
|
+
handleName,
|
|
139
|
+
params
|
|
140
|
+
};
|
|
141
|
+
if (targetId === 0) {
|
|
142
|
+
const handler = xpcMain.getHandler(handleName);
|
|
143
|
+
if (!handler) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return await handler(payload);
|
|
148
|
+
} catch (_e) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const target = electron.webContents.fromId(targetId);
|
|
153
|
+
if (!target || target.isDestroyed() || target.isCrashed()) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const task = new XpcTask(payload);
|
|
157
|
+
this.pendingTasks.set(task.id, task);
|
|
158
|
+
target.send(handleName, payload);
|
|
159
|
+
await task.block();
|
|
160
|
+
this.pendingTasks.delete(task.id);
|
|
161
|
+
return task.toPayload().ret ?? null;
|
|
162
|
+
}
|
|
163
|
+
setupListeners() {
|
|
164
|
+
electron.ipcMain.on(XPC_REGISTER, (event, payload) => {
|
|
165
|
+
this.registry.set(payload.handleName, event.sender.id);
|
|
166
|
+
});
|
|
167
|
+
electron.ipcMain.handle(XPC_EXEC, async (_event, payload) => {
|
|
168
|
+
return this.exec(payload.handleName, payload.params);
|
|
169
|
+
});
|
|
170
|
+
electron.ipcMain.on(XPC_FINISH, (_event, payload) => {
|
|
171
|
+
const task = this.pendingTasks.get(payload.id);
|
|
172
|
+
if (task) {
|
|
173
|
+
task.ret = payload.ret ?? null;
|
|
174
|
+
task.unblock();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
var xpcCenter = new XpcCenter();
|
|
180
|
+
|
|
181
|
+
exports.XpcTask = XpcTask;
|
|
182
|
+
exports.xpcCenter = xpcCenter;
|
|
183
|
+
exports.xpcMain = xpcMain;
|
|
184
|
+
//# sourceMappingURL=index.js.map
|
|
185
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/xpc/main/xpc-task.helper.ts","../../../src/xpc/main/xpc-id.helper.ts","../../../src/pathHelper/main/pathMain.helper.ts","../../../src/xpc/main/xpc-main.helper.ts","../../../src/xpc/main/xpc-center.helper.ts"],"names":["Semaphore","ipcMain","app","webContents"],"mappings":"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","file":"index.js","sourcesContent":["import { Semaphore } from 'rig-foundation';\nimport { XpcPayload } from '../shared/xpc.type';\n\nexport class XpcTask implements XpcPayload {\n id: string;\n handleName: string;\n params?: any;\n ret?: any;\n\n private semaphore: Semaphore;\n\n constructor(payload: XpcPayload) {\n this.id = payload.id;\n this.handleName = payload.handleName;\n this.params = payload.params;\n this.ret = payload.ret ?? null;\n this.semaphore = new Semaphore(1);\n this.semaphore.take(() => {});\n }\n\n /** Block until unblock() is called */\n block(): Promise<void> {\n return this.semaphore.takeAsync();\n }\n\n /** Release the semaphore, unblocking the waiting block() call */\n unblock(): void {\n this.semaphore.leave();\n }\n\n /** Convert to a plain XpcPayload (serializable for IPC) */\n toPayload(): XpcPayload {\n return {\n id: this.id,\n handleName: this.handleName,\n params: this.params,\n ret: this.ret,\n };\n }\n}\n","/**\n * High-performance process-unique ID generator.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `${prefix}-${(++counter).toString(36)}`;\n};\n","import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n","import { XpcPayload } from '../shared/xpc.type';\nimport { xpcCenter } from './xpc-center.helper';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcMain: runs in the main process.\n * - handle(): register a handler callable by renderers or other main-process code.\n * - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.\n */\nclass XpcMain {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handler in the main process.\n * When another renderer calls send() with this handleName, xpcCenter will\n * invoke this handler directly (webContentsId = 0) without forwarding to a renderer.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n xpcCenter.registerMainHandler(handleName);\n }\n\n /**\n * Get the registered handler for a given handleName.\n */\n getHandler(handleName: string): XpcHandler | undefined {\n return this.handlers.get(handleName);\n }\n\n /**\n * Send a message to a registered handler by handleName.\n * Delegates to xpcCenter.exec() which handles both main-process and renderer targets.\n */\n async send(handleName: string, params?: any): Promise<any> {\n return xpcCenter.exec(handleName, params);\n }\n}\n\nexport const xpcMain = new XpcMain();\n","import { ipcMain, webContents } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { XpcTask } from './xpc-task.helper';\nimport { generateXpcId } from './xpc-id.helper';\nimport { pathMainHelper } from '../../pathHelper/main/pathMain.helper';\nimport { xpcMain } from './xpc-main.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\n/**\n * XpcCenter: runs in the main process.\n * - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}\n * - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,\n * blocks via semaphore until __xpc_finish__ is received, then returns result.\n * - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.\n */\nclass XpcCenter {\n /** handleName → webContentsId */\n private registry = new Map<string, number>();\n /** task.id → XpcTask (with semaphore block/unblock) */\n private pendingTasks = new Map<string, XpcTask>();\n\n init(): void {\n pathMainHelper.init();\n this.setupListeners();\n }\n\n /**\n * Register a main-process handleName in the registry with webContentsId = 0.\n */\n registerMainHandler(handleName: string): void {\n this.registry.set(handleName, 0);\n }\n\n /**\n * Execute a handleName: if main-process handler, call directly;\n * otherwise forward to target renderer, block until __xpc_finish__.\n * Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().\n */\n async exec(handleName: string, params?: any): Promise<any> {\n const targetId = this.registry.get(handleName);\n if (targetId == null) {\n return null;\n }\n\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n };\n\n // targetId === 0 means the handler is registered in the main process\n if (targetId === 0) {\n const handler = xpcMain.getHandler(handleName);\n if (!handler) {\n return null;\n }\n try {\n return await handler(payload);\n } catch (_e) {\n return null;\n }\n }\n\n const target = webContents.fromId(targetId);\n if (!target || target.isDestroyed() || target.isCrashed()) {\n return null;\n }\n\n // Create semaphore-blocked task\n const task = new XpcTask(payload);\n\n this.pendingTasks.set(task.id, task);\n\n // Forward handleName event + payload to target renderer\n target.send(handleName, payload);\n\n // Block until __xpc_finish__ unblocks\n await task.block();\n this.pendingTasks.delete(task.id);\n\n return task.toPayload().ret ?? null;\n }\n\n private setupListeners(): void {\n // Renderer registers a handleName\n ipcMain.on(XPC_REGISTER, (event, payload: { handleName: string }) => {\n this.registry.set(payload.handleName, event.sender.id);\n });\n\n // Renderer invokes exec via IPC\n ipcMain.handle(XPC_EXEC, async (_event, payload: XpcPayload): Promise<any> => {\n return this.exec(payload.handleName, payload.params);\n });\n\n // Target renderer finished execution, unblock pending task\n ipcMain.on(XPC_FINISH, (_event, payload: XpcPayload) => {\n const task = this.pendingTasks.get(payload.id);\n if (task) {\n task.ret = payload.ret ?? null;\n task.unblock();\n }\n });\n }\n}\n\nexport const xpcCenter = new XpcCenter();\n"]}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { webContents, ipcMain, app } from 'electron';
|
|
2
|
+
import { Semaphore } from 'rig-foundation';
|
|
3
|
+
|
|
4
|
+
// src/xpc/main/xpc-center.helper.ts
|
|
5
|
+
var XpcTask = class {
|
|
6
|
+
constructor(payload) {
|
|
7
|
+
this.id = payload.id;
|
|
8
|
+
this.handleName = payload.handleName;
|
|
9
|
+
this.params = payload.params;
|
|
10
|
+
this.ret = payload.ret ?? null;
|
|
11
|
+
this.semaphore = new Semaphore(1);
|
|
12
|
+
this.semaphore.take(() => {
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** Block until unblock() is called */
|
|
16
|
+
block() {
|
|
17
|
+
return this.semaphore.takeAsync();
|
|
18
|
+
}
|
|
19
|
+
/** Release the semaphore, unblocking the waiting block() call */
|
|
20
|
+
unblock() {
|
|
21
|
+
this.semaphore.leave();
|
|
22
|
+
}
|
|
23
|
+
/** Convert to a plain XpcPayload (serializable for IPC) */
|
|
24
|
+
toPayload() {
|
|
25
|
+
return {
|
|
26
|
+
id: this.id,
|
|
27
|
+
handleName: this.handleName,
|
|
28
|
+
params: this.params,
|
|
29
|
+
ret: this.ret
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/xpc/main/xpc-id.helper.ts
|
|
35
|
+
var prefix = Math.random().toString(36).slice(2, 8);
|
|
36
|
+
var counter = 0;
|
|
37
|
+
var generateXpcId = () => {
|
|
38
|
+
return `${prefix}-${(++counter).toString(36)}`;
|
|
39
|
+
};
|
|
40
|
+
var IPC_GET_APP_PATH = "__buff_path_getAppPath__";
|
|
41
|
+
var IPC_GET_PATH = "__buff_path_getPath__";
|
|
42
|
+
var IPC_GET_USER_DATA_PATH = "__buff_path_getUserDataPath__";
|
|
43
|
+
var PathMainHelper = class {
|
|
44
|
+
init() {
|
|
45
|
+
this.setupListeners();
|
|
46
|
+
}
|
|
47
|
+
setupListeners() {
|
|
48
|
+
ipcMain.handle(IPC_GET_APP_PATH, () => {
|
|
49
|
+
return app.getAppPath();
|
|
50
|
+
});
|
|
51
|
+
ipcMain.handle(IPC_GET_PATH, (_event, name) => {
|
|
52
|
+
return app.getPath(name);
|
|
53
|
+
});
|
|
54
|
+
ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {
|
|
55
|
+
return this.getUserDataPath();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** Get the app installation path */
|
|
59
|
+
getAppPath() {
|
|
60
|
+
return app.getAppPath();
|
|
61
|
+
}
|
|
62
|
+
/** Get a special directory or file path by name */
|
|
63
|
+
getPath(name) {
|
|
64
|
+
return app.getPath(name);
|
|
65
|
+
}
|
|
66
|
+
/** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */
|
|
67
|
+
getUserDataPath() {
|
|
68
|
+
return app.getPath("userData");
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var pathMainHelper = new PathMainHelper();
|
|
72
|
+
|
|
73
|
+
// src/xpc/main/xpc-main.helper.ts
|
|
74
|
+
var XpcMain = class {
|
|
75
|
+
constructor() {
|
|
76
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Register a handler in the main process.
|
|
80
|
+
* When another renderer calls send() with this handleName, xpcCenter will
|
|
81
|
+
* invoke this handler directly (webContentsId = 0) without forwarding to a renderer.
|
|
82
|
+
*/
|
|
83
|
+
handle(handleName, handler) {
|
|
84
|
+
this.handlers.set(handleName, handler);
|
|
85
|
+
xpcCenter.registerMainHandler(handleName);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the registered handler for a given handleName.
|
|
89
|
+
*/
|
|
90
|
+
getHandler(handleName) {
|
|
91
|
+
return this.handlers.get(handleName);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Send a message to a registered handler by handleName.
|
|
95
|
+
* Delegates to xpcCenter.exec() which handles both main-process and renderer targets.
|
|
96
|
+
*/
|
|
97
|
+
async send(handleName, params) {
|
|
98
|
+
return xpcCenter.exec(handleName, params);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var xpcMain = new XpcMain();
|
|
102
|
+
|
|
103
|
+
// src/xpc/main/xpc-center.helper.ts
|
|
104
|
+
var XPC_REGISTER = "__xpc_register__";
|
|
105
|
+
var XPC_EXEC = "__xpc_exec__";
|
|
106
|
+
var XPC_FINISH = "__xpc_finish__";
|
|
107
|
+
var XpcCenter = class {
|
|
108
|
+
constructor() {
|
|
109
|
+
/** handleName → webContentsId */
|
|
110
|
+
this.registry = /* @__PURE__ */ new Map();
|
|
111
|
+
/** task.id → XpcTask (with semaphore block/unblock) */
|
|
112
|
+
this.pendingTasks = /* @__PURE__ */ new Map();
|
|
113
|
+
}
|
|
114
|
+
init() {
|
|
115
|
+
pathMainHelper.init();
|
|
116
|
+
this.setupListeners();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Register a main-process handleName in the registry with webContentsId = 0.
|
|
120
|
+
*/
|
|
121
|
+
registerMainHandler(handleName) {
|
|
122
|
+
this.registry.set(handleName, 0);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Execute a handleName: if main-process handler, call directly;
|
|
126
|
+
* otherwise forward to target renderer, block until __xpc_finish__.
|
|
127
|
+
* Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().
|
|
128
|
+
*/
|
|
129
|
+
async exec(handleName, params) {
|
|
130
|
+
const targetId = this.registry.get(handleName);
|
|
131
|
+
if (targetId == null) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const payload = {
|
|
135
|
+
id: generateXpcId(),
|
|
136
|
+
handleName,
|
|
137
|
+
params
|
|
138
|
+
};
|
|
139
|
+
if (targetId === 0) {
|
|
140
|
+
const handler = xpcMain.getHandler(handleName);
|
|
141
|
+
if (!handler) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
return await handler(payload);
|
|
146
|
+
} catch (_e) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const target = webContents.fromId(targetId);
|
|
151
|
+
if (!target || target.isDestroyed() || target.isCrashed()) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const task = new XpcTask(payload);
|
|
155
|
+
this.pendingTasks.set(task.id, task);
|
|
156
|
+
target.send(handleName, payload);
|
|
157
|
+
await task.block();
|
|
158
|
+
this.pendingTasks.delete(task.id);
|
|
159
|
+
return task.toPayload().ret ?? null;
|
|
160
|
+
}
|
|
161
|
+
setupListeners() {
|
|
162
|
+
ipcMain.on(XPC_REGISTER, (event, payload) => {
|
|
163
|
+
this.registry.set(payload.handleName, event.sender.id);
|
|
164
|
+
});
|
|
165
|
+
ipcMain.handle(XPC_EXEC, async (_event, payload) => {
|
|
166
|
+
return this.exec(payload.handleName, payload.params);
|
|
167
|
+
});
|
|
168
|
+
ipcMain.on(XPC_FINISH, (_event, payload) => {
|
|
169
|
+
const task = this.pendingTasks.get(payload.id);
|
|
170
|
+
if (task) {
|
|
171
|
+
task.ret = payload.ret ?? null;
|
|
172
|
+
task.unblock();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var xpcCenter = new XpcCenter();
|
|
178
|
+
|
|
179
|
+
export { XpcTask, xpcCenter, xpcMain };
|
|
180
|
+
//# sourceMappingURL=index.mjs.map
|
|
181
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/xpc/main/xpc-task.helper.ts","../../../src/xpc/main/xpc-id.helper.ts","../../../src/pathHelper/main/pathMain.helper.ts","../../../src/xpc/main/xpc-main.helper.ts","../../../src/xpc/main/xpc-center.helper.ts"],"names":["ipcMain"],"mappings":"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","file":"index.mjs","sourcesContent":["import { Semaphore } from 'rig-foundation';\nimport { XpcPayload } from '../shared/xpc.type';\n\nexport class XpcTask implements XpcPayload {\n id: string;\n handleName: string;\n params?: any;\n ret?: any;\n\n private semaphore: Semaphore;\n\n constructor(payload: XpcPayload) {\n this.id = payload.id;\n this.handleName = payload.handleName;\n this.params = payload.params;\n this.ret = payload.ret ?? null;\n this.semaphore = new Semaphore(1);\n this.semaphore.take(() => {});\n }\n\n /** Block until unblock() is called */\n block(): Promise<void> {\n return this.semaphore.takeAsync();\n }\n\n /** Release the semaphore, unblocking the waiting block() call */\n unblock(): void {\n this.semaphore.leave();\n }\n\n /** Convert to a plain XpcPayload (serializable for IPC) */\n toPayload(): XpcPayload {\n return {\n id: this.id,\n handleName: this.handleName,\n params: this.params,\n ret: this.ret,\n };\n }\n}\n","/**\n * High-performance process-unique ID generator.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `${prefix}-${(++counter).toString(36)}`;\n};\n","import { app, ipcMain } from 'electron';\nimport type { PathName } from '../shared/pathHelper.type';\n\nconst IPC_GET_APP_PATH = '__buff_path_getAppPath__';\nconst IPC_GET_PATH = '__buff_path_getPath__';\nconst IPC_GET_USER_DATA_PATH = '__buff_path_getUserDataPath__';\n\nclass PathMainHelper {\n init(): void {\n this.setupListeners();\n }\n\n private setupListeners(): void {\n ipcMain.handle(IPC_GET_APP_PATH, () => {\n return app.getAppPath();\n });\n\n ipcMain.handle(IPC_GET_PATH, (_event, name: PathName) => {\n return app.getPath(name);\n });\n\n ipcMain.handle(IPC_GET_USER_DATA_PATH, () => {\n return this.getUserDataPath();\n });\n }\n\n /** Get the app installation path */\n getAppPath(): string {\n return app.getAppPath();\n }\n\n /** Get a special directory or file path by name */\n getPath(name: PathName): string {\n return app.getPath(name);\n }\n\n /** Get the user data path (e.g. Application Support on macOS, Roaming on Windows) */\n getUserDataPath(): string {\n return app.getPath('userData');\n }\n\n}\n\nexport const pathMainHelper = new PathMainHelper();\n","import { XpcPayload } from '../shared/xpc.type';\nimport { xpcCenter } from './xpc-center.helper';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcMain: runs in the main process.\n * - handle(): register a handler callable by renderers or other main-process code.\n * - send(): invoke a registered handleName (main-process or renderer), delegating to xpcCenter.\n */\nclass XpcMain {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handler in the main process.\n * When another renderer calls send() with this handleName, xpcCenter will\n * invoke this handler directly (webContentsId = 0) without forwarding to a renderer.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n xpcCenter.registerMainHandler(handleName);\n }\n\n /**\n * Get the registered handler for a given handleName.\n */\n getHandler(handleName: string): XpcHandler | undefined {\n return this.handlers.get(handleName);\n }\n\n /**\n * Send a message to a registered handler by handleName.\n * Delegates to xpcCenter.exec() which handles both main-process and renderer targets.\n */\n async send(handleName: string, params?: any): Promise<any> {\n return xpcCenter.exec(handleName, params);\n }\n}\n\nexport const xpcMain = new XpcMain();\n","import { ipcMain, webContents } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { XpcTask } from './xpc-task.helper';\nimport { generateXpcId } from './xpc-id.helper';\nimport { pathMainHelper } from '../../pathHelper/main/pathMain.helper';\nimport { xpcMain } from './xpc-main.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\n/**\n * XpcCenter: runs in the main process.\n * - Listens for __xpc_register__: renderer registers a handleName, center stores {handleName → webContentsId}\n * - Listens for __xpc_exec__ (ipcMain.handle): renderer invokes exec, center forwards to target renderer,\n * blocks via semaphore until __xpc_finish__ is received, then returns result.\n * - Listens for __xpc_finish__: target renderer finished execution, unblocks the pending task.\n */\nclass XpcCenter {\n /** handleName → webContentsId */\n private registry = new Map<string, number>();\n /** task.id → XpcTask (with semaphore block/unblock) */\n private pendingTasks = new Map<string, XpcTask>();\n\n init(): void {\n pathMainHelper.init();\n this.setupListeners();\n }\n\n /**\n * Register a main-process handleName in the registry with webContentsId = 0.\n */\n registerMainHandler(handleName: string): void {\n this.registry.set(handleName, 0);\n }\n\n /**\n * Execute a handleName: if main-process handler, call directly;\n * otherwise forward to target renderer, block until __xpc_finish__.\n * Used by both ipcMain.handle(XPC_EXEC) and xpcMain.send().\n */\n async exec(handleName: string, params?: any): Promise<any> {\n const targetId = this.registry.get(handleName);\n if (targetId == null) {\n return null;\n }\n\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n };\n\n // targetId === 0 means the handler is registered in the main process\n if (targetId === 0) {\n const handler = xpcMain.getHandler(handleName);\n if (!handler) {\n return null;\n }\n try {\n return await handler(payload);\n } catch (_e) {\n return null;\n }\n }\n\n const target = webContents.fromId(targetId);\n if (!target || target.isDestroyed() || target.isCrashed()) {\n return null;\n }\n\n // Create semaphore-blocked task\n const task = new XpcTask(payload);\n\n this.pendingTasks.set(task.id, task);\n\n // Forward handleName event + payload to target renderer\n target.send(handleName, payload);\n\n // Block until __xpc_finish__ unblocks\n await task.block();\n this.pendingTasks.delete(task.id);\n\n return task.toPayload().ret ?? null;\n }\n\n private setupListeners(): void {\n // Renderer registers a handleName\n ipcMain.on(XPC_REGISTER, (event, payload: { handleName: string }) => {\n this.registry.set(payload.handleName, event.sender.id);\n });\n\n // Renderer invokes exec via IPC\n ipcMain.handle(XPC_EXEC, async (_event, payload: XpcPayload): Promise<any> => {\n return this.exec(payload.handleName, payload.params);\n });\n\n // Target renderer finished execution, unblock pending task\n ipcMain.on(XPC_FINISH, (_event, payload: XpcPayload) => {\n const task = this.pendingTasks.get(payload.id);\n if (task) {\n task.ret = payload.ret ?? null;\n task.unblock();\n }\n });\n }\n}\n\nexport const xpcCenter = new XpcCenter();\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type XpcPayload = {
|
|
2
|
+
/** Unique task ID, guaranteed unique within process lifetime */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Event handle name */
|
|
5
|
+
handleName: string;
|
|
6
|
+
/** Parameters, nullable */
|
|
7
|
+
params?: any;
|
|
8
|
+
/** Return data from target process, nullable, defaults to null */
|
|
9
|
+
ret?: any;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type XpcHandler = (payload: XpcPayload) => Promise<any>;
|
|
13
|
+
/**
|
|
14
|
+
* XpcRenderer: runs in the renderer process (via preload).
|
|
15
|
+
* - register({handleName}): registers a handleName with main process, also sets up local listener
|
|
16
|
+
* - handle(handleName, handler): registers handler locally + sends __xpc_register__ to main
|
|
17
|
+
* - send(handleName, params): invokes __xpc_exec__ on main, awaits result via ipcRenderer.invoke
|
|
18
|
+
*/
|
|
19
|
+
declare class XpcRenderer {
|
|
20
|
+
private handlers;
|
|
21
|
+
/**
|
|
22
|
+
* Register a handleName with the main process and bind a local async handler.
|
|
23
|
+
* When another renderer calls send() with this handleName, xpcCenter will forward
|
|
24
|
+
* the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.
|
|
25
|
+
*/
|
|
26
|
+
handle(handleName: string, handler: XpcHandler): void;
|
|
27
|
+
/**
|
|
28
|
+
* Remove a registered handler.
|
|
29
|
+
*/
|
|
30
|
+
removeHandle(handleName: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Send a message to another renderer (or any registered handler) via main process.
|
|
33
|
+
* Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.
|
|
34
|
+
* Returns the ret value from the target handler, or null.
|
|
35
|
+
*/
|
|
36
|
+
send(handleName: string, params?: any): Promise<any>;
|
|
37
|
+
}
|
|
38
|
+
declare const xpcRenderer: XpcRenderer;
|
|
39
|
+
type XpcRendererApi = {
|
|
40
|
+
handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>) => void;
|
|
41
|
+
removeHandle: (handleName: string) => void;
|
|
42
|
+
send: (handleName: string, params?: any) => Promise<any>;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Returns a contextBridge-safe object for exposeInMainWorld.
|
|
46
|
+
* Usage: contextBridge.exposeInMainWorld('xpcRenderer', exposeXpcRenderer())
|
|
47
|
+
*/
|
|
48
|
+
declare const exposeXpcRenderer: () => XpcRendererApi;
|
|
49
|
+
|
|
50
|
+
export { type XpcPayload, type XpcRendererApi, exposeXpcRenderer, xpcRenderer };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type XpcPayload = {
|
|
2
|
+
/** Unique task ID, guaranteed unique within process lifetime */
|
|
3
|
+
id: string;
|
|
4
|
+
/** Event handle name */
|
|
5
|
+
handleName: string;
|
|
6
|
+
/** Parameters, nullable */
|
|
7
|
+
params?: any;
|
|
8
|
+
/** Return data from target process, nullable, defaults to null */
|
|
9
|
+
ret?: any;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type XpcHandler = (payload: XpcPayload) => Promise<any>;
|
|
13
|
+
/**
|
|
14
|
+
* XpcRenderer: runs in the renderer process (via preload).
|
|
15
|
+
* - register({handleName}): registers a handleName with main process, also sets up local listener
|
|
16
|
+
* - handle(handleName, handler): registers handler locally + sends __xpc_register__ to main
|
|
17
|
+
* - send(handleName, params): invokes __xpc_exec__ on main, awaits result via ipcRenderer.invoke
|
|
18
|
+
*/
|
|
19
|
+
declare class XpcRenderer {
|
|
20
|
+
private handlers;
|
|
21
|
+
/**
|
|
22
|
+
* Register a handleName with the main process and bind a local async handler.
|
|
23
|
+
* When another renderer calls send() with this handleName, xpcCenter will forward
|
|
24
|
+
* the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.
|
|
25
|
+
*/
|
|
26
|
+
handle(handleName: string, handler: XpcHandler): void;
|
|
27
|
+
/**
|
|
28
|
+
* Remove a registered handler.
|
|
29
|
+
*/
|
|
30
|
+
removeHandle(handleName: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Send a message to another renderer (or any registered handler) via main process.
|
|
33
|
+
* Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.
|
|
34
|
+
* Returns the ret value from the target handler, or null.
|
|
35
|
+
*/
|
|
36
|
+
send(handleName: string, params?: any): Promise<any>;
|
|
37
|
+
}
|
|
38
|
+
declare const xpcRenderer: XpcRenderer;
|
|
39
|
+
type XpcRendererApi = {
|
|
40
|
+
handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>) => void;
|
|
41
|
+
removeHandle: (handleName: string) => void;
|
|
42
|
+
send: (handleName: string, params?: any) => Promise<any>;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Returns a contextBridge-safe object for exposeInMainWorld.
|
|
46
|
+
* Usage: contextBridge.exposeInMainWorld('xpcRenderer', exposeXpcRenderer())
|
|
47
|
+
*/
|
|
48
|
+
declare const exposeXpcRenderer: () => XpcRendererApi;
|
|
49
|
+
|
|
50
|
+
export { type XpcPayload, type XpcRendererApi, exposeXpcRenderer, xpcRenderer };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var electron = require('electron');
|
|
4
|
+
|
|
5
|
+
// src/xpc/preload/xpc-renderer.helper.ts
|
|
6
|
+
|
|
7
|
+
// src/xpc/preload/xpc-id.helper.ts
|
|
8
|
+
var prefix = Math.random().toString(36).slice(2, 8);
|
|
9
|
+
var counter = 0;
|
|
10
|
+
var generateXpcId = () => {
|
|
11
|
+
return `r-${prefix}-${(++counter).toString(36)}`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/xpc/preload/xpc-renderer.helper.ts
|
|
15
|
+
var XPC_REGISTER = "__xpc_register__";
|
|
16
|
+
var XPC_EXEC = "__xpc_exec__";
|
|
17
|
+
var XPC_FINISH = "__xpc_finish__";
|
|
18
|
+
var XpcRenderer = class {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register a handleName with the main process and bind a local async handler.
|
|
24
|
+
* When another renderer calls send() with this handleName, xpcCenter will forward
|
|
25
|
+
* the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.
|
|
26
|
+
*/
|
|
27
|
+
handle(handleName, handler) {
|
|
28
|
+
this.handlers.set(handleName, handler);
|
|
29
|
+
electron.ipcRenderer.send(XPC_REGISTER, { handleName });
|
|
30
|
+
electron.ipcRenderer.on(handleName, async (_event, payload) => {
|
|
31
|
+
let ret = null;
|
|
32
|
+
const localHandler = this.handlers.get(handleName);
|
|
33
|
+
if (localHandler) {
|
|
34
|
+
try {
|
|
35
|
+
ret = await localHandler(payload);
|
|
36
|
+
} catch (_e) {
|
|
37
|
+
ret = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
electron.ipcRenderer.send(XPC_FINISH, {
|
|
41
|
+
id: payload.id,
|
|
42
|
+
handleName: payload.handleName,
|
|
43
|
+
params: payload.params,
|
|
44
|
+
ret
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Remove a registered handler.
|
|
50
|
+
*/
|
|
51
|
+
removeHandle(handleName) {
|
|
52
|
+
this.handlers.delete(handleName);
|
|
53
|
+
electron.ipcRenderer.removeAllListeners(handleName);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Send a message to another renderer (or any registered handler) via main process.
|
|
57
|
+
* Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.
|
|
58
|
+
* Returns the ret value from the target handler, or null.
|
|
59
|
+
*/
|
|
60
|
+
async send(handleName, params) {
|
|
61
|
+
const payload = {
|
|
62
|
+
id: generateXpcId(),
|
|
63
|
+
handleName,
|
|
64
|
+
params,
|
|
65
|
+
ret: null
|
|
66
|
+
};
|
|
67
|
+
return await electron.ipcRenderer.invoke(XPC_EXEC, payload);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var xpcRenderer = new XpcRenderer();
|
|
71
|
+
var exposeXpcRenderer = () => {
|
|
72
|
+
return {
|
|
73
|
+
handle: (handleName, handler) => {
|
|
74
|
+
xpcRenderer.handle(handleName, handler);
|
|
75
|
+
},
|
|
76
|
+
removeHandle: (handleName) => {
|
|
77
|
+
xpcRenderer.removeHandle(handleName);
|
|
78
|
+
},
|
|
79
|
+
send: (handleName, params) => {
|
|
80
|
+
return xpcRenderer.send(handleName, params);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
exports.exposeXpcRenderer = exposeXpcRenderer;
|
|
86
|
+
exports.xpcRenderer = xpcRenderer;
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
88
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/xpc/preload/xpc-id.helper.ts","../../../src/xpc/preload/xpc-renderer.helper.ts"],"names":["ipcRenderer"],"mappings":";;;;;;;AAKA,IAAM,MAAA,GAAS,KAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACpD,IAAI,OAAA,GAAU,CAAA;AAEP,IAAM,gBAAgB,MAAc;AACzC,EAAA,OAAO,KAAK,MAAM,CAAA,CAAA,EAAA,CAAK,EAAE,OAAA,EAAS,QAAA,CAAS,EAAE,CAAC,CAAA,CAAA;AAChD,CAAA;;;ACNA,IAAM,YAAA,GAAe,kBAAA;AACrB,IAAM,QAAA,GAAW,cAAA;AACjB,IAAM,UAAA,GAAa,gBAAA;AAUnB,IAAM,cAAN,MAAkB;AAAA,EAAlB,WAAA,GAAA;AACE,IAAA,IAAA,CAAQ,QAAA,uBAAe,GAAA,EAAwB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/C,MAAA,CAAO,YAAoB,OAAA,EAA2B;AACpD,IAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,UAAA,EAAY,OAAO,CAAA;AAGrC,IAAAA,oBAAA,CAAY,IAAA,CAAK,YAAA,EAAc,EAAE,UAAA,EAAY,CAAA;AAG7C,IAAAA,oBAAA,CAAY,EAAA,CAAG,UAAA,EAAY,OAAO,MAAA,EAAQ,OAAA,KAAwB;AAChE,MAAA,IAAI,GAAA,GAAW,IAAA;AACf,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA;AACjD,MAAA,IAAI,YAAA,EAAc;AAChB,QAAA,IAAI;AACF,UAAA,GAAA,GAAM,MAAM,aAAa,OAAO,CAAA;AAAA,QAClC,SAAS,EAAA,EAAI;AACX,UAAA,GAAA,GAAM,IAAA;AAAA,QACR;AAAA,MACF;AAEA,MAAAA,oBAAA,CAAY,KAAK,UAAA,EAAY;AAAA,QAC3B,IAAI,OAAA,CAAQ,EAAA;AAAA,QACZ,YAAY,OAAA,CAAQ,UAAA;AAAA,QACpB,QAAQ,OAAA,CAAQ,MAAA;AAAA,QAChB;AAAA,OACa,CAAA;AAAA,IACjB,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,UAAA,EAA0B;AACrC,IAAA,IAAA,CAAK,QAAA,CAAS,OAAO,UAAU,CAAA;AAC/B,IAAAA,oBAAA,CAAY,mBAAmB,UAAU,CAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IAAA,CAAK,UAAA,EAAoB,MAAA,EAA4B;AACzD,IAAA,MAAM,OAAA,GAAsB;AAAA,MAC1B,IAAI,aAAA,EAAc;AAAA,MAClB,UAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA,EAAK;AAAA,KACP;AAEA,IAAA,OAAO,MAAMA,oBAAA,CAAY,MAAA,CAAO,QAAA,EAAU,OAAO,CAAA;AAAA,EACnD;AACF,CAAA;AAEO,IAAM,WAAA,GAAc,IAAI,WAAA;AAYxB,IAAM,oBAAoB,MAAsB;AACrD,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,CAAC,UAAA,EAAoB,OAAA,KAAyD;AACpF,MAAA,WAAA,CAAY,MAAA,CAAO,YAAY,OAAO,CAAA;AAAA,IACxC,CAAA;AAAA,IACA,YAAA,EAAc,CAAC,UAAA,KAA6B;AAC1C,MAAA,WAAA,CAAY,aAAa,UAAU,CAAA;AAAA,IACrC,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,UAAA,EAAoB,MAAA,KAA+B;AACxD,MAAA,OAAO,WAAA,CAAY,IAAA,CAAK,UAAA,EAAY,MAAM,CAAA;AAAA,IAC5C;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * High-performance process-unique ID generator for renderer process.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `r-${prefix}-${(++counter).toString(36)}`;\n};\n","import { ipcRenderer } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { generateXpcId } from './xpc-id.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcRenderer: runs in the renderer process (via preload).\n * - register({handleName}): registers a handleName with main process, also sets up local listener\n * - handle(handleName, handler): registers handler locally + sends __xpc_register__ to main\n * - send(handleName, params): invokes __xpc_exec__ on main, awaits result via ipcRenderer.invoke\n */\nclass XpcRenderer {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handleName with the main process and bind a local async handler.\n * When another renderer calls send() with this handleName, xpcCenter will forward\n * the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n\n // Notify main process about this registration\n ipcRenderer.send(XPC_REGISTER, { handleName });\n\n // Listen for incoming handleName events forwarded by xpcCenter\n ipcRenderer.on(handleName, async (_event, payload: XpcPayload) => {\n let ret: any = null;\n const localHandler = this.handlers.get(handleName);\n if (localHandler) {\n try {\n ret = await localHandler(payload);\n } catch (_e) {\n ret = null;\n }\n }\n // Send __xpc_finish__ back to main with result\n ipcRenderer.send(XPC_FINISH, {\n id: payload.id,\n handleName: payload.handleName,\n params: payload.params,\n ret,\n } as XpcPayload);\n });\n }\n\n /**\n * Remove a registered handler.\n */\n removeHandle(handleName: string): void {\n this.handlers.delete(handleName);\n ipcRenderer.removeAllListeners(handleName);\n }\n\n /**\n * Send a message to another renderer (or any registered handler) via main process.\n * Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.\n * Returns the ret value from the target handler, or null.\n */\n async send(handleName: string, params?: any): Promise<any> {\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n ret: null,\n };\n\n return await ipcRenderer.invoke(XPC_EXEC, payload);\n }\n}\n\nexport const xpcRenderer = new XpcRenderer();\n\nexport type XpcRendererApi = {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>) => void;\n removeHandle: (handleName: string) => void;\n send: (handleName: string, params?: any) => Promise<any>;\n};\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n * Usage: contextBridge.exposeInMainWorld('xpcRenderer', exposeXpcRenderer())\n */\nexport const exposeXpcRenderer = (): XpcRendererApi => {\n return {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>): void => {\n xpcRenderer.handle(handleName, handler);\n },\n removeHandle: (handleName: string): void => {\n xpcRenderer.removeHandle(handleName);\n },\n send: (handleName: string, params?: any): Promise<any> => {\n return xpcRenderer.send(handleName, params);\n },\n };\n};\n"]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ipcRenderer } from 'electron';
|
|
2
|
+
|
|
3
|
+
// src/xpc/preload/xpc-renderer.helper.ts
|
|
4
|
+
|
|
5
|
+
// src/xpc/preload/xpc-id.helper.ts
|
|
6
|
+
var prefix = Math.random().toString(36).slice(2, 8);
|
|
7
|
+
var counter = 0;
|
|
8
|
+
var generateXpcId = () => {
|
|
9
|
+
return `r-${prefix}-${(++counter).toString(36)}`;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/xpc/preload/xpc-renderer.helper.ts
|
|
13
|
+
var XPC_REGISTER = "__xpc_register__";
|
|
14
|
+
var XPC_EXEC = "__xpc_exec__";
|
|
15
|
+
var XPC_FINISH = "__xpc_finish__";
|
|
16
|
+
var XpcRenderer = class {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Register a handleName with the main process and bind a local async handler.
|
|
22
|
+
* When another renderer calls send() with this handleName, xpcCenter will forward
|
|
23
|
+
* the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.
|
|
24
|
+
*/
|
|
25
|
+
handle(handleName, handler) {
|
|
26
|
+
this.handlers.set(handleName, handler);
|
|
27
|
+
ipcRenderer.send(XPC_REGISTER, { handleName });
|
|
28
|
+
ipcRenderer.on(handleName, async (_event, payload) => {
|
|
29
|
+
let ret = null;
|
|
30
|
+
const localHandler = this.handlers.get(handleName);
|
|
31
|
+
if (localHandler) {
|
|
32
|
+
try {
|
|
33
|
+
ret = await localHandler(payload);
|
|
34
|
+
} catch (_e) {
|
|
35
|
+
ret = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
ipcRenderer.send(XPC_FINISH, {
|
|
39
|
+
id: payload.id,
|
|
40
|
+
handleName: payload.handleName,
|
|
41
|
+
params: payload.params,
|
|
42
|
+
ret
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Remove a registered handler.
|
|
48
|
+
*/
|
|
49
|
+
removeHandle(handleName) {
|
|
50
|
+
this.handlers.delete(handleName);
|
|
51
|
+
ipcRenderer.removeAllListeners(handleName);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Send a message to another renderer (or any registered handler) via main process.
|
|
55
|
+
* Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.
|
|
56
|
+
* Returns the ret value from the target handler, or null.
|
|
57
|
+
*/
|
|
58
|
+
async send(handleName, params) {
|
|
59
|
+
const payload = {
|
|
60
|
+
id: generateXpcId(),
|
|
61
|
+
handleName,
|
|
62
|
+
params,
|
|
63
|
+
ret: null
|
|
64
|
+
};
|
|
65
|
+
return await ipcRenderer.invoke(XPC_EXEC, payload);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var xpcRenderer = new XpcRenderer();
|
|
69
|
+
var exposeXpcRenderer = () => {
|
|
70
|
+
return {
|
|
71
|
+
handle: (handleName, handler) => {
|
|
72
|
+
xpcRenderer.handle(handleName, handler);
|
|
73
|
+
},
|
|
74
|
+
removeHandle: (handleName) => {
|
|
75
|
+
xpcRenderer.removeHandle(handleName);
|
|
76
|
+
},
|
|
77
|
+
send: (handleName, params) => {
|
|
78
|
+
return xpcRenderer.send(handleName, params);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export { exposeXpcRenderer, xpcRenderer };
|
|
84
|
+
//# sourceMappingURL=index.mjs.map
|
|
85
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/xpc/preload/xpc-id.helper.ts","../../../src/xpc/preload/xpc-renderer.helper.ts"],"names":[],"mappings":";;;;;AAKA,IAAM,MAAA,GAAS,KAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACpD,IAAI,OAAA,GAAU,CAAA;AAEP,IAAM,gBAAgB,MAAc;AACzC,EAAA,OAAO,KAAK,MAAM,CAAA,CAAA,EAAA,CAAK,EAAE,OAAA,EAAS,QAAA,CAAS,EAAE,CAAC,CAAA,CAAA;AAChD,CAAA;;;ACNA,IAAM,YAAA,GAAe,kBAAA;AACrB,IAAM,QAAA,GAAW,cAAA;AACjB,IAAM,UAAA,GAAa,gBAAA;AAUnB,IAAM,cAAN,MAAkB;AAAA,EAAlB,WAAA,GAAA;AACE,IAAA,IAAA,CAAQ,QAAA,uBAAe,GAAA,EAAwB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/C,MAAA,CAAO,YAAoB,OAAA,EAA2B;AACpD,IAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,UAAA,EAAY,OAAO,CAAA;AAGrC,IAAA,WAAA,CAAY,IAAA,CAAK,YAAA,EAAc,EAAE,UAAA,EAAY,CAAA;AAG7C,IAAA,WAAA,CAAY,EAAA,CAAG,UAAA,EAAY,OAAO,MAAA,EAAQ,OAAA,KAAwB;AAChE,MAAA,IAAI,GAAA,GAAW,IAAA;AACf,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA;AACjD,MAAA,IAAI,YAAA,EAAc;AAChB,QAAA,IAAI;AACF,UAAA,GAAA,GAAM,MAAM,aAAa,OAAO,CAAA;AAAA,QAClC,SAAS,EAAA,EAAI;AACX,UAAA,GAAA,GAAM,IAAA;AAAA,QACR;AAAA,MACF;AAEA,MAAA,WAAA,CAAY,KAAK,UAAA,EAAY;AAAA,QAC3B,IAAI,OAAA,CAAQ,EAAA;AAAA,QACZ,YAAY,OAAA,CAAQ,UAAA;AAAA,QACpB,QAAQ,OAAA,CAAQ,MAAA;AAAA,QAChB;AAAA,OACa,CAAA;AAAA,IACjB,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,UAAA,EAA0B;AACrC,IAAA,IAAA,CAAK,QAAA,CAAS,OAAO,UAAU,CAAA;AAC/B,IAAA,WAAA,CAAY,mBAAmB,UAAU,CAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IAAA,CAAK,UAAA,EAAoB,MAAA,EAA4B;AACzD,IAAA,MAAM,OAAA,GAAsB;AAAA,MAC1B,IAAI,aAAA,EAAc;AAAA,MAClB,UAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA,EAAK;AAAA,KACP;AAEA,IAAA,OAAO,MAAM,WAAA,CAAY,MAAA,CAAO,QAAA,EAAU,OAAO,CAAA;AAAA,EACnD;AACF,CAAA;AAEO,IAAM,WAAA,GAAc,IAAI,WAAA;AAYxB,IAAM,oBAAoB,MAAsB;AACrD,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,CAAC,UAAA,EAAoB,OAAA,KAAyD;AACpF,MAAA,WAAA,CAAY,MAAA,CAAO,YAAY,OAAO,CAAA;AAAA,IACxC,CAAA;AAAA,IACA,YAAA,EAAc,CAAC,UAAA,KAA6B;AAC1C,MAAA,WAAA,CAAY,aAAa,UAAU,CAAA;AAAA,IACrC,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,UAAA,EAAoB,MAAA,KAA+B;AACxD,MAAA,OAAO,WAAA,CAAY,IAAA,CAAK,UAAA,EAAY,MAAM,CAAA;AAAA,IAC5C;AAAA,GACF;AACF","file":"index.mjs","sourcesContent":["/**\n * High-performance process-unique ID generator for renderer process.\n * Combines a random prefix (per process) with an incrementing counter.\n * Guaranteed unique within a single process lifetime.\n */\nconst prefix = Math.random().toString(36).slice(2, 8);\nlet counter = 0;\n\nexport const generateXpcId = (): string => {\n return `r-${prefix}-${(++counter).toString(36)}`;\n};\n","import { ipcRenderer } from 'electron';\nimport { XpcPayload } from '../shared/xpc.type';\nimport { generateXpcId } from './xpc-id.helper';\n\nconst XPC_REGISTER = '__xpc_register__';\nconst XPC_EXEC = '__xpc_exec__';\nconst XPC_FINISH = '__xpc_finish__';\n\ntype XpcHandler = (payload: XpcPayload) => Promise<any>;\n\n/**\n * XpcRenderer: runs in the renderer process (via preload).\n * - register({handleName}): registers a handleName with main process, also sets up local listener\n * - handle(handleName, handler): registers handler locally + sends __xpc_register__ to main\n * - send(handleName, params): invokes __xpc_exec__ on main, awaits result via ipcRenderer.invoke\n */\nclass XpcRenderer {\n private handlers = new Map<string, XpcHandler>();\n\n /**\n * Register a handleName with the main process and bind a local async handler.\n * When another renderer calls send() with this handleName, xpcCenter will forward\n * the payload to this renderer, the handler executes, and result is sent back via __xpc_finish__.\n */\n handle(handleName: string, handler: XpcHandler): void {\n this.handlers.set(handleName, handler);\n\n // Notify main process about this registration\n ipcRenderer.send(XPC_REGISTER, { handleName });\n\n // Listen for incoming handleName events forwarded by xpcCenter\n ipcRenderer.on(handleName, async (_event, payload: XpcPayload) => {\n let ret: any = null;\n const localHandler = this.handlers.get(handleName);\n if (localHandler) {\n try {\n ret = await localHandler(payload);\n } catch (_e) {\n ret = null;\n }\n }\n // Send __xpc_finish__ back to main with result\n ipcRenderer.send(XPC_FINISH, {\n id: payload.id,\n handleName: payload.handleName,\n params: payload.params,\n ret,\n } as XpcPayload);\n });\n }\n\n /**\n * Remove a registered handler.\n */\n removeHandle(handleName: string): void {\n this.handlers.delete(handleName);\n ipcRenderer.removeAllListeners(handleName);\n }\n\n /**\n * Send a message to another renderer (or any registered handler) via main process.\n * Uses ipcRenderer.invoke(__xpc_exec__) which blocks until the target finishes.\n * Returns the ret value from the target handler, or null.\n */\n async send(handleName: string, params?: any): Promise<any> {\n const payload: XpcPayload = {\n id: generateXpcId(),\n handleName,\n params,\n ret: null,\n };\n\n return await ipcRenderer.invoke(XPC_EXEC, payload);\n }\n}\n\nexport const xpcRenderer = new XpcRenderer();\n\nexport type XpcRendererApi = {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>) => void;\n removeHandle: (handleName: string) => void;\n send: (handleName: string, params?: any) => Promise<any>;\n};\n\n/**\n * Returns a contextBridge-safe object for exposeInMainWorld.\n * Usage: contextBridge.exposeInMainWorld('xpcRenderer', exposeXpcRenderer())\n */\nexport const exposeXpcRenderer = (): XpcRendererApi => {\n return {\n handle: (handleName: string, handler: (payload: XpcPayload) => Promise<any>): void => {\n xpcRenderer.handle(handleName, handler);\n },\n removeHandle: (handleName: string): void => {\n xpcRenderer.removeHandle(handleName);\n },\n send: (handleName: string, params?: any): Promise<any> => {\n return xpcRenderer.send(handleName, params);\n },\n };\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "electron-buff",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Electron enhancement utilities for electron-vite projects",
|
|
5
|
+
"exports": {
|
|
6
|
+
"./xpc/main": {
|
|
7
|
+
"require": {
|
|
8
|
+
"types": "./dist/xpc/main/index.d.ts",
|
|
9
|
+
"default": "./dist/xpc/main/index.js"
|
|
10
|
+
},
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/xpc/main/index.d.mts",
|
|
13
|
+
"default": "./dist/xpc/main/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"./xpc/preload": {
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/xpc/preload/index.d.ts",
|
|
19
|
+
"default": "./dist/xpc/preload/index.js"
|
|
20
|
+
},
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/xpc/preload/index.d.mts",
|
|
23
|
+
"default": "./dist/xpc/preload/index.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"./pathHelper/main": {
|
|
27
|
+
"require": {
|
|
28
|
+
"types": "./dist/pathHelper/main/index.d.ts",
|
|
29
|
+
"default": "./dist/pathHelper/main/index.js"
|
|
30
|
+
},
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/pathHelper/main/index.d.mts",
|
|
33
|
+
"default": "./dist/pathHelper/main/index.mjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"./pathHelper/renderer": {
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/pathHelper/renderer/index.d.ts",
|
|
39
|
+
"default": "./dist/pathHelper/renderer/index.js"
|
|
40
|
+
},
|
|
41
|
+
"import": {
|
|
42
|
+
"types": "./dist/pathHelper/renderer/index.d.mts",
|
|
43
|
+
"default": "./dist/pathHelper/renderer/index.mjs"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"dev": "tsup --watch",
|
|
53
|
+
"prepublishOnly": "yarn build",
|
|
54
|
+
"publish:npm": "npm publish --registry=https://registry.npmjs.org/"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"electron": ">=20.0.0",
|
|
58
|
+
"rig-foundation": ">=1.0.0"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"registry": "https://registry.npmjs.org/"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"tsup": "^8.0.0",
|
|
65
|
+
"typescript": "^5.3.0"
|
|
66
|
+
},
|
|
67
|
+
"repository": "git@github.com:FlashHand/electron-buff.git",
|
|
68
|
+
"author": "WangBo <ralwayne@163.com>",
|
|
69
|
+
"license": "MIT"
|
|
70
|
+
}
|