craft-native 0.0.87 → 0.0.88

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/dist/index.js CHANGED
@@ -16,11 +16,11 @@ var __export = (target, all) => {
16
16
  var __require = import.meta.require;
17
17
 
18
18
  // src/index.ts
19
- import { execFile, spawn as spawn4 } from "child_process";
20
- import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
21
- import { tmpdir as tmpdir2 } from "os";
22
- import { join as join3 } from "path";
23
- import { promisify } from "util";
19
+ import { execFile as execFile2, spawn as spawn4 } from "child_process";
20
+ import { mkdtempSync as mkdtempSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
21
+ import { tmpdir as tmpdir4 } from "os";
22
+ import { join as join4 } from "path";
23
+ import { promisify as promisify2 } from "util";
24
24
 
25
25
  // src/binary-resolver.ts
26
26
  import { existsSync } from "fs";
@@ -9753,24 +9753,236 @@ var commonOptimizations = {
9753
9753
  // src/updater/index.ts
9754
9754
  var exports_updater = {};
9755
9755
  __export(exports_updater, {
9756
+ verifyBundleTrust: () => verifyBundleTrust,
9756
9757
  updaterCommand: () => updaterCommand,
9758
+ swapBundle: () => swapBundle,
9759
+ readBundleIdentity: () => readBundleIdentity,
9760
+ isMacOS: () => isMacOS,
9757
9761
  generateUpdateManifest: () => generateUpdateManifest,
9762
+ extractBundleFromZip: () => extractBundleFromZip,
9763
+ extractBundleFromDmg: () => extractBundleFromDmg,
9764
+ extractBundle: () => extractBundle,
9765
+ dittoBundle: () => dittoBundle,
9758
9766
  default: () => updater_default,
9767
+ clearQuarantine: () => clearQuarantine,
9768
+ canReplaceBundle: () => canReplaceBundle,
9759
9769
  DeltaGenerator: () => DeltaGenerator,
9760
9770
  AutoUpdater: () => AutoUpdater
9761
9771
  });
9762
- import { createReadStream, createWriteStream, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
9763
- import { join as join2, basename as basename2, dirname } from "path";
9764
- import { execFileSync, spawn as spawn3 } from "child_process";
9772
+ import { createReadStream, createWriteStream, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmdirSync, rmSync as rmSync3, statSync as statSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
9773
+ import { join as join3, basename as basename3, dirname as dirname2 } from "path";
9774
+ import { tmpdir as tmpdir3 } from "os";
9775
+ import { execFileSync as execFileSync2, spawn as spawn3 } from "child_process";
9765
9776
  import { createHash as createHash2 } from "crypto";
9766
9777
  import { EventEmitter as EventEmitter3 } from "events";
9767
- function findFirstByName(root, namePattern, kind) {
9768
- const out = execFileSync("find", [root, "-name", namePattern, "-type", kind, "-print0"]);
9769
- const nullByte = String.fromCharCode(0);
9770
- const parts = out.toString("utf-8").split(nullByte).filter((s) => s.length > 0);
9771
- return parts[0] ?? "";
9778
+
9779
+ // src/updater/macos-bundle.ts
9780
+ import { execFile, execFileSync } from "child_process";
9781
+ import { accessSync, constants, existsSync as existsSync3, mkdtempSync as mkdtempSync2, renameSync, rmSync as rmSync2, statSync } from "fs";
9782
+ import { tmpdir as tmpdir2 } from "os";
9783
+ import { basename as basename2, dirname, join as join2 } from "path";
9784
+ import { promisify } from "util";
9785
+ var execFileAsync = promisify(execFile);
9786
+ function isMacOS() {
9787
+ return process.platform === "darwin";
9788
+ }
9789
+ async function readBundleIdentity(appPath) {
9790
+ const empty = { identifier: null, teamId: null, authority: null };
9791
+ if (!isMacOS())
9792
+ return empty;
9793
+ let report;
9794
+ try {
9795
+ const { stderr, stdout } = await execFileAsync("codesign", ["-dv", "--verbose=4", appPath]);
9796
+ report = `${stderr}
9797
+ ${stdout}`;
9798
+ } catch (error) {
9799
+ const e = error;
9800
+ report = `${e.stderr ?? ""}
9801
+ ${e.stdout ?? ""}`;
9802
+ if (!report.trim())
9803
+ return empty;
9804
+ }
9805
+ const field = (name) => {
9806
+ const match = report.match(new RegExp(`^${name}=(.+)$`, "m"));
9807
+ if (!match)
9808
+ return null;
9809
+ const value = match[1].trim();
9810
+ return value === "not set" || value.length === 0 ? null : value;
9811
+ };
9812
+ return {
9813
+ identifier: field("Identifier"),
9814
+ teamId: field("TeamIdentifier"),
9815
+ authority: field("Authority")
9816
+ };
9817
+ }
9818
+ async function verifyBundleTrust(appPath, policy = {}) {
9819
+ const identity = await readBundleIdentity(appPath);
9820
+ const requireNotarized = policy.requireNotarized ?? true;
9821
+ if (!existsSync3(appPath)) {
9822
+ return { ok: false, identity, gatekeeperSource: null, reason: "unreadable", detail: `No such bundle: ${appPath}` };
9823
+ }
9824
+ try {
9825
+ await execFileAsync("codesign", ["--verify", "--deep", "--strict", "--verbose=2", appPath]);
9826
+ } catch (error) {
9827
+ return {
9828
+ ok: false,
9829
+ identity,
9830
+ gatekeeperSource: null,
9831
+ reason: "codesign-invalid",
9832
+ detail: describe(error)
9833
+ };
9834
+ }
9835
+ let gatekeeperSource = null;
9836
+ if (requireNotarized) {
9837
+ try {
9838
+ const { stderr, stdout } = await execFileAsync("spctl", ["-a", "-t", "exec", "-vv", appPath]);
9839
+ const report = `${stderr}
9840
+ ${stdout}`;
9841
+ gatekeeperSource = report.match(/^source=(.+)$/m)?.[1]?.trim() ?? null;
9842
+ } catch (error) {
9843
+ return {
9844
+ ok: false,
9845
+ identity,
9846
+ gatekeeperSource: null,
9847
+ reason: "gatekeeper-rejected",
9848
+ detail: describe(error)
9849
+ };
9850
+ }
9851
+ }
9852
+ if (policy.teamId && identity.teamId !== policy.teamId) {
9853
+ return {
9854
+ ok: false,
9855
+ identity,
9856
+ gatekeeperSource,
9857
+ reason: "team-mismatch",
9858
+ detail: `Expected Team ID ${policy.teamId}, bundle is signed by ${identity.teamId ?? "nobody"}`
9859
+ };
9860
+ }
9861
+ return { ok: true, identity, gatekeeperSource };
9862
+ }
9863
+ async function clearQuarantine(appPath) {
9864
+ if (!isMacOS())
9865
+ return;
9866
+ try {
9867
+ await execFileAsync("xattr", ["-d", "-r", "com.apple.quarantine", appPath]);
9868
+ } catch {}
9869
+ }
9870
+ async function dittoBundle(source, destination) {
9871
+ await execFileAsync("ditto", [source, destination]);
9872
+ }
9873
+ function findTopLevelApp(root) {
9874
+ const out = execFileSync("find", [root, "-maxdepth", "2", "-name", "*.app", "-type", "d", "-print0"]);
9875
+ const parts = out.toString("utf-8").split("\x00").filter((s) => s.length > 0);
9876
+ parts.sort((a, b) => a.split("/").length - b.split("/").length);
9877
+ return parts[0] ?? null;
9878
+ }
9879
+ async function extractBundleFromDmg(dmgPath, stagingDir) {
9880
+ const staging = stagingDir ?? mkdtempSync2(join2(tmpdir2(), "craft-update-"));
9881
+ const mountPoint = mkdtempSync2(join2(tmpdir2(), "craft-mount-"));
9882
+ let mounted = false;
9883
+ try {
9884
+ await execFileAsync("hdiutil", ["attach", dmgPath, "-nobrowse", "-readonly", "-mountpoint", mountPoint]);
9885
+ mounted = true;
9886
+ const source = findTopLevelApp(mountPoint);
9887
+ if (!source)
9888
+ throw new Error(`No .app found in disk image: ${dmgPath}`);
9889
+ const appPath = join2(staging, basename2(source));
9890
+ await dittoBundle(source, appPath);
9891
+ return { appPath, stagingDir: staging };
9892
+ } finally {
9893
+ if (mounted) {
9894
+ try {
9895
+ await execFileAsync("hdiutil", ["detach", mountPoint, "-force"]);
9896
+ } catch {}
9897
+ }
9898
+ try {
9899
+ rmSync2(mountPoint, { recursive: true, force: true });
9900
+ } catch {}
9901
+ }
9902
+ }
9903
+ async function extractBundleFromZip(zipPath, stagingDir) {
9904
+ const staging = stagingDir ?? mkdtempSync2(join2(tmpdir2(), "craft-update-"));
9905
+ const unpacked = join2(staging, "payload");
9906
+ await execFileAsync("ditto", ["-x", "-k", zipPath, unpacked]);
9907
+ const source = findTopLevelApp(unpacked);
9908
+ if (!source)
9909
+ throw new Error(`No .app found in archive: ${zipPath}`);
9910
+ return { appPath: source, stagingDir: staging };
9911
+ }
9912
+ async function extractBundle(archivePath, stagingDir) {
9913
+ if (archivePath.endsWith(".dmg"))
9914
+ return extractBundleFromDmg(archivePath, stagingDir);
9915
+ if (archivePath.endsWith(".zip"))
9916
+ return extractBundleFromZip(archivePath, stagingDir);
9917
+ throw new Error(`Cannot extract an app bundle from ${archivePath}`);
9918
+ }
9919
+ async function swapBundle(stagedPath, installedPath) {
9920
+ if (!existsSync3(stagedPath))
9921
+ throw new Error(`Staged bundle is missing: ${stagedPath}`);
9922
+ const parent = dirname(installedPath);
9923
+ const stamp = `${Date.now()}.${process.pid}`;
9924
+ const incoming = join2(parent, `.${basename2(installedPath)}.${stamp}.incoming`);
9925
+ const retired = join2(parent, `.${basename2(installedPath)}.${stamp}.retired`);
9926
+ try {
9927
+ renameSync(stagedPath, incoming);
9928
+ } catch {
9929
+ await dittoBundle(stagedPath, incoming);
9930
+ }
9931
+ const hadPrevious = existsSync3(installedPath);
9932
+ try {
9933
+ if (hadPrevious)
9934
+ renameSync(installedPath, retired);
9935
+ try {
9936
+ renameSync(incoming, installedPath);
9937
+ } catch (error) {
9938
+ if (hadPrevious) {
9939
+ try {
9940
+ renameSync(retired, installedPath);
9941
+ } catch {}
9942
+ }
9943
+ throw error;
9944
+ }
9945
+ } catch (error) {
9946
+ try {
9947
+ rmSync2(incoming, { recursive: true, force: true });
9948
+ } catch {}
9949
+ throw error;
9950
+ }
9951
+ if (hadPrevious) {
9952
+ try {
9953
+ rmSync2(retired, { recursive: true, force: true });
9954
+ return { previousPath: null };
9955
+ } catch {
9956
+ return { previousPath: retired };
9957
+ }
9958
+ }
9959
+ return { previousPath: null };
9960
+ }
9961
+ function canReplaceBundle(installedPath) {
9962
+ try {
9963
+ const parent = dirname(installedPath);
9964
+ accessSync(parent, constants.W_OK);
9965
+ if (existsSync3(installedPath))
9966
+ statSync(installedPath);
9967
+ return true;
9968
+ } catch {
9969
+ return false;
9970
+ }
9971
+ }
9972
+ function describe(error) {
9973
+ if (error && typeof error === "object") {
9974
+ const e = error;
9975
+ const stderr = e.stderr?.trim();
9976
+ if (stderr)
9977
+ return stderr;
9978
+ if (e.message)
9979
+ return e.message;
9980
+ }
9981
+ return String(error);
9772
9982
  }
9773
9983
 
9984
+ // src/updater/index.ts
9985
+ var PROGRESS_INTERVAL_MS = 100;
9774
9986
  class AutoUpdater extends EventEmitter3 {
9775
9987
  config;
9776
9988
  updateInfo = null;
@@ -9779,12 +9991,22 @@ class AutoUpdater extends EventEmitter3 {
9779
9991
  cachedEtag = null;
9780
9992
  cachedLastModified = null;
9781
9993
  cachedManifest = null;
9994
+ lastError = null;
9782
9995
  emitError(error) {
9996
+ this.lastError = error instanceof Error ? error : new Error(String(error));
9783
9997
  if (this.listenerCount("error") > 0)
9784
9998
  this.emit("error", error);
9785
9999
  else
9786
10000
  console.error("[Updater]", error);
9787
10001
  }
10002
+ downloadDir() {
10003
+ if (this.config.downloadDir)
10004
+ return this.config.downloadDir;
10005
+ return join3(tmpdir3(), "craft-updates", basename3(this.config.appPath) || "app");
10006
+ }
10007
+ getLastError() {
10008
+ return this.lastError;
10009
+ }
9788
10010
  constructor(config) {
9789
10011
  super();
9790
10012
  const url = config.updateUrl;
@@ -9818,6 +10040,7 @@ class AutoUpdater extends EventEmitter3 {
9818
10040
  }
9819
10041
  async checkForUpdates() {
9820
10042
  this.emit("checking-for-update");
10043
+ this.lastError = null;
9821
10044
  try {
9822
10045
  const platform = this.getPlatform();
9823
10046
  const url = `${this.config.updateUrl}?v=${this.config.currentVersion}&channel=${this.config.channel}&platform=${platform}`;
@@ -9866,20 +10089,21 @@ class AutoUpdater extends EventEmitter3 {
9866
10089
  if (!this.updateInfo) {
9867
10090
  throw new Error("No update available");
9868
10091
  }
10092
+ this.lastError = null;
9869
10093
  const platform = this.getPlatform();
9870
10094
  const platformUpdate = this.updateInfo.platforms[platform];
9871
10095
  if (!platformUpdate) {
9872
10096
  throw new Error(`No update available for platform: ${platform}`);
9873
10097
  }
9874
- const deltaUpdate = platformUpdate.delta?.find((d) => d.fromVersion === this.config.currentVersion);
10098
+ const deltaUpdate = DeltaGenerator.isSupported() ? platformUpdate.delta?.find((d) => d.fromVersion === this.config.currentVersion) : undefined;
9875
10099
  const _updateSource = deltaUpdate || platformUpdate;
9876
10100
  const url = deltaUpdate?.url || platformUpdate.url;
9877
10101
  const expectedSize = deltaUpdate?.size || platformUpdate.size;
9878
10102
  const expectedHash = deltaUpdate?.sha256 || platformUpdate.sha256;
9879
- const downloadDir = join2(this.config.appPath, "..", ".craft-updates");
10103
+ const downloadDir = this.downloadDir();
9880
10104
  mkdirSync2(downloadDir, { recursive: true });
9881
- const fileName = basename2(url);
9882
- this.downloadPath = join2(downloadDir, fileName);
10105
+ const fileName = basename3(url);
10106
+ this.downloadPath = join3(downloadDir, fileName);
9883
10107
  try {
9884
10108
  const response = await fetch(url);
9885
10109
  if (!response.ok) {
@@ -9894,39 +10118,59 @@ class AutoUpdater extends EventEmitter3 {
9894
10118
  if (!reader) {
9895
10119
  throw new Error("Failed to get response reader");
9896
10120
  }
10121
+ const digest = createHash2("sha256");
10122
+ let lastProgressAt = 0;
9897
10123
  while (true) {
9898
10124
  const { done, value } = await reader.read();
9899
10125
  if (done)
9900
10126
  break;
9901
- fileStream.write(value);
10127
+ digest.update(value);
10128
+ if (!fileStream.write(value)) {
10129
+ await new Promise((resolve, reject) => {
10130
+ const onDrain = () => {
10131
+ fileStream.off("error", onError);
10132
+ resolve();
10133
+ };
10134
+ const onError = (error) => {
10135
+ fileStream.off("drain", onDrain);
10136
+ reject(error);
10137
+ };
10138
+ fileStream.once("drain", onDrain);
10139
+ fileStream.once("error", onError);
10140
+ });
10141
+ }
9902
10142
  downloaded += value.length;
9903
- const elapsed = (Date.now() - startTime) / 1000;
9904
- const speed = downloaded / elapsed;
9905
- const progress = {
9906
- phase: "downloading",
9907
- percent: Math.round(downloaded / total * 100),
9908
- bytesDownloaded: downloaded,
9909
- bytesTotal: total,
9910
- speed
9911
- };
9912
- this.emit("download-progress", progress);
10143
+ const now = Date.now();
10144
+ if (now - lastProgressAt >= PROGRESS_INTERVAL_MS || downloaded === total) {
10145
+ lastProgressAt = now;
10146
+ const elapsed = (now - startTime) / 1000;
10147
+ const speed = elapsed > 0 ? downloaded / elapsed : 0;
10148
+ const progress = {
10149
+ phase: "downloading",
10150
+ percent: total > 0 ? Math.round(downloaded / total * 100) : 0,
10151
+ bytesDownloaded: downloaded,
10152
+ bytesTotal: total,
10153
+ speed
10154
+ };
10155
+ this.emit("download-progress", progress);
10156
+ }
9913
10157
  }
9914
10158
  fileStream.end();
9915
10159
  await new Promise((resolve, reject) => {
9916
10160
  fileStream.once("finish", resolve);
9917
10161
  fileStream.once("error", reject);
9918
10162
  });
9919
- const hash = await this.computeFileHash(this.downloadPath);
10163
+ const hash = digest.digest("hex");
9920
10164
  if (hash !== expectedHash) {
9921
10165
  unlinkSync(this.downloadPath);
9922
10166
  throw new Error("Download verification failed: hash mismatch");
9923
10167
  }
9924
10168
  if (deltaUpdate) {
9925
- if (!statSync(this.config.appPath).isFile()) {
10169
+ if (!statSync2(this.config.appPath).isFile()) {
9926
10170
  throw new Error("Delta updates require appPath to point to the installed bundle file");
9927
10171
  }
9928
10172
  const patchPath = this.downloadPath;
9929
- const reconstructedPath = join2(downloadDir, `reconstructed-${basename2(platformUpdate.url)}`);
10173
+ const reconstructedPath = join3(downloadDir, `reconstructed-${basename3(platformUpdate.url)}`);
9930
10174
  await DeltaGenerator.apply(this.config.appPath, patchPath, reconstructedPath);
9931
10175
  const reconstructedHash = await this.computeFileHash(reconstructedPath);
9932
10176
  if (reconstructedHash !== platformUpdate.sha256) {
@@ -9964,7 +10208,7 @@ class AutoUpdater extends EventEmitter3 {
9964
10208
  }
9965
10209
  }
9966
10210
  async installUpdate(restartAfter = true) {
9967
- if (!this.downloadPath || !existsSync3(this.downloadPath)) {
10211
+ if (!this.downloadPath || !existsSync4(this.downloadPath)) {
9968
10212
  throw new Error("No update downloaded");
9969
10213
  }
9970
10214
  if (this.updateInfo) {
@@ -10008,14 +10252,17 @@ class AutoUpdater extends EventEmitter3 {
10008
10252
  await this.installLinuxUpdate();
10009
10253
  break;
10010
10254
  }
10011
- if (this.downloadPath && existsSync3(this.downloadPath)) {
10255
+ if (this.downloadPath && existsSync4(this.downloadPath)) {
10012
10256
  unlinkSync(this.downloadPath);
10013
10257
  }
10258
+ try {
10259
+ rmdirSync(this.downloadDir());
10260
+ } catch {}
10014
10261
  progress.phase = "done";
10015
10262
  progress.percent = 100;
10016
10263
  this.emit("download-progress", progress);
10017
10264
  if (restartAfter) {
10018
- this.restartApp();
10265
+ await this.restartApp();
10019
10266
  }
10020
10267
  } catch (error) {
10021
10268
  progress.phase = "error";
@@ -10026,38 +10273,45 @@ class AutoUpdater extends EventEmitter3 {
10026
10273
  async installMacOSUpdate() {
10027
10274
  const downloadPath = this.downloadPath;
10028
10275
  const appPath = this.config.appPath;
10029
- if (downloadPath.endsWith(".zip")) {
10030
- const tempDir2 = join2(dirname(downloadPath), "extracted");
10031
- mkdirSync2(tempDir2, { recursive: true });
10032
- execFileSync("unzip", ["-o", downloadPath, "-d", tempDir2]);
10033
- const extractedApp = findFirstByName(tempDir2, "*.app", "d");
10034
- if (extractedApp) {
10035
- const { rmSync: rmSync3, renameSync } = await import("fs");
10036
- rmSync3(appPath, { recursive: true, force: true });
10037
- renameSync(extractedApp, appPath);
10038
- }
10039
- const { rmSync: rmSync2 } = await import("fs");
10040
- rmSync2(tempDir2, { recursive: true, force: true });
10041
- } else if (downloadPath.endsWith(".dmg")) {
10042
- const mountOutput = execFileSync("hdiutil", ["attach", downloadPath, "-nobrowse"]).toString();
10043
- const mountPoint = mountOutput.match(/\/Volumes\/[^\n]+/)?.[0];
10044
- if (mountPoint) {
10045
- const dmgApp = findFirstByName(mountPoint, "*.app", "d");
10046
- if (dmgApp) {
10047
- const { rmSync: rmSync2, cpSync: cpSync2 } = await import("fs");
10048
- rmSync2(appPath, { recursive: true, force: true });
10049
- cpSync2(dmgApp, appPath, { recursive: true });
10050
- }
10051
- execFileSync("hdiutil", ["detach", mountPoint]);
10052
- }
10053
- } else if (downloadPath.endsWith(".pkg")) {
10276
+ if (downloadPath.endsWith(".pkg")) {
10277
+ await this.installMacOSPackage(downloadPath);
10278
+ return;
10279
+ }
10280
+ if (!canReplaceBundle(appPath)) {
10281
+ throw new Error(`Cannot replace ${appPath}: ${dirname2(appPath)} is not writable by this user. ` + "Install the update manually, or move the app somewhere you own.");
10282
+ }
10283
+ const staged = await extractBundle(downloadPath);
10284
+ try {
10285
+ const trust = await this.assertBundleTrusted(staged.appPath);
10286
+ await clearQuarantine(staged.appPath);
10287
+ const { previousPath } = await swapBundle(staged.appPath, appPath);
10288
+ this.emit("update-installed", {
10289
+ path: appPath,
10290
+ version: this.updateInfo?.version,
10291
+ identity: trust.identity,
10292
+ leftoverPath: previousPath
10293
+ });
10294
+ } finally {
10054
10295
  try {
10055
- execFileSync("pkgutil", ["--check-signature", downloadPath], { stdio: "pipe" });
10056
- } catch (e) {
10057
- throw new Error(`Refusing to install ${downloadPath}: pkgutil --check-signature failed. The .pkg is not signed by a trusted Apple certificate chain. Underlying error: ${e.message}`);
10058
- }
10059
- execFileSync("sudo", ["installer", "-pkg", downloadPath, "-target", "/"]);
10296
+ rmSync3(staged.stagingDir, { recursive: true, force: true });
10297
+ } catch {}
10298
+ }
10299
+ }
10300
+ async assertBundleTrusted(bundlePath) {
10301
+ const policy = this.config.macos ?? {};
10302
+ const trust = await verifyBundleTrust(bundlePath, policy);
10303
+ if (!trust.ok) {
10304
+ throw new Error(`Refusing to install ${bundlePath}: ${trust.reason} (${trust.detail ?? "no detail"}). ` + "The downloaded bundle is not one macOS will run as this application.");
10305
+ }
10306
+ return trust;
10307
+ }
10308
+ async installMacOSPackage(downloadPath) {
10309
+ try {
10310
+ execFileSync2("pkgutil", ["--check-signature", downloadPath], { stdio: "pipe" });
10311
+ } catch (e) {
10312
+ throw new Error(`Refusing to install ${downloadPath}: pkgutil --check-signature failed. ` + "The .pkg is not signed by a trusted Apple certificate chain. " + `Underlying error: ${e.message}`);
10060
10313
  }
10314
+ execFileSync2("sudo", ["installer", "-pkg", downloadPath, "-target", "/"]);
10061
10315
  }
10062
10316
  async installWindowsUpdate() {
10063
10317
  const downloadPath = this.downloadPath;
@@ -10072,8 +10326,8 @@ class AutoUpdater extends EventEmitter3 {
10072
10326
  stdio: "ignore"
10073
10327
  });
10074
10328
  } else if (downloadPath.endsWith(".zip")) {
10075
- const appDir = dirname(this.config.appPath);
10076
- execFileSync("powershell", [
10329
+ const appDir = dirname2(this.config.appPath);
10330
+ execFileSync2("powershell", [
10077
10331
  "-NoProfile",
10078
10332
  "-NonInteractive",
10079
10333
  "-Command",
@@ -10086,28 +10340,29 @@ class AutoUpdater extends EventEmitter3 {
10086
10340
  async installLinuxUpdate() {
10087
10341
  const downloadPath = this.downloadPath;
10088
10342
  if (downloadPath.endsWith(".AppImage")) {
10089
- execFileSync("chmod", ["+x", downloadPath]);
10090
- const { renameSync } = await import("fs");
10091
- renameSync(downloadPath, this.config.appPath);
10343
+ execFileSync2("chmod", ["+x", downloadPath]);
10344
+ const { renameSync: renameSync2 } = await import("fs");
10345
+ renameSync2(downloadPath, this.config.appPath);
10092
10346
  } else if (downloadPath.endsWith(".deb")) {
10093
- execFileSync("sudo", ["dpkg", "-i", downloadPath]);
10347
+ execFileSync2("sudo", ["dpkg", "-i", downloadPath]);
10094
10348
  } else if (downloadPath.endsWith(".rpm")) {
10095
- execFileSync("sudo", ["rpm", "-U", downloadPath]);
10349
+ execFileSync2("sudo", ["rpm", "-U", downloadPath]);
10096
10350
  } else if (downloadPath.endsWith(".tar.gz")) {
10097
- const appDir = dirname(this.config.appPath);
10098
- execFileSync("tar", ["-xzf", downloadPath, "-C", appDir]);
10351
+ const appDir = dirname2(this.config.appPath);
10352
+ execFileSync2("tar", ["-xzf", downloadPath, "-C", appDir]);
10099
10353
  }
10100
10354
  }
10101
- restartApp() {
10102
- const platform = this.getPlatform();
10355
+ async restartApp() {
10103
10356
  const appPath = this.config.appPath;
10104
- switch (platform) {
10357
+ if (this.config.relaunch) {
10358
+ await this.config.relaunch(appPath);
10359
+ return;
10360
+ }
10361
+ switch (this.getPlatform()) {
10105
10362
  case "darwin":
10106
10363
  spawn3("open", ["-n", appPath], { detached: true, stdio: "ignore" });
10107
10364
  break;
10108
10365
  case "win32":
10109
- spawn3(appPath, [], { detached: true, stdio: "ignore" });
10110
- break;
10111
10366
  case "linux":
10112
10367
  spawn3(appPath, [], { detached: true, stdio: "ignore" });
10113
10368
  break;
@@ -10183,18 +10438,32 @@ class AutoUpdater extends EventEmitter3 {
10183
10438
  }
10184
10439
 
10185
10440
  class DeltaGenerator {
10441
+ static supported = null;
10442
+ static isSupported() {
10443
+ if (DeltaGenerator.supported !== null)
10444
+ return DeltaGenerator.supported;
10445
+ DeltaGenerator.supported = ["bspatch", "xdelta3"].some((tool) => {
10446
+ try {
10447
+ execFileSync2("command", ["-v", tool], { stdio: "ignore", shell: true });
10448
+ return true;
10449
+ } catch {
10450
+ return false;
10451
+ }
10452
+ });
10453
+ return DeltaGenerator.supported;
10454
+ }
10186
10455
  static async generate(oldPath, newPath, outputPath) {
10187
10456
  try {
10188
- execFileSync("bsdiff", [oldPath, newPath, outputPath]);
10189
- const stats = statSync(outputPath);
10457
+ execFileSync2("bsdiff", [oldPath, newPath, outputPath]);
10458
+ const stats = statSync2(outputPath);
10190
10459
  const hash = await DeltaGenerator.computeHash(outputPath);
10191
10460
  return {
10192
10461
  size: stats.size,
10193
10462
  sha256: hash
10194
10463
  };
10195
10464
  } catch {
10196
- execFileSync("xdelta3", ["-e", "-s", oldPath, newPath, outputPath]);
10197
- const stats = statSync(outputPath);
10465
+ execFileSync2("xdelta3", ["-e", "-s", oldPath, newPath, outputPath]);
10466
+ const stats = statSync2(outputPath);
10198
10467
  const hash = await DeltaGenerator.computeHash(outputPath);
10199
10468
  return {
10200
10469
  size: stats.size,
@@ -10204,9 +10473,9 @@ class DeltaGenerator {
10204
10473
  }
10205
10474
  static async apply(basePath, deltaPath, outputPath) {
10206
10475
  try {
10207
- execFileSync("bspatch", [basePath, outputPath, deltaPath]);
10476
+ execFileSync2("bspatch", [basePath, outputPath, deltaPath]);
10208
10477
  } catch {
10209
- execFileSync("xdelta3", ["-d", "-s", basePath, deltaPath, outputPath]);
10478
+ execFileSync2("xdelta3", ["-d", "-s", basePath, deltaPath, outputPath]);
10210
10479
  }
10211
10480
  }
10212
10481
  static async computeHash(filePath) {
@@ -10334,11 +10603,11 @@ Examples:
10334
10603
  var updater_default = AutoUpdater;
10335
10604
 
10336
10605
  // src/index.ts
10337
- var execFileAsync = promisify(execFile);
10606
+ var execFileAsync2 = promisify2(execFile2);
10338
10607
  var versionMismatchWarned = false;
10339
10608
  function getSdkVersion() {
10340
10609
  try {
10341
- const pkgPath = join3(import.meta.dir, "..", "package.json");
10610
+ const pkgPath = join4(import.meta.dir, "..", "package.json");
10342
10611
  return JSON.parse(readFileSync3(pkgPath, "utf-8")).version ?? "unknown";
10343
10612
  } catch {
10344
10613
  return "unknown";
@@ -10348,7 +10617,7 @@ async function probeBinaryVersion(craftPath) {
10348
10617
  if (versionMismatchWarned)
10349
10618
  return;
10350
10619
  try {
10351
- const { stdout } = await execFileAsync(craftPath, ["--version"], { timeout: 2000 });
10620
+ const { stdout } = await execFileAsync2(craftPath, ["--version"], { timeout: 2000 });
10352
10621
  const nativeVersion = parseCraftVersionOutput(stdout);
10353
10622
  const sdkVersion = getSdkVersion();
10354
10623
  if (sdkVersion !== "unknown" && nativeVersion && nativeVersion !== sdkVersion) {
@@ -10565,8 +10834,8 @@ class CraftApp {
10565
10834
  if (safeForArgv) {
10566
10835
  args.push("--sidebar-config", json);
10567
10836
  } else {
10568
- const dir = mkdtempSync2(join3(tmpdir2(), "craft-sidebar-"));
10569
- const path = join3(dir, "sidebar-config.json");
10837
+ const dir = mkdtempSync3(join4(tmpdir4(), "craft-sidebar-"));
10838
+ const path = join4(dir, "sidebar-config.json");
10570
10839
  writeFileSync3(path, json, "utf-8");
10571
10840
  args.push("--sidebar-config-file", path);
10572
10841
  }
@@ -10605,6 +10874,7 @@ export {
10605
10874
  watch,
10606
10875
  vueOptimizations,
10607
10876
  verifyPassword,
10877
+ verifyBundleTrust,
10608
10878
  variants,
10609
10879
  uuid,
10610
10880
  exports_updater as updater,
@@ -10623,6 +10893,7 @@ export {
10623
10893
  tahoeStyles,
10624
10894
  tahoeStyle,
10625
10895
  tahoeDemoData,
10896
+ swapBundle,
10626
10897
  svelteOptimizations,
10627
10898
  submenu,
10628
10899
  styles,
@@ -10670,6 +10941,7 @@ export {
10670
10941
  registerShortcut,
10671
10942
  readText,
10672
10943
  readHTML,
10944
+ readBundleIdentity,
10673
10945
  readBinaryFile,
10674
10946
  reactOptimizations,
10675
10947
  randomString,
@@ -10737,9 +11009,13 @@ export {
10737
11009
  foregroundService,
10738
11010
  focusFilters,
10739
11011
  focus,
11012
+ extractBundleFromZip,
11013
+ extractBundleFromDmg,
11014
+ extractBundle,
10740
11015
  exit,
10741
11016
  exec,
10742
11017
  env,
11018
+ dittoBundle,
10743
11019
  dialog,
10744
11020
  device,
10745
11021
  desktopWidgets,
@@ -10770,8 +11046,10 @@ export {
10770
11046
  commonOptimizations,
10771
11047
  cx as clsx,
10772
11048
  clipboard,
11049
+ clearQuarantine,
10773
11050
  checkbox,
10774
11051
  carplay,
11052
+ canReplaceBundle,
10775
11053
  canQuickLook,
10776
11054
  camera,
10777
11055
  buildMenu,