bunite-core 0.0.1
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 +24 -0
- package/src/bun/core/App.ts +142 -0
- package/src/bun/core/BrowserView.ts +262 -0
- package/src/bun/core/BrowserWindow.ts +322 -0
- package/src/bun/core/Socket.ts +186 -0
- package/src/bun/core/Utils.ts +72 -0
- package/src/bun/core/windowIds.ts +7 -0
- package/src/bun/events/appEvents.ts +7 -0
- package/src/bun/events/event.ts +20 -0
- package/src/bun/events/eventEmitter.ts +28 -0
- package/src/bun/events/webviewEvents.ts +13 -0
- package/src/bun/events/windowEvents.ts +19 -0
- package/src/bun/index.ts +41 -0
- package/src/bun/preload/index.ts +73 -0
- package/src/bun/preload/inline.ts +87 -0
- package/src/bun/proc/native.ts +666 -0
- package/src/native/shared/callbacks.h +6 -0
- package/src/native/shared/cef_response_filter.h +116 -0
- package/src/native/shared/ffi_exports.h +119 -0
- package/src/native/shared/webview_storage.h +89 -0
- package/src/native/win/native_host.cpp +2453 -0
- package/src/native/win/process_helper_win.cpp +26 -0
- package/src/preload/runtime.built.js +1 -0
- package/src/preload/runtime.ts +215 -0
- package/src/preload/tsconfig.json +13 -0
- package/src/shared/paths.ts +133 -0
- package/src/shared/platform.ts +9 -0
- package/src/shared/rpc.ts +399 -0
- package/src/shared/rpcWire.ts +54 -0
- package/src/shared/rpcWireConstants.ts +3 -0
- package/src/types/config.ts +29 -0
- package/src/view/index.ts +159 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import { CString, dlopen, FFIType, JSCallback, ptr, type Pointer } from "bun:ffi";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { delimiter, join } from "node:path";
|
|
4
|
+
import { buniteEventEmitter } from "../events/eventEmitter";
|
|
5
|
+
import { resolveNativeArtifacts, type ResolvedNativeArtifacts } from "../../shared/paths";
|
|
6
|
+
|
|
7
|
+
export type NativeBootstrapOptions = {
|
|
8
|
+
allowStub?: boolean;
|
|
9
|
+
hideConsole?: boolean;
|
|
10
|
+
popupBlocking?: boolean;
|
|
11
|
+
chromiumFlags?: Record<string, string | boolean>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type NativeRuntimeState = {
|
|
15
|
+
initialized: boolean;
|
|
16
|
+
usingStub: boolean;
|
|
17
|
+
nativeLoaded: boolean;
|
|
18
|
+
artifacts: ResolvedNativeArtifacts;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type CStringPointer = Pointer;
|
|
22
|
+
|
|
23
|
+
type NativeSymbols = {
|
|
24
|
+
bunite_init: (
|
|
25
|
+
processHelperPath: CStringPointer,
|
|
26
|
+
cefDir: CStringPointer,
|
|
27
|
+
hideConsole: boolean,
|
|
28
|
+
popupBlocking: boolean,
|
|
29
|
+
chromiumFlagsJson: CStringPointer
|
|
30
|
+
) => boolean;
|
|
31
|
+
bunite_run_loop: () => void;
|
|
32
|
+
bunite_quit: () => void;
|
|
33
|
+
bunite_free_cstring: (value: Pointer) => void;
|
|
34
|
+
bunite_window_create: (
|
|
35
|
+
windowId: number,
|
|
36
|
+
x: number,
|
|
37
|
+
y: number,
|
|
38
|
+
width: number,
|
|
39
|
+
height: number,
|
|
40
|
+
title: CStringPointer,
|
|
41
|
+
titleBarStyle: CStringPointer,
|
|
42
|
+
transparent: boolean,
|
|
43
|
+
hidden: boolean,
|
|
44
|
+
minimized: boolean,
|
|
45
|
+
maximized: boolean
|
|
46
|
+
) => Pointer;
|
|
47
|
+
bunite_window_show: (windowPtr: Pointer) => void;
|
|
48
|
+
bunite_window_close: (windowPtr: Pointer) => void;
|
|
49
|
+
bunite_window_set_title: (windowPtr: Pointer, title: CStringPointer) => void;
|
|
50
|
+
bunite_window_minimize: (windowPtr: Pointer) => void;
|
|
51
|
+
bunite_window_unminimize: (windowPtr: Pointer) => void;
|
|
52
|
+
bunite_window_is_minimized: (windowPtr: Pointer) => boolean;
|
|
53
|
+
bunite_window_maximize: (windowPtr: Pointer) => void;
|
|
54
|
+
bunite_window_unmaximize: (windowPtr: Pointer) => void;
|
|
55
|
+
bunite_window_is_maximized: (windowPtr: Pointer) => boolean;
|
|
56
|
+
bunite_window_set_frame: (
|
|
57
|
+
windowPtr: Pointer,
|
|
58
|
+
x: number,
|
|
59
|
+
y: number,
|
|
60
|
+
width: number,
|
|
61
|
+
height: number
|
|
62
|
+
) => void;
|
|
63
|
+
bunite_view_create: (
|
|
64
|
+
viewId: number,
|
|
65
|
+
windowPtr: Pointer | null,
|
|
66
|
+
url: CStringPointer,
|
|
67
|
+
html: CStringPointer,
|
|
68
|
+
preload: CStringPointer,
|
|
69
|
+
viewsRoot: CStringPointer,
|
|
70
|
+
navigationRulesJson: CStringPointer,
|
|
71
|
+
x: number,
|
|
72
|
+
y: number,
|
|
73
|
+
width: number,
|
|
74
|
+
height: number,
|
|
75
|
+
autoResize: boolean,
|
|
76
|
+
sandbox: boolean
|
|
77
|
+
) => Pointer;
|
|
78
|
+
bunite_register_view_route: (path: CStringPointer) => void;
|
|
79
|
+
bunite_unregister_view_route: (path: CStringPointer) => void;
|
|
80
|
+
bunite_complete_route_request: (requestId: number, html: CStringPointer) => void;
|
|
81
|
+
bunite_view_set_visible: (viewPtr: Pointer, visible: boolean) => void;
|
|
82
|
+
bunite_view_set_bounds: (viewPtr: Pointer, x: number, y: number, width: number, height: number) => void;
|
|
83
|
+
bunite_view_set_anchor: (viewPtr: Pointer, mode: number, inset: number) => void;
|
|
84
|
+
bunite_view_go_back: (viewPtr: Pointer) => void;
|
|
85
|
+
bunite_view_reload: (viewPtr: Pointer) => void;
|
|
86
|
+
bunite_view_load_url: (viewPtr: Pointer, url: CStringPointer) => void;
|
|
87
|
+
bunite_view_load_html: (viewPtr: Pointer, html: CStringPointer) => void;
|
|
88
|
+
bunite_view_remove: (viewPtr: Pointer) => void;
|
|
89
|
+
bunite_view_open_devtools: (viewPtr: Pointer) => void;
|
|
90
|
+
bunite_view_close_devtools: (viewPtr: Pointer) => void;
|
|
91
|
+
bunite_view_toggle_devtools: (viewPtr: Pointer) => void;
|
|
92
|
+
bunite_complete_permission_request: (requestId: number, state: number) => void;
|
|
93
|
+
bunite_show_message_box: (
|
|
94
|
+
type: CStringPointer,
|
|
95
|
+
title: CStringPointer,
|
|
96
|
+
message: CStringPointer,
|
|
97
|
+
detail: CStringPointer,
|
|
98
|
+
buttons: CStringPointer,
|
|
99
|
+
defaultId: number,
|
|
100
|
+
cancelId: number
|
|
101
|
+
) => number;
|
|
102
|
+
bunite_show_browser_message_box: (
|
|
103
|
+
type: CStringPointer,
|
|
104
|
+
title: CStringPointer,
|
|
105
|
+
message: CStringPointer,
|
|
106
|
+
detail: CStringPointer,
|
|
107
|
+
buttons: CStringPointer,
|
|
108
|
+
defaultId: number,
|
|
109
|
+
cancelId: number
|
|
110
|
+
) => number;
|
|
111
|
+
bunite_cancel_browser_message_box: (requestId: number) => void;
|
|
112
|
+
bunite_set_webview_event_handler: (handler: JSCallback) => void;
|
|
113
|
+
bunite_set_window_event_handler: (handler: JSCallback) => void;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
type LoadedNativeLibrary = {
|
|
117
|
+
symbols: NativeSymbols;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const messageBoxButtonSeparator = "\x1f";
|
|
121
|
+
const unsetCancelId = -1;
|
|
122
|
+
|
|
123
|
+
const nativeSymbolDefinitions = {
|
|
124
|
+
bunite_init: {
|
|
125
|
+
args: [FFIType.cstring, FFIType.cstring, FFIType.bool, FFIType.bool, FFIType.cstring],
|
|
126
|
+
returns: FFIType.bool
|
|
127
|
+
},
|
|
128
|
+
bunite_run_loop: {
|
|
129
|
+
args: [],
|
|
130
|
+
returns: FFIType.void
|
|
131
|
+
},
|
|
132
|
+
bunite_quit: {
|
|
133
|
+
args: [],
|
|
134
|
+
returns: FFIType.void
|
|
135
|
+
},
|
|
136
|
+
bunite_free_cstring: {
|
|
137
|
+
args: [FFIType.ptr],
|
|
138
|
+
returns: FFIType.void
|
|
139
|
+
},
|
|
140
|
+
bunite_window_create: {
|
|
141
|
+
args: [
|
|
142
|
+
FFIType.u32,
|
|
143
|
+
FFIType.f64,
|
|
144
|
+
FFIType.f64,
|
|
145
|
+
FFIType.f64,
|
|
146
|
+
FFIType.f64,
|
|
147
|
+
FFIType.cstring,
|
|
148
|
+
FFIType.cstring,
|
|
149
|
+
FFIType.bool,
|
|
150
|
+
FFIType.bool,
|
|
151
|
+
FFIType.bool,
|
|
152
|
+
FFIType.bool
|
|
153
|
+
],
|
|
154
|
+
returns: FFIType.ptr
|
|
155
|
+
},
|
|
156
|
+
bunite_window_show: {
|
|
157
|
+
args: [FFIType.ptr],
|
|
158
|
+
returns: FFIType.void
|
|
159
|
+
},
|
|
160
|
+
bunite_window_close: {
|
|
161
|
+
args: [FFIType.ptr],
|
|
162
|
+
returns: FFIType.void
|
|
163
|
+
},
|
|
164
|
+
bunite_window_set_title: {
|
|
165
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
166
|
+
returns: FFIType.void
|
|
167
|
+
},
|
|
168
|
+
bunite_window_minimize: {
|
|
169
|
+
args: [FFIType.ptr],
|
|
170
|
+
returns: FFIType.void
|
|
171
|
+
},
|
|
172
|
+
bunite_window_unminimize: {
|
|
173
|
+
args: [FFIType.ptr],
|
|
174
|
+
returns: FFIType.void
|
|
175
|
+
},
|
|
176
|
+
bunite_window_is_minimized: {
|
|
177
|
+
args: [FFIType.ptr],
|
|
178
|
+
returns: FFIType.bool
|
|
179
|
+
},
|
|
180
|
+
bunite_window_maximize: {
|
|
181
|
+
args: [FFIType.ptr],
|
|
182
|
+
returns: FFIType.void
|
|
183
|
+
},
|
|
184
|
+
bunite_window_unmaximize: {
|
|
185
|
+
args: [FFIType.ptr],
|
|
186
|
+
returns: FFIType.void
|
|
187
|
+
},
|
|
188
|
+
bunite_window_is_maximized: {
|
|
189
|
+
args: [FFIType.ptr],
|
|
190
|
+
returns: FFIType.bool
|
|
191
|
+
},
|
|
192
|
+
bunite_window_set_frame: {
|
|
193
|
+
args: [FFIType.ptr, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64],
|
|
194
|
+
returns: FFIType.void
|
|
195
|
+
},
|
|
196
|
+
bunite_view_create: {
|
|
197
|
+
args: [
|
|
198
|
+
FFIType.u32,
|
|
199
|
+
FFIType.ptr,
|
|
200
|
+
FFIType.cstring,
|
|
201
|
+
FFIType.cstring,
|
|
202
|
+
FFIType.cstring,
|
|
203
|
+
FFIType.cstring,
|
|
204
|
+
FFIType.cstring,
|
|
205
|
+
FFIType.f64,
|
|
206
|
+
FFIType.f64,
|
|
207
|
+
FFIType.f64,
|
|
208
|
+
FFIType.f64,
|
|
209
|
+
FFIType.bool,
|
|
210
|
+
FFIType.bool
|
|
211
|
+
],
|
|
212
|
+
returns: FFIType.ptr
|
|
213
|
+
},
|
|
214
|
+
bunite_register_view_route: {
|
|
215
|
+
args: [FFIType.cstring],
|
|
216
|
+
returns: FFIType.void
|
|
217
|
+
},
|
|
218
|
+
bunite_unregister_view_route: {
|
|
219
|
+
args: [FFIType.cstring],
|
|
220
|
+
returns: FFIType.void
|
|
221
|
+
},
|
|
222
|
+
bunite_complete_route_request: {
|
|
223
|
+
args: [FFIType.u32, FFIType.cstring],
|
|
224
|
+
returns: FFIType.void
|
|
225
|
+
},
|
|
226
|
+
bunite_view_set_visible: {
|
|
227
|
+
args: [FFIType.ptr, FFIType.bool],
|
|
228
|
+
returns: FFIType.void
|
|
229
|
+
},
|
|
230
|
+
bunite_view_set_bounds: {
|
|
231
|
+
args: [FFIType.ptr, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64],
|
|
232
|
+
returns: FFIType.void
|
|
233
|
+
},
|
|
234
|
+
bunite_view_set_anchor: {
|
|
235
|
+
args: [FFIType.ptr, FFIType.i32, FFIType.f64],
|
|
236
|
+
returns: FFIType.void
|
|
237
|
+
},
|
|
238
|
+
bunite_view_go_back: {
|
|
239
|
+
args: [FFIType.ptr],
|
|
240
|
+
returns: FFIType.void
|
|
241
|
+
},
|
|
242
|
+
bunite_view_reload: {
|
|
243
|
+
args: [FFIType.ptr],
|
|
244
|
+
returns: FFIType.void
|
|
245
|
+
},
|
|
246
|
+
bunite_view_load_url: {
|
|
247
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
248
|
+
returns: FFIType.void
|
|
249
|
+
},
|
|
250
|
+
bunite_view_load_html: {
|
|
251
|
+
args: [FFIType.ptr, FFIType.cstring],
|
|
252
|
+
returns: FFIType.void
|
|
253
|
+
},
|
|
254
|
+
bunite_view_remove: {
|
|
255
|
+
args: [FFIType.ptr],
|
|
256
|
+
returns: FFIType.void
|
|
257
|
+
},
|
|
258
|
+
bunite_view_open_devtools: {
|
|
259
|
+
args: [FFIType.ptr],
|
|
260
|
+
returns: FFIType.void
|
|
261
|
+
},
|
|
262
|
+
bunite_view_close_devtools: {
|
|
263
|
+
args: [FFIType.ptr],
|
|
264
|
+
returns: FFIType.void
|
|
265
|
+
},
|
|
266
|
+
bunite_view_toggle_devtools: {
|
|
267
|
+
args: [FFIType.ptr],
|
|
268
|
+
returns: FFIType.void
|
|
269
|
+
},
|
|
270
|
+
bunite_complete_permission_request: {
|
|
271
|
+
args: [FFIType.u32, FFIType.u32],
|
|
272
|
+
returns: FFIType.void
|
|
273
|
+
},
|
|
274
|
+
bunite_show_message_box: {
|
|
275
|
+
args: [
|
|
276
|
+
FFIType.cstring,
|
|
277
|
+
FFIType.cstring,
|
|
278
|
+
FFIType.cstring,
|
|
279
|
+
FFIType.cstring,
|
|
280
|
+
FFIType.cstring,
|
|
281
|
+
FFIType.i32,
|
|
282
|
+
FFIType.i32
|
|
283
|
+
],
|
|
284
|
+
returns: FFIType.i32
|
|
285
|
+
},
|
|
286
|
+
bunite_show_browser_message_box: {
|
|
287
|
+
args: [
|
|
288
|
+
FFIType.cstring,
|
|
289
|
+
FFIType.cstring,
|
|
290
|
+
FFIType.cstring,
|
|
291
|
+
FFIType.cstring,
|
|
292
|
+
FFIType.cstring,
|
|
293
|
+
FFIType.i32,
|
|
294
|
+
FFIType.i32
|
|
295
|
+
],
|
|
296
|
+
returns: FFIType.u32
|
|
297
|
+
},
|
|
298
|
+
bunite_cancel_browser_message_box: {
|
|
299
|
+
args: [FFIType.u32],
|
|
300
|
+
returns: FFIType.void
|
|
301
|
+
},
|
|
302
|
+
bunite_set_webview_event_handler: {
|
|
303
|
+
args: [FFIType.function],
|
|
304
|
+
returns: FFIType.void
|
|
305
|
+
},
|
|
306
|
+
bunite_set_window_event_handler: {
|
|
307
|
+
args: [FFIType.function],
|
|
308
|
+
returns: FFIType.void
|
|
309
|
+
}
|
|
310
|
+
} as const;
|
|
311
|
+
|
|
312
|
+
let state: NativeRuntimeState | null = null;
|
|
313
|
+
let nativeLibrary: LoadedNativeLibrary | null = null;
|
|
314
|
+
const retainedCStringBuffers: Buffer[] = [];
|
|
315
|
+
let webviewEventCallback: JSCallback | null = null;
|
|
316
|
+
let windowEventCallback: JSCallback | null = null;
|
|
317
|
+
let routeRequestHandler: ((requestId: number, path: string) => void) | null = null;
|
|
318
|
+
|
|
319
|
+
export function setRouteRequestHandler(handler: (requestId: number, path: string) => void) {
|
|
320
|
+
routeRequestHandler = handler;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function toCString(value: string): CStringPointer {
|
|
324
|
+
const normalized = value.endsWith("\0") ? value : `${value}\0`;
|
|
325
|
+
const buffer = Buffer.from(normalized, "utf8");
|
|
326
|
+
|
|
327
|
+
// Keep recent CString buffers alive long enough for native code to copy them.
|
|
328
|
+
// This is not a long-term ownership model for retained native pointers, but it
|
|
329
|
+
// avoids immediate GC hazards across the current FFI call boundary.
|
|
330
|
+
retainedCStringBuffers.push(buffer);
|
|
331
|
+
if (retainedCStringBuffers.length > 1024) {
|
|
332
|
+
retainedCStringBuffers.shift();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return ptr(buffer);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function applyEnvironment(artifacts: ResolvedNativeArtifacts) {
|
|
339
|
+
const cefBinaryDir = artifacts.cefDir && existsSync(join(artifacts.cefDir, "Release", "libcef.dll"))
|
|
340
|
+
? join(artifacts.cefDir, "Release")
|
|
341
|
+
: artifacts.cefDir;
|
|
342
|
+
const cefResourceDir = artifacts.cefDir && existsSync(join(artifacts.cefDir, "Resources", "resources.pak"))
|
|
343
|
+
? join(artifacts.cefDir, "Resources")
|
|
344
|
+
: artifacts.cefDir;
|
|
345
|
+
|
|
346
|
+
if (cefResourceDir && !process.env.ICU_DATA) {
|
|
347
|
+
process.env.ICU_DATA = cefResourceDir;
|
|
348
|
+
}
|
|
349
|
+
if (cefBinaryDir) {
|
|
350
|
+
const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
351
|
+
if (!pathEntries.includes(cefBinaryDir)) {
|
|
352
|
+
process.env.PATH = [cefBinaryDir, ...pathEntries].join(delimiter);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function tryLoadNativeLibrary(artifacts: ResolvedNativeArtifacts) {
|
|
358
|
+
if (!artifacts.nativeLibPath || !existsSync(artifacts.nativeLibPath)) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const library = dlopen(artifacts.nativeLibPath, nativeSymbolDefinitions as any);
|
|
364
|
+
return {
|
|
365
|
+
symbols: library.symbols as unknown as NativeSymbols
|
|
366
|
+
} satisfies LoadedNativeLibrary;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.warn("[bunite] Failed to load native library via FFI.", error);
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function maybeParsePayload(payload: string) {
|
|
374
|
+
const trimmed = payload.trim();
|
|
375
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
376
|
+
return payload;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
return JSON.parse(trimmed);
|
|
381
|
+
} catch {
|
|
382
|
+
return payload;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function registerNativeCallbacks(library: LoadedNativeLibrary) {
|
|
387
|
+
if (!webviewEventCallback) {
|
|
388
|
+
webviewEventCallback = new JSCallback(
|
|
389
|
+
(viewId, eventNamePtr, payloadPtr) => {
|
|
390
|
+
const eventName = new CString(eventNamePtr).toString();
|
|
391
|
+
const payload = new CString(payloadPtr).toString();
|
|
392
|
+
nativeLibrary?.symbols.bunite_free_cstring(eventNamePtr as Pointer);
|
|
393
|
+
nativeLibrary?.symbols.bunite_free_cstring(payloadPtr as Pointer);
|
|
394
|
+
|
|
395
|
+
switch (eventName) {
|
|
396
|
+
case "will-navigate":
|
|
397
|
+
buniteEventEmitter.emitEvent(
|
|
398
|
+
buniteEventEmitter.events.webview.willNavigate({ detail: payload }),
|
|
399
|
+
viewId
|
|
400
|
+
);
|
|
401
|
+
break;
|
|
402
|
+
case "did-navigate":
|
|
403
|
+
buniteEventEmitter.emitEvent(
|
|
404
|
+
buniteEventEmitter.events.webview.didNavigate({ detail: payload }),
|
|
405
|
+
viewId
|
|
406
|
+
);
|
|
407
|
+
break;
|
|
408
|
+
case "dom-ready":
|
|
409
|
+
buniteEventEmitter.emitEvent(
|
|
410
|
+
buniteEventEmitter.events.webview.domReady({ detail: payload }),
|
|
411
|
+
viewId
|
|
412
|
+
);
|
|
413
|
+
break;
|
|
414
|
+
case "new-window-open":
|
|
415
|
+
buniteEventEmitter.emitEvent(
|
|
416
|
+
buniteEventEmitter.events.webview.newWindowOpen({
|
|
417
|
+
detail: maybeParsePayload(payload) as string | { url: string }
|
|
418
|
+
}),
|
|
419
|
+
viewId
|
|
420
|
+
);
|
|
421
|
+
break;
|
|
422
|
+
case "permission-requested":
|
|
423
|
+
buniteEventEmitter.emitEvent(
|
|
424
|
+
buniteEventEmitter.events.webview.permissionRequested(
|
|
425
|
+
maybeParsePayload(payload) as { requestId: number; kind: number; url?: string }
|
|
426
|
+
),
|
|
427
|
+
viewId
|
|
428
|
+
);
|
|
429
|
+
break;
|
|
430
|
+
case "message-box-response":
|
|
431
|
+
buniteEventEmitter.emitEvent(
|
|
432
|
+
buniteEventEmitter.events.webview.messageBoxResponse(
|
|
433
|
+
maybeParsePayload(payload) as { requestId: number; response: number }
|
|
434
|
+
),
|
|
435
|
+
viewId
|
|
436
|
+
);
|
|
437
|
+
break;
|
|
438
|
+
case "route-request": {
|
|
439
|
+
const parsed = maybeParsePayload(payload) as { requestId: number; path: string };
|
|
440
|
+
routeRequestHandler?.(parsed.requestId, parsed.path);
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
args: [FFIType.u32, FFIType.cstring, FFIType.cstring],
|
|
447
|
+
returns: FFIType.void,
|
|
448
|
+
threadsafe: true
|
|
449
|
+
}
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!windowEventCallback) {
|
|
454
|
+
windowEventCallback = new JSCallback(
|
|
455
|
+
(windowId, eventNamePtr, payloadPtr) => {
|
|
456
|
+
const eventName = new CString(eventNamePtr).toString();
|
|
457
|
+
const payload = new CString(payloadPtr).toString();
|
|
458
|
+
nativeLibrary?.symbols.bunite_free_cstring(eventNamePtr as Pointer);
|
|
459
|
+
nativeLibrary?.symbols.bunite_free_cstring(payloadPtr as Pointer);
|
|
460
|
+
const parsedPayload = maybeParsePayload(payload);
|
|
461
|
+
|
|
462
|
+
switch (eventName) {
|
|
463
|
+
case "close":
|
|
464
|
+
buniteEventEmitter.emitEvent(
|
|
465
|
+
buniteEventEmitter.events.window.close({ id: windowId }),
|
|
466
|
+
windowId
|
|
467
|
+
);
|
|
468
|
+
break;
|
|
469
|
+
case "focus":
|
|
470
|
+
buniteEventEmitter.emitEvent(
|
|
471
|
+
buniteEventEmitter.events.window.focus({ id: windowId }),
|
|
472
|
+
windowId
|
|
473
|
+
);
|
|
474
|
+
break;
|
|
475
|
+
case "blur":
|
|
476
|
+
buniteEventEmitter.emitEvent(
|
|
477
|
+
buniteEventEmitter.events.window.blur({ id: windowId }),
|
|
478
|
+
windowId
|
|
479
|
+
);
|
|
480
|
+
break;
|
|
481
|
+
case "move":
|
|
482
|
+
if (parsedPayload && typeof parsedPayload === "object") {
|
|
483
|
+
const { x = 0, y = 0, maximized = false, minimized = false } = parsedPayload as {
|
|
484
|
+
x?: number;
|
|
485
|
+
y?: number;
|
|
486
|
+
maximized?: boolean;
|
|
487
|
+
minimized?: boolean;
|
|
488
|
+
};
|
|
489
|
+
buniteEventEmitter.emitEvent(
|
|
490
|
+
buniteEventEmitter.events.window.move({ id: windowId, x, y, maximized, minimized }),
|
|
491
|
+
windowId
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
break;
|
|
495
|
+
case "resize":
|
|
496
|
+
if (parsedPayload && typeof parsedPayload === "object") {
|
|
497
|
+
const { x = 0, y = 0, width = 0, height = 0, maximized = false, minimized = false } = parsedPayload as {
|
|
498
|
+
x?: number;
|
|
499
|
+
y?: number;
|
|
500
|
+
width?: number;
|
|
501
|
+
height?: number;
|
|
502
|
+
maximized?: boolean;
|
|
503
|
+
minimized?: boolean;
|
|
504
|
+
};
|
|
505
|
+
buniteEventEmitter.emitEvent(
|
|
506
|
+
buniteEventEmitter.events.window.resize({
|
|
507
|
+
id: windowId,
|
|
508
|
+
x,
|
|
509
|
+
y,
|
|
510
|
+
width,
|
|
511
|
+
height,
|
|
512
|
+
maximized,
|
|
513
|
+
minimized
|
|
514
|
+
}),
|
|
515
|
+
windowId
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
args: [FFIType.u32, FFIType.cstring, FFIType.cstring],
|
|
523
|
+
returns: FFIType.void,
|
|
524
|
+
threadsafe: true
|
|
525
|
+
}
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
library.symbols.bunite_set_webview_event_handler(webviewEventCallback);
|
|
530
|
+
library.symbols.bunite_set_window_event_handler(windowEventCallback);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export async function initNativeRuntime(
|
|
534
|
+
options: NativeBootstrapOptions = {}
|
|
535
|
+
): Promise<NativeRuntimeState> {
|
|
536
|
+
if (state) {
|
|
537
|
+
return state;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const allowStub = options.allowStub ?? true;
|
|
541
|
+
const artifacts = resolveNativeArtifacts();
|
|
542
|
+
const hasNativeArtifacts = Boolean(
|
|
543
|
+
artifacts.nativeLibPath &&
|
|
544
|
+
artifacts.processHelperPath &&
|
|
545
|
+
existsSync(artifacts.nativeLibPath) &&
|
|
546
|
+
existsSync(artifacts.processHelperPath)
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
applyEnvironment(artifacts);
|
|
550
|
+
|
|
551
|
+
if (!hasNativeArtifacts && !allowStub) {
|
|
552
|
+
throw new Error(
|
|
553
|
+
"bunite native runtime packages are missing. Install platform packages or allow stub mode."
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
nativeLibrary = hasNativeArtifacts ? tryLoadNativeLibrary(artifacts) : null;
|
|
558
|
+
|
|
559
|
+
if (nativeLibrary) {
|
|
560
|
+
registerNativeCallbacks(nativeLibrary);
|
|
561
|
+
const chromiumFlagsJson = options.chromiumFlags
|
|
562
|
+
? JSON.stringify(options.chromiumFlags)
|
|
563
|
+
: "";
|
|
564
|
+
const initOk = nativeLibrary.symbols.bunite_init(
|
|
565
|
+
toCString(artifacts.processHelperPath ?? ""),
|
|
566
|
+
toCString(artifacts.cefDir ?? ""),
|
|
567
|
+
options.hideConsole ?? false,
|
|
568
|
+
options.popupBlocking ?? false,
|
|
569
|
+
toCString(chromiumFlagsJson)
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
if (!initOk) {
|
|
573
|
+
nativeLibrary = null;
|
|
574
|
+
if (!allowStub) {
|
|
575
|
+
throw new Error("bunite native runtime failed to initialize.");
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (!nativeLibrary) {
|
|
581
|
+
console.warn(
|
|
582
|
+
"[bunite] Native runtime packages were not found or could not be loaded. Initializing in stub mode."
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
state = {
|
|
587
|
+
initialized: true,
|
|
588
|
+
usingStub: !nativeLibrary,
|
|
589
|
+
nativeLoaded: Boolean(nativeLibrary),
|
|
590
|
+
artifacts
|
|
591
|
+
};
|
|
592
|
+
return state;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export function getNativeRuntimeState(): NativeRuntimeState | null {
|
|
596
|
+
return state;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
export function ensureNativeRuntime(): NativeRuntimeState {
|
|
600
|
+
if (!state) {
|
|
601
|
+
throw new Error("bunite app has not been initialized. Call await app.init() first.");
|
|
602
|
+
}
|
|
603
|
+
return state;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export function getNativeLibrary(): LoadedNativeLibrary | null {
|
|
607
|
+
return nativeLibrary;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
export function completePermissionRequest(requestId: number, stateValue: number): void {
|
|
611
|
+
nativeLibrary?.symbols.bunite_complete_permission_request(requestId, stateValue);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export function showNativeMessageBox(params: {
|
|
615
|
+
type?: string;
|
|
616
|
+
title?: string;
|
|
617
|
+
message?: string;
|
|
618
|
+
detail?: string;
|
|
619
|
+
buttons?: string[];
|
|
620
|
+
defaultId?: number;
|
|
621
|
+
cancelId?: number;
|
|
622
|
+
}): number {
|
|
623
|
+
const native = getNativeLibrary();
|
|
624
|
+
if (!native) {
|
|
625
|
+
return params.cancelId ?? params.defaultId ?? 0;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return native.symbols.bunite_show_message_box(
|
|
629
|
+
toCString(params.type ?? "info"),
|
|
630
|
+
toCString(params.title ?? ""),
|
|
631
|
+
toCString(params.message ?? ""),
|
|
632
|
+
toCString(params.detail ?? ""),
|
|
633
|
+
toCString((params.buttons ?? ["OK"]).join(messageBoxButtonSeparator)),
|
|
634
|
+
params.defaultId ?? 0,
|
|
635
|
+
params.cancelId ?? unsetCancelId
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
export function requestBrowserMessageBox(params: {
|
|
640
|
+
type?: string;
|
|
641
|
+
title?: string;
|
|
642
|
+
message?: string;
|
|
643
|
+
detail?: string;
|
|
644
|
+
buttons?: string[];
|
|
645
|
+
defaultId?: number;
|
|
646
|
+
cancelId?: number;
|
|
647
|
+
}): number {
|
|
648
|
+
const native = getNativeLibrary();
|
|
649
|
+
if (!native) {
|
|
650
|
+
return 0;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return native.symbols.bunite_show_browser_message_box(
|
|
654
|
+
toCString(params.type ?? "info"),
|
|
655
|
+
toCString(params.title ?? ""),
|
|
656
|
+
toCString(params.message ?? ""),
|
|
657
|
+
toCString(params.detail ?? ""),
|
|
658
|
+
toCString((params.buttons ?? ["OK"]).join(messageBoxButtonSeparator)),
|
|
659
|
+
params.defaultId ?? 0,
|
|
660
|
+
params.cancelId ?? unsetCancelId
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export function cancelBrowserMessageBoxRequest(requestId: number): void {
|
|
665
|
+
getNativeLibrary()?.symbols.bunite_cancel_browser_message_box(requestId);
|
|
666
|
+
}
|