electrobun 1.0.3-beta.2 → 1.0.4-beta.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/bun.lock CHANGED
@@ -15,6 +15,8 @@
15
15
  "devDependencies": {
16
16
  "@types/archiver": "^6.0.3",
17
17
  "@types/bun": "1.1.9",
18
+ "@types/tar": "^6.1.13",
19
+ "typescript": "^5.9.3",
18
20
  },
19
21
  },
20
22
  },
@@ -39,10 +41,12 @@
39
41
 
40
42
  "@types/har-format": ["@types/har-format@1.2.16", "", {}, "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A=="],
41
43
 
42
- "@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
44
+ "@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
43
45
 
44
46
  "@types/readdir-glob": ["@types/readdir-glob@1.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg=="],
45
47
 
48
+ "@types/tar": ["@types/tar@6.1.13", "", { "dependencies": { "@types/node": "*", "minipass": "^4.0.0" } }, "sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw=="],
49
+
46
50
  "@types/webextension-polyfill": ["@types/webextension-polyfill@0.12.3", "", {}, "sha512-F58aDVSeN/MjUGazXo/cPsmR76EvqQhQ1v4x23hFjUX0cfAJYE+JBWwiOGW36/VJGGxoH74sVlRIF3z7SJCKyg=="],
47
51
 
48
52
  "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
@@ -143,7 +147,7 @@
143
147
 
144
148
  "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
145
149
 
146
- "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
150
+ "minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
147
151
 
148
152
  "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
149
153
 
@@ -199,6 +203,8 @@
199
203
 
200
204
  "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
201
205
 
206
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
207
+
202
208
  "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
203
209
 
204
210
  "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
@@ -213,12 +219,6 @@
213
219
 
214
220
  "zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="],
215
221
 
216
- "@types/readdir-glob/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
217
-
218
- "@types/ws/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
219
-
220
- "bun-types/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
221
-
222
222
  "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
223
223
 
224
224
  "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
@@ -231,12 +231,16 @@
231
231
 
232
232
  "path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
233
233
 
234
+ "png-to-ico/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
235
+
234
236
  "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
235
237
 
236
238
  "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
237
239
 
238
240
  "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
239
241
 
242
+ "tar/minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
243
+
240
244
  "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
241
245
 
242
246
  "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -0,0 +1,31 @@
1
+ // Global type declarations for Electrobun browser environment
2
+
3
+ interface ElectrobunEncryptResult {
4
+ encryptedData: string;
5
+ iv: string;
6
+ tag: string;
7
+ }
8
+
9
+ interface ElectrobunBridge {
10
+ receiveMessageFromBun: (msg: unknown) => void;
11
+ receiveInternalMessageFromBun: (msg: unknown) => void;
12
+ }
13
+
14
+ interface MessageHandler {
15
+ postMessage: (msg: string) => void;
16
+ }
17
+
18
+ declare global {
19
+ interface Window {
20
+ __electrobunWebviewId: number;
21
+ __electrobunWindowId: number;
22
+ __electrobunRpcSocketPort: number;
23
+ __electrobun?: ElectrobunBridge;
24
+ __electrobun_encrypt: (msg: string) => Promise<ElectrobunEncryptResult>;
25
+ __electrobun_decrypt: (encryptedData: string, iv: string, tag: string) => Promise<string>;
26
+ __electrobunInternalBridge?: MessageHandler;
27
+ __electrobunBunBridge?: MessageHandler;
28
+ }
29
+ }
30
+
31
+ export {};
@@ -7,27 +7,32 @@ import {
7
7
  type RPCTransport,
8
8
  createRPC,
9
9
  } from "rpc-anywhere";
10
- import { ConfigureWebviewTags } from "./webviewtag";
10
+ import { ConfigureWebviewTags, type WebviewTagElement, type WebviewEventTypes } from "./webviewtag";
11
11
  // todo: should this just be injected as a preload script?
12
12
  import { isAppRegionDrag } from "./stylesAndElements";
13
13
  import type { BuiltinBunToWebviewSchema, BuiltinWebviewToBunSchema } from "./builtinrpcSchema";
14
14
  import type { InternalWebviewHandlers, WebviewTagHandlers } from "./rpc/webview";
15
+ import "./global.d.ts";
15
16
 
16
17
  interface ElectrobunWebviewRPCSChema {
17
18
  bun: RPCSchema;
18
19
  webview: RPCSchema;
19
20
  }
20
21
 
22
+ interface RPCWithTransport {
23
+ setTransport: (transport: RPCTransport) => void;
24
+ }
25
+
21
26
  const WEBVIEW_ID = window.__electrobunWebviewId;
22
27
  const WINDOW_ID = window.__electrobunWindowId;
23
28
  const RPC_SOCKET_PORT = window.__electrobunRpcSocketPort;
24
29
 
25
30
 
26
- class Electroview<T> {
31
+ class Electroview<T extends RPCWithTransport> {
27
32
  bunSocket?: WebSocket;
28
33
  // user's custom rpc browser <-> bun
29
34
  rpc?: T;
30
- rpcHandler?: (msg: any) => void;
35
+ rpcHandler?: (msg: unknown) => void;
31
36
  // electrobun rpc browser <-> bun
32
37
  internalRpc?: any;
33
38
  internalRpcHandler?: (msg: any) => void;
@@ -106,7 +111,7 @@ class Electroview<T> {
106
111
  console.error("Socket error:", event);
107
112
  });
108
113
 
109
- socket.addEventListener("close", (event) => {
114
+ socket.addEventListener("close", (_event) => {
110
115
  // console.log("Socket closed:", event);
111
116
  });
112
117
  }
@@ -123,7 +128,7 @@ class Electroview<T> {
123
128
  // TODO: implement proper rpc-anywhere style rpc here
124
129
  // todo: this is duplicated in webviewtag.ts and should be DRYed up
125
130
  isProcessingQueue = false;
126
- sendToInternalQueue = [];
131
+ sendToInternalQueue: string[] = [];
127
132
  sendToBunInternal(message: {}) {
128
133
  try {
129
134
  const strMessage = JSON.stringify(message);
@@ -189,10 +194,10 @@ class Electroview<T> {
189
194
  });
190
195
  }
191
196
 
192
- createTransport() {
197
+ createTransport(): RPCTransport {
193
198
  const that = this;
194
199
  return {
195
- send(message) {
200
+ send(message: unknown) {
196
201
  try {
197
202
  const messageString = JSON.stringify(message);
198
203
  // console.log("sending message bunbridge", messageString);
@@ -201,7 +206,7 @@ class Electroview<T> {
201
206
  console.error("bun: failed to serialize message to webview", error);
202
207
  }
203
208
  },
204
- registerHandler(handler) {
209
+ registerHandler(handler: (msg: unknown) => void) {
205
210
  that.rpcHandler = handler;
206
211
  },
207
212
  };
@@ -270,7 +275,7 @@ class Electroview<T> {
270
275
  }
271
276
  }
272
277
 
273
- receiveMessageFromBun(msg) {
278
+ receiveMessageFromBun(msg: unknown) {
274
279
  // NOTE: in the webview messages are passed by executing ElectrobunView.receiveMessageFromBun(object)
275
280
  // so they're already parsed into an object here
276
281
  if (this.rpcHandler) {
@@ -380,7 +385,8 @@ class Electroview<T> {
380
385
  // while types in here are borked, they resolve correctly/bubble up to the defineRPC call site.
381
386
  rpc.addMessageListener(
382
387
  "*",
383
- (messageName: keyof WebviewSchema["messages"], payload) => {
388
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
389
+ (messageName: keyof WebviewSchema["messages"], payload: any) => {
384
390
  const globalHandler = messageHandlers["*"];
385
391
  if (globalHandler) {
386
392
  globalHandler(messageName, payload);
@@ -398,7 +404,7 @@ class Electroview<T> {
398
404
  }
399
405
  }
400
406
 
401
- export { type RPCSchema, createRPC, Electroview };
407
+ export { type RPCSchema, createRPC, Electroview, type WebviewTagElement, type WebviewEventTypes };
402
408
 
403
409
  const Electrobun = {
404
410
  Electroview,
@@ -1,3 +1,3 @@
1
1
  export const isAppRegionDrag = (e: MouseEvent) => {
2
- return e.target?.classList.contains("electrobun-webkit-app-region-drag");
2
+ return (e.target as HTMLElement)?.classList?.contains("electrobun-webkit-app-region-drag");
3
3
  };
@@ -1,16 +1,81 @@
1
+ import "./global.d.ts";
2
+
1
3
  type WebviewEventTypes =
2
4
  | "did-navigate"
3
5
  | "did-navigate-in-page"
4
6
  | "did-commit-navigation"
5
7
  | "dom-ready"
6
- | "host-message";
8
+ | "host-message"
9
+ | "new-window-open";
7
10
 
8
11
  type Rect = { x: number; y: number; width: number; height: number };
9
12
 
13
+ /**
14
+ * Interface representing an <electrobun-webview> custom element.
15
+ * Use this to properly type webview elements obtained via querySelector.
16
+ *
17
+ * @example
18
+ * const webview = document.querySelector('electrobun-webview') as WebviewTagElement;
19
+ * webview.loadURL('https://example.com');
20
+ * webview.toggleHidden(false);
21
+ */
22
+ interface WebviewTagElement extends HTMLElement {
23
+ // Properties
24
+ webviewId?: number;
25
+ maskSelectors: Set<string>;
26
+ transparent: boolean;
27
+ passthroughEnabled: boolean;
28
+ hidden: boolean;
29
+ hiddenMirrorMode: boolean;
30
+ partition: string | null;
31
+
32
+ // Attribute-backed properties (getters/setters)
33
+ src: string | null;
34
+ html: string | null;
35
+ preload: string | null;
36
+ renderer: 'cef' | 'native';
37
+
38
+ // Mask management
39
+ addMaskSelector(selector: string): void;
40
+ removeMaskSelector(selector: string): void;
41
+
42
+ // Navigation
43
+ canGoBack(): Promise<boolean>;
44
+ canGoForward(): Promise<boolean>;
45
+ goBack(): void;
46
+ goForward(): void;
47
+ reload(): void;
48
+ loadURL(url: string): void;
49
+ loadHTML(html: string): void;
50
+
51
+ // JavaScript execution
52
+ callAsyncJavaScript(options: { script: string }): Promise<unknown>;
53
+
54
+ // Visibility and interaction
55
+ toggleTransparent(transparent?: boolean, bypassState?: boolean): void;
56
+ togglePassthrough(enablePassthrough?: boolean, bypassState?: boolean): void;
57
+ toggleHidden(hidden?: boolean, bypassState?: boolean): void;
58
+
59
+ // Events - listener receives a CustomEvent with detail property
60
+ on(event: WebviewEventTypes, listener: (event: CustomEvent) => void): void;
61
+ off(event: WebviewEventTypes, listener: (event: CustomEvent) => void): void;
62
+ emit(event: WebviewEventTypes, detail: unknown): void;
63
+
64
+ // Dimension sync
65
+ syncDimensions(force?: boolean): void;
66
+
67
+ // Navigation rules
68
+ setNavigationRules(rules: string[]): void;
69
+
70
+ // Find in page
71
+ findInPage(searchText: string, options?: { forward?: boolean; matchCase?: boolean }): void;
72
+ stopFindInPage(): void;
73
+ }
74
+
10
75
  const ConfigureWebviewTags = (
11
76
  enableWebviewTags: boolean,
12
- internalRpc: (params: any) => any,
13
- bunRpc: (params: any) => any
77
+ internalRpc: unknown,
78
+ bunRpc: unknown
14
79
  ) => {
15
80
  if (!enableWebviewTags) {
16
81
  return;
@@ -398,12 +463,12 @@ const ConfigureWebviewTags = (
398
463
  return ["src", "html", "preload", "class", "style"];
399
464
  }
400
465
 
401
- attributeChangedCallback(name, oldValue, newValue) {
402
- if (name === "src" && oldValue !== newValue) {
466
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
467
+ if (name === "src" && oldValue !== newValue && newValue !== null) {
403
468
  this.updateIFrameSrc(newValue);
404
- } else if (name === "html" && oldValue !== newValue) {
469
+ } else if (name === "html" && oldValue !== newValue && newValue !== null) {
405
470
  this.updateIFrameHtml(newValue);
406
- } else if (name === "preload" && oldValue !== newValue) {
471
+ } else if (name === "preload" && oldValue !== newValue && newValue !== null) {
407
472
  this.updateIFramePreload(newValue);
408
473
  } else {
409
474
  this.syncDimensions();
@@ -638,4 +703,4 @@ electrobun-webview {
638
703
  }
639
704
  };
640
705
 
641
- export { ConfigureWebviewTags };
706
+ export { ConfigureWebviewTags, type WebviewTagElement, type WebviewEventTypes };
@@ -1,6 +1,20 @@
1
1
  import { ffi, type ApplicationMenuItemConfig } from "../proc/native";
2
2
  import electrobunEventEmitter from "../events/eventEmitter";
3
3
 
4
+ type NonDividerMenuItem = {
5
+ type?: "normal";
6
+ label?: string;
7
+ tooltip?: string;
8
+ action?: string;
9
+ role?: string;
10
+ data?: unknown;
11
+ submenu?: Array<ApplicationMenuItemConfig>;
12
+ enabled?: boolean;
13
+ checked?: boolean;
14
+ hidden?: boolean;
15
+ accelerator?: string;
16
+ };
17
+
4
18
  export const setApplicationMenu = (menu: Array<ApplicationMenuItemConfig>) => {
5
19
  const menuWithDefaults = menuConfigWithDefaults(menu);
6
20
  ffi.request.setApplicationMenu({
@@ -8,7 +22,7 @@ export const setApplicationMenu = (menu: Array<ApplicationMenuItemConfig>) => {
8
22
  });
9
23
  };
10
24
 
11
- export const on = (name: "application-menu-clicked", handler) => {
25
+ export const on = (name: "application-menu-clicked", handler: (event: unknown) => void) => {
12
26
  const specificName = `${name}`;
13
27
  electrobunEventEmitter.on(specificName, handler);
14
28
  };
@@ -44,24 +58,25 @@ const menuConfigWithDefaults = (
44
58
  ): Array<ApplicationMenuItemConfig> => {
45
59
  return menu.map((item) => {
46
60
  if (item.type === "divider" || item.type === "separator") {
47
- return { type: "divider" };
61
+ return { type: "divider" } as const;
48
62
  } else {
63
+ const menuItem = item as NonDividerMenuItem;
49
64
  // Use shared serialization method
50
- const actionWithDataId = ffi.internal.serializeMenuAction(item.action || "", item.data);
51
-
65
+ const actionWithDataId = ffi.internal.serializeMenuAction(menuItem.action || "", menuItem.data);
66
+
52
67
  return {
53
- label: item.label || roleLabelMap[item.role] || "",
54
- type: item.type || "normal",
68
+ label: menuItem.label || roleLabelMap[menuItem.role as keyof typeof roleLabelMap] || "",
69
+ type: menuItem.type || "normal",
55
70
  // application menus can either have an action or a role. not both.
56
- ...(item.role ? { role: item.role } : { action: actionWithDataId }),
71
+ ...(menuItem.role ? { role: menuItem.role } : { action: actionWithDataId }),
57
72
  // default enabled to true unless explicitly set to false
58
- enabled: item.enabled === false ? false : true,
59
- checked: Boolean(item.checked),
60
- hidden: Boolean(item.hidden),
61
- tooltip: item.tooltip || undefined,
62
- accelerator: item.accelerator || undefined,
63
- ...(item.submenu
64
- ? { submenu: menuConfigWithDefaults(item.submenu) }
73
+ enabled: menuItem.enabled === false ? false : true,
74
+ checked: Boolean(menuItem.checked),
75
+ hidden: Boolean(menuItem.hidden),
76
+ tooltip: menuItem.tooltip || undefined,
77
+ accelerator: menuItem.accelerator || undefined,
78
+ ...(menuItem.submenu
79
+ ? { submenu: menuConfigWithDefaults(menuItem.submenu) }
65
80
  : {}),
66
81
  };
67
82
  }
@@ -1,6 +1,5 @@
1
- import { native, toCString, ffi } from "../proc/native";
1
+ import { native, toCString, ffi } from "../proc/native";
2
2
  import * as fs from "fs";
3
- import { execSync } from "child_process";
4
3
  import electrobunEventEmitter from "../events/eventEmitter";
5
4
  import {
6
5
  type RPCSchema,
@@ -12,18 +11,15 @@ import {
12
11
  } from "rpc-anywhere";
13
12
  import { Updater } from "./Updater";
14
13
  import { BuildConfig } from "./BuildConfig";
15
- import type { BuiltinBunToWebviewSchema,BuiltinWebviewToBunSchema } from "../../browser/builtinrpcSchema";
16
14
  import { rpcPort, sendMessageToWebviewViaSocket } from "./Socket";
17
15
  import { randomBytes } from "crypto";
18
- import {FFIType, type Pointer} from 'bun:ffi';
16
+ import { type Pointer } from 'bun:ffi';
19
17
 
20
18
  const BrowserViewMap: {
21
19
  [id: number]: BrowserView<any>;
22
20
  } = {};
23
21
  let nextWebviewId = 1;
24
22
 
25
- const CHUNK_SIZE = 1024 * 4; // 4KB
26
-
27
23
  type BrowserViewOptions<T = undefined> = {
28
24
  url: string | null;
29
25
  html: string | null;
@@ -50,6 +46,10 @@ interface ElectrobunWebviewRPCSChema {
50
46
  webview: RPCSchema;
51
47
  }
52
48
 
49
+ interface RPCWithTransport {
50
+ setTransport: (transport: { send: (msg: unknown) => void; registerHandler: (handler: (msg: unknown) => void) => void }) => void;
51
+ }
52
+
53
53
  const hash = await Updater.localInfo.hash();
54
54
  const buildConfig = await BuildConfig.get();
55
55
 
@@ -69,12 +69,12 @@ const defaultOptions: Partial<BrowserViewOptions> = {
69
69
  // but we also want a randomId to separate different instances of the same app
70
70
  const randomId = Math.random().toString(36).substring(7);
71
71
 
72
- export class BrowserView<T> {
72
+ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
73
73
  id: number = nextWebviewId++;
74
- ptr: Pointer;
74
+ ptr!: Pointer;
75
75
  hostWebviewId?: number;
76
- windowId: number;
77
- renderer: 'cef' | 'native';
76
+ windowId!: number;
77
+ renderer!: 'cef' | 'native';
78
78
  url: string | null = null;
79
79
  html: string | null = null;
80
80
  preload: string | null = null;
@@ -91,37 +91,40 @@ export class BrowserView<T> {
91
91
  width: 800,
92
92
  height: 600,
93
93
  };
94
- pipePrefix: string;
95
- inStream: fs.WriteStream;
96
- outStream: ReadableStream<Uint8Array>;
97
- secretKey: Uint8Array;
98
- rpc?: T;
99
- rpcHandler?: (msg: any) => void;
100
- navigationRules: string | null;
94
+ pipePrefix!: string;
95
+ inStream!: fs.WriteStream;
96
+ outStream!: ReadableStream<Uint8Array>;
97
+ secretKey!: Uint8Array;
98
+ rpc?: T;
99
+ rpcHandler?: (msg: unknown) => void;
100
+ navigationRules: string | null = null;
101
101
 
102
102
  constructor(options: Partial<BrowserViewOptions<T>> = defaultOptions) {
103
- // const rpc = options.rpc;
104
-
103
+ // const rpc = options.rpc;
104
+
105
105
  this.url = options.url || defaultOptions.url || null;
106
106
  this.html = options.html || defaultOptions.html || null;
107
107
  this.preload = options.preload || defaultOptions.preload || null;
108
- this.frame = options.frame
109
- ? { ...defaultOptions.frame, ...options.frame }
110
- : { ...defaultOptions.frame };
108
+ this.frame = {
109
+ x: options.frame?.x ?? defaultOptions.frame!.x,
110
+ y: options.frame?.y ?? defaultOptions.frame!.y,
111
+ width: options.frame?.width ?? defaultOptions.frame!.width,
112
+ height: options.frame?.height ?? defaultOptions.frame!.height,
113
+ };
111
114
  this.rpc = options.rpc;
112
- this.secretKey = new Uint8Array(randomBytes(32));
115
+ this.secretKey = new Uint8Array(randomBytes(32));
113
116
  this.partition = options.partition || null;
114
117
  // todo (yoav): since collisions can crash the app add a function that checks if the
115
118
  // file exists first
116
119
  this.pipePrefix = `/private/tmp/electrobun_ipc_pipe_${hash}_${randomId}_${this.id}`;
117
120
  this.hostWebviewId = options.hostWebviewId;
118
- this.windowId = options.windowId;
121
+ this.windowId = options.windowId ?? 0;
119
122
  this.autoResize = options.autoResize === false ? false : true;
120
123
  this.navigationRules = options.navigationRules || null;
121
- this.renderer = options.renderer || defaultOptions.renderer;
124
+ this.renderer = options.renderer ?? defaultOptions.renderer ?? 'native';
122
125
 
123
126
  BrowserViewMap[this.id] = this;
124
- this.ptr = this.init();
127
+ this.ptr = this.init() as Pointer;
125
128
 
126
129
  // If HTML content was provided, load it after webview creation
127
130
  if (this.html) {
@@ -168,18 +171,18 @@ export class BrowserView<T> {
168
171
 
169
172
  }
170
173
 
171
- createStreams() {
174
+ createStreams() {
172
175
  if (!this.rpc) {
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
173
177
  this.rpc = BrowserView.defineRPC({
174
178
  handlers: { requests: {}, messages: {} },
175
- });
179
+ }) as any;
176
180
  }
177
-
178
- this.rpc.setTransport(this.createTransport());
179
-
181
+
182
+ this.rpc!.setTransport(this.createTransport());
180
183
  }
181
184
 
182
- sendMessageToWebviewViaExecute(jsonMessage) {
185
+ sendMessageToWebviewViaExecute(jsonMessage: unknown) {
183
186
  const stringifiedMessage =
184
187
  typeof jsonMessage === "string"
185
188
  ? jsonMessage
@@ -189,7 +192,7 @@ export class BrowserView<T> {
189
192
  this.executeJavascript(wrappedMessage);
190
193
  }
191
194
 
192
- sendInternalMessageViaExecute(jsonMessage) {
195
+ sendInternalMessageViaExecute(jsonMessage: unknown) {
193
196
  const stringifiedMessage =
194
197
  typeof jsonMessage === "string"
195
198
  ? jsonMessage
@@ -258,7 +261,7 @@ export class BrowserView<T> {
258
261
  | "download-progress"
259
262
  | "download-completed"
260
263
  | "download-failed",
261
- handler
264
+ handler: (event: unknown) => void
262
265
  ) {
263
266
  const specificName = `${name}-${this.id}`;
264
267
  electrobunEventEmitter.on(specificName, handler);
@@ -280,8 +283,8 @@ export class BrowserView<T> {
280
283
  }
281
284
  }
282
285
  },
283
- registerHandler(handler) {
284
- that.rpcHandler = handler;
286
+ registerHandler(handler: (msg: unknown) => void) {
287
+ that.rpcHandler = handler;
285
288
  },
286
289
  };
287
290
  };
@@ -342,7 +345,8 @@ export class BrowserView<T> {
342
345
  messages: WebviewSchema["messages"];
343
346
  };
344
347
 
345
- type mixedBunSchema = {
348
+ type mixedBunSchema = {
349
+ requests: WebviewSchema["requests"];
346
350
  messages: BunSchema["messages"];
347
351
  };
348
352
 
@@ -367,7 +371,8 @@ export class BrowserView<T> {
367
371
  // while types in here are borked, they resolve correctly/bubble up to the defineRPC call site.
368
372
  rpc.addMessageListener(
369
373
  "*",
370
- (messageName: keyof BunSchema["messages"], payload) => {
374
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
375
+ (messageName: keyof BunSchema["messages"], payload: any) => {
371
376
  const globalHandler = messageHandlers["*"];
372
377
  if (globalHandler) {
373
378
  globalHandler(messageName, payload);
@@ -1,8 +1,7 @@
1
1
  import { ffi } from "../proc/native";
2
2
  import electrobunEventEmitter from "../events/eventEmitter";
3
3
  import { BrowserView } from "./BrowserView";
4
- import { type RPC } from "rpc-anywhere";
5
- import {FFIType} from 'bun:ffi'
4
+ import { type Pointer } from 'bun:ffi';
6
5
  import { BuildConfig } from "./BuildConfig";
7
6
 
8
7
  const buildConfig = await BuildConfig.get();
@@ -50,18 +49,23 @@ const defaultOptions: WindowOptionsType = {
50
49
  navigationRules: null,
51
50
  };
52
51
 
53
- export const BrowserWindowMap = {};
52
+ export const BrowserWindowMap: { [id: number]: BrowserWindow<RPCWithTransport> } = {};
54
53
 
55
- export class BrowserWindow<T> {
54
+ interface RPCWithTransport {
55
+ setTransport: (transport: { send: (msg: unknown) => void; registerHandler: (handler: (msg: unknown) => void) => void }) => void;
56
+ }
57
+
58
+ export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
56
59
  id: number = nextWindowId++;
57
- ptr: FFIType.ptr;
60
+ ptr!: Pointer;
58
61
  title: string = "Electrobun";
59
62
  state: "creating" | "created" = "creating";
60
63
  url: string | null = null;
61
64
  html: string | null = null;
62
65
  preload: string | null = null;
63
- renderer: 'native' | 'cef';
66
+ renderer: 'native' | 'cef' = 'native';
64
67
  transparent: boolean = false;
68
+ navigationRules: string | null = null;
65
69
  frame: {
66
70
  x: number;
67
71
  y: number;
@@ -74,7 +78,7 @@ export class BrowserWindow<T> {
74
78
  height: 600,
75
79
  };
76
80
  // todo (yoav): make this an array of ids or something
77
- webviewId: number;
81
+ webviewId!: number;
78
82
 
79
83
  constructor(options: Partial<WindowOptionsType<T>> = defaultOptions) {
80
84
  this.title = options.title || "New Window";
@@ -139,7 +143,7 @@ export class BrowserWindow<T> {
139
143
  },
140
144
  titleBarStyle: titleBarStyle || "default",
141
145
  transparent: transparent ?? false,
142
- });
146
+ }) as Pointer;
143
147
 
144
148
  BrowserWindowMap[this.id] = this;
145
149
 
@@ -280,7 +284,7 @@ export class BrowserWindow<T> {
280
284
 
281
285
  // todo (yoav): move this to a class that also has off, append, prepend, etc.
282
286
  // name should only allow browserWindow events
283
- on(name, handler) {
287
+ on(name: string, handler: (event: unknown) => void) {
284
288
  const specificName = `${name}-${this.id}`;
285
289
  electrobunEventEmitter.on(specificName, handler);
286
290
  }