app-builder-lib 26.9.1 → 26.11.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.
Files changed (113) hide show
  1. package/out/ProtonFramework.d.ts +1 -1
  2. package/out/ProtonFramework.js +2 -2
  3. package/out/ProtonFramework.js.map +1 -1
  4. package/out/binDownload.d.ts +2 -18
  5. package/out/binDownload.js +33 -185
  6. package/out/binDownload.js.map +1 -1
  7. package/out/codeSign/windowsSignToolManager.js +1 -1
  8. package/out/codeSign/windowsSignToolManager.js.map +1 -1
  9. package/out/configuration.d.ts +100 -68
  10. package/out/configuration.js.map +1 -1
  11. package/out/core.d.ts +2 -2
  12. package/out/core.js.map +1 -1
  13. package/out/electron/ElectronFramework.d.ts +2 -19
  14. package/out/electron/ElectronFramework.js +47 -40
  15. package/out/electron/ElectronFramework.js.map +1 -1
  16. package/out/electron/injectFFMPEG.d.ts +10 -1
  17. package/out/electron/injectFFMPEG.js +48 -34
  18. package/out/electron/injectFFMPEG.js.map +1 -1
  19. package/out/frameworks/LibUiFramework.d.ts +3 -2
  20. package/out/frameworks/LibUiFramework.js +5 -2
  21. package/out/frameworks/LibUiFramework.js.map +1 -1
  22. package/out/index.d.ts +3 -2
  23. package/out/index.js +1 -1
  24. package/out/index.js.map +1 -1
  25. package/out/macPackager.js +24 -3
  26. package/out/macPackager.js.map +1 -1
  27. package/out/node-module-collector/moduleManager.d.ts +7 -1
  28. package/out/node-module-collector/moduleManager.js +9 -2
  29. package/out/node-module-collector/moduleManager.js.map +1 -1
  30. package/out/node-module-collector/nodeModulesCollector.js +5 -3
  31. package/out/node-module-collector/nodeModulesCollector.js.map +1 -1
  32. package/out/node-module-collector/pnpmNodeModulesCollector.d.ts +26 -2
  33. package/out/node-module-collector/pnpmNodeModulesCollector.js +98 -18
  34. package/out/node-module-collector/pnpmNodeModulesCollector.js.map +1 -1
  35. package/out/options/AppXOptions.d.ts +1 -1
  36. package/out/options/AppXOptions.js.map +1 -1
  37. package/out/options/PlatformSpecificBuildOptions.d.ts +31 -11
  38. package/out/options/PlatformSpecificBuildOptions.js.map +1 -1
  39. package/out/options/SnapOptions.d.ts +1 -1
  40. package/out/options/SnapOptions.js.map +1 -1
  41. package/out/options/linuxOptions.d.ts +38 -12
  42. package/out/options/linuxOptions.js.map +1 -1
  43. package/out/options/macOptions.d.ts +10 -7
  44. package/out/options/macOptions.js.map +1 -1
  45. package/out/options/metadata.d.ts +1 -2
  46. package/out/options/metadata.js.map +1 -1
  47. package/out/options/pkgOptions.d.ts +1 -1
  48. package/out/options/pkgOptions.js.map +1 -1
  49. package/out/options/winOptions.d.ts +15 -5
  50. package/out/options/winOptions.js.map +1 -1
  51. package/out/packager.js +12 -12
  52. package/out/packager.js.map +1 -1
  53. package/out/platformPackager.js +2 -5
  54. package/out/platformPackager.js.map +1 -1
  55. package/out/targets/FpmTarget.js +3 -2
  56. package/out/targets/FpmTarget.js.map +1 -1
  57. package/out/targets/LinuxTargetHelper.d.ts +4 -4
  58. package/out/targets/LinuxTargetHelper.js.map +1 -1
  59. package/out/targets/MsiTarget.js +1 -1
  60. package/out/targets/MsiTarget.js.map +1 -1
  61. package/out/targets/appimage/AppImageTarget.d.ts +1 -0
  62. package/out/targets/appimage/AppImageTarget.js +30 -10
  63. package/out/targets/appimage/AppImageTarget.js.map +1 -1
  64. package/out/targets/appimage/appImageUtil.d.ts +30 -2
  65. package/out/targets/appimage/appImageUtil.js +26 -24
  66. package/out/targets/appimage/appImageUtil.js.map +1 -1
  67. package/out/targets/appimage/appLauncher.d.ts +1 -1
  68. package/out/targets/appimage/appLauncher.js +7 -3
  69. package/out/targets/appimage/appLauncher.js.map +1 -1
  70. package/out/targets/archive.js +20 -6
  71. package/out/targets/archive.js.map +1 -1
  72. package/out/targets/nsis/NsisTarget.js +11 -11
  73. package/out/targets/nsis/NsisTarget.js.map +1 -1
  74. package/out/targets/nsis/nsisOptions.d.ts +20 -16
  75. package/out/targets/nsis/nsisOptions.js.map +1 -1
  76. package/out/targets/nsis/nsisScriptGenerator.d.ts +1 -0
  77. package/out/targets/nsis/nsisScriptGenerator.js +14 -1
  78. package/out/targets/nsis/nsisScriptGenerator.js.map +1 -1
  79. package/out/targets/nsis/nsisUtil.js +4 -3
  80. package/out/targets/nsis/nsisUtil.js.map +1 -1
  81. package/out/toolsets/linux.js +8 -8
  82. package/out/toolsets/linux.js.map +1 -1
  83. package/out/toolsets/windows.d.ts +16 -16
  84. package/out/toolsets/windows.js +29 -21
  85. package/out/toolsets/windows.js.map +1 -1
  86. package/out/util/NodeModuleCopyHelper.js +1 -1
  87. package/out/util/NodeModuleCopyHelper.js.map +1 -1
  88. package/out/util/appBuilder.js +1 -1
  89. package/out/util/appBuilder.js.map +1 -1
  90. package/out/util/cacheState.d.ts +28 -0
  91. package/out/util/cacheState.js +143 -0
  92. package/out/util/cacheState.js.map +1 -0
  93. package/out/util/electronGet.d.ts +56 -0
  94. package/out/util/electronGet.js +388 -0
  95. package/out/util/electronGet.js.map +1 -0
  96. package/out/util/macroExpander.js +3 -0
  97. package/out/util/macroExpander.js.map +1 -1
  98. package/out/util/resEdit.d.ts +10 -0
  99. package/out/util/resEdit.js +48 -0
  100. package/out/util/resEdit.js.map +1 -0
  101. package/out/util/resolve.d.ts +1 -1
  102. package/out/util/resolve.js +16 -1
  103. package/out/util/resolve.js.map +1 -1
  104. package/out/version.d.ts +1 -1
  105. package/out/version.js +1 -1
  106. package/out/version.js.map +1 -1
  107. package/out/vm/ParallelsVm.d.ts +16 -0
  108. package/out/vm/ParallelsVm.js +0 -1
  109. package/out/vm/ParallelsVm.js.map +1 -1
  110. package/out/winPackager.js +44 -42
  111. package/out/winPackager.js.map +1 -1
  112. package/package.json +11 -9
  113. package/scheme.json +672 -135
@@ -0,0 +1,388 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCacheDirectory = getCacheDirectory;
4
+ exports.extractArchive = extractArchive;
5
+ exports.downloadBuilderToolset = downloadBuilderToolset;
6
+ exports.downloadElectronArtifactZip = downloadElectronArtifactZip;
7
+ exports.downloadElectronArtifact = downloadElectronArtifact;
8
+ exports.getBinariesMirrorUrl = getBinariesMirrorUrl;
9
+ const get = require("@electron/get");
10
+ const get_1 = require("@electron/get");
11
+ const builder_util_1 = require("builder-util");
12
+ const multiProgress_1 = require("electron-publish/out/multiProgress");
13
+ const fs_1 = require("fs");
14
+ const fs = require("fs/promises");
15
+ const os = require("os");
16
+ const path = require("path");
17
+ const lockfile = require("proper-lockfile");
18
+ const promises_1 = require("stream/promises");
19
+ const tar = require("tar");
20
+ const unzipper = require("unzipper");
21
+ const cacheState_1 = require("./cacheState");
22
+ function hashUrlSafe(input, length = 6) {
23
+ let hash = 5381;
24
+ for (let i = 0; i < input.length; i++) {
25
+ hash = ((hash << 5) + hash) ^ input.charCodeAt(i);
26
+ }
27
+ hash >>>= 0;
28
+ const out = hash.toString(36);
29
+ return out.length >= length ? out.slice(0, length) : out.padStart(length, "0");
30
+ }
31
+ function getCacheDirectory(isAvoidSystemOnWindows = false, allowEnvVarOverride = true) {
32
+ var _a, _b, _c, _d, _e;
33
+ const env = (_a = process.env.ELECTRON_BUILDER_CACHE) === null || _a === void 0 ? void 0 : _a.trim();
34
+ if (allowEnvVarOverride && env && path.parse(env).root) {
35
+ return env;
36
+ }
37
+ const appName = "electron-builder";
38
+ const platform = os.platform();
39
+ const homeDir = os.homedir();
40
+ if (platform === "darwin") {
41
+ return path.join(homeDir, "Library", "Caches", appName);
42
+ }
43
+ if (platform === "win32") {
44
+ const localAppData = (_b = process.env.LOCALAPPDATA) === null || _b === void 0 ? void 0 : _b.trim();
45
+ const username = (_d = (_c = process.env.USERNAME) === null || _c === void 0 ? void 0 : _c.trim()) === null || _d === void 0 ? void 0 : _d.toLowerCase();
46
+ const isSystemUser = isAvoidSystemOnWindows && (((_e = localAppData === null || localAppData === void 0 ? void 0 : localAppData.toLowerCase()) === null || _e === void 0 ? void 0 : _e.includes("\\windows\\system32\\")) || username === "system");
47
+ if (!localAppData || isSystemUser) {
48
+ return path.join(os.tmpdir(), `${appName}-cache`);
49
+ }
50
+ return path.join(localAppData, appName, "Cache");
51
+ }
52
+ const xdgCache = process.env.XDG_CACHE_HOME;
53
+ return xdgCache && path.parse(xdgCache).root ? path.join(xdgCache, appName) : path.join(homeDir, ".cache", appName);
54
+ }
55
+ function resolveCacheMode() {
56
+ var _a;
57
+ const varName = "ELECTRON_DOWNLOAD_CACHE_MODE";
58
+ const cacheOverride = (_a = process.env[varName]) === null || _a === void 0 ? void 0 : _a.trim();
59
+ if (cacheOverride && Number(cacheOverride) in get_1.ElectronDownloadCacheMode) {
60
+ const mode = Number(cacheOverride);
61
+ builder_util_1.log.debug({ mode }, `cache mode overridden via env var ${varName}`);
62
+ return mode;
63
+ }
64
+ return get_1.ElectronDownloadCacheMode.ReadWrite;
65
+ }
66
+ async function extractZipStreaming(file, dir) {
67
+ var _a;
68
+ // Pass 1: read central directory once to collect Unix modes (one seek to EOF)
69
+ const zipDir = await unzipper.Open.file(file);
70
+ const entryModes = new Map();
71
+ for (const entry of zipDir.files) {
72
+ const mode = (entry.externalFileAttributes >> 16) & 0xffff;
73
+ if (mode > 0) {
74
+ entryModes.set(entry.path, mode);
75
+ }
76
+ }
77
+ const isSymlink = (mode) => (mode & 0o170000) === 0o120000;
78
+ // Pass 2: stream from byte 0 — no per-entry seeks, no Docker hang
79
+ const entries = (0, fs_1.createReadStream)(file).pipe(unzipper.Parse({ forceStream: true }));
80
+ for await (const entry of entries) {
81
+ const destPath = path.resolve(dir, entry.path);
82
+ if (!destPath.startsWith(dir + path.sep) && destPath !== dir) {
83
+ throw new Error(`Path traversal blocked: ${entry.path}`);
84
+ }
85
+ const mode = (_a = entryModes.get(entry.path)) !== null && _a !== void 0 ? _a : 0;
86
+ if (mode > 0 && isSymlink(mode)) {
87
+ const target = (await entry.buffer()).toString();
88
+ if (path.isAbsolute(target)) {
89
+ throw new Error(`Absolute symlink target blocked: ${target}`);
90
+ }
91
+ const resolvedTarget = path.resolve(path.dirname(destPath), target);
92
+ if (!resolvedTarget.startsWith(dir + path.sep) && resolvedTarget !== dir) {
93
+ throw new Error(`Symlink target escapes extraction dir: ${target}`);
94
+ }
95
+ await fs.mkdir(path.dirname(destPath), { recursive: true });
96
+ await fs.symlink(target, destPath);
97
+ }
98
+ else if (entry.type === "Directory") {
99
+ await fs.mkdir(destPath, { recursive: true });
100
+ entry.autodrain();
101
+ }
102
+ else {
103
+ await fs.mkdir(path.dirname(destPath), { recursive: true });
104
+ await (0, promises_1.pipeline)(entry, (0, fs_1.createWriteStream)(destPath));
105
+ if (mode > 0) {
106
+ await fs.chmod(destPath, mode & 0o7777);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ async function extractArchive(file, dir) {
112
+ const tmpDir = `${dir}.tmp`;
113
+ await fs.mkdir(tmpDir, { recursive: true });
114
+ const release = await lockfile.lock(tmpDir, {
115
+ retries: { retries: 5, minTimeout: 1000, maxTimeout: 5000 },
116
+ stale: 120000, // Increased from 60s to allow long-running extractions
117
+ });
118
+ try {
119
+ // rm + mkdir happen AFTER acquiring the lock so no concurrent caller can clear our tmpDir mid-extraction
120
+ await fs.rm(tmpDir, { recursive: true, force: true });
121
+ await fs.mkdir(tmpDir, { recursive: true });
122
+ if (file.endsWith(".tar.gz") || file.endsWith(".tgz")) {
123
+ await tar.extract({ file, cwd: tmpDir, strip: 1 });
124
+ }
125
+ else if (file.endsWith(".zip")) {
126
+ await extractZipStreaming(file, tmpDir);
127
+ }
128
+ else if (file.endsWith(".7z")) {
129
+ const cmd7za = await (0, builder_util_1.getPath7za)();
130
+ try {
131
+ await (0, builder_util_1.exec)(cmd7za, ["x", "-bd", file, `-o${tmpDir}`, "-y"]);
132
+ }
133
+ catch (e) {
134
+ // Check if extraction actually failed or just had benign warnings
135
+ const files = await fs.readdir(tmpDir);
136
+ if (files.length === 0) {
137
+ builder_util_1.log.warn({ file, tmpDir, error: e.message }, "7z extraction produced no output");
138
+ throw new Error(`7z extraction failed for ${file}: ${e.message}`);
139
+ }
140
+ // If files were extracted despite the error, log and continue
141
+ builder_util_1.log.warn({ error: e.message, filesExtracted: files.length }, "7z reported error but extracted files");
142
+ }
143
+ }
144
+ else {
145
+ throw new Error(`Unsupported archive format: ${path.basename(file)}`);
146
+ }
147
+ // Verify extraction produced files
148
+ const extractedFiles = await fs.readdir(tmpDir);
149
+ if (extractedFiles.length === 0) {
150
+ throw new Error(`Extraction of ${path.basename(file)} produced no files`);
151
+ }
152
+ await fs.rm(dir, { recursive: true, force: true });
153
+ await fs.rename(tmpDir, dir);
154
+ }
155
+ finally {
156
+ await release().catch(err => builder_util_1.log.warn({ err }, "failed to release lockfile"));
157
+ }
158
+ }
159
+ async function downloadArtifactToFile(config, label) {
160
+ var _a, _b;
161
+ let lastLoggedMilestone = -1;
162
+ const state = { bar: undefined };
163
+ const downloadOptions = {
164
+ timeout: { request: 10 * 60 * 1000 }, // prevent indefinite hang on stalled connections
165
+ ...config.downloadOptions,
166
+ getProgressCallback: info => {
167
+ // @electron/get passes downloadOptions (including this callback) to its internal
168
+ // SHASUMS256.txt validation download. That file is tiny (<1 MB) and fires at 100%
169
+ // immediately, producing a spurious bar even when the artifact itself is cached.
170
+ // Skip progress display for any download whose total size is known and small.
171
+ if (info.total && info.total < 1000000) {
172
+ return Promise.resolve();
173
+ }
174
+ if (!state.bar && process.stdout.isTTY) {
175
+ builder_util_1.log.info({ label }, "downloading");
176
+ state.bar = new multiProgress_1.MultiProgress().createBar(`${" ".repeat(builder_util_1.PADDING + 2)}[:bar] :percent | ${label}`, { total: 100 });
177
+ }
178
+ const pct = info.percent != null ? Math.floor(info.percent * 100) : 0;
179
+ if (state.bar) {
180
+ state.bar.update(pct);
181
+ }
182
+ else {
183
+ // log every 25% milestone for non-TTY environments (e.g. CI logs) to provide some visibility into download progress without overwhelming logs with too many updates
184
+ const percentCompleted = info.transferred && info.total ? Math.floor((info.transferred / info.total) * 100) : Math.floor(pct / 25) * 25;
185
+ if (percentCompleted > lastLoggedMilestone && percentCompleted > 0) {
186
+ lastLoggedMilestone = percentCompleted;
187
+ builder_util_1.log.info({ label, progress: `${percentCompleted}%` }, percentCompleted !== 100 ? "downloading" : "downloaded");
188
+ }
189
+ }
190
+ return Promise.resolve();
191
+ },
192
+ };
193
+ const configWithProgress = { ...config, downloadOptions };
194
+ try {
195
+ let filePath;
196
+ try {
197
+ filePath = await get.downloadArtifact(configWithProgress);
198
+ }
199
+ catch (err) {
200
+ if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string" && err.message.includes("dest already exists")) {
201
+ filePath = await get.downloadArtifact(configWithProgress);
202
+ }
203
+ else {
204
+ throw err;
205
+ }
206
+ }
207
+ if (!(await (0, builder_util_1.exists)(filePath))) {
208
+ builder_util_1.log.warn({ filePath, label }, "cached artifact missing from disk; retrying with cache write");
209
+ filePath = await get.downloadArtifact({ ...configWithProgress, cacheMode: get_1.ElectronDownloadCacheMode.WriteOnly });
210
+ }
211
+ if (!state.bar && lastLoggedMilestone === -1) {
212
+ builder_util_1.log.info({ label }, "using cached artifact");
213
+ }
214
+ return filePath;
215
+ }
216
+ finally {
217
+ (_a = state.bar) === null || _a === void 0 ? void 0 : _a.update(100);
218
+ (_b = state.bar) === null || _b === void 0 ? void 0 : _b.terminate();
219
+ }
220
+ }
221
+ /**
222
+ * Core download + extract engine. Handles lockfile, cache-hit short-circuit,
223
+ * progress bar, extraction (.zip or .tar.gz), and completion marker.
224
+ * Both public download functions delegate here after building their respective configs.
225
+ */
226
+ async function downloadAndExtract(config, extractDir, label) {
227
+ await fs.mkdir(extractDir, { recursive: true });
228
+ // Pre-lock fast path: only short-circuit for a definitively complete and valid cache.
229
+ // Do NOT clean up here — concurrent processes could race to delete under each other.
230
+ const stateData = await (0, cacheState_1.readCacheStateFile)(extractDir);
231
+ if ((stateData === null || stateData === void 0 ? void 0 : stateData.state) === cacheState_1.CacheState.complete) {
232
+ const isValid = await (0, cacheState_1.validateCacheDirectory)(extractDir, stateData.fileCount);
233
+ if (isValid) {
234
+ builder_util_1.log.debug({ file: label, path: extractDir }, "using cached artifact - cache valid");
235
+ return extractDir;
236
+ }
237
+ }
238
+ const release = await lockfile.lock(extractDir, {
239
+ retries: { retries: 5, minTimeout: 1000, maxTimeout: 5000 },
240
+ stale: 120000,
241
+ });
242
+ let downloadedFile = null;
243
+ try {
244
+ // Re-check after acquiring lock: another worker may have completed while we waited.
245
+ const stateDataAfterLock = await (0, cacheState_1.readCacheStateFile)(extractDir);
246
+ if ((stateDataAfterLock === null || stateDataAfterLock === void 0 ? void 0 : stateDataAfterLock.state) === cacheState_1.CacheState.complete) {
247
+ const isValid = await (0, cacheState_1.validateCacheDirectory)(extractDir, stateDataAfterLock.fileCount);
248
+ if (isValid) {
249
+ builder_util_1.log.debug({ file: label, path: extractDir }, "using cached artifact - skipping download/extract");
250
+ return extractDir;
251
+ }
252
+ builder_util_1.log.warn({ extractDir }, "Cache marked complete but files invalid, clearing");
253
+ }
254
+ else if ((stateDataAfterLock === null || stateDataAfterLock === void 0 ? void 0 : stateDataAfterLock.state) === cacheState_1.CacheState.corrupted) {
255
+ builder_util_1.log.warn({ extractDir }, "Corrupted cache detected, cleaning up and re-extracting");
256
+ }
257
+ // Cleanup inside the lock — skip lock files since we currently hold them
258
+ await (0, cacheState_1.cleanupCacheDirectory)(extractDir, { skipLockFiles: true });
259
+ await fs.mkdir(extractDir, { recursive: true });
260
+ builder_util_1.log.debug({ file: label }, "downloading");
261
+ downloadedFile = await downloadArtifactToFile(config, label);
262
+ if (!downloadedFile) {
263
+ throw new Error(`Failed to download artifact: ${label}`);
264
+ }
265
+ await (0, cacheState_1.writeCacheState)(extractDir, cacheState_1.CacheState.downloaded);
266
+ // Mark extracting so stale-lock detection can recover a crashed mid-extraction process
267
+ await (0, cacheState_1.writeCacheState)(extractDir, cacheState_1.CacheState.extracting);
268
+ // Extract while holding the lock to prevent concurrent interference
269
+ await extractArchive(downloadedFile, extractDir);
270
+ // Compute metadata with a full recursive walk now that tmpDir has been renamed to extractDir
271
+ const { fileCount, extractedSize } = await (0, cacheState_1.computeCacheMetadata)(extractDir);
272
+ // Write complete state with metadata BEFORE releasing the lock; throwOnError=true so a
273
+ // failed write (disk full, permissions) propagates instead of silently producing a no-op cache
274
+ await (0, cacheState_1.writeCacheState)(extractDir, cacheState_1.CacheState.complete, { fileCount, extractedSize }, true);
275
+ }
276
+ finally {
277
+ await release().catch(err => builder_util_1.log.warn({ err }, "failed to release lockfile"));
278
+ }
279
+ return extractDir;
280
+ }
281
+ // ─── Public API ───────────────────────────────────────────────────────────────
282
+ /**
283
+ * Downloads a generic artifact (.tar.gz or .zip) from a GitHub release.
284
+ * Used for electron-builder-binaries tools (appimage, etc.).
285
+ */
286
+ async function downloadBuilderToolset(options) {
287
+ const { releaseName, filenameWithExt, checksums, githubOrgRepo = "electron-userland/electron-builder-binaries", overrideUrl } = options;
288
+ const baseUrl = getBinariesMirrorUrl(githubOrgRepo);
289
+ const hashInput = overrideUrl ? `${overrideUrl}/${filenameWithExt}` : `${baseUrl}${releaseName}/${filenameWithExt}`;
290
+ const suffix = hashUrlSafe(hashInput, 5);
291
+ const folderName = `${filenameWithExt.replace(/\.(tar\.gz|tgz|zip|7z)$/, "")}-${suffix}`;
292
+ const extractDir = path.join(getCacheDirectory(), releaseName, folderName);
293
+ const mirrorOptions = overrideUrl
294
+ ? { resolveAssetURL: async () => Promise.resolve(`${overrideUrl}/${filenameWithExt}`) }
295
+ : {
296
+ // `${mirror}${customDir}/${customFilename}`
297
+ mirror: baseUrl,
298
+ customDir: releaseName,
299
+ customFilename: filenameWithExt,
300
+ };
301
+ const config = {
302
+ version: "9.9.9", // must be >1.3.2 to bypass @electron/get validation shortcut
303
+ artifactName: filenameWithExt,
304
+ cacheRoot: path.resolve(getCacheDirectory(), "downloads"),
305
+ cacheMode: resolveCacheMode(),
306
+ ...(checksums != null ? { checksums } : { unsafelyDisableChecksums: true }),
307
+ mirrorOptions,
308
+ isGeneric: true,
309
+ };
310
+ return downloadAndExtract(config, extractDir, filenameWithExt);
311
+ }
312
+ // Keys present in ElectronGetOptions but absent from ElectronDownloadOptions.
313
+ // Used to discriminate between the two config shapes at runtime.
314
+ const ELECTRON_GET_EXCLUSIVE_KEYS = ["mirrorOptions", "force", "unsafelyDisableChecksums"];
315
+ function isElectronGetOptions(dl) {
316
+ return ELECTRON_GET_EXCLUSIVE_KEYS.some(k => Object.hasOwnProperty.call(dl, k));
317
+ }
318
+ /**
319
+ * Downloads and extracts an electron platform artifact (e.g. ffmpeg) using @electron/get.
320
+ * Deduplicates concurrent calls for the same artifact within the same process.
321
+ */
322
+ function buildElectronArtifactConfig(options) {
323
+ const { electronDownload, arch, version, platformName: platform, artifactName, cacheDir: cacheRoot } = options;
324
+ let artifactConfig = { cacheRoot, platform, arch, version, artifactName };
325
+ if (electronDownload != null) {
326
+ if (isElectronGetOptions(electronDownload)) {
327
+ const { mirrorOptions, ...rest } = electronDownload;
328
+ artifactConfig = { ...artifactConfig, ...rest, cacheRoot, mirrorOptions };
329
+ }
330
+ else {
331
+ const { mirror, customDir, cache, customFilename, isVerifyChecksum, strictSSL, platform: overridePlatform, arch: overrideArch } = electronDownload;
332
+ artifactConfig = {
333
+ ...artifactConfig,
334
+ unsafelyDisableChecksums: isVerifyChecksum === false,
335
+ cacheRoot: cache !== null && cache !== void 0 ? cache : cacheRoot,
336
+ mirrorOptions: {
337
+ mirror: mirror || undefined,
338
+ customDir: customDir || undefined,
339
+ customFilename: customFilename || undefined,
340
+ },
341
+ ...(strictSSL === false ? { downloadOptions: { https: { rejectUnauthorized: false } } } : {}),
342
+ };
343
+ if (overridePlatform != null) {
344
+ artifactConfig.platform = overridePlatform;
345
+ }
346
+ if (overrideArch != null) {
347
+ artifactConfig.arch = overrideArch;
348
+ }
349
+ }
350
+ }
351
+ artifactConfig.cacheMode = resolveCacheMode();
352
+ return artifactConfig;
353
+ }
354
+ /**
355
+ * Downloads the electron artifact zip via @electron/get (with caching) and returns the zip file path.
356
+ * Use when you need to extract the zip yourself (e.g. directly to appOutDir to preserve empty dirs and symlinks).
357
+ */
358
+ function downloadElectronArtifactZip(options) {
359
+ const config = buildElectronArtifactConfig(options);
360
+ return downloadArtifactToFile(config, config.artifactName);
361
+ }
362
+ async function downloadElectronArtifact(options) {
363
+ const { arch, version, platformName: platform, artifactName } = options;
364
+ const artifactConfig = buildElectronArtifactConfig(options);
365
+ const suffix = hashUrlSafe(JSON.stringify(artifactConfig), 5);
366
+ const folderName = `${artifactName}-v${version}-${platform}-${arch}-${suffix}`;
367
+ const extractDir = path.join(getCacheDirectory(), `${artifactName}-v${version}`, folderName);
368
+ return downloadAndExtract(artifactConfig, extractDir, artifactName);
369
+ }
370
+ /**
371
+ * Get the binaries mirror URL from environment variables.
372
+ * Supports various npm config formats and falls back to GitHub.
373
+ */
374
+ function getBinariesMirrorUrl(githubOrgRepo) {
375
+ for (const envVar of [
376
+ "NPM_CONFIG_ELECTRON_BUILDER_BINARIES_MIRROR",
377
+ "npm_config_electron_builder_binaries_mirror",
378
+ "npm_package_config_electron_builder_binaries_mirror",
379
+ "ELECTRON_BUILDER_BINARIES_MIRROR",
380
+ ]) {
381
+ const url = (0, builder_util_1.parseValidEnvVarUrl)(envVar);
382
+ if (url) {
383
+ return url.endsWith("/") ? url : `${url}/`;
384
+ }
385
+ }
386
+ return `https://github.com/${githubOrgRepo}/releases/download/`;
387
+ }
388
+ //# sourceMappingURL=electronGet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"electronGet.js","sourceRoot":"","sources":["../../src/util/electronGet.ts"],"names":[],"mappings":"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","sourcesContent":["import * as get from \"@electron/get\"\nimport {\n ElectronDownloadCacheMode,\n ElectronDownloadRequest,\n ElectronDownloadRequestOptions,\n ElectronPlatformArtifactDetails,\n GotDownloaderOptions,\n MirrorOptions,\n} from \"@electron/get\"\nimport { exec, exists, getPath7za, log, PADDING, parseValidEnvVarUrl } from \"builder-util\"\nimport { MultiProgress } from \"electron-publish/out/multiProgress\"\nimport { createReadStream, createWriteStream } from \"fs\"\nimport * as fs from \"fs/promises\"\nimport * as os from \"os\"\nimport * as path from \"path\"\nimport * as lockfile from \"proper-lockfile\"\nimport { pipeline } from \"stream/promises\"\nimport * as tar from \"tar\"\nimport * as unzipper from \"unzipper\"\nimport { ElectronPlatformName } from \"../electron/ElectronFramework\"\nimport { CacheState, cleanupCacheDirectory, computeCacheMetadata, readCacheStateFile, validateCacheDirectory, writeCacheState } from \"./cacheState\"\nimport type { ProgressBar } from \"electron-publish\"\n\nexport type ElectronGetOptions = Omit<\n ElectronPlatformArtifactDetails,\n | \"platform\"\n | \"arch\"\n | \"version\"\n | \"artifactName\"\n | \"artifactSuffix\"\n | \"customFilename\"\n | \"tempDirectory\"\n | \"downloader\"\n | \"cacheMode\"\n | \"cacheRoot\"\n | \"downloadOptions\"\n | \"isGeneric\"\n | \"mirrorOptions\" // to be added below\n> & {\n mirrorOptions?: Omit<MirrorOptions, \"customDir\" | \"customFilename\" | \"customVersion\">\n}\n\nexport type ArtifactDownloadOptions = {\n electronDownload?: ElectronGetOptions | ElectronDownloadOptions | null\n artifactName: string\n platformName: string\n arch: string\n version: string\n cacheDir?: string\n}\n\nexport interface ElectronDownloadOptions {\n // https://github.com/electron-userland/electron-builder/issues/3077\n // must be optional\n version?: string\n\n /**\n * The [cache location](https://github.com/electron-userland/electron-download#cache-location).\n */\n cache?: string | null\n\n /**\n * The mirror.\n */\n mirror?: string | null\n\n /** @private */\n customDir?: string | null\n /** @private */\n customFilename?: string | null\n\n strictSSL?: boolean\n isVerifyChecksum?: boolean\n\n platform?: ElectronPlatformName\n arch?: string\n}\n\nfunction hashUrlSafe(input: string, length = 6): string {\n let hash = 5381\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash) ^ input.charCodeAt(i)\n }\n hash >>>= 0\n const out = hash.toString(36)\n return out.length >= length ? out.slice(0, length) : out.padStart(length, \"0\")\n}\n\nexport function getCacheDirectory(isAvoidSystemOnWindows = false, allowEnvVarOverride = true): string {\n const env = process.env.ELECTRON_BUILDER_CACHE?.trim()\n if (allowEnvVarOverride && env && path.parse(env).root) {\n return env\n }\n\n const appName = \"electron-builder\"\n const platform = os.platform()\n const homeDir = os.homedir()\n\n if (platform === \"darwin\") {\n return path.join(homeDir, \"Library\", \"Caches\", appName)\n }\n if (platform === \"win32\") {\n const localAppData = process.env.LOCALAPPDATA?.trim()\n const username = process.env.USERNAME?.trim()?.toLowerCase()\n const isSystemUser = isAvoidSystemOnWindows && (localAppData?.toLowerCase()?.includes(\"\\\\windows\\\\system32\\\\\") || username === \"system\")\n if (!localAppData || isSystemUser) {\n return path.join(os.tmpdir(), `${appName}-cache`)\n }\n return path.join(localAppData, appName, \"Cache\")\n }\n\n const xdgCache = process.env.XDG_CACHE_HOME\n return xdgCache && path.parse(xdgCache).root ? path.join(xdgCache, appName) : path.join(homeDir, \".cache\", appName)\n}\n\nfunction resolveCacheMode(): ElectronDownloadCacheMode {\n const varName = \"ELECTRON_DOWNLOAD_CACHE_MODE\"\n const cacheOverride = process.env[varName]?.trim()\n if (cacheOverride && Number(cacheOverride) in ElectronDownloadCacheMode) {\n const mode = Number(cacheOverride) as ElectronDownloadCacheMode\n log.debug({ mode }, `cache mode overridden via env var ${varName}`)\n return mode\n }\n return ElectronDownloadCacheMode.ReadWrite\n}\n\nasync function extractZipStreaming(file: string, dir: string): Promise<void> {\n // Pass 1: read central directory once to collect Unix modes (one seek to EOF)\n const zipDir = await unzipper.Open.file(file)\n const entryModes = new Map<string, number>()\n for (const entry of zipDir.files) {\n const mode = (entry.externalFileAttributes >> 16) & 0xffff\n if (mode > 0) {\n entryModes.set(entry.path, mode)\n }\n }\n const isSymlink = (mode: number) => (mode & 0o170000) === 0o120000\n\n // Pass 2: stream from byte 0 — no per-entry seeks, no Docker hang\n const entries = createReadStream(file).pipe(unzipper.Parse({ forceStream: true }))\n for await (const entry of entries as AsyncIterable<unzipper.Entry>) {\n const destPath = path.resolve(dir, entry.path)\n if (!destPath.startsWith(dir + path.sep) && destPath !== dir) {\n throw new Error(`Path traversal blocked: ${entry.path}`)\n }\n const mode = entryModes.get(entry.path) ?? 0\n if (mode > 0 && isSymlink(mode)) {\n const target = (await entry.buffer()).toString()\n if (path.isAbsolute(target)) {\n throw new Error(`Absolute symlink target blocked: ${target}`)\n }\n const resolvedTarget = path.resolve(path.dirname(destPath), target)\n if (!resolvedTarget.startsWith(dir + path.sep) && resolvedTarget !== dir) {\n throw new Error(`Symlink target escapes extraction dir: ${target}`)\n }\n await fs.mkdir(path.dirname(destPath), { recursive: true })\n await fs.symlink(target, destPath)\n } else if (entry.type === \"Directory\") {\n await fs.mkdir(destPath, { recursive: true })\n entry.autodrain()\n } else {\n await fs.mkdir(path.dirname(destPath), { recursive: true })\n await pipeline(entry, createWriteStream(destPath))\n if (mode > 0) {\n await fs.chmod(destPath, mode & 0o7777)\n }\n }\n }\n}\n\nexport async function extractArchive(file: string, dir: string) {\n const tmpDir = `${dir}.tmp`\n await fs.mkdir(tmpDir, { recursive: true })\n\n const release = await lockfile.lock(tmpDir, {\n retries: { retries: 5, minTimeout: 1000, maxTimeout: 5000 },\n stale: 120000, // Increased from 60s to allow long-running extractions\n })\n\n try {\n // rm + mkdir happen AFTER acquiring the lock so no concurrent caller can clear our tmpDir mid-extraction\n await fs.rm(tmpDir, { recursive: true, force: true })\n await fs.mkdir(tmpDir, { recursive: true })\n\n if (file.endsWith(\".tar.gz\") || file.endsWith(\".tgz\")) {\n await tar.extract({ file, cwd: tmpDir, strip: 1 })\n } else if (file.endsWith(\".zip\")) {\n await extractZipStreaming(file, tmpDir)\n } else if (file.endsWith(\".7z\")) {\n const cmd7za = await getPath7za()\n try {\n await exec(cmd7za, [\"x\", \"-bd\", file, `-o${tmpDir}`, \"-y\"])\n } catch (e: any) {\n // Check if extraction actually failed or just had benign warnings\n const files = await fs.readdir(tmpDir)\n if (files.length === 0) {\n log.warn({ file, tmpDir, error: e.message }, \"7z extraction produced no output\")\n throw new Error(`7z extraction failed for ${file}: ${e.message}`)\n }\n // If files were extracted despite the error, log and continue\n log.warn({ error: e.message, filesExtracted: files.length }, \"7z reported error but extracted files\")\n }\n } else {\n throw new Error(`Unsupported archive format: ${path.basename(file)}`)\n }\n\n // Verify extraction produced files\n const extractedFiles = await fs.readdir(tmpDir)\n if (extractedFiles.length === 0) {\n throw new Error(`Extraction of ${path.basename(file)} produced no files`)\n }\n\n await fs.rm(dir, { recursive: true, force: true })\n await fs.rename(tmpDir, dir)\n } finally {\n await release().catch(err => log.warn({ err }, \"failed to release lockfile\"))\n }\n}\n\nasync function downloadArtifactToFile(config: Parameters<typeof get.downloadArtifact>[0], label: string): Promise<string> {\n let lastLoggedMilestone = -1\n const state: { bar: ProgressBar | undefined } = { bar: undefined }\n\n const downloadOptions: GotDownloaderOptions = {\n timeout: { request: 10 * 60 * 1000 }, // prevent indefinite hang on stalled connections\n ...config.downloadOptions,\n getProgressCallback: info => {\n // @electron/get passes downloadOptions (including this callback) to its internal\n // SHASUMS256.txt validation download. That file is tiny (<1 MB) and fires at 100%\n // immediately, producing a spurious bar even when the artifact itself is cached.\n // Skip progress display for any download whose total size is known and small.\n if (info.total && info.total < 1_000_000) {\n return Promise.resolve()\n }\n if (!state.bar && process.stdout.isTTY) {\n log.info({ label }, \"downloading\")\n state.bar = new MultiProgress().createBar(`${\" \".repeat(PADDING + 2)}[:bar] :percent | ${label}`, { total: 100 })\n }\n const pct = info.percent != null ? Math.floor(info.percent * 100) : 0\n if (state.bar) {\n state.bar.update(pct)\n } else {\n // log every 25% milestone for non-TTY environments (e.g. CI logs) to provide some visibility into download progress without overwhelming logs with too many updates\n const percentCompleted = info.transferred && info.total ? Math.floor((info.transferred / info.total) * 100) : Math.floor(pct / 25) * 25\n if (percentCompleted > lastLoggedMilestone && percentCompleted > 0) {\n lastLoggedMilestone = percentCompleted\n log.info({ label, progress: `${percentCompleted}%` }, percentCompleted !== 100 ? \"downloading\" : \"downloaded\")\n }\n }\n return Promise.resolve()\n },\n }\n\n const configWithProgress = { ...config, downloadOptions }\n try {\n let filePath: string\n try {\n filePath = await get.downloadArtifact(configWithProgress)\n } catch (err) {\n if (typeof (err as any)?.message === \"string\" && (err as any).message.includes(\"dest already exists\")) {\n filePath = await get.downloadArtifact(configWithProgress)\n } else {\n throw err\n }\n }\n if (!(await exists(filePath))) {\n log.warn({ filePath, label }, \"cached artifact missing from disk; retrying with cache write\")\n filePath = await get.downloadArtifact({ ...configWithProgress, cacheMode: ElectronDownloadCacheMode.WriteOnly })\n }\n if (!state.bar && lastLoggedMilestone === -1) {\n log.info({ label }, \"using cached artifact\")\n }\n return filePath\n } finally {\n state.bar?.update(100)\n state.bar?.terminate()\n }\n}\n\n/**\n * Core download + extract engine. Handles lockfile, cache-hit short-circuit,\n * progress bar, extraction (.zip or .tar.gz), and completion marker.\n * Both public download functions delegate here after building their respective configs.\n */\nasync function downloadAndExtract(config: Parameters<typeof get.downloadArtifact>[0], extractDir: string, label: string): Promise<string> {\n await fs.mkdir(extractDir, { recursive: true })\n\n // Pre-lock fast path: only short-circuit for a definitively complete and valid cache.\n // Do NOT clean up here — concurrent processes could race to delete under each other.\n const stateData = await readCacheStateFile(extractDir)\n if (stateData?.state === CacheState.complete) {\n const isValid = await validateCacheDirectory(extractDir, stateData.fileCount)\n if (isValid) {\n log.debug({ file: label, path: extractDir }, \"using cached artifact - cache valid\")\n return extractDir\n }\n }\n\n const release = await lockfile.lock(extractDir, {\n retries: { retries: 5, minTimeout: 1000, maxTimeout: 5000 },\n stale: 120000,\n })\n let downloadedFile: string | null = null\n try {\n // Re-check after acquiring lock: another worker may have completed while we waited.\n const stateDataAfterLock = await readCacheStateFile(extractDir)\n if (stateDataAfterLock?.state === CacheState.complete) {\n const isValid = await validateCacheDirectory(extractDir, stateDataAfterLock.fileCount)\n if (isValid) {\n log.debug({ file: label, path: extractDir }, \"using cached artifact - skipping download/extract\")\n return extractDir\n }\n log.warn({ extractDir }, \"Cache marked complete but files invalid, clearing\")\n } else if (stateDataAfterLock?.state === CacheState.corrupted) {\n log.warn({ extractDir }, \"Corrupted cache detected, cleaning up and re-extracting\")\n }\n\n // Cleanup inside the lock — skip lock files since we currently hold them\n await cleanupCacheDirectory(extractDir, { skipLockFiles: true })\n await fs.mkdir(extractDir, { recursive: true })\n\n log.debug({ file: label }, \"downloading\")\n downloadedFile = await downloadArtifactToFile(config, label)\n if (!downloadedFile) {\n throw new Error(`Failed to download artifact: ${label}`)\n }\n\n await writeCacheState(extractDir, CacheState.downloaded)\n\n // Mark extracting so stale-lock detection can recover a crashed mid-extraction process\n await writeCacheState(extractDir, CacheState.extracting)\n\n // Extract while holding the lock to prevent concurrent interference\n await extractArchive(downloadedFile, extractDir)\n\n // Compute metadata with a full recursive walk now that tmpDir has been renamed to extractDir\n const { fileCount, extractedSize } = await computeCacheMetadata(extractDir)\n\n // Write complete state with metadata BEFORE releasing the lock; throwOnError=true so a\n // failed write (disk full, permissions) propagates instead of silently producing a no-op cache\n await writeCacheState(extractDir, CacheState.complete, { fileCount, extractedSize }, true)\n } finally {\n await release().catch(err => log.warn({ err }, \"failed to release lockfile\"))\n }\n return extractDir\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Downloads a generic artifact (.tar.gz or .zip) from a GitHub release.\n * Used for electron-builder-binaries tools (appimage, etc.).\n */\nexport async function downloadBuilderToolset(options: {\n releaseName: string\n filenameWithExt: string\n checksums?: Record<string, string>\n githubOrgRepo?: string\n overrideUrl?: string\n}): Promise<string> {\n const { releaseName, filenameWithExt, checksums, githubOrgRepo = \"electron-userland/electron-builder-binaries\", overrideUrl } = options\n\n const baseUrl = getBinariesMirrorUrl(githubOrgRepo)\n const hashInput = overrideUrl ? `${overrideUrl}/${filenameWithExt}` : `${baseUrl}${releaseName}/${filenameWithExt}`\n const suffix = hashUrlSafe(hashInput, 5)\n const folderName = `${filenameWithExt.replace(/\\.(tar\\.gz|tgz|zip|7z)$/, \"\")}-${suffix}`\n const extractDir = path.join(getCacheDirectory(), releaseName, folderName)\n\n const mirrorOptions: MirrorOptions = overrideUrl\n ? { resolveAssetURL: async () => Promise.resolve(`${overrideUrl}/${filenameWithExt}`) }\n : {\n // `${mirror}${customDir}/${customFilename}`\n mirror: baseUrl,\n customDir: releaseName,\n customFilename: filenameWithExt,\n }\n\n const config: ElectronDownloadRequest & ElectronDownloadRequestOptions & { isGeneric: true } = {\n version: \"9.9.9\", // must be >1.3.2 to bypass @electron/get validation shortcut\n artifactName: filenameWithExt,\n cacheRoot: path.resolve(getCacheDirectory(), \"downloads\"),\n cacheMode: resolveCacheMode(),\n ...(checksums != null ? { checksums } : { unsafelyDisableChecksums: true }),\n mirrorOptions,\n isGeneric: true,\n }\n return downloadAndExtract(config, extractDir, filenameWithExt)\n}\n\n// Keys present in ElectronGetOptions but absent from ElectronDownloadOptions.\n// Used to discriminate between the two config shapes at runtime.\nconst ELECTRON_GET_EXCLUSIVE_KEYS: readonly string[] = [\"mirrorOptions\", \"force\", \"unsafelyDisableChecksums\"]\n\nfunction isElectronGetOptions(dl: ElectronGetOptions | ElectronDownloadOptions): dl is ElectronGetOptions {\n return ELECTRON_GET_EXCLUSIVE_KEYS.some(k => Object.hasOwnProperty.call(dl, k))\n}\n\n/**\n * Downloads and extracts an electron platform artifact (e.g. ffmpeg) using @electron/get.\n * Deduplicates concurrent calls for the same artifact within the same process.\n */\nfunction buildElectronArtifactConfig(options: ArtifactDownloadOptions): ElectronPlatformArtifactDetails {\n const { electronDownload, arch, version, platformName: platform, artifactName, cacheDir: cacheRoot } = options\n\n let artifactConfig: ElectronPlatformArtifactDetails = { cacheRoot, platform, arch, version, artifactName }\n\n if (electronDownload != null) {\n if (isElectronGetOptions(electronDownload)) {\n const { mirrorOptions, ...rest } = electronDownload\n artifactConfig = { ...artifactConfig, ...rest, cacheRoot, mirrorOptions }\n } else {\n const { mirror, customDir, cache, customFilename, isVerifyChecksum, strictSSL, platform: overridePlatform, arch: overrideArch } = electronDownload\n artifactConfig = {\n ...artifactConfig,\n unsafelyDisableChecksums: isVerifyChecksum === false,\n cacheRoot: cache ?? cacheRoot,\n mirrorOptions: {\n mirror: mirror || undefined,\n customDir: customDir || undefined,\n customFilename: customFilename || undefined,\n },\n ...(strictSSL === false ? { downloadOptions: { https: { rejectUnauthorized: false } } } : {}),\n }\n if (overridePlatform != null) {\n artifactConfig.platform = overridePlatform\n }\n if (overrideArch != null) {\n artifactConfig.arch = overrideArch\n }\n }\n }\n\n artifactConfig.cacheMode = resolveCacheMode()\n return artifactConfig\n}\n\n/**\n * Downloads the electron artifact zip via @electron/get (with caching) and returns the zip file path.\n * Use when you need to extract the zip yourself (e.g. directly to appOutDir to preserve empty dirs and symlinks).\n */\nexport function downloadElectronArtifactZip(options: ArtifactDownloadOptions): Promise<string> {\n const config = buildElectronArtifactConfig(options)\n return downloadArtifactToFile(config, config.artifactName)\n}\n\nexport async function downloadElectronArtifact(options: ArtifactDownloadOptions): Promise<string> {\n const { arch, version, platformName: platform, artifactName } = options\n const artifactConfig = buildElectronArtifactConfig(options)\n\n const suffix = hashUrlSafe(JSON.stringify(artifactConfig), 5)\n const folderName = `${artifactName}-v${version}-${platform}-${arch}-${suffix}`\n const extractDir = path.join(getCacheDirectory(), `${artifactName}-v${version}`, folderName)\n\n return downloadAndExtract(artifactConfig, extractDir, artifactName)\n}\n\n/**\n * Get the binaries mirror URL from environment variables.\n * Supports various npm config formats and falls back to GitHub.\n */\nexport function getBinariesMirrorUrl(githubOrgRepo: string): string {\n for (const envVar of [\n \"NPM_CONFIG_ELECTRON_BUILDER_BINARIES_MIRROR\",\n \"npm_config_electron_builder_binaries_mirror\",\n \"npm_package_config_electron_builder_binaries_mirror\",\n \"ELECTRON_BUILDER_BINARIES_MIRROR\",\n ]) {\n const url = parseValidEnvVarUrl(envVar)\n if (url) {\n return url.endsWith(\"/\") ? url : `${url}/`\n }\n }\n return `https://github.com/${githubOrgRepo}/releases/download/`\n}\n"]}
@@ -45,6 +45,9 @@ function expandMacro(pattern, arch, appInfo, extra = {}, isProductNameSanitized
45
45
  if (envValue == null) {
46
46
  throw new builder_util_1.InvalidConfigurationError(`cannot expand pattern "${pattern}": env ${envName} is not defined`, "ERR_ELECTRON_BUILDER_ENV_NOT_DEFINED");
47
47
  }
48
+ if (/TOKEN|SECRET|KEY|PASSWORD|CREDENTIAL/i.test(envName)) {
49
+ builder_util_1.log.warn({ envName, pattern }, "macro expansion includes an env var that may contain a secret — verify this is intentional");
50
+ }
48
51
  return envValue;
49
52
  }
50
53
  const value = extra[p1];
@@ -1 +1 @@
1
- {"version":3,"file":"macroExpander.js","sourceRoot":"","sources":["../../src/util/macroExpander.ts"],"names":[],"mappings":";;AAIA,kCA8DC;AAlED,+CAAwD;AAIxD,SAAgB,WAAW,CAAC,OAAe,EAAE,IAAsB,EAAE,OAAgB,EAAE,QAAa,EAAE,EAAE,sBAAsB,GAAG,IAAI;IACnI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO;YACf,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YACxB,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YACxB,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YACxB,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAC5B,CAAC;IAED,OAAO,OAAO,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE,EAAU,EAAE;QACpE,QAAQ,EAAE,EAAE,CAAC;YACX,KAAK,aAAa;gBAChB,OAAO,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAA;YAEpF,KAAK,MAAM;gBACT,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;oBACjB,wCAAwC;oBACxC,OAAO,EAAE,CAAA;gBACX,CAAC;gBACD,OAAO,IAAI,CAAA;YAEb,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAA;gBACvC,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;oBACxB,MAAM,IAAI,wCAAyB,CAAC,0BAA0B,OAAO,4BAA4B,EAAE,yCAAyC,CAAC,CAAA;gBAC/I,CAAC;gBACD,OAAO,WAAW,CAAA;YACpB,CAAC;YAED,KAAK,UAAU;gBACb,OAAO,OAAO,CAAC,QAAQ,CAAA;YAEzB,KAAK,SAAS;gBACZ,OAAO,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAA;YAEpC,OAAO,CAAC,CAAC,CAAC;gBACR,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC;oBAClB,OAAQ,OAAe,CAAC,EAAE,CAAC,CAAA;gBAC7B,CAAC;gBAED,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1B,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;oBAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;oBACrC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;wBACrB,MAAM,IAAI,wCAAyB,CAAC,0BAA0B,OAAO,UAAU,OAAO,iBAAiB,EAAE,sCAAsC,CAAC,CAAA;oBAClJ,CAAC;oBACD,OAAO,QAAQ,CAAA;gBACjB,CAAC;gBAED,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,CAAA;gBACvB,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;oBAClB,MAAM,IAAI,wCAAyB,CAAC,0BAA0B,OAAO,YAAY,EAAE,iBAAiB,EAAE,wCAAwC,CAAC,CAAA;gBACjJ,CAAC;qBAAM,CAAC;oBACN,OAAO,KAAK,CAAA;gBACd,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { InvalidConfigurationError } from \"builder-util\"\nimport { Nullish } from \"builder-util-runtime\"\nimport { AppInfo } from \"../appInfo\"\n\nexport function expandMacro(pattern: string, arch: string | Nullish, appInfo: AppInfo, extra: any = {}, isProductNameSanitized = true): string {\n if (arch == null) {\n pattern = pattern\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\"-${arch}\", \"\")\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\" ${arch}\", \"\")\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\"_${arch}\", \"\")\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\"/${arch}\", \"\")\n }\n\n return pattern.replace(/\\${([_a-zA-Z./*+]+)}/g, (match, p1): string => {\n switch (p1) {\n case \"productName\":\n return isProductNameSanitized ? appInfo.sanitizedProductName : appInfo.productName\n\n case \"arch\":\n if (arch == null) {\n // see above, we remove macro if no arch\n return \"\"\n }\n return arch\n\n case \"author\": {\n const companyName = appInfo.companyName\n if (companyName == null) {\n throw new InvalidConfigurationError(`cannot expand pattern \"${pattern}\": author is not specified`, \"ERR_ELECTRON_BUILDER_AUTHOR_UNSPECIFIED\")\n }\n return companyName\n }\n\n case \"platform\":\n return process.platform\n\n case \"channel\":\n return appInfo.channel || \"latest\"\n\n default: {\n if (p1 in appInfo) {\n return (appInfo as any)[p1]\n }\n\n if (p1.startsWith(\"env.\")) {\n const envName = p1.substring(\"env.\".length)\n const envValue = process.env[envName]\n if (envValue == null) {\n throw new InvalidConfigurationError(`cannot expand pattern \"${pattern}\": env ${envName} is not defined`, \"ERR_ELECTRON_BUILDER_ENV_NOT_DEFINED\")\n }\n return envValue\n }\n\n const value = extra[p1]\n if (value == null) {\n throw new InvalidConfigurationError(`cannot expand pattern \"${pattern}\": macro ${p1} is not defined`, \"ERR_ELECTRON_BUILDER_MACRO_NOT_DEFINED\")\n } else {\n return value\n }\n }\n }\n })\n}\n"]}
1
+ {"version":3,"file":"macroExpander.js","sourceRoot":"","sources":["../../src/util/macroExpander.ts"],"names":[],"mappings":";;AAIA,kCAiEC;AArED,+CAA6D;AAI7D,SAAgB,WAAW,CAAC,OAAe,EAAE,IAAsB,EAAE,OAAgB,EAAE,QAAa,EAAE,EAAE,sBAAsB,GAAG,IAAI;IACnI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO;YACf,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YACxB,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YACxB,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YACxB,uDAAuD;aACtD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAC5B,CAAC;IAED,OAAO,OAAO,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE,EAAU,EAAE;QACpE,QAAQ,EAAE,EAAE,CAAC;YACX,KAAK,aAAa;gBAChB,OAAO,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAA;YAEpF,KAAK,MAAM;gBACT,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;oBACjB,wCAAwC;oBACxC,OAAO,EAAE,CAAA;gBACX,CAAC;gBACD,OAAO,IAAI,CAAA;YAEb,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAA;gBACvC,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;oBACxB,MAAM,IAAI,wCAAyB,CAAC,0BAA0B,OAAO,4BAA4B,EAAE,yCAAyC,CAAC,CAAA;gBAC/I,CAAC;gBACD,OAAO,WAAW,CAAA;YACpB,CAAC;YAED,KAAK,UAAU;gBACb,OAAO,OAAO,CAAC,QAAQ,CAAA;YAEzB,KAAK,SAAS;gBACZ,OAAO,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAA;YAEpC,OAAO,CAAC,CAAC,CAAC;gBACR,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC;oBAClB,OAAQ,OAAe,CAAC,EAAE,CAAC,CAAA;gBAC7B,CAAC;gBAED,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1B,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;oBAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;oBACrC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;wBACrB,MAAM,IAAI,wCAAyB,CAAC,0BAA0B,OAAO,UAAU,OAAO,iBAAiB,EAAE,sCAAsC,CAAC,CAAA;oBAClJ,CAAC;oBACD,IAAI,uCAAuC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;wBAC1D,kBAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,4FAA4F,CAAC,CAAA;oBAC9H,CAAC;oBACD,OAAO,QAAQ,CAAA;gBACjB,CAAC;gBAED,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,CAAA;gBACvB,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;oBAClB,MAAM,IAAI,wCAAyB,CAAC,0BAA0B,OAAO,YAAY,EAAE,iBAAiB,EAAE,wCAAwC,CAAC,CAAA;gBACjJ,CAAC;qBAAM,CAAC;oBACN,OAAO,KAAK,CAAA;gBACd,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { InvalidConfigurationError, log } from \"builder-util\"\nimport { Nullish } from \"builder-util-runtime\"\nimport { AppInfo } from \"../appInfo\"\n\nexport function expandMacro(pattern: string, arch: string | Nullish, appInfo: AppInfo, extra: any = {}, isProductNameSanitized = true): string {\n if (arch == null) {\n pattern = pattern\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\"-${arch}\", \"\")\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\" ${arch}\", \"\")\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\"_${arch}\", \"\")\n // tslint:disable-next-line:no-invalid-template-strings\n .replace(\"/${arch}\", \"\")\n }\n\n return pattern.replace(/\\${([_a-zA-Z./*+]+)}/g, (match, p1): string => {\n switch (p1) {\n case \"productName\":\n return isProductNameSanitized ? appInfo.sanitizedProductName : appInfo.productName\n\n case \"arch\":\n if (arch == null) {\n // see above, we remove macro if no arch\n return \"\"\n }\n return arch\n\n case \"author\": {\n const companyName = appInfo.companyName\n if (companyName == null) {\n throw new InvalidConfigurationError(`cannot expand pattern \"${pattern}\": author is not specified`, \"ERR_ELECTRON_BUILDER_AUTHOR_UNSPECIFIED\")\n }\n return companyName\n }\n\n case \"platform\":\n return process.platform\n\n case \"channel\":\n return appInfo.channel || \"latest\"\n\n default: {\n if (p1 in appInfo) {\n return (appInfo as any)[p1]\n }\n\n if (p1.startsWith(\"env.\")) {\n const envName = p1.substring(\"env.\".length)\n const envValue = process.env[envName]\n if (envValue == null) {\n throw new InvalidConfigurationError(`cannot expand pattern \"${pattern}\": env ${envName} is not defined`, \"ERR_ELECTRON_BUILDER_ENV_NOT_DEFINED\")\n }\n if (/TOKEN|SECRET|KEY|PASSWORD|CREDENTIAL/i.test(envName)) {\n log.warn({ envName, pattern }, \"macro expansion includes an env var that may contain a secret — verify this is intentional\")\n }\n return envValue\n }\n\n const value = extra[p1]\n if (value == null) {\n throw new InvalidConfigurationError(`cannot expand pattern \"${pattern}\": macro ${p1} is not defined`, \"ERR_ELECTRON_BUILDER_MACRO_NOT_DEFINED\")\n } else {\n return value\n }\n }\n }\n })\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { RequestedExecutionLevel } from "../options/winOptions";
2
+ export interface ResourceEditOptions {
3
+ file: string;
4
+ versionStrings: Record<string, string>;
5
+ fileVersion: string;
6
+ productVersion: string;
7
+ requestedExecutionLevel?: RequestedExecutionLevel | null;
8
+ iconPath?: string | null;
9
+ }
10
+ export declare function editWindowsResources(opts: ResourceEditOptions): Promise<void>;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.editWindowsResources = editWindowsResources;
4
+ const builder_util_1 = require("builder-util");
5
+ const promises_1 = require("fs/promises");
6
+ const resedit_1 = require("resedit");
7
+ async function editWindowsResources(opts) {
8
+ const buffer = await (0, promises_1.readFile)(opts.file);
9
+ const executable = resedit_1.NtExecutable.from(buffer);
10
+ const res = resedit_1.NtExecutableResource.from(executable);
11
+ const viList = resedit_1.Resource.VersionInfo.fromEntries(res.entries);
12
+ // Mirror rcedit: create version info from scratch if none exists; use first if multiple
13
+ const vi = viList.length > 0 ? viList[0] : resedit_1.Resource.VersionInfo.createEmpty();
14
+ // Mirror rcedit: default to en-US (1033) if no languages present; use first if multiple
15
+ const languages = vi.getAllLanguagesForStringValues();
16
+ const lang = languages.length > 0 ? languages[0] : { lang: 0x0409, codepage: 1200 };
17
+ vi.setStringValues(lang, opts.versionStrings);
18
+ vi.setFileVersion(opts.fileVersion);
19
+ vi.setProductVersion(opts.productVersion);
20
+ // resedit normalizes the string to 4-part numeric; restore the original (e.g. "1.1.0" or "3.0.0-beta.2")
21
+ vi.setStringValues(lang, { FileVersion: opts.fileVersion });
22
+ vi.outputToResourceEntries(res.entries);
23
+ if (opts.iconPath) {
24
+ const iconBuf = await (0, promises_1.readFile)(opts.iconPath);
25
+ const iconFile = resedit_1.Data.IconFile.from(iconBuf);
26
+ resedit_1.Resource.IconGroupEntry.replaceIconsForResource(res.entries, 1, lang.lang, iconFile.icons.map(i => i.data));
27
+ }
28
+ if (opts.requestedExecutionLevel && opts.requestedExecutionLevel !== "asInvoker") {
29
+ const manifestEntry = res.entries.find(e => e.type === 24 && e.id === 1);
30
+ if (!manifestEntry) {
31
+ builder_util_1.log.warn({ file: opts.file }, "no RT_MANIFEST resource found; requestedExecutionLevel will not be applied");
32
+ }
33
+ else {
34
+ const originalXml = Buffer.from(manifestEntry.bin).toString("utf-8");
35
+ const updatedXml = originalXml.replace(/(<requestedExecutionLevel[^>]*\blevel=")[^"]*(")/i, `$1${opts.requestedExecutionLevel}$2`);
36
+ if (updatedXml === originalXml) {
37
+ builder_util_1.log.warn({ file: opts.file, requestedExecutionLevel: opts.requestedExecutionLevel }, "requestedExecutionLevel node not found in manifest; execution level not updated");
38
+ }
39
+ else {
40
+ const newBuf = Buffer.from(updatedXml, "utf-8");
41
+ manifestEntry.bin = newBuf.buffer.slice(newBuf.byteOffset, newBuf.byteOffset + newBuf.byteLength);
42
+ }
43
+ }
44
+ }
45
+ res.outputResource(executable);
46
+ await (0, promises_1.writeFile)(opts.file, Buffer.from(executable.generate()));
47
+ }
48
+ //# sourceMappingURL=resEdit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resEdit.js","sourceRoot":"","sources":["../../src/util/resEdit.ts"],"names":[],"mappings":"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","sourcesContent":["import { log } from \"builder-util\"\nimport { readFile, writeFile } from \"fs/promises\"\nimport { Data, NtExecutable, NtExecutableResource, Resource } from \"resedit\"\nimport { RequestedExecutionLevel } from \"../options/winOptions\"\n\nexport interface ResourceEditOptions {\n file: string\n versionStrings: Record<string, string>\n fileVersion: string\n productVersion: string\n requestedExecutionLevel?: RequestedExecutionLevel | null\n iconPath?: string | null\n}\n\nexport async function editWindowsResources(opts: ResourceEditOptions): Promise<void> {\n const buffer = await readFile(opts.file)\n const executable = NtExecutable.from(buffer)\n const res = NtExecutableResource.from(executable)\n\n const viList = Resource.VersionInfo.fromEntries(res.entries)\n // Mirror rcedit: create version info from scratch if none exists; use first if multiple\n const vi = viList.length > 0 ? viList[0] : Resource.VersionInfo.createEmpty()\n // Mirror rcedit: default to en-US (1033) if no languages present; use first if multiple\n const languages = vi.getAllLanguagesForStringValues()\n const lang = languages.length > 0 ? languages[0] : { lang: 0x0409, codepage: 1200 }\n\n vi.setStringValues(lang, opts.versionStrings)\n vi.setFileVersion(opts.fileVersion)\n vi.setProductVersion(opts.productVersion)\n // resedit normalizes the string to 4-part numeric; restore the original (e.g. \"1.1.0\" or \"3.0.0-beta.2\")\n vi.setStringValues(lang, { FileVersion: opts.fileVersion })\n vi.outputToResourceEntries(res.entries)\n\n if (opts.iconPath) {\n const iconBuf = await readFile(opts.iconPath)\n const iconFile = Data.IconFile.from(iconBuf)\n Resource.IconGroupEntry.replaceIconsForResource(\n res.entries,\n 1,\n lang.lang,\n iconFile.icons.map(i => i.data)\n )\n }\n\n if (opts.requestedExecutionLevel && opts.requestedExecutionLevel !== \"asInvoker\") {\n const manifestEntry = res.entries.find(e => e.type === 24 && e.id === 1)\n if (!manifestEntry) {\n log.warn({ file: opts.file }, \"no RT_MANIFEST resource found; requestedExecutionLevel will not be applied\")\n } else {\n const originalXml = Buffer.from(manifestEntry.bin).toString(\"utf-8\")\n const updatedXml = originalXml.replace(/(<requestedExecutionLevel[^>]*\\blevel=\")[^\"]*(\")/i, `$1${opts.requestedExecutionLevel}$2`)\n if (updatedXml === originalXml) {\n log.warn({ file: opts.file, requestedExecutionLevel: opts.requestedExecutionLevel }, \"requestedExecutionLevel node not found in manifest; execution level not updated\")\n } else {\n const newBuf = Buffer.from(updatedXml, \"utf-8\")\n manifestEntry.bin = newBuf.buffer.slice(newBuf.byteOffset, newBuf.byteOffset + newBuf.byteLength)\n }\n }\n }\n\n res.outputResource(executable)\n await writeFile(opts.file, Buffer.from(executable.generate()))\n}\n"]}
@@ -1,2 +1,2 @@
1
1
  export declare function resolveModule<T>(type: string | undefined, name: string): Promise<T>;
2
- export declare function resolveFunction<T>(type: string | undefined, executor: T | string, name: string): Promise<T>;
2
+ export declare function resolveFunction<T>(type: string | undefined, executor: T | string, name: string, rootSearchDir: string): Promise<T>;
@@ -2,8 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveModule = resolveModule;
4
4
  exports.resolveFunction = resolveFunction;
5
+ const builder_util_1 = require("builder-util");
5
6
  const log_1 = require("builder-util/out/log");
6
7
  const debug_1 = require("debug");
8
+ const promises_1 = require("fs/promises");
7
9
  const path = require("path");
8
10
  const requireMaybe = require("../../helpers/dynamic-import");
9
11
  async function resolveModule(type, name) {
@@ -16,7 +18,7 @@ async function resolveModule(type, name) {
16
18
  throw error;
17
19
  }
18
20
  }
19
- async function resolveFunction(type, executor, name) {
21
+ async function resolveFunction(type, executor, name, rootSearchDir) {
20
22
  if (executor == null || typeof executor !== "string") {
21
23
  // is already function or explicitly ignored by user
22
24
  return executor;
@@ -24,6 +26,19 @@ async function resolveFunction(type, executor, name) {
24
26
  let p = executor;
25
27
  if (p.startsWith(".")) {
26
28
  p = path.resolve(p);
29
+ let realP = p;
30
+ let realRoot = rootSearchDir;
31
+ try {
32
+ realP = await (0, promises_1.realpath)(p);
33
+ realRoot = await (0, promises_1.realpath)(rootSearchDir);
34
+ }
35
+ catch {
36
+ // path may not exist yet; fall back to lexical check
37
+ }
38
+ const relative = path.relative(realRoot, realP);
39
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
40
+ throw new builder_util_1.InvalidConfigurationError(`Hook module path "${executor}" resolves outside the workspace root ("${rootSearchDir}")`);
41
+ }
27
42
  }
28
43
  try {
29
44
  p = require.resolve(p);