muon-ui 0.7.0 → 0.9.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 (46) hide show
  1. package/README.md +9 -12
  2. package/dist/cli.cjs +291 -126
  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-256.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/libmuon-ui.so +0 -0
  19. package/dist/runtime/linux-amd64/muon-core +0 -0
  20. package/dist/runtime/linux-arm64/CREDITS.md +24 -0
  21. package/dist/runtime/linux-arm64/libmuon-ui.so +0 -0
  22. package/dist/runtime/linux-arm64/muon-core +0 -0
  23. package/dist/runtime/linux-armhf/CREDITS.md +24 -0
  24. package/dist/runtime/linux-armhf/libmuon-ui.so +0 -0
  25. package/dist/runtime/linux-armhf/muon-core +0 -0
  26. package/dist/runtime/windows-amd64/CREDITS.md +24 -0
  27. package/dist/runtime/windows-amd64/libcardio.dll +0 -0
  28. package/dist/runtime/windows-amd64/libmuon-ui.dll +0 -0
  29. package/dist/runtime/windows-amd64/muon-core.exe +0 -0
  30. package/dist/runtime/windows-i686/CREDITS.md +24 -0
  31. package/dist/runtime/windows-i686/libcardio.dll +0 -0
  32. package/dist/runtime/windows-i686/libmuon-ui.dll +0 -0
  33. package/dist/runtime/windows-i686/muon-core.exe +0 -0
  34. package/dist/{vite-options-FFh0NWUa.cjs → vite-internals-HXgH7b5M.cjs} +1460 -121
  35. package/dist/vite-internals-HXgH7b5M.cjs.map +1 -0
  36. package/dist/vite.cjs +452 -10
  37. package/dist/vite.cjs.map +1 -1
  38. package/dist/vite.mjs +1552 -92
  39. package/dist/vite.mjs.map +1 -1
  40. package/muon.d.ts +141 -27
  41. package/package.json +14 -10
  42. package/vite.d.ts +276 -14
  43. package/dist/native/linux-amd64/muon-prepare +0 -0
  44. package/dist/native/windows-amd64/muon-prepare.exe +0 -0
  45. package/dist/native/windows-i686/muon-prepare.exe +0 -0
  46. 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.9.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: 3437b522a51d8772713b72722b109c5ff30fc711
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",
@@ -146,6 +148,20 @@ Object.fromEntries(allMuonTargets.map((target) => [targetDescriptors[target].cef
146
148
  */
147
149
  var getMuonTargetDescriptor = (target) => targetDescriptors[target];
148
150
  /**
151
+ * Returns the runtime app identifier embedded for one target distribution.
152
+ *
153
+ * @param appId Base sanitized application identifier.
154
+ * @param target Public Muon target.
155
+ * @returns Target runtime application identifier.
156
+ *
157
+ * @remarks Windows targets append their public architecture name so 32-bit and
158
+ * 64-bit packages do not share runtime state or installer identities.
159
+ */
160
+ var getMuonTargetRuntimeAppId = (appId, target) => {
161
+ const descriptor = getMuonTargetDescriptor(target);
162
+ return descriptor.os === "windows" ? `${appId}.${descriptor.arch}` : appId;
163
+ };
164
+ /**
149
165
  * Normalizes a user supplied public Muon target.
150
166
  *
151
167
  * @param target Target value supplied by the user.
@@ -192,8 +208,8 @@ var getDefaultMuonPrepareTarget = (platform, architecture) => {
192
208
  throw new Error(`Unsupported Muon prepare target: platform=${platform}, arch=${architecture}`);
193
209
  }
194
210
  };
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));
211
+ var getBuilderExecutableName = (platform) => platform === "win32" ? "muon-builder.exe" : "muon-builder";
212
+ var moduleDirectory$4 = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
197
213
  var canExecute = async (path) => {
198
214
  try {
199
215
  await (0, node_fs_promises.access)(path, node_fs.constants.X_OK);
@@ -202,12 +218,12 @@ var canExecute = async (path) => {
202
218
  return false;
203
219
  }
204
220
  };
205
- var resolveMuonPrepareExecutable = async (options) => {
206
- const explicit = options.prepareExecutablePath ?? options.environment.MUON_PREPARE_PATH;
221
+ var resolveMuonBuilderExecutable = async (options) => {
222
+ const explicit = options.prepareExecutablePath ?? options.environment.MUON_BUILDER_PATH;
207
223
  if (explicit !== void 0 && explicit !== "") return explicit;
208
- const executableName = getPrepareExecutableName(process.platform);
224
+ const executableName = getBuilderExecutableName(process.platform);
209
225
  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)];
226
+ const candidates = [(0, node_path.join)(moduleDirectory$4, "native", target, executableName), (0, node_path.join)(moduleDirectory$4, "..", "dist", "native", target, executableName)];
211
227
  for (const candidate of candidates) if (await canExecute(candidate)) return candidate;
212
228
  return candidates[0] ?? executableName;
213
229
  };
@@ -226,14 +242,100 @@ var createMuonPrepareArguments = (options) => {
226
242
  if (options.quiet) args.push("--quiet");
227
243
  return args;
228
244
  };
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), {
245
+ var spinnerFrames = [
246
+ "-",
247
+ "\\",
248
+ "|",
249
+ "/"
250
+ ];
251
+ var spinnerIntervalMilliseconds = 100;
252
+ var terminalLineStart = "\r";
253
+ var terminalClearLine = "\x1B[K";
254
+ var isMuonPrepareStatusLine = (line) => {
255
+ const trimmed = line.trimEnd();
256
+ return trimmed.endsWith("...") || trimmed === "Failed to prepare CEF." || trimmed.startsWith("Downloading CEF binary:");
257
+ };
258
+ var shouldKeepMuonPrepareStatusAfterLine = (line) => line.startsWith("Muon files copied to staging:");
259
+ var createPlainStderrForwarder = () => ({
260
+ write: (chunk) => {
261
+ process.stderr.write(chunk);
262
+ },
263
+ flush: () => {}
264
+ });
265
+ var createSpinnerStderrForwarder = () => {
266
+ let pending = "";
267
+ let activeStatus = void 0;
268
+ let frameIndex = 0;
269
+ let timer = void 0;
270
+ const writeRaw = (chunk) => {
271
+ process.stderr.write(chunk);
272
+ };
273
+ const stopTimer = () => {
274
+ if (timer !== void 0) {
275
+ clearInterval(timer);
276
+ timer = void 0;
277
+ }
278
+ };
279
+ const renderStatus = () => {
280
+ if (activeStatus === void 0) return;
281
+ writeRaw(`${terminalLineStart}${spinnerFrames[frameIndex] ?? spinnerFrames[0]} ${activeStatus}${terminalClearLine}`);
282
+ frameIndex = (frameIndex + 1) % spinnerFrames.length;
283
+ };
284
+ const ensureTimer = () => {
285
+ if (timer === void 0) timer = setInterval(renderStatus, spinnerIntervalMilliseconds);
286
+ };
287
+ const finishStatus = () => {
288
+ if (activeStatus === void 0) return;
289
+ const status = activeStatus;
290
+ activeStatus = void 0;
291
+ stopTimer();
292
+ frameIndex = 0;
293
+ writeRaw(`${terminalLineStart}${status}${terminalClearLine}\n`);
294
+ };
295
+ const startStatus = (status) => {
296
+ if (activeStatus !== void 0 && activeStatus !== status) finishStatus();
297
+ activeStatus = status;
298
+ renderStatus();
299
+ ensureTimer();
300
+ };
301
+ const writeLine = (line) => {
302
+ const text = line.replace(/\r?\n$/, "");
303
+ if (isMuonPrepareStatusLine(text)) {
304
+ startStatus(text.trimEnd());
305
+ return;
306
+ }
307
+ if (activeStatus !== void 0 && shouldKeepMuonPrepareStatusAfterLine(text)) {
308
+ writeRaw(`${terminalLineStart}${terminalClearLine}${line.endsWith("\n") ? line : `${line}\n`}`);
309
+ renderStatus();
310
+ return;
311
+ }
312
+ finishStatus();
313
+ writeRaw(line);
314
+ };
315
+ return {
316
+ write: (chunk) => {
317
+ pending += chunk;
318
+ for (;;) {
319
+ const newlineIndex = pending.indexOf("\n");
320
+ if (newlineIndex < 0) break;
321
+ const line = pending.slice(0, newlineIndex + 1);
322
+ pending = pending.slice(newlineIndex + 1);
323
+ writeLine(line);
324
+ }
325
+ },
326
+ flush: () => {
327
+ if (pending.length > 0) {
328
+ const line = pending;
329
+ pending = "";
330
+ writeLine(line);
331
+ }
332
+ finishStatus();
333
+ }
334
+ };
335
+ };
336
+ var createMuonPrepareStderrForwarder = () => process.stderr.isTTY === true ? createSpinnerStderrForwarder() : createPlainStderrForwarder();
337
+ var runMuonPrepareCommand = async (options) => {
338
+ const child = (0, node_child_process.spawn)(await resolveMuonBuilderExecutable(options), [...options.args], {
237
339
  cwd: options.cwd,
238
340
  env: options.environment,
239
341
  stdio: [
@@ -246,22 +348,46 @@ var runMuonPrepare = async (options) => {
246
348
  let stderr = "";
247
349
  child.stdout.setEncoding("utf8");
248
350
  child.stderr.setEncoding("utf8");
351
+ const stderrForwarder = options.quiet ? void 0 : createMuonPrepareStderrForwarder();
249
352
  child.stdout.on("data", (chunk) => {
250
353
  stdout += chunk;
251
354
  });
252
355
  child.stderr.on("data", (chunk) => {
253
356
  stderr += chunk;
254
- if (!options.quiet) process.stderr.write(chunk);
357
+ stderrForwarder?.write(chunk);
255
358
  });
256
- const exitCode = await new Promise((resolvePromise, reject) => {
257
- child.on("error", reject);
258
- child.on("close", (code) => {
259
- resolvePromise(code ?? 1);
260
- });
359
+ const exitCode = await (async () => {
360
+ try {
361
+ return await new Promise((resolvePromise, reject) => {
362
+ child.on("error", reject);
363
+ child.on("close", (code) => {
364
+ resolvePromise(code ?? 1);
365
+ });
366
+ });
367
+ } finally {
368
+ stderrForwarder?.flush();
369
+ }
370
+ })();
371
+ if (exitCode !== 0) throw new Error(`muon-builder failed with exit code ${exitCode}.\n${stderr.trim()}`);
372
+ return stdout;
373
+ };
374
+ /**
375
+ * Invokes the native muon-builder executable and returns the prepared runtime.
376
+ *
377
+ * @param options Native prepare invocation options.
378
+ * @returns Prepared runtime location.
379
+ */
380
+ var runMuonPrepare = async (options) => {
381
+ const args = createMuonPrepareArguments(options);
382
+ const stdout = await runMuonPrepareCommand({
383
+ prepareExecutablePath: options.prepareExecutablePath,
384
+ environment: options.environment,
385
+ cwd: options.cwd,
386
+ quiet: options.quiet,
387
+ args
261
388
  });
262
- if (exitCode !== 0) throw new Error(`muon-prepare failed with exit code ${exitCode}.\n${stderr.trim()}`);
263
389
  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}`);
390
+ 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
391
  return {
266
392
  ...result.stagePath === void 0 ? {} : { stagePath: result.stagePath },
267
393
  muonPath: result.muonPath,
@@ -269,11 +395,34 @@ var runMuonPrepare = async (options) => {
269
395
  cacheHit: result.cacheHit
270
396
  };
271
397
  };
398
+ /**
399
+ * Invokes the native muon-builder executable to write Windows PE resources.
400
+ *
401
+ * @param options Resource update invocation options.
402
+ */
403
+ var runMuonPrepareResourceUpdate = async (options) => {
404
+ await runMuonPrepareCommand({
405
+ prepareExecutablePath: options.prepareExecutablePath,
406
+ environment: options.environment,
407
+ cwd: options.cwd,
408
+ quiet: options.quiet,
409
+ args: [
410
+ "resource",
411
+ "--input",
412
+ options.inputPath,
413
+ "--updates-json",
414
+ options.updatesJsonPath,
415
+ "--output",
416
+ options.outputPath,
417
+ ...options.quiet ? ["--quiet"] : []
418
+ ]
419
+ });
420
+ };
272
421
  //#endregion
273
422
  //#region src/gitignore.ts
274
423
  var muonGitignoreEntries = [
275
424
  ".muon/",
276
- "dist-muon-*/",
425
+ "dist-muon/",
277
426
  "artifacts/"
278
427
  ];
279
428
  var muonGitignoreEntryAliases = {
@@ -283,11 +432,13 @@ var muonGitignoreEntryAliases = {
283
432
  ".muon",
284
433
  "/.muon"
285
434
  ],
286
- "dist-muon-*/": [
287
- "dist-muon-*/",
288
- "/dist-muon-*/",
289
- "dist-muon-*",
290
- "/dist-muon-*"
435
+ "dist-muon/": [
436
+ "dist-muon/",
437
+ "/dist-muon/",
438
+ "dist-muon",
439
+ "/dist-muon",
440
+ "dist-muon/*",
441
+ "/dist-muon/*"
291
442
  ],
292
443
  "artifacts/": [
293
444
  "artifacts/",
@@ -1602,8 +1753,10 @@ var encodeTaggedBytes = (tag, bytes) => Buffer.concat([
1602
1753
  encodeVarUint(BigInt(bytes.length)),
1603
1754
  Buffer.from(bytes)
1604
1755
  ]);
1605
- var isJsonObject$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1756
+ var isJsonObject$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1606
1757
  var isPath = (path, first, second) => path.length === 2 && path[0] === first && path[1] === second;
1758
+ var isPluginSignaturePath = (path) => path.length === 3 && path[0] === "plugin" && path[1] === "plugins" && path[2] === "signature";
1759
+ var isPluginSaltPath = (path) => path.length === 3 && path[0] === "plugin" && path[1] === "plugins" && path[2] === "salt";
1607
1760
  var isHexString = (value) => value.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(value);
1608
1761
  var decodeHexString = (value) => {
1609
1762
  const bytes = Buffer.alloc(value.length / 2);
@@ -1613,6 +1766,8 @@ var decodeHexString = (value) => {
1613
1766
  var encodeKnownBinaryString = (path, value) => {
1614
1767
  if (isPath(path, "asset", "signature")) return value.length === 40 && isHexString(value) ? decodeHexString(value) : void 0;
1615
1768
  if (isPath(path, "asset", "salt")) return isHexString(value) ? decodeHexString(value) : void 0;
1769
+ if (isPluginSignaturePath(path)) return value.length === 40 && isHexString(value) ? decodeHexString(value) : void 0;
1770
+ if (isPluginSaltPath(path)) return isHexString(value) ? decodeHexString(value) : void 0;
1616
1771
  if (isPath(path, "browser", "backgroundColor")) {
1617
1772
  const hex = value.startsWith("#") ? value.slice(1) : value;
1618
1773
  return hex.length === 6 && isHexString(hex) ? decodeHexString(hex) : void 0;
@@ -1635,7 +1790,7 @@ var encodeTlvValue = (value, path) => {
1635
1790
  encodeVarUint(BigInt(value.length)),
1636
1791
  ...value.map((entry) => encodeTlvValue(entry, path))
1637
1792
  ]);
1638
- if (isJsonObject$2(value)) {
1793
+ if (isJsonObject$4(value)) {
1639
1794
  const entries = Object.entries(value);
1640
1795
  return Buffer.concat([
1641
1796
  Buffer.from([tlvObjectTag]),
@@ -1660,8 +1815,8 @@ var encodeMuonConfigTlv = (config) => encodeTlvValue(config, []);
1660
1815
  */
1661
1816
  var createMuonEmbeddedConfigSlot = (payload = void 0) => {
1662
1817
  if (payload !== void 0 && payload.length > muonEmbeddedConfigPayloadCapacity) throw new Error(`Encoded muon config exceeds the embedded slot capacity: ${payload.length} > ${muonEmbeddedConfigPayloadCapacity}`);
1663
- const slot = createEmptySlot();
1664
- if (payload === void 0) return slot;
1818
+ if (payload === void 0) return createEmptySlot();
1819
+ const slot = (0, node_crypto.randomBytes)(muonEmbeddedConfigSlotSize);
1665
1820
  Buffer.from(payload).copy(slot, 0);
1666
1821
  return slot;
1667
1822
  };
@@ -1673,9 +1828,12 @@ var createMuonEmbeddedConfigSlot = (payload = void 0) => {
1673
1828
  */
1674
1829
  var createMuonBootstrapEmbeddedConfigSlot = (payload = void 0) => {
1675
1830
  if (payload !== void 0 && payload.length > muonBootstrapEmbeddedConfigPayloadCapacity) throw new Error(`Encoded muon config exceeds the embedded bootstrap slot capacity: ${payload.length} > ${muonBootstrapEmbeddedConfigPayloadCapacity}`);
1676
- const slot = Buffer.alloc(muonBootstrapEmbeddedConfigSlotSize, 0);
1677
- muonBootstrapEmbeddedConfigEmptySlotMarker.copy(slot, 0);
1678
- if (payload === void 0) return slot;
1831
+ if (payload === void 0) {
1832
+ const slot = Buffer.alloc(muonBootstrapEmbeddedConfigSlotSize, 0);
1833
+ muonBootstrapEmbeddedConfigEmptySlotMarker.copy(slot, 0);
1834
+ return slot;
1835
+ }
1836
+ const slot = (0, node_crypto.randomBytes)(muonBootstrapEmbeddedConfigSlotSize);
1679
1837
  Buffer.from(payload).copy(slot, 0);
1680
1838
  return slot;
1681
1839
  };
@@ -1749,7 +1907,7 @@ var findMuonBootstrapEmbeddedConfigSlot = (content) => {
1749
1907
  if (candidate === void 0) throw new Error("Embedded muon-bootstrap config slot was not found.");
1750
1908
  return candidate;
1751
1909
  };
1752
- var fileExists$2 = async (path) => {
1910
+ var fileExists$5 = async (path) => {
1753
1911
  try {
1754
1912
  return (await (0, node_fs_promises.stat)(path)).isFile();
1755
1913
  } catch {
@@ -1765,10 +1923,10 @@ var resolveMuonConfigInputPath = async (configPath) => {
1765
1923
  "muon.json"
1766
1924
  ]) {
1767
1925
  const candidate = (0, node_path.join)(directory, fileName);
1768
- if (await fileExists$2(candidate)) return candidate;
1926
+ if (await fileExists$5(candidate)) return candidate;
1769
1927
  }
1770
1928
  }
1771
- if (!await fileExists$2(configPath)) throw new Error(`muon config does not exist: ${configPath}`);
1929
+ if (!await fileExists$5(configPath)) throw new Error(`muon config does not exist: ${configPath}`);
1772
1930
  return configPath;
1773
1931
  };
1774
1932
  var readMuonConfigInput = async (configPath) => {
@@ -1864,6 +2022,831 @@ var embedMuonConfigInRuntime = async ({ runtimePath, configPath, outputRuntimePa
1864
2022
  });
1865
2023
  };
1866
2024
  //#endregion
2025
+ //#region src/windows-icon.ts
2026
+ var sharp$1 = __resolveDefaultExport$1(sharp, true);
2027
+ globalThis.__screwUpIsInCJS_27e672935538 = true;
2028
+ function __resolveDefaultExport$1(module, isESM) {
2029
+ const __isInCJS = typeof globalThis !== "undefined" && globalThis.__screwUpIsInCJS_27e672935538 === true;
2030
+ const maybe = module;
2031
+ const hasDefault = !!(maybe && typeof maybe === "object" && "default" in maybe);
2032
+ const unwrapNamespaceDefault = (value) => {
2033
+ if (!value || typeof value !== "object") return value;
2034
+ const inner = value;
2035
+ if ((inner.__esModule === true || typeof Symbol !== "undefined" && inner[Symbol.toStringTag] === "Module") && "default" in inner) return inner.default;
2036
+ return value;
2037
+ };
2038
+ const resolvedDefault = hasDefault ? unwrapNamespaceDefault(maybe.default) : void 0;
2039
+ if (__isInCJS) return hasDefault ? resolvedDefault ?? module : module;
2040
+ if (isESM) {
2041
+ if (hasDefault) return resolvedDefault;
2042
+ throw new Error("Default export not found.");
2043
+ }
2044
+ return hasDefault ? resolvedDefault ?? module : module;
2045
+ }
2046
+ var normalizedIconSize = 256;
2047
+ var windowsIconDibSizes = [
2048
+ 16,
2049
+ 24,
2050
+ 32,
2051
+ 48,
2052
+ 64,
2053
+ 128
2054
+ ];
2055
+ var pngSignature = Buffer.from([
2056
+ 137,
2057
+ 80,
2058
+ 78,
2059
+ 71,
2060
+ 13,
2061
+ 10,
2062
+ 26,
2063
+ 10
2064
+ ]);
2065
+ /**
2066
+ * Creates a normalized square PNG icon image.
2067
+ *
2068
+ * @param pngData Source PNG bytes.
2069
+ * @param source Diagnostic source label used in errors.
2070
+ * @param label User-facing diagnostic label.
2071
+ * @returns PNG bytes normalized to the Muon base icon size.
2072
+ * @remarks Non-square images are fitted into transparent padding without
2073
+ * stretching. The normalized PNG is the source for all platform-specific icon
2074
+ * derivatives.
2075
+ */
2076
+ var createNormalizedIconPngData = async (pngData, source, label) => {
2077
+ await assertPngData(pngData, source, label);
2078
+ try {
2079
+ return await sharp$1(pngData).resize(normalizedIconSize, normalizedIconSize, {
2080
+ fit: "contain",
2081
+ background: {
2082
+ r: 0,
2083
+ g: 0,
2084
+ b: 0,
2085
+ alpha: 0
2086
+ }
2087
+ }).ensureAlpha().png().toBuffer();
2088
+ } catch {
2089
+ throw new Error(`${label} must be a valid PNG: ${source}`);
2090
+ }
2091
+ };
2092
+ /**
2093
+ * Creates a normalized square PNG icon image for Windows resources.
2094
+ *
2095
+ * @param pngData Source PNG bytes.
2096
+ * @param source Diagnostic source label used in errors.
2097
+ * @returns PNG bytes normalized to the Muon base icon size.
2098
+ */
2099
+ var createNormalizedMuonIconPngData = async (pngData, source) => await createNormalizedIconPngData(pngData, source, "Windows resource icon");
2100
+ /**
2101
+ * Creates a Windows ICO file from PNG bytes.
2102
+ *
2103
+ * @param pngData Source PNG bytes.
2104
+ * @param source Diagnostic source label used in errors.
2105
+ * @returns ICO file bytes containing 16, 24, 32, 48, 64, 128, and 256 pixel images.
2106
+ * @remarks The 256px entry is PNG-compressed. Smaller entries are 32-bit DIB
2107
+ * images, matching the common Windows ICO layout recommended by Microsoft.
2108
+ */
2109
+ var createWindowsIconBufferFromPngData = async (pngData, source) => {
2110
+ const normalizedPng = await createNormalizedMuonIconPngData(pngData, source);
2111
+ const images = [];
2112
+ for (const size of windowsIconDibSizes) {
2113
+ const rawImage = await resizePngToRawRgba(normalizedPng, size, source);
2114
+ images.push({
2115
+ data: createDibIconImage(rawImage),
2116
+ size,
2117
+ bitCount: 32
2118
+ });
2119
+ }
2120
+ images.push({
2121
+ data: normalizedPng,
2122
+ size: normalizedIconSize,
2123
+ bitCount: 32
2124
+ });
2125
+ return createIco(images);
2126
+ };
2127
+ /**
2128
+ * Creates a Windows ICO file from a PNG file.
2129
+ *
2130
+ * @param sourcePath Source PNG file path.
2131
+ * @param outputPath Output ICO file path.
2132
+ * @returns Promise resolved when the ICO file has been written.
2133
+ */
2134
+ var createWindowsIconFromPngFile = async (sourcePath, outputPath) => {
2135
+ const icon = await createWindowsIconBufferFromPngData(await (0, node_fs_promises.readFile)(sourcePath), sourcePath);
2136
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(outputPath), { recursive: true });
2137
+ await (0, node_fs_promises.writeFile)(outputPath, icon);
2138
+ };
2139
+ var assertPngData = async (data, source, label) => {
2140
+ if (data.length === 0) throw new Error(`${label} PNG must not be empty: ${source}`);
2141
+ if (!data.subarray(0, pngSignature.length).equals(pngSignature)) throw new Error(`${label} must be a valid PNG: ${source}`);
2142
+ try {
2143
+ if ((await sharp$1(data).metadata()).format !== "png") throw new Error();
2144
+ } catch {
2145
+ throw new Error(`${label} must be a valid PNG: ${source}`);
2146
+ }
2147
+ };
2148
+ var resizePngToRawRgba = async (pngData, size, source) => {
2149
+ try {
2150
+ const { data, info } = await sharp$1(pngData).resize(size, size, {
2151
+ fit: "fill",
2152
+ kernel: "lanczos3"
2153
+ }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
2154
+ if (info.width !== size || info.height !== size || info.channels !== 4) throw new Error();
2155
+ return {
2156
+ data,
2157
+ width: info.width,
2158
+ height: info.height
2159
+ };
2160
+ } catch {
2161
+ throw new Error(`Failed to resize Windows resource icon PNG: ${source}`);
2162
+ }
2163
+ };
2164
+ var createDibIconImage = (image) => {
2165
+ const pixelBytes = image.width * image.height * 4;
2166
+ const maskBytes = Math.ceil(image.width / 32) * 4 * image.height;
2167
+ const output = Buffer.alloc(40 + pixelBytes + maskBytes);
2168
+ output.writeUInt32LE(40, 0);
2169
+ output.writeInt32LE(image.width, 4);
2170
+ output.writeInt32LE(image.height * 2, 8);
2171
+ output.writeUInt16LE(1, 12);
2172
+ output.writeUInt16LE(32, 14);
2173
+ output.writeUInt32LE(0, 16);
2174
+ output.writeUInt32LE(pixelBytes + maskBytes, 20);
2175
+ output.writeInt32LE(0, 24);
2176
+ output.writeInt32LE(0, 28);
2177
+ output.writeUInt32LE(0, 32);
2178
+ output.writeUInt32LE(0, 36);
2179
+ for (let y = 0; y < image.height; y += 1) {
2180
+ const sourceY = image.height - 1 - y;
2181
+ for (let x = 0; x < image.width; x += 1) {
2182
+ const sourceOffset = (sourceY * image.width + x) * 4;
2183
+ const outputOffset = 40 + (y * image.width + x) * 4;
2184
+ output[outputOffset] = image.data[sourceOffset + 2];
2185
+ output[outputOffset + 1] = image.data[sourceOffset + 1];
2186
+ output[outputOffset + 2] = image.data[sourceOffset];
2187
+ output[outputOffset + 3] = image.data[sourceOffset + 3];
2188
+ }
2189
+ }
2190
+ return output;
2191
+ };
2192
+ var createIco = (images) => {
2193
+ const directorySize = 6 + images.length * 16;
2194
+ let imageOffset = directorySize;
2195
+ const header = Buffer.alloc(directorySize);
2196
+ header.writeUInt16LE(0, 0);
2197
+ header.writeUInt16LE(1, 2);
2198
+ header.writeUInt16LE(images.length, 4);
2199
+ const imageData = [];
2200
+ images.forEach((image, index) => {
2201
+ const entryOffset = 6 + index * 16;
2202
+ header[entryOffset] = image.size === 256 ? 0 : image.size;
2203
+ header[entryOffset + 1] = image.size === 256 ? 0 : image.size;
2204
+ header[entryOffset + 2] = 0;
2205
+ header[entryOffset + 3] = 0;
2206
+ header.writeUInt16LE(1, entryOffset + 4);
2207
+ header.writeUInt16LE(image.bitCount, entryOffset + 6);
2208
+ header.writeUInt32LE(image.data.length, entryOffset + 8);
2209
+ header.writeUInt32LE(imageOffset, entryOffset + 12);
2210
+ imageOffset += image.data.length;
2211
+ imageData.push(image.data);
2212
+ });
2213
+ return Buffer.concat([header, ...imageData]);
2214
+ };
2215
+ //#endregion
2216
+ //#region src/app-icon.ts
2217
+ /**
2218
+ * ZIP entry used for the generated static application icon asset.
2219
+ */
2220
+ var appIconAssetEntryName = "main/.muon/app-icon.png";
2221
+ /**
2222
+ * Runtime asset URL injected as the generated initial title bar icon.
2223
+ */
2224
+ var appIconAssetUrl = "asset://main/.muon/app-icon.png";
2225
+ /**
2226
+ * Creates a static application icon source from API or CLI options.
2227
+ */
2228
+ var createAppIconOptionsSource = (iconPath, directory, label = "iconPath") => {
2229
+ if (iconPath === void 0) return;
2230
+ if (typeof iconPath !== "string") throw new Error(`${label} must be a string when present.`);
2231
+ if (iconPath.trim() === "") return;
2232
+ return {
2233
+ iconPath: iconPath.trim(),
2234
+ directory
2235
+ };
2236
+ };
2237
+ /**
2238
+ * Reads the top-level `iconPath` from a Muon config object.
2239
+ */
2240
+ var readConfigAppIconSource = (config, directory, label) => {
2241
+ const value = config.iconPath;
2242
+ if (value === void 0) return;
2243
+ if (typeof value !== "string") throw new Error(`${label} iconPath must be a string when present.`);
2244
+ return createAppIconOptionsSource(value, directory);
2245
+ };
2246
+ /**
2247
+ * Reads the top-level `iconPath` from project metadata.
2248
+ */
2249
+ var readProjectAppIconSource = (projectJson, directory) => {
2250
+ const value = projectJson.iconPath;
2251
+ return typeof value === "string" ? createAppIconOptionsSource(value, directory) : void 0;
2252
+ };
2253
+ /**
2254
+ * Resolves and validates the first available static application icon source.
2255
+ */
2256
+ var resolveMuonAppIconPath = async (sources) => {
2257
+ for (const source of sources) {
2258
+ const iconPath = resolveResourcePath$2(source.directory, source.iconPath);
2259
+ await assertMuonAppIconPath(iconPath);
2260
+ return iconPath;
2261
+ }
2262
+ };
2263
+ var assertMuonAppIconPath = async (iconPath) => {
2264
+ if ((0, node_path.extname)(iconPath).toLowerCase() !== ".png") throw new Error(`Muon app icon must be a .png file: ${iconPath}`);
2265
+ if (!await fileExists$4(iconPath)) throw new Error(`Muon app icon does not exist: ${iconPath}`);
2266
+ await createNormalizedIconPngData(await (0, node_fs_promises.readFile)(iconPath), iconPath, "Muon app icon");
2267
+ };
2268
+ var resolveResourcePath$2 = (directory, value) => (0, node_path.isAbsolute)(value) ? value : (0, node_path.resolve)(directory, value);
2269
+ var fileExists$4 = async (path) => {
2270
+ try {
2271
+ await (0, node_fs_promises.access)(path, node_fs.constants.F_OK);
2272
+ return true;
2273
+ } catch {
2274
+ return false;
2275
+ }
2276
+ };
2277
+ //#endregion
2278
+ //#region src/windows-resource.ts
2279
+ var defaultConfigFileNames$1 = [
2280
+ "muon.json5",
2281
+ "muon.jsonc",
2282
+ "muon.json"
2283
+ ];
2284
+ var defaultLanguage = 1033;
2285
+ var defaultCodePage = 1200;
2286
+ var moduleDirectory$3 = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
2287
+ /**
2288
+ * Reads the Muon config file using the same default file names as muon build.
2289
+ */
2290
+ var readMuonConfigForWindowsResource = async (root, configPath) => {
2291
+ const resolvedConfigPath = await resolveConfigPath$1(root, configPath);
2292
+ if (resolvedConfigPath === void 0) return {
2293
+ config: {},
2294
+ directory: root
2295
+ };
2296
+ return {
2297
+ config: await readJsonObjectFile$2(resolvedConfigPath, "Muon config file"),
2298
+ directory: (0, node_path.dirname)(resolvedConfigPath)
2299
+ };
2300
+ };
2301
+ /**
2302
+ * Merges Windows resource options while preserving field-level fallback.
2303
+ */
2304
+ var mergeMuonWindowsResourceOptions = (primary, fallback) => {
2305
+ if (primary === void 0) return fallback;
2306
+ if (fallback === void 0) return primary;
2307
+ const merged = { ...fallback };
2308
+ if (primary.iconPath !== void 0) merged.iconPath = primary.iconPath;
2309
+ if (primary.productName !== void 0) merged.productName = primary.productName;
2310
+ if (primary.fileDescription !== void 0) merged.fileDescription = primary.fileDescription;
2311
+ if (primary.companyName !== void 0) merged.companyName = primary.companyName;
2312
+ if (primary.version !== void 0) merged.version = primary.version;
2313
+ if (primary.copyright !== void 0) merged.copyright = primary.copyright;
2314
+ if (primary.language !== void 0) merged.language = primary.language;
2315
+ if (primary.codePage !== void 0) merged.codePage = primary.codePage;
2316
+ return merged;
2317
+ };
2318
+ /**
2319
+ * Resolves Windows resource metadata from options, config files, and package metadata.
2320
+ */
2321
+ var resolveMuonWindowsResource = async (input) => {
2322
+ const projectJson = await readProjectJson$1(input.root);
2323
+ const windowsSources = [
2324
+ createOptionsSource$1(input.options, input.root, "windowsResource"),
2325
+ readConfigWindowsResourceSource(input.muonConfig, input.muonConfigDirectory, "muon.json"),
2326
+ readConfigWindowsResourceSource(projectJson, input.root, "project.json")
2327
+ ].filter((source) => source !== void 0);
2328
+ const sources = [
2329
+ ...windowsSources,
2330
+ createProjectSource(projectJson, input.root),
2331
+ createPackageSource(input.packageJson, input.root)
2332
+ ].filter((source) => source !== void 0);
2333
+ const appIconSources = [
2334
+ createAppIconOptionsSource(input.appIconPath, input.root),
2335
+ readConfigAppIconSource(input.muonConfig, input.muonConfigDirectory, "muon.json"),
2336
+ readProjectAppIconSource(projectJson, input.root)
2337
+ ].filter((source) => source !== void 0);
2338
+ const productName = resolveStringField$1(sources, "productName", input.defaults.productName);
2339
+ const fileDescription = resolveStringField$1(sources, "fileDescription", input.defaults.fileDescription);
2340
+ const companyName = resolveStringField$1(sources, "companyName", input.defaults.companyName);
2341
+ const version = resolveStringField$1(sources, "version", input.defaults.version);
2342
+ const copyright = resolveOptionalStringField(sources, "copyright", input.defaults.copyright);
2343
+ const language = resolveNumericField(sources, "language", defaultLanguage);
2344
+ const codePage = resolveNumericField(sources, "codePage", defaultCodePage);
2345
+ const fixedVersion = normalizeWindowsVersion(version);
2346
+ return {
2347
+ iconPath: await resolveWindowsIconPath(windowsSources, appIconSources, await resolveDefaultWindowsIcon(input.packageDirectory)),
2348
+ productName,
2349
+ fileDescription,
2350
+ companyName,
2351
+ version,
2352
+ fixedVersion,
2353
+ copyright,
2354
+ language,
2355
+ codePage
2356
+ };
2357
+ };
2358
+ /**
2359
+ * Removes build-only Windows resource settings from runtime config.
2360
+ */
2361
+ var stripBuildOnlyWindowsResourceConfig = (sourceConfig) => {
2362
+ const sourceWindows = sourceConfig.windows;
2363
+ if (!isJsonObject$3(sourceWindows)) return sourceConfig;
2364
+ const windowsConfig = {};
2365
+ for (const [key, value] of Object.entries(sourceWindows)) if (key !== "resource") windowsConfig[key] = value;
2366
+ const output = {};
2367
+ for (const [key, value] of Object.entries(sourceConfig)) if (key !== "windows") output[key] = value;
2368
+ if (Object.keys(windowsConfig).length > 0) output.windows = windowsConfig;
2369
+ return output;
2370
+ };
2371
+ /**
2372
+ * Returns true when the file has a Windows PE header.
2373
+ */
2374
+ var isWindowsPeExecutable = async (path) => {
2375
+ const content = await (0, node_fs_promises.readFile)(path);
2376
+ if (content.length < 64 || content.toString("ascii", 0, 2) !== "MZ") return false;
2377
+ const peOffset = content.readUInt32LE(60);
2378
+ return peOffset + 4 <= content.length && content.toString("ascii", peOffset, peOffset + 4) === "PE\0\0";
2379
+ };
2380
+ /**
2381
+ * Applies resolved resource metadata to a Windows PE executable in place.
2382
+ */
2383
+ var updateWindowsPeResources = async (input) => {
2384
+ return await updateWindowsPeResourceFile(input, createEngraverUpdate);
2385
+ };
2386
+ /**
2387
+ * Applies only resolved icon metadata to a Windows PE executable in place.
2388
+ */
2389
+ var updateWindowsPeIconResource = async (input) => {
2390
+ return await updateWindowsPeResourceFile(input, createEngraverIconUpdate);
2391
+ };
2392
+ var updateWindowsPeResourceFile = async (input, createUpdate) => {
2393
+ if (!await isWindowsPeExecutable(input.executablePath)) return false;
2394
+ const mode = (await (0, node_fs_promises.stat)(input.executablePath)).mode & 511;
2395
+ const tempDirectory = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "muon-windows-resource-"));
2396
+ const updatesJsonPath = (0, node_path.join)(tempDirectory, "updates.json");
2397
+ const outputPath = (0, node_path.join)(tempDirectory, "output.exe");
2398
+ const iconPath = (0, node_path.join)(tempDirectory, "icon.ico");
2399
+ try {
2400
+ await createWindowsIconFromPngFile(input.resource.iconPath, iconPath);
2401
+ await (0, node_fs_promises.writeFile)(updatesJsonPath, `${JSON.stringify(createUpdate(input.resource, iconPath), null, 2)}\n`);
2402
+ await runMuonPrepareResourceUpdate({
2403
+ inputPath: input.executablePath,
2404
+ updatesJsonPath,
2405
+ outputPath,
2406
+ quiet: true,
2407
+ prepareExecutablePath: void 0,
2408
+ environment: input.environment,
2409
+ cwd: input.cwd
2410
+ });
2411
+ await (0, node_fs_promises.copyFile)(outputPath, input.executablePath);
2412
+ await (0, node_fs_promises.chmod)(input.executablePath, mode === 0 ? 493 : mode);
2413
+ return true;
2414
+ } finally {
2415
+ await (0, node_fs_promises.rm)(tempDirectory, {
2416
+ recursive: true,
2417
+ force: true
2418
+ });
2419
+ }
2420
+ };
2421
+ var createEngraverUpdate = (resource, iconPath) => {
2422
+ const strings = {
2423
+ CompanyName: resource.companyName,
2424
+ FileDescription: resource.fileDescription,
2425
+ FileVersion: resource.version,
2426
+ ProductName: resource.productName,
2427
+ ProductVersion: resource.version
2428
+ };
2429
+ if (resource.copyright !== void 0) strings.LegalCopyright = resource.copyright;
2430
+ return {
2431
+ version: {
2432
+ language: resource.language,
2433
+ codePage: resource.codePage,
2434
+ fixed: {
2435
+ fileVersion: resource.fixedVersion,
2436
+ productVersion: resource.fixedVersion,
2437
+ fileOS: "windows32",
2438
+ fileType: "app"
2439
+ },
2440
+ strings
2441
+ },
2442
+ icons: createEngraverIcons(resource, iconPath)
2443
+ };
2444
+ };
2445
+ var createEngraverIconUpdate = (resource, iconPath) => ({ icons: createEngraverIcons(resource, iconPath) });
2446
+ var createEngraverIcons = (resource, iconPath) => [{
2447
+ id: 1,
2448
+ language: resource.language,
2449
+ path: iconPath
2450
+ }];
2451
+ var resolveConfigPath$1 = async (root, configPath) => {
2452
+ if (configPath !== void 0) {
2453
+ const resolvedPath = (0, node_path.resolve)(root, configPath);
2454
+ if (await fileExists$3(resolvedPath)) return resolvedPath;
2455
+ throw new Error(`Muon config file does not exist: ${resolvedPath}`);
2456
+ }
2457
+ for (const fileName of defaultConfigFileNames$1) {
2458
+ const candidatePath = (0, node_path.join)(root, fileName);
2459
+ if (await fileExists$3(candidatePath)) return candidatePath;
2460
+ }
2461
+ };
2462
+ var readProjectJson$1 = async (root) => {
2463
+ const projectJsonPath = (0, node_path.join)(root, "project.json");
2464
+ if (!await fileExists$3(projectJsonPath)) return {};
2465
+ return await readJsonObjectFile$2(projectJsonPath, "project.json");
2466
+ };
2467
+ var readJsonObjectFile$2 = async (filePath, label) => {
2468
+ const parsed = (0, import_dist.parse)(await (0, node_fs_promises.readFile)(filePath, "utf8"));
2469
+ if (!isJsonObject$3(parsed)) throw new Error(`${label} must contain a JSON object: ${filePath}`);
2470
+ return parsed;
2471
+ };
2472
+ var createOptionsSource$1 = (options, directory, label) => {
2473
+ if (options === void 0) return;
2474
+ return {
2475
+ options: validateWindowsResourceOptions(options, label),
2476
+ directory
2477
+ };
2478
+ };
2479
+ var readConfigWindowsResourceSource = (config, directory, label) => {
2480
+ const windows = config.windows;
2481
+ if (windows === void 0) return;
2482
+ if (!isJsonObject$3(windows)) throw new Error(`${label} windows must be an object when present.`);
2483
+ const resource = windows.resource;
2484
+ if (resource === void 0) return;
2485
+ return createOptionsSource$1(validateWindowsResourceOptions(resource, `${label} windows.resource`), directory, `${label} windows.resource`);
2486
+ };
2487
+ var createProjectSource = (projectJson, directory) => {
2488
+ const topLevel = {};
2489
+ copyStringField(projectJson, "productName", topLevel, "productName");
2490
+ copyStringField(projectJson, "name", topLevel, "productName");
2491
+ copyStringField(projectJson, "fileDescription", topLevel, "fileDescription");
2492
+ copyStringField(projectJson, "description", topLevel, "fileDescription");
2493
+ copyStringField(projectJson, "companyName", topLevel, "companyName");
2494
+ copyStringField(projectJson, "version", topLevel, "version");
2495
+ copyStringField(projectJson, "copyright", topLevel, "copyright");
2496
+ const author = stringifyAuthor(projectJson.author);
2497
+ if (author !== void 0 && topLevel.companyName === void 0) topLevel.companyName = author;
2498
+ const options = mergeMuonWindowsResourceOptions(readConfigWindowsResourceSource(projectJson, directory, "project.json")?.options, topLevel);
2499
+ if (options === void 0 || Object.keys(options).length === 0) return;
2500
+ return {
2501
+ options,
2502
+ directory
2503
+ };
2504
+ };
2505
+ var createPackageSource = (packageJson, directory) => {
2506
+ const options = {};
2507
+ copyStringField(packageJson, "name", options, "productName");
2508
+ copyStringField(packageJson, "description", options, "fileDescription");
2509
+ copyStringField(packageJson, "version", options, "version");
2510
+ copyStringField(packageJson, "copyright", options, "copyright");
2511
+ const author = stringifyAuthor(packageJson.author);
2512
+ if (author !== void 0) options.companyName = author;
2513
+ return Object.keys(options).length === 0 ? void 0 : {
2514
+ options,
2515
+ directory
2516
+ };
2517
+ };
2518
+ var copyStringField = (source, sourceKey, target, targetKey) => {
2519
+ const value = source[sourceKey];
2520
+ if (typeof value === "string" && value.trim() !== "") target[targetKey] = value.trim();
2521
+ };
2522
+ var validateWindowsResourceOptions = (value, label) => {
2523
+ if (!isJsonObject$3(value)) throw new Error(`${label} must be an object.`);
2524
+ const output = {};
2525
+ copyOptionalStringResourceField(value, "iconPath", output);
2526
+ copyOptionalStringResourceField(value, "productName", output);
2527
+ copyOptionalStringResourceField(value, "fileDescription", output);
2528
+ copyOptionalStringResourceField(value, "companyName", output);
2529
+ copyOptionalStringResourceField(value, "version", output);
2530
+ copyOptionalStringResourceField(value, "copyright", output);
2531
+ copyOptionalNumberResourceField(value, "language", output, label);
2532
+ copyOptionalNumberResourceField(value, "codePage", output, label);
2533
+ return output;
2534
+ };
2535
+ var copyOptionalStringResourceField = (source, key, target) => {
2536
+ const value = source[key];
2537
+ if (value === void 0) return;
2538
+ if (typeof value !== "string") throw new Error(`${key} must be a string when present.`);
2539
+ if (value.trim() !== "") target[key] = value.trim();
2540
+ };
2541
+ var copyOptionalNumberResourceField = (source, key, target, label) => {
2542
+ const value = source[key];
2543
+ if (value === void 0) return;
2544
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 65535) throw new Error(`${label} ${key} must be an integer from 0 to 65535.`);
2545
+ target[key] = value;
2546
+ };
2547
+ var resolveStringField$1 = (sources, field, fallback) => {
2548
+ return resolveOptionalStringField(sources, field, fallback) ?? fallback;
2549
+ };
2550
+ var resolveOptionalStringField = (sources, field, fallback) => {
2551
+ for (const source of sources) {
2552
+ const value = source.options[field];
2553
+ if (value !== void 0 && value.trim() !== "") return value.trim();
2554
+ }
2555
+ return fallback;
2556
+ };
2557
+ var resolveNumericField = (sources, field, fallback) => {
2558
+ for (const source of sources) {
2559
+ const value = source.options[field];
2560
+ if (value !== void 0) return value;
2561
+ }
2562
+ return fallback;
2563
+ };
2564
+ var resolveWindowsIconPath = async (windowsSources, appIconSources, defaultIconPath) => {
2565
+ for (const source of windowsSources) {
2566
+ const value = source.options.iconPath;
2567
+ if (value !== void 0 && value.trim() !== "") {
2568
+ const iconPath = resolveResourcePath$1(source.directory, value);
2569
+ await assertIconPath$1(iconPath);
2570
+ return iconPath;
2571
+ }
2572
+ }
2573
+ const appIconPath = await resolveMuonAppIconPath(appIconSources);
2574
+ if (appIconPath !== void 0) return appIconPath;
2575
+ await assertIconPath$1(defaultIconPath);
2576
+ return defaultIconPath;
2577
+ };
2578
+ var assertIconPath$1 = async (iconPath) => {
2579
+ if ((0, node_path.extname)(iconPath).toLowerCase() !== ".png") throw new Error(`Windows resource icon must be a .png file: ${iconPath}`);
2580
+ if (!await fileExists$3(iconPath)) throw new Error(`Windows resource icon does not exist: ${iconPath}`);
2581
+ await createNormalizedMuonIconPngData(await (0, node_fs_promises.readFile)(iconPath), iconPath);
2582
+ };
2583
+ var resolveDefaultWindowsIcon = async (packageDirectory) => {
2584
+ const sourceIconPath = (0, node_path.join)(moduleDirectory$3, "..", "..", "images", "muon-256.png");
2585
+ const candidates = [
2586
+ (0, node_path.join)((0, node_path.resolve)(packageDirectory), "native", "muon-256.png"),
2587
+ (0, node_path.join)(moduleDirectory$3, "native", "muon-256.png"),
2588
+ (0, node_path.join)(moduleDirectory$3, "..", "dist", "native", "muon-256.png"),
2589
+ sourceIconPath
2590
+ ];
2591
+ for (const candidate of candidates) if (await fileExists$3(candidate)) return candidate;
2592
+ return sourceIconPath;
2593
+ };
2594
+ var resolveResourcePath$1 = (directory, path) => {
2595
+ return (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(directory, path);
2596
+ };
2597
+ var normalizeWindowsVersion = (version) => {
2598
+ const normalized = version.trim();
2599
+ const match = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?(?:$|[-+])/.exec(normalized);
2600
+ if (match === null) throw new Error(`Windows resource version must start with numeric parts: ${version}`);
2601
+ const values = [
2602
+ match[1],
2603
+ match[2],
2604
+ match[3],
2605
+ match[4]
2606
+ ].map((value) => value === void 0 ? 0 : Number.parseInt(value, 10));
2607
+ 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}`);
2608
+ return values.join(".");
2609
+ };
2610
+ var stringifyAuthor = (value) => {
2611
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
2612
+ if (!isJsonObject$3(value) || typeof value.name !== "string") return;
2613
+ const name = value.name.trim();
2614
+ if (name === "") return;
2615
+ return value.email === void 0 || typeof value.email !== "string" ? name : `${name} <${value.email}>`;
2616
+ };
2617
+ var fileExists$3 = async (path) => {
2618
+ try {
2619
+ await (0, node_fs_promises.access)(path, node_fs.constants.F_OK);
2620
+ return true;
2621
+ } catch {
2622
+ return false;
2623
+ }
2624
+ };
2625
+ var isJsonObject$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2626
+ //#endregion
2627
+ //#region src/linux-desktop.ts
2628
+ var defaultIconFileName = "muon-desktop-icon.png";
2629
+ var defaultDesktopConfigFileName = "muon-desktop.json";
2630
+ var moduleDirectory$2 = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
2631
+ /**
2632
+ * Merges Linux desktop options while preserving field-level fallback.
2633
+ */
2634
+ var mergeMuonLinuxDesktopOptions = (primary, fallback) => {
2635
+ if (primary === void 0) return fallback;
2636
+ if (fallback === void 0) return primary;
2637
+ const merged = { ...fallback };
2638
+ if (primary.desktopId !== void 0) merged.desktopId = primary.desktopId;
2639
+ if (primary.name !== void 0) merged.name = primary.name;
2640
+ if (primary.comment !== void 0) merged.comment = primary.comment;
2641
+ if (primary.iconPath !== void 0) merged.iconPath = primary.iconPath;
2642
+ if (primary.categories !== void 0) merged.categories = primary.categories;
2643
+ if (primary.startupNotify !== void 0) merged.startupNotify = primary.startupNotify;
2644
+ return merged;
2645
+ };
2646
+ /**
2647
+ * Resolves Linux desktop integration metadata from options, config files, and
2648
+ * package metadata.
2649
+ */
2650
+ var resolveMuonLinuxDesktop = async (input) => {
2651
+ const projectJson = await readProjectJson(input.root);
2652
+ const sources = [createOptionsSource(input.options, input.root, "linuxDesktop"), readConfigLinuxDesktopSource(input.muonConfig, input.muonConfigDirectory, "muon.json")].filter((source) => source !== void 0);
2653
+ const appIconSources = [
2654
+ createAppIconOptionsSource(input.appIconPath, input.root),
2655
+ readConfigAppIconSource(input.muonConfig, input.muonConfigDirectory, "muon.json"),
2656
+ readProjectAppIconSource(projectJson, input.root)
2657
+ ].filter((source) => source !== void 0);
2658
+ return {
2659
+ desktopId: sanitizeDesktopId(resolveStringField(sources, "desktopId", input.defaults.desktopId)),
2660
+ name: resolveStringField(sources, "name", input.defaults.name),
2661
+ comment: resolveStringField(sources, "comment", input.defaults.comment),
2662
+ categories: resolveCategories(sources, input.defaults.categories),
2663
+ startupNotify: resolveBooleanField(sources, "startupNotify", input.defaults.startupNotify),
2664
+ iconPath: await resolveLinuxIconPath(sources, appIconSources, await resolveDefaultLinuxDesktopIcon(input.packageDirectory)),
2665
+ iconFileName: defaultIconFileName,
2666
+ configFileName: defaultDesktopConfigFileName
2667
+ };
2668
+ };
2669
+ /**
2670
+ * Removes build-only Linux desktop settings from runtime config.
2671
+ */
2672
+ var stripBuildOnlyLinuxDesktopConfig = (sourceConfig) => {
2673
+ const sourceLinux = sourceConfig.linux;
2674
+ if (!isJsonObject$2(sourceLinux)) return sourceConfig;
2675
+ const linuxConfig = {};
2676
+ for (const [key, value] of Object.entries(sourceLinux)) if (key !== "desktop") linuxConfig[key] = value;
2677
+ const output = {};
2678
+ for (const [key, value] of Object.entries(sourceConfig)) if (key !== "linux") output[key] = value;
2679
+ if (Object.keys(linuxConfig).length > 0) output.linux = linuxConfig;
2680
+ return output;
2681
+ };
2682
+ /**
2683
+ * Writes normalized Linux desktop sidecar files into a target distribution.
2684
+ */
2685
+ var writeLinuxDesktopDistributionFiles = async (outputPath, desktop) => {
2686
+ const iconData = await createNormalizedIconPngData(await (0, node_fs_promises.readFile)(desktop.iconPath), desktop.iconPath, "Linux desktop icon");
2687
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputPath, desktop.iconFileName), iconData);
2688
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputPath, desktop.configFileName), `${JSON.stringify({
2689
+ desktopId: desktop.desktopId,
2690
+ name: desktop.name,
2691
+ comment: desktop.comment,
2692
+ categories: desktop.categories,
2693
+ startupNotify: desktop.startupNotify,
2694
+ iconFileName: desktop.iconFileName
2695
+ }, void 0, 2)}\n`);
2696
+ };
2697
+ /**
2698
+ * Creates a Desktop Entry file.
2699
+ */
2700
+ var createLinuxDesktopEntry = (input) => {
2701
+ const lines = [
2702
+ "[Desktop Entry]",
2703
+ "Type=Application",
2704
+ `Name=${escapeDesktopString(input.desktop.name)}`
2705
+ ];
2706
+ if (input.desktop.comment.length > 0) lines.push(`Comment=${escapeDesktopString(input.desktop.comment)}`);
2707
+ lines.push(`Exec=${input.exec}`);
2708
+ lines.push(`TryExec=${input.tryExec}`);
2709
+ lines.push(`Icon=${escapeDesktopString(input.icon)}`);
2710
+ lines.push(`Terminal=false`);
2711
+ if (input.desktop.categories.length > 0) lines.push(`Categories=${input.desktop.categories.join(";")};`);
2712
+ lines.push(`StartupNotify=${input.desktop.startupNotify ? "true" : "false"}`);
2713
+ lines.push(`StartupWMClass=${escapeDesktopString(input.desktop.desktopId)}`);
2714
+ lines.push("X-Muon-Managed=true");
2715
+ lines.push("");
2716
+ return lines.join("\n");
2717
+ };
2718
+ /**
2719
+ * Quotes one Desktop Entry Exec argument.
2720
+ */
2721
+ var quoteDesktopExecArgument = (value) => {
2722
+ let escaped = "\"";
2723
+ for (const character of value) {
2724
+ if (character === "\"" || character === "\\" || character === "$" || character === "`") escaped += "\\";
2725
+ escaped += character;
2726
+ }
2727
+ return `${escaped}"`;
2728
+ };
2729
+ var readJsonObjectFile$1 = async (filePath, label) => {
2730
+ const parsed = (0, import_dist.parse)(await (0, node_fs_promises.readFile)(filePath, "utf8"));
2731
+ if (!isJsonObject$2(parsed)) throw new Error(`${label} must contain a JSON object: ${filePath}`);
2732
+ return parsed;
2733
+ };
2734
+ var readProjectJson = async (root) => {
2735
+ const projectJsonPath = (0, node_path.join)(root, "project.json");
2736
+ if (!await fileExists$2(projectJsonPath)) return {};
2737
+ return await readJsonObjectFile$1(projectJsonPath, "project.json");
2738
+ };
2739
+ var createOptionsSource = (options, directory, label) => {
2740
+ if (options === void 0) return;
2741
+ return {
2742
+ options: validateLinuxDesktopOptions(options, label),
2743
+ directory
2744
+ };
2745
+ };
2746
+ var readConfigLinuxDesktopSource = (config, directory, label) => {
2747
+ const linux = config.linux;
2748
+ if (linux === void 0) return;
2749
+ if (!isJsonObject$2(linux)) throw new Error(`${label} linux must be an object when present.`);
2750
+ const desktop = linux.desktop;
2751
+ if (desktop === void 0) return;
2752
+ return createOptionsSource(validateLinuxDesktopOptions(desktop, `${label} linux.desktop`), directory, `${label} linux.desktop`);
2753
+ };
2754
+ var validateLinuxDesktopOptions = (value, label) => {
2755
+ if (!isJsonObject$2(value)) throw new Error(`${label} must be an object.`);
2756
+ const output = {};
2757
+ copyOptionalStringDesktopField(value, "desktopId", output);
2758
+ copyOptionalStringDesktopField(value, "name", output);
2759
+ copyOptionalStringDesktopField(value, "comment", output);
2760
+ copyOptionalStringDesktopField(value, "iconPath", output);
2761
+ copyOptionalCategoriesField(value, output, label);
2762
+ copyOptionalBooleanDesktopField(value, "startupNotify", output, label);
2763
+ return output;
2764
+ };
2765
+ var copyOptionalStringDesktopField = (source, key, target) => {
2766
+ const value = source[key];
2767
+ if (value === void 0) return;
2768
+ if (typeof value !== "string") throw new Error(`${key} must be a string when present.`);
2769
+ if (value.trim() !== "") target[key] = value.trim();
2770
+ };
2771
+ var copyOptionalCategoriesField = (source, target, label) => {
2772
+ const value = source.categories;
2773
+ if (value === void 0) return;
2774
+ 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.`);
2775
+ target.categories = value.map((entry) => entry.trim());
2776
+ };
2777
+ var copyOptionalBooleanDesktopField = (source, key, target, label) => {
2778
+ const value = source[key];
2779
+ if (value === void 0) return;
2780
+ if (typeof value !== "boolean") throw new Error(`${label} ${key} must be a boolean when present.`);
2781
+ target[key] = value;
2782
+ };
2783
+ var resolveStringField = (sources, field, fallback) => {
2784
+ for (const source of sources) {
2785
+ const value = source.options[field];
2786
+ if (value !== void 0 && value.trim() !== "") return value.trim();
2787
+ }
2788
+ return fallback;
2789
+ };
2790
+ var resolveCategories = (sources, fallback) => {
2791
+ for (const source of sources) if (source.options.categories !== void 0) return source.options.categories;
2792
+ return fallback;
2793
+ };
2794
+ var resolveBooleanField = (sources, field, fallback) => {
2795
+ for (const source of sources) {
2796
+ const value = source.options[field];
2797
+ if (value !== void 0) return value;
2798
+ }
2799
+ return fallback;
2800
+ };
2801
+ var resolveLinuxIconPath = async (sources, appIconSources, defaultIconPath) => {
2802
+ for (const source of sources) {
2803
+ const value = source.options.iconPath;
2804
+ if (value !== void 0 && value.trim() !== "") {
2805
+ const iconPath = resolveResourcePath(source.directory, value);
2806
+ await assertIconPath(iconPath, true);
2807
+ return iconPath;
2808
+ }
2809
+ }
2810
+ const appIconPath = await resolveMuonAppIconPath(appIconSources);
2811
+ if (appIconPath !== void 0) return appIconPath;
2812
+ if (defaultIconPath !== void 0 && await fileExists$2(defaultIconPath)) {
2813
+ await assertIconPath(defaultIconPath, false);
2814
+ return defaultIconPath;
2815
+ }
2816
+ throw new Error("Linux desktop icon does not exist.");
2817
+ };
2818
+ var assertIconPath = async (iconPath, required) => {
2819
+ if ((0, node_path.extname)(iconPath).toLowerCase() !== ".png") throw new Error(`Linux desktop icon must be a .png file: ${iconPath}`);
2820
+ if (required && !await fileExists$2(iconPath)) throw new Error(`Linux desktop icon does not exist: ${iconPath}`);
2821
+ if (await fileExists$2(iconPath)) await createNormalizedIconPngData(await (0, node_fs_promises.readFile)(iconPath), iconPath, "Linux desktop icon");
2822
+ };
2823
+ var resolveDefaultLinuxDesktopIcon = async (packageDirectory) => {
2824
+ const candidates = [
2825
+ (0, node_path.join)((0, node_path.resolve)(packageDirectory), "native", "muon-256.png"),
2826
+ (0, node_path.join)(moduleDirectory$2, "native", "muon-256.png"),
2827
+ (0, node_path.join)(moduleDirectory$2, "..", "dist", "native", "muon-256.png"),
2828
+ (0, node_path.join)(moduleDirectory$2, "..", "..", "images", "muon-256.png")
2829
+ ];
2830
+ for (const candidate of candidates) if (await fileExists$2(candidate)) return candidate;
2831
+ };
2832
+ var sanitizeDesktopId = (value) => {
2833
+ const sanitized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[.-]+/g, "").replace(/[.-]+$/g, "");
2834
+ return sanitized.length > 0 ? sanitized : "muon-app";
2835
+ };
2836
+ var escapeDesktopString = (value) => value.replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t");
2837
+ var resolveResourcePath = (directory, path) => {
2838
+ return (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(directory, path);
2839
+ };
2840
+ var fileExists$2 = async (path) => {
2841
+ try {
2842
+ await (0, node_fs_promises.access)(path, node_fs.constants.F_OK);
2843
+ return true;
2844
+ } catch {
2845
+ return false;
2846
+ }
2847
+ };
2848
+ var isJsonObject$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2849
+ //#endregion
1867
2850
  //#region src/build.ts
1868
2851
  var AdmZip = __resolveDefaultExport(adm_zip.default, false);
1869
2852
  globalThis.__screwUpIsInCJS_f9d0012cc677 = true;
@@ -1922,18 +2905,58 @@ var buildMuonApp = async (options = {}) => {
1922
2905
  const appName = resolveAppName(packageJson, options.appName);
1923
2906
  const appId = resolveAppId(packageJson, options.appId);
1924
2907
  const buildConfig = await readBuildConfig(root, options.configPath);
1925
- const assetInput = resolveAssetInput(root, options.assetSourcePath, options.assetPrefix, buildConfig);
2908
+ const sourceConfig = applyRuntimePluginConfig(buildConfig.config, options.runtimePluginConfig ?? { mode: "simple" });
2909
+ assertNoUserInitialTitleBarIcon(sourceConfig);
2910
+ const resolvedBuildConfig = {
2911
+ ...buildConfig,
2912
+ config: sourceConfig
2913
+ };
2914
+ const assetInput = resolveAssetInput(root, options.assetSourcePath, options.assetPrefix, resolvedBuildConfig);
2915
+ const windowsResource = await resolveMuonWindowsResource({
2916
+ root,
2917
+ packageDirectory,
2918
+ packageJson,
2919
+ muonConfig: sourceConfig,
2920
+ muonConfigDirectory: buildConfig.directory,
2921
+ options: options.windowsResource,
2922
+ appIconPath: options.iconPath,
2923
+ defaults: {
2924
+ productName: appName,
2925
+ fileDescription: appName,
2926
+ companyName: "Unknown",
2927
+ version: "0.0.0",
2928
+ copyright: void 0
2929
+ }
2930
+ });
2931
+ const linuxDesktop = await resolveMuonLinuxDesktop({
2932
+ root,
2933
+ packageDirectory,
2934
+ muonConfig: sourceConfig,
2935
+ muonConfigDirectory: buildConfig.directory,
2936
+ options: options.linuxDesktop,
2937
+ appIconPath: options.iconPath,
2938
+ defaults: {
2939
+ desktopId: appId,
2940
+ name: resolveLinuxDesktopDefaultName(packageJson, appName),
2941
+ comment: resolvePackageDescription(packageJson),
2942
+ categories: ["Utility"],
2943
+ startupNotify: true
2944
+ }
2945
+ });
1926
2946
  const salt = Buffer.from(options.assetSalt ?? (0, node_crypto.randomBytes)(assetSaltByteLength));
1927
2947
  const results = [];
1928
2948
  for (const target of targets) {
1929
2949
  const result = await buildMuonTarget({
1930
2950
  packageDirectory,
2951
+ root,
1931
2952
  outputRoot,
1932
2953
  appName,
1933
2954
  appId,
1934
2955
  target,
1935
2956
  assetInput,
1936
- sourceConfig: buildConfig.config,
2957
+ sourceConfig,
2958
+ windowsResource,
2959
+ linuxDesktop,
1937
2960
  salt
1938
2961
  });
1939
2962
  results.push(result);
@@ -1991,6 +3014,8 @@ var sanitizeAppId = (value) => {
1991
3014
  const sanitized = (value.startsWith("@") ? value.slice(1) : value).trim().toLowerCase().replace("/", ".").replace(/[^a-z0-9._-]+/g, ".").replace(/^[.]+/g, "").replace(/[.]+$/g, "");
1992
3015
  return sanitized.length > 0 ? sanitized : defaultAppId;
1993
3016
  };
3017
+ var resolveLinuxDesktopDefaultName = (packageJson, appName) => typeof packageJson.name === "string" && packageJson.name.trim() !== "" ? packageJson.name.trim() : appName;
3018
+ var resolvePackageDescription = (packageJson) => typeof packageJson.description === "string" ? packageJson.description.trim() : "";
1994
3019
  var readBuildConfig = async (root, configPath) => {
1995
3020
  const resolvedConfigPath = await resolveConfigPath(root, configPath);
1996
3021
  if (resolvedConfigPath === void 0) return {
@@ -2002,6 +3027,19 @@ var readBuildConfig = async (root, configPath) => {
2002
3027
  directory: (0, node_path.dirname)(resolvedConfigPath)
2003
3028
  };
2004
3029
  };
3030
+ var applyRuntimePluginConfig = (sourceConfig, runtimePluginConfig) => {
3031
+ if (runtimePluginConfig === void 0) return sourceConfig;
3032
+ const sourcePlugin = sourceConfig.plugin;
3033
+ if (sourcePlugin !== void 0 && !isJsonObject$1(sourcePlugin)) throw new Error("muon.json plugin must be an object when runtime plugin config is applied.");
3034
+ const plugin = sourcePlugin ?? {};
3035
+ return {
3036
+ ...sourceConfig,
3037
+ plugin: {
3038
+ ...plugin,
3039
+ ...runtimePluginConfig
3040
+ }
3041
+ };
3042
+ };
2005
3043
  var readConfigAssetSourcePath = (sourceConfig) => {
2006
3044
  const sourceAsset = sourceConfig.asset;
2007
3045
  if (sourceAsset === void 0) return;
@@ -2045,6 +3083,8 @@ var buildMuonTarget = async (input) => {
2045
3083
  const outputPath = (0, node_path.join)(input.outputRoot, descriptor.distributionDirectoryName);
2046
3084
  const launcherPath = (0, node_path.join)(outputPath, getLauncherFileName(input.appName, descriptor));
2047
3085
  const assetZipPath = (0, node_path.join)(outputPath, "assets.zip");
3086
+ const runtimeAppId = getMuonTargetRuntimeAppId(input.appId, input.target);
3087
+ const appIconPath = descriptor.os === "windows" ? input.windowsResource.iconPath : input.linuxDesktop.iconPath;
2048
3088
  await verifyTargetInputs({
2049
3089
  sourceRuntimePath,
2050
3090
  sourceBootstrapPath,
@@ -2063,8 +3103,11 @@ var buildMuonTarget = async (input) => {
2063
3103
  await (0, node_fs_promises.chmod)((0, node_path.join)(outputPath, descriptor.runtimeExecutableName), executableMode);
2064
3104
  await (0, node_fs_promises.copyFile)(sourceBootstrapPath, launcherPath);
2065
3105
  await (0, node_fs_promises.chmod)(launcherPath, executableMode);
2066
- const asset = await writeAssetArchive(input.assetInput, assetZipPath, input.salt);
2067
- const embeddedConfig = createEmbeddedConfig(input.sourceConfig, asset, input.appId);
3106
+ const asset = await writeAssetArchive(input.assetInput, assetZipPath, input.salt, [{
3107
+ name: appIconAssetEntryName,
3108
+ data: await (0, node_fs_promises.readFile)(appIconPath)
3109
+ }]);
3110
+ const embeddedConfig = createEmbeddedConfig(input.sourceConfig, asset, runtimeAppId, input.linuxDesktop.desktopId, appIconAssetUrl);
2068
3111
  await withTemporaryConfig(embeddedConfig, async (configPath) => {
2069
3112
  await embedMuonConfigInRuntime({
2070
3113
  runtimePath: outputPath,
@@ -2077,13 +3120,29 @@ var buildMuonTarget = async (input) => {
2077
3120
  outputPath: void 0
2078
3121
  });
2079
3122
  });
3123
+ if (descriptor.os === "windows") {
3124
+ await updateWindowsPeIconResource({
3125
+ executablePath: (0, node_path.join)(outputPath, descriptor.runtimeExecutableName),
3126
+ resource: input.windowsResource,
3127
+ environment: process.env,
3128
+ cwd: input.root
3129
+ });
3130
+ await updateWindowsPeResources({
3131
+ executablePath: launcherPath,
3132
+ resource: input.windowsResource,
3133
+ environment: process.env,
3134
+ cwd: input.root
3135
+ });
3136
+ } else if (descriptor.os === "linux") await writeLinuxDesktopDistributionFiles(outputPath, input.linuxDesktop);
2080
3137
  return {
2081
3138
  target: input.target,
2082
3139
  distributionDirectoryName: descriptor.distributionDirectoryName,
2083
3140
  outputPath,
2084
3141
  launcherPath,
2085
3142
  asset,
2086
- embeddedConfig
3143
+ runtimeAppId,
3144
+ embeddedConfig,
3145
+ ...descriptor.os === "linux" ? { linuxDesktop: input.linuxDesktop } : {}
2087
3146
  };
2088
3147
  };
2089
3148
  var verifyTargetInputs = async (input) => {
@@ -2104,10 +3163,10 @@ var copyRuntimeFiles = async (sourceRuntimePath, outputPath, descriptor) => {
2104
3163
  }
2105
3164
  await (0, node_fs_promises.copyFile)((0, node_path.join)(sourceRuntimePath, muonLicenseFileName), (0, node_path.join)(outputPath, muonLicenseFileName));
2106
3165
  };
2107
- var writeAssetArchive = async (input, outputPath, salt) => {
3166
+ var writeAssetArchive = async (input, outputPath, salt, extraEntries) => {
2108
3167
  const sourceStats = await statOrUndefined(input.sourcePath);
2109
3168
  if (sourceStats === void 0) throw new Error(`Muon asset source does not exist: ${input.sourcePath}`);
2110
- const archive = sourceStats.isDirectory() ? await createAssetArchiveFromDirectory(input) : sourceStats.isFile() ? await (0, node_fs_promises.readFile)(input.sourcePath) : void 0;
3169
+ const archive = sourceStats.isDirectory() ? await createAssetArchiveFromDirectory(input, extraEntries) : sourceStats.isFile() ? await createAssetArchiveFromZipFile(input.sourcePath, extraEntries) : void 0;
2111
3170
  if (archive === void 0) throw new Error(`Muon asset source is not a directory or file: ${input.sourcePath}`);
2112
3171
  await (0, node_fs_promises.writeFile)(outputPath, archive);
2113
3172
  return {
@@ -2117,10 +3176,30 @@ var writeAssetArchive = async (input, outputPath, salt) => {
2117
3176
  entryCount: sourceStats.isDirectory() ? readZipEntryCount(archive, outputPath) : readZipEntryCount(archive, input.sourcePath)
2118
3177
  };
2119
3178
  };
2120
- var createAssetArchiveFromDirectory = async (input) => {
3179
+ var createAssetArchiveFromDirectory = async (input, extraEntries) => {
2121
3180
  const entries = await collectZipEntries(input.sourcePath, input.prefix);
2122
3181
  if (entries.length === 0) throw new Error(`Muon asset source has no files: ${input.sourcePath}`);
2123
- return createZipArchive(entries);
3182
+ return createZipArchive(appendZipEntries(entries, extraEntries));
3183
+ };
3184
+ var createAssetArchiveFromZipFile = async (sourcePath, extraEntries) => {
3185
+ const zip = new AdmZip(await (0, node_fs_promises.readFile)(sourcePath));
3186
+ for (const entry of extraEntries) {
3187
+ assertSafeZipEntryName(entry.name);
3188
+ if (zip.getEntry(entry.name) !== null) throw new Error(`Muon app icon asset entry already exists: ${entry.name}`);
3189
+ zip.addFile(entry.name, entry.data);
3190
+ }
3191
+ return zip.toBuffer();
3192
+ };
3193
+ var appendZipEntries = (entries, extraEntries) => {
3194
+ const output = [...entries];
3195
+ const names = new Set(entries.map((entry) => entry.name));
3196
+ for (const entry of extraEntries) {
3197
+ assertSafeZipEntryName(entry.name);
3198
+ if (names.has(entry.name)) throw new Error(`Muon app icon asset entry already exists: ${entry.name}`);
3199
+ names.add(entry.name);
3200
+ output.push(entry);
3201
+ }
3202
+ return output;
2124
3203
  };
2125
3204
  var readZipEntryCount = (archive, sourcePath) => {
2126
3205
  const endSignature = 101010256;
@@ -2158,13 +3237,20 @@ var createZipArchive = (entries) => {
2158
3237
  for (const entry of entries) zip.addFile(entry.name, entry.data);
2159
3238
  return zip.toBuffer();
2160
3239
  };
2161
- var createEmbeddedConfig = (sourceConfig, asset, appId) => {
3240
+ var createEmbeddedConfig = (sourceConfig, asset, appId, desktopId, initialTitleBarIcon) => {
2162
3241
  const sourceAsset = sourceConfig.asset;
2163
3242
  if (sourceAsset !== void 0 && !isJsonObject$1(sourceAsset)) throw new Error("muon.json asset must be an object when present.");
2164
3243
  const sourceBootstrap = sourceConfig.bootstrap;
2165
3244
  if (sourceBootstrap !== void 0 && !isJsonObject$1(sourceBootstrap)) throw new Error("muon.json bootstrap must be an object when present.");
3245
+ const runtimeConfig = stripBuildOnlyAppIconConfig(stripBuildOnlyLinuxDesktopConfig(stripBuildOnlyWindowsResourceConfig(sourceConfig)));
3246
+ const sourceBrowser = runtimeConfig.browser;
3247
+ if (sourceBrowser !== void 0 && !isJsonObject$1(sourceBrowser)) throw new Error("muon.json browser must be an object when present.");
2166
3248
  return {
2167
- ...sourceConfig,
3249
+ ...runtimeConfig,
3250
+ browser: {
3251
+ ...sourceBrowser ?? {},
3252
+ initialTitleBarIcon
3253
+ },
2168
3254
  asset: {
2169
3255
  ...sourceAsset ?? {},
2170
3256
  sourcePath: appConfigSourcePath,
@@ -2173,10 +3259,22 @@ var createEmbeddedConfig = (sourceConfig, asset, appId) => {
2173
3259
  },
2174
3260
  bootstrap: {
2175
3261
  ...sourceBootstrap ?? {},
2176
- appId
3262
+ appId,
3263
+ desktopId
2177
3264
  }
2178
3265
  };
2179
3266
  };
3267
+ var assertNoUserInitialTitleBarIcon = (sourceConfig) => {
3268
+ const sourceBrowser = sourceConfig.browser;
3269
+ if (sourceBrowser === void 0) return;
3270
+ if (!isJsonObject$1(sourceBrowser)) throw new Error("muon.json browser must be an object when present.");
3271
+ if (sourceBrowser.initialTitleBarIcon !== void 0) throw new Error("muon.json browser.initialTitleBarIcon is generated by muon build; use top-level iconPath instead.");
3272
+ };
3273
+ var stripBuildOnlyAppIconConfig = (sourceConfig) => {
3274
+ const output = {};
3275
+ for (const [key, value] of Object.entries(sourceConfig)) if (key !== "iconPath") output[key] = value;
3276
+ return output;
3277
+ };
2180
3278
  var withTemporaryConfig = async (config, callback) => {
2181
3279
  const tempDirectory = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "muon-build-config-"));
2182
3280
  const configPath = (0, node_path.join)(tempDirectory, "muon.json");
@@ -2218,6 +3316,203 @@ var isJsonObject$1 = (value) => {
2218
3316
  };
2219
3317
  var getErrorMessage$1 = (error) => error instanceof Error ? error.message : String(error);
2220
3318
  //#endregion
3319
+ //#region src/vite-options.ts
3320
+ /**
3321
+ * Metadata symbol used to recover `muon()` plugin options from `vite.config.*`.
3322
+ */
3323
+ var muonVitePluginOptionsSymbol = Symbol.for("muon.vite.plugin.options");
3324
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3325
+ var isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
3326
+ var isWindowsResourceOptions = (value) => {
3327
+ if (!isRecord(value)) return false;
3328
+ 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));
3329
+ };
3330
+ var isLinuxDesktopOptions = (value) => {
3331
+ if (!isRecord(value)) return false;
3332
+ 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");
3333
+ };
3334
+ var isMuonViteBuildOptions = (value) => {
3335
+ if (!isRecord(value)) return false;
3336
+ 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.iconPath === void 0 || typeof value.iconPath === "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);
3337
+ };
3338
+ var isMuonVitePluginAccessImportOptions = (value) => {
3339
+ if (!isRecord(value)) return false;
3340
+ return (value.sources === void 0 || isStringArray(value.sources)) && (value.packages === void 0 || isStringArray(value.packages)) && (value.allow === void 0 || isStringArray(value.allow));
3341
+ };
3342
+ var isMuonVitePluginAccessEntryOptions = (value) => {
3343
+ if (!isRecord(value)) return false;
3344
+ return typeof value.name === "string" && (value.signature === void 0 || typeof value.signature === "string") && (value.salt === void 0 || typeof value.salt === "string") && (value.allow === void 0 || isStringArray(value.allow)) && (value.imports === void 0 || Array.isArray(value.imports) && value.imports.every(isMuonVitePluginAccessImportOptions));
3345
+ };
3346
+ var isMuonVitePluginAccessOptions = (value) => {
3347
+ if (!isRecord(value)) return false;
3348
+ return (value.path === void 0 || typeof value.path === "string") && (value.mode === void 0 || value.mode === "simple" || value.mode === "validate") && (value.pages === void 0 || isStringArray(value.pages)) && (value.plugins === void 0 || Array.isArray(value.plugins) && value.plugins.every(isMuonVitePluginAccessEntryOptions));
3349
+ };
3350
+ var isMuonVitePluginOptions = (value) => {
3351
+ if (!isRecord(value)) return false;
3352
+ 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.pluginAccess === void 0 || value.pluginAccess === false || isMuonVitePluginAccessOptions(value.pluginAccess)) && (value.build === void 0 || typeof value.build === "boolean" || isMuonViteBuildOptions(value.build));
3353
+ };
3354
+ /**
3355
+ * Attaches raw Muon Vite plugin options to a plugin instance.
3356
+ *
3357
+ * @param plugin Plugin object.
3358
+ * @param options Raw plugin options.
3359
+ * @returns Plugin object with internal Muon metadata.
3360
+ */
3361
+ var attachMuonVitePluginOptions = (plugin, options) => {
3362
+ Object.defineProperty(plugin, muonVitePluginOptionsSymbol, {
3363
+ configurable: false,
3364
+ enumerable: false,
3365
+ value: { ...options },
3366
+ writable: false
3367
+ });
3368
+ return plugin;
3369
+ };
3370
+ /**
3371
+ * Reads raw Muon Vite plugin options from a plugin instance.
3372
+ *
3373
+ * @param plugin Candidate plugin object.
3374
+ * @returns Attached Muon options, if present.
3375
+ */
3376
+ var getMuonVitePluginOptions = (plugin) => {
3377
+ if (!isRecord(plugin)) return;
3378
+ const options = plugin[muonVitePluginOptionsSymbol];
3379
+ return isMuonVitePluginOptions(options) ? options : void 0;
3380
+ };
3381
+ /**
3382
+ * Resolves nested Vite plugin option values into a flat list.
3383
+ *
3384
+ * @param pluginOptions Raw `plugins` field from a Vite config.
3385
+ * @returns Flat plugin object list.
3386
+ */
3387
+ var flattenVitePluginOptions = async (pluginOptions) => {
3388
+ const resolvedValue = await pluginOptions;
3389
+ if (resolvedValue === null || resolvedValue === void 0 || !resolvedValue) return [];
3390
+ if (Array.isArray(resolvedValue)) return (await Promise.all(resolvedValue.map((entry) => flattenVitePluginOptions(entry)))).flat();
3391
+ return [resolvedValue];
3392
+ };
3393
+ //#endregion
3394
+ //#region src/build-sequence.ts
3395
+ /**
3396
+ * Environment variable used to prevent the Vite plugin build hook from running
3397
+ * when a CLI command owns the Muon build sequence.
3398
+ */
3399
+ var muonBuildSequenceSuppressViteBuildEnvironmentKey = "MUON_SUPPRESS_VITE_MUON_BUILD";
3400
+ var isMissingVitePackageError = (error) => {
3401
+ const candidate = error;
3402
+ return candidate.code === "ERR_MODULE_NOT_FOUND" && typeof candidate.message === "string" && candidate.message.includes("vite");
3403
+ };
3404
+ var resolveViteOutputDirectory = (config) => {
3405
+ return (0, node_path.isAbsolute)(config.build.outDir) ? config.build.outDir : (0, node_path.resolve)(config.root, config.build.outDir);
3406
+ };
3407
+ var findMuonVitePluginOptions = async (plugins) => {
3408
+ const muonPlugins = (await flattenVitePluginOptions(plugins)).map((plugin) => getMuonVitePluginOptions(plugin)).filter((pluginOptions) => pluginOptions !== void 0);
3409
+ if (muonPlugins.length > 1) throw new Error("Multiple muon() plugin definitions were found in vite.config.*.");
3410
+ return muonPlugins[0];
3411
+ };
3412
+ /**
3413
+ * Loads Vite configuration only far enough to discover whether a Muon plugin
3414
+ * controls the build sequence.
3415
+ */
3416
+ var loadMuonBuildSequenceProject = async (cwd) => {
3417
+ const resolvedCwd = (0, node_path.resolve)(cwd);
3418
+ let vite;
3419
+ try {
3420
+ vite = await import("vite");
3421
+ } catch (error) {
3422
+ if (isMissingVitePackageError(error)) return {
3423
+ root: resolvedCwd,
3424
+ viteBuildRoot: resolvedCwd,
3425
+ viteOutputDirectory: void 0,
3426
+ pluginOptions: void 0
3427
+ };
3428
+ throw error;
3429
+ }
3430
+ const resolvedConfig = await vite.resolveConfig({
3431
+ root: resolvedCwd,
3432
+ logLevel: "silent"
3433
+ }, "build", "production", "production");
3434
+ const pluginOptions = await findMuonVitePluginOptions(resolvedConfig.plugins);
3435
+ if (pluginOptions === void 0) return {
3436
+ root: resolvedCwd,
3437
+ viteBuildRoot: resolvedCwd,
3438
+ viteOutputDirectory: void 0,
3439
+ pluginOptions: void 0
3440
+ };
3441
+ return {
3442
+ root: resolvedConfig.root,
3443
+ viteBuildRoot: resolvedCwd,
3444
+ viteOutputDirectory: resolveViteOutputDirectory(resolvedConfig),
3445
+ pluginOptions
3446
+ };
3447
+ };
3448
+ /**
3449
+ * Returns Muon build options from a Muon Vite plugin declaration.
3450
+ */
3451
+ var resolveMuonViteBuildOptions = (pluginOptions) => {
3452
+ if (pluginOptions?.build === false) throw new Error("Muon build is disabled by muon({ build: false }).");
3453
+ return typeof pluginOptions?.build === "object" ? pluginOptions.build : {};
3454
+ };
3455
+ var runViteBuild = async (root) => {
3456
+ const vite = await import("vite");
3457
+ const previous = process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey];
3458
+ process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey] = "1";
3459
+ try {
3460
+ await vite.build({
3461
+ root,
3462
+ logLevel: "silent"
3463
+ });
3464
+ } finally {
3465
+ if (previous === void 0) delete process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey];
3466
+ else process.env[muonBuildSequenceSuppressViteBuildEnvironmentKey] = previous;
3467
+ }
3468
+ };
3469
+ var hasExplicitTargets = (options) => options.targets !== void 0 && options.targets.length > 0;
3470
+ var copyDefinedBuildOptions = (output, input, usesViteAssets) => {
3471
+ if (input.assetSourcePath !== void 0) {
3472
+ if (usesViteAssets) throw new Error("--assets cannot be used when a muon() Vite plugin provides the build sequence.");
3473
+ output.assetSourcePath = input.assetSourcePath;
3474
+ }
3475
+ if (input.assetPrefix !== void 0) output.assetPrefix = input.assetPrefix;
3476
+ if (input.targets !== void 0 && input.targets.length > 0) {
3477
+ output.targets = input.targets;
3478
+ if (input.allTargets === void 0) output.allTargets = false;
3479
+ }
3480
+ if (input.allTargets !== void 0) output.allTargets = input.allTargets;
3481
+ if (input.appName !== void 0) output.appName = input.appName;
3482
+ if (input.appId !== void 0) output.appId = input.appId;
3483
+ if (input.outputRoot !== void 0) output.outputRoot = input.outputRoot;
3484
+ if (input.configPath !== void 0) output.configPath = input.configPath;
3485
+ if (input.iconPath !== void 0) output.iconPath = input.iconPath;
3486
+ if (input.windowsResource !== void 0) {
3487
+ const windowsResource = mergeMuonWindowsResourceOptions(input.windowsResource, output.windowsResource);
3488
+ if (windowsResource !== void 0) output.windowsResource = windowsResource;
3489
+ }
3490
+ if (input.linuxDesktop !== void 0) {
3491
+ const linuxDesktop = mergeMuonLinuxDesktopOptions(input.linuxDesktop, output.linuxDesktop);
3492
+ if (linuxDesktop !== void 0) output.linuxDesktop = linuxDesktop;
3493
+ }
3494
+ if (input.packageDirectory !== void 0) output.packageDirectory = input.packageDirectory;
3495
+ if (input.assetSalt !== void 0) output.assetSalt = input.assetSalt;
3496
+ };
3497
+ /**
3498
+ * Runs the project build sequence used by `muon build` and `muon pack`.
3499
+ */
3500
+ var runMuonBuildSequence = async (options = {}, loadedProject) => {
3501
+ const project = loadedProject ?? await loadMuonBuildSequenceProject(options.root ?? process.cwd());
3502
+ const pluginBuildOptions = resolveMuonViteBuildOptions(project.pluginOptions);
3503
+ const usesViteAssets = project.pluginOptions !== void 0;
3504
+ const buildOptions = { root: project.root };
3505
+ if (usesViteAssets) {
3506
+ if (project.viteOutputDirectory === void 0) throw new Error("Vite output directory could not be resolved.");
3507
+ Object.assign(buildOptions, pluginBuildOptions);
3508
+ buildOptions.assetSourcePath = project.viteOutputDirectory;
3509
+ buildOptions.assetPrefix = "main";
3510
+ await runViteBuild(project.viteBuildRoot);
3511
+ } else if (options.defaultAllTargets !== void 0 && options.allTargets === void 0 && !hasExplicitTargets(options)) buildOptions.allTargets = options.defaultAllTargets;
3512
+ copyDefinedBuildOptions(buildOptions, options, usesViteAssets);
3513
+ return await buildMuonApp(buildOptions);
3514
+ };
3515
+ //#endregion
2221
3516
  //#region src/vite-internals.ts
2222
3517
  var resolveFromRoot = (root, path) => (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(root, path);
2223
3518
  var moduleDirectory = typeof __dirname === "string" ? __dirname : (0, node_path.dirname)((0, node_url.fileURLToPath)({}.url));
@@ -2323,7 +3618,7 @@ var getWebSocketOrigin = (startUrl) => {
2323
3618
  else if (url.protocol === "http:") url.protocol = "ws:";
2324
3619
  return url.origin;
2325
3620
  };
2326
- var createMuonOverrideConfig = (startUrl, enableDebugger) => {
3621
+ var createMuonOverrideConfig = (startUrl, enableDebugger, runtimePluginConfig) => {
2327
3622
  const origin = new URL(startUrl).origin;
2328
3623
  return {
2329
3624
  ...enableDebugger ? { cdp: { enable: true } } : {},
@@ -2332,19 +3627,22 @@ var createMuonOverrideConfig = (startUrl, enableDebugger) => {
2332
3627
  ...enableDebugger ? { keybind: {
2333
3628
  devtools: "f12",
2334
3629
  recycle: "ctrl+f12"
2335
- } } : {},
2336
- plugin: { allow: [`${origin}/**`] }
3630
+ } } : {}
3631
+ },
3632
+ plugin: {
3633
+ ...runtimePluginConfig,
3634
+ pages: [`${origin}/**`]
2337
3635
  },
2338
3636
  network: { allow: [`${origin}/**`, `${getWebSocketOrigin(startUrl)}/**`] }
2339
3637
  };
2340
3638
  };
2341
- var writeMuonOverrideConfig = (server, overrideConfigPath, enableDebugger) => {
3639
+ var writeMuonOverrideConfig = (server, overrideConfigPath, pluginOptions, getRuntimePluginConfig) => {
2342
3640
  const startUrl = getBaseUrl(server);
2343
3641
  if (startUrl === void 0) {
2344
3642
  server.config.logger.warn("Muon Vite plugin could not resolve a Vite URL.");
2345
3643
  return false;
2346
3644
  }
2347
- (0, node_fs.writeFileSync)(overrideConfigPath, `${JSON.stringify(createMuonOverrideConfig(startUrl, enableDebugger), null, 2)}\n`);
3645
+ (0, node_fs.writeFileSync)(overrideConfigPath, `${JSON.stringify(createMuonOverrideConfig(startUrl, pluginOptions.enableDebugger !== false, getRuntimePluginConfig()), null, 2)}\n`);
2348
3646
  return true;
2349
3647
  };
2350
3648
  var createRuntimePaths = async (server, stagePath, platform, projectConfigPath) => {
@@ -2407,7 +3705,7 @@ var launchMuon = (paths, platform, server) => {
2407
3705
  });
2408
3706
  child.unref();
2409
3707
  };
2410
- var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, architecture, environment }) => {
3708
+ var startMuonViteBrowserBridge = async ({ server, pluginOptions, getRuntimePluginConfig, platform, architecture, environment }) => {
2411
3709
  await ensureMuonGitignoreEntry(server.config.root);
2412
3710
  if (pluginOptions.open === false || server.httpServer === null) return;
2413
3711
  const target = getDefaultMuonPrepareTarget(platform, architecture);
@@ -2427,7 +3725,7 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
2427
3725
  environment,
2428
3726
  cwd: server.config.root
2429
3727
  });
2430
- if (preparedRuntime.stagePath === void 0) throw new Error("muon-prepare did not return a staged runtime path.");
3728
+ if (preparedRuntime.stagePath === void 0) throw new Error("muon-builder did not return a staged runtime path.");
2431
3729
  const paths = await createRuntimePaths(server, preparedRuntime.stagePath, platform, await resolveProjectConfigPath(server));
2432
3730
  await writeLaunchScript(paths, platform);
2433
3731
  let cleanupPromise = void 0;
@@ -2453,63 +3751,8 @@ var startMuonViteBrowserBridge = async ({ server, pluginOptions, platform, archi
2453
3751
  cleanup();
2454
3752
  });
2455
3753
  server.httpServer.once("listening", () => {
2456
- if (writeMuonOverrideConfig(server, paths.overrideConfigPath, pluginOptions.enableDebugger !== false)) launchMuon(paths, platform, server);
2457
- });
2458
- };
2459
- //#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
3754
+ if (writeMuonOverrideConfig(server, paths.overrideConfigPath, pluginOptions, getRuntimePluginConfig)) launchMuon(paths, platform, server);
2488
3755
  });
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
3756
  };
2514
3757
  //#endregion
2515
3758
  Object.defineProperty(exports, "__toESM", {
@@ -2518,6 +3761,12 @@ Object.defineProperty(exports, "__toESM", {
2518
3761
  return __toESM;
2519
3762
  }
2520
3763
  });
3764
+ Object.defineProperty(exports, "allMuonTargets", {
3765
+ enumerable: true,
3766
+ get: function() {
3767
+ return allMuonTargets;
3768
+ }
3769
+ });
2521
3770
  Object.defineProperty(exports, "attachMuonVitePluginOptions", {
2522
3771
  enumerable: true,
2523
3772
  get: function() {
@@ -2530,6 +3779,18 @@ Object.defineProperty(exports, "buildMuonApp", {
2530
3779
  return buildMuonApp;
2531
3780
  }
2532
3781
  });
3782
+ Object.defineProperty(exports, "createLinuxDesktopEntry", {
3783
+ enumerable: true,
3784
+ get: function() {
3785
+ return createLinuxDesktopEntry;
3786
+ }
3787
+ });
3788
+ Object.defineProperty(exports, "createWindowsIconFromPngFile", {
3789
+ enumerable: true,
3790
+ get: function() {
3791
+ return createWindowsIconFromPngFile;
3792
+ }
3793
+ });
2533
3794
  Object.defineProperty(exports, "embedMuonConfigInBootstrapFile", {
2534
3795
  enumerable: true,
2535
3796
  get: function() {
@@ -2560,6 +3821,12 @@ Object.defineProperty(exports, "flattenVitePluginOptions", {
2560
3821
  return flattenVitePluginOptions;
2561
3822
  }
2562
3823
  });
3824
+ Object.defineProperty(exports, "getDefaultMuonBuildTarget", {
3825
+ enumerable: true,
3826
+ get: function() {
3827
+ return getDefaultMuonBuildTarget;
3828
+ }
3829
+ });
2563
3830
  Object.defineProperty(exports, "getDefaultMuonPrepareTarget", {
2564
3831
  enumerable: true,
2565
3832
  get: function() {
@@ -2584,6 +3851,48 @@ Object.defineProperty(exports, "getMuonVitePluginOptions", {
2584
3851
  return getMuonVitePluginOptions;
2585
3852
  }
2586
3853
  });
3854
+ Object.defineProperty(exports, "loadMuonBuildSequenceProject", {
3855
+ enumerable: true,
3856
+ get: function() {
3857
+ return loadMuonBuildSequenceProject;
3858
+ }
3859
+ });
3860
+ Object.defineProperty(exports, "mergeMuonLinuxDesktopOptions", {
3861
+ enumerable: true,
3862
+ get: function() {
3863
+ return mergeMuonLinuxDesktopOptions;
3864
+ }
3865
+ });
3866
+ Object.defineProperty(exports, "mergeMuonWindowsResourceOptions", {
3867
+ enumerable: true,
3868
+ get: function() {
3869
+ return mergeMuonWindowsResourceOptions;
3870
+ }
3871
+ });
3872
+ Object.defineProperty(exports, "muonBuildSequenceSuppressViteBuildEnvironmentKey", {
3873
+ enumerable: true,
3874
+ get: function() {
3875
+ return muonBuildSequenceSuppressViteBuildEnvironmentKey;
3876
+ }
3877
+ });
3878
+ Object.defineProperty(exports, "normalizeMuonTarget", {
3879
+ enumerable: true,
3880
+ get: function() {
3881
+ return normalizeMuonTarget;
3882
+ }
3883
+ });
3884
+ Object.defineProperty(exports, "quoteDesktopExecArgument", {
3885
+ enumerable: true,
3886
+ get: function() {
3887
+ return quoteDesktopExecArgument;
3888
+ }
3889
+ });
3890
+ Object.defineProperty(exports, "readMuonConfigForWindowsResource", {
3891
+ enumerable: true,
3892
+ get: function() {
3893
+ return readMuonConfigForWindowsResource;
3894
+ }
3895
+ });
2587
3896
  Object.defineProperty(exports, "require_dist", {
2588
3897
  enumerable: true,
2589
3898
  get: function() {
@@ -2596,6 +3905,24 @@ Object.defineProperty(exports, "resolveMuonRuntimePath", {
2596
3905
  return resolveMuonRuntimePath;
2597
3906
  }
2598
3907
  });
3908
+ Object.defineProperty(exports, "resolveMuonViteBuildOptions", {
3909
+ enumerable: true,
3910
+ get: function() {
3911
+ return resolveMuonViteBuildOptions;
3912
+ }
3913
+ });
3914
+ Object.defineProperty(exports, "resolveMuonWindowsResource", {
3915
+ enumerable: true,
3916
+ get: function() {
3917
+ return resolveMuonWindowsResource;
3918
+ }
3919
+ });
3920
+ Object.defineProperty(exports, "runMuonBuildSequence", {
3921
+ enumerable: true,
3922
+ get: function() {
3923
+ return runMuonBuildSequence;
3924
+ }
3925
+ });
2599
3926
  Object.defineProperty(exports, "runMuonPrepare", {
2600
3927
  enumerable: true,
2601
3928
  get: function() {
@@ -2608,5 +3935,17 @@ Object.defineProperty(exports, "startMuonViteBrowserBridge", {
2608
3935
  return startMuonViteBrowserBridge;
2609
3936
  }
2610
3937
  });
3938
+ Object.defineProperty(exports, "updateWindowsPeIconResource", {
3939
+ enumerable: true,
3940
+ get: function() {
3941
+ return updateWindowsPeIconResource;
3942
+ }
3943
+ });
3944
+ Object.defineProperty(exports, "updateWindowsPeResources", {
3945
+ enumerable: true,
3946
+ get: function() {
3947
+ return updateWindowsPeResources;
3948
+ }
3949
+ });
2611
3950
 
2612
- //# sourceMappingURL=vite-options-FFh0NWUa.cjs.map
3951
+ //# sourceMappingURL=vite-internals-HXgH7b5M.cjs.map