bunite-core 0.17.1 → 0.17.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/package.json +1 -1
- package/src/host/core/App.ts +28 -18
- package/src/host/core/BrowserView.ts +333 -116
- package/src/host/core/BrowserWindow.ts +44 -22
- package/src/host/core/Socket.ts +26 -9
- package/src/host/core/SurfaceBrowserIPC.ts +15 -5
- package/src/host/core/SurfaceManager.ts +345 -158
- package/src/host/core/SurfaceRegistry.ts +1 -1
- package/src/host/core/inputDispatch.ts +71 -42
- package/src/host/core/singleInstanceLock.ts +10 -2
- package/src/host/core/windowCap.ts +42 -23
- package/src/host/encryptedPipe.ts +20 -3
- package/src/host/events/appEvents.ts +1 -1
- package/src/host/events/eventEmitter.ts +6 -6
- package/src/host/events/webviewEvents.ts +11 -4
- package/src/host/events/windowEvents.ts +1 -2
- package/src/host/index.ts +9 -17
- package/src/host/log.ts +6 -4
- package/src/host/native.ts +271 -163
- package/src/host/paths.ts +18 -15
- package/src/host/platform.ts +2 -4
- package/src/host/preloadBundle.ts +1 -1
- package/src/host/serveWeb.ts +45 -30
- package/src/preload/runtime.built.js +1 -1
- package/src/preload/runtime.ts +66 -25
- package/src/rpc/encrypt.ts +23 -4
- package/src/rpc/error.ts +2 -7
- package/src/rpc/framework.ts +105 -35
- package/src/rpc/index.ts +130 -140
- package/src/rpc/peer.ts +442 -164
- package/src/rpc/renderer.ts +29 -11
- package/src/rpc/schema.ts +86 -61
- package/src/rpc/stream.ts +12 -3
- package/src/rpc/transport.ts +76 -12
- package/src/rpc/wire.ts +23 -10
- package/src/webview/native.ts +150 -73
- package/src/webview/polyfill.ts +184 -48
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import type { Connection } from "../../rpc/index";
|
|
3
3
|
import { buniteEventEmitter } from "../events/eventEmitter";
|
|
4
4
|
import { ensureNativeRuntime, getNativeLibrary, toCString } from "../native";
|
|
5
|
+
import { getBaseDir, resolveDefaultAppResRoot } from "../paths";
|
|
5
6
|
import { BrowserView } from "./BrowserView";
|
|
6
|
-
import type { Connection } from "../../rpc/index";
|
|
7
7
|
import { getNextWindowId } from "./windowIds";
|
|
8
|
-
import { getBaseDir, resolveDefaultAppResRoot } from "../paths";
|
|
9
8
|
|
|
10
9
|
export type WindowOptionsType = {
|
|
11
10
|
title: string;
|
|
@@ -38,7 +37,7 @@ const defaultOptions: WindowOptionsType = {
|
|
|
38
37
|
x: 80,
|
|
39
38
|
y: 80,
|
|
40
39
|
width: 1280,
|
|
41
|
-
height: 900
|
|
40
|
+
height: 900,
|
|
42
41
|
},
|
|
43
42
|
url: null,
|
|
44
43
|
html: null,
|
|
@@ -49,7 +48,7 @@ const defaultOptions: WindowOptionsType = {
|
|
|
49
48
|
transparent: false,
|
|
50
49
|
hidden: false,
|
|
51
50
|
navigationRules: null,
|
|
52
|
-
sandbox: false
|
|
51
|
+
sandbox: false,
|
|
53
52
|
};
|
|
54
53
|
|
|
55
54
|
const BrowserWindowMap: Record<number, BrowserWindow> = {};
|
|
@@ -80,12 +79,19 @@ export class BrowserWindow {
|
|
|
80
79
|
private closed = false;
|
|
81
80
|
private restoreMaximizedAfterMinimize = false;
|
|
82
81
|
private _focused = false;
|
|
83
|
-
private readonly handleNativeFocus = () => {
|
|
84
|
-
|
|
82
|
+
private readonly handleNativeFocus = () => {
|
|
83
|
+
lastFocusedWindowId = this.id;
|
|
84
|
+
this._focused = true;
|
|
85
|
+
};
|
|
86
|
+
private readonly handleNativeBlur = () => {
|
|
87
|
+
this._focused = false;
|
|
88
|
+
};
|
|
85
89
|
private readonly handleNativeMove = (event: unknown) => {
|
|
86
|
-
const data = (
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
const data = (
|
|
91
|
+
event as {
|
|
92
|
+
data?: { x?: number; y?: number; maximized?: boolean; minimized?: boolean };
|
|
93
|
+
}
|
|
94
|
+
).data;
|
|
89
95
|
if (!data) {
|
|
90
96
|
return;
|
|
91
97
|
}
|
|
@@ -95,13 +101,22 @@ export class BrowserWindow {
|
|
|
95
101
|
x: data.x ?? this.frame.x,
|
|
96
102
|
y: data.y ?? this.frame.y,
|
|
97
103
|
maximized: data.maximized ?? this.frame.maximized,
|
|
98
|
-
minimized: data.minimized ?? this.frame.minimized
|
|
104
|
+
minimized: data.minimized ?? this.frame.minimized,
|
|
99
105
|
};
|
|
100
106
|
};
|
|
101
107
|
private readonly handleNativeResize = (event: unknown) => {
|
|
102
|
-
const data = (
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
const data = (
|
|
109
|
+
event as {
|
|
110
|
+
data?: {
|
|
111
|
+
x?: number;
|
|
112
|
+
y?: number;
|
|
113
|
+
width?: number;
|
|
114
|
+
height?: number;
|
|
115
|
+
maximized?: boolean;
|
|
116
|
+
minimized?: boolean;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
).data;
|
|
105
120
|
if (!data) {
|
|
106
121
|
return;
|
|
107
122
|
}
|
|
@@ -113,7 +128,7 @@ export class BrowserWindow {
|
|
|
113
128
|
width: data.width ?? this.frame.width,
|
|
114
129
|
height: data.height ?? this.frame.height,
|
|
115
130
|
maximized: data.maximized ?? this.frame.maximized,
|
|
116
|
-
minimized: data.minimized ?? this.frame.minimized
|
|
131
|
+
minimized: data.minimized ?? this.frame.minimized,
|
|
117
132
|
};
|
|
118
133
|
};
|
|
119
134
|
private readonly handleNativeClose = () => {
|
|
@@ -190,12 +205,12 @@ export class BrowserWindow {
|
|
|
190
205
|
this.transparent,
|
|
191
206
|
this.hidden,
|
|
192
207
|
Boolean(this.frame.minimized),
|
|
193
|
-
Boolean(this.frame.maximized)
|
|
208
|
+
Boolean(this.frame.maximized),
|
|
194
209
|
) ?? false;
|
|
195
210
|
if (!this.nativeAttached) {
|
|
196
211
|
console.error(
|
|
197
212
|
`[bunite] bunite_window_create returned false for window ${this.id} — ` +
|
|
198
|
-
|
|
213
|
+
`window will be unusable. Check native log (BUNITE_LOG_LEVEL=info).`,
|
|
199
214
|
);
|
|
200
215
|
}
|
|
201
216
|
|
|
@@ -209,12 +224,12 @@ export class BrowserWindow {
|
|
|
209
224
|
x: 0,
|
|
210
225
|
y: 0,
|
|
211
226
|
width: this.frame.width,
|
|
212
|
-
height: this.frame.height
|
|
227
|
+
height: this.frame.height,
|
|
213
228
|
},
|
|
214
229
|
serve: options.serve,
|
|
215
230
|
windowId: this.id,
|
|
216
231
|
navigationRules: this.navigationRules,
|
|
217
|
-
sandbox: this.sandbox
|
|
232
|
+
sandbox: this.sandbox,
|
|
218
233
|
});
|
|
219
234
|
|
|
220
235
|
this.webviewId = webview.id;
|
|
@@ -290,7 +305,10 @@ export class BrowserWindow {
|
|
|
290
305
|
buniteEventEmitter.off(`close-${this.id}`, this.handleNativeClose);
|
|
291
306
|
buniteEventEmitter.removeAllListeners(`close-requested-${this.id}`);
|
|
292
307
|
if (!hadNative) {
|
|
293
|
-
buniteEventEmitter.emitEvent(
|
|
308
|
+
buniteEventEmitter.emitEvent(
|
|
309
|
+
buniteEventEmitter.events.window.close({ id: this.id }),
|
|
310
|
+
this.id,
|
|
311
|
+
);
|
|
294
312
|
}
|
|
295
313
|
}
|
|
296
314
|
|
|
@@ -388,7 +406,8 @@ export class BrowserWindow {
|
|
|
388
406
|
}
|
|
389
407
|
|
|
390
408
|
toggleMaximize() {
|
|
391
|
-
if (this.isMaximized()) this.unmaximize();
|
|
409
|
+
if (this.isMaximized()) this.unmaximize();
|
|
410
|
+
else this.maximize();
|
|
392
411
|
}
|
|
393
412
|
|
|
394
413
|
getState() {
|
|
@@ -420,7 +439,10 @@ export class BrowserWindow {
|
|
|
420
439
|
getNativeLibrary()?.symbols.bunite_window_begin_move_drag(this.id);
|
|
421
440
|
}
|
|
422
441
|
|
|
423
|
-
on(
|
|
442
|
+
on(
|
|
443
|
+
name: "close-requested" | "close" | "focus" | "blur" | "move" | "resize",
|
|
444
|
+
handler: (event: unknown) => void,
|
|
445
|
+
) {
|
|
424
446
|
const specificName = `${name}-${this.id}`;
|
|
425
447
|
buniteEventEmitter.on(specificName, handler);
|
|
426
448
|
return () => buniteEventEmitter.off(specificName, handler);
|
package/src/host/core/Socket.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Server, WebSocketHandler } from "bun";
|
|
2
|
-
import type { BrowserView } from "./BrowserView";
|
|
3
|
-
import { log } from "../log";
|
|
4
2
|
import { DEFAULT_MAX_BYTES } from "../../rpc/wire";
|
|
3
|
+
import { log } from "../log";
|
|
4
|
+
import type { BrowserView } from "./BrowserView";
|
|
5
5
|
|
|
6
6
|
type ViewRegistry = {
|
|
7
7
|
getById(id: number): BrowserView | undefined;
|
|
@@ -24,24 +24,36 @@ function asBytes(message: unknown): Uint8Array | null {
|
|
|
24
24
|
if (typeof message === "string") return null;
|
|
25
25
|
if (message instanceof Uint8Array) return message;
|
|
26
26
|
if (message instanceof ArrayBuffer) return new Uint8Array(message);
|
|
27
|
-
if (ArrayBuffer.isView(message))
|
|
27
|
+
if (ArrayBuffer.isView(message))
|
|
28
|
+
return new Uint8Array(message.buffer, message.byteOffset, message.byteLength);
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const websocket: WebSocketHandler<WebSocketData> = {
|
|
32
33
|
open(ws) {
|
|
33
34
|
const view = registry?.getById(ws.data.webviewId);
|
|
34
|
-
if (!view) {
|
|
35
|
+
if (!view) {
|
|
36
|
+
ws.close();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
35
39
|
let handler: ((bytes: Uint8Array) => void) | undefined;
|
|
36
40
|
const pending: Uint8Array[] = [];
|
|
37
41
|
const pipe = {
|
|
38
|
-
send: (bytes: Uint8Array) => {
|
|
42
|
+
send: (bytes: Uint8Array) => {
|
|
43
|
+
ws.send(bytes);
|
|
44
|
+
},
|
|
39
45
|
setReceive: (h: (bytes: Uint8Array) => void) => {
|
|
40
46
|
handler = h;
|
|
41
47
|
for (const b of pending) h(b);
|
|
42
48
|
pending.length = 0;
|
|
43
49
|
},
|
|
44
|
-
close: () => {
|
|
50
|
+
close: () => {
|
|
51
|
+
try {
|
|
52
|
+
ws.close();
|
|
53
|
+
} catch {
|
|
54
|
+
/* swallow */
|
|
55
|
+
}
|
|
56
|
+
},
|
|
45
57
|
deliver: (bytes: Uint8Array) => {
|
|
46
58
|
if (handler) handler(bytes);
|
|
47
59
|
else pending.push(bytes);
|
|
@@ -74,8 +86,10 @@ export function ensureRpcServer() {
|
|
|
74
86
|
const url = new URL(req.url);
|
|
75
87
|
if (url.pathname !== "/rpc") return new Response("Not found", { status: 404 });
|
|
76
88
|
const webviewId = Number(url.searchParams.get("webviewId"));
|
|
77
|
-
if (!Number.isFinite(webviewId))
|
|
78
|
-
|
|
89
|
+
if (!Number.isFinite(webviewId))
|
|
90
|
+
return new Response("Missing webviewId", { status: 400 });
|
|
91
|
+
if (!registry?.getById(webviewId))
|
|
92
|
+
return new Response("Unknown webviewId", { status: 403 });
|
|
79
93
|
const upgraded = server.upgrade(req, { data: { webviewId } });
|
|
80
94
|
return upgraded ? undefined : new Response("Upgrade failed", { status: 500 });
|
|
81
95
|
},
|
|
@@ -84,7 +98,10 @@ export function ensureRpcServer() {
|
|
|
84
98
|
rpcPort = port;
|
|
85
99
|
break;
|
|
86
100
|
} catch (error: any) {
|
|
87
|
-
if (error?.code === "EADDRINUSE") {
|
|
101
|
+
if (error?.code === "EADDRINUSE") {
|
|
102
|
+
port += 1;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
88
105
|
throw error;
|
|
89
106
|
}
|
|
90
107
|
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { log } from "../log";
|
|
1
2
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
clearConsoleBuffer,
|
|
4
|
+
disposeSurfaceState,
|
|
5
|
+
emitConsole,
|
|
6
|
+
emitDownload,
|
|
7
|
+
emitPopupRequested,
|
|
8
|
+
emitSurfaceEvent,
|
|
9
|
+
onSurfaceInit,
|
|
10
|
+
registerDialogRequest,
|
|
4
11
|
} from "./SurfaceManager";
|
|
5
|
-
import { log } from "../log";
|
|
6
12
|
|
|
7
13
|
onSurfaceInit((surfaceId, hostViewId, view) => {
|
|
8
14
|
log.debug("surface/init surfaceId=" + surfaceId + " hostViewId=" + hostViewId);
|
|
@@ -24,7 +30,9 @@ onSurfaceInit((surfaceId, hostViewId, view) => {
|
|
|
24
30
|
view.on("load-fail", (event: any) => {
|
|
25
31
|
const d = event.data;
|
|
26
32
|
emitSurfaceEvent(hostViewId, surfaceId, {
|
|
27
|
-
type: "load-fail",
|
|
33
|
+
type: "load-fail",
|
|
34
|
+
url: d.url ?? "",
|
|
35
|
+
reason: d.reason,
|
|
28
36
|
});
|
|
29
37
|
});
|
|
30
38
|
view.on("dialog", (event: any) => {
|
|
@@ -34,7 +42,9 @@ onSurfaceInit((surfaceId, hostViewId, view) => {
|
|
|
34
42
|
message: string;
|
|
35
43
|
defaultPrompt?: string;
|
|
36
44
|
};
|
|
37
|
-
log.debug(
|
|
45
|
+
log.debug(
|
|
46
|
+
"surface/dialog-ipc surfaceId=" + surfaceId + " hostViewId=" + hostViewId + " kind=" + d.kind,
|
|
47
|
+
);
|
|
38
48
|
registerDialogRequest(hostViewId, surfaceId, d);
|
|
39
49
|
});
|
|
40
50
|
view.on("console-message", (event: any) => {
|