electrobun 0.0.19-beta.75 → 0.0.19-beta.77

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.
@@ -14,8 +14,8 @@ function getAppDataDir(): string {
14
14
  // Use LOCALAPPDATA to match extractor location
15
15
  return process.env.LOCALAPPDATA || join(homedir(), "AppData", "Local");
16
16
  case 'linux':
17
- // Use XDG_CONFIG_HOME or fallback to ~/.config
18
- return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
17
+ // Use XDG_DATA_HOME or fallback to ~/.local/share to match extractor
18
+ return process.env.XDG_DATA_HOME || join(homedir(), ".local", "share");
19
19
  default:
20
20
  // Fallback to home directory with .config
21
21
  return join(homedir(), ".config");
@@ -352,6 +352,13 @@ const Updater = {
352
352
  const parentDir = resolve(extractionFolder, "..");
353
353
  newAppBundlePath = join(parentDir, "app_new");
354
354
 
355
+ // Remove app_new if it already exists
356
+ try {
357
+ rmdirSync(newAppBundlePath, { recursive: true });
358
+ } catch {
359
+ // Ignore if it doesn't exist
360
+ }
361
+
355
362
  // Move the extracted app to a temporary location
356
363
  renameSync(extractedAppPath, newAppBundlePath);
357
364
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrobun",
3
- "version": "0.0.19-beta.75",
3
+ "version": "0.0.19-beta.77",
4
4
  "description": "Build ultra fast, tiny, and cross-platform desktop apps with Typescript.",
5
5
  "license": "MIT",
6
6
  "author": "Blackboard Technologies Inc.",
package/src/cli/index.ts CHANGED
@@ -11,6 +11,8 @@ import {
11
11
  readdirSync,
12
12
  rmSync,
13
13
  symlinkSync,
14
+ statSync,
15
+ copyFileSync,
14
16
  } from "fs";
15
17
  import { execSync } from "child_process";
16
18
  import tar from "tar";
@@ -1504,8 +1506,8 @@ if (commandArg === "init") {
1504
1506
  buildEnvironment
1505
1507
  );
1506
1508
 
1507
- // Wrap Windows exe in zip for native Windows support
1508
- const wrappedExePath = await wrapInArchive(selfExtractingExePath, buildFolder, 'zip');
1509
+ // Wrap Windows installer files in zip for distribution
1510
+ const wrappedExePath = await wrapWindowsInstallerInZip(selfExtractingExePath, buildFolder);
1509
1511
  artifactsToUpload.push(wrappedExePath);
1510
1512
 
1511
1513
  // Also keep the raw exe for backwards compatibility (optional)
@@ -1878,7 +1880,7 @@ async function createWindowsSelfExtractingExe(
1878
1880
  targetPaths: any,
1879
1881
  buildEnvironment: string
1880
1882
  ): Promise<string> {
1881
- console.log("Creating self-extracting Windows exe...");
1883
+ console.log("Creating Windows installer with separate archive...");
1882
1884
 
1883
1885
  // Format: MyApp-Setup.exe (stable) or MyApp-Setup-canary.exe (non-stable)
1884
1886
  const setupFileName = buildEnvironment === "stable"
@@ -1887,43 +1889,40 @@ async function createWindowsSelfExtractingExe(
1887
1889
 
1888
1890
  const outputExePath = join(buildFolder, setupFileName);
1889
1891
 
1890
- // Read the extractor exe
1892
+ // Copy the extractor exe
1891
1893
  const extractorExe = readFileSync(targetPaths.EXTRACTOR);
1894
+ writeFileSync(outputExePath, extractorExe);
1892
1895
 
1893
- // Read the compressed archive
1894
- const compressedArchive = readFileSync(compressedTarPath);
1895
-
1896
- // Create metadata JSON
1896
+ // Create metadata JSON file
1897
1897
  const metadata = {
1898
1898
  identifier: config.app.identifier,
1899
1899
  name: config.app.name,
1900
1900
  channel: buildEnvironment
1901
1901
  };
1902
- const metadataJson = JSON.stringify(metadata);
1903
- const metadataBuffer = Buffer.from(metadataJson, 'utf8');
1904
-
1905
- // Create marker buffers
1906
- const metadataMarker = Buffer.from('ELECTROBUN_METADATA_V1', 'utf8');
1907
- const archiveMarker = Buffer.from('ELECTROBUN_ARCHIVE_V1', 'utf8');
1908
-
1909
- // Combine extractor + metadata marker + metadata + archive marker + archive
1910
- const combinedBuffer = Buffer.concat([
1911
- extractorExe,
1912
- metadataMarker,
1913
- metadataBuffer,
1914
- archiveMarker,
1915
- compressedArchive
1916
- ]);
1902
+ const metadataJson = JSON.stringify(metadata, null, 2);
1903
+ const metadataFileName = setupFileName.replace('.exe', '.metadata.json');
1904
+ const metadataPath = join(buildFolder, metadataFileName);
1905
+ writeFileSync(metadataPath, metadataJson);
1917
1906
 
1918
- // Write the self-extracting exe
1919
- writeFileSync(outputExePath, combinedBuffer);
1907
+ // Copy the compressed archive with matching name
1908
+ const archiveFileName = setupFileName.replace('.exe', '.tar.zst');
1909
+ const archivePath = join(buildFolder, archiveFileName);
1910
+ copyFileSync(compressedTarPath, archivePath);
1920
1911
 
1921
- // Make it executable (though Windows doesn't need chmod)
1912
+ // Make the exe executable (though Windows doesn't need chmod)
1922
1913
  if (OS !== 'win') {
1923
1914
  execSync(`chmod +x ${escapePathForTerminal(outputExePath)}`);
1924
1915
  }
1925
1916
 
1926
- console.log(`Created self-extracting exe: ${outputExePath} (${(combinedBuffer.length / 1024 / 1024).toFixed(2)} MB)`);
1917
+ const exeSize = statSync(outputExePath).size;
1918
+ const archiveSize = statSync(archivePath).size;
1919
+ const totalSize = exeSize + archiveSize;
1920
+
1921
+ console.log(`Created Windows installer:`);
1922
+ console.log(` - Extractor: ${outputExePath} (${(exeSize / 1024 / 1024).toFixed(2)} MB)`);
1923
+ console.log(` - Archive: ${archivePath} (${(archiveSize / 1024 / 1024).toFixed(2)} MB)`);
1924
+ console.log(` - Metadata: ${metadataPath}`);
1925
+ console.log(` - Total size: ${(totalSize / 1024 / 1024).toFixed(2)} MB`);
1927
1926
 
1928
1927
  return outputExePath;
1929
1928
  }
@@ -1983,6 +1982,53 @@ async function createLinuxSelfExtractingBinary(
1983
1982
  return outputPath;
1984
1983
  }
1985
1984
 
1985
+ async function wrapWindowsInstallerInZip(exePath: string, buildFolder: string): Promise<string> {
1986
+ const exeName = basename(exePath);
1987
+ const exeStem = exeName.replace('.exe', '');
1988
+
1989
+ // Derive the paths for metadata and archive files
1990
+ const metadataPath = join(buildFolder, `${exeStem}.metadata.json`);
1991
+ const archivePath = join(buildFolder, `${exeStem}.tar.zst`);
1992
+ const zipPath = join(buildFolder, `${exeStem}.zip`);
1993
+
1994
+ // Verify all files exist
1995
+ if (!existsSync(exePath)) {
1996
+ throw new Error(`Installer exe not found: ${exePath}`);
1997
+ }
1998
+ if (!existsSync(metadataPath)) {
1999
+ throw new Error(`Metadata file not found: ${metadataPath}`);
2000
+ }
2001
+ if (!existsSync(archivePath)) {
2002
+ throw new Error(`Archive file not found: ${archivePath}`);
2003
+ }
2004
+
2005
+ // Create zip archive
2006
+ const output = createWriteStream(zipPath);
2007
+ const archive = archiver('zip', {
2008
+ zlib: { level: 9 } // Maximum compression
2009
+ });
2010
+
2011
+ return new Promise((resolve, reject) => {
2012
+ output.on('close', () => {
2013
+ console.log(`Created Windows installer package: ${zipPath} (${(archive.pointer() / 1024 / 1024).toFixed(2)} MB)`);
2014
+ resolve(zipPath);
2015
+ });
2016
+
2017
+ archive.on('error', (err) => {
2018
+ reject(err);
2019
+ });
2020
+
2021
+ archive.pipe(output);
2022
+
2023
+ // Add all three files to the archive
2024
+ archive.file(exePath, { name: basename(exePath) });
2025
+ archive.file(metadataPath, { name: basename(metadataPath) });
2026
+ archive.file(archivePath, { name: basename(archivePath) });
2027
+
2028
+ archive.finalize();
2029
+ });
2030
+ }
2031
+
1986
2032
  async function wrapInArchive(filePath: string, buildFolder: string, archiveType: 'tar.gz' | 'zip'): Promise<string> {
1987
2033
  const fileName = basename(filePath);
1988
2034
  const fileDir = dirname(filePath);