bunite-core 0.0.14 → 0.2.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.0.14",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "setup:cef": "bun ../tools/bunite-dev/scripts/setup-cef.ts",
@@ -31,6 +31,10 @@ type AppOptions = NativeBootstrapOptions & {
31
31
 
32
32
  export type GlobalIPCHandler = (params: unknown, ctx: { viewId: number }) => unknown | Promise<unknown>;
33
33
 
34
+ function normalizeAppResPath(path: string): string {
35
+ return path.replace(/^\/+/, "").replace(/\/+$/, "");
36
+ }
37
+
34
38
  export class AppRuntime {
35
39
  private stubKeepAliveTimer: ReturnType<typeof setInterval> | null = null;
36
40
  private readonly globalIPCHandlers = new Map<string, GlobalIPCHandler>();
@@ -209,13 +213,15 @@ export class AppRuntime {
209
213
  private readonly appresHandlers = new Map<string, () => string>();
210
214
 
211
215
  getAppRes(path: string, handler: () => string) {
212
- this.appresHandlers.set(path, handler);
213
- getNativeLibrary()?.symbols.bunite_register_appres_route(toCString(path));
216
+ const normalized = normalizeAppResPath(path);
217
+ this.appresHandlers.set(normalized, handler);
218
+ getNativeLibrary()?.symbols.bunite_register_appres_route(toCString(normalized));
214
219
  }
215
220
 
216
221
  removeAppRes(path: string) {
217
- this.appresHandlers.delete(path);
218
- getNativeLibrary()?.symbols.bunite_unregister_appres_route(toCString(path));
222
+ const normalized = normalizeAppResPath(path);
223
+ this.appresHandlers.delete(normalized);
224
+ getNativeLibrary()?.symbols.bunite_unregister_appres_route(toCString(normalized));
219
225
  }
220
226
 
221
227
  /** @internal */
@@ -2,8 +2,7 @@ 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";
6
- import { createWebRPCHandler } from "../../shared/webRpcHandler";
5
+ import { type RPCPacket, type RPCTransport, type RPCWithTransport } from "../../shared/rpc";
7
6
  import { ensureNativeRuntime, getNativeLibrary, toCString, waitForViewReady, cancelWaitForViewReady } from "../proc/native";
8
7
  import { attachBrowserViewRegistry, getRPCPort, sendMessageToView } from "./Socket";
9
8
  import { randomBytes } from "node:crypto";
@@ -14,6 +13,16 @@ import { cancelPendingMessageBoxesForView } from "./Utils";
14
13
  const BrowserViewMap: Record<number, BrowserView<any>> = {};
15
14
  let nextWebviewId = 1;
16
15
 
16
+ function createNativeViewPipe(viewId: number) {
17
+ let handler: ((packet: RPCPacket) => void) | undefined;
18
+ const transport: RPCTransport = {
19
+ send: (packet) => { sendMessageToView(viewId, packet); },
20
+ registerHandler: (h) => { handler = h; },
21
+ unregisterHandler: () => { handler = undefined; }
22
+ };
23
+ return { transport, receive: (packet: RPCPacket) => handler?.(packet) };
24
+ }
25
+
17
26
  export type BrowserViewOptions<T = undefined> = {
18
27
  url: string | null;
19
28
  html: string | null;
@@ -66,7 +75,8 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
66
75
  partition: string | null;
67
76
  frame: BrowserViewOptions["frame"];
68
77
  rpc?: T;
69
- rpcHandler?: (message: unknown) => void;
78
+ readonly transport: RPCTransport;
79
+ private pipe: ReturnType<typeof createNativeViewPipe>;
70
80
  autoResize: boolean;
71
81
  navigationRules: string[] | null;
72
82
  sandbox: boolean;
@@ -75,6 +85,9 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
75
85
  constructor(options: Partial<BrowserViewOptions<T>>) {
76
86
  ensureNativeRuntime();
77
87
 
88
+ this.pipe = createNativeViewPipe(this.id);
89
+ this.transport = this.pipe.transport;
90
+
78
91
  this.windowId = options.windowId ?? defaultOptions.windowId;
79
92
  this.url = options.url ?? defaultOptions.url;
80
93
  this.html = options.html ?? defaultOptions.html;
@@ -105,7 +118,7 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
105
118
  });
106
119
 
107
120
  BrowserViewMap[this.id] = this;
108
- this.rpc?.setTransport(this.createTransport());
121
+ this.rpc?.setTransport(this.transport);
109
122
  // Register ready waiter BEFORE native create — OnAfterCreated can fire
110
123
  // on the CEF UI thread before bunite_view_create returns to JS.
111
124
  this._readyPromise = waitForViewReady(this.id);
@@ -161,34 +174,8 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
161
174
  return Object.values(BrowserViewMap);
162
175
  }
163
176
 
164
- static defineRPC<Schema extends BuniteRPCSchema>(
165
- config: BuniteRPCConfig<Schema, "bun">
166
- ) {
167
- const rpc = defineBuniteRPC("bun", config);
168
- const webRpc = createWebRPCHandler<Schema>(config);
169
- return Object.assign(rpc, {
170
- webHandler: webRpc,
171
- webClients: webRpc.webClients,
172
- broadcast: webRpc.broadcast,
173
- });
174
- }
175
-
176
- handleIncomingRPC(message: unknown) {
177
- this.rpcHandler?.(message);
178
- }
179
-
180
- createTransport() {
181
- return {
182
- send: (message: any) => {
183
- sendMessageToView(this.id, message);
184
- },
185
- registerHandler: (handler: (message: any) => void) => {
186
- this.rpcHandler = handler;
187
- },
188
- unregisterHandler: () => {
189
- this.rpcHandler = undefined;
190
- }
191
- };
177
+ handleIncomingRPC(packet: RPCPacket) {
178
+ this.pipe.receive(packet);
192
179
  }
193
180
 
194
181
  get rpcPort() {
@@ -202,6 +202,12 @@ export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
202
202
  this.webviewId = webview.id;
203
203
  }
204
204
 
205
+ get view(): BrowserView<T> {
206
+ const view = BrowserView.getById(this.webviewId);
207
+ if (!view) throw new Error(`BrowserWindow ${this.id} has no attached view`);
208
+ return view as BrowserView<T>;
209
+ }
210
+
205
211
  static getById(id: number) {
206
212
  return BrowserWindowMap[id];
207
213
  }
package/src/bun/index.ts CHANGED
@@ -7,12 +7,15 @@ import { BuniteEvent } from "./events/event";
7
7
  import { completePermissionRequest } from "./proc/native";
8
8
  import {
9
9
  createRPC,
10
- defineBuniteRPC,
10
+ defineBunRPC,
11
11
  type BuniteRPCConfig,
12
12
  type BuniteRPCSchema,
13
13
  type RPCSchema,
14
14
  type RPCWithTransport
15
15
  } from "../shared/rpc";
16
+ import { createTransportDemuxer, type TransportDemuxer } from "../shared/rpcDemux";
17
+ import { createWebSocketTransport, type WebSocketLike, type WebSocketTransportPipe } from "../shared/webSocketTransport";
18
+ import { createWebRPCHandler, type WebRPCClient } from "../shared/webRpcHandler";
16
19
  import type { MessageBoxOptions, MessageBoxResponse } from "./core/Utils";
17
20
  import { log, type LogLevel } from "../shared/log";
18
21
 
@@ -24,7 +27,10 @@ export {
24
27
  buniteEventEmitter,
25
28
  completePermissionRequest,
26
29
  createRPC,
27
- defineBuniteRPC,
30
+ createTransportDemuxer,
31
+ createWebRPCHandler,
32
+ createWebSocketTransport,
33
+ defineBunRPC,
28
34
  log
29
35
  };
30
36
 
@@ -38,5 +44,9 @@ export type {
38
44
  MessageBoxResponse,
39
45
  RPCSchema,
40
46
  RPCWithTransport,
47
+ TransportDemuxer,
48
+ WebRPCClient,
49
+ WebSocketLike,
50
+ WebSocketTransportPipe,
41
51
  WindowOptionsType
42
52
  };
package/src/shared/rpc.ts CHANGED
@@ -370,11 +370,10 @@ export function createRPC<
370
370
  };
371
371
  }
372
372
 
373
- export function defineBuniteRPC<
373
+ function defineSideRPC<
374
374
  Schema extends BuniteRPCSchema,
375
375
  Side extends "bun" | "webview"
376
376
  >(
377
- side: Side,
378
377
  config: BuniteRPCConfig<Schema, Side> & {
379
378
  extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
380
379
  }
@@ -416,3 +415,19 @@ export function defineBuniteRPC<
416
415
 
417
416
  return rpc;
418
417
  }
418
+
419
+ export function defineBunRPC<Schema extends BuniteRPCSchema>(
420
+ config: BuniteRPCConfig<Schema, "bun"> & {
421
+ extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
422
+ }
423
+ ) {
424
+ return defineSideRPC<Schema, "bun">(config);
425
+ }
426
+
427
+ export function defineWebviewRPC<Schema extends BuniteRPCSchema>(
428
+ config: BuniteRPCConfig<Schema, "webview"> & {
429
+ extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
430
+ }
431
+ ) {
432
+ return defineSideRPC<Schema, "webview">(config);
433
+ }
@@ -0,0 +1,65 @@
1
+ import type { RPCPacket, RPCTransport } from "./rpc";
2
+
3
+ type DemuxEnvelope = { channel: string; packet: RPCPacket };
4
+
5
+ function isDemuxEnvelope(value: unknown): value is DemuxEnvelope {
6
+ if (typeof value !== "object" || value === null) return false;
7
+ const v = value as Partial<DemuxEnvelope>;
8
+ return typeof v.channel === "string" && typeof v.packet === "object" && v.packet !== null;
9
+ }
10
+
11
+ export type TransportDemuxer = {
12
+ channel(name: string): RPCTransport;
13
+ dispose(): void;
14
+ };
15
+
16
+ export function createTransportDemuxer(base: RPCTransport): TransportDemuxer {
17
+ if (!base.send || !base.registerHandler) {
18
+ throw new Error("createTransportDemuxer requires a base transport with both send and registerHandler");
19
+ }
20
+
21
+ const handlers = new Map<string, (packet: RPCPacket) => void>();
22
+ let disposed = false;
23
+
24
+ base.registerHandler((data) => {
25
+ // Envelopes missing or malformed are dropped. A future fallthrough hook
26
+ // would land here if we ever multiplex legacy RPC on the same transport.
27
+ if (!isDemuxEnvelope(data)) return;
28
+ handlers.get(data.channel)?.(data.packet);
29
+ });
30
+
31
+ return {
32
+ channel(name) {
33
+ let ownHandler: ((packet: RPCPacket) => void) | undefined;
34
+
35
+ return {
36
+ send(packet) {
37
+ if (disposed) throw new Error(`Demuxer disposed; cannot send on channel "${name}"`);
38
+ const envelope: DemuxEnvelope = { channel: name, packet };
39
+ // The wire layer (msgpackr) serializes the envelope opaquely, so
40
+ // routing the wider type through RPCTransport.send is safe in practice.
41
+ base.send!(envelope as unknown as RPCPacket);
42
+ },
43
+ registerHandler(handler) {
44
+ if (disposed) throw new Error(`Demuxer disposed; cannot register on channel "${name}"`);
45
+ if (handlers.has(name)) {
46
+ throw new Error(`Channel "${name}" already has a handler on this demuxer`);
47
+ }
48
+ ownHandler = handler;
49
+ handlers.set(name, handler);
50
+ },
51
+ unregisterHandler() {
52
+ if (ownHandler && handlers.get(name) === ownHandler) {
53
+ handlers.delete(name);
54
+ ownHandler = undefined;
55
+ }
56
+ }
57
+ };
58
+ },
59
+ dispose() {
60
+ disposed = true;
61
+ handlers.clear();
62
+ base.unregisterHandler?.();
63
+ }
64
+ };
65
+ }
@@ -1,19 +1,14 @@
1
1
  import {
2
- defineBuniteRPC,
2
+ defineBunRPC,
3
3
  type BuniteRPCConfig,
4
- type BuniteRPCSchema,
5
- type RPCTransport,
6
- type RPCPacket
4
+ type BuniteRPCSchema
7
5
  } from "./rpc";
8
- import { decodeRPCPacket, encodeRPCPacket, asUint8Array } from "./rpcWire";
6
+ import { createWebSocketTransport, type WebSocketLike } from "./webSocketTransport";
9
7
  import { log } from "./log";
10
8
 
11
- type WebRPCSocket = { send(data: Uint8Array | ArrayBuffer): void | number };
12
-
13
9
  export type WebRPCClient<Schema extends BuniteRPCSchema = BuniteRPCSchema> = {
14
- ws: WebRPCSocket;
15
- rpc: ReturnType<typeof defineBuniteRPC<Schema, "bun">>;
16
- handlePacket: (packet: RPCPacket) => void | Promise<void>;
10
+ ws: WebSocketLike;
11
+ rpc: ReturnType<typeof defineBunRPC<Schema>>;
17
12
  };
18
13
 
19
14
  export function createWebRPCHandler<Schema extends BuniteRPCSchema>(
@@ -21,62 +16,44 @@ export function createWebRPCHandler<Schema extends BuniteRPCSchema>(
21
16
  extraRequestHandlers?: Record<string, (...args: any[]) => unknown>;
22
17
  }
23
18
  ) {
24
- const connections = new WeakMap<WebRPCSocket, WebRPCClient<Schema>>();
19
+ type Entry = { client: WebRPCClient<Schema>; receive: (raw: ArrayBuffer | Uint8Array) => void };
20
+
21
+ const connections = new WeakMap<WebSocketLike, Entry>();
25
22
  const webClients = new Set<WebRPCClient<Schema>>();
26
23
 
27
24
  const handler = {
28
- open(ws: WebRPCSocket) {
29
- let handlePacket: ((packet: RPCPacket) => void | Promise<void>) | undefined;
30
-
31
- const transport: RPCTransport = {
32
- send(packet) {
33
- ws.send(encodeRPCPacket(packet));
34
- },
35
- registerHandler(h) {
36
- handlePacket = h;
37
- },
38
- unregisterHandler() {
39
- handlePacket = undefined;
40
- }
41
- };
25
+ open(ws: WebSocketLike) {
26
+ const pipe = createWebSocketTransport(ws);
27
+ const rpc = defineBunRPC(config);
28
+ rpc.setTransport(pipe.transport);
42
29
 
43
- const rpc = defineBuniteRPC("bun", config);
44
- rpc.setTransport(transport);
30
+ const client: WebRPCClient<Schema> = { ws, rpc: rpc as WebRPCClient<Schema>["rpc"] };
45
31
 
46
- const client: WebRPCClient<Schema> = {
47
- ws,
48
- rpc: rpc as WebRPCClient<Schema>["rpc"],
49
- handlePacket: (packet) => handlePacket?.(packet)
50
- };
51
-
52
- connections.set(ws, client);
32
+ connections.set(ws, { client, receive: pipe.receive });
53
33
  webClients.add(client);
54
34
  handler.onWebClientConnected?.(client);
55
35
  },
56
36
 
57
- message(ws: WebRPCSocket, raw: string | Buffer | ArrayBuffer | Uint8Array) {
37
+ message(ws: WebSocketLike, raw: string | Buffer | ArrayBuffer | Uint8Array) {
58
38
  if (typeof raw === "string") return;
59
-
60
- const client = connections.get(ws);
61
- if (!client) return;
39
+ const entry = connections.get(ws);
40
+ if (!entry) return;
62
41
 
63
42
  try {
64
- Promise.resolve(client.handlePacket(decodeRPCPacket(asUint8Array(raw)))).catch((error) => {
65
- log.error("Web RPC packet handler error", error);
66
- });
67
- } catch {
68
- // malformed packet — decode failure
43
+ entry.receive(raw);
44
+ } catch (error) {
45
+ log.error("Web RPC packet handler error", error);
69
46
  }
70
47
  },
71
48
 
72
- close(ws: WebRPCSocket) {
73
- const client = connections.get(ws);
74
- if (!client) return;
49
+ close(ws: WebSocketLike) {
50
+ const entry = connections.get(ws);
51
+ if (!entry) return;
75
52
 
76
- client.rpc.dispose();
77
- webClients.delete(client);
53
+ entry.client.rpc.dispose();
54
+ webClients.delete(entry.client);
78
55
  connections.delete(ws);
79
- handler.onWebClientDisconnected?.(client);
56
+ handler.onWebClientDisconnected?.(entry.client);
80
57
  },
81
58
 
82
59
  webClients: webClients as ReadonlySet<WebRPCClient<Schema>>,
@@ -0,0 +1,26 @@
1
+ import type { RPCPacket, RPCTransport } from "./rpc";
2
+ import { asUint8Array, decodeRPCPacket, encodeRPCPacket } from "./rpcWire";
3
+
4
+ export type WebSocketLike = {
5
+ send(data: Uint8Array | ArrayBuffer): void | number;
6
+ };
7
+
8
+ export type WebSocketTransportPipe = {
9
+ transport: RPCTransport;
10
+ receive(raw: ArrayBuffer | ArrayBufferView | Uint8Array): void;
11
+ };
12
+
13
+ export function createWebSocketTransport(ws: WebSocketLike): WebSocketTransportPipe {
14
+ let handler: ((packet: RPCPacket) => void) | undefined;
15
+
16
+ return {
17
+ transport: {
18
+ send(packet) { ws.send(encodeRPCPacket(packet)); },
19
+ registerHandler(h) { handler = h; },
20
+ unregisterHandler() { handler = undefined; }
21
+ },
22
+ receive(raw) {
23
+ handler?.(decodeRPCPacket(asUint8Array(raw)));
24
+ }
25
+ };
26
+ }
package/src/view/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import "../shared/webviewPolyfill";
2
2
  import {
3
- defineBuniteRPC,
3
+ defineWebviewRPC,
4
4
  type BuniteRPCConfig,
5
5
  type RPCPacket,
6
6
  type BuniteRPCSchema,
@@ -8,6 +8,8 @@ import {
8
8
  type RPCTransport,
9
9
  type RPCWithTransport
10
10
  } from "../shared/rpc";
11
+ import { createTransportDemuxer, type TransportDemuxer } from "../shared/rpcDemux";
12
+ import { createWebSocketTransport, type WebSocketLike, type WebSocketTransportPipe } from "../shared/webSocketTransport";
11
13
  import { decodeRPCPacket, encodeRPCPacket } from "../shared/rpcWire";
12
14
  import { log } from "../shared/log";
13
15
 
@@ -34,24 +36,37 @@ function toArrayBuffer(bytes: Uint8Array): ArrayBuffer {
34
36
  return copy.buffer;
35
37
  }
36
38
 
37
- export class BuniteView<T extends RPCWithTransport> {
39
+ export class BuniteView<T extends RPCWithTransport = RPCWithTransport> {
38
40
  bunSocket?: WebSocket;
39
41
  rpc?: T;
40
- rpcHandler?: (message: unknown) => void;
42
+ readonly transport: RPCTransport;
43
+
44
+ private handler?: (packet: RPCPacket) => void;
41
45
  private pendingPackets: RPCPacket[] = [];
42
46
 
43
- constructor(config: { rpc: T }) {
44
- this.rpc = config.rpc;
45
- this.init();
46
- }
47
+ constructor(config?: { rpc?: T }) {
48
+ this.rpc = config?.rpc;
49
+
50
+ this.transport = {
51
+ send: (packet) => {
52
+ if (this.bunSocket?.readyState === WebSocket.OPEN) {
53
+ this.sendPacket(packet);
54
+ } else if (this.bunSocket?.readyState === WebSocket.CONNECTING) {
55
+ this.pendingPackets.push(packet);
56
+ }
57
+ },
58
+ registerHandler: (h) => { this.handler = h; },
59
+ unregisterHandler: () => { this.handler = undefined; }
60
+ };
47
61
 
48
- init() {
49
62
  this.initSocketToBun();
50
63
  if (isNative) {
51
64
  buniteWindow.__bunite ??= {};
52
- buniteWindow.__bunite.receiveMessageFromBun = this.receiveMessageFromBun.bind(this);
65
+ buniteWindow.__bunite.receiveMessageFromBun = (message) => {
66
+ this.handler?.(message as RPCPacket);
67
+ };
53
68
  }
54
- this.rpc?.setTransport(this.createTransport());
69
+ this.rpc?.setTransport(this.transport);
55
70
  }
56
71
 
57
72
  private sendPacket(packet: RPCPacket) {
@@ -72,10 +87,10 @@ export class BuniteView<T extends RPCWithTransport> {
72
87
  this.bunSocket = socket;
73
88
 
74
89
  socket.addEventListener("message", async (event) => {
75
- const bytes = await this.messageToUint8Array(event.data);
90
+ const bytes = await messageToUint8Array(event.data);
76
91
  if (!bytes) return;
77
92
  try {
78
- this.rpcHandler?.(decodeRPCPacket(bytes));
93
+ this.handler?.(decodeRPCPacket(bytes));
79
94
  } catch (error) {
80
95
  log.error("Failed to parse WebSocket message", error);
81
96
  }
@@ -97,7 +112,7 @@ export class BuniteView<T extends RPCWithTransport> {
97
112
  }
98
113
 
99
114
  this.bunSocket!.addEventListener("message", async (event) => {
100
- const binaryMessage = await this.messageToUint8Array(event.data);
115
+ const binaryMessage = await messageToUint8Array(event.data);
101
116
  if (!binaryMessage) return;
102
117
 
103
118
  try {
@@ -109,7 +124,7 @@ export class BuniteView<T extends RPCWithTransport> {
109
124
  const decrypted = await decrypt(binaryMessage);
110
125
  const packet = decodeRPCPacket(decrypted);
111
126
  if ((packet as any).scope === "global") return;
112
- this.rpcHandler?.(packet);
127
+ this.handler?.(packet);
113
128
  } catch (error) {
114
129
  log.error("Failed to parse message from Bun", error);
115
130
  }
@@ -133,35 +148,6 @@ export class BuniteView<T extends RPCWithTransport> {
133
148
  });
134
149
  }
135
150
 
136
- async messageToUint8Array(data: unknown) {
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;
140
- return null;
141
- }
142
-
143
- createTransport(): RPCTransport {
144
- return {
145
- send: (message) => {
146
- if (this.bunSocket?.readyState === WebSocket.OPEN) {
147
- this.sendPacket(message);
148
- } else if (this.bunSocket?.readyState === WebSocket.CONNECTING) {
149
- this.pendingPackets.push(message);
150
- }
151
- },
152
- registerHandler: (handler: (packet: any) => void) => {
153
- this.rpcHandler = handler;
154
- },
155
- unregisterHandler: () => {
156
- this.rpcHandler = undefined;
157
- }
158
- };
159
- }
160
-
161
- receiveMessageFromBun(message: unknown) {
162
- this.rpcHandler?.(message);
163
- }
164
-
165
151
  async bunBridge(message: RPCPacket) {
166
152
  if (this.bunSocket?.readyState !== WebSocket.OPEN) return;
167
153
 
@@ -175,19 +161,23 @@ export class BuniteView<T extends RPCWithTransport> {
175
161
  this.bunSocket.send(toArrayBuffer(encrypted));
176
162
  }
177
163
 
178
- static defineRPC<Schema extends BuniteRPCSchema>(
179
- config: BuniteRPCConfig<Schema, "webview">
180
- ) {
181
- const rpc = defineBuniteRPC("webview", config);
182
- new BuniteView({ rpc });
183
- return rpc;
184
- }
164
+ }
165
+
166
+ async function messageToUint8Array(data: unknown) {
167
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
168
+ if (data instanceof Blob) return new Uint8Array(await data.arrayBuffer());
169
+ if (data instanceof Uint8Array) return data;
170
+ return null;
185
171
  }
186
172
 
187
173
  export { log, type LogLevel } from "../shared/log";
174
+ export { createTransportDemuxer, createWebSocketTransport, defineWebviewRPC };
188
175
 
189
176
  export type {
190
177
  BuniteRPCConfig,
191
178
  BuniteRPCSchema,
192
- RPCSchema
179
+ RPCSchema,
180
+ TransportDemuxer,
181
+ WebSocketLike,
182
+ WebSocketTransportPipe
193
183
  };