craft-native 0.0.186 → 0.0.189

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
@@ -17,9 +17,8 @@ var __require = import.meta.require;
17
17
 
18
18
  // src/index.ts
19
19
  import { execFile as execFile2, spawn as spawn4 } from "child_process";
20
- import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync3 } from "fs";
21
- import { tmpdir as tmpdir4 } from "os";
22
- import { join as join4 } from "path";
20
+ import { writeFileSync as writeFileSync4 } from "fs";
21
+ import { join as join5 } from "path";
23
22
  import { promisify as promisify2 } from "util";
24
23
 
25
24
  // src/binary-resolver.ts
@@ -58,11 +57,125 @@ function craftBinaryNotFoundMessage(triedPath) {
58
57
  `);
59
58
  }
60
59
  // package.json
61
- var version = "0.0.186";
60
+ var version = "0.0.189";
62
61
 
63
62
  // src/sdk-version.ts
64
63
  var SDK_VERSION = version;
65
64
 
65
+ // src/temp-dir.ts
66
+ import { existsSync as existsSync2, mkdtempSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
67
+ import { constants as osConstants, tmpdir } from "os";
68
+ import { join } from "path";
69
+ var OWNED_NAME = /^craft-(?:.+-)?p(\d+)-[A-Za-z0-9]{6}$/;
70
+ var KEEP_MARKER = ".craft-keep";
71
+ var SWEEP_MIN_AGE_MS = 60000;
72
+ var SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
73
+ var live = new Set;
74
+ var hooked = false;
75
+ var swept = false;
76
+ function removeQuietly(dir) {
77
+ try {
78
+ rmSync(dir, { recursive: true, force: true });
79
+ } catch {}
80
+ }
81
+ function removeAllTempDirs() {
82
+ for (const dir of live)
83
+ removeQuietly(dir);
84
+ live.clear();
85
+ unhook();
86
+ }
87
+ function onExit() {
88
+ for (const dir of live)
89
+ removeQuietly(dir);
90
+ live.clear();
91
+ }
92
+ function onSignal(signal) {
93
+ removeAllTempDirs();
94
+ if (process.listenerCount(signal) === 0)
95
+ process.exit(128 + (osConstants.signals[signal] ?? 1));
96
+ }
97
+ var signalHandlers = new Map(SIGNALS.map((signal) => [signal, () => onSignal(signal)]));
98
+ function hook() {
99
+ if (hooked)
100
+ return;
101
+ hooked = true;
102
+ process.on("exit", onExit);
103
+ for (const [signal, handler] of signalHandlers) {
104
+ try {
105
+ process.on(signal, handler);
106
+ } catch {}
107
+ }
108
+ }
109
+ function unhook() {
110
+ if (!hooked)
111
+ return;
112
+ hooked = false;
113
+ process.off("exit", onExit);
114
+ for (const [signal, handler] of signalHandlers)
115
+ process.off(signal, handler);
116
+ }
117
+ function pidIsAlive(pid) {
118
+ try {
119
+ process.kill(pid, 0);
120
+ return true;
121
+ } catch (error) {
122
+ return error.code === "EPERM";
123
+ }
124
+ }
125
+ function sweepOrphanedTempDirs(options = {}) {
126
+ const root = options.root ?? tmpdir();
127
+ const isAlive = options.isAlive ?? pidIsAlive;
128
+ const minAge = options.minAgeMs ?? SWEEP_MIN_AGE_MS;
129
+ const now = options.now ?? Date.now();
130
+ const removed = [];
131
+ let names;
132
+ try {
133
+ names = readdirSync(root);
134
+ } catch {
135
+ return removed;
136
+ }
137
+ for (const name of names) {
138
+ const match = OWNED_NAME.exec(name);
139
+ if (!match)
140
+ continue;
141
+ const pid = Number(match[1]);
142
+ if (pid === process.pid || isAlive(pid))
143
+ continue;
144
+ const path = join(root, name);
145
+ try {
146
+ if (now - statSync(path).mtimeMs < minAge)
147
+ continue;
148
+ if (existsSync2(join(path, KEEP_MARKER)))
149
+ continue;
150
+ } catch {
151
+ continue;
152
+ }
153
+ removeQuietly(path);
154
+ removed.push(path);
155
+ }
156
+ return removed;
157
+ }
158
+ function tempDirPrefix(label) {
159
+ const bare = label.replace(/^craft-/, "").replace(/-+$/, "");
160
+ return bare ? `craft-${bare}-p${process.pid}-` : `craft-p${process.pid}-`;
161
+ }
162
+ function createTempDir(label, root = tmpdir()) {
163
+ if (!swept) {
164
+ swept = true;
165
+ sweepOrphanedTempDirs({ root });
166
+ }
167
+ hook();
168
+ const dir = mkdtempSync(join(root, tempDirPrefix(label)));
169
+ live.add(dir);
170
+ return dir;
171
+ }
172
+ function removeTempDir(dir) {
173
+ removeQuietly(dir);
174
+ live.delete(dir);
175
+ if (live.size === 0)
176
+ unhook();
177
+ }
178
+
66
179
  // src/package.ts
67
180
  import { spawn } from "child_process";
68
181
  import { createHash } from "crypto";
@@ -70,17 +183,16 @@ import {
70
183
  chmodSync,
71
184
  copyFileSync,
72
185
  cpSync,
73
- existsSync as existsSync2,
186
+ existsSync as existsSync3,
74
187
  lstatSync,
75
188
  mkdirSync,
76
- mkdtempSync,
77
189
  readFileSync,
78
- readdirSync,
79
- rmSync,
80
- writeFileSync
190
+ readdirSync as readdirSync2,
191
+ rmSync as rmSync2,
192
+ writeFileSync as writeFileSync2
81
193
  } from "fs";
82
- import { homedir, tmpdir } from "os";
83
- import { basename, join } from "path";
194
+ import { homedir } from "os";
195
+ import { basename, join as join2 } from "path";
84
196
  import { deflateRawSync } from "zlib";
85
197
  var DEFAULT_DEB_DEPENDENCIES = ["libgtk-3-0", "libwebkit2gtk-4.1-37", "libnotify-bin"];
86
198
  var CRC32_TABLE = (() => {
@@ -165,12 +277,12 @@ function formatPackagingCommandError(tool, code, stdout, stderr) {
165
277
  }
166
278
  async function packageApp(config) {
167
279
  const results = [];
168
- if (!config.binaryPath || !existsSync2(config.binaryPath)) {
280
+ if (!config.binaryPath || !existsSync3(config.binaryPath)) {
169
281
  throw new Error(`Binary not found: ${config.binaryPath}`);
170
282
  }
171
283
  const platforms = config.platforms || [detectPlatform()];
172
- const outDir = config.outDir || join(process.cwd(), "dist");
173
- if (!existsSync2(outDir)) {
284
+ const outDir = config.outDir || join2(process.cwd(), "dist");
285
+ if (!existsSync3(outDir)) {
174
286
  mkdirSync(outDir, { recursive: true });
175
287
  }
176
288
  for (const platform of platforms) {
@@ -193,7 +305,7 @@ async function packageMacOS(config, outDir) {
193
305
  const { name, version: version2, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
194
306
  const opts = config.macos || {};
195
307
  const hardenedRuntime = !opts.appStore;
196
- const appBundlePath = join(outDir, `${name}.app`);
308
+ const appBundlePath = join2(outDir, `${name}.app`);
197
309
  const appBundle = createMacOSAppBundle({
198
310
  name,
199
311
  version: version2,
@@ -240,7 +352,7 @@ async function packageMacOS(config, outDir) {
240
352
  }
241
353
  results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
242
354
  if (opts.dmg !== false) {
243
- const outputPath = join(outDir, `${name}-${version2}.dmg`);
355
+ const outputPath = join2(outDir, `${name}-${version2}.dmg`);
244
356
  const dmgResult = await createDMG({ appBundlePath, outputPath, volumeName: name });
245
357
  results.push({
246
358
  success: dmgResult.success,
@@ -253,7 +365,7 @@ async function packageMacOS(config, outDir) {
253
365
  results.push(...await notarizeIfRequested(opts, outputPath, "dmg"));
254
366
  }
255
367
  if (opts.pkg || opts.appStore) {
256
- const outputPath = join(outDir, `${name}-${version2}.pkg`);
368
+ const outputPath = join2(outDir, `${name}-${version2}.pkg`);
257
369
  const pkgResult = await createPKG({
258
370
  appBundlePath,
259
371
  outputPath,
@@ -311,7 +423,7 @@ async function packageWindows(config, outDir) {
311
423
  name,
312
424
  version: version2,
313
425
  binaryPath: config.binaryPath,
314
- outputPath: join(outDir, `${name}-${version2}.msi`),
426
+ outputPath: join2(outDir, `${name}-${version2}.msi`),
315
427
  manufacturer: config.author || "Unknown",
316
428
  architecture: opts.architecture || windowsArchitecture(process.arch),
317
429
  additionalFiles: opts.additionalFiles,
@@ -335,7 +447,7 @@ async function packageWindows(config, outDir) {
335
447
  manufacturer: config.author || "Unknown",
336
448
  binaryPath: config.binaryPath,
337
449
  additionalFiles: opts.additionalFiles,
338
- outputPath: join(outDir, `${name}-${version2}-windows.zip`)
450
+ outputPath: join2(outDir, `${name}-${version2}-windows.zip`)
339
451
  });
340
452
  results.push({
341
453
  success: zipResult.success,
@@ -356,7 +468,7 @@ async function packageLinux(config, outDir) {
356
468
  name,
357
469
  version: version2,
358
470
  binaryPath: config.binaryPath,
359
- outputPath: join(outDir, `${name}_${version2}_amd64.deb`),
471
+ outputPath: join2(outDir, `${name}_${version2}_amd64.deb`),
360
472
  description: config.description || "",
361
473
  maintainer: config.author || "Unknown",
362
474
  dependencies: opts.debDependencies || [...DEFAULT_DEB_DEPENDENCIES],
@@ -376,7 +488,7 @@ async function packageLinux(config, outDir) {
376
488
  name,
377
489
  version: version2,
378
490
  binaryPath: config.binaryPath,
379
- outputPath: join(outDir, `${name}-${version2}-1.x86_64.rpm`),
491
+ outputPath: join2(outDir, `${name}-${version2}-1.x86_64.rpm`),
380
492
  description: config.description || "",
381
493
  requires: opts.rpmDependencies || ["gtk3", "webkit2gtk3"]
382
494
  });
@@ -393,7 +505,7 @@ async function packageLinux(config, outDir) {
393
505
  name,
394
506
  version: version2,
395
507
  binaryPath: config.binaryPath,
396
- outputPath: join(outDir, `${name}-${version2}-x86_64.AppImage`),
508
+ outputPath: join2(outDir, `${name}-${version2}-x86_64.AppImage`),
397
509
  iconPath: config.iconPath
398
510
  });
399
511
  results.push({
@@ -538,32 +650,32 @@ function notarytoolArguments(opts) {
538
650
  function createMacOSAppBundle(opts) {
539
651
  try {
540
652
  const { name, binaryPath, iconPath, provisioningProfile, additionalExecutables, outputPath } = opts;
541
- const contents = join(outputPath, "Contents");
542
- const macOS = join(contents, "MacOS");
653
+ const contents = join2(outputPath, "Contents");
654
+ const macOS = join2(contents, "MacOS");
543
655
  mkdirSync(macOS, { recursive: true });
544
- mkdirSync(join(contents, "Resources"), { recursive: true });
545
- copyFileSync(binaryPath, join(macOS, name));
546
- chmodSync(join(macOS, name), 493);
656
+ mkdirSync(join2(contents, "Resources"), { recursive: true });
657
+ copyFileSync(binaryPath, join2(macOS, name));
658
+ chmodSync(join2(macOS, name), 493);
547
659
  for (const executable of additionalExecutables || []) {
548
- if (!existsSync2(executable))
660
+ if (!existsSync3(executable))
549
661
  return { success: false, error: `Additional executable not found: ${executable}` };
550
- const destination = join(macOS, basename(executable));
662
+ const destination = join2(macOS, basename(executable));
551
663
  copyFileSync(executable, destination);
552
664
  chmodSync(destination, 493);
553
665
  }
554
666
  let iconName;
555
667
  if (iconPath) {
556
- if (!existsSync2(iconPath))
668
+ if (!existsSync3(iconPath))
557
669
  return { success: false, error: `Icon not found: ${iconPath}` };
558
670
  iconName = basename(iconPath).replace(/\.icns$/i, "");
559
- copyFileSync(iconPath, join(contents, "Resources", `${iconName}.icns`));
671
+ copyFileSync(iconPath, join2(contents, "Resources", `${iconName}.icns`));
560
672
  }
561
673
  if (provisioningProfile) {
562
- if (!existsSync2(provisioningProfile))
674
+ if (!existsSync3(provisioningProfile))
563
675
  return { success: false, error: `Provisioning profile not found: ${provisioningProfile}` };
564
- copyFileSync(provisioningProfile, join(contents, "embedded.provisionprofile"));
676
+ copyFileSync(provisioningProfile, join2(contents, "embedded.provisionprofile"));
565
677
  }
566
- writeFileSync(join(contents, "Info.plist"), macOSInfoPlist({ ...opts, iconName }));
678
+ writeFileSync2(join2(contents, "Info.plist"), macOSInfoPlist({ ...opts, iconName }));
567
679
  return { success: true };
568
680
  } catch (error) {
569
681
  return { success: false, error: error.message };
@@ -574,7 +686,7 @@ function pathContentBytes(path) {
574
686
  const stat = lstatSync(path);
575
687
  if (!stat.isDirectory())
576
688
  return stat.size;
577
- return readdirSync(path).reduce((total, entry) => total + pathContentBytes(join(path, entry)), 0);
689
+ return readdirSync2(path).reduce((total, entry) => total + pathContentBytes(join2(path, entry)), 0);
578
690
  }
579
691
  function dmgCapacityMegabytes(contentBytes) {
580
692
  if (!Number.isSafeInteger(contentBytes) || contentBytes < 0)
@@ -637,7 +749,7 @@ async function createDMG(opts) {
637
749
  if (!shouldRetryHdiutil(result.error, attempt))
638
750
  return { success: false, error: failures.join(`
639
751
  `) };
640
- rmSync(opts.outputPath, { force: true });
752
+ rmSync2(opts.outputPath, { force: true });
641
753
  await new Promise((resolve) => setTimeout(resolve, attempt * 2000));
642
754
  }
643
755
  return { success: false, error: failures.join(`
@@ -695,16 +807,16 @@ async function createPKG(opts) {
695
807
  }));
696
808
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
697
809
  }
698
- const tempDir = mkdtempSync(join(tmpdir(), "craft-pkg-"));
810
+ const tempDir = createTempDir("craft-pkg-");
699
811
  try {
700
- const root = join(tempDir, "root");
701
- const appsDir = join(root, "Applications");
812
+ const root = join2(tempDir, "root");
813
+ const appsDir = join2(root, "Applications");
702
814
  mkdirSync(appsDir, { recursive: true, mode: 493 });
703
815
  chmodSync(root, 493);
704
816
  const bundleName = basename(opts.appBundlePath);
705
- cpSync(opts.appBundlePath, join(appsDir, bundleName), { recursive: true });
706
- const componentPlistPath = join(tempDir, "component.plist");
707
- writeFileSync(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
817
+ cpSync(opts.appBundlePath, join2(appsDir, bundleName), { recursive: true });
818
+ const componentPlistPath = join2(tempDir, "component.plist");
819
+ writeFileSync2(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
708
820
  const built = await runTool("pkgbuild", pkgbuildArguments({
709
821
  root,
710
822
  componentPlistPath,
@@ -715,7 +827,7 @@ async function createPKG(opts) {
715
827
  }));
716
828
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
717
829
  } finally {
718
- rmSync(tempDir, { recursive: true, force: true });
830
+ removeTempDir(tempDir);
719
831
  }
720
832
  }
721
833
  function xml(value) {
@@ -812,7 +924,7 @@ function windowsCompanionFiles(paths, executableName) {
812
924
  if (seen.has(name.toLowerCase()))
813
925
  throw new Error(`Duplicate Windows package file: ${name}`);
814
926
  seen.add(name.toLowerCase());
815
- if (!existsSync2(path) || lstatSync(path).isDirectory())
927
+ if (!existsSync3(path) || lstatSync(path).isDirectory())
816
928
  throw new Error(`Windows companion file not found: ${path}`);
817
929
  return { path, name };
818
930
  });
@@ -835,18 +947,18 @@ function runCommand(command, args, cwd) {
835
947
  });
836
948
  }
837
949
  async function createMSI(opts) {
838
- const tempDir = mkdtempSync(join(tmpdir(), "craft-msi-"));
950
+ const tempDir = createTempDir("craft-msi-");
839
951
  try {
840
952
  const binaryName = windowsExecutableName(opts.name);
841
953
  const additionalFiles = windowsCompanionFiles(opts.additionalFiles, binaryName);
842
- const sourcePath = join(tempDir, binaryName);
843
- const wxsPath = join(tempDir, "installer.wxs");
844
- const wixobjPath = join(tempDir, "installer.wixobj");
954
+ const sourcePath = join2(tempDir, binaryName);
955
+ const wxsPath = join2(tempDir, "installer.wxs");
956
+ const wixobjPath = join2(tempDir, "installer.wixobj");
845
957
  copyFileSync(opts.binaryPath, sourcePath);
846
- writeFileSync(join(tempDir, WINDOWS_NOTIFICATION_ID_FILE), windowsNotificationAppId(opts.name, opts.manufacturer));
958
+ writeFileSync2(join2(tempDir, WINDOWS_NOTIFICATION_ID_FILE), windowsNotificationAppId(opts.name, opts.manufacturer));
847
959
  for (const file of additionalFiles)
848
- copyFileSync(file.path, join(tempDir, file.name));
849
- writeFileSync(wxsPath, renderWixSource(opts, binaryName, additionalFiles.map((file) => file.name)));
960
+ copyFileSync(file.path, join2(tempDir, file.name));
961
+ writeFileSync2(wxsPath, renderWixSource(opts, binaryName, additionalFiles.map((file) => file.name)));
850
962
  await runCommand("candle.exe", candleArguments(opts.architecture, wixobjPath, wxsPath), tempDir);
851
963
  await runCommand("light.exe", ["-nologo", "-sval", "-out", opts.outputPath, wixobjPath], tempDir);
852
964
  if (opts.certificatePath) {
@@ -863,7 +975,7 @@ async function createMSI(opts) {
863
975
  const missingTool = /ENOENT|not found/i.test(message);
864
976
  return { success: false, error: missingTool ? `WiX Toolset not found: ${message}` : message };
865
977
  } finally {
866
- rmSync(tempDir, { recursive: true, force: true });
978
+ removeTempDir(tempDir);
867
979
  }
868
980
  }
869
981
  async function createZIP(opts) {
@@ -875,7 +987,7 @@ async function createZIP(opts) {
875
987
  for (const file of additionalFiles)
876
988
  entries.push({ name: file.name, data: new Uint8Array(readFileSync(file.path)) });
877
989
  const zip = buildZip(entries);
878
- writeFileSync(opts.outputPath, zip);
990
+ writeFileSync2(opts.outputPath, zip);
879
991
  return { success: true, outputPath: opts.outputPath };
880
992
  } catch (err) {
881
993
  return { success: false, error: `Failed to write ZIP: ${err.message}` };
@@ -883,6 +995,7 @@ async function createZIP(opts) {
883
995
  }
884
996
  async function createDEB(opts) {
885
997
  return new Promise((resolve) => {
998
+ let staging;
886
999
  try {
887
1000
  const sanitizedName = opts.name.toLowerCase();
888
1001
  const safeDebName = /^[a-z0-9._+-]+$/;
@@ -905,16 +1018,17 @@ async function createDEB(opts) {
905
1018
  }
906
1019
  }
907
1020
  const desktopContent = linuxDesktopEntry(opts.name, sanitizedName, opts.urlSchemes, opts.launchUrl);
908
- const tempDir = mkdtempSync(join(tmpdir(), "craft-deb-"));
909
- const debianDir = join(tempDir, "DEBIAN");
910
- const binDir = join(tempDir, "usr", "bin");
911
- const applicationsDir = join(tempDir, "usr", "share", "applications");
1021
+ const tempDir = createTempDir("craft-deb-");
1022
+ staging = tempDir;
1023
+ const debianDir = join2(tempDir, "DEBIAN");
1024
+ const binDir = join2(tempDir, "usr", "bin");
1025
+ const applicationsDir = join2(tempDir, "usr", "share", "applications");
912
1026
  mkdirSync(debianDir, { recursive: true });
913
1027
  mkdirSync(binDir, { recursive: true });
914
1028
  mkdirSync(applicationsDir, { recursive: true });
915
1029
  const binaryName = sanitizedName;
916
- copyFileSync(opts.binaryPath, join(binDir, binaryName));
917
- chmodSync(join(binDir, binaryName), 493);
1030
+ copyFileSync(opts.binaryPath, join2(binDir, binaryName));
1031
+ chmodSync(join2(binDir, binaryName), 493);
918
1032
  const controlContent = `Package: ${sanitizedName}
919
1033
  Version: ${opts.version}
920
1034
  Section: utils
@@ -924,11 +1038,11 @@ Depends: ${opts.dependencies.join(", ")}
924
1038
  Maintainer: ${maintainer}
925
1039
  Description: ${description || opts.name}
926
1040
  `;
927
- writeFileSync(join(debianDir, "control"), controlContent);
928
- writeFileSync(join(applicationsDir, `${binaryName}.desktop`), desktopContent);
1041
+ writeFileSync2(join2(debianDir, "control"), controlContent);
1042
+ writeFileSync2(join2(applicationsDir, `${binaryName}.desktop`), desktopContent);
929
1043
  const proc = spawn("dpkg-deb", ["--build", tempDir, opts.outputPath]);
930
1044
  proc.on("close", (code) => {
931
- rmSync(tempDir, { recursive: true, force: true });
1045
+ removeTempDir(tempDir);
932
1046
  if (code === 0) {
933
1047
  resolve({ success: true, outputPath: opts.outputPath });
934
1048
  } else {
@@ -936,10 +1050,12 @@ Description: ${description || opts.name}
936
1050
  }
937
1051
  });
938
1052
  proc.on("error", (err) => {
939
- rmSync(tempDir, { recursive: true, force: true });
1053
+ removeTempDir(tempDir);
940
1054
  resolve({ success: false, error: err.message });
941
1055
  });
942
1056
  } catch (error) {
1057
+ if (staging)
1058
+ removeTempDir(staging);
943
1059
  resolve({ success: false, error: error.message });
944
1060
  }
945
1061
  });
@@ -947,11 +1063,11 @@ Description: ${description || opts.name}
947
1063
  async function createRPM(opts) {
948
1064
  return new Promise((resolve) => {
949
1065
  try {
950
- const buildRoot = join(homedir(), "rpmbuild");
951
- const specDir = join(buildRoot, "SPECS");
952
- const sourcesDir = join(buildRoot, "SOURCES");
953
- const buildDir = join(buildRoot, "BUILD");
954
- const rpmsDir = join(buildRoot, "RPMS");
1066
+ const buildRoot = join2(homedir(), "rpmbuild");
1067
+ const specDir = join2(buildRoot, "SPECS");
1068
+ const sourcesDir = join2(buildRoot, "SOURCES");
1069
+ const buildDir = join2(buildRoot, "BUILD");
1070
+ const rpmsDir = join2(buildRoot, "RPMS");
955
1071
  mkdirSync(specDir, { recursive: true });
956
1072
  mkdirSync(sourcesDir, { recursive: true });
957
1073
  mkdirSync(buildDir, { recursive: true });
@@ -975,7 +1091,7 @@ async function createRPM(opts) {
975
1091
  }
976
1092
  return r;
977
1093
  }).join(", ");
978
- copyFileSync(opts.binaryPath, join(sourcesDir, sanitizedName));
1094
+ copyFileSync(opts.binaryPath, join2(sourcesDir, sanitizedName));
979
1095
  const specContent = `Name: ${sanitizedName}
980
1096
  Version: ${opts.version}
981
1097
  Release: 1%{?dist}
@@ -993,13 +1109,13 @@ install -m 755 %{SOURCE0} %{buildroot}/usr/bin/${sanitizedName}
993
1109
  %files
994
1110
  /usr/bin/${sanitizedName}
995
1111
  `;
996
- const specPath = join(specDir, `${opts.name.toLowerCase()}.spec`);
997
- writeFileSync(specPath, specContent);
1112
+ const specPath = join2(specDir, `${opts.name.toLowerCase()}.spec`);
1113
+ writeFileSync2(specPath, specContent);
998
1114
  const proc = spawn("rpmbuild", ["-bb", specPath]);
999
1115
  proc.on("close", (code) => {
1000
1116
  if (code === 0) {
1001
1117
  const rpmName = `${opts.name.toLowerCase()}-${opts.version}-1.x86_64.rpm`;
1002
- const builtRpmPath = join(rpmsDir, "x86_64", rpmName);
1118
+ const builtRpmPath = join2(rpmsDir, "x86_64", rpmName);
1003
1119
  try {
1004
1120
  copyFileSync(builtRpmPath, opts.outputPath);
1005
1121
  resolve({ success: true, outputPath: opts.outputPath });
@@ -1020,24 +1136,26 @@ install -m 755 %{SOURCE0} %{buildroot}/usr/bin/${sanitizedName}
1020
1136
  }
1021
1137
  async function createAppImage(opts) {
1022
1138
  return new Promise((resolve) => {
1139
+ let staging;
1023
1140
  try {
1024
- const appDir = mkdtempSync(join(tmpdir(), "craft-appimage-"));
1025
- const appDirPath = join(appDir, `${opts.name}.AppDir`);
1026
- const binDir = join(appDirPath, "usr", "bin");
1027
- const shareDir = join(appDirPath, "usr", "share");
1141
+ const appDir = createTempDir("craft-appimage-");
1142
+ staging = appDir;
1143
+ const appDirPath = join2(appDir, `${opts.name}.AppDir`);
1144
+ const binDir = join2(appDirPath, "usr", "bin");
1145
+ const shareDir = join2(appDirPath, "usr", "share");
1028
1146
  mkdirSync(binDir, { recursive: true });
1029
1147
  mkdirSync(shareDir, { recursive: true });
1030
1148
  const binaryName = opts.name.toLowerCase();
1031
- copyFileSync(opts.binaryPath, join(binDir, binaryName));
1032
- chmodSync(join(binDir, binaryName), 493);
1149
+ copyFileSync(opts.binaryPath, join2(binDir, binaryName));
1150
+ chmodSync(join2(binDir, binaryName), 493);
1033
1151
  const appRunContent = `#!/bin/bash
1034
1152
  SELF=$(readlink -f "$0")
1035
1153
  HERE=\${SELF%/*}
1036
1154
  export PATH="\${HERE}/usr/bin/:\${PATH}"
1037
1155
  exec "\${HERE}/usr/bin/${binaryName}" "$@"
1038
1156
  `;
1039
- writeFileSync(join(appDirPath, "AppRun"), appRunContent);
1040
- chmodSync(join(appDirPath, "AppRun"), 493);
1157
+ writeFileSync2(join2(appDirPath, "AppRun"), appRunContent);
1158
+ chmodSync(join2(appDirPath, "AppRun"), 493);
1041
1159
  const desktopContent = `[Desktop Entry]
1042
1160
  Type=Application
1043
1161
  Name=${opts.name}
@@ -1046,9 +1164,9 @@ Terminal=false
1046
1164
  Categories=Utility;
1047
1165
  Icon=${binaryName}
1048
1166
  `;
1049
- writeFileSync(join(appDirPath, `${binaryName}.desktop`), desktopContent);
1050
- if (opts.iconPath && existsSync2(opts.iconPath)) {
1051
- copyFileSync(opts.iconPath, join(appDirPath, `${binaryName}.png`));
1167
+ writeFileSync2(join2(appDirPath, `${binaryName}.desktop`), desktopContent);
1168
+ if (opts.iconPath && existsSync3(opts.iconPath)) {
1169
+ copyFileSync(opts.iconPath, join2(appDirPath, `${binaryName}.png`));
1052
1170
  } else {
1053
1171
  const placeholderPng = Buffer.from([
1054
1172
  137,
@@ -1121,13 +1239,13 @@ Icon=${binaryName}
1121
1239
  96,
1122
1240
  130
1123
1241
  ]);
1124
- writeFileSync(join(appDirPath, `${binaryName}.png`), placeholderPng);
1242
+ writeFileSync2(join2(appDirPath, `${binaryName}.png`), placeholderPng);
1125
1243
  }
1126
1244
  const proc = spawn("appimagetool", [appDirPath, opts.outputPath], {
1127
1245
  env: { ...process.env, ARCH: "x86_64" }
1128
1246
  });
1129
1247
  proc.on("close", (code) => {
1130
- rmSync(appDir, { recursive: true, force: true });
1248
+ removeTempDir(appDir);
1131
1249
  if (code === 0) {
1132
1250
  chmodSync(opts.outputPath, 493);
1133
1251
  resolve({ success: true, outputPath: opts.outputPath });
@@ -1136,10 +1254,12 @@ Icon=${binaryName}
1136
1254
  }
1137
1255
  });
1138
1256
  proc.on("error", (err) => {
1139
- rmSync(appDir, { recursive: true, force: true });
1257
+ removeTempDir(appDir);
1140
1258
  resolve({ success: false, error: `appimagetool not found: ${err.message}` });
1141
1259
  });
1142
1260
  } catch (error) {
1261
+ if (staging)
1262
+ removeTempDir(staging);
1143
1263
  resolve({ success: false, error: error.message });
1144
1264
  }
1145
1265
  });
@@ -9996,18 +10116,17 @@ __export(exports_updater, {
9996
10116
  DeltaGenerator: () => DeltaGenerator,
9997
10117
  AutoUpdater: () => AutoUpdater
9998
10118
  });
9999
- 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";
10000
- import { join as join3, basename as basename3, dirname as dirname2 } from "path";
10001
- import { tmpdir as tmpdir3 } from "os";
10119
+ import { createReadStream, createWriteStream, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmdirSync, statSync as statSync3, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
10120
+ import { join as join4, basename as basename3, dirname as dirname2 } from "path";
10121
+ import { tmpdir as tmpdir2 } from "os";
10002
10122
  import { execFileSync as execFileSync2, spawn as spawn3 } from "child_process";
10003
10123
  import { createHash as createHash2 } from "crypto";
10004
10124
  import { EventEmitter as EventEmitter3 } from "events";
10005
10125
 
10006
10126
  // src/updater/macos-bundle.ts
10007
10127
  import { execFile, execFileSync } from "child_process";
10008
- import { accessSync, constants, existsSync as existsSync3, mkdtempSync as mkdtempSync2, renameSync, rmSync as rmSync2, statSync } from "fs";
10009
- import { tmpdir as tmpdir2 } from "os";
10010
- import { basename as basename2, dirname, join as join2 } from "path";
10128
+ import { accessSync, constants, existsSync as existsSync4, renameSync, rmSync as rmSync3, statSync as statSync2 } from "fs";
10129
+ import { basename as basename2, dirname, join as join3 } from "path";
10011
10130
  import { promisify } from "util";
10012
10131
  var execFileAsync = promisify(execFile);
10013
10132
  function isMacOS() {
@@ -10045,7 +10164,7 @@ ${e.stdout ?? ""}`;
10045
10164
  async function verifyBundleTrust(appPath, policy = {}) {
10046
10165
  const identity = await readBundleIdentity(appPath);
10047
10166
  const requireNotarized = policy.requireNotarized ?? true;
10048
- if (!existsSync3(appPath)) {
10167
+ if (!existsSync4(appPath)) {
10049
10168
  return { ok: false, identity, gatekeeperSource: null, reason: "unreadable", detail: `No such bundle: ${appPath}` };
10050
10169
  }
10051
10170
  try {
@@ -10104,17 +10223,19 @@ function findTopLevelApp(root) {
10104
10223
  return parts[0] ?? null;
10105
10224
  }
10106
10225
  async function extractBundleFromDmg(dmgPath, stagingDir) {
10107
- const staging = stagingDir ?? mkdtempSync2(join2(tmpdir2(), "craft-update-"));
10108
- const mountPoint = mkdtempSync2(join2(tmpdir2(), "craft-mount-"));
10226
+ const staging = stagingDir ?? createTempDir("craft-update-");
10227
+ const mountPoint = createTempDir("craft-mount-");
10109
10228
  let mounted = false;
10229
+ let staged = false;
10110
10230
  try {
10111
10231
  await execFileAsync("hdiutil", ["attach", dmgPath, "-nobrowse", "-readonly", "-mountpoint", mountPoint]);
10112
10232
  mounted = true;
10113
10233
  const source = findTopLevelApp(mountPoint);
10114
10234
  if (!source)
10115
10235
  throw new Error(`No .app found in disk image: ${dmgPath}`);
10116
- const appPath = join2(staging, basename2(source));
10236
+ const appPath = join3(staging, basename2(source));
10117
10237
  await dittoBundle(source, appPath);
10238
+ staged = true;
10118
10239
  return { appPath, stagingDir: staging };
10119
10240
  } finally {
10120
10241
  if (mounted) {
@@ -10122,19 +10243,25 @@ async function extractBundleFromDmg(dmgPath, stagingDir) {
10122
10243
  await execFileAsync("hdiutil", ["detach", mountPoint, "-force"]);
10123
10244
  } catch {}
10124
10245
  }
10125
- try {
10126
- rmSync2(mountPoint, { recursive: true, force: true });
10127
- } catch {}
10246
+ removeTempDir(mountPoint);
10247
+ if (!staged && !stagingDir)
10248
+ removeTempDir(staging);
10128
10249
  }
10129
10250
  }
10130
10251
  async function extractBundleFromZip(zipPath, stagingDir) {
10131
- const staging = stagingDir ?? mkdtempSync2(join2(tmpdir2(), "craft-update-"));
10132
- const unpacked = join2(staging, "payload");
10133
- await execFileAsync("ditto", ["-x", "-k", zipPath, unpacked]);
10134
- const source = findTopLevelApp(unpacked);
10135
- if (!source)
10136
- throw new Error(`No .app found in archive: ${zipPath}`);
10137
- return { appPath: source, stagingDir: staging };
10252
+ const staging = stagingDir ?? createTempDir("craft-update-");
10253
+ const unpacked = join3(staging, "payload");
10254
+ try {
10255
+ await execFileAsync("ditto", ["-x", "-k", zipPath, unpacked]);
10256
+ const source = findTopLevelApp(unpacked);
10257
+ if (!source)
10258
+ throw new Error(`No .app found in archive: ${zipPath}`);
10259
+ return { appPath: source, stagingDir: staging };
10260
+ } catch (error) {
10261
+ if (!stagingDir)
10262
+ removeTempDir(staging);
10263
+ throw error;
10264
+ }
10138
10265
  }
10139
10266
  async function extractBundle(archivePath, stagingDir) {
10140
10267
  if (archivePath.endsWith(".dmg"))
@@ -10144,18 +10271,18 @@ async function extractBundle(archivePath, stagingDir) {
10144
10271
  throw new Error(`Cannot extract an app bundle from ${archivePath}`);
10145
10272
  }
10146
10273
  async function swapBundle(stagedPath, installedPath) {
10147
- if (!existsSync3(stagedPath))
10274
+ if (!existsSync4(stagedPath))
10148
10275
  throw new Error(`Staged bundle is missing: ${stagedPath}`);
10149
10276
  const parent = dirname(installedPath);
10150
10277
  const stamp = `${Date.now()}.${process.pid}`;
10151
- const incoming = join2(parent, `.${basename2(installedPath)}.${stamp}.incoming`);
10152
- const retired = join2(parent, `.${basename2(installedPath)}.${stamp}.retired`);
10278
+ const incoming = join3(parent, `.${basename2(installedPath)}.${stamp}.incoming`);
10279
+ const retired = join3(parent, `.${basename2(installedPath)}.${stamp}.retired`);
10153
10280
  try {
10154
10281
  renameSync(stagedPath, incoming);
10155
10282
  } catch {
10156
10283
  await dittoBundle(stagedPath, incoming);
10157
10284
  }
10158
- const hadPrevious = existsSync3(installedPath);
10285
+ const hadPrevious = existsSync4(installedPath);
10159
10286
  try {
10160
10287
  if (hadPrevious)
10161
10288
  renameSync(installedPath, retired);
@@ -10171,13 +10298,13 @@ async function swapBundle(stagedPath, installedPath) {
10171
10298
  }
10172
10299
  } catch (error) {
10173
10300
  try {
10174
- rmSync2(incoming, { recursive: true, force: true });
10301
+ rmSync3(incoming, { recursive: true, force: true });
10175
10302
  } catch {}
10176
10303
  throw error;
10177
10304
  }
10178
10305
  if (hadPrevious) {
10179
10306
  try {
10180
- rmSync2(retired, { recursive: true, force: true });
10307
+ rmSync3(retired, { recursive: true, force: true });
10181
10308
  return { previousPath: null };
10182
10309
  } catch {
10183
10310
  return { previousPath: retired };
@@ -10189,8 +10316,8 @@ function canReplaceBundle(installedPath) {
10189
10316
  try {
10190
10317
  const parent = dirname(installedPath);
10191
10318
  accessSync(parent, constants.W_OK);
10192
- if (existsSync3(installedPath))
10193
- statSync(installedPath);
10319
+ if (existsSync4(installedPath))
10320
+ statSync2(installedPath);
10194
10321
  return true;
10195
10322
  } catch {
10196
10323
  return false;
@@ -10229,7 +10356,7 @@ class AutoUpdater extends EventEmitter3 {
10229
10356
  downloadDir() {
10230
10357
  if (this.config.downloadDir)
10231
10358
  return this.config.downloadDir;
10232
- return join3(tmpdir3(), "craft-updates", basename3(this.config.appPath) || "app");
10359
+ return join4(tmpdir2(), "craft-updates", basename3(this.config.appPath) || "app");
10233
10360
  }
10234
10361
  getLastError() {
10235
10362
  return this.lastError;
@@ -10330,7 +10457,7 @@ class AutoUpdater extends EventEmitter3 {
10330
10457
  const downloadDir = this.downloadDir();
10331
10458
  mkdirSync2(downloadDir, { recursive: true });
10332
10459
  const fileName = basename3(url);
10333
- this.downloadPath = join3(downloadDir, fileName);
10460
+ this.downloadPath = join4(downloadDir, fileName);
10334
10461
  try {
10335
10462
  const response = await fetch(url);
10336
10463
  if (!response.ok) {
@@ -10393,11 +10520,11 @@ class AutoUpdater extends EventEmitter3 {
10393
10520
  throw new Error("Download verification failed: hash mismatch");
10394
10521
  }
10395
10522
  if (deltaUpdate) {
10396
- if (!statSync2(this.config.appPath).isFile()) {
10523
+ if (!statSync3(this.config.appPath).isFile()) {
10397
10524
  throw new Error("Delta updates require appPath to point to the installed bundle file");
10398
10525
  }
10399
10526
  const patchPath = this.downloadPath;
10400
- const reconstructedPath = join3(downloadDir, `reconstructed-${basename3(platformUpdate.url)}`);
10527
+ const reconstructedPath = join4(downloadDir, `reconstructed-${basename3(platformUpdate.url)}`);
10401
10528
  await DeltaGenerator.apply(this.config.appPath, patchPath, reconstructedPath);
10402
10529
  const reconstructedHash = await this.computeFileHash(reconstructedPath);
10403
10530
  if (reconstructedHash !== platformUpdate.sha256) {
@@ -10435,7 +10562,7 @@ class AutoUpdater extends EventEmitter3 {
10435
10562
  }
10436
10563
  }
10437
10564
  async installUpdate(restartAfter = true) {
10438
- if (!this.downloadPath || !existsSync4(this.downloadPath)) {
10565
+ if (!this.downloadPath || !existsSync5(this.downloadPath)) {
10439
10566
  throw new Error("No update downloaded");
10440
10567
  }
10441
10568
  if (this.updateInfo) {
@@ -10479,7 +10606,7 @@ class AutoUpdater extends EventEmitter3 {
10479
10606
  await this.installLinuxUpdate();
10480
10607
  break;
10481
10608
  }
10482
- if (this.downloadPath && existsSync4(this.downloadPath)) {
10609
+ if (this.downloadPath && existsSync5(this.downloadPath)) {
10483
10610
  unlinkSync(this.downloadPath);
10484
10611
  }
10485
10612
  try {
@@ -10519,9 +10646,7 @@ class AutoUpdater extends EventEmitter3 {
10519
10646
  leftoverPath: previousPath
10520
10647
  });
10521
10648
  } finally {
10522
- try {
10523
- rmSync3(staged.stagingDir, { recursive: true, force: true });
10524
- } catch {}
10649
+ removeTempDir(staged.stagingDir);
10525
10650
  }
10526
10651
  }
10527
10652
  async assertBundleTrusted(bundlePath) {
@@ -10682,7 +10807,7 @@ class DeltaGenerator {
10682
10807
  static async generate(oldPath, newPath, outputPath) {
10683
10808
  try {
10684
10809
  execFileSync2("bsdiff", [oldPath, newPath, outputPath]);
10685
- const stats = statSync2(outputPath);
10810
+ const stats = statSync3(outputPath);
10686
10811
  const hash = await DeltaGenerator.computeHash(outputPath);
10687
10812
  return {
10688
10813
  size: stats.size,
@@ -10690,7 +10815,7 @@ class DeltaGenerator {
10690
10815
  };
10691
10816
  } catch {
10692
10817
  execFileSync2("xdelta3", ["-e", "-s", oldPath, newPath, outputPath]);
10693
- const stats = statSync2(outputPath);
10818
+ const stats = statSync3(outputPath);
10694
10819
  const hash = await DeltaGenerator.computeHash(outputPath);
10695
10820
  return {
10696
10821
  size: stats.size,
@@ -10804,7 +10929,7 @@ ${update.releaseNotes}`);
10804
10929
  releaseDate: new Date().toISOString(),
10805
10930
  platforms: {}
10806
10931
  };
10807
- writeFileSync2(outputPath, JSON.stringify(manifest, null, 2));
10932
+ writeFileSync3(outputPath, JSON.stringify(manifest, null, 2));
10808
10933
  console.log(`Manifest generated: ${outputPath}`);
10809
10934
  console.log("Edit the file to add platform-specific update URLs");
10810
10935
  break;
@@ -10892,6 +11017,7 @@ function detectDevMode() {
10892
11017
  class CraftApp {
10893
11018
  process;
10894
11019
  config;
11020
+ sidebarConfigDir;
10895
11021
  constructor(config = {}) {
10896
11022
  if (config.window?.width !== undefined && (config.window.width < 100 || config.window.width > 1e4)) {
10897
11023
  throw new Error(`Invalid window width: ${config.window.width}. Must be between 100 and 10000 pixels.`);
@@ -10928,8 +11054,15 @@ class CraftApp {
10928
11054
  throw new Error(`No content to display. Provide either html or url:
10929
11055
  ` + " show(html, options) or loadURL(url, options)");
10930
11056
  }
11057
+ this.removeSidebarConfig();
10931
11058
  const args = this.buildArgs();
10932
- const craftPath = await this.findCraftBinary();
11059
+ let craftPath;
11060
+ try {
11061
+ craftPath = await this.findCraftBinary();
11062
+ } catch (error) {
11063
+ this.removeSidebarConfig();
11064
+ throw error;
11065
+ }
10933
11066
  probeBinaryVersion(craftPath);
10934
11067
  const quiet = this.config.quiet === true;
10935
11068
  this.process = spawn4(craftPath, args, {
@@ -10942,6 +11075,7 @@ class CraftApp {
10942
11075
  });
10943
11076
  return new Promise((resolve, reject) => {
10944
11077
  this.process?.on("error", (error) => {
11078
+ this.removeSidebarConfig();
10945
11079
  if (error.code === "ENOENT") {
10946
11080
  reject(new Error(craftBinaryNotFoundMessage(craftPath)));
10947
11081
  return;
@@ -10959,6 +11093,7 @@ class CraftApp {
10959
11093
  ` + " https://github.com/home-lang/craft/issues"));
10960
11094
  });
10961
11095
  this.process?.on("close", (code) => {
11096
+ this.removeSidebarConfig();
10962
11097
  if (code === 0 || code === null) {
10963
11098
  resolve();
10964
11099
  } else {
@@ -10976,6 +11111,12 @@ class CraftApp {
10976
11111
  this.process.kill();
10977
11112
  }
10978
11113
  }
11114
+ removeSidebarConfig() {
11115
+ if (!this.sidebarConfigDir)
11116
+ return;
11117
+ removeTempDir(this.sidebarConfigDir);
11118
+ this.sidebarConfigDir = undefined;
11119
+ }
10979
11120
  buildArgs() {
10980
11121
  const args = [];
10981
11122
  const { window: window3, html, url, appName } = this.config;
@@ -11074,9 +11215,10 @@ class CraftApp {
11074
11215
  if (safeForArgv) {
11075
11216
  args.push("--sidebar-config", json);
11076
11217
  } else {
11077
- const dir = mkdtempSync3(join4(tmpdir4(), "craft-sidebar-"));
11078
- const path = join4(dir, "sidebar-config.json");
11079
- writeFileSync3(path, json, "utf-8");
11218
+ const dir = createTempDir("craft-sidebar-");
11219
+ this.sidebarConfigDir = dir;
11220
+ const path = join5(dir, "sidebar-config.json");
11221
+ writeFileSync4(path, json, "utf-8");
11080
11222
  args.push("--sidebar-config-file", path);
11081
11223
  }
11082
11224
  }