bunite-core 0.0.4 → 0.0.5
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/package.json +1 -1
- package/src/bun/core/App.ts +86 -97
- package/src/bun/core/BrowserView.ts +6 -2
- package/src/bun/index.ts +2 -2
- package/src/shared/webRpcHandler.ts +45 -0
- package/src/shared/webviewPolyfill.ts +1 -8
- package/src/view/index.ts +85 -54
package/package.json
CHANGED
package/src/bun/core/App.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { log, logLevelToInt } from "../../shared/log";
|
|
|
22
22
|
|
|
23
23
|
import type { LogLevel } from "../../shared/log";
|
|
24
24
|
|
|
25
|
-
type
|
|
25
|
+
type AppOptions = NativeBootstrapOptions & {
|
|
26
26
|
userDataDir?: string;
|
|
27
27
|
cefDir?: string;
|
|
28
28
|
exitOnLastWindowClosed?: boolean;
|
|
@@ -31,123 +31,116 @@ type AppInitOptions = NativeBootstrapOptions & {
|
|
|
31
31
|
|
|
32
32
|
export type GlobalIPCHandler = (params: unknown, ctx: { viewId: number }) => unknown | Promise<unknown>;
|
|
33
33
|
|
|
34
|
-
class AppRuntime {
|
|
35
|
-
private initPromise: Promise<void> | null = null;
|
|
34
|
+
export class AppRuntime {
|
|
36
35
|
private stubKeepAliveTimer: ReturnType<typeof setInterval> | null = null;
|
|
37
36
|
private readonly globalIPCHandlers = new Map<string, GlobalIPCHandler>();
|
|
38
37
|
private exitOnLastWindowClosed = true;
|
|
39
38
|
private quitting = false;
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
if (!this.initPromise) {
|
|
43
|
-
this.initPromise = (async () => {
|
|
44
|
-
if (options.exitOnLastWindowClosed !== undefined) {
|
|
45
|
-
this.exitOnLastWindowClosed = options.exitOnLastWindowClosed;
|
|
46
|
-
}
|
|
40
|
+
readonly ready: Promise<void>;
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
constructor(options: AppOptions = {}) {
|
|
43
|
+
this.ready = this.bootstrap(options);
|
|
44
|
+
}
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
private async bootstrap(options: AppOptions) {
|
|
47
|
+
if (options.exitOnLastWindowClosed !== undefined) {
|
|
48
|
+
this.exitOnLastWindowClosed = options.exitOnLastWindowClosed;
|
|
49
|
+
}
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// XDG_DATA_HOME takes priority on any platform, then OS convention
|
|
60
|
-
const appDataDir = process.env.XDG_DATA_HOME
|
|
61
|
-
?? (process.platform === "win32"
|
|
62
|
-
? (process.env.APPDATA ?? join(process.env.USERPROFILE ?? "", "AppData", "Roaming"))
|
|
63
|
-
: process.platform === "darwin"
|
|
64
|
-
? join(process.env.HOME ?? "", "Library", "Application Support")
|
|
65
|
-
: join(process.env.HOME ?? "", ".local", "share"));
|
|
66
|
-
let name = "bunite-app";
|
|
67
|
-
try {
|
|
68
|
-
// Walk up from entry script to find nearest package.json
|
|
69
|
-
let dir = getBaseDir();
|
|
70
|
-
while (dir) {
|
|
71
|
-
const pkgPath = join(dir, "package.json");
|
|
72
|
-
if (existsSync(pkgPath)) {
|
|
73
|
-
name = JSON.parse(require("node:fs").readFileSync(pkgPath, "utf8")).name ?? name;
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
const parent = resolve(dir, "..");
|
|
77
|
-
if (parent === dir) break;
|
|
78
|
-
dir = parent;
|
|
79
|
-
}
|
|
80
|
-
} catch {}
|
|
81
|
-
process.env.BUNITE_USER_DATA_DIR = join(appDataDir, name);
|
|
82
|
-
}
|
|
51
|
+
if (options.logLevel) {
|
|
52
|
+
log.setLevel(options.logLevel);
|
|
53
|
+
}
|
|
83
54
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
popupBlocking: options.popupBlocking,
|
|
88
|
-
chromiumFlags: options.chromiumFlags
|
|
89
|
-
});
|
|
55
|
+
if (options.cefDir) {
|
|
56
|
+
process.env.BUNITE_CEF_DIR = options.cefDir;
|
|
57
|
+
}
|
|
90
58
|
|
|
91
|
-
|
|
92
|
-
|
|
59
|
+
if (options.userDataDir) {
|
|
60
|
+
process.env.BUNITE_USER_DATA_DIR = options.userDataDir;
|
|
61
|
+
} else if (!process.env.BUNITE_USER_DATA_DIR) {
|
|
62
|
+
const appDataDir = process.env.XDG_DATA_HOME
|
|
63
|
+
?? (process.platform === "win32"
|
|
64
|
+
? (process.env.APPDATA ?? join(process.env.USERPROFILE ?? "", "AppData", "Roaming"))
|
|
65
|
+
: process.platform === "darwin"
|
|
66
|
+
? join(process.env.HOME ?? "", "Library", "Application Support")
|
|
67
|
+
: join(process.env.HOME ?? "", ".local", "share"));
|
|
68
|
+
let name = "bunite-app";
|
|
69
|
+
try {
|
|
70
|
+
let dir = getBaseDir();
|
|
71
|
+
while (dir) {
|
|
72
|
+
const pkgPath = join(dir, "package.json");
|
|
73
|
+
if (existsSync(pkgPath)) {
|
|
74
|
+
name = JSON.parse(require("node:fs").readFileSync(pkgPath, "utf8")).name ?? name;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
const parent = resolve(dir, "..");
|
|
78
|
+
if (parent === dir) break;
|
|
79
|
+
dir = parent;
|
|
93
80
|
}
|
|
81
|
+
} catch {}
|
|
82
|
+
process.env.BUNITE_USER_DATA_DIR = join(appDataDir, name);
|
|
83
|
+
}
|
|
94
84
|
|
|
95
|
-
|
|
85
|
+
const runtime = await initNativeRuntime({
|
|
86
|
+
allowStub: options.allowStub,
|
|
87
|
+
hideConsole: options.hideConsole,
|
|
88
|
+
popupBlocking: options.popupBlocking,
|
|
89
|
+
chromiumFlags: options.chromiumFlags
|
|
90
|
+
});
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
for (const [channel, handler] of getWebviewIPCHandlers()) {
|
|
101
|
-
this.globalIPCHandlers.set(channel, handler);
|
|
102
|
-
}
|
|
92
|
+
if (options.logLevel && runtime.nativeLoaded) {
|
|
93
|
+
setNativeLogLevel(logLevelToInt(options.logLevel));
|
|
94
|
+
}
|
|
103
95
|
|
|
104
|
-
|
|
105
|
-
const { requestId, response } = params as { requestId: number; response: number };
|
|
106
|
-
handleMessageBoxResponse(requestId, response);
|
|
107
|
-
return {};
|
|
108
|
-
});
|
|
96
|
+
attachGlobalIPCResolver((channel) => this.getGlobalIPCHandler(channel));
|
|
109
97
|
|
|
110
|
-
|
|
98
|
+
for (const [channel, handler] of getSurfaceIPCHandlers()) {
|
|
99
|
+
this.globalIPCHandlers.set(channel, handler);
|
|
100
|
+
}
|
|
101
|
+
for (const [channel, handler] of getWebviewIPCHandlers()) {
|
|
102
|
+
this.globalIPCHandlers.set(channel, handler);
|
|
103
|
+
}
|
|
111
104
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
this.globalIPCHandlers.set("__bunite:messageBoxResponse", (params) => {
|
|
106
|
+
const { requestId, response } = params as { requestId: number; response: number };
|
|
107
|
+
handleMessageBoxResponse(requestId, response);
|
|
108
|
+
return {};
|
|
109
|
+
});
|
|
116
110
|
|
|
111
|
+
setRouteRequestHandler((requestId, path) => this.handleRouteRequest(requestId, path));
|
|
117
112
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
queueMicrotask(() => {
|
|
124
|
-
if (this.quitting) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
// Recheck: a new window may have been created since the event
|
|
128
|
-
if (BrowserWindow.getAll().length === 0) {
|
|
129
|
-
this.quit();
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
}
|
|
113
|
+
for (const path of this.appresHandlers.keys()) {
|
|
114
|
+
getNativeLibrary()?.symbols.bunite_register_appres_route(toCString(path));
|
|
115
|
+
}
|
|
134
116
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
117
|
+
if (this.exitOnLastWindowClosed && runtime.nativeLoaded) {
|
|
118
|
+
buniteEventEmitter.on("all-windows-closed", () => {
|
|
119
|
+
if (this.quitting) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
queueMicrotask(() => {
|
|
123
|
+
if (this.quitting) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (BrowserWindow.getAll().length === 0) {
|
|
127
|
+
this.quit();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
143
131
|
}
|
|
144
132
|
|
|
145
|
-
|
|
133
|
+
ensureRPCServer();
|
|
134
|
+
buniteEventEmitter.emitEvent(
|
|
135
|
+
new BuniteEvent("ready", {
|
|
136
|
+
usingStub: runtime.usingStub,
|
|
137
|
+
artifacts: runtime.artifacts
|
|
138
|
+
})
|
|
139
|
+
);
|
|
146
140
|
}
|
|
147
141
|
|
|
148
142
|
on(name: string, handler: (payload: unknown) => void) {
|
|
149
143
|
if (name === "before-quit") {
|
|
150
|
-
// before-quit listeners receive the BuniteEvent directly so they can set event.response
|
|
151
144
|
buniteEventEmitter.on(name, handler);
|
|
152
145
|
return () => buniteEventEmitter.off(name, handler);
|
|
153
146
|
}
|
|
@@ -188,7 +181,6 @@ class AppRuntime {
|
|
|
188
181
|
clearInterval(this.stubKeepAliveTimer);
|
|
189
182
|
this.stubKeepAliveTimer = null;
|
|
190
183
|
}
|
|
191
|
-
// bunite_quit() blocks until native shutdown completes or times out
|
|
192
184
|
getNativeLibrary()?.symbols.bunite_quit();
|
|
193
185
|
process.exitCode = code;
|
|
194
186
|
process.exit(code);
|
|
@@ -238,7 +230,6 @@ class AppRuntime {
|
|
|
238
230
|
getNativeLibrary()?.symbols.bunite_complete_route_request(requestId, toCString(html));
|
|
239
231
|
}
|
|
240
232
|
|
|
241
|
-
/** Resolve a path relative to the entry script (dev) or executable (compiled). */
|
|
242
233
|
resolve(relativePath: string): string {
|
|
243
234
|
if (isAbsolute(relativePath)) return relativePath;
|
|
244
235
|
return resolve(getBaseDir(), relativePath);
|
|
@@ -278,5 +269,3 @@ class AppRuntime {
|
|
|
278
269
|
return this.cachedCefVersion;
|
|
279
270
|
}
|
|
280
271
|
}
|
|
281
|
-
|
|
282
|
-
export const app = new AppRuntime();
|
|
@@ -2,7 +2,8 @@ import { ptr } from "bun:ffi";
|
|
|
2
2
|
import { buildViewPreloadScript } from "../preload/inline";
|
|
3
3
|
import { log } from "../../shared/log";
|
|
4
4
|
import { buniteEventEmitter } from "../events/eventEmitter";
|
|
5
|
-
import { defineBuniteRPC, type BuniteRPCConfig, type BuniteRPCSchema, type RPCWithTransport } from "../../shared/rpc";
|
|
5
|
+
import { defineBuniteRPC, type BuniteRPCConfig, type BuniteRPCSchema, type RPCWithTransport, type RPCRequestHandler } from "../../shared/rpc";
|
|
6
|
+
import { createWebRPCHandler } from "../../shared/webRpcHandler";
|
|
6
7
|
import { ensureNativeRuntime, getNativeLibrary, toCString, waitForViewReady, cancelWaitForViewReady } from "../proc/native";
|
|
7
8
|
import { attachBrowserViewRegistry, getRPCPort, sendMessageToView } from "./Socket";
|
|
8
9
|
import { randomBytes } from "node:crypto";
|
|
@@ -163,7 +164,10 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
163
164
|
static defineRPC<Schema extends BuniteRPCSchema>(
|
|
164
165
|
config: BuniteRPCConfig<Schema, "bun">
|
|
165
166
|
) {
|
|
166
|
-
|
|
167
|
+
const rpc = defineBuniteRPC("bun", config);
|
|
168
|
+
return Object.assign(rpc, {
|
|
169
|
+
webHandler: createWebRPCHandler((config.handlers.requests ?? {}) as any)
|
|
170
|
+
});
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
handleIncomingRPC(message: unknown) {
|
package/src/bun/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AppRuntime } from "./core/App";
|
|
2
2
|
import { BrowserWindow, type WindowOptionsType } from "./core/BrowserWindow";
|
|
3
3
|
import { BrowserView, type BrowserViewOptions } from "./core/BrowserView";
|
|
4
4
|
import * as Utils from "./core/Utils";
|
|
@@ -17,7 +17,7 @@ import type { MessageBoxOptions, MessageBoxResponse } from "./core/Utils";
|
|
|
17
17
|
import { log, type LogLevel } from "../shared/log";
|
|
18
18
|
|
|
19
19
|
export {
|
|
20
|
-
|
|
20
|
+
AppRuntime,
|
|
21
21
|
BrowserWindow,
|
|
22
22
|
BrowserView,
|
|
23
23
|
Utils,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { decodeRPCPacket, encodeRPCPacket, asUint8Array } from "./rpcWire";
|
|
2
|
+
|
|
3
|
+
type WebRPCHandlers = Record<string, (params?: unknown) => unknown>;
|
|
4
|
+
|
|
5
|
+
function errorMessage(error: unknown): string {
|
|
6
|
+
return error instanceof Error ? error.message : String(error);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createWebRPCHandler(handlers: WebRPCHandlers) {
|
|
10
|
+
return {
|
|
11
|
+
message(ws: { send(data: Uint8Array): void }, raw: string | Buffer) {
|
|
12
|
+
if (typeof raw === "string") return;
|
|
13
|
+
|
|
14
|
+
let packet;
|
|
15
|
+
try {
|
|
16
|
+
packet = decodeRPCPacket(asUint8Array(raw));
|
|
17
|
+
} catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (packet.type !== "request" || typeof packet.method !== "string") return;
|
|
22
|
+
|
|
23
|
+
const handler = handlers[packet.method];
|
|
24
|
+
if (!handler) {
|
|
25
|
+
ws.send(
|
|
26
|
+
encodeRPCPacket({ type: "response", id: packet.id, success: false, error: `Unknown method: ${packet.method}` })
|
|
27
|
+
);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
Promise.resolve(handler(packet.params)).then(
|
|
33
|
+
(payload) =>
|
|
34
|
+
ws.send(encodeRPCPacket({ type: "response", id: packet.id, success: true, payload })),
|
|
35
|
+
(error) =>
|
|
36
|
+
ws.send(encodeRPCPacket({ type: "response", id: packet.id, success: false, error: errorMessage(error) }))
|
|
37
|
+
);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
ws.send(
|
|
40
|
+
encodeRPCPacket({ type: "response", id: packet.id, success: false, error: errorMessage(error) })
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* <bunite-webview> iframe polyfill — registered automatically via the
|
|
3
|
-
* bunite-dev vite plugin when the native bunite runtime is not available.
|
|
4
|
-
*
|
|
5
|
-
* In desktop CEF the native element is already registered by the preload
|
|
6
|
-
* runtime, so this module does nothing.
|
|
7
|
-
*/
|
|
8
|
-
|
|
1
|
+
// Iframe-based fallback for web browsers. No-op when the native element is already registered by the CEF preload.
|
|
9
2
|
if (
|
|
10
3
|
typeof customElements !== "undefined" &&
|
|
11
4
|
!customElements.get("bunite-webview")
|
package/src/view/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "../shared/webviewPolyfill";
|
|
1
2
|
import {
|
|
2
3
|
defineBuniteRPC,
|
|
3
4
|
type BuniteRPCConfig,
|
|
@@ -25,6 +26,8 @@ const buniteWindow = window as BuniteWindowGlobals;
|
|
|
25
26
|
const WEBVIEW_ID = buniteWindow.__buniteWebviewId;
|
|
26
27
|
const RPC_SOCKET_PORT = buniteWindow.__buniteRpcSocketPort;
|
|
27
28
|
|
|
29
|
+
const isNative = WEBVIEW_ID != null && RPC_SOCKET_PORT != null;
|
|
30
|
+
|
|
28
31
|
function toArrayBuffer(bytes: Uint8Array): ArrayBuffer {
|
|
29
32
|
const copy = new Uint8Array(bytes.byteLength);
|
|
30
33
|
copy.set(bytes);
|
|
@@ -35,6 +38,7 @@ export class BuniteView<T extends RPCWithTransport> {
|
|
|
35
38
|
bunSocket?: WebSocket;
|
|
36
39
|
rpc?: T;
|
|
37
40
|
rpcHandler?: (message: unknown) => void;
|
|
41
|
+
private pendingPackets: RPCPacket[] = [];
|
|
38
42
|
|
|
39
43
|
constructor(config: { rpc: T }) {
|
|
40
44
|
this.rpc = config.rpc;
|
|
@@ -43,69 +47,96 @@ export class BuniteView<T extends RPCWithTransport> {
|
|
|
43
47
|
|
|
44
48
|
init() {
|
|
45
49
|
this.initSocketToBun();
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
if (isNative) {
|
|
51
|
+
buniteWindow.__bunite ??= {};
|
|
52
|
+
buniteWindow.__bunite.receiveMessageFromBun = this.receiveMessageFromBun.bind(this);
|
|
53
|
+
}
|
|
48
54
|
this.rpc?.setTransport(this.createTransport());
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
private sendPacket(packet: RPCPacket) {
|
|
58
|
+
if (isNative) {
|
|
59
|
+
void this.bunBridge(packet).catch((error) => {
|
|
60
|
+
log.error("Failed to send RPC packet", error);
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
this.bunSocket!.send(toArrayBuffer(encodeRPCPacket(packet)));
|
|
55
64
|
}
|
|
65
|
+
}
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
globals.__bunite ??= {};
|
|
62
|
-
const existing = globals.__bunite._socket;
|
|
63
|
-
if (existing && existing.readyState <= WebSocket.OPEN) {
|
|
64
|
-
this.bunSocket = existing;
|
|
65
|
-
} else {
|
|
66
|
-
const socket = new WebSocket(
|
|
67
|
-
`ws://localhost:${RPC_SOCKET_PORT}/socket?webviewId=${WEBVIEW_ID}`
|
|
68
|
-
);
|
|
67
|
+
initSocketToBun() {
|
|
68
|
+
if (!isNative) {
|
|
69
|
+
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
|
70
|
+
const socket = new WebSocket(`${proto}//${location.host}/rpc`);
|
|
69
71
|
socket.binaryType = "arraybuffer";
|
|
70
72
|
this.bunSocket = socket;
|
|
71
|
-
globals.__bunite._socket = socket;
|
|
72
|
-
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
socket.addEventListener("message", async (event) => {
|
|
75
|
+
const bytes = await this.messageToUint8Array(event.data);
|
|
76
|
+
if (!bytes) return;
|
|
77
|
+
try {
|
|
78
|
+
this.rpcHandler?.(decodeRPCPacket(bytes));
|
|
79
|
+
} catch (error) {
|
|
80
|
+
log.error("Failed to parse WebSocket message", error);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
// Share a single WebSocket with the preload's bunite.invoke.
|
|
85
|
+
const globals = buniteWindow as any;
|
|
86
|
+
globals.__bunite ??= {};
|
|
87
|
+
const existing = globals.__bunite._socket;
|
|
88
|
+
if (existing && existing.readyState <= WebSocket.OPEN) {
|
|
89
|
+
this.bunSocket = existing;
|
|
90
|
+
} else {
|
|
91
|
+
const socket = new WebSocket(
|
|
92
|
+
`ws://localhost:${RPC_SOCKET_PORT}/socket?webviewId=${WEBVIEW_ID}`
|
|
93
|
+
);
|
|
94
|
+
socket.binaryType = "arraybuffer";
|
|
95
|
+
this.bunSocket = socket;
|
|
96
|
+
globals.__bunite._socket = socket;
|
|
78
97
|
}
|
|
79
98
|
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
if (!
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
this.bunSocket!.addEventListener("message", async (event) => {
|
|
100
|
+
const binaryMessage = await this.messageToUint8Array(event.data);
|
|
101
|
+
if (!binaryMessage) return;
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const decrypt = buniteWindow.__bunite_decrypt;
|
|
105
|
+
if (!decrypt) {
|
|
106
|
+
log.error("No decrypt function available in preload globals");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const decrypted = await decrypt(binaryMessage);
|
|
110
|
+
const packet = decodeRPCPacket(decrypted);
|
|
111
|
+
if ((packet as any).scope === "global") return;
|
|
112
|
+
this.rpcHandler?.(packet);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
log.error("Failed to parse message from Bun", error);
|
|
91
115
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.bunSocket!.addEventListener("open", () => {
|
|
120
|
+
for (const packet of this.pendingPackets) this.sendPacket(packet);
|
|
121
|
+
this.pendingPackets = [];
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
this.bunSocket!.addEventListener("error", () => {
|
|
125
|
+
log.error("RPC WebSocket error");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
this.bunSocket!.addEventListener("close", () => {
|
|
129
|
+
if (this.pendingPackets.length > 0) {
|
|
130
|
+
log.error(`RPC WebSocket closed with ${this.pendingPackets.length} pending packets`);
|
|
131
|
+
this.pendingPackets = [];
|
|
95
132
|
}
|
|
96
133
|
});
|
|
97
134
|
}
|
|
98
135
|
|
|
99
136
|
async messageToUint8Array(data: unknown) {
|
|
100
|
-
if (data instanceof ArrayBuffer)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (data instanceof Blob) {
|
|
104
|
-
return new Uint8Array(await data.arrayBuffer());
|
|
105
|
-
}
|
|
106
|
-
if (data instanceof Uint8Array) {
|
|
107
|
-
return data;
|
|
108
|
-
}
|
|
137
|
+
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
138
|
+
if (data instanceof Blob) return new Uint8Array(await data.arrayBuffer());
|
|
139
|
+
if (data instanceof Uint8Array) return data;
|
|
109
140
|
return null;
|
|
110
141
|
}
|
|
111
142
|
|
|
@@ -113,9 +144,9 @@ export class BuniteView<T extends RPCWithTransport> {
|
|
|
113
144
|
return {
|
|
114
145
|
send: (message) => {
|
|
115
146
|
if (this.bunSocket?.readyState === WebSocket.OPEN) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
147
|
+
this.sendPacket(message);
|
|
148
|
+
} else if (this.bunSocket?.readyState === WebSocket.CONNECTING) {
|
|
149
|
+
this.pendingPackets.push(message);
|
|
119
150
|
}
|
|
120
151
|
},
|
|
121
152
|
registerHandler: (handler: (packet: any) => void) => {
|
|
@@ -132,9 +163,7 @@ export class BuniteView<T extends RPCWithTransport> {
|
|
|
132
163
|
}
|
|
133
164
|
|
|
134
165
|
async bunBridge(message: RPCPacket) {
|
|
135
|
-
if (this.bunSocket?.readyState !== WebSocket.OPEN)
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
166
|
+
if (this.bunSocket?.readyState !== WebSocket.OPEN) return;
|
|
138
167
|
|
|
139
168
|
const encrypt = buniteWindow.__bunite_encrypt;
|
|
140
169
|
if (!encrypt) {
|
|
@@ -149,7 +178,9 @@ export class BuniteView<T extends RPCWithTransport> {
|
|
|
149
178
|
static defineRPC<Schema extends BuniteRPCSchema>(
|
|
150
179
|
config: BuniteRPCConfig<Schema, "webview">
|
|
151
180
|
) {
|
|
152
|
-
|
|
181
|
+
const rpc = defineBuniteRPC("webview", config);
|
|
182
|
+
new BuniteView({ rpc });
|
|
183
|
+
return rpc;
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
186
|
|