electrobun 1.18.1 → 1.18.4-beta.10
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 -14
- package/dash.config.ts +28 -0
- package/dist/api/browser/global.d.ts +6 -0
- package/dist/api/browser/index.ts +162 -44
- package/dist/api/{bun → config}/ElectrobunConfig.ts +109 -40
- package/dist/api/config/validate.test.ts +31 -0
- package/dist/api/config/validate.ts +19 -0
- package/dist/api/preload/.generated/compiled.ts +8 -0
- package/dist/api/{bun/preload → preload}/build.ts +6 -6
- package/dist/api/{bun/preload → preload}/encryption.ts +11 -5
- package/dist/api/{bun/preload → preload}/globals.d.ts +8 -0
- package/dist/api/{bun/preload → preload}/index.ts +18 -8
- package/dist/api/{bun/preload → preload}/webviewTag.ts +32 -3
- package/dist/api/{bun/preload → preload}/wgpuTag.ts +33 -2
- package/dist/api/{bun → sdks/bun}/__tests__/ffi-contract.test.ts +6 -9
- package/dist/api/{bun → sdks/bun}/core/BrowserView.ts +96 -55
- package/dist/api/{bun → sdks/bun}/core/BrowserWindow.ts +20 -30
- package/dist/api/{bun → sdks/bun}/core/BuildConfig.ts +32 -5
- package/dist/api/{bun → sdks/bun}/core/GpuWindow.ts +20 -7
- package/dist/api/sdks/bun/core/Socket.ts +22 -0
- package/dist/api/{bun → sdks/bun}/core/Tray.ts +33 -32
- package/dist/api/{bun → sdks/bun}/core/Updater.ts +10 -10
- package/dist/api/{bun → sdks/bun}/core/Utils.ts +3 -5
- package/dist/api/{bun → sdks/bun}/core/WGPUView.ts +43 -13
- package/dist/api/{bun → sdks/bun}/index.ts +39 -10
- package/dist/api/{bun → sdks/bun}/proc/native.ts +942 -746
- package/dist/api/{bun → sdks/bun}/webGPU.ts +1 -1
- package/dist/api/{bun → sdks/bun}/webgpuAdapter.ts +40 -33
- package/dist/api/shared/build-dependencies.test.ts +48 -0
- package/dist/api/shared/build-dependencies.ts +46 -0
- package/dist/api/shared/go-version.ts +3 -0
- package/dist/api/shared/odin-version.ts +6 -0
- package/dist/api/shared/rust-version.ts +3 -0
- package/dist/go-sdk/callbacks.go +260 -0
- package/dist/go-sdk/electrobun.go +2021 -0
- package/dist/main.js +35 -28
- package/dist/odin-sdk/electrobun/electrobun.odin +2337 -0
- package/dist/preload-full.js +948 -0
- package/dist/preload-sandboxed.js +111 -0
- package/dist/rust-sdk/electrobun.rs +2396 -0
- package/dist/zig-sdk/electrobun.zig +2005 -0
- package/package.json +5 -29
- package/src/cli/index.ts +868 -654
- package/bin/electrobun.cjs +0 -169
- package/dist/api/bun/core/Socket.ts +0 -205
- package/dist/api/bun/core/windowIds.ts +0 -5
- package/dist/api/bun/preload/.generated/compiled.ts +0 -8
- package/dist/api/shared/bun-version.ts +0 -3
- /package/dist/api/{bun/preload → preload}/dragRegions.ts +0 -0
- /package/dist/api/{bun/preload → preload}/events.ts +0 -0
- /package/dist/api/{bun/preload → preload}/index-sandboxed.ts +0 -0
- /package/dist/api/{bun/preload → preload}/internalRpc.ts +0 -0
- /package/dist/api/{bun/preload → preload}/overlaySync.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/core/ApplicationMenu.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/core/ContextMenu.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/core/Paths.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/core/menuRoles.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/events/ApplicationEvents.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/events/event.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/events/eventEmitter.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/events/trayEvents.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/events/webviewEvents.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/events/windowEvents.ts +0 -0
- /package/dist/api/{bun → sdks/bun}/proc/linux.md +0 -0
package/README.md
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
## What is Electrobun?
|
|
15
15
|
|
|
16
16
|
Electrobun aims to be a complete **solution-in-a-box** for building, updating, and shipping ultra fast, tiny, and cross-platform desktop applications written in Typescript.
|
|
17
|
-
Under the hood it uses
|
|
17
|
+
Under the hood it uses Cottontail, Electrobun's JSC-based JavaScript runtime, to execute the main process and bundle webview TypeScript. Native bindings are written in ObjC and C++, with several core components written in <a href="https://ziglang.org/">Zig</a>.
|
|
18
18
|
|
|
19
|
-
Visit <a href="https://
|
|
19
|
+
Visit <a href="https://framework.blackboard.sh/electrobun/">https://framework.blackboard.sh/electrobun/</a> to see api documentation, guides, and more.
|
|
20
20
|
|
|
21
21
|
You use it via npm.
|
|
22
22
|
|
|
@@ -25,7 +25,7 @@ Don't miss our:
|
|
|
25
25
|
- zig optimized BSDIFF implementation that lets you ship tiny app updates as small as 4KB
|
|
26
26
|
- `bundleCEF` flag to bundle and pin Chromium for those that want that tradeoff of consistency over file size
|
|
27
27
|
- `bundleWGPU` that lets you use Bun Typescript -> WGPU to control a native GPU surface without a webview
|
|
28
|
-
- Our Three.js and Babylon.js adapters that work
|
|
28
|
+
- Our Three.js and Babylon.js adapters that work directly in the Cottontail main process
|
|
29
29
|
- Our `<electrobun-webview>` and `<electrobun-wpgu>` html elements that let you composit proper OOPIFs and native GPU surfaces into your UIs
|
|
30
30
|
- so much more.
|
|
31
31
|
|
|
@@ -33,7 +33,7 @@ Don't miss our:
|
|
|
33
33
|
|
|
34
34
|
- Write typescript for the main process and webviews without having to think about it.
|
|
35
35
|
- Isolation between main and webview processes with fast, typed, easy to implement RPC between them.
|
|
36
|
-
- Small self-extracting app bundles ~14MB (when using system webview, most of this is the
|
|
36
|
+
- Small self-extracting app bundles ~14MB (when using the system webview, most of this is the Cottontail/JSC runtime)
|
|
37
37
|
- Even smaller app updates as small as 4KB (using bsdiff it only downloads tiny patches between versions)
|
|
38
38
|
- Provide everything you need in one tightly integrated workflow to start writing code in 5 minutes and distribute in 10.
|
|
39
39
|
|
|
@@ -63,9 +63,11 @@ Don't miss our:
|
|
|
63
63
|
- [GOG Achievements GUI](https://github.com/timendum/gog-achievements-gui) - desktop app for managing GOG achievements
|
|
64
64
|
- [groov](https://github.com/laurenzcodes/groov) - desktop audio deck monitor
|
|
65
65
|
- [Guerilla Glass](https://github.com/okikeSolutions/guerillaglass) - open-source cross-platform creator studio for fast Record -> Edit -> Deliver workflows
|
|
66
|
+
- [Invoke](https://getinvoke.com) - macOS UI automation & shortcut platform
|
|
66
67
|
- [Marginalia](https://github.com/lars-hoeijmans/Marginalia) - a simple note taking app
|
|
67
68
|
- [MarkBun](https://github.com/xiaochong/markbun) - fast, beautiful, Typora-like markdown desktop editor
|
|
68
69
|
- [md-browse](https://github.com/needle-tools/md-browse) - a markdown-first browser that converts web pages to clean markdown
|
|
70
|
+
- [moop](https://github.com/zrubinrattet/moop/) - desktop app for batch image optimization for the web
|
|
69
71
|
- [Patchline](https://github.com/adwaithks/Patchline) - lightweight desktop Git client for reading patches and line diffs, then staging and committing changes
|
|
70
72
|
- [peekachu](https://github.com/needle-tools/peekachu) - password manager for AIs; store secrets in your OS keychain and scrub output so AI assistants never see actual values
|
|
71
73
|
- [PiBun](https://github.com/khairold/pibun) - desktop GUI for the Pi coding agent with chat, terminal, git integration, and plugin system
|
|
@@ -113,6 +115,18 @@ Building apps with Electrobun is as easy as updating your package.json dependenc
|
|
|
113
115
|
|
|
114
116
|
### Prerequisites
|
|
115
117
|
|
|
118
|
+
Install Dash globally before building Electrobun:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
curl -fsSL https://electrobun-artifacts.blackboard.sh/dash-cli/install.sh | sh
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
On Windows PowerShell:
|
|
125
|
+
|
|
126
|
+
```powershell
|
|
127
|
+
& ([scriptblock]::Create((irm https://electrobun-artifacts.blackboard.sh/dash-cli/install.ps1)))
|
|
128
|
+
```
|
|
129
|
+
|
|
116
130
|
**macOS:**
|
|
117
131
|
- Xcode command line tools
|
|
118
132
|
- cmake (install via homebrew: `brew install cmake`)
|
|
@@ -133,8 +147,8 @@ On Ubuntu/Debian based distros: `sudo apt install build-essential cmake pkg-conf
|
|
|
133
147
|
```bash
|
|
134
148
|
git clone --recurse-submodules https://github.com/blackboardsh/electrobun.git
|
|
135
149
|
cd electrobun/package
|
|
136
|
-
|
|
137
|
-
|
|
150
|
+
dash install
|
|
151
|
+
dash dev:clean
|
|
138
152
|
```
|
|
139
153
|
|
|
140
154
|
### Development Workflow
|
|
@@ -144,22 +158,31 @@ bun dev:clean
|
|
|
144
158
|
cd electrobun/package
|
|
145
159
|
|
|
146
160
|
# After making changes to source code
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
# If you only changed kitchen sink code (not electrobun source)
|
|
150
|
-
bun dev:rerun
|
|
161
|
+
dash dev
|
|
151
162
|
|
|
152
163
|
# If you need a completely fresh start
|
|
153
|
-
|
|
164
|
+
dash dev:clean
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
With sibling `jsc`, `cottontail`, `dash-cloud`, and `electrobun` checkouts, use
|
|
168
|
+
one command to build only changed native layers and run Kitchen with the local
|
|
169
|
+
stack:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
dash dev --local
|
|
154
173
|
```
|
|
155
174
|
|
|
175
|
+
The first Dash is globally installed. Stack preparation explicitly selects the
|
|
176
|
+
completed local Dash engine and Cottontail build for the remainder of the
|
|
177
|
+
command.
|
|
178
|
+
|
|
156
179
|
### Additional Commands
|
|
157
180
|
|
|
158
181
|
All commands are run from the `/package` directory:
|
|
159
182
|
|
|
160
|
-
- `
|
|
161
|
-
- `
|
|
162
|
-
- `
|
|
183
|
+
- `dash dev:canary` - Build and run kitchen sink in canary mode
|
|
184
|
+
- `dash build:dev` - Build Electrobun in development mode
|
|
185
|
+
- `dash build:release` - Build Electrobun in release mode
|
|
163
186
|
|
|
164
187
|
### Debugging
|
|
165
188
|
|
package/dash.config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @dash cli=0.2.0-canary.2 cottontail=0.1.1-canary.2
|
|
2
|
+
export default {
|
|
3
|
+
scripts: {
|
|
4
|
+
start: "dash src/sdks/bun/index.ts",
|
|
5
|
+
"check-zig-version": "vendors/zig/zig version",
|
|
6
|
+
"check-rust-version": "vendors/rust/bin/rustc --version",
|
|
7
|
+
"build:dev": "dash build.ts",
|
|
8
|
+
"build:local": "scripts/build-local.js",
|
|
9
|
+
"build:release": "dash build.ts --release",
|
|
10
|
+
dev: "scripts/dev.ts",
|
|
11
|
+
"dev:test": "scripts/dev.test.ts",
|
|
12
|
+
"dev:clean": "cd ../kitchen && rm -rf node_modules vendors/cef && cd ../package && dash dev",
|
|
13
|
+
"dev:canary": "dash install && dash build:release && cd ../kitchen && dash install && dash electrobun build --env=canary",
|
|
14
|
+
"dev:production": "dash install && dash build:release && cd ../kitchen && dash install && dash electrobun build --env=production",
|
|
15
|
+
"build:docs:release": "cd ../docs && dash run build",
|
|
16
|
+
"npm:publish": "dash build:release && npm publish",
|
|
17
|
+
"npm:publish:beta": "dash build:release && npm publish --tag beta",
|
|
18
|
+
typecheck: "./node_modules/.bin/tsc --noEmit && cd ../kitchen && ./node_modules/.bin/tsc --noEmit",
|
|
19
|
+
"push:beta": "dash typecheck && node scripts/push-version.js beta",
|
|
20
|
+
"push:patch": "dash typecheck && node scripts/push-version.js patch",
|
|
21
|
+
"push:minor": "dash typecheck && node scripts/push-version.js minor",
|
|
22
|
+
"push:major": "dash typecheck && node scripts/push-version.js major",
|
|
23
|
+
"push:stable": "dash typecheck && node scripts/push-version.js stable",
|
|
24
|
+
"build:push:artifacts": "node scripts/build-and-upload-artifacts.js",
|
|
25
|
+
"test:unit": "dash test src/shared src/sdks/bun src/config",
|
|
26
|
+
"bump-cef": "dash scripts/update-cef-version.ts",
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -7,6 +7,8 @@ interface ElectrobunEncryptResult {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
interface ElectrobunBridge {
|
|
10
|
+
receiveMessageFromHost: (msg: unknown) => void;
|
|
11
|
+
receiveInternalMessageFromHost: (msg: unknown) => void;
|
|
10
12
|
receiveMessageFromBun: (msg: unknown) => void;
|
|
11
13
|
receiveInternalMessageFromBun: (msg: unknown) => void;
|
|
12
14
|
}
|
|
@@ -20,10 +22,14 @@ declare global {
|
|
|
20
22
|
__electrobunWebviewId: number;
|
|
21
23
|
__electrobunWindowId: number;
|
|
22
24
|
__electrobunRpcSocketPort: number;
|
|
25
|
+
__electrobunHostSocketPort?: number;
|
|
26
|
+
__electrobunPlaintextHostSocket?: boolean;
|
|
23
27
|
__electrobun?: ElectrobunBridge;
|
|
28
|
+
__electrobunPendingHostMessages?: unknown[];
|
|
24
29
|
__electrobun_encrypt: (msg: string) => Promise<ElectrobunEncryptResult>;
|
|
25
30
|
__electrobun_decrypt: (encryptedData: string, iv: string, tag: string) => Promise<string>;
|
|
26
31
|
__electrobunInternalBridge?: MessageHandler;
|
|
32
|
+
__electrobunHostBridge?: MessageHandler;
|
|
27
33
|
__electrobunBunBridge?: MessageHandler;
|
|
28
34
|
}
|
|
29
35
|
}
|
|
@@ -15,13 +15,27 @@ import { type WgpuTagElement, type WgpuEventTypes } from "./wgputag";
|
|
|
15
15
|
import "./global.d.ts";
|
|
16
16
|
|
|
17
17
|
const WEBVIEW_ID = window.__electrobunWebviewId;
|
|
18
|
-
const
|
|
18
|
+
const HOST_SOCKET_PORT =
|
|
19
|
+
window.__electrobunHostSocketPort ?? window.__electrobunRpcSocketPort;
|
|
19
20
|
|
|
20
21
|
class Electroview<T extends RPCWithTransport> {
|
|
21
|
-
|
|
22
|
+
hostSocket?: WebSocket;
|
|
23
|
+
hostSocketCanSend = false;
|
|
24
|
+
pendingHostSocketMessages: string[] = [];
|
|
25
|
+
flushingHostSocketMessages = false;
|
|
26
|
+
hostSocketSendQueue: string[] = [];
|
|
27
|
+
flushingHostSocketSendQueue = false;
|
|
22
28
|
// user's custom rpc browser <-> bun
|
|
23
29
|
rpc?: T;
|
|
24
30
|
rpcHandler?: (msg: unknown) => void;
|
|
31
|
+
carrots = {
|
|
32
|
+
invoke: <R = unknown>(
|
|
33
|
+
carrotId: string,
|
|
34
|
+
method: string,
|
|
35
|
+
params?: unknown,
|
|
36
|
+
options?: { windowId?: string },
|
|
37
|
+
) => this.invokeCarrot<R>(carrotId, method, params, options),
|
|
38
|
+
};
|
|
25
39
|
|
|
26
40
|
constructor(config: { rpc: T }) {
|
|
27
41
|
this.rpc = config.rpc;
|
|
@@ -29,49 +43,65 @@ class Electroview<T extends RPCWithTransport> {
|
|
|
29
43
|
}
|
|
30
44
|
|
|
31
45
|
init() {
|
|
32
|
-
this.
|
|
46
|
+
this.initSocketToHost();
|
|
33
47
|
|
|
34
|
-
// Set up handler for user RPC messages from
|
|
35
|
-
|
|
36
|
-
window.__electrobun!.
|
|
37
|
-
|
|
48
|
+
// Set up handler for user RPC messages from the host runtime.
|
|
49
|
+
const hostMessageHandler = this.receiveMessageFromHost.bind(this);
|
|
50
|
+
window.__electrobun!.receiveMessageFromHost = hostMessageHandler;
|
|
51
|
+
window.__electrobun!.receiveMessageFromBun = hostMessageHandler;
|
|
38
52
|
|
|
39
53
|
if (this.rpc) {
|
|
40
54
|
this.rpc.setTransport(this.createTransport());
|
|
41
55
|
}
|
|
56
|
+
|
|
57
|
+
const pendingMessages = window.__electrobunPendingHostMessages;
|
|
58
|
+
if (pendingMessages?.length) {
|
|
59
|
+
window.__electrobunPendingHostMessages = [];
|
|
60
|
+
for (const message of pendingMessages) {
|
|
61
|
+
hostMessageHandler(message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
42
64
|
}
|
|
43
65
|
|
|
44
|
-
|
|
66
|
+
initSocketToHost() {
|
|
45
67
|
// Skip native socket when running in a remote browser (no port/webview ID)
|
|
46
|
-
if (!
|
|
68
|
+
if (!HOST_SOCKET_PORT || !WEBVIEW_ID) {
|
|
47
69
|
return;
|
|
48
70
|
}
|
|
49
71
|
|
|
50
|
-
// Note: Using ws:// for
|
|
72
|
+
// Note: Using ws:// for loopback is intentional - all RPC messages are
|
|
51
73
|
// encrypted with per-webview AES-GCM keys, making TLS redundant
|
|
52
74
|
const socket = new WebSocket(
|
|
53
|
-
`ws://
|
|
75
|
+
`ws://127.0.0.1:${HOST_SOCKET_PORT}/socket?webviewId=${WEBVIEW_ID}`,
|
|
54
76
|
);
|
|
55
77
|
|
|
56
|
-
this.
|
|
78
|
+
this.hostSocket = socket;
|
|
57
79
|
|
|
58
80
|
socket.addEventListener("open", () => {
|
|
59
|
-
|
|
81
|
+
this.hostSocketCanSend = true;
|
|
82
|
+
void this.flushPendingHostSocketMessages();
|
|
60
83
|
});
|
|
61
84
|
|
|
62
85
|
socket.addEventListener("message", async (event) => {
|
|
63
86
|
const message = event.data;
|
|
64
87
|
if (typeof message === "string") {
|
|
65
88
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
const packet = JSON.parse(message);
|
|
90
|
+
this.hostSocketCanSend = true;
|
|
91
|
+
if (
|
|
92
|
+
typeof packet?.encryptedData === "string" &&
|
|
93
|
+
typeof packet?.iv === "string" &&
|
|
94
|
+
typeof packet?.tag === "string"
|
|
95
|
+
) {
|
|
96
|
+
const decrypted = await window.__electrobun_decrypt(
|
|
97
|
+
packet.encryptedData,
|
|
98
|
+
packet.iv,
|
|
99
|
+
packet.tag,
|
|
100
|
+
);
|
|
101
|
+
this.rpcHandler?.(JSON.parse(decrypted));
|
|
102
|
+
} else {
|
|
103
|
+
this.rpcHandler?.(packet);
|
|
104
|
+
}
|
|
75
105
|
} catch (err) {
|
|
76
106
|
console.error("Error parsing bun message:", err);
|
|
77
107
|
}
|
|
@@ -83,10 +113,13 @@ class Electroview<T extends RPCWithTransport> {
|
|
|
83
113
|
});
|
|
84
114
|
|
|
85
115
|
socket.addEventListener("error", (event) => {
|
|
116
|
+
this.hostSocketCanSend = false;
|
|
86
117
|
console.error("Socket error:", event);
|
|
87
118
|
});
|
|
88
119
|
|
|
89
120
|
socket.addEventListener("close", (_event) => {
|
|
121
|
+
this.hostSocketCanSend = false;
|
|
122
|
+
this.pendingHostSocketMessages = [];
|
|
90
123
|
// console.log("Socket closed:", event);
|
|
91
124
|
});
|
|
92
125
|
}
|
|
@@ -97,9 +130,9 @@ class Electroview<T extends RPCWithTransport> {
|
|
|
97
130
|
send(message: unknown) {
|
|
98
131
|
try {
|
|
99
132
|
const messageString = JSON.stringify(message);
|
|
100
|
-
that.
|
|
133
|
+
that.sendMessageToHost(messageString);
|
|
101
134
|
} catch (error) {
|
|
102
|
-
console.error("
|
|
135
|
+
console.error("host: failed to serialize message to webview", error);
|
|
103
136
|
}
|
|
104
137
|
},
|
|
105
138
|
registerHandler(handler: (msg: unknown) => void) {
|
|
@@ -108,36 +141,121 @@ class Electroview<T extends RPCWithTransport> {
|
|
|
108
141
|
};
|
|
109
142
|
}
|
|
110
143
|
|
|
111
|
-
async
|
|
112
|
-
if (this.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
};
|
|
122
|
-
const encryptedPacketString = JSON.stringify(encryptedPacket);
|
|
123
|
-
this.bunSocket.send(encryptedPacketString);
|
|
124
|
-
return;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error("Error sending message to bun via socket:", error);
|
|
127
|
-
}
|
|
144
|
+
async sendMessageToHost(msg: string) {
|
|
145
|
+
if (this.canSendToHostSocket()) {
|
|
146
|
+
this.hostSocketSendQueue.push(msg);
|
|
147
|
+
void this.flushHostSocketSendQueue();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.hostSocket?.readyState === WebSocket.CONNECTING) {
|
|
152
|
+
this.pendingHostSocketMessages.push(msg);
|
|
153
|
+
return;
|
|
128
154
|
}
|
|
129
155
|
|
|
130
156
|
// if socket's are unavailable, fallback to postMessage
|
|
131
|
-
window.
|
|
157
|
+
window.__electrobunHostBridge?.postMessage(msg);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
canSendToHostSocket() {
|
|
161
|
+
return (
|
|
162
|
+
this.hostSocketCanSend &&
|
|
163
|
+
this.hostSocket?.readyState === WebSocket.OPEN
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async sendMessageToHostSocket(msg: string) {
|
|
168
|
+
if (!this.canSendToHostSocket()) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
if (window.__electrobunPlaintextHostSocket) {
|
|
174
|
+
this.hostSocket!.send(msg);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
178
|
+
const { encryptedData, iv, tag } =
|
|
179
|
+
await window.__electrobun_encrypt(msg);
|
|
180
|
+
|
|
181
|
+
const encryptedPacket = {
|
|
182
|
+
encryptedData: encryptedData,
|
|
183
|
+
iv: iv,
|
|
184
|
+
tag: tag,
|
|
185
|
+
};
|
|
186
|
+
const encryptedPacketString = JSON.stringify(encryptedPacket);
|
|
187
|
+
this.hostSocket!.send(encryptedPacketString);
|
|
188
|
+
return true;
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error("Error sending message to host via socket:", error);
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async flushHostSocketSendQueue() {
|
|
196
|
+
if (this.flushingHostSocketSendQueue) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.flushingHostSocketSendQueue = true;
|
|
201
|
+
try {
|
|
202
|
+
while (
|
|
203
|
+
this.hostSocketSendQueue.length > 0 &&
|
|
204
|
+
this.canSendToHostSocket()
|
|
205
|
+
) {
|
|
206
|
+
const message = this.hostSocketSendQueue[0]!;
|
|
207
|
+
this.hostSocketSendQueue.shift();
|
|
208
|
+
if (!(await this.sendMessageToHostSocket(message))) {
|
|
209
|
+
window.__electrobunHostBridge?.postMessage(message);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} finally {
|
|
213
|
+
this.flushingHostSocketSendQueue = false;
|
|
214
|
+
}
|
|
132
215
|
}
|
|
133
216
|
|
|
134
|
-
|
|
135
|
-
|
|
217
|
+
async flushPendingHostSocketMessages() {
|
|
218
|
+
if (this.flushingHostSocketMessages) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.flushingHostSocketMessages = true;
|
|
223
|
+
try {
|
|
224
|
+
while (this.pendingHostSocketMessages.length > 0) {
|
|
225
|
+
const message = this.pendingHostSocketMessages.shift()!;
|
|
226
|
+
this.hostSocketSendQueue.push(message);
|
|
227
|
+
}
|
|
228
|
+
await this.flushHostSocketSendQueue();
|
|
229
|
+
} finally {
|
|
230
|
+
this.flushingHostSocketMessages = false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
receiveMessageFromHost(msg: unknown) {
|
|
235
|
+
// NOTE: in the webview messages are passed by executing window.__electrobun.receiveMessageFromHost(object)
|
|
136
236
|
// so they're already parsed into an object here
|
|
137
237
|
if (this.rpcHandler) {
|
|
138
238
|
this.rpcHandler(msg);
|
|
139
239
|
}
|
|
140
240
|
}
|
|
241
|
+
|
|
242
|
+
async invokeCarrot<R = unknown>(
|
|
243
|
+
carrotId: string,
|
|
244
|
+
method: string,
|
|
245
|
+
params?: unknown,
|
|
246
|
+
options?: { windowId?: string },
|
|
247
|
+
): Promise<R> {
|
|
248
|
+
const requestProxy = (this.rpc as any)?.request;
|
|
249
|
+
if (!requestProxy || typeof requestProxy.invokeCarrot !== "function") {
|
|
250
|
+
throw new Error("Renderer carrot invocation is not available in this Electrobun host.");
|
|
251
|
+
}
|
|
252
|
+
return requestProxy.invokeCarrot({
|
|
253
|
+
carrotId,
|
|
254
|
+
method,
|
|
255
|
+
params,
|
|
256
|
+
windowId: options?.windowId,
|
|
257
|
+
}) as Promise<R>;
|
|
258
|
+
}
|
|
141
259
|
static defineRPC<Schema extends ElectrobunRPCSchema>(
|
|
142
260
|
config: ElectrobunRPCConfig<Schema, "webview">,
|
|
143
261
|
) {
|
|
@@ -3,15 +3,38 @@
|
|
|
3
3
|
* Used in electrobun.config.ts files
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
type BundlerOptions = {
|
|
7
|
+
external?: string[];
|
|
8
|
+
define?: Record<string, string>;
|
|
9
|
+
loader?: Record<string, string>;
|
|
10
|
+
minify?: boolean;
|
|
11
|
+
sourcemap?: boolean | "inline" | "external" | "linked";
|
|
12
|
+
splitting?: boolean;
|
|
13
|
+
plugins?: unknown[];
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type CarrotFileActivatorConfig = {
|
|
18
|
+
baseName?: string;
|
|
19
|
+
nodeType?: "file" | "dir" | "any";
|
|
20
|
+
slate: {
|
|
21
|
+
type: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
icon?: string;
|
|
24
|
+
config?: Record<string, unknown>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type CarrotContributionsConfig = {
|
|
29
|
+
fileActivators?: CarrotFileActivatorConfig[];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type CarrotUIDefinition = {
|
|
33
|
+
name?: string;
|
|
34
|
+
entrypoint?: string;
|
|
35
|
+
path?: string;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
};
|
|
15
38
|
|
|
16
39
|
export interface ElectrobunConfig {
|
|
17
40
|
/**
|
|
@@ -113,22 +136,85 @@ export interface ElectrobunConfig {
|
|
|
113
136
|
*/
|
|
114
137
|
build?: {
|
|
115
138
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
139
|
+
* Main process implementation to build and package.
|
|
140
|
+
* - "cottontail": bundle and run the Cottontail main process entrypoint
|
|
141
|
+
* - "zig": compile and run the Zig main process entrypoint
|
|
142
|
+
* - "rust": compile and run the Rust main process entrypoint
|
|
143
|
+
* - "go": compile and run the Go main process entrypoint
|
|
144
|
+
* - "odin": compile and run the Odin main process entrypoint
|
|
145
|
+
* @default "cottontail"
|
|
146
|
+
*/
|
|
147
|
+
mainProcess?: "cottontail" | "zig" | "rust" | "go" | "odin";
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Cottontail main process build configuration.
|
|
151
|
+
* Used when `build.mainProcess` is set to `"cottontail"`.
|
|
119
152
|
*/
|
|
120
|
-
|
|
153
|
+
cottontail?: {
|
|
121
154
|
/**
|
|
122
|
-
* Entry point for the main
|
|
155
|
+
* Entry point for the main Cottontail process
|
|
123
156
|
* @default "src/bun/index.ts"
|
|
124
157
|
*/
|
|
125
158
|
entrypoint?: string;
|
|
126
|
-
} &
|
|
159
|
+
} & BundlerOptions;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Zig main process build configuration.
|
|
163
|
+
* Used when `build.mainProcess` is set to `"zig"`.
|
|
164
|
+
*/
|
|
165
|
+
zig?: {
|
|
166
|
+
/**
|
|
167
|
+
* Entry point for the main Zig process
|
|
168
|
+
* @default "src/zig/main.zig"
|
|
169
|
+
*/
|
|
170
|
+
entrypoint?: string;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Rust main process build configuration.
|
|
175
|
+
* Used when `build.mainProcess` is set to `"rust"`.
|
|
176
|
+
*/
|
|
177
|
+
rust?: {
|
|
178
|
+
/**
|
|
179
|
+
* Entry point for the main Rust process
|
|
180
|
+
* @default "src/rust/main.rs"
|
|
181
|
+
*/
|
|
182
|
+
entrypoint?: string;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Go main process build configuration.
|
|
187
|
+
* Used when `build.mainProcess` is set to `"go"`.
|
|
188
|
+
*/
|
|
189
|
+
go?: {
|
|
190
|
+
/**
|
|
191
|
+
* Entry point for the main Go process
|
|
192
|
+
* @default "src/go/main.go"
|
|
193
|
+
*/
|
|
194
|
+
entrypoint?: string;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Odin main process build configuration.
|
|
199
|
+
* Used when `build.mainProcess` is set to `"odin"`.
|
|
200
|
+
*
|
|
201
|
+
* Note: Odin support has platform caveats (x64-only on Windows, MSVC
|
|
202
|
+
* Build Tools required on Windows). See docs for details.
|
|
203
|
+
*/
|
|
204
|
+
odin?: {
|
|
205
|
+
/**
|
|
206
|
+
* Entry point for the main Odin process. May be a .odin file or a
|
|
207
|
+
* package directory; a file entrypoint compiles its containing
|
|
208
|
+
* directory as the root package.
|
|
209
|
+
* @default "src/odin/main.odin"
|
|
210
|
+
*/
|
|
211
|
+
entrypoint?: string;
|
|
212
|
+
};
|
|
127
213
|
|
|
128
214
|
/**
|
|
129
215
|
* Browser view build configurations.
|
|
130
|
-
* Each view accepts
|
|
131
|
-
* in addition to the entrypoint.
|
|
216
|
+
* Each view accepts bundler options (plugins, sourcemap, minify, define, etc.)
|
|
217
|
+
* in addition to the entrypoint.
|
|
132
218
|
*/
|
|
133
219
|
views?: {
|
|
134
220
|
[viewName: string]: {
|
|
@@ -136,7 +222,7 @@ export interface ElectrobunConfig {
|
|
|
136
222
|
* Entry point for this view's TypeScript code
|
|
137
223
|
*/
|
|
138
224
|
entrypoint: string;
|
|
139
|
-
} &
|
|
225
|
+
} & BundlerOptions;
|
|
140
226
|
};
|
|
141
227
|
|
|
142
228
|
/**
|
|
@@ -199,24 +285,6 @@ export interface ElectrobunConfig {
|
|
|
199
285
|
*/
|
|
200
286
|
wgpuVersion?: string;
|
|
201
287
|
|
|
202
|
-
/**
|
|
203
|
-
* Override the Bun runtime version.
|
|
204
|
-
* Format: semver string (e.g., "1.4.2")
|
|
205
|
-
*
|
|
206
|
-
* This downloads the specified Bun version from GitHub releases and uses it
|
|
207
|
-
* instead of the version bundled with this Electrobun release.
|
|
208
|
-
* @default Uses the version bundled with this Electrobun release
|
|
209
|
-
*/
|
|
210
|
-
bunVersion?: string;
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Locales to include in the ICU data file (Linux/Windows only).
|
|
214
|
-
* Set to '*' to include all locales, or specify a subset like ['en', 'de']
|
|
215
|
-
* to reduce app size. Has no effect on macOS (uses system ICU).
|
|
216
|
-
* @default '*'
|
|
217
|
-
*/
|
|
218
|
-
locales?: string[] | "*";
|
|
219
|
-
|
|
220
288
|
/**
|
|
221
289
|
* Additional file or directory paths to watch for changes during `electrobun dev --watch`.
|
|
222
290
|
* Paths are relative to the project root.
|
|
@@ -242,9 +310,10 @@ export interface ElectrobunConfig {
|
|
|
242
310
|
description?: string;
|
|
243
311
|
mode?: "window" | "background";
|
|
244
312
|
carrotOnly?: boolean;
|
|
245
|
-
permissions?: Record<string, unknown>;
|
|
246
313
|
dependencies?: Record<string, string>;
|
|
247
|
-
remoteUIs?: Record<string,
|
|
314
|
+
remoteUIs?: Record<string, CarrotUIDefinition>;
|
|
315
|
+
slateUIs?: Record<string, CarrotUIDefinition>;
|
|
316
|
+
contributions?: CarrotContributionsConfig;
|
|
248
317
|
};
|
|
249
318
|
|
|
250
319
|
/**
|
|
@@ -425,7 +494,7 @@ export interface ElectrobunConfig {
|
|
|
425
494
|
|
|
426
495
|
/**
|
|
427
496
|
* Runtime behaviour configuration.
|
|
428
|
-
* These values are copied into build.json and available to the
|
|
497
|
+
* These values are copied into build.json and available to the main process at runtime.
|
|
429
498
|
* You can add arbitrary keys here and access them via BuildConfig.
|
|
430
499
|
*/
|
|
431
500
|
runtime?: {
|
|
@@ -478,7 +547,7 @@ export interface ElectrobunConfig {
|
|
|
478
547
|
baseUrl?: string;
|
|
479
548
|
/**
|
|
480
549
|
* Generate delta patch files by diffing against the previous release.
|
|
481
|
-
* Disable to skip patch generation for local canary/
|
|
550
|
+
* Disable to skip patch generation for local canary/production testing.
|
|
482
551
|
* @default true
|
|
483
552
|
*/
|
|
484
553
|
generatePatch?: boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { assertNoLegacyBunMainProcessConfig } from "./validate";
|
|
3
|
+
|
|
4
|
+
describe("assertNoLegacyBunMainProcessConfig", () => {
|
|
5
|
+
it("accepts Cottontail and native main-process configurations", () => {
|
|
6
|
+
expect(() =>
|
|
7
|
+
assertNoLegacyBunMainProcessConfig({
|
|
8
|
+
build: {
|
|
9
|
+
mainProcess: "cottontail",
|
|
10
|
+
cottontail: { entrypoint: "src/bun/index.ts" },
|
|
11
|
+
},
|
|
12
|
+
}),
|
|
13
|
+
).not.toThrow();
|
|
14
|
+
expect(() =>
|
|
15
|
+
assertNoLegacyBunMainProcessConfig({
|
|
16
|
+
build: { mainProcess: "zig" },
|
|
17
|
+
}),
|
|
18
|
+
).not.toThrow();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it.each([
|
|
22
|
+
{ mainProcess: "bun" },
|
|
23
|
+
{ bun: { entrypoint: "src/bun/index.ts" } },
|
|
24
|
+
{ bunVersion: "1.3.8" },
|
|
25
|
+
{ bunnyBun: "bunny-bun-test" },
|
|
26
|
+
])("rejects removed Bun configuration: %p", (build) => {
|
|
27
|
+
expect(() => assertNoLegacyBunMainProcessConfig({ build })).toThrow(
|
|
28
|
+
'Use build.mainProcess = "cottontail"',
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
});
|