bunite-core 0.4.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bunite-core",
3
3
  "description": "Uniting UI and Bun",
4
- "version": "0.4.0",
4
+ "version": "0.6.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
  },
package/src/shared/rpc.ts CHANGED
@@ -374,9 +374,7 @@ function defineSideRpc<
374
374
  Schema extends BuniteRpcSchema,
375
375
  Side extends "bun" | "webview"
376
376
  >(
377
- config: BuniteRpcConfig<Schema, Side> & {
378
- extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
379
- }
377
+ config: BuniteRpcConfig<Schema, Side>
380
378
  ) {
381
379
  type RemoteSide = Side extends "bun" ? "webview" : "bun";
382
380
  type LocalSchema = {
@@ -390,10 +388,7 @@ function defineSideRpc<
390
388
 
391
389
  const rpc = createRpc<LocalSchema, RemoteSchema>({
392
390
  maxRequestTime: config.maxRequestTime,
393
- requestHandler: {
394
- ...(config.handlers.requests ?? {}),
395
- ...(config.extraRequestHandlers ?? {})
396
- } as RpcRequestHandler<LocalSchema["requests"]>,
391
+ requestHandler: config.handlers.requests,
397
392
  transport: {
398
393
  registerHandler: () => {}
399
394
  }
@@ -417,17 +412,13 @@ function defineSideRpc<
417
412
  }
418
413
 
419
414
  export function defineBunRpc<Schema extends BuniteRpcSchema>(
420
- config: BuniteRpcConfig<Schema, "bun"> & {
421
- extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
422
- }
415
+ config: BuniteRpcConfig<Schema, "bun">
423
416
  ) {
424
417
  return defineSideRpc<Schema, "bun">(config);
425
418
  }
426
419
 
427
420
  export function defineWebviewRpc<Schema extends BuniteRpcSchema>(
428
- config: BuniteRpcConfig<Schema, "webview"> & {
429
- extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
430
- }
421
+ config: BuniteRpcConfig<Schema, "webview">
431
422
  ) {
432
423
  return defineSideRpc<Schema, "webview">(config);
433
424
  }
@@ -12,9 +12,7 @@ export type WebRpcClient<Schema extends BuniteRpcSchema = BuniteRpcSchema> = {
12
12
  };
13
13
 
14
14
  export function createWebRpcHandler<Schema extends BuniteRpcSchema>(
15
- config: BuniteRpcConfig<Schema, "bun"> & {
16
- extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
17
- }
15
+ config: BuniteRpcConfig<Schema, "bun">
18
16
  ) {
19
17
  type Entry = { client: WebRpcClient<Schema>; receive: (raw: ArrayBuffer | Uint8Array) => void };
20
18
 
@@ -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
- if (
3
- typeof customElements !== "undefined" &&
4
- !customElements.get("bunite-webview")
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
- customElements.define("bunite-webview", BuniteWebviewPolyfill);
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
- const buniteWindow = window as BuniteWindowGlobals;
28
- const WEBVIEW_ID = buniteWindow.__buniteWebviewId;
29
- const RPC_SOCKET_PORT = buniteWindow.__buniteRpcSocketPort;
27
+ type BuniteEnv = {
28
+ window: BuniteWindowGlobals | null;
29
+ webviewId: number | undefined;
30
+ rpcPort: number | undefined;
31
+ isNative: boolean;
32
+ };
30
33
 
31
- const isNative = WEBVIEW_ID != null && RPC_SOCKET_PORT != null;
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
- buniteWindow.__bunite ??= {};
65
- buniteWindow.__bunite.receiveMessageFromBun = (message) => {
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 = buniteWindow as any;
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:${RPC_SOCKET_PORT}/socket?webviewId=${WEBVIEW_ID}`
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 = buniteWindow.__bunite_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 = buniteWindow.__bunite_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,