electrobun 1.15.1 → 1.16.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/bin/electrobun.cjs +5 -1
- package/bun.lock +55 -0
- package/dist/api/browser/webviewtag.ts +3 -0
- package/dist/api/bun/ElectrobunConfig.ts +53 -9
- package/dist/api/bun/core/BrowserView.ts +23 -13
- package/dist/api/bun/core/BrowserWindow.ts +41 -2
- package/dist/api/bun/core/ContextMenu.ts +5 -1
- package/dist/api/bun/core/Tray.ts +59 -7
- package/dist/api/bun/core/Updater.ts +4 -4
- package/dist/api/bun/core/Utils.ts +8 -0
- package/dist/api/bun/events/ApplicationEvents.ts +1 -0
- package/dist/api/bun/events/windowEvents.ts +1 -0
- package/dist/api/bun/preload/.generated/compiled.ts +1 -1
- package/dist/api/bun/preload/dragRegions.ts +8 -0
- package/dist/api/bun/preload/webviewTag.ts +6 -0
- package/dist/api/bun/proc/native.ts +159 -6
- package/dist/api/shared/electrobun-version.ts +2 -0
- package/dist/api/shared/rpc.ts +3 -0
- package/package.json +2 -1
- package/src/cli/index.ts +104 -85
package/src/cli/index.ts
CHANGED
|
@@ -5,12 +5,11 @@ import {
|
|
|
5
5
|
readFileSync,
|
|
6
6
|
writeFileSync,
|
|
7
7
|
cpSync,
|
|
8
|
-
|
|
8
|
+
rmSync,
|
|
9
9
|
mkdirSync,
|
|
10
10
|
createWriteStream,
|
|
11
11
|
unlinkSync,
|
|
12
12
|
readdirSync,
|
|
13
|
-
rmSync,
|
|
14
13
|
symlinkSync,
|
|
15
14
|
statSync,
|
|
16
15
|
copyFileSync,
|
|
@@ -21,6 +20,7 @@ import * as readline from "readline";
|
|
|
21
20
|
import { OS, ARCH } from "../shared/platform";
|
|
22
21
|
import { DEFAULT_CEF_VERSION_STRING } from "../shared/cef-version";
|
|
23
22
|
import { BUN_VERSION } from "../shared/bun-version";
|
|
23
|
+
import { ELECTROBUN_VERSION } from "../shared/electrobun-version";
|
|
24
24
|
import {
|
|
25
25
|
getAppFileName,
|
|
26
26
|
getBundleFileName,
|
|
@@ -74,7 +74,21 @@ const indexOfElectrobun = process.argv.findIndex((arg) =>
|
|
|
74
74
|
);
|
|
75
75
|
const commandArg = process.argv[indexOfElectrobun + 1] || "build";
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
// Walk up from projectRoot to find electrobun in node_modules (supports hoisted monorepo layouts)
|
|
78
|
+
function resolveElectrobunDir(): string {
|
|
79
|
+
let dir = projectRoot;
|
|
80
|
+
while (dir !== dirname(dir)) {
|
|
81
|
+
const candidate = join(dir, "node_modules", "electrobun");
|
|
82
|
+
if (existsSync(join(candidate, "package.json"))) {
|
|
83
|
+
return candidate;
|
|
84
|
+
}
|
|
85
|
+
dir = dirname(dir);
|
|
86
|
+
}
|
|
87
|
+
return join(projectRoot, "node_modules", "electrobun");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const ELECTROBUN_DEP_PATH = resolveElectrobunDir();
|
|
91
|
+
const ELECTROBUN_CACHE_PATH = join(dirname(ELECTROBUN_DEP_PATH), ".electrobun-cache");
|
|
78
92
|
|
|
79
93
|
// When debugging electrobun with the example app use the builds (dev or release) right from the source folder
|
|
80
94
|
// For developers using electrobun cli via npm use the release versions in /dist
|
|
@@ -182,18 +196,7 @@ async function ensureCoreDependencies(
|
|
|
182
196
|
);
|
|
183
197
|
console.log(`Downloading core binaries for ${platformOS}-${platformArch}...`);
|
|
184
198
|
|
|
185
|
-
|
|
186
|
-
const packageJsonPath = join(ELECTROBUN_DEP_PATH, "package.json");
|
|
187
|
-
let version = "latest";
|
|
188
|
-
|
|
189
|
-
if (existsSync(packageJsonPath)) {
|
|
190
|
-
try {
|
|
191
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
192
|
-
version = `v${packageJson.version}`;
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.warn("Could not read package version, using latest");
|
|
195
|
-
}
|
|
196
|
-
}
|
|
199
|
+
const version = `v${ELECTROBUN_VERSION}`;
|
|
197
200
|
|
|
198
201
|
const platformName =
|
|
199
202
|
platformOS === "macos" ? "darwin" : platformOS === "win" ? "win" : "linux";
|
|
@@ -366,13 +369,7 @@ function getEffectiveCEFDir(
|
|
|
366
369
|
cefVersion?: string,
|
|
367
370
|
): string {
|
|
368
371
|
if (cefVersion) {
|
|
369
|
-
return join(
|
|
370
|
-
projectRoot,
|
|
371
|
-
"node_modules",
|
|
372
|
-
".electrobun-cache",
|
|
373
|
-
"cef-override",
|
|
374
|
-
`${platformOS}-${platformArch}`,
|
|
375
|
-
);
|
|
372
|
+
return join(ELECTROBUN_CACHE_PATH, "cef-override", `${platformOS}-${platformArch}`);
|
|
376
373
|
}
|
|
377
374
|
return getPlatformPaths(platformOS, platformArch).CEF_DIR;
|
|
378
375
|
}
|
|
@@ -491,13 +488,7 @@ async function ensureBunBinary(
|
|
|
491
488
|
}
|
|
492
489
|
|
|
493
490
|
const binExt = targetOS === "win" ? ".exe" : "";
|
|
494
|
-
const overrideDir = join(
|
|
495
|
-
projectRoot,
|
|
496
|
-
"node_modules",
|
|
497
|
-
".electrobun-cache",
|
|
498
|
-
"bun-override",
|
|
499
|
-
`${targetOS}-${targetArch}`,
|
|
500
|
-
);
|
|
491
|
+
const overrideDir = join(ELECTROBUN_CACHE_PATH, "bun-override", `${targetOS}-${targetArch}`);
|
|
501
492
|
const overrideBinary = join(overrideDir, `bun${binExt}`);
|
|
502
493
|
const versionFile = join(overrideDir, ".bun-version");
|
|
503
494
|
|
|
@@ -557,13 +548,7 @@ async function downloadCustomBun(
|
|
|
557
548
|
}
|
|
558
549
|
|
|
559
550
|
const binExt = platformOS === "win" ? ".exe" : "";
|
|
560
|
-
const overrideDir = join(
|
|
561
|
-
projectRoot,
|
|
562
|
-
"node_modules",
|
|
563
|
-
".electrobun-cache",
|
|
564
|
-
"bun-override",
|
|
565
|
-
`${platformOS}-${platformArch}`,
|
|
566
|
-
);
|
|
551
|
+
const overrideDir = join(ELECTROBUN_CACHE_PATH, "bun-override", `${platformOS}-${platformArch}`);
|
|
567
552
|
const overrideBinary = join(overrideDir, `bun${binExt}`);
|
|
568
553
|
const bunUrl = `https://github.com/oven-sh/bun/releases/download/bun-v${bunVersion}/${bunUrlSegment}`;
|
|
569
554
|
|
|
@@ -628,7 +613,7 @@ async function downloadCustomBun(
|
|
|
628
613
|
{ stdio: "inherit" },
|
|
629
614
|
);
|
|
630
615
|
} else {
|
|
631
|
-
execSync(`unzip -o
|
|
616
|
+
execSync(`unzip -o ${escapePathForTerminal(tempZipPath)} -d ${escapePathForTerminal(overrideDir)}`, {
|
|
632
617
|
stdio: "inherit",
|
|
633
618
|
});
|
|
634
619
|
}
|
|
@@ -645,7 +630,7 @@ async function downloadCustomBun(
|
|
|
645
630
|
|
|
646
631
|
// Set execute permissions on non-Windows
|
|
647
632
|
if (platformOS !== "win") {
|
|
648
|
-
execSync(`chmod +x
|
|
633
|
+
execSync(`chmod +x ${escapePathForTerminal(overrideBinary)}`);
|
|
649
634
|
}
|
|
650
635
|
|
|
651
636
|
// Write version stamp
|
|
@@ -735,18 +720,7 @@ async function ensureCEFDependencies(
|
|
|
735
720
|
`CEF dependencies not found for ${platformOS}-${platformArch}, downloading...`,
|
|
736
721
|
);
|
|
737
722
|
|
|
738
|
-
|
|
739
|
-
const packageJsonPath = join(ELECTROBUN_DEP_PATH, "package.json");
|
|
740
|
-
let version = "latest";
|
|
741
|
-
|
|
742
|
-
if (existsSync(packageJsonPath)) {
|
|
743
|
-
try {
|
|
744
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
745
|
-
version = `v${packageJson.version}`;
|
|
746
|
-
} catch (error) {
|
|
747
|
-
console.warn("Could not read package version, using latest");
|
|
748
|
-
}
|
|
749
|
-
}
|
|
723
|
+
const version = `v${ELECTROBUN_VERSION}`;
|
|
750
724
|
|
|
751
725
|
const platformName =
|
|
752
726
|
platformOS === "macos" ? "darwin" : platformOS === "win" ? "win" : "linux";
|
|
@@ -1386,6 +1360,7 @@ const defaultConfig = {
|
|
|
1386
1360
|
locales: undefined as string[] | "*" | undefined, // ICU locales subset (Linux/Windows)
|
|
1387
1361
|
mac: {
|
|
1388
1362
|
codesign: false,
|
|
1363
|
+
createDmg: true,
|
|
1389
1364
|
notarize: false,
|
|
1390
1365
|
bundleCEF: false,
|
|
1391
1366
|
bundleWGPU: false,
|
|
@@ -1398,21 +1373,21 @@ const defaultConfig = {
|
|
|
1398
1373
|
} as Record<string, boolean | string>,
|
|
1399
1374
|
icons: "icon.iconset",
|
|
1400
1375
|
defaultRenderer: undefined as "native" | "cef" | undefined,
|
|
1401
|
-
chromiumFlags: undefined as Record<string, string |
|
|
1376
|
+
chromiumFlags: undefined as Record<string, string | boolean> | undefined,
|
|
1402
1377
|
},
|
|
1403
1378
|
win: {
|
|
1404
1379
|
bundleCEF: false,
|
|
1405
1380
|
bundleWGPU: false,
|
|
1406
1381
|
icon: undefined as string | undefined,
|
|
1407
1382
|
defaultRenderer: undefined as "native" | "cef" | undefined,
|
|
1408
|
-
chromiumFlags: undefined as Record<string, string |
|
|
1383
|
+
chromiumFlags: undefined as Record<string, string | boolean> | undefined,
|
|
1409
1384
|
},
|
|
1410
1385
|
linux: {
|
|
1411
1386
|
bundleCEF: false,
|
|
1412
1387
|
bundleWGPU: false,
|
|
1413
1388
|
icon: undefined as string | undefined,
|
|
1414
1389
|
defaultRenderer: undefined as "native" | "cef" | undefined,
|
|
1415
|
-
chromiumFlags: undefined as Record<string, string |
|
|
1390
|
+
chromiumFlags: undefined as Record<string, string | boolean> | undefined,
|
|
1416
1391
|
},
|
|
1417
1392
|
bun: {
|
|
1418
1393
|
entrypoint: "src/bun/index.ts",
|
|
@@ -1480,7 +1455,11 @@ function escapeXml(str: string): string {
|
|
|
1480
1455
|
|
|
1481
1456
|
// Helper functions
|
|
1482
1457
|
function escapePathForTerminal(path: string): string {
|
|
1483
|
-
|
|
1458
|
+
if (OS === "win") {
|
|
1459
|
+
return `"${path.replace(/"/g, '""')}"`;
|
|
1460
|
+
} else {
|
|
1461
|
+
return `'${path.replace(/'/g, "'\\''")}'`;
|
|
1462
|
+
}
|
|
1484
1463
|
}
|
|
1485
1464
|
|
|
1486
1465
|
/**
|
|
@@ -2021,7 +2000,7 @@ Categories=Utility;Application;
|
|
|
2021
2000
|
|
|
2022
2001
|
// refresh build folder
|
|
2023
2002
|
if (existsSync(buildFolder)) {
|
|
2024
|
-
|
|
2003
|
+
rmSync(buildFolder, { recursive: true });
|
|
2025
2004
|
}
|
|
2026
2005
|
mkdirSync(buildFolder, { recursive: true });
|
|
2027
2006
|
// bundle bun to build/bun
|
|
@@ -2629,7 +2608,7 @@ Categories=Utility;Application;
|
|
|
2629
2608
|
// Fallback to copying the file
|
|
2630
2609
|
cpSync(cefFilePath, mainDirPath, { dereference: true });
|
|
2631
2610
|
console.log(
|
|
2632
|
-
`Fallback:
|
|
2611
|
+
`Fallback: Copying CEF library to main directory: ${soFile}`,
|
|
2633
2612
|
);
|
|
2634
2613
|
}
|
|
2635
2614
|
}
|
|
@@ -3053,7 +3032,7 @@ Categories=Utility;Application;
|
|
|
3053
3032
|
console.log("✓ Created app.asar");
|
|
3054
3033
|
|
|
3055
3034
|
// Remove the entire app folder since it's now packed in ASAR
|
|
3056
|
-
|
|
3035
|
+
rmSync(appDirPath, { recursive: true });
|
|
3057
3036
|
console.log("✓ Removed app/ folder (now in ASAR)");
|
|
3058
3037
|
}
|
|
3059
3038
|
}
|
|
@@ -3212,7 +3191,7 @@ Categories=Utility;Application;
|
|
|
3212
3191
|
|
|
3213
3192
|
// Remove the app bundle folder after tarring (except on Linux where it might be needed for dev)
|
|
3214
3193
|
if (targetOS !== "linux" || buildEnvironment !== "dev") {
|
|
3215
|
-
|
|
3194
|
+
rmSync(appBundleFolderPath, { recursive: true });
|
|
3216
3195
|
}
|
|
3217
3196
|
|
|
3218
3197
|
// generate bsdiff
|
|
@@ -3516,7 +3495,7 @@ Categories=Utility;Application;
|
|
|
3516
3495
|
}
|
|
3517
3496
|
|
|
3518
3497
|
// DMG creation for macOS only
|
|
3519
|
-
if (targetOS === "macos") {
|
|
3498
|
+
if (targetOS === "macos" && config.build.mac?.createDmg !== false) {
|
|
3520
3499
|
console.log("creating dmg...");
|
|
3521
3500
|
const finalDmgPath = join(buildFolder, `${appFileName}.dmg`);
|
|
3522
3501
|
// NOTE: For some ungodly reason using the bare name in CI can conflict with some mysterious
|
|
@@ -3586,6 +3565,9 @@ Categories=Utility;Application;
|
|
|
3586
3565
|
}
|
|
3587
3566
|
}
|
|
3588
3567
|
} else {
|
|
3568
|
+
if (targetOS === "macos") {
|
|
3569
|
+
console.log("skipping dmg");
|
|
3570
|
+
}
|
|
3589
3571
|
// For Windows and Linux, add the self-extracting bundle directly
|
|
3590
3572
|
// @ts-expect-error - reserved for future use
|
|
3591
3573
|
const _platformBundlePath = join(
|
|
@@ -3639,7 +3621,7 @@ Categories=Utility;Application;
|
|
|
3639
3621
|
console.log("creating artifacts folder...");
|
|
3640
3622
|
if (existsSync(artifactFolder)) {
|
|
3641
3623
|
console.info("deleting artifact folder: ", artifactFolder);
|
|
3642
|
-
|
|
3624
|
+
rmSync(artifactFolder, { recursive: true });
|
|
3643
3625
|
}
|
|
3644
3626
|
|
|
3645
3627
|
mkdirSync(artifactFolder, { recursive: true });
|
|
@@ -4585,7 +4567,7 @@ Categories=Utility;Application;
|
|
|
4585
4567
|
} else if (entry.isFile()) {
|
|
4586
4568
|
// Check if it's an executable or library
|
|
4587
4569
|
try {
|
|
4588
|
-
const fileInfo = execSync(`file -b
|
|
4570
|
+
const fileInfo = execSync(`file -b ${escapePathForTerminal(fullPath)}`, {
|
|
4589
4571
|
encoding: "utf8",
|
|
4590
4572
|
}).trim();
|
|
4591
4573
|
if (
|
|
@@ -4638,6 +4620,28 @@ Categories=Utility;Application;
|
|
|
4638
4620
|
}
|
|
4639
4621
|
}
|
|
4640
4622
|
|
|
4623
|
+
// Sign .node native modules in Resources/app/bun
|
|
4624
|
+
const resourcesPath = join(contentsPath, "Resources", "app", "bun");
|
|
4625
|
+
if (existsSync(resourcesPath)) {
|
|
4626
|
+
console.log("Signing native modules in Resources/app/bun...");
|
|
4627
|
+
try {
|
|
4628
|
+
const nodeFiles = execSync(`find ${escapePathForTerminal(resourcesPath)} -name "*.node" -type f`, {
|
|
4629
|
+
encoding: "utf8",
|
|
4630
|
+
}).trim().split("\n").filter(Boolean);
|
|
4631
|
+
|
|
4632
|
+
for (const nodeFile of nodeFiles) {
|
|
4633
|
+
if (nodeFile) {
|
|
4634
|
+
console.log(`Signing native module: ${nodeFile.replace(resourcesPath + "/", "")}`);
|
|
4635
|
+
execSync(
|
|
4636
|
+
`codesign --force --verbose --timestamp --sign "${ELECTROBUN_DEVELOPER_ID}" --options runtime ${escapePathForTerminal(nodeFile)}`,
|
|
4637
|
+
);
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
} catch (err) {
|
|
4641
|
+
console.error("Error signing native modules:", err);
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
|
|
4641
4645
|
// Note: main.js is now in Resources and will be automatically sealed when signing the app bundle
|
|
4642
4646
|
|
|
4643
4647
|
// Sign the main executable (launcher) - this should use the app's bundle identifier, not "launcher"
|
|
@@ -4700,43 +4704,58 @@ Categories=Utility;Application;
|
|
|
4700
4704
|
// }
|
|
4701
4705
|
|
|
4702
4706
|
const ELECTROBUN_APPLEID = process.env["ELECTROBUN_APPLEID"];
|
|
4703
|
-
|
|
4704
|
-
if (!ELECTROBUN_APPLEID) {
|
|
4705
|
-
console.error("Env var ELECTROBUN_APPLEID is required to notarize");
|
|
4706
|
-
process.exit(1);
|
|
4707
|
-
}
|
|
4708
|
-
|
|
4709
4707
|
const ELECTROBUN_APPLEIDPASS = process.env["ELECTROBUN_APPLEIDPASS"];
|
|
4708
|
+
const ELECTROBUN_TEAMID = process.env["ELECTROBUN_TEAMID"];
|
|
4710
4709
|
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
}
|
|
4710
|
+
const ELECTROBUN_APPLEAPIISSUER = process.env["ELECTROBUN_APPLEAPIISSUER"];
|
|
4711
|
+
const ELECTROBUN_APPLEAPIKEY = process.env["ELECTROBUN_APPLEAPIKEY"];
|
|
4712
|
+
const ELECTROBUN_APPLEAPIKEYPATH = process.env["ELECTROBUN_APPLEAPIKEYPATH"];
|
|
4715
4713
|
|
|
4716
|
-
const
|
|
4714
|
+
const useApiKey = ELECTROBUN_APPLEAPIISSUER && ELECTROBUN_APPLEAPIKEY && ELECTROBUN_APPLEAPIKEYPATH;
|
|
4715
|
+
const useAppleId = ELECTROBUN_APPLEID && ELECTROBUN_APPLEIDPASS && ELECTROBUN_TEAMID;
|
|
4717
4716
|
|
|
4718
|
-
if (!
|
|
4719
|
-
console.error("
|
|
4717
|
+
if (!useApiKey && !useAppleId) {
|
|
4718
|
+
console.error("Provide either App Store Connect API key credentials (ELECTROBUN_APPLEAPIISSUER, ELECTROBUN_APPLEAPIKEY, ELECTROBUN_APPLEAPIKEYPATH) or Apple ID credentials (ELECTROBUN_APPLEID, ELECTROBUN_APPLEIDPASS, ELECTROBUN_TEAMID) to notarize");
|
|
4720
4719
|
process.exit(1);
|
|
4721
4720
|
}
|
|
4722
4721
|
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4722
|
+
let statusInfo: string;
|
|
4723
|
+
if (useApiKey) {
|
|
4724
|
+
if (!existsSync(ELECTROBUN_APPLEAPIKEYPATH)) {
|
|
4725
|
+
console.error(`ELECTROBUN_APPLEAPIKEYPATH does not exist: ${ELECTROBUN_APPLEAPIKEYPATH}`);
|
|
4726
|
+
process.exit(1);
|
|
4727
|
+
}
|
|
4728
|
+
statusInfo = execSync(
|
|
4729
|
+
`xcrun notarytool submit --key "${ELECTROBUN_APPLEAPIKEYPATH}" --key-id "${ELECTROBUN_APPLEAPIKEY}" --issuer "${ELECTROBUN_APPLEAPIISSUER}" --wait ${escapePathForTerminal(
|
|
4730
|
+
fileToNotarize,
|
|
4731
|
+
)}`,
|
|
4732
|
+
).toString();
|
|
4733
|
+
} else {
|
|
4734
|
+
// notarize
|
|
4735
|
+
// todo (yoav): follow up on options here like --s3-acceleration and --webhook
|
|
4736
|
+
// todo (yoav): don't use execSync since it's blocking and we'll only see the output at the end
|
|
4737
|
+
statusInfo = execSync(
|
|
4738
|
+
`xcrun notarytool submit --apple-id "${ELECTROBUN_APPLEID}" --password "${ELECTROBUN_APPLEIDPASS}" --team-id "${ELECTROBUN_TEAMID}" --wait ${escapePathForTerminal(
|
|
4739
|
+
fileToNotarize,
|
|
4740
|
+
)}`,
|
|
4741
|
+
).toString();
|
|
4742
|
+
}
|
|
4731
4743
|
const uuid = statusInfo.match(/id: ([^\n]+)/)?.[1];
|
|
4732
4744
|
console.log("statusInfo", statusInfo);
|
|
4733
4745
|
console.log("uuid", uuid);
|
|
4734
4746
|
|
|
4735
4747
|
if (statusInfo.match("Current status: Invalid")) {
|
|
4736
4748
|
console.error("notarization failed", statusInfo);
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4749
|
+
let log: string;
|
|
4750
|
+
if (useApiKey) {
|
|
4751
|
+
log = execSync(
|
|
4752
|
+
`xcrun notarytool log --key "${ELECTROBUN_APPLEAPIKEYPATH}" --key-id "${ELECTROBUN_APPLEAPIKEY}" --issuer "${ELECTROBUN_APPLEAPIISSUER}" ${uuid}`,
|
|
4753
|
+
).toString();
|
|
4754
|
+
} else {
|
|
4755
|
+
log = execSync(
|
|
4756
|
+
`xcrun notarytool log --apple-id "${ELECTROBUN_APPLEID}" --password "${ELECTROBUN_APPLEIDPASS}" --team-id "${ELECTROBUN_TEAMID}" ${uuid}`,
|
|
4757
|
+
).toString();
|
|
4758
|
+
}
|
|
4740
4759
|
console.log("log", log);
|
|
4741
4760
|
process.exit(1);
|
|
4742
4761
|
}
|