muon-ui 0.7.0 → 0.8.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 (43) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.cjs +269 -120
  3. package/dist/cli.cjs.map +1 -1
  4. package/dist/index.cjs +2 -2
  5. package/dist/index.mjs +2 -2
  6. package/dist/native/linux-amd64/muon-bootstrap +0 -0
  7. package/dist/native/linux-amd64/muon-builder +0 -0
  8. package/dist/native/linux-arm64/muon-bootstrap +0 -0
  9. package/dist/native/linux-arm64/{muon-prepare → muon-builder} +0 -0
  10. package/dist/native/linux-armhf/muon-bootstrap +0 -0
  11. package/dist/native/linux-armhf/{muon-prepare → muon-builder} +0 -0
  12. package/dist/native/muon-bootstrap.png +0 -0
  13. package/dist/native/windows-amd64/muon-bootstrap.exe +0 -0
  14. package/dist/native/windows-amd64/muon-builder.exe +0 -0
  15. package/dist/native/windows-i686/muon-bootstrap.exe +0 -0
  16. package/dist/native/windows-i686/muon-builder.exe +0 -0
  17. package/dist/runtime/linux-amd64/CREDITS.md +24 -0
  18. package/dist/runtime/linux-amd64/muon-core +0 -0
  19. package/dist/runtime/linux-arm64/CREDITS.md +24 -0
  20. package/dist/runtime/linux-arm64/muon-core +0 -0
  21. package/dist/runtime/linux-armhf/CREDITS.md +24 -0
  22. package/dist/runtime/linux-armhf/muon-core +0 -0
  23. package/dist/runtime/windows-amd64/CREDITS.md +24 -0
  24. package/dist/runtime/windows-amd64/libcardio.dll +0 -0
  25. package/dist/runtime/windows-amd64/libmuon-ui.dll +0 -0
  26. package/dist/runtime/windows-amd64/muon-core.exe +0 -0
  27. package/dist/runtime/windows-i686/CREDITS.md +24 -0
  28. package/dist/runtime/windows-i686/libcardio.dll +0 -0
  29. package/dist/runtime/windows-i686/libmuon-ui.dll +0 -0
  30. package/dist/runtime/windows-i686/muon-core.exe +0 -0
  31. package/dist/{vite-options-FFh0NWUa.cjs → vite-internals-ChWiL2TL.cjs} +1225 -102
  32. package/dist/vite-internals-ChWiL2TL.cjs.map +1 -0
  33. package/dist/vite.cjs +10 -8
  34. package/dist/vite.cjs.map +1 -1
  35. package/dist/vite.mjs +876 -49
  36. package/dist/vite.mjs.map +1 -1
  37. package/muon.d.ts +129 -27
  38. package/package.json +14 -10
  39. package/vite.d.ts +165 -13
  40. package/dist/native/linux-amd64/muon-prepare +0 -0
  41. package/dist/native/windows-amd64/muon-prepare.exe +0 -0
  42. package/dist/native/windows-i686/muon-prepare.exe +0 -0
  43. package/dist/vite-options-FFh0NWUa.cjs.map +0 -1
@@ -1,11 +1,11 @@
1
1
  /*!
2
2
  * name: muon-ui
3
- * version: 0.7.0
3
+ * version: 0.8.0
4
4
  * description: A multi-platform GUI application framework that uses CEF as its backend
5
5
  * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
6
  * license: MIT
7
7
  * repository.url: https://github.com/kekyo/muon-ui.git
8
- * git.commit.hash: fe80718aaa617c0c0ea5c816bbe38d22bf2e8402
8
+ * git.commit.hash: 9bb6148e155dab16c4ca441073b144e21981b038
9
9
  */
10
10
  //#region \0rolldown/runtime.js
11
11
  var __create = Object.create;
@@ -39,6 +39,8 @@ let node_crypto = require("node:crypto");
39
39
  let node_os = require("node:os");
40
40
  let adm_zip = require("adm-zip");
41
41
  adm_zip = __toESM(adm_zip, 1);
42
+ let sharp = require("sharp");
43
+ sharp = __toESM(sharp, 1);
42
44
  //#region src/targets.ts
43
45
  /**
44
46
  * Supported public Muon targets in deterministic build order.
@@ -56,7 +58,7 @@ var targetDescriptors = {
56
58
  cefTarget: "linux64",
57
59
  os: "linux",
58
60
  arch: "amd64",
59
- distributionDirectoryName: "dist-muon-linux-amd64",
61
+ distributionDirectoryName: "dist-muon/linux-amd64",
60
62
  runtimeExecutableName: "muon-core",
61
63
  bootstrapExecutableName: "muon-bootstrap",
62
64
  launcherExtension: "",
@@ -71,7 +73,7 @@ var targetDescriptors = {
71
73
  cefTarget: "linuxarm",
72
74
  os: "linux",
73
75
  arch: "armhf",
74
- distributionDirectoryName: "dist-muon-linux-armhf",
76
+ distributionDirectoryName: "dist-muon/linux-armhf",
75
77
  runtimeExecutableName: "muon-core",
76
78
  bootstrapExecutableName: "muon-bootstrap",
77
79
  launcherExtension: "",
@@ -86,7 +88,7 @@ var targetDescriptors = {
86
88
  cefTarget: "linuxarm64",
87
89
  os: "linux",
88
90
  arch: "arm64",
89
- distributionDirectoryName: "dist-muon-linux-arm64",
91
+ distributionDirectoryName: "dist-muon/linux-arm64",
90
92
  runtimeExecutableName: "muon-core",
91
93
  bootstrapExecutableName: "muon-bootstrap",
92
94
  launcherExtension: "",
@@ -101,7 +103,7 @@ var targetDescriptors = {
101
103
  cefTarget: "windows32",
102
104
  os: "windows",
103
105
  arch: "i686",
104
- distributionDirectoryName: "dist-muon-windows-i686",
106
+ distributionDirectoryName: "dist-muon/windows-i686",
105
107
  runtimeExecutableName: "muon-core.exe",
106
108
  bootstrapExecutableName: "muon-bootstrap.exe",
107
109
  launcherExtension: ".exe",
@@ -121,7 +123,7 @@ var targetDescriptors = {
121
123
  cefTarget: "windows64",
122
124
  os: "windows",
123
125
  arch: "amd64",
124
- distributionDirectoryName: "dist-muon-windows-amd64",
126
+ distributionDirectoryName: "dist-muon/windows-amd64",
125
127
  runtimeExecutableName: "muon-core.exe",
126
128
  bootstrapExecutableName: "muon-bootstrap.exe",
127
129
  launcherExtension: ".exe",
@@ -192,8 +194,8 @@ var getDefaultMuonPrepareTarget = (platform, architecture) => {
192
194
  throw new Error(`Unsupported Muon prepare target: platform=${platform}, arch=${architecture}`);
193
195
  }
194
196
  };
195
- var getPrepareExecutableName = (platform) => platform === "win32" ? "muon-prepare.exe" : "muon-prepare";
196
- var moduleDirectory$2 = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
197
+ var getBuilderExecutableName = (platform) => platform === "win32" ? "muon-builder.exe" : "muon-builder";
198
+ var moduleDirectory$4 = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
197
199
  var canExecute = async (path) => {
198
200
  try {
199
201
  await (0, node_fs_promises.access)(path, node_fs.constants.X_OK);
@@ -202,12 +204,12 @@ var canExecute = async (path) => {
202
204
  return false;
203
205
  }
204
206
  };
205
- var resolveMuonPrepareExecutable = async (options) => {
206
- const explicit = options.prepareExecutablePath ?? options.environment.MUON_PREPARE_PATH;
207
+ var resolveMuonBuilderExecutable = async (options) => {
208
+ const explicit = options.prepareExecutablePath ?? options.environment.MUON_BUILDER_PATH;
207
209
  if (explicit !== void 0 && explicit !== "") return explicit;
208
- const executableName = getPrepareExecutableName(process.platform);
210
+ const executableName = getBuilderExecutableName(process.platform);
209
211
  const target = getDefaultMuonPrepareTarget(process.platform, process.arch);
210
- const candidates = [(0, node_path.join)(moduleDirectory$2, "native", target, executableName), (0, node_path.join)(moduleDirectory$2, "..", "dist", "native", target, executableName)];
212
+ const candidates = [(0, node_path.join)(moduleDirectory$4, "native", target, executableName), (0, node_path.join)(moduleDirectory$4, "..", "dist", "native", target, executableName)];
211
213
  for (const candidate of candidates) if (await canExecute(candidate)) return candidate;
212
214
  return candidates[0] ?? executableName;
213
215
  };
@@ -226,14 +228,100 @@ var createMuonPrepareArguments = (options) => {
226
228
  if (options.quiet) args.push("--quiet");
227
229
  return args;
228
230
  };
229
- /**
230
- * Invokes the native muon-prepare executable and returns the prepared runtime.
231
- *
232
- * @param options Native prepare invocation options.
233
- * @returns Prepared runtime location.
234
- */
235
- var runMuonPrepare = async (options) => {
236
- const child = (0, node_child_process.spawn)(await resolveMuonPrepareExecutable(options), createMuonPrepareArguments(options), {
231
+ var spinnerFrames = [
232
+ "-",
233
+ "\\",
234
+ "|",
235
+ "/"
236
+ ];
237
+ var spinnerIntervalMilliseconds = 100;
238
+ var terminalLineStart = "\r";
239
+ var terminalClearLine = "\x1B[K";
240
+ var isMuonPrepareStatusLine = (line) => {
241
+ const trimmed = line.trimEnd();
242
+ return trimmed.endsWith("...") || trimmed === "Failed to prepare CEF." || trimmed.startsWith("Downloading CEF binary:");
243
+ };
244
+ var shouldKeepMuonPrepareStatusAfterLine = (line) => line.startsWith("Muon files copied to staging:");
245
+ var createPlainStderrForwarder = () => ({
246
+ write: (chunk) => {
247
+ process.stderr.write(chunk);
248
+ },
249
+ flush: () => {}
250
+ });
251
+ var createSpinnerStderrForwarder = () => {
252
+ let pending = "";
253
+ let activeStatus = void 0;
254
+ let frameIndex = 0;
255
+ let timer = void 0;
256
+ const writeRaw = (chunk) => {
257
+ process.stderr.write(chunk);
258
+ };
259
+ const stopTimer = () => {
260
+ if (timer !== void 0) {
261
+ clearInterval(timer);
262
+ timer = void 0;
263
+ }
264
+ };
265
+ const renderStatus = () => {
266
+ if (activeStatus === void 0) return;
267
+ writeRaw(`${terminalLineStart}${spinnerFrames[frameIndex] ?? spinnerFrames[0]} ${activeStatus}${terminalClearLine}`);
268
+ frameIndex = (frameIndex + 1) % spinnerFrames.length;
269
+ };
270
+ const ensureTimer = () => {
271
+ if (timer === void 0) timer = setInterval(renderStatus, spinnerIntervalMilliseconds);
272
+ };
273
+ const finishStatus = () => {
274
+ if (activeStatus === void 0) return;
275
+ const status = activeStatus;
276
+ activeStatus = void 0;
277
+ stopTimer();
278
+ frameIndex = 0;
279
+ writeRaw(`${terminalLineStart}${status}${terminalClearLine}\n`);
280
+ };
281
+ const startStatus = (status) => {
282
+ if (activeStatus !== void 0 && activeStatus !== status) finishStatus();
283
+ activeStatus = status;
284
+ renderStatus();
285
+ ensureTimer();
286
+ };
287
+ const writeLine = (line) => {
288
+ const text = line.replace(/\r?\n$/, "");
289
+ if (isMuonPrepareStatusLine(text)) {
290
+ startStatus(text.trimEnd());
291
+ return;
292
+ }
293
+ if (activeStatus !== void 0 && shouldKeepMuonPrepareStatusAfterLine(text)) {
294
+ writeRaw(`${terminalLineStart}${terminalClearLine}${line.endsWith("\n") ? line : `${line}\n`}`);
295
+ renderStatus();
296
+ return;
297
+ }
298
+ finishStatus();
299
+ writeRaw(line);
300
+ };
301
+ return {
302
+ write: (chunk) => {
303
+ pending += chunk;
304
+ for (;;) {
305
+ const newlineIndex = pending.indexOf("\n");
306
+ if (newlineIndex < 0) break;
307
+ const line = pending.slice(0, newlineIndex + 1);
308
+ pending = pending.slice(newlineIndex + 1);
309
+ writeLine(line);
310
+ }
311
+ },
312
+ flush: () => {
313
+ if (pending.length > 0) {
314
+ const line = pending;
315
+ pending = "";
316
+ writeLine(line);
317
+ }
318
+ finishStatus();
319
+ }
320
+ };
321
+ };
322
+ var createMuonPrepareStderrForwarder = () => process.stderr.isTTY === true ? createSpinnerStderrForwarder() : createPlainStderrForwarder();
323
+ var runMuonPrepareCommand = async (options) => {
324
+ const child = (0, node_child_process.spawn)(await resolveMuonBuilderExecutable(options), [...options.args], {
237
325
  cwd: options.cwd,
238
326
  env: options.environment,
239
327
  stdio: [
@@ -246,22 +334,46 @@ var runMuonPrepare = async (options) => {
246
334
  let stderr = "";
247
335
  child.stdout.setEncoding("utf8");
248
336
  child.stderr.setEncoding("utf8");
337
+ const stderrForwarder = options.quiet ? void 0 : createMuonPrepareStderrForwarder();
249
338
  child.stdout.on("data", (chunk) => {
250
339
  stdout += chunk;
251
340
  });
252
341
  child.stderr.on("data", (chunk) => {
253
342
  stderr += chunk;
254
- if (!options.quiet) process.stderr.write(chunk);
343
+ stderrForwarder?.write(chunk);
255
344
  });
256
- const exitCode = await new Promise((resolvePromise, reject) => {
257
- child.on("error", reject);
258
- child.on("close", (code) => {
259
- resolvePromise(code ?? 1);
260
- });
345
+ const exitCode = await (async () => {
346
+ try {
347
+ return await new Promise((resolvePromise, reject) => {
348
+ child.on("error", reject);
349
+ child.on("close", (code) => {
350
+ resolvePromise(code ?? 1);
351
+ });
352
+ });
353
+ } finally {
354
+ stderrForwarder?.flush();
355
+ }
356
+ })();
357
+ if (exitCode !== 0) throw new Error(`muon-builder failed with exit code ${exitCode}.\n${stderr.trim()}`);
358
+ return stdout;
359
+ };
360
+ /**
361
+ * Invokes the native muon-builder executable and returns the prepared runtime.
362
+ *
363
+ * @param options Native prepare invocation options.
364
+ * @returns Prepared runtime location.
365
+ */
366
+ var runMuonPrepare = async (options) => {
367
+ const args = createMuonPrepareArguments(options);
368
+ const stdout = await runMuonPrepareCommand({
369
+ prepareExecutablePath: options.prepareExecutablePath,
370
+ environment: options.environment,
371
+ cwd: options.cwd,
372
+ quiet: options.quiet,
373
+ args
261
374
  });
262
- if (exitCode !== 0) throw new Error(`muon-prepare failed with exit code ${exitCode}.\n${stderr.trim()}`);
263
375
  const result = JSON.parse(stdout);
264
- if (result.stagePath !== void 0 && typeof result.stagePath !== "string" || typeof result.muonPath !== "string" || typeof result.cefPath !== "string" || typeof result.cacheHit !== "boolean") throw new Error(`muon-prepare returned invalid JSON: ${stdout}`);
376
+ if (result.stagePath !== void 0 && typeof result.stagePath !== "string" || typeof result.muonPath !== "string" || typeof result.cefPath !== "string" || typeof result.cacheHit !== "boolean") throw new Error(`muon-builder returned invalid JSON: ${stdout}`);
265
377
  return {
266
378
  ...result.stagePath === void 0 ? {} : { stagePath: result.stagePath },
267
379
  muonPath: result.muonPath,
@@ -269,11 +381,34 @@ var runMuonPrepare = async (options) => {
269
381
  cacheHit: result.cacheHit
270
382
  };
271
383
  };
384
+ /**
385
+ * Invokes the native muon-builder executable to write Windows PE resources.
386
+ *
387
+ * @param options Resource update invocation options.
388
+ */
389
+ var runMuonPrepareResourceUpdate = async (options) => {
390
+ await runMuonPrepareCommand({
391
+ prepareExecutablePath: options.prepareExecutablePath,
392
+ environment: options.environment,
393
+ cwd: options.cwd,
394
+ quiet: options.quiet,
395
+ args: [
396
+ "resource",
397
+ "--input",
398
+ options.inputPath,
399
+ "--updates-json",
400
+ options.updatesJsonPath,
401
+ "--output",
402
+ options.outputPath,
403
+ ...options.quiet ? ["--quiet"] : []
404
+ ]
405
+ });
406
+ };
272
407
  //#endregion
273
408
  //#region src/gitignore.ts
274
409
  var muonGitignoreEntries = [
275
410
  ".muon/",
276
- "dist-muon-*/",
411
+ "dist-muon/",
277
412
  "artifacts/"
278
413
  ];
279
414
  var muonGitignoreEntryAliases = {
@@ -283,11 +418,13 @@ var muonGitignoreEntryAliases = {
283
418
  ".muon",
284
419
  "/.muon"
285
420
  ],
286
- "dist-muon-*/": [
287
- "dist-muon-*/",
288
- "/dist-muon-*/",
289
- "dist-muon-*",
290
- "/dist-muon-*"
421
+ "dist-muon/": [
422
+ "dist-muon/",
423
+ "/dist-muon/",
424
+ "dist-muon",
425
+ "/dist-muon",
426
+ "dist-muon/*",
427
+ "/dist-muon/*"
291
428
  ],
292
429
  "artifacts/": [
293
430
  "artifacts/",
@@ -1602,7 +1739,7 @@ var encodeTaggedBytes = (tag, bytes) => Buffer.concat([
1602
1739
  encodeVarUint(BigInt(bytes.length)),
1603
1740
  Buffer.from(bytes)
1604
1741
  ]);
1605
- var isJsonObject$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1742
+ var isJsonObject$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1606
1743
  var isPath = (path, first, second) => path.length === 2 && path[0] === first && path[1] === second;
1607
1744
  var isHexString = (value) => value.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(value);
1608
1745
  var decodeHexString = (value) => {
@@ -1635,7 +1772,7 @@ var encodeTlvValue = (value, path) => {
1635
1772
  encodeVarUint(BigInt(value.length)),
1636
1773
  ...value.map((entry) => encodeTlvValue(entry, path))
1637
1774
  ]);
1638
- if (isJsonObject$2(value)) {
1775
+ if (isJsonObject$4(value)) {
1639
1776
  const entries = Object.entries(value);
1640
1777
  return Buffer.concat([
1641
1778
  Buffer.from([tlvObjectTag]),
@@ -1749,7 +1886,7 @@ var findMuonBootstrapEmbeddedConfigSlot = (content) => {
1749
1886
  if (candidate === void 0) throw new Error("Embedded muon-bootstrap config slot was not found.");
1750
1887
  return candidate;
1751
1888
  };
1752
- var fileExists$2 = async (path) => {
1889
+ var fileExists$4 = async (path) => {
1753
1890
  try {
1754
1891
  return (await (0, node_fs_promises.stat)(path)).isFile();
1755
1892
  } catch {
@@ -1765,10 +1902,10 @@ var resolveMuonConfigInputPath = async (configPath) => {
1765
1902
  "muon.json"
1766
1903
  ]) {
1767
1904
  const candidate = (0, node_path.join)(directory, fileName);
1768
- if (await fileExists$2(candidate)) return candidate;
1905
+ if (await fileExists$4(candidate)) return candidate;
1769
1906
  }
1770
1907
  }
1771
- if (!await fileExists$2(configPath)) throw new Error(`muon config does not exist: ${configPath}`);
1908
+ if (!await fileExists$4(configPath)) throw new Error(`muon config does not exist: ${configPath}`);
1772
1909
  return configPath;
1773
1910
  };
1774
1911
  var readMuonConfigInput = async (configPath) => {
@@ -1864,6 +2001,730 @@ var embedMuonConfigInRuntime = async ({ runtimePath, configPath, outputRuntimePa
1864
2001
  });
1865
2002
  };
1866
2003
  //#endregion
2004
+ //#region src/windows-icon.ts
2005
+ var sharp$1 = __resolveDefaultExport$1(sharp, true);
2006
+ globalThis.__screwUpIsInCJS_27e672935538 = true;
2007
+ function __resolveDefaultExport$1(module, isESM) {
2008
+ const __isInCJS = typeof globalThis !== "undefined" && globalThis.__screwUpIsInCJS_27e672935538 === true;
2009
+ const maybe = module;
2010
+ const hasDefault = !!(maybe && typeof maybe === "object" && "default" in maybe);
2011
+ const unwrapNamespaceDefault = (value) => {
2012
+ if (!value || typeof value !== "object") return value;
2013
+ const inner = value;
2014
+ if ((inner.__esModule === true || typeof Symbol !== "undefined" && inner[Symbol.toStringTag] === "Module") && "default" in inner) return inner.default;
2015
+ return value;
2016
+ };
2017
+ const resolvedDefault = hasDefault ? unwrapNamespaceDefault(maybe.default) : void 0;
2018
+ if (__isInCJS) return hasDefault ? resolvedDefault ?? module : module;
2019
+ if (isESM) {
2020
+ if (hasDefault) return resolvedDefault;
2021
+ throw new Error("Default export not found.");
2022
+ }
2023
+ return hasDefault ? resolvedDefault ?? module : module;
2024
+ }
2025
+ var normalizedIconSize = 256;
2026
+ var windowsIconDibSizes = [
2027
+ 16,
2028
+ 24,
2029
+ 32,
2030
+ 48,
2031
+ 64,
2032
+ 128
2033
+ ];
2034
+ var pngSignature = Buffer.from([
2035
+ 137,
2036
+ 80,
2037
+ 78,
2038
+ 71,
2039
+ 13,
2040
+ 10,
2041
+ 26,
2042
+ 10
2043
+ ]);
2044
+ /**
2045
+ * Creates a normalized square PNG icon image.
2046
+ *
2047
+ * @param pngData Source PNG bytes.
2048
+ * @param source Diagnostic source label used in errors.
2049
+ * @param label User-facing diagnostic label.
2050
+ * @returns PNG bytes normalized to the Muon base icon size.
2051
+ * @remarks Non-square images are fitted into transparent padding without
2052
+ * stretching. The normalized PNG is the source for all platform-specific icon
2053
+ * derivatives.
2054
+ */
2055
+ var createNormalizedIconPngData = async (pngData, source, label) => {
2056
+ await assertPngData(pngData, source, label);
2057
+ try {
2058
+ return await sharp$1(pngData).resize(normalizedIconSize, normalizedIconSize, {
2059
+ fit: "contain",
2060
+ background: {
2061
+ r: 0,
2062
+ g: 0,
2063
+ b: 0,
2064
+ alpha: 0
2065
+ }
2066
+ }).ensureAlpha().png().toBuffer();
2067
+ } catch {
2068
+ throw new Error(`${label} must be a valid PNG: ${source}`);
2069
+ }
2070
+ };
2071
+ /**
2072
+ * Creates a normalized square PNG icon image for Windows resources.
2073
+ *
2074
+ * @param pngData Source PNG bytes.
2075
+ * @param source Diagnostic source label used in errors.
2076
+ * @returns PNG bytes normalized to the Muon base icon size.
2077
+ */
2078
+ var createNormalizedMuonIconPngData = async (pngData, source) => await createNormalizedIconPngData(pngData, source, "Windows resource icon");
2079
+ /**
2080
+ * Creates a Windows ICO file from PNG bytes.
2081
+ *
2082
+ * @param pngData Source PNG bytes.
2083
+ * @param source Diagnostic source label used in errors.
2084
+ * @returns ICO file bytes containing 16, 24, 32, 48, 64, 128, and 256 pixel images.
2085
+ * @remarks The 256px entry is PNG-compressed. Smaller entries are 32-bit DIB
2086
+ * images, matching the common Windows ICO layout recommended by Microsoft.
2087
+ */
2088
+ var createWindowsIconBufferFromPngData = async (pngData, source) => {
2089
+ const normalizedPng = await createNormalizedMuonIconPngData(pngData, source);
2090
+ const images = [];
2091
+ for (const size of windowsIconDibSizes) {
2092
+ const rawImage = await resizePngToRawRgba(normalizedPng, size, source);
2093
+ images.push({
2094
+ data: createDibIconImage(rawImage),
2095
+ size,
2096
+ bitCount: 32
2097
+ });
2098
+ }
2099
+ images.push({
2100
+ data: normalizedPng,
2101
+ size: normalizedIconSize,
2102
+ bitCount: 32
2103
+ });
2104
+ return createIco(images);
2105
+ };
2106
+ /**
2107
+ * Creates a Windows ICO file from a PNG file.
2108
+ *
2109
+ * @param sourcePath Source PNG file path.
2110
+ * @param outputPath Output ICO file path.
2111
+ * @returns Promise resolved when the ICO file has been written.
2112
+ */
2113
+ var createWindowsIconFromPngFile = async (sourcePath, outputPath) => {
2114
+ const icon = await createWindowsIconBufferFromPngData(await (0, node_fs_promises.readFile)(sourcePath), sourcePath);
2115
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(outputPath), { recursive: true });
2116
+ await (0, node_fs_promises.writeFile)(outputPath, icon);
2117
+ };
2118
+ var assertPngData = async (data, source, label) => {
2119
+ if (data.length === 0) throw new Error(`${label} PNG must not be empty: ${source}`);
2120
+ if (!data.subarray(0, pngSignature.length).equals(pngSignature)) throw new Error(`${label} must be a valid PNG: ${source}`);
2121
+ try {
2122
+ if ((await sharp$1(data).metadata()).format !== "png") throw new Error();
2123
+ } catch {
2124
+ throw new Error(`${label} must be a valid PNG: ${source}`);
2125
+ }
2126
+ };
2127
+ var resizePngToRawRgba = async (pngData, size, source) => {
2128
+ try {
2129
+ const { data, info } = await sharp$1(pngData).resize(size, size, {
2130
+ fit: "fill",
2131
+ kernel: "lanczos3"
2132
+ }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
2133
+ if (info.width !== size || info.height !== size || info.channels !== 4) throw new Error();
2134
+ return {
2135
+ data,
2136
+ width: info.width,
2137
+ height: info.height
2138
+ };
2139
+ } catch {
2140
+ throw new Error(`Failed to resize Windows resource icon PNG: ${source}`);
2141
+ }
2142
+ };
2143
+ var createDibIconImage = (image) => {
2144
+ const pixelBytes = image.width * image.height * 4;
2145
+ const maskBytes = Math.ceil(image.width / 32) * 4 * image.height;
2146
+ const output = Buffer.alloc(40 + pixelBytes + maskBytes);
2147
+ output.writeUInt32LE(40, 0);
2148
+ output.writeInt32LE(image.width, 4);
2149
+ output.writeInt32LE(image.height * 2, 8);
2150
+ output.writeUInt16LE(1, 12);
2151
+ output.writeUInt16LE(32, 14);
2152
+ output.writeUInt32LE(0, 16);
2153
+ output.writeUInt32LE(pixelBytes + maskBytes, 20);
2154
+ output.writeInt32LE(0, 24);
2155
+ output.writeInt32LE(0, 28);
2156
+ output.writeUInt32LE(0, 32);
2157
+ output.writeUInt32LE(0, 36);
2158
+ for (let y = 0; y < image.height; y += 1) {
2159
+ const sourceY = image.height - 1 - y;
2160
+ for (let x = 0; x < image.width; x += 1) {
2161
+ const sourceOffset = (sourceY * image.width + x) * 4;
2162
+ const outputOffset = 40 + (y * image.width + x) * 4;
2163
+ output[outputOffset] = image.data[sourceOffset + 2];
2164
+ output[outputOffset + 1] = image.data[sourceOffset + 1];
2165
+ output[outputOffset + 2] = image.data[sourceOffset];
2166
+ output[outputOffset + 3] = image.data[sourceOffset + 3];
2167
+ }
2168
+ }
2169
+ return output;
2170
+ };
2171
+ var createIco = (images) => {
2172
+ const directorySize = 6 + images.length * 16;
2173
+ let imageOffset = directorySize;
2174
+ const header = Buffer.alloc(directorySize);
2175
+ header.writeUInt16LE(0, 0);
2176
+ header.writeUInt16LE(1, 2);
2177
+ header.writeUInt16LE(images.length, 4);
2178
+ const imageData = [];
2179
+ images.forEach((image, index) => {
2180
+ const entryOffset = 6 + index * 16;
2181
+ header[entryOffset] = image.size === 256 ? 0 : image.size;
2182
+ header[entryOffset + 1] = image.size === 256 ? 0 : image.size;
2183
+ header[entryOffset + 2] = 0;
2184
+ header[entryOffset + 3] = 0;
2185
+ header.writeUInt16LE(1, entryOffset + 4);
2186
+ header.writeUInt16LE(image.bitCount, entryOffset + 6);
2187
+ header.writeUInt32LE(image.data.length, entryOffset + 8);
2188
+ header.writeUInt32LE(imageOffset, entryOffset + 12);
2189
+ imageOffset += image.data.length;
2190
+ imageData.push(image.data);
2191
+ });
2192
+ return Buffer.concat([header, ...imageData]);
2193
+ };
2194
+ //#endregion
2195
+ //#region src/windows-resource.ts
2196
+ var defaultConfigFileNames$1 = [
2197
+ "muon.json5",
2198
+ "muon.jsonc",
2199
+ "muon.json"
2200
+ ];
2201
+ var defaultLanguage = 1033;
2202
+ var defaultCodePage = 1200;
2203
+ var moduleDirectory$3 = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
2204
+ /**
2205
+ * Reads the Muon config file using the same default file names as muon build.
2206
+ */
2207
+ var readMuonConfigForWindowsResource = async (root, configPath) => {
2208
+ const resolvedConfigPath = await resolveConfigPath$1(root, configPath);
2209
+ if (resolvedConfigPath === void 0) return {
2210
+ config: {},
2211
+ directory: root
2212
+ };
2213
+ return {
2214
+ config: await readJsonObjectFile$1(resolvedConfigPath, "Muon config file"),
2215
+ directory: (0, node_path.dirname)(resolvedConfigPath)
2216
+ };
2217
+ };
2218
+ /**
2219
+ * Merges Windows resource options while preserving field-level fallback.
2220
+ */
2221
+ var mergeMuonWindowsResourceOptions = (primary, fallback) => {
2222
+ if (primary === void 0) return fallback;
2223
+ if (fallback === void 0) return primary;
2224
+ const merged = { ...fallback };
2225
+ if (primary.iconPath !== void 0) merged.iconPath = primary.iconPath;
2226
+ if (primary.productName !== void 0) merged.productName = primary.productName;
2227
+ if (primary.fileDescription !== void 0) merged.fileDescription = primary.fileDescription;
2228
+ if (primary.companyName !== void 0) merged.companyName = primary.companyName;
2229
+ if (primary.version !== void 0) merged.version = primary.version;
2230
+ if (primary.copyright !== void 0) merged.copyright = primary.copyright;
2231
+ if (primary.language !== void 0) merged.language = primary.language;
2232
+ if (primary.codePage !== void 0) merged.codePage = primary.codePage;
2233
+ return merged;
2234
+ };
2235
+ /**
2236
+ * Resolves Windows resource metadata from options, config files, and package metadata.
2237
+ */
2238
+ var resolveMuonWindowsResource = async (input) => {
2239
+ const projectJson = await readProjectJson(input.root);
2240
+ const sources = [
2241
+ createOptionsSource$1(input.options, input.root, "windowsResource"),
2242
+ readConfigWindowsResourceSource(input.muonConfig, input.muonConfigDirectory, "muon.json"),
2243
+ createProjectSource(projectJson, input.root),
2244
+ createPackageSource(input.packageJson, input.root)
2245
+ ].filter((source) => source !== void 0);
2246
+ const productName = resolveStringField$1(sources, "productName", input.defaults.productName);
2247
+ const fileDescription = resolveStringField$1(sources, "fileDescription", input.defaults.fileDescription);
2248
+ const companyName = resolveStringField$1(sources, "companyName", input.defaults.companyName);
2249
+ const version = resolveStringField$1(sources, "version", input.defaults.version);
2250
+ const copyright = resolveOptionalStringField(sources, "copyright", input.defaults.copyright);
2251
+ const language = resolveNumericField(sources, "language", defaultLanguage);
2252
+ const codePage = resolveNumericField(sources, "codePage", defaultCodePage);
2253
+ const fixedVersion = normalizeWindowsVersion(version);
2254
+ return {
2255
+ iconPath: await resolveIconPath$1(sources, await resolveDefaultWindowsIcon(input.packageDirectory)),
2256
+ productName,
2257
+ fileDescription,
2258
+ companyName,
2259
+ version,
2260
+ fixedVersion,
2261
+ copyright,
2262
+ language,
2263
+ codePage
2264
+ };
2265
+ };
2266
+ /**
2267
+ * Removes build-only Windows resource settings from runtime config.
2268
+ */
2269
+ var stripBuildOnlyWindowsResourceConfig = (sourceConfig) => {
2270
+ const sourceWindows = sourceConfig.windows;
2271
+ if (!isJsonObject$3(sourceWindows)) return sourceConfig;
2272
+ const windowsConfig = {};
2273
+ for (const [key, value] of Object.entries(sourceWindows)) if (key !== "resource") windowsConfig[key] = value;
2274
+ const output = {};
2275
+ for (const [key, value] of Object.entries(sourceConfig)) if (key !== "windows") output[key] = value;
2276
+ if (Object.keys(windowsConfig).length > 0) output.windows = windowsConfig;
2277
+ return output;
2278
+ };
2279
+ /**
2280
+ * Returns true when the file has a Windows PE header.
2281
+ */
2282
+ var isWindowsPeExecutable = async (path) => {
2283
+ const content = await (0, node_fs_promises.readFile)(path);
2284
+ if (content.length < 64 || content.toString("ascii", 0, 2) !== "MZ") return false;
2285
+ const peOffset = content.readUInt32LE(60);
2286
+ return peOffset + 4 <= content.length && content.toString("ascii", peOffset, peOffset + 4) === "PE\0\0";
2287
+ };
2288
+ /**
2289
+ * Applies resolved resource metadata to a Windows PE executable in place.
2290
+ */
2291
+ var updateWindowsPeResources = async (input) => {
2292
+ if (!await isWindowsPeExecutable(input.executablePath)) return false;
2293
+ const mode = (await (0, node_fs_promises.stat)(input.executablePath)).mode & 511;
2294
+ const tempDirectory = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "muon-windows-resource-"));
2295
+ const updatesJsonPath = (0, node_path.join)(tempDirectory, "updates.json");
2296
+ const outputPath = (0, node_path.join)(tempDirectory, "output.exe");
2297
+ const iconPath = input.resource.iconPath === void 0 ? void 0 : (0, node_path.join)(tempDirectory, "icon.ico");
2298
+ try {
2299
+ if (input.resource.iconPath !== void 0 && iconPath !== void 0) await createWindowsIconFromPngFile(input.resource.iconPath, iconPath);
2300
+ await (0, node_fs_promises.writeFile)(updatesJsonPath, `${JSON.stringify(createEngraverUpdate(input.resource, iconPath), null, 2)}\n`);
2301
+ await runMuonPrepareResourceUpdate({
2302
+ inputPath: input.executablePath,
2303
+ updatesJsonPath,
2304
+ outputPath,
2305
+ quiet: true,
2306
+ prepareExecutablePath: void 0,
2307
+ environment: input.environment,
2308
+ cwd: input.cwd
2309
+ });
2310
+ await (0, node_fs_promises.copyFile)(outputPath, input.executablePath);
2311
+ await (0, node_fs_promises.chmod)(input.executablePath, mode === 0 ? 493 : mode);
2312
+ return true;
2313
+ } finally {
2314
+ await (0, node_fs_promises.rm)(tempDirectory, {
2315
+ recursive: true,
2316
+ force: true
2317
+ });
2318
+ }
2319
+ };
2320
+ var createEngraverUpdate = (resource, iconPath) => {
2321
+ const strings = {
2322
+ CompanyName: resource.companyName,
2323
+ FileDescription: resource.fileDescription,
2324
+ FileVersion: resource.version,
2325
+ ProductName: resource.productName,
2326
+ ProductVersion: resource.version
2327
+ };
2328
+ if (resource.copyright !== void 0) strings.LegalCopyright = resource.copyright;
2329
+ return {
2330
+ version: {
2331
+ language: resource.language,
2332
+ codePage: resource.codePage,
2333
+ fixed: {
2334
+ fileVersion: resource.fixedVersion,
2335
+ productVersion: resource.fixedVersion,
2336
+ fileOS: "windows32",
2337
+ fileType: "app"
2338
+ },
2339
+ strings
2340
+ },
2341
+ icons: iconPath === void 0 ? [] : [{
2342
+ id: 1,
2343
+ language: resource.language,
2344
+ path: iconPath
2345
+ }]
2346
+ };
2347
+ };
2348
+ var resolveConfigPath$1 = async (root, configPath) => {
2349
+ if (configPath !== void 0) {
2350
+ const resolvedPath = (0, node_path.resolve)(root, configPath);
2351
+ if (await fileExists$3(resolvedPath)) return resolvedPath;
2352
+ throw new Error(`Muon config file does not exist: ${resolvedPath}`);
2353
+ }
2354
+ for (const fileName of defaultConfigFileNames$1) {
2355
+ const candidatePath = (0, node_path.join)(root, fileName);
2356
+ if (await fileExists$3(candidatePath)) return candidatePath;
2357
+ }
2358
+ };
2359
+ var readProjectJson = async (root) => {
2360
+ const projectJsonPath = (0, node_path.join)(root, "project.json");
2361
+ if (!await fileExists$3(projectJsonPath)) return {};
2362
+ return await readJsonObjectFile$1(projectJsonPath, "project.json");
2363
+ };
2364
+ var readJsonObjectFile$1 = async (filePath, label) => {
2365
+ const parsed = (0, import_dist.parse)(await (0, node_fs_promises.readFile)(filePath, "utf8"));
2366
+ if (!isJsonObject$3(parsed)) throw new Error(`${label} must contain a JSON object: ${filePath}`);
2367
+ return parsed;
2368
+ };
2369
+ var createOptionsSource$1 = (options, directory, label) => {
2370
+ if (options === void 0) return;
2371
+ return {
2372
+ options: validateWindowsResourceOptions(options, label),
2373
+ directory
2374
+ };
2375
+ };
2376
+ var readConfigWindowsResourceSource = (config, directory, label) => {
2377
+ const windows = config.windows;
2378
+ if (windows === void 0) return;
2379
+ if (!isJsonObject$3(windows)) throw new Error(`${label} windows must be an object when present.`);
2380
+ const resource = windows.resource;
2381
+ if (resource === void 0) return;
2382
+ return createOptionsSource$1(validateWindowsResourceOptions(resource, `${label} windows.resource`), directory, `${label} windows.resource`);
2383
+ };
2384
+ var createProjectSource = (projectJson, directory) => {
2385
+ const topLevel = {};
2386
+ copyStringField(projectJson, "iconPath", topLevel, "iconPath");
2387
+ copyStringField(projectJson, "productName", topLevel, "productName");
2388
+ copyStringField(projectJson, "name", topLevel, "productName");
2389
+ copyStringField(projectJson, "fileDescription", topLevel, "fileDescription");
2390
+ copyStringField(projectJson, "description", topLevel, "fileDescription");
2391
+ copyStringField(projectJson, "companyName", topLevel, "companyName");
2392
+ copyStringField(projectJson, "version", topLevel, "version");
2393
+ copyStringField(projectJson, "copyright", topLevel, "copyright");
2394
+ const author = stringifyAuthor(projectJson.author);
2395
+ if (author !== void 0 && topLevel.companyName === void 0) topLevel.companyName = author;
2396
+ const options = mergeMuonWindowsResourceOptions(readConfigWindowsResourceSource(projectJson, directory, "project.json")?.options, topLevel);
2397
+ if (options === void 0 || Object.keys(options).length === 0) return;
2398
+ return {
2399
+ options,
2400
+ directory
2401
+ };
2402
+ };
2403
+ var createPackageSource = (packageJson, directory) => {
2404
+ const options = {};
2405
+ copyStringField(packageJson, "name", options, "productName");
2406
+ copyStringField(packageJson, "description", options, "fileDescription");
2407
+ copyStringField(packageJson, "version", options, "version");
2408
+ copyStringField(packageJson, "copyright", options, "copyright");
2409
+ const author = stringifyAuthor(packageJson.author);
2410
+ if (author !== void 0) options.companyName = author;
2411
+ return Object.keys(options).length === 0 ? void 0 : {
2412
+ options,
2413
+ directory
2414
+ };
2415
+ };
2416
+ var copyStringField = (source, sourceKey, target, targetKey) => {
2417
+ const value = source[sourceKey];
2418
+ if (typeof value === "string" && value.trim() !== "") target[targetKey] = value.trim();
2419
+ };
2420
+ var validateWindowsResourceOptions = (value, label) => {
2421
+ if (!isJsonObject$3(value)) throw new Error(`${label} must be an object.`);
2422
+ const output = {};
2423
+ copyOptionalStringResourceField(value, "iconPath", output);
2424
+ copyOptionalStringResourceField(value, "productName", output);
2425
+ copyOptionalStringResourceField(value, "fileDescription", output);
2426
+ copyOptionalStringResourceField(value, "companyName", output);
2427
+ copyOptionalStringResourceField(value, "version", output);
2428
+ copyOptionalStringResourceField(value, "copyright", output);
2429
+ copyOptionalNumberResourceField(value, "language", output, label);
2430
+ copyOptionalNumberResourceField(value, "codePage", output, label);
2431
+ return output;
2432
+ };
2433
+ var copyOptionalStringResourceField = (source, key, target) => {
2434
+ const value = source[key];
2435
+ if (value === void 0) return;
2436
+ if (typeof value !== "string") throw new Error(`${key} must be a string when present.`);
2437
+ if (value.trim() !== "") target[key] = value.trim();
2438
+ };
2439
+ var copyOptionalNumberResourceField = (source, key, target, label) => {
2440
+ const value = source[key];
2441
+ if (value === void 0) return;
2442
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 65535) throw new Error(`${label} ${key} must be an integer from 0 to 65535.`);
2443
+ target[key] = value;
2444
+ };
2445
+ var resolveStringField$1 = (sources, field, fallback) => {
2446
+ return resolveOptionalStringField(sources, field, fallback) ?? fallback;
2447
+ };
2448
+ var resolveOptionalStringField = (sources, field, fallback) => {
2449
+ for (const source of sources) {
2450
+ const value = source.options[field];
2451
+ if (value !== void 0 && value.trim() !== "") return value.trim();
2452
+ }
2453
+ return fallback;
2454
+ };
2455
+ var resolveNumericField = (sources, field, fallback) => {
2456
+ for (const source of sources) {
2457
+ const value = source.options[field];
2458
+ if (value !== void 0) return value;
2459
+ }
2460
+ return fallback;
2461
+ };
2462
+ var resolveIconPath$1 = async (sources, defaultIconPath) => {
2463
+ for (const source of sources) {
2464
+ const value = source.options.iconPath;
2465
+ if (value !== void 0 && value.trim() !== "") {
2466
+ const iconPath = resolveResourcePath$1(source.directory, value);
2467
+ await assertIconPath$1(iconPath, true);
2468
+ return iconPath;
2469
+ }
2470
+ }
2471
+ if (defaultIconPath !== void 0 && await fileExists$3(defaultIconPath)) {
2472
+ await assertIconPath$1(defaultIconPath, false);
2473
+ return defaultIconPath;
2474
+ }
2475
+ };
2476
+ var assertIconPath$1 = async (iconPath, required) => {
2477
+ if ((0, node_path.extname)(iconPath).toLowerCase() !== ".png") throw new Error(`Windows resource icon must be a .png file: ${iconPath}`);
2478
+ if (required && !await fileExists$3(iconPath)) throw new Error(`Windows resource icon does not exist: ${iconPath}`);
2479
+ if (await fileExists$3(iconPath)) await createNormalizedMuonIconPngData(await (0, node_fs_promises.readFile)(iconPath), iconPath);
2480
+ };
2481
+ var resolveDefaultWindowsIcon = async (packageDirectory) => {
2482
+ const candidates = [
2483
+ (0, node_path.join)((0, node_path.resolve)(packageDirectory), "native", "muon-bootstrap.png"),
2484
+ (0, node_path.join)(moduleDirectory$3, "native", "muon-bootstrap.png"),
2485
+ (0, node_path.join)(moduleDirectory$3, "..", "dist", "native", "muon-bootstrap.png"),
2486
+ (0, node_path.join)(moduleDirectory$3, "..", "..", "images", "muon-bootstrap-256.png")
2487
+ ];
2488
+ for (const candidate of candidates) if (await fileExists$3(candidate)) return candidate;
2489
+ };
2490
+ var resolveResourcePath$1 = (directory, path) => {
2491
+ return (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(directory, path);
2492
+ };
2493
+ var normalizeWindowsVersion = (version) => {
2494
+ const normalized = version.trim();
2495
+ const match = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?(?:$|[-+])/.exec(normalized);
2496
+ if (match === null) throw new Error(`Windows resource version must start with numeric parts: ${version}`);
2497
+ const values = [
2498
+ match[1],
2499
+ match[2],
2500
+ match[3],
2501
+ match[4]
2502
+ ].map((value) => value === void 0 ? 0 : Number.parseInt(value, 10));
2503
+ for (const value of values) if (!Number.isInteger(value) || value < 0 || value > 65535) throw new Error(`Windows resource version part must be from 0 to 65535: ${version}`);
2504
+ return values.join(".");
2505
+ };
2506
+ var stringifyAuthor = (value) => {
2507
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
2508
+ if (!isJsonObject$3(value) || typeof value.name !== "string") return;
2509
+ const name = value.name.trim();
2510
+ if (name === "") return;
2511
+ return value.email === void 0 || typeof value.email !== "string" ? name : `${name} <${value.email}>`;
2512
+ };
2513
+ var fileExists$3 = async (path) => {
2514
+ try {
2515
+ await (0, node_fs_promises.access)(path, node_fs.constants.F_OK);
2516
+ return true;
2517
+ } catch {
2518
+ return false;
2519
+ }
2520
+ };
2521
+ var isJsonObject$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2522
+ //#endregion
2523
+ //#region src/linux-desktop.ts
2524
+ var defaultIconFileName = "muon-desktop-icon.png";
2525
+ var defaultDesktopConfigFileName = "muon-desktop.json";
2526
+ var moduleDirectory$2 = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
2527
+ /**
2528
+ * Merges Linux desktop options while preserving field-level fallback.
2529
+ */
2530
+ var mergeMuonLinuxDesktopOptions = (primary, fallback) => {
2531
+ if (primary === void 0) return fallback;
2532
+ if (fallback === void 0) return primary;
2533
+ const merged = { ...fallback };
2534
+ if (primary.desktopId !== void 0) merged.desktopId = primary.desktopId;
2535
+ if (primary.name !== void 0) merged.name = primary.name;
2536
+ if (primary.comment !== void 0) merged.comment = primary.comment;
2537
+ if (primary.iconPath !== void 0) merged.iconPath = primary.iconPath;
2538
+ if (primary.categories !== void 0) merged.categories = primary.categories;
2539
+ if (primary.startupNotify !== void 0) merged.startupNotify = primary.startupNotify;
2540
+ return merged;
2541
+ };
2542
+ /**
2543
+ * Resolves Linux desktop integration metadata from options, config files, and
2544
+ * package metadata.
2545
+ */
2546
+ var resolveMuonLinuxDesktop = async (input) => {
2547
+ const sources = [createOptionsSource(input.options, input.root, "linuxDesktop"), readConfigLinuxDesktopSource(input.muonConfig, input.muonConfigDirectory, "muon.json")].filter((source) => source !== void 0);
2548
+ return {
2549
+ desktopId: sanitizeDesktopId(resolveStringField(sources, "desktopId", input.defaults.desktopId)),
2550
+ name: resolveStringField(sources, "name", input.defaults.name),
2551
+ comment: resolveStringField(sources, "comment", input.defaults.comment),
2552
+ categories: resolveCategories(sources, input.defaults.categories),
2553
+ startupNotify: resolveBooleanField(sources, "startupNotify", input.defaults.startupNotify),
2554
+ iconPath: await resolveIconPath(sources, await resolveDefaultLinuxDesktopIcon(input.packageDirectory)),
2555
+ iconFileName: defaultIconFileName,
2556
+ configFileName: defaultDesktopConfigFileName
2557
+ };
2558
+ };
2559
+ /**
2560
+ * Removes build-only Linux desktop settings from runtime config.
2561
+ */
2562
+ var stripBuildOnlyLinuxDesktopConfig = (sourceConfig) => {
2563
+ const sourceLinux = sourceConfig.linux;
2564
+ if (!isJsonObject$2(sourceLinux)) return sourceConfig;
2565
+ const linuxConfig = {};
2566
+ for (const [key, value] of Object.entries(sourceLinux)) if (key !== "desktop") linuxConfig[key] = value;
2567
+ const output = {};
2568
+ for (const [key, value] of Object.entries(sourceConfig)) if (key !== "linux") output[key] = value;
2569
+ if (Object.keys(linuxConfig).length > 0) output.linux = linuxConfig;
2570
+ return output;
2571
+ };
2572
+ /**
2573
+ * Writes normalized Linux desktop sidecar files into a target distribution.
2574
+ */
2575
+ var writeLinuxDesktopDistributionFiles = async (outputPath, desktop) => {
2576
+ const iconData = await createNormalizedIconPngData(await (0, node_fs_promises.readFile)(desktop.iconPath), desktop.iconPath, "Linux desktop icon");
2577
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputPath, desktop.iconFileName), iconData);
2578
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputPath, desktop.configFileName), `${JSON.stringify({
2579
+ desktopId: desktop.desktopId,
2580
+ name: desktop.name,
2581
+ comment: desktop.comment,
2582
+ categories: desktop.categories,
2583
+ startupNotify: desktop.startupNotify,
2584
+ iconFileName: desktop.iconFileName
2585
+ }, void 0, 2)}\n`);
2586
+ };
2587
+ /**
2588
+ * Creates a Desktop Entry file.
2589
+ */
2590
+ var createLinuxDesktopEntry = (input) => {
2591
+ const lines = [
2592
+ "[Desktop Entry]",
2593
+ "Type=Application",
2594
+ `Name=${escapeDesktopString(input.desktop.name)}`
2595
+ ];
2596
+ if (input.desktop.comment.length > 0) lines.push(`Comment=${escapeDesktopString(input.desktop.comment)}`);
2597
+ lines.push(`Exec=${input.exec}`);
2598
+ lines.push(`TryExec=${input.tryExec}`);
2599
+ lines.push(`Icon=${escapeDesktopString(input.icon)}`);
2600
+ lines.push(`Terminal=false`);
2601
+ if (input.desktop.categories.length > 0) lines.push(`Categories=${input.desktop.categories.join(";")};`);
2602
+ lines.push(`StartupNotify=${input.desktop.startupNotify ? "true" : "false"}`);
2603
+ lines.push(`StartupWMClass=${escapeDesktopString(input.desktop.desktopId)}`);
2604
+ lines.push("X-Muon-Managed=true");
2605
+ lines.push("");
2606
+ return lines.join("\n");
2607
+ };
2608
+ /**
2609
+ * Quotes one Desktop Entry Exec argument.
2610
+ */
2611
+ var quoteDesktopExecArgument = (value) => {
2612
+ let escaped = "\"";
2613
+ for (const character of value) {
2614
+ if (character === "\"" || character === "\\" || character === "$" || character === "`") escaped += "\\";
2615
+ escaped += character;
2616
+ }
2617
+ return `${escaped}"`;
2618
+ };
2619
+ var createOptionsSource = (options, directory, label) => {
2620
+ if (options === void 0) return;
2621
+ return {
2622
+ options: validateLinuxDesktopOptions(options, label),
2623
+ directory
2624
+ };
2625
+ };
2626
+ var readConfigLinuxDesktopSource = (config, directory, label) => {
2627
+ const linux = config.linux;
2628
+ if (linux === void 0) return;
2629
+ if (!isJsonObject$2(linux)) throw new Error(`${label} linux must be an object when present.`);
2630
+ const desktop = linux.desktop;
2631
+ if (desktop === void 0) return;
2632
+ return createOptionsSource(validateLinuxDesktopOptions(desktop, `${label} linux.desktop`), directory, `${label} linux.desktop`);
2633
+ };
2634
+ var validateLinuxDesktopOptions = (value, label) => {
2635
+ if (!isJsonObject$2(value)) throw new Error(`${label} must be an object.`);
2636
+ const output = {};
2637
+ copyOptionalStringDesktopField(value, "desktopId", output);
2638
+ copyOptionalStringDesktopField(value, "name", output);
2639
+ copyOptionalStringDesktopField(value, "comment", output);
2640
+ copyOptionalStringDesktopField(value, "iconPath", output);
2641
+ copyOptionalCategoriesField(value, output, label);
2642
+ copyOptionalBooleanDesktopField(value, "startupNotify", output, label);
2643
+ return output;
2644
+ };
2645
+ var copyOptionalStringDesktopField = (source, key, target) => {
2646
+ const value = source[key];
2647
+ if (value === void 0) return;
2648
+ if (typeof value !== "string") throw new Error(`${key} must be a string when present.`);
2649
+ if (value.trim() !== "") target[key] = value.trim();
2650
+ };
2651
+ var copyOptionalCategoriesField = (source, target, label) => {
2652
+ const value = source.categories;
2653
+ if (value === void 0) return;
2654
+ if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string" && entry.trim() !== "" && !entry.includes(";") && !entry.includes("\n") && !entry.includes("\r"))) throw new Error(`${label} categories must be a string array without semicolons when present.`);
2655
+ target.categories = value.map((entry) => entry.trim());
2656
+ };
2657
+ var copyOptionalBooleanDesktopField = (source, key, target, label) => {
2658
+ const value = source[key];
2659
+ if (value === void 0) return;
2660
+ if (typeof value !== "boolean") throw new Error(`${label} ${key} must be a boolean when present.`);
2661
+ target[key] = value;
2662
+ };
2663
+ var resolveStringField = (sources, field, fallback) => {
2664
+ for (const source of sources) {
2665
+ const value = source.options[field];
2666
+ if (value !== void 0 && value.trim() !== "") return value.trim();
2667
+ }
2668
+ return fallback;
2669
+ };
2670
+ var resolveCategories = (sources, fallback) => {
2671
+ for (const source of sources) if (source.options.categories !== void 0) return source.options.categories;
2672
+ return fallback;
2673
+ };
2674
+ var resolveBooleanField = (sources, field, fallback) => {
2675
+ for (const source of sources) {
2676
+ const value = source.options[field];
2677
+ if (value !== void 0) return value;
2678
+ }
2679
+ return fallback;
2680
+ };
2681
+ var resolveIconPath = async (sources, defaultIconPath) => {
2682
+ for (const source of sources) {
2683
+ const value = source.options.iconPath;
2684
+ if (value !== void 0 && value.trim() !== "") {
2685
+ const iconPath = resolveResourcePath(source.directory, value);
2686
+ await assertIconPath(iconPath, true);
2687
+ return iconPath;
2688
+ }
2689
+ }
2690
+ if (defaultIconPath !== void 0 && await fileExists$2(defaultIconPath)) {
2691
+ await assertIconPath(defaultIconPath, false);
2692
+ return defaultIconPath;
2693
+ }
2694
+ throw new Error("Linux desktop icon does not exist.");
2695
+ };
2696
+ var assertIconPath = async (iconPath, required) => {
2697
+ if ((0, node_path.extname)(iconPath).toLowerCase() !== ".png") throw new Error(`Linux desktop icon must be a .png file: ${iconPath}`);
2698
+ if (required && !await fileExists$2(iconPath)) throw new Error(`Linux desktop icon does not exist: ${iconPath}`);
2699
+ if (await fileExists$2(iconPath)) await createNormalizedIconPngData(await (0, node_fs_promises.readFile)(iconPath), iconPath, "Linux desktop icon");
2700
+ };
2701
+ var resolveDefaultLinuxDesktopIcon = async (packageDirectory) => {
2702
+ const candidates = [
2703
+ (0, node_path.join)((0, node_path.resolve)(packageDirectory), "native", "muon-bootstrap.png"),
2704
+ (0, node_path.join)(moduleDirectory$2, "native", "muon-bootstrap.png"),
2705
+ (0, node_path.join)(moduleDirectory$2, "..", "dist", "native", "muon-bootstrap.png"),
2706
+ (0, node_path.join)(moduleDirectory$2, "..", "..", "images", "muon-bootstrap-256.png")
2707
+ ];
2708
+ for (const candidate of candidates) if (await fileExists$2(candidate)) return candidate;
2709
+ };
2710
+ var sanitizeDesktopId = (value) => {
2711
+ const sanitized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[.-]+/g, "").replace(/[.-]+$/g, "");
2712
+ return sanitized.length > 0 ? sanitized : "muon-app";
2713
+ };
2714
+ var escapeDesktopString = (value) => value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t");
2715
+ var resolveResourcePath = (directory, path) => {
2716
+ return (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(directory, path);
2717
+ };
2718
+ var fileExists$2 = async (path) => {
2719
+ try {
2720
+ await (0, node_fs_promises.access)(path, node_fs.constants.F_OK);
2721
+ return true;
2722
+ } catch {
2723
+ return false;
2724
+ }
2725
+ };
2726
+ var isJsonObject$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2727
+ //#endregion
1867
2728
  //#region src/build.ts
1868
2729
  var AdmZip = __resolveDefaultExport(adm_zip.default, false);
1869
2730
  globalThis.__screwUpIsInCJS_f9d0012cc677 = true;
@@ -1923,17 +2784,49 @@ var buildMuonApp = async (options = {}) => {
1923
2784
  const appId = resolveAppId(packageJson, options.appId);
1924
2785
  const buildConfig = await readBuildConfig(root, options.configPath);
1925
2786
  const assetInput = resolveAssetInput(root, options.assetSourcePath, options.assetPrefix, buildConfig);
2787
+ const windowsResource = await resolveMuonWindowsResource({
2788
+ root,
2789
+ packageDirectory,
2790
+ packageJson,
2791
+ muonConfig: buildConfig.config,
2792
+ muonConfigDirectory: buildConfig.directory,
2793
+ options: options.windowsResource,
2794
+ defaults: {
2795
+ productName: appName,
2796
+ fileDescription: appName,
2797
+ companyName: "Unknown",
2798
+ version: "0.0.0",
2799
+ copyright: void 0
2800
+ }
2801
+ });
2802
+ const linuxDesktop = await resolveMuonLinuxDesktop({
2803
+ root,
2804
+ packageDirectory,
2805
+ muonConfig: buildConfig.config,
2806
+ muonConfigDirectory: buildConfig.directory,
2807
+ options: options.linuxDesktop,
2808
+ defaults: {
2809
+ desktopId: appId,
2810
+ name: resolveLinuxDesktopDefaultName(packageJson, appName),
2811
+ comment: resolvePackageDescription(packageJson),
2812
+ categories: ["Utility"],
2813
+ startupNotify: true
2814
+ }
2815
+ });
1926
2816
  const salt = Buffer.from(options.assetSalt ?? (0, node_crypto.randomBytes)(assetSaltByteLength));
1927
2817
  const results = [];
1928
2818
  for (const target of targets) {
1929
2819
  const result = await buildMuonTarget({
1930
2820
  packageDirectory,
2821
+ root,
1931
2822
  outputRoot,
1932
2823
  appName,
1933
2824
  appId,
1934
2825
  target,
1935
2826
  assetInput,
1936
2827
  sourceConfig: buildConfig.config,
2828
+ windowsResource,
2829
+ linuxDesktop,
1937
2830
  salt
1938
2831
  });
1939
2832
  results.push(result);
@@ -1991,6 +2884,8 @@ var sanitizeAppId = (value) => {
1991
2884
  const sanitized = (value.startsWith("@") ? value.slice(1) : value).trim().toLowerCase().replace("/", ".").replace(/[^a-z0-9._-]+/g, ".").replace(/^[.]+/g, "").replace(/[.]+$/g, "");
1992
2885
  return sanitized.length > 0 ? sanitized : defaultAppId;
1993
2886
  };
2887
+ var resolveLinuxDesktopDefaultName = (packageJson, appName) => typeof packageJson.name === "string" && packageJson.name.trim() !== "" ? packageJson.name.trim() : appName;
2888
+ var resolvePackageDescription = (packageJson) => typeof packageJson.description === "string" ? packageJson.description.trim() : "";
1994
2889
  var readBuildConfig = async (root, configPath) => {
1995
2890
  const resolvedConfigPath = await resolveConfigPath(root, configPath);
1996
2891
  if (resolvedConfigPath === void 0) return {
@@ -2064,7 +2959,7 @@ var buildMuonTarget = async (input) => {
2064
2959
  await (0, node_fs_promises.copyFile)(sourceBootstrapPath, launcherPath);
2065
2960
  await (0, node_fs_promises.chmod)(launcherPath, executableMode);
2066
2961
  const asset = await writeAssetArchive(input.assetInput, assetZipPath, input.salt);
2067
- const embeddedConfig = createEmbeddedConfig(input.sourceConfig, asset, input.appId);
2962
+ const embeddedConfig = createEmbeddedConfig(input.sourceConfig, asset, input.appId, input.linuxDesktop.desktopId);
2068
2963
  await withTemporaryConfig(embeddedConfig, async (configPath) => {
2069
2964
  await embedMuonConfigInRuntime({
2070
2965
  runtimePath: outputPath,
@@ -2077,13 +2972,21 @@ var buildMuonTarget = async (input) => {
2077
2972
  outputPath: void 0
2078
2973
  });
2079
2974
  });
2975
+ if (descriptor.os === "windows") await updateWindowsPeResources({
2976
+ executablePath: launcherPath,
2977
+ resource: input.windowsResource,
2978
+ environment: process.env,
2979
+ cwd: input.root
2980
+ });
2981
+ else if (descriptor.os === "linux") await writeLinuxDesktopDistributionFiles(outputPath, input.linuxDesktop);
2080
2982
  return {
2081
2983
  target: input.target,
2082
2984
  distributionDirectoryName: descriptor.distributionDirectoryName,
2083
2985
  outputPath,
2084
2986
  launcherPath,
2085
2987
  asset,
2086
- embeddedConfig
2988
+ embeddedConfig,
2989
+ ...descriptor.os === "linux" ? { linuxDesktop: input.linuxDesktop } : {}
2087
2990
  };
2088
2991
  };
2089
2992
  var verifyTargetInputs = async (input) => {
@@ -2158,13 +3061,13 @@ var createZipArchive = (entries) => {
2158
3061
  for (const entry of entries) zip.addFile(entry.name, entry.data);
2159
3062
  return zip.toBuffer();
2160
3063
  };
2161
- var createEmbeddedConfig = (sourceConfig, asset, appId) => {
3064
+ var createEmbeddedConfig = (sourceConfig, asset, appId, desktopId) => {
2162
3065
  const sourceAsset = sourceConfig.asset;
2163
3066
  if (sourceAsset !== void 0 && !isJsonObject$1(sourceAsset)) throw new Error("muon.json asset must be an object when present.");
2164
3067
  const sourceBootstrap = sourceConfig.bootstrap;
2165
3068
  if (sourceBootstrap !== void 0 && !isJsonObject$1(sourceBootstrap)) throw new Error("muon.json bootstrap must be an object when present.");
2166
3069
  return {
2167
- ...sourceConfig,
3070
+ ...stripBuildOnlyLinuxDesktopConfig(stripBuildOnlyWindowsResourceConfig(sourceConfig)),
2168
3071
  asset: {
2169
3072
  ...sourceAsset ?? {},
2170
3073
  sourcePath: appConfigSourcePath,
@@ -2173,7 +3076,8 @@ var createEmbeddedConfig = (sourceConfig, asset, appId) => {
2173
3076
  },
2174
3077
  bootstrap: {
2175
3078
  ...sourceBootstrap ?? {},
2176
- appId
3079
+ appId,
3080
+ desktopId
2177
3081
  }
2178
3082
  };
2179
3083
  };
@@ -2218,6 +3122,190 @@ var isJsonObject$1 = (value) => {
2218
3122
  };
2219
3123
  var getErrorMessage$1 = (error) => error instanceof Error ? error.message : String(error);
2220
3124
  //#endregion
3125
+ //#region src/vite-options.ts
3126
+ /**
3127
+ * Metadata symbol used to recover `muon()` plugin options from `vite.config.*`.
3128
+ */
3129
+ var muonVitePluginOptionsSymbol = Symbol.for("muon.vite.plugin.options");
3130
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3131
+ var isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
3132
+ var isWindowsResourceOptions = (value) => {
3133
+ if (!isRecord(value)) return false;
3134
+ return (value.iconPath === void 0 || typeof value.iconPath === "string") && (value.productName === void 0 || typeof value.productName === "string") && (value.fileDescription === void 0 || typeof value.fileDescription === "string") && (value.companyName === void 0 || typeof value.companyName === "string") && (value.version === void 0 || typeof value.version === "string") && (value.copyright === void 0 || typeof value.copyright === "string") && (value.language === void 0 || typeof value.language === "number" && Number.isInteger(value.language)) && (value.codePage === void 0 || typeof value.codePage === "number" && Number.isInteger(value.codePage));
3135
+ };
3136
+ var isLinuxDesktopOptions = (value) => {
3137
+ if (!isRecord(value)) return false;
3138
+ return (value.desktopId === void 0 || typeof value.desktopId === "string") && (value.name === void 0 || typeof value.name === "string") && (value.comment === void 0 || typeof value.comment === "string") && (value.iconPath === void 0 || typeof value.iconPath === "string") && (value.categories === void 0 || isStringArray(value.categories)) && (value.startupNotify === void 0 || typeof value.startupNotify === "boolean");
3139
+ };
3140
+ var isMuonViteBuildOptions = (value) => {
3141
+ if (!isRecord(value)) return false;
3142
+ return (value.targets === void 0 || isStringArray(value.targets)) && (value.allTargets === void 0 || typeof value.allTargets === "boolean") && (value.appName === void 0 || typeof value.appName === "string") && (value.appId === void 0 || typeof value.appId === "string") && (value.outputRoot === void 0 || typeof value.outputRoot === "string") && (value.configPath === void 0 || typeof value.configPath === "string") && (value.windowsResource === void 0 || isWindowsResourceOptions(value.windowsResource)) && (value.linuxDesktop === void 0 || isLinuxDesktopOptions(value.linuxDesktop)) && (value.packageDirectory === void 0 || typeof value.packageDirectory === "string") && (value.assetSalt === void 0 || value.assetSalt instanceof Uint8Array);
3143
+ };
3144
+ var isMuonVitePluginOptions = (value) => {
3145
+ if (!isRecord(value)) return false;
3146
+ return (value.muonPath === void 0 || typeof value.muonPath === "string") && (value.cefPath === void 0 || typeof value.cefPath === "string") && (value.stagePath === void 0 || typeof value.stagePath === "string") && (value.open === void 0 || typeof value.open === "boolean") && (value.enableDebugger === void 0 || typeof value.enableDebugger === "boolean") && (value.build === void 0 || typeof value.build === "boolean" || isMuonViteBuildOptions(value.build));
3147
+ };
3148
+ /**
3149
+ * Attaches raw Muon Vite plugin options to a plugin instance.
3150
+ *
3151
+ * @param plugin Plugin object.
3152
+ * @param options Raw plugin options.
3153
+ * @returns Plugin object with internal Muon metadata.
3154
+ */
3155
+ var attachMuonVitePluginOptions = (plugin, options) => {
3156
+ Object.defineProperty(plugin, muonVitePluginOptionsSymbol, {
3157
+ configurable: false,
3158
+ enumerable: false,
3159
+ value: { ...options },
3160
+ writable: false
3161
+ });
3162
+ return plugin;
3163
+ };
3164
+ /**
3165
+ * Reads raw Muon Vite plugin options from a plugin instance.
3166
+ *
3167
+ * @param plugin Candidate plugin object.
3168
+ * @returns Attached Muon options, if present.
3169
+ */
3170
+ var getMuonVitePluginOptions = (plugin) => {
3171
+ if (!isRecord(plugin)) return;
3172
+ const options = plugin[muonVitePluginOptionsSymbol];
3173
+ return isMuonVitePluginOptions(options) ? options : void 0;
3174
+ };
3175
+ /**
3176
+ * Resolves nested Vite plugin option values into a flat list.
3177
+ *
3178
+ * @param pluginOptions Raw `plugins` field from a Vite config.
3179
+ * @returns Flat plugin object list.
3180
+ */
3181
+ var flattenVitePluginOptions = async (pluginOptions) => {
3182
+ const resolvedValue = await pluginOptions;
3183
+ if (resolvedValue === null || resolvedValue === void 0 || !resolvedValue) return [];
3184
+ if (Array.isArray(resolvedValue)) return (await Promise.all(resolvedValue.map((entry) => flattenVitePluginOptions(entry)))).flat();
3185
+ return [resolvedValue];
3186
+ };
3187
+ //#endregion
3188
+ //#region src/build-sequence.ts
3189
+ /**
3190
+ * Environment variable used to prevent the Vite plugin build hook from running
3191
+ * when a CLI command owns the Muon build sequence.
3192
+ */
3193
+ var muonBuildSequenceSuppressViteBuildEnvironmentKey = "MUON_SUPPRESS_VITE_MUON_BUILD";
3194
+ var isMissingVitePackageError = (error) => {
3195
+ const candidate = error;
3196
+ return candidate.code === "ERR_MODULE_NOT_FOUND" && typeof candidate.message === "string" && candidate.message.includes("vite");
3197
+ };
3198
+ var resolveViteOutputDirectory = (config) => {
3199
+ return (0, node_path.isAbsolute)(config.build.outDir) ? config.build.outDir : (0, node_path.resolve)(config.root, config.build.outDir);
3200
+ };
3201
+ var findMuonVitePluginOptions = async (plugins) => {
3202
+ const muonPlugins = (await flattenVitePluginOptions(plugins)).map((plugin) => getMuonVitePluginOptions(plugin)).filter((pluginOptions) => pluginOptions !== void 0);
3203
+ if (muonPlugins.length > 1) throw new Error("Multiple muon() plugin definitions were found in vite.config.*.");
3204
+ return muonPlugins[0];
3205
+ };
3206
+ /**
3207
+ * Loads Vite configuration only far enough to discover whether a Muon plugin
3208
+ * controls the build sequence.
3209
+ */
3210
+ var loadMuonBuildSequenceProject = async (cwd) => {
3211
+ const resolvedCwd = (0, node_path.resolve)(cwd);
3212
+ let vite;
3213
+ try {
3214
+ vite = await import("vite");
3215
+ } catch (error) {
3216
+ if (isMissingVitePackageError(error)) return {
3217
+ root: resolvedCwd,
3218
+ viteBuildRoot: resolvedCwd,
3219
+ viteOutputDirectory: void 0,
3220
+ pluginOptions: void 0
3221
+ };
3222
+ throw error;
3223
+ }
3224
+ const resolvedConfig = await vite.resolveConfig({
3225
+ root: resolvedCwd,
3226
+ logLevel: "silent"
3227
+ }, "build", "production", "production");
3228
+ const pluginOptions = await findMuonVitePluginOptions(resolvedConfig.plugins);
3229
+ if (pluginOptions === void 0) return {
3230
+ root: resolvedCwd,
3231
+ viteBuildRoot: resolvedCwd,
3232
+ viteOutputDirectory: void 0,
3233
+ pluginOptions: void 0
3234
+ };
3235
+ return {
3236
+ root: resolvedConfig.root,
3237
+ viteBuildRoot: resolvedCwd,
3238
+ viteOutputDirectory: resolveViteOutputDirectory(resolvedConfig),
3239
+ pluginOptions
3240
+ };
3241
+ };
3242
+ /**
3243
+ * Returns Muon build options from a Muon Vite plugin declaration.
3244
+ */
3245
+ var resolveMuonViteBuildOptions = (pluginOptions) => {
3246
+ if (pluginOptions?.build === false) throw new Error("Muon build is disabled by muon({ build: false }).");
3247
+ return typeof pluginOptions?.build === "object" ? pluginOptions.build : {};
3248
+ };
3249
+ var runViteBuild = async (root) => {
3250
+ const vite = await import("vite");
3251
+ const previous = process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey];
3252
+ process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey] = "1";
3253
+ try {
3254
+ await vite.build({
3255
+ root,
3256
+ logLevel: "silent"
3257
+ });
3258
+ } finally {
3259
+ if (previous === void 0) delete process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey];
3260
+ else process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey] = previous;
3261
+ }
3262
+ };
3263
+ var hasExplicitTargets = (options) => options.targets !== void 0 && options.targets.length > 0;
3264
+ var copyDefinedBuildOptions = (output, input, usesViteAssets) => {
3265
+ if (input.assetSourcePath !== void 0) {
3266
+ if (usesViteAssets) throw new Error("--assets cannot be used when a muon() Vite plugin provides the build sequence.");
3267
+ output.assetSourcePath = input.assetSourcePath;
3268
+ }
3269
+ if (input.assetPrefix !== void 0) output.assetPrefix = input.assetPrefix;
3270
+ if (input.targets !== void 0 && input.targets.length > 0) {
3271
+ output.targets = input.targets;
3272
+ if (input.allTargets === void 0) output.allTargets = false;
3273
+ }
3274
+ if (input.allTargets !== void 0) output.allTargets = input.allTargets;
3275
+ if (input.appName !== void 0) output.appName = input.appName;
3276
+ if (input.appId !== void 0) output.appId = input.appId;
3277
+ if (input.outputRoot !== void 0) output.outputRoot = input.outputRoot;
3278
+ if (input.configPath !== void 0) output.configPath = input.configPath;
3279
+ if (input.windowsResource !== void 0) {
3280
+ const windowsResource = mergeMuonWindowsResourceOptions(input.windowsResource, output.windowsResource);
3281
+ if (windowsResource !== void 0) output.windowsResource = windowsResource;
3282
+ }
3283
+ if (input.linuxDesktop !== void 0) {
3284
+ const linuxDesktop = mergeMuonLinuxDesktopOptions(input.linuxDesktop, output.linuxDesktop);
3285
+ if (linuxDesktop !== void 0) output.linuxDesktop = linuxDesktop;
3286
+ }
3287
+ if (input.packageDirectory !== void 0) output.packageDirectory = input.packageDirectory;
3288
+ if (input.assetSalt !== void 0) output.assetSalt = input.assetSalt;
3289
+ };
3290
+ /**
3291
+ * Runs the project build sequence used by `muon build` and `muon pack`.
3292
+ */
3293
+ var runMuonBuildSequence = async (options = {}, loadedProject) => {
3294
+ const project = loadedProject ?? await loadMuonBuildSequenceProject(options.root ?? process.cwd());
3295
+ const pluginBuildOptions = resolveMuonViteBuildOptions(project.pluginOptions);
3296
+ const usesViteAssets = project.pluginOptions !== void 0;
3297
+ const buildOptions = { root: project.root };
3298
+ if (usesViteAssets) {
3299
+ if (project.viteOutputDirectory === void 0) throw new Error("Vite output directory could not be resolved.");
3300
+ Object.assign(buildOptions, pluginBuildOptions);
3301
+ buildOptions.assetSourcePath = project.viteOutputDirectory;
3302
+ buildOptions.assetPrefix = "main";
3303
+ await runViteBuild(project.viteBuildRoot);
3304
+ } else if (options.defaultAllTargets !== void 0 && options.allTargets === void 0 && !hasExplicitTargets(options)) buildOptions.allTargets = options.defaultAllTargets;
3305
+ copyDefinedBuildOptions(buildOptions, options, usesViteAssets);
3306
+ return await buildMuonApp(buildOptions);
3307
+ };
3308
+ //#endregion
2221
3309
  //#region src/vite-internals.ts
2222
3310
  var resolveFromRoot = (root, path) => (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(root, path);
2223
3311
  var moduleDirectory = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
@@ -2427,7 +3515,7 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
2427
3515
  environment,
2428
3516
  cwd: server.config.root
2429
3517
  });
2430
- if (preparedRuntime.stagePath === void 0) throw new Error("muon-prepare did not return a staged runtime path.");
3518
+ if (preparedRuntime.stagePath === void 0) throw new Error("muon-builder did not return a staged runtime path.");
2431
3519
  const paths = await createRuntimePaths(server, preparedRuntime.stagePath, platform, await resolveProjectConfigPath(server));
2432
3520
  await writeLaunchScript(paths, platform);
2433
3521
  let cleanupPromise = void 0;
@@ -2457,67 +3545,18 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
2457
3545
  });
2458
3546
  };
2459
3547
  //#endregion
2460
- //#region src/vite-options.ts
2461
- /**
2462
- * Metadata symbol used to recover `muon()` plugin options from `vite.config.*`.
2463
- */
2464
- var muonVitePluginOptionsSymbol = Symbol.for("muon.vite.plugin.options");
2465
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2466
- var isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
2467
- var isMuonViteBuildOptions = (value) => {
2468
- if (!isRecord(value)) return false;
2469
- return (value.targets === void 0 || isStringArray(value.targets)) && (value.allTargets === void 0 || typeof value.allTargets === "boolean") && (value.appName === void 0 || typeof value.appName === "string") && (value.appId === void 0 || typeof value.appId === "string") && (value.outputRoot === void 0 || typeof value.outputRoot === "string") && (value.configPath === void 0 || typeof value.configPath === "string") && (value.packageDirectory === void 0 || typeof value.packageDirectory === "string") && (value.assetSalt === void 0 || value.assetSalt instanceof Uint8Array);
2470
- };
2471
- var isMuonVitePluginOptions = (value) => {
2472
- if (!isRecord(value)) return false;
2473
- return (value.muonPath === void 0 || typeof value.muonPath === "string") && (value.cefPath === void 0 || typeof value.cefPath === "string") && (value.stagePath === void 0 || typeof value.stagePath === "string") && (value.open === void 0 || typeof value.open === "boolean") && (value.enableDebugger === void 0 || typeof value.enableDebugger === "boolean") && (value.build === void 0 || typeof value.build === "boolean" || isMuonViteBuildOptions(value.build));
2474
- };
2475
- /**
2476
- * Attaches raw Muon Vite plugin options to a plugin instance.
2477
- *
2478
- * @param plugin Plugin object.
2479
- * @param options Raw plugin options.
2480
- * @returns Plugin object with internal Muon metadata.
2481
- */
2482
- var attachMuonVitePluginOptions = (plugin, options) => {
2483
- Object.defineProperty(plugin, muonVitePluginOptionsSymbol, {
2484
- configurable: false,
2485
- enumerable: false,
2486
- value: { ...options },
2487
- writable: false
2488
- });
2489
- return plugin;
2490
- };
2491
- /**
2492
- * Reads raw Muon Vite plugin options from a plugin instance.
2493
- *
2494
- * @param plugin Candidate plugin object.
2495
- * @returns Attached Muon options, if present.
2496
- */
2497
- var getMuonVitePluginOptions = (plugin) => {
2498
- if (!isRecord(plugin)) return;
2499
- const options = plugin[muonVitePluginOptionsSymbol];
2500
- return isMuonVitePluginOptions(options) ? options : void 0;
2501
- };
2502
- /**
2503
- * Resolves nested Vite plugin option values into a flat list.
2504
- *
2505
- * @param pluginOptions Raw `plugins` field from a Vite config.
2506
- * @returns Flat plugin object list.
2507
- */
2508
- var flattenVitePluginOptions = async (pluginOptions) => {
2509
- const resolvedValue = await pluginOptions;
2510
- if (resolvedValue === null || resolvedValue === void 0 || !resolvedValue) return [];
2511
- if (Array.isArray(resolvedValue)) return (await Promise.all(resolvedValue.map((entry) => flattenVitePluginOptions(entry)))).flat();
2512
- return [resolvedValue];
2513
- };
2514
- //#endregion
2515
3548
  Object.defineProperty(exports, "__toESM", {
2516
3549
  enumerable: true,
2517
3550
  get: function() {
2518
3551
  return __toESM;
2519
3552
  }
2520
3553
  });
3554
+ Object.defineProperty(exports, "allMuonTargets", {
3555
+ enumerable: true,
3556
+ get: function() {
3557
+ return allMuonTargets;
3558
+ }
3559
+ });
2521
3560
  Object.defineProperty(exports, "attachMuonVitePluginOptions", {
2522
3561
  enumerable: true,
2523
3562
  get: function() {
@@ -2530,6 +3569,18 @@ Object.defineProperty(exports, "buildMuonApp", {
2530
3569
  return buildMuonApp;
2531
3570
  }
2532
3571
  });
3572
+ Object.defineProperty(exports, "createLinuxDesktopEntry", {
3573
+ enumerable: true,
3574
+ get: function() {
3575
+ return createLinuxDesktopEntry;
3576
+ }
3577
+ });
3578
+ Object.defineProperty(exports, "createWindowsIconFromPngFile", {
3579
+ enumerable: true,
3580
+ get: function() {
3581
+ return createWindowsIconFromPngFile;
3582
+ }
3583
+ });
2533
3584
  Object.defineProperty(exports, "embedMuonConfigInBootstrapFile", {
2534
3585
  enumerable: true,
2535
3586
  get: function() {
@@ -2560,6 +3611,12 @@ Object.defineProperty(exports, "flattenVitePluginOptions", {
2560
3611
  return flattenVitePluginOptions;
2561
3612
  }
2562
3613
  });
3614
+ Object.defineProperty(exports, "getDefaultMuonBuildTarget", {
3615
+ enumerable: true,
3616
+ get: function() {
3617
+ return getDefaultMuonBuildTarget;
3618
+ }
3619
+ });
2563
3620
  Object.defineProperty(exports, "getDefaultMuonPrepareTarget", {
2564
3621
  enumerable: true,
2565
3622
  get: function() {
@@ -2584,6 +3641,48 @@ Object.defineProperty(exports, "getMuonVitePluginOptions", {
2584
3641
  return getMuonVitePluginOptions;
2585
3642
  }
2586
3643
  });
3644
+ Object.defineProperty(exports, "loadMuonBuildSequenceProject", {
3645
+ enumerable: true,
3646
+ get: function() {
3647
+ return loadMuonBuildSequenceProject;
3648
+ }
3649
+ });
3650
+ Object.defineProperty(exports, "mergeMuonLinuxDesktopOptions", {
3651
+ enumerable: true,
3652
+ get: function() {
3653
+ return mergeMuonLinuxDesktopOptions;
3654
+ }
3655
+ });
3656
+ Object.defineProperty(exports, "mergeMuonWindowsResourceOptions", {
3657
+ enumerable: true,
3658
+ get: function() {
3659
+ return mergeMuonWindowsResourceOptions;
3660
+ }
3661
+ });
3662
+ Object.defineProperty(exports, "muonBuildSequenceSuppressViteBuildEnvironmentKey", {
3663
+ enumerable: true,
3664
+ get: function() {
3665
+ return muonBuildSequenceSuppressViteBuildEnvironmentKey;
3666
+ }
3667
+ });
3668
+ Object.defineProperty(exports, "normalizeMuonTarget", {
3669
+ enumerable: true,
3670
+ get: function() {
3671
+ return normalizeMuonTarget;
3672
+ }
3673
+ });
3674
+ Object.defineProperty(exports, "quoteDesktopExecArgument", {
3675
+ enumerable: true,
3676
+ get: function() {
3677
+ return quoteDesktopExecArgument;
3678
+ }
3679
+ });
3680
+ Object.defineProperty(exports, "readMuonConfigForWindowsResource", {
3681
+ enumerable: true,
3682
+ get: function() {
3683
+ return readMuonConfigForWindowsResource;
3684
+ }
3685
+ });
2587
3686
  Object.defineProperty(exports, "require_dist", {
2588
3687
  enumerable: true,
2589
3688
  get: function() {
@@ -2596,6 +3695,24 @@ Object.defineProperty(exports, "resolveMuonRuntimePath", {
2596
3695
  return resolveMuonRuntimePath;
2597
3696
  }
2598
3697
  });
3698
+ Object.defineProperty(exports, "resolveMuonViteBuildOptions", {
3699
+ enumerable: true,
3700
+ get: function() {
3701
+ return resolveMuonViteBuildOptions;
3702
+ }
3703
+ });
3704
+ Object.defineProperty(exports, "resolveMuonWindowsResource", {
3705
+ enumerable: true,
3706
+ get: function() {
3707
+ return resolveMuonWindowsResource;
3708
+ }
3709
+ });
3710
+ Object.defineProperty(exports, "runMuonBuildSequence", {
3711
+ enumerable: true,
3712
+ get: function() {
3713
+ return runMuonBuildSequence;
3714
+ }
3715
+ });
2599
3716
  Object.defineProperty(exports, "runMuonPrepare", {
2600
3717
  enumerable: true,
2601
3718
  get: function() {
@@ -2608,5 +3725,11 @@ Object.defineProperty(exports, "startMuonViteBrowserBridge", {
2608
3725
  return startMuonViteBrowserBridge;
2609
3726
  }
2610
3727
  });
3728
+ Object.defineProperty(exports, "updateWindowsPeResources", {
3729
+ enumerable: true,
3730
+ get: function() {
3731
+ return updateWindowsPeResources;
3732
+ }
3733
+ });
2611
3734
 
2612
- //# sourceMappingURL=vite-options-FFh0NWUa.cjs.map
3735
+ //# sourceMappingURL=vite-internals-ChWiL2TL.cjs.map