bunite-core 0.4.0 → 0.5.0
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 -2
- package/src/shared/webviewPolyfill.ts +21 -5
- package/src/view/index.ts +26 -15
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bunite-core",
|
|
3
3
|
"description": "Uniting UI and Bun",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"setup:cef": "bun ../tools/bunite-dev/scripts/setup-cef.ts",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
".": "./src/bun/index.ts",
|
|
13
13
|
"./bun": "./src/bun/index.ts",
|
|
14
14
|
"./view": "./src/view/index.ts",
|
|
15
|
-
"./webview-polyfill": "./src/shared/webviewPolyfill.ts",
|
|
16
15
|
"./shared/rpc": "./src/shared/rpc.ts",
|
|
17
16
|
"./package.json": "./package.json"
|
|
18
17
|
},
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// Iframe-based fallback for web browsers. No-op when the native element is already registered by the CEF preload.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
// HTMLElement reference is lazy so this module is import-safe in Node/Bun.
|
|
3
|
+
|
|
4
|
+
let cachedClass: CustomElementConstructor | null = null;
|
|
5
|
+
|
|
6
|
+
function definePolyfillClass(): CustomElementConstructor {
|
|
7
|
+
if (cachedClass) return cachedClass;
|
|
8
|
+
|
|
6
9
|
class BuniteWebviewPolyfill extends HTMLElement {
|
|
7
10
|
static observedAttributes = ["src"];
|
|
8
11
|
|
|
@@ -69,5 +72,18 @@ if (
|
|
|
69
72
|
}
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
|
|
75
|
+
cachedClass = BuniteWebviewPolyfill;
|
|
76
|
+
return cachedClass;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Register the `<bunite-webview>` iframe polyfill. No-op in non-browser
|
|
81
|
+
* environments and when the native CEF preload has already registered the element.
|
|
82
|
+
* `BuniteView` calls this automatically on construction; call directly only when
|
|
83
|
+
* using `<bunite-webview>` markup without instantiating `BuniteView`.
|
|
84
|
+
*/
|
|
85
|
+
export function registerBuniteWebviewPolyfill() {
|
|
86
|
+
if (typeof customElements === "undefined") return;
|
|
87
|
+
if (customElements.get("bunite-webview")) return;
|
|
88
|
+
customElements.define("bunite-webview", definePolyfillClass());
|
|
73
89
|
}
|
package/src/view/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../shared/webviewPolyfill";
|
|
1
|
+
import { registerBuniteWebviewPolyfill } from "../shared/webviewPolyfill";
|
|
2
2
|
import {
|
|
3
3
|
defineWebviewRpc,
|
|
4
4
|
type BuniteRpcConfig,
|
|
@@ -24,11 +24,19 @@ type BuniteWindowGlobals = Window &
|
|
|
24
24
|
__bunite_decrypt?: (data: Uint8Array) => Promise<Uint8Array>;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
type BuniteEnv = {
|
|
28
|
+
window: BuniteWindowGlobals | null;
|
|
29
|
+
webviewId: number | undefined;
|
|
30
|
+
rpcPort: number | undefined;
|
|
31
|
+
isNative: boolean;
|
|
32
|
+
};
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
function readBuniteEnv(): BuniteEnv {
|
|
35
|
+
const w = typeof window !== "undefined" ? (window as BuniteWindowGlobals) : null;
|
|
36
|
+
const webviewId = w?.__buniteWebviewId;
|
|
37
|
+
const rpcPort = w?.__buniteRpcSocketPort;
|
|
38
|
+
return { window: w, webviewId, rpcPort, isNative: webviewId != null && rpcPort != null };
|
|
39
|
+
}
|
|
32
40
|
|
|
33
41
|
function toArrayBuffer(bytes: Uint8Array): ArrayBuffer {
|
|
34
42
|
const copy = new Uint8Array(bytes.byteLength);
|
|
@@ -41,10 +49,13 @@ export class BuniteView<T extends RpcWithTransport = RpcWithTransport> {
|
|
|
41
49
|
rpc?: T;
|
|
42
50
|
readonly transport: RpcTransport;
|
|
43
51
|
|
|
52
|
+
private env: BuniteEnv;
|
|
44
53
|
private handler?: (packet: RpcPacket) => void;
|
|
45
54
|
private pendingPackets: RpcPacket[] = [];
|
|
46
55
|
|
|
47
56
|
constructor(config?: { rpc?: T }) {
|
|
57
|
+
registerBuniteWebviewPolyfill();
|
|
58
|
+
this.env = readBuniteEnv();
|
|
48
59
|
this.rpc = config?.rpc;
|
|
49
60
|
|
|
50
61
|
this.transport = {
|
|
@@ -60,9 +71,9 @@ export class BuniteView<T extends RpcWithTransport = RpcWithTransport> {
|
|
|
60
71
|
};
|
|
61
72
|
|
|
62
73
|
this.initSocketToBun();
|
|
63
|
-
if (isNative) {
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
if (this.env.isNative && this.env.window) {
|
|
75
|
+
this.env.window.__bunite ??= {};
|
|
76
|
+
this.env.window.__bunite.receiveMessageFromBun = (message) => {
|
|
66
77
|
this.handler?.(message as RpcPacket);
|
|
67
78
|
};
|
|
68
79
|
}
|
|
@@ -70,7 +81,7 @@ export class BuniteView<T extends RpcWithTransport = RpcWithTransport> {
|
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
private sendPacket(packet: RpcPacket) {
|
|
73
|
-
if (isNative) {
|
|
84
|
+
if (this.env.isNative) {
|
|
74
85
|
void this.bunBridge(packet).catch((error) => {
|
|
75
86
|
log.error("Failed to send RPC packet", error);
|
|
76
87
|
});
|
|
@@ -80,7 +91,7 @@ export class BuniteView<T extends RpcWithTransport = RpcWithTransport> {
|
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
initSocketToBun() {
|
|
83
|
-
if (!isNative) {
|
|
94
|
+
if (!this.env.isNative) {
|
|
84
95
|
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
|
85
96
|
const socket = new WebSocket(`${proto}//${location.host}/rpc`);
|
|
86
97
|
socket.binaryType = "arraybuffer";
|
|
@@ -97,14 +108,14 @@ export class BuniteView<T extends RpcWithTransport = RpcWithTransport> {
|
|
|
97
108
|
});
|
|
98
109
|
} else {
|
|
99
110
|
// Share a single WebSocket with the preload's bunite.invoke.
|
|
100
|
-
const globals =
|
|
111
|
+
const globals = this.env.window as any;
|
|
101
112
|
globals.__bunite ??= {};
|
|
102
113
|
const existing = globals.__bunite._socket;
|
|
103
114
|
if (existing && existing.readyState <= WebSocket.OPEN) {
|
|
104
115
|
this.bunSocket = existing;
|
|
105
116
|
} else {
|
|
106
117
|
const socket = new WebSocket(
|
|
107
|
-
`ws://localhost:${
|
|
118
|
+
`ws://localhost:${this.env.rpcPort}/socket?webviewId=${this.env.webviewId}`
|
|
108
119
|
);
|
|
109
120
|
socket.binaryType = "arraybuffer";
|
|
110
121
|
this.bunSocket = socket;
|
|
@@ -116,7 +127,7 @@ export class BuniteView<T extends RpcWithTransport = RpcWithTransport> {
|
|
|
116
127
|
if (!binaryMessage) return;
|
|
117
128
|
|
|
118
129
|
try {
|
|
119
|
-
const decrypt =
|
|
130
|
+
const decrypt = this.env.window?.__bunite_decrypt;
|
|
120
131
|
if (!decrypt) {
|
|
121
132
|
log.error("No decrypt function available in preload globals");
|
|
122
133
|
return;
|
|
@@ -151,7 +162,7 @@ export class BuniteView<T extends RpcWithTransport = RpcWithTransport> {
|
|
|
151
162
|
async bunBridge(message: RpcPacket) {
|
|
152
163
|
if (this.bunSocket?.readyState !== WebSocket.OPEN) return;
|
|
153
164
|
|
|
154
|
-
const encrypt =
|
|
165
|
+
const encrypt = this.env.window?.__bunite_encrypt;
|
|
155
166
|
if (!encrypt) {
|
|
156
167
|
log.error("No encrypt function available in preload globals");
|
|
157
168
|
return;
|
|
@@ -171,7 +182,7 @@ async function messageToUint8Array(data: unknown) {
|
|
|
171
182
|
}
|
|
172
183
|
|
|
173
184
|
export { log, type LogLevel } from "../shared/log";
|
|
174
|
-
export { createRpcTransportDemuxer, createWebSocketTransport, defineWebviewRpc };
|
|
185
|
+
export { createRpcTransportDemuxer, createWebSocketTransport, defineWebviewRpc, registerBuniteWebviewPolyfill };
|
|
175
186
|
|
|
176
187
|
export type {
|
|
177
188
|
BuniteRpcConfig,
|