electrobun 0.0.18 → 0.0.19-beta.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.
@@ -1,622 +0,0 @@
1
- import { join, resolve } from "path";
2
- import { type RPCSchema, type RPCTransport, createRPC } from "rpc-anywhere";
3
- import { execSync } from "child_process";
4
- import * as fs from "fs";
5
- import electrobunEventEmitter from "../events/eventEmitter";
6
- import { BrowserView } from "../core/BrowserView";
7
- import { Updater } from "../core/Updater";
8
- import { Tray } from "../core/Tray";
9
- const CHUNK_SIZE = 1024 * 4; // 4KB
10
- // todo (yoav): webviewBinaryPath and ELECTROBUN_VIEWS_FOLDER should be passed in as cli/env args by the launcher binary
11
- // will likely be different on different platforms. Right now these are hardcoded for relative paths inside the mac app bundle.
12
- const webviewBinaryPath = join("native", "webview");
13
-
14
- const hash = await Updater.localInfo.hash();
15
- // Note: we use the build's hash to separate from different apps and different builds
16
- // but we also want a randomId to separate different instances of the same app
17
- // todo (yoav): since collisions can crash the app add a function that checks if the
18
- // file exists first
19
- const randomId = Math.random().toString(36).substring(7);
20
- const mainPipe = `/private/tmp/electrobun_ipc_pipe_${hash}_${randomId}_main_in`;
21
-
22
- try {
23
- execSync("mkfifo " + mainPipe);
24
- } catch (e) {
25
- console.log("pipe out already exists");
26
- }
27
-
28
- const zigProc = Bun.spawn([webviewBinaryPath], {
29
- stdin: "pipe",
30
- stdout: "pipe",
31
- env: {
32
- ...process.env,
33
- ELECTROBUN_VIEWS_FOLDER: resolve("../Resources/app/views"),
34
- MAIN_PIPE_IN: mainPipe,
35
- },
36
- onExit: (_zigProc) => {
37
- // right now just exit the whole app if the webview process dies.
38
- // in the future we probably want to try spin it back up aagain
39
- process.exit(0);
40
- },
41
- });
42
-
43
- process.on("SIGINT", (code) => {
44
- // todo (yoav): maybe send a friendly signal to the webviews to let them know
45
- // we're shutting down
46
- // clean up the webview process when the bun process dies.
47
- zigProc.kill();
48
- // fs.unlinkSync(mainPipe);
49
- process.exit();
50
- });
51
-
52
- process.on("exit", (code) => {
53
- // Note: this can happen when the bun process crashes
54
- // make sure that zigProc is killed so it doesn't linger around
55
- zigProc.kill();
56
- });
57
-
58
- const inStream = fs.createWriteStream(mainPipe, {
59
- flags: "r+",
60
- });
61
-
62
- function createStdioTransport(proc): RPCTransport {
63
- return {
64
- send(message) {
65
- try {
66
- // TODO: this is the same chunking code as browserview pipes,
67
- // should dedupe
68
- const messageString = JSON.stringify(message) + "\n";
69
-
70
- let offset = 0;
71
- while (offset < messageString.length) {
72
- const chunk = messageString.slice(offset, offset + CHUNK_SIZE);
73
- inStream.write(chunk);
74
- offset += CHUNK_SIZE;
75
- }
76
- } catch (error) {
77
- console.error("bun: failed to serialize message to zig", error);
78
- }
79
- },
80
- registerHandler(handler) {
81
- async function readStream(stream) {
82
- const reader = stream.getReader();
83
- let buffer = "";
84
-
85
- try {
86
- while (true) {
87
- const { done, value } = await reader.read();
88
- if (done) break;
89
- buffer += new TextDecoder().decode(value);
90
- let eolIndex;
91
- // Process each line contained in the buffer
92
- while ((eolIndex = buffer.indexOf("\n")) >= 0) {
93
- const line = buffer.slice(0, eolIndex).trim();
94
- buffer = buffer.slice(eolIndex + 1);
95
- if (line) {
96
- try {
97
- const event = JSON.parse(line);
98
- handler(event);
99
- } catch (error) {
100
- // Non-json things are just bubbled up to the console.
101
- console.error("zig: ", line);
102
- }
103
- }
104
- }
105
- }
106
- } catch (error) {
107
- console.error("Error reading from stream:", error);
108
- } finally {
109
- reader.releaseLock();
110
- }
111
- }
112
-
113
- readStream(proc.stdout);
114
- },
115
- };
116
- }
117
-
118
- // todo: consider renaming to TrayMenuItemConfig
119
- export type MenuItemConfig =
120
- | { type: "divider" | "separator" }
121
- | {
122
- type: "normal";
123
- label: string;
124
- tooltip?: string;
125
- action?: string;
126
- submenu?: Array<MenuItemConfig>;
127
- enabled?: boolean;
128
- checked?: boolean;
129
- hidden?: boolean;
130
- };
131
-
132
- export type ApplicationMenuItemConfig =
133
- | { type: "divider" | "separator" }
134
- | {
135
- type?: "normal";
136
- label: string;
137
- tooltip?: string;
138
- action?: string;
139
- submenu?: Array<ApplicationMenuItemConfig>;
140
- enabled?: boolean;
141
- checked?: boolean;
142
- hidden?: boolean;
143
- accelerator?: string;
144
- }
145
- | {
146
- type?: "normal";
147
- label?: string;
148
- tooltip?: string;
149
- role?: string;
150
- submenu?: Array<ApplicationMenuItemConfig>;
151
- enabled?: boolean;
152
- checked?: boolean;
153
- hidden?: boolean;
154
- accelerator?: string;
155
- };
156
-
157
- // todo (yoav): move this stuff to bun/rpc/zig.ts
158
- type ZigHandlers = RPCSchema<{
159
- requests: {
160
- createWindow: {
161
- params: {
162
- id: number;
163
- url: string | null;
164
- html: string | null;
165
- title: string;
166
- frame: {
167
- width: number;
168
- height: number;
169
- x: number;
170
- y: number;
171
- };
172
- styleMask: {
173
- Borderless: boolean;
174
- Titled: boolean;
175
- Closable: boolean;
176
- Miniaturizable: boolean;
177
- Resizable: boolean;
178
- UnifiedTitleAndToolbar: boolean;
179
- FullScreen: boolean;
180
- FullSizeContentView: boolean;
181
- UtilityWindow: boolean;
182
- DocModalWindow: boolean;
183
- NonactivatingPanel: boolean;
184
- HUDWindow: boolean;
185
- };
186
- titleBarStyle: string;
187
- };
188
- response: void;
189
- };
190
- createWebview: {
191
- params: {
192
- id: number;
193
- rpcPort: number;
194
- secretKey: string;
195
- hostWebviewId: number | null;
196
- pipePrefix: string;
197
- url: string | null;
198
- html: string | null;
199
- partition: string | null;
200
- preload: string | null;
201
- frame: {
202
- x: number;
203
- y: number;
204
- width: number;
205
- height: number;
206
- };
207
- autoResize: boolean;
208
- };
209
- response: void;
210
- };
211
-
212
- addWebviewToWindow: {
213
- params: {
214
- windowId: number;
215
- webviewId: number;
216
- };
217
- response: void;
218
- };
219
-
220
- loadURL: {
221
- params: {
222
- webviewId: number;
223
- url: string;
224
- };
225
- response: void;
226
- };
227
- loadHTML: {
228
- params: {
229
- webviewId: number;
230
- html: string;
231
- };
232
- response: void;
233
- };
234
-
235
- setTitle: {
236
- params: {
237
- winId: number;
238
- title: string;
239
- };
240
- response: void;
241
- };
242
-
243
- closeWindow: {
244
- params: {
245
- winId: number;
246
- };
247
- response: void;
248
- };
249
-
250
- // fs
251
- moveToTrash: {
252
- params: {
253
- path: string;
254
- };
255
- response: boolean;
256
- };
257
- showItemInFolder: {
258
- params: {
259
- path: string;
260
- };
261
- response: boolean;
262
- };
263
- openFileDialog: {
264
- params: {
265
- startingFolder: string | null;
266
- allowedFileTypes: string | null;
267
- canChooseFiles: boolean;
268
- canChooseDirectory: boolean;
269
- allowsMultipleSelection: boolean;
270
- };
271
- response: { openFileDialogResponse: string };
272
- };
273
-
274
- // tray and menu
275
- createTray: {
276
- params: {
277
- id: number;
278
- title: string;
279
- image: string;
280
- template: boolean;
281
- width: number;
282
- height: number;
283
- };
284
- response: void;
285
- };
286
- setTrayTitle: {
287
- params: {
288
- id: number;
289
- title: string;
290
- };
291
- response: void;
292
- };
293
- setTrayImage: {
294
- params: {
295
- id: number;
296
- image: string;
297
- };
298
- response: void;
299
- };
300
- setTrayMenu: {
301
- params: {
302
- id: number;
303
- // json string of config
304
- menuConfig: string;
305
- };
306
- response: void;
307
- };
308
- setApplicationMenu: {
309
- params: {
310
- // json string of config
311
- menuConfig: string;
312
- };
313
- response: void;
314
- };
315
- showContextMenu: {
316
- params: {
317
- // json string of config
318
- menuConfig: string;
319
- };
320
- response: void;
321
- };
322
- };
323
- }>;
324
-
325
- type BunHandlers = RPCSchema<{
326
- requests: {
327
- decideNavigation: {
328
- params: {
329
- webviewId: number;
330
- url: string;
331
- };
332
- response: {
333
- allow: boolean;
334
- };
335
- };
336
- syncRequest: {
337
- params: {
338
- webviewId: number;
339
- request: string;
340
- };
341
- response: {
342
- payload: string;
343
- };
344
- };
345
- // todo: make these messages instead of requests
346
- log: {
347
- params: {
348
- msg: string;
349
- };
350
- response: {
351
- success: boolean;
352
- };
353
- };
354
- trayEvent: {
355
- params: {
356
- id: number;
357
- action: string;
358
- };
359
- response: {
360
- success: boolean;
361
- };
362
- };
363
- applicationMenuEvent: {
364
- params: {
365
- id: number;
366
- action: string;
367
- };
368
- response: {
369
- success: boolean;
370
- };
371
- };
372
- contextMenuEvent: {
373
- params: {
374
- action: string;
375
- };
376
- response: {
377
- success: boolean;
378
- };
379
- };
380
- webviewEvent: {
381
- params: {
382
- id: number;
383
- eventName: string;
384
- detail: string;
385
- };
386
- response: {
387
- success: boolean;
388
- };
389
- };
390
- windowClose: {
391
- params: {
392
- id: number;
393
- };
394
- response: {
395
- success: boolean;
396
- };
397
- };
398
- windowMove: {
399
- params: {
400
- id: number;
401
- x: number;
402
- y: number;
403
- };
404
- response: {
405
- success: boolean;
406
- };
407
- };
408
- windowResize: {
409
- params: {
410
- id: number;
411
- x: number;
412
- y: number;
413
- width: number;
414
- height: number;
415
- };
416
- response: {
417
- success: boolean;
418
- };
419
- };
420
- };
421
- }>;
422
-
423
- const zigRPC = createRPC<BunHandlers, ZigHandlers>({
424
- transport: createStdioTransport(zigProc),
425
- requestHandler: {
426
- decideNavigation: ({ webviewId, url }) => {
427
- const willNavigate = electrobunEventEmitter.events.webview.willNavigate({
428
- url,
429
- webviewId,
430
- });
431
-
432
- let result;
433
- // global will-navigate event
434
- result = electrobunEventEmitter.emitEvent(willNavigate);
435
-
436
- result = electrobunEventEmitter.emitEvent(willNavigate, webviewId);
437
-
438
- if (willNavigate.responseWasSet) {
439
- return willNavigate.response || { allow: true };
440
- } else {
441
- return { allow: true };
442
- }
443
- },
444
- syncRequest: ({ webviewId, request: requestStr }) => {
445
- const webview = BrowserView.getById(webviewId);
446
- const { method, params } = JSON.parse(requestStr);
447
-
448
- if (!webview) {
449
- const err = `error: could not find webview with id ${webviewId}`;
450
- console.log(err);
451
- return { payload: err };
452
- }
453
-
454
- if (!method) {
455
- const err = `error: request missing a method`;
456
- console.log(err);
457
- return { payload: err };
458
- }
459
-
460
- if (!webview.syncRpc || !webview.syncRpc[method]) {
461
- const err = `error: webview does not have a handler for method ${method}`;
462
- console.log(err);
463
- return { payload: err };
464
- }
465
- console.warn("DEPRECATED: use async rpc if possible", method);
466
- const handler = webview.syncRpc[method];
467
- var response;
468
- try {
469
- response = handler(params);
470
- // Note: Stringify(undefined) returns undefined,
471
- // if we send undefined as the payload it'll crash
472
- // so send an empty string which is a better analog for
473
- // undefined json string
474
- if (response === undefined) {
475
- response = "";
476
- }
477
- } catch (err) {
478
- console.log(err);
479
- console.log("syncRPC failed with", { method, params });
480
- return { payload: String(err) };
481
- }
482
-
483
- const payload = JSON.stringify(response);
484
- return { payload };
485
- },
486
- log: ({ msg }) => {
487
- console.log("zig: ", msg);
488
- return { success: true };
489
- },
490
- trayEvent: ({ id, action }) => {
491
- const tray = Tray.getById(id);
492
- if (!tray) {
493
- return { success: true };
494
- }
495
-
496
- const event = electrobunEventEmitter.events.tray.trayClicked({
497
- id,
498
- action,
499
- });
500
-
501
- let result;
502
- // global event
503
- result = electrobunEventEmitter.emitEvent(event);
504
-
505
- result = electrobunEventEmitter.emitEvent(event, id);
506
- // Note: we don't care about the result right now
507
-
508
- return { success: true };
509
- },
510
- applicationMenuEvent: ({ id, action }) => {
511
- const event = electrobunEventEmitter.events.app.applicationMenuClicked({
512
- id,
513
- action,
514
- });
515
-
516
- let result;
517
- // global event
518
- result = electrobunEventEmitter.emitEvent(event);
519
-
520
- return { success: true };
521
- },
522
- contextMenuEvent: ({ action }) => {
523
- const event = electrobunEventEmitter.events.app.contextMenuClicked({
524
- action,
525
- });
526
-
527
- let result;
528
- // global event
529
- result = electrobunEventEmitter.emitEvent(event);
530
-
531
- return { success: true };
532
- },
533
- webviewEvent: ({ id, eventName, detail }) => {
534
- const eventMap = {
535
- "did-navigate": "didNavigate",
536
- "did-navigate-in-page": "didNavigateInPage",
537
- "did-commit-navigation": "didCommitNavigation",
538
- "dom-ready": "domReady",
539
- "new-window-open": "newWindowOpen",
540
- };
541
-
542
- // todo: the events map should use the same hyphenated names instead of camelCase
543
- const handler =
544
- electrobunEventEmitter.events.webview[eventMap[eventName]];
545
-
546
- if (!handler) {
547
- console.log(`!!!no handler for webview event ${eventName}`);
548
- return { success: false };
549
- }
550
-
551
- const event = handler({
552
- id,
553
- detail,
554
- });
555
-
556
- let result;
557
- // global event
558
- result = electrobunEventEmitter.emitEvent(event);
559
-
560
- result = electrobunEventEmitter.emitEvent(event, id);
561
- // Note: we don't care about the result right now
562
- return { success: true };
563
- },
564
- windowClose: ({ id }) => {
565
- const handler = electrobunEventEmitter.events.window.close;
566
-
567
- const event = handler({
568
- id,
569
- });
570
-
571
- let result;
572
- // global event
573
- result = electrobunEventEmitter.emitEvent(event);
574
-
575
- result = electrobunEventEmitter.emitEvent(event, id);
576
- // Note: we don't care about the result right now
577
-
578
- return { success: false };
579
- },
580
- windowMove: ({ id, x, y }) => {
581
- const handler = electrobunEventEmitter.events.window.move;
582
-
583
- const event = handler({
584
- id,
585
- x,
586
- y,
587
- });
588
-
589
- let result;
590
- // global event
591
- result = electrobunEventEmitter.emitEvent(event);
592
-
593
- result = electrobunEventEmitter.emitEvent(event, id);
594
- // Note: we don't care about the result right now
595
-
596
- return { success: false };
597
- },
598
- windowResize: ({ id, x, y, width, height }) => {
599
- const handler = electrobunEventEmitter.events.window.resize;
600
-
601
- const event = handler({
602
- id,
603
- x,
604
- y,
605
- width,
606
- height,
607
- });
608
-
609
- let result;
610
- // global event
611
- result = electrobunEventEmitter.emitEvent(event);
612
-
613
- result = electrobunEventEmitter.emitEvent(event, id);
614
- // Note: we don't care about the result right now
615
-
616
- return { success: false };
617
- },
618
- },
619
- maxRequestTime: 25000,
620
- });
621
-
622
- export { zigRPC, zigProc };
package/dist/bsdiff DELETED
Binary file
package/dist/bspatch DELETED
Binary file
package/dist/bun DELETED
Binary file
package/dist/electrobun DELETED
Binary file
package/dist/extractor DELETED
Binary file
package/dist/launcher DELETED
Binary file
package/dist/webview DELETED
Binary file