muon-ui 0.25.0 → 0.26.47

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 (100) hide show
  1. package/README.md +10 -0
  2. package/android.d.ts +61 -0
  3. package/dist/android/lib/index.cjs +233 -0
  4. package/dist/android/lib/index.cjs.map +1 -0
  5. package/dist/android/lib/index.mjs +231 -0
  6. package/dist/android/lib/index.mjs.map +1 -0
  7. package/dist/android/licenses/cardio.txt +21 -0
  8. package/dist/android/licenses/libcxx.txt +15179 -0
  9. package/dist/android/licenses/libffi.txt +21 -0
  10. package/dist/android/licenses/muon.txt +21 -0
  11. package/dist/android/licenses/sha2.txt +83 -0
  12. package/dist/android/licenses/tra-ffic.txt +21 -0
  13. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.aar +0 -0
  14. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.aar.md5 +1 -0
  15. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.aar.sha1 +1 -0
  16. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.aar.sha256 +1 -0
  17. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.aar.sha512 +1 -0
  18. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.module +90 -0
  19. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.module.md5 +1 -0
  20. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.module.sha1 +1 -0
  21. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.module.sha256 +1 -0
  22. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.module.sha512 +1 -0
  23. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.pom +34 -0
  24. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.pom.md5 +1 -0
  25. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.pom.sha1 +1 -0
  26. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.pom.sha256 +1 -0
  27. package/dist/android/maven/dev/muon/runtime/0.1.0/runtime-0.1.0.pom.sha512 +1 -0
  28. package/dist/android/maven/dev/muon/runtime/maven-metadata.xml +13 -0
  29. package/dist/android/maven/dev/muon/runtime/maven-metadata.xml.md5 +1 -0
  30. package/dist/android/maven/dev/muon/runtime/maven-metadata.xml.sha1 +1 -0
  31. package/dist/android/maven/dev/muon/runtime/maven-metadata.xml.sha256 +1 -0
  32. package/dist/android/maven/dev/muon/runtime/maven-metadata.xml.sha512 +1 -0
  33. package/dist/android/renderer/renderer.js +1391 -0
  34. package/dist/android/renderer/renderer.js.map +1 -0
  35. package/dist/android/templates/AndroidManifest.xml.fc +17 -0
  36. package/dist/android/templates/app/build.gradle +22 -0
  37. package/dist/android/templates/app/src/main/res/mipmap/ic_launcher.xml +5 -0
  38. package/dist/android/templates/app/src/main/res/values/styles.xml +7 -0
  39. package/dist/android/templates/build.gradle +1 -0
  40. package/dist/android/templates/gradle/wrapper/gradle-wrapper.jar +0 -0
  41. package/dist/android/templates/gradle/wrapper/gradle-wrapper.properties +8 -0
  42. package/dist/android/templates/gradle.properties +3 -0
  43. package/dist/android/templates/gradlew +248 -0
  44. package/dist/android/templates/gradlew.bat +93 -0
  45. package/dist/android/templates/settings.gradle +14 -0
  46. package/dist/android/templates/strings.xml.fc +2 -0
  47. package/dist/android/toolchain.json +14 -0
  48. package/dist/cli.cjs +157 -55
  49. package/dist/cli.cjs.map +1 -1
  50. package/dist/index.cjs +2 -2
  51. package/dist/index.mjs +2 -2
  52. package/dist/native/linux-amd64/muon-builder +0 -0
  53. package/dist/native/linux-amd64/muon-launcher +0 -0
  54. package/dist/native/linux-amd64/muon-runtime-helper +0 -0
  55. package/dist/native/linux-arm64/muon-builder +0 -0
  56. package/dist/native/linux-arm64/muon-launcher +0 -0
  57. package/dist/native/linux-arm64/muon-runtime-helper +0 -0
  58. package/dist/native/linux-armhf/muon-builder +0 -0
  59. package/dist/native/linux-armhf/muon-launcher +0 -0
  60. package/dist/native/linux-armhf/muon-runtime-helper +0 -0
  61. package/dist/native/windows-amd64/muon-builder.exe +0 -0
  62. package/dist/native/windows-amd64/muon-launcher.exe +0 -0
  63. package/dist/native/windows-amd64/muon-plugin-inspector.exe +0 -0
  64. package/dist/native/windows-i686/muon-builder.exe +0 -0
  65. package/dist/native/windows-i686/muon-launcher.exe +0 -0
  66. package/dist/native/windows-i686/muon-plugin-inspector.exe +0 -0
  67. package/dist/runtime/linux-amd64/libmuon-ui.so +0 -0
  68. package/dist/runtime/linux-amd64/muon-core +0 -0
  69. package/dist/runtime/linux-amd64/plugins/node-bridge.mjs +5 -5
  70. package/dist/runtime/linux-amd64/plugins/node.so +0 -0
  71. package/dist/runtime/linux-arm64/libmuon-ui.so +0 -0
  72. package/dist/runtime/linux-arm64/muon-core +0 -0
  73. package/dist/runtime/linux-arm64/plugins/node-bridge.mjs +5 -5
  74. package/dist/runtime/linux-arm64/plugins/node.so +0 -0
  75. package/dist/runtime/linux-armhf/libmuon-ui.so +0 -0
  76. package/dist/runtime/linux-armhf/muon-core +0 -0
  77. package/dist/runtime/linux-armhf/plugins/node-bridge.mjs +5 -5
  78. package/dist/runtime/linux-armhf/plugins/node.so +0 -0
  79. package/dist/runtime/windows-amd64/libcardio.dll +0 -0
  80. package/dist/runtime/windows-amd64/libmuon-ui.dll +0 -0
  81. package/dist/runtime/windows-amd64/muon-core.exe +0 -0
  82. package/dist/runtime/windows-amd64/plugins/node-bridge.mjs +5 -5
  83. package/dist/runtime/windows-amd64/plugins/node.dll +0 -0
  84. package/dist/runtime/windows-i686/libcardio.dll +0 -0
  85. package/dist/runtime/windows-i686/libmuon-ui.dll +0 -0
  86. package/dist/runtime/windows-i686/muon-core.exe +0 -0
  87. package/dist/runtime/windows-i686/plugins/node-bridge.mjs +5 -5
  88. package/dist/runtime/windows-i686/plugins/node.dll +0 -0
  89. package/dist/{vite-internals-CLwNqMCY.cjs → vite-internals-CueaxMaY.cjs} +4172 -1167
  90. package/dist/vite-internals-CueaxMaY.cjs.map +1 -0
  91. package/dist/vite.cjs +107 -1296
  92. package/dist/vite.cjs.map +1 -1
  93. package/dist/vite.mjs +6036 -4354
  94. package/dist/vite.mjs.map +1 -1
  95. package/images/get-started-android.png +0 -0
  96. package/muon-virtual-modules.d.ts +53 -0
  97. package/muon.d.ts +29 -2
  98. package/package.json +16 -14
  99. package/vite.d.ts +18 -0
  100. package/dist/vite-internals-CLwNqMCY.cjs.map +0 -1
package/README.md CHANGED
@@ -11,6 +11,8 @@ A multi-platform GUI application framework that uses CEF as its backend.
11
11
 
12
12
  ## What is this?
13
13
 
14
+ Android uses WebView and supports debug APKs and signed release APKs containing your web assets. Build on Linux x64 without NDK/CMake for ordinary applications. See [building and distributing Android apps](https://github.com/kekyo/muon-ui/blob/main/docs/en/android.md) for setup, signing, and support limits. The CEF, HMR, and network filtering descriptions below apply to the desktop backend.
15
+
14
16
  Have you ever wanted to update an aging native GUI application into a modern application?
15
17
  Application replacement is extremely complex, and it has always been a hard problem.
16
18
 
@@ -51,6 +53,10 @@ This lets you build modern local GUI applications with the web technology ecosys
51
53
  muon also minimizes the steps required to create a muon application, keeping the barrier to entry low.
52
54
  With the minimal setup, installing the NPM package and adding one line to the configuration file is enough to launch your first muon app.
53
55
 
56
+ WIP: And once you've created a muon application, you can deploy it directly to Android.
57
+
58
+ ![android](./images/get-started-android.png)
59
+
54
60
  ### Features
55
61
 
56
62
  - Restricts all network access through a whitelist filter, letting you completely exclude problematic content.
@@ -58,8 +64,10 @@ With the minimal setup, installing the NPM package and adding one line to the co
58
64
  - The rendering browser is CEF (Chromium Embedded Framework). From the web application's point of view, this is almost the same as using Chromium or Chrome.
59
65
  - Supports the Vite plugin system. It also supports Vite HMR, so previews update in real time during development.
60
66
  - Supports Linux (deb) and Windows (NSIS) package generation, and portable distribution.
67
+ - You can also build Android applications (experimental).
61
68
  - DevTools can be used. CDP (Chrome DevTools Protocol) is also supported, so remote debugging from external tools is possible.
62
69
  - Includes a plugin system. Plugin capabilities can also be restricted by a whitelist filter.
70
+ - An independent SDK supports C/C++ plugin development and distribution as prebuilt NPM packages.
63
71
  - Built-in plugins provide access to local files, open dialogs, child process launching, and window operations.
64
72
 
65
73
  ### Environment
@@ -75,6 +83,8 @@ With the minimal setup, installing the NPM package and adding one line to the co
75
83
 
76
84
  ## Getting started with muon (Quick version)
77
85
 
86
+ This section covers Linux and Windows. For Android SDK/JDK setup, Vite configuration, and APK building and signing, see [building and distributing Android apps](https://github.com/kekyo/muon-ui/blob/main/docs/en/android.md).
87
+
78
88
  Let's get started by creating an application with muon (a muon app).
79
89
  Use a [Vite template](https://vite.dev/guide/) to create "my-muon-app" as you normally would:
80
90
 
package/android.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ // muon - Multi-platform GUI application framework that uses CEF as its backend
2
+ // Copyright (c) Kouji Matsui. (@kekyo@mi.kekyo.net)
3
+ // Under MIT.
4
+ // https://github.com/kekyo/muon-ui
5
+
6
+ /** ABI supported by the prebuilt Muon Android runtime. */
7
+ export type MuonAndroidAbi = "arm64-v8a" | "x86_64";
8
+
9
+ /** Prebuilt native plugin bundled in the application. */
10
+ export interface MuonAndroidPluginOptions {
11
+ /** Unique plugin registration name. */
12
+ name: string;
13
+ /** ELF soname, including the lib prefix and .so suffix. */
14
+ soname: string;
15
+ /** Library path for each ABI selected by android.abis. */
16
+ libraries: Partial<Record<MuonAndroidAbi, string>>;
17
+ /** Ordinary shared dependencies by ABI, copied beside the plugin. */
18
+ dependencies?: Partial<Record<MuonAndroidAbi, readonly string[]>>;
19
+ /** Producer catalog JSON path, required for validate mode; resolved like libraries. */
20
+ metadata?: string;
21
+ }
22
+
23
+ /** Android application options, overriding the muon.json android section. */
24
+ export interface MuonAndroidOptions {
25
+ /** External keystore and environment variable references used by muon pack. */
26
+ signing?: MuonAndroidSigningOptions;
27
+ /** Stable reverse-domain application identifier used for installation and updates. */
28
+ applicationId?: string;
29
+ /** Launcher label; defaults to the package name. */
30
+ label?: string;
31
+ /** Positive integer update version; defaults to 1. Increase for every release. */
32
+ versionCode?: number;
33
+ /** Display version; defaults to package.json version. */
34
+ versionName?: string;
35
+ /** Native ABIs to package; defaults to both supported ABIs. */
36
+ abis?: readonly MuonAndroidAbi[];
37
+ /** PNG launcher icon; relative to the configuration that supplies it. */
38
+ icon?: string;
39
+ /** Android manifest permissions. Runtime permission prompts remain app-specific. */
40
+ permissions?: readonly string[];
41
+ /** SDK directory, otherwise detected through the environment. */
42
+ sdkPath?: string;
43
+ /** Prebuilt native plugins; no NDK is required when building the app. */
44
+ plugins?: readonly MuonAndroidPluginOptions[];
45
+ /** FCM is unavailable in this release. Only false is accepted. */
46
+ fcm?: false;
47
+ /** QuickJS is unavailable in this release. Only false is accepted. */
48
+ quickjs?: false;
49
+ }
50
+
51
+ /** External release credentials; passwords must not appear in project files. */
52
+ export interface MuonAndroidSigningOptions {
53
+ /** Path to the application keystore; relative to the supplying configuration. */
54
+ keystore: string;
55
+ /** Key alias inside the keystore. */
56
+ keyAlias: string;
57
+ /** Environment variable containing the keystore password. */
58
+ storePasswordEnv: string;
59
+ /** Environment variable containing the key password; defaults to storePasswordEnv. */
60
+ keyPasswordEnv?: string;
61
+ }
@@ -0,0 +1,233 @@
1
+ /*!
2
+ * name: muon-ui
3
+ * version: 0.26.47
4
+ * description: A multi-platform GUI application framework that uses CEF as its backend
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/muon-ui.git
8
+ * git.commit.hash: 95dc2712686873c8a022483fd9abf18598019bd7
9
+ */
10
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
11
+ let node_fs_promises = require("node:fs/promises");
12
+ let node_path = require("node:path");
13
+ let node_url = require("node:url");
14
+ let funcity = require("funcity");
15
+ let node_child_process = require("node:child_process");
16
+ let node_fs = require("node:fs");
17
+ let node_os = require("node:os");
18
+ //#region android/command.ts
19
+ /**
20
+ * Runs a build tool without a shell and awaits process completion.
21
+ * @param command - Executable path.
22
+ * @param args - Literal argument vector.
23
+ * @param cwd - Working directory.
24
+ * @param environment - Child environment, including any external credentials.
25
+ * @param output - Optional progress sink; undefined captures output only.
26
+ * @returns Captured standard output and standard error.
27
+ */
28
+ var runAndroidCommand = async (command, args, cwd, environment, output) => await new Promise((resolve, reject) => {
29
+ const child = (0, node_child_process.spawn)(command, args, {
30
+ cwd,
31
+ env: environment,
32
+ stdio: [
33
+ "ignore",
34
+ "pipe",
35
+ "pipe"
36
+ ]
37
+ });
38
+ let captured = "";
39
+ const receive = (chunk) => {
40
+ const text = chunk.toString();
41
+ captured += text;
42
+ output?.(text);
43
+ };
44
+ child.stdout.on("data", receive);
45
+ child.stderr.on("data", receive);
46
+ child.once("error", reject);
47
+ child.once("close", (code, signal) => {
48
+ if (code === 0) resolve(captured);
49
+ else reject(/* @__PURE__ */ new Error(`${command} failed (${signal ?? code}):\n${captured}`));
50
+ });
51
+ });
52
+ //#endregion
53
+ //#region android/toolchain.ts
54
+ var accessible = async (path, mode) => {
55
+ try {
56
+ await (0, node_fs_promises.access)(path, mode);
57
+ return true;
58
+ } catch {
59
+ return false;
60
+ }
61
+ };
62
+ var findExecutable = async (name, environment) => {
63
+ for (const directory of (environment.PATH ?? "").split(node_path.delimiter).filter(Boolean)) {
64
+ const path = (0, node_path.join)(directory, name);
65
+ if (await accessible(path, node_fs.constants.X_OK)) return path;
66
+ }
67
+ };
68
+ /**
69
+ * Diagnoses a Linux x64 SDK/JDK installation and optionally prepares Gradle.
70
+ * @param options - Component location, SDK override and process environment.
71
+ * @returns Validated tool paths and pinned versions.
72
+ * @remarks Does not install SDK packages, accept licenses, or require a device.
73
+ */
74
+ var prepareAndroid = async (options) => {
75
+ if (process.platform !== "linux" || process.arch !== "x64") throw new Error("Android builds currently support Linux x64 hosts.");
76
+ const componentsDirectory = (0, node_path.resolve)(options.componentsDirectory);
77
+ const toolchain = JSON.parse(await (0, node_fs_promises.readFile)((0, node_path.join)(componentsDirectory, "toolchain.json"), "utf8"));
78
+ if (toolchain.schemaVersion !== 1) throw new Error("Unsupported Muon Android toolchain schema.");
79
+ const adb = await findExecutable("adb", options.environment);
80
+ const candidate = options.sdkPath ?? options.environment.ANDROID_HOME ?? options.environment.ANDROID_SDK_ROOT ?? (adb === void 0 ? void 0 : (0, node_path.dirname)((0, node_path.dirname)(adb)));
81
+ if (!candidate) throw new Error("Android SDK not found. Set ANDROID_HOME or android.sdkPath.");
82
+ const sdkPath = (0, node_path.resolve)(candidate);
83
+ for (const [file, packageName] of [[`platforms/${toolchain.platform}/android.jar`, `platforms;${toolchain.platform}`], ...[
84
+ "aapt2",
85
+ "zipalign",
86
+ "apksigner"
87
+ ].map((name) => [`build-tools/${toolchain.buildTools}/${name}`, `build-tools;${toolchain.buildTools}`])]) if (!await accessible((0, node_path.join)(sdkPath, file), node_fs.constants.R_OK)) throw new Error(`Android SDK ${sdkPath} is missing ${packageName}. Install this package using the Android CLI: android sdk install '${packageName}'. See https://developer.android.com/tools/agents/android-cli/commands/sdk`);
88
+ const platformProperties = await (0, node_fs_promises.readFile)((0, node_path.join)(sdkPath, "platforms", toolchain.platform, "source.properties"), "utf8");
89
+ if (Number(/^AndroidVersion.ApiLevel\s*=\s*(\S+)/mu.exec(platformProperties)?.[1]) !== toolchain.compileSdk) throw new Error(`Android SDK platform version differs from the required API ${toolchain.compileSdk}: ${sdkPath}`);
90
+ const buildProperties = await (0, node_fs_promises.readFile)((0, node_path.join)(sdkPath, "build-tools", toolchain.buildTools, "source.properties"), "utf8");
91
+ if (/^Pkg.Revision\s*=\s*(\S+)/mu.exec(buildProperties)?.[1] !== toolchain.buildTools) throw new Error(`Android SDK Build Tools version differs from ${toolchain.buildTools}: ${sdkPath}`);
92
+ const javaPath = options.environment.JAVA_HOME ? (0, node_path.join)(options.environment.JAVA_HOME, "bin/java") : await findExecutable("java", options.environment);
93
+ if (!javaPath || !await accessible(javaPath, node_fs.constants.X_OK)) throw new Error(`JDK ${toolchain.javaMin}–${toolchain.javaMax} is required. Set JAVA_HOME or add java to PATH.`);
94
+ const versionText = await runAndroidCommand(javaPath, ["-version"], componentsDirectory, options.environment, void 0);
95
+ const javaVersion = Number(/(?:openjdk|java) version "(\d+)[."]/u.exec(versionText)?.[1]);
96
+ if (!Number.isInteger(javaVersion) || javaVersion < toolchain.javaMin || javaVersion > toolchain.javaMax) throw new Error(`JDK ${toolchain.javaMin}–${toolchain.javaMax} is required; detected ${versionText.trim()}.`);
97
+ if (options.prepareGradle) {
98
+ const cache = (0, node_path.join)((0, node_os.homedir)(), ".cache/muon/android", toolchain.gradle);
99
+ await (0, node_fs_promises.mkdir)(cache, { recursive: true });
100
+ if (!(await runAndroidCommand((0, node_path.join)(componentsDirectory, "templates/gradlew"), ["--version"], cache, options.environment, void 0)).includes(`Gradle ${toolchain.gradle}`)) throw new Error("Gradle Wrapper version differs from the shipped toolchain.");
101
+ }
102
+ return {
103
+ target: "android",
104
+ sdkPath,
105
+ javaVersion,
106
+ javaPath,
107
+ toolchain,
108
+ componentsDirectory
109
+ };
110
+ };
111
+ //#endregion
112
+ //#region android/build.ts
113
+ var xml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
114
+ var render = async (template, variables) => {
115
+ const logs = [];
116
+ const value = await (0, funcity.runScriptOnceToText)(await (0, node_fs_promises.readFile)(template, "utf8"), {
117
+ sourceId: template,
118
+ variables,
119
+ logs
120
+ });
121
+ if (value === void 0 || logs.some((log) => log.type === "error")) throw new Error(`Cannot render Android template ${template}: ${JSON.stringify(logs)}`);
122
+ return value;
123
+ };
124
+ /**
125
+ * Builds a self-contained APK from the shipped AAR and prebuilt web assets.
126
+ * @param input - Explicit application, artifact and toolchain inputs.
127
+ * @returns The APK path and its actual variant/signing state.
128
+ * @remarks Does not run Vite, compile native code or infer a repository root.
129
+ */
130
+ var buildAndroidApplication = async (input) => {
131
+ const tools = await prepareAndroid({
132
+ componentsDirectory: input.componentsDirectory,
133
+ sdkPath: input.sdkPath,
134
+ environment: input.environment,
135
+ prepareGradle: false
136
+ });
137
+ const project = (0, node_path.resolve)(input.projectDirectory);
138
+ const assetRelative = (0, node_path.relative)((0, node_path.resolve)(input.assetsDirectory), project);
139
+ if (assetRelative === "" || !assetRelative.startsWith("..") && !(0, node_path.isAbsolute)(assetRelative)) throw new Error("The generated Android project must be outside the web assets directory.");
140
+ if ((0, node_path.isAbsolute)(input.assetPath) || input.assetPath.split(/[\\/]/u).some((part) => part === ".." || part === "muon")) throw new Error("Android assetPath must be relative and must not use the reserved muon directory.");
141
+ await (0, node_fs_promises.rm)(project, {
142
+ recursive: true,
143
+ force: true
144
+ });
145
+ await (0, node_fs_promises.cp)((0, node_path.join)(tools.componentsDirectory, "templates"), project, { recursive: true });
146
+ await (0, node_fs_promises.chmod)((0, node_path.join)(project, "gradlew"), 493);
147
+ const assets = (0, node_path.join)(project, "app/src/main/assets");
148
+ await (0, node_fs_promises.mkdir)(assets, { recursive: true });
149
+ await (0, node_fs_promises.cp)(input.assetsDirectory, (0, node_path.join)(assets, input.assetPath), {
150
+ recursive: true,
151
+ dereference: true
152
+ });
153
+ const frameworkAssets = (0, node_path.join)(assets, "muon");
154
+ try {
155
+ await (0, node_fs_promises.stat)(frameworkAssets);
156
+ throw new Error("Web assets must not contain the reserved muon directory.");
157
+ } catch (error) {
158
+ if (error.code !== "ENOENT") throw error;
159
+ }
160
+ await (0, node_fs_promises.mkdir)(frameworkAssets, { recursive: true });
161
+ await (0, node_fs_promises.cp)((0, node_path.join)(tools.componentsDirectory, "licenses"), (0, node_path.join)(frameworkAssets, "licenses"), { recursive: true });
162
+ await (0, node_fs_promises.copyFile)((0, node_path.join)(tools.componentsDirectory, "renderer/renderer.js"), (0, node_path.join)(frameworkAssets, "renderer.js"));
163
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(frameworkAssets, "config.json"), JSON.stringify({
164
+ startPage: input.startPage,
165
+ values: input.values,
166
+ plugin: input.pluginAccess
167
+ }));
168
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(frameworkAssets, "plugins.json"), JSON.stringify({
169
+ schemaVersion: 1,
170
+ plugins: input.plugins.map(({ name, soname, allow, config, expectedFunctions }) => ({
171
+ name,
172
+ soname,
173
+ allow,
174
+ config,
175
+ ...expectedFunctions === void 0 ? {} : { expectedFunctions }
176
+ }))
177
+ }));
178
+ for (const plugin of input.plugins) for (const abi of input.abis) {
179
+ const library = plugin.libraries[abi];
180
+ if (!library) throw new Error(`${plugin.name} is missing its ${abi} library.`);
181
+ const destination = (0, node_path.join)(project, "app/src/main/jniLibs", abi, plugin.soname);
182
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(destination), { recursive: true });
183
+ await (0, node_fs_promises.copyFile)(library, destination);
184
+ for (const dependency of plugin.dependencies?.[abi] ?? []) await (0, node_fs_promises.copyFile)(dependency, (0, node_path.join)((0, node_path.dirname)(destination), (0, node_path.basename)(dependency)));
185
+ }
186
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(project, "muon-build.json"), JSON.stringify({
187
+ ...tools.toolchain,
188
+ applicationId: input.applicationId,
189
+ versionCode: input.versionCode,
190
+ versionName: input.versionName,
191
+ abis: input.abis,
192
+ maven: (0, node_url.pathToFileURL)((0, node_path.join)(tools.componentsDirectory, "maven")).href
193
+ }, null, 2));
194
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(project, "app/src/main/AndroidManifest.xml"), await render((0, node_path.join)(project, "AndroidManifest.xml.fc"), /* @__PURE__ */ new Map([["permissions", input.permissions.map(xml)]])));
195
+ const resources = (0, node_path.join)(project, "app/src/main/res");
196
+ await (0, node_fs_promises.mkdir)((0, node_path.join)(resources, "values"), { recursive: true });
197
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(resources, "values/strings.xml"), await render((0, node_path.join)(project, "strings.xml.fc"), /* @__PURE__ */ new Map([["label", xml(input.label.replaceAll("\\", "\\\\").replaceAll("\"", "\\\""))]])));
198
+ if (input.icon !== void 0) {
199
+ await (0, node_fs_promises.rm)((0, node_path.join)(resources, "mipmap/ic_launcher.xml"));
200
+ await (0, node_fs_promises.copyFile)(input.icon, (0, node_path.join)(resources, "mipmap/ic_launcher.png"));
201
+ }
202
+ await runAndroidCommand((0, node_path.join)(project, "gradlew"), [
203
+ "--no-daemon",
204
+ "--console=plain",
205
+ input.variant === "debug" ? ":app:assembleDebug" : ":app:assembleRelease"
206
+ ], project, {
207
+ ...input.environment,
208
+ ANDROID_HOME: tools.sdkPath,
209
+ ANDROID_SDK_ROOT: tools.sdkPath
210
+ }, input.output);
211
+ const name = input.variant === "debug" ? "app-debug.apk" : "app-release-unsigned.apk";
212
+ const packagePath = (0, node_path.join)((0, node_path.resolve)(input.outputDirectory), `${input.applicationId}-${input.versionCode}-${input.variant}.apk`);
213
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(packagePath), { recursive: true });
214
+ await (0, node_fs_promises.copyFile)((0, node_path.join)(project, "app/build/outputs/apk", input.variant, name), packagePath);
215
+ const result = {
216
+ target: "android",
217
+ packagePath,
218
+ projectDirectory: project,
219
+ variant: input.variant,
220
+ abis: input.abis,
221
+ applicationId: input.applicationId,
222
+ versionCode: input.versionCode,
223
+ versionName: input.versionName,
224
+ signing: input.variant === "debug" ? "debug" : "unsigned"
225
+ };
226
+ await (0, node_fs_promises.writeFile)(`${packagePath}.json`, JSON.stringify(result, null, 2) + "\n");
227
+ return result;
228
+ };
229
+ //#endregion
230
+ exports.buildAndroidApplication = buildAndroidApplication;
231
+ exports.prepareAndroid = prepareAndroid;
232
+
233
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["spawn","access","delimiter","join","constants","resolve","readFile","dirname","homedir","mkdir","runScriptOnceToText","readFile","resolve","relative","isAbsolute","rm","cp","join","chmod","mkdir","stat","copyFile","writeFile","dirname","basename","pathToFileURL"],"sources":["../../android/command.ts","../../android/toolchain.ts","../../android/build.ts"],"sourcesContent":["// muon - Multi-platform GUI application framework that uses CEF as its backend\n// Copyright (c) Kouji Matsui. (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/muon-ui\n\nimport { spawn } from \"node:child_process\";\n\n/**\n * Runs a build tool without a shell and awaits process completion.\n * @param command - Executable path.\n * @param args - Literal argument vector.\n * @param cwd - Working directory.\n * @param environment - Child environment, including any external credentials.\n * @param output - Optional progress sink; undefined captures output only.\n * @returns Captured standard output and standard error.\n */\nexport const runAndroidCommand = async (\n command: string,\n args: readonly string[],\n cwd: string,\n environment: NodeJS.ProcessEnv,\n output: ((text: string) => void) | undefined,\n): Promise<string> =>\n await new Promise<string>((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n env: environment,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let captured = \"\";\n const receive = (chunk: Buffer) => {\n const text = chunk.toString();\n captured += text;\n output?.(text);\n };\n child.stdout.on(\"data\", receive);\n child.stderr.on(\"data\", receive);\n child.once(\"error\", reject);\n child.once(\"close\", (code, signal) => {\n if (code === 0) resolve(captured);\n else\n reject(\n new Error(`${command} failed (${signal ?? code}):\\n${captured}`),\n );\n });\n });\n","// muon - Multi-platform GUI application framework that uses CEF as its backend\n// Copyright (c) Kouji Matsui. (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/muon-ui\n\nimport { constants } from \"node:fs\";\nimport { access, mkdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { delimiter, dirname, join, resolve } from \"node:path\";\nimport { runAndroidCommand } from \"./command.js\";\n\n/** Versions fixed by the shipped Android component set. */\nexport interface MuonAndroidToolchain {\n /** Exact Android SDK platform package directory. */ readonly platform: string;\n /** Component schema. */ readonly schemaVersion: 1;\n /** Android Gradle plugin version. */ readonly agp: string;\n /** Wrapper version. */ readonly gradle: string;\n /** API used for compilation. */ readonly compileSdk: number;\n /** API targeted by generated applications. */ readonly targetSdk: number;\n /** Minimum installation API; see documented verification range. */ readonly minSdk: number;\n /** Required SDK Build Tools version. */ readonly buildTools: string;\n /** Minimum JDK runtime version. */ readonly javaMin: number;\n /** Highest supported JDK runtime version. */ readonly javaMax: number;\n /** Version of dev.muon:runtime. */ readonly runtimeVersion: string;\n /** ABIs included in the standard runtime. */ readonly abis: readonly string[];\n}\n\n/** Inputs for the Android environment diagnostic. */\nexport interface MuonAndroidPrepareOptions {\n /** Directory containing templates, maven, renderer and toolchain.json. */ componentsDirectory: string;\n /** Explicit SDK location, otherwise resolved from the environment or adb. */ sdkPath:\n string | undefined;\n /** Child environment. */ environment: NodeJS.ProcessEnv;\n /** Whether to download/verify the pinned Gradle distribution using its Wrapper. */ prepareGradle: boolean;\n}\n\n/** Validated tools for an Android build; no NDK or CMake is needed. */\nexport interface MuonAndroidPrepareResult {\n /** Discriminates Android from desktop preparation. */ readonly target: \"android\";\n /** Absolute SDK directory. */ readonly sdkPath: string;\n /** Detected JDK major version. */ readonly javaVersion: number;\n /** Absolute Java executable. */ readonly javaPath: string;\n /** Shipped version requirements. */ readonly toolchain: MuonAndroidToolchain;\n /** Component directory used for this build. */ readonly componentsDirectory: string;\n}\n\nconst accessible = async (path: string, mode: number): Promise<boolean> => {\n try {\n await access(path, mode);\n return true;\n } catch {\n return false;\n }\n};\n\nconst findExecutable = async (\n name: string,\n environment: NodeJS.ProcessEnv,\n): Promise<string | undefined> => {\n for (const directory of (environment.PATH ?? \"\")\n .split(delimiter)\n .filter(Boolean)) {\n const path = join(directory, name);\n if (await accessible(path, constants.X_OK)) return path;\n }\n return undefined;\n};\n\n/**\n * Diagnoses a Linux x64 SDK/JDK installation and optionally prepares Gradle.\n * @param options - Component location, SDK override and process environment.\n * @returns Validated tool paths and pinned versions.\n * @remarks Does not install SDK packages, accept licenses, or require a device.\n */\nexport const prepareAndroid = async (\n options: MuonAndroidPrepareOptions,\n): Promise<MuonAndroidPrepareResult> => {\n if (process.platform !== \"linux\" || process.arch !== \"x64\") {\n throw new Error(\"Android builds currently support Linux x64 hosts.\");\n }\n const componentsDirectory = resolve(options.componentsDirectory);\n const toolchain = JSON.parse(\n await readFile(join(componentsDirectory, \"toolchain.json\"), \"utf8\"),\n ) as MuonAndroidToolchain;\n if (toolchain.schemaVersion !== 1)\n throw new Error(\"Unsupported Muon Android toolchain schema.\");\n const adb = await findExecutable(\"adb\", options.environment);\n const candidate =\n options.sdkPath ??\n options.environment.ANDROID_HOME ??\n options.environment.ANDROID_SDK_ROOT ??\n (adb === undefined ? undefined : dirname(dirname(adb)));\n if (!candidate)\n throw new Error(\n \"Android SDK not found. Set ANDROID_HOME or android.sdkPath.\",\n );\n const sdkPath = resolve(candidate);\n for (const [file, packageName] of [\n [\n `platforms/${toolchain.platform}/android.jar`,\n `platforms;${toolchain.platform}`,\n ],\n ...[\"aapt2\", \"zipalign\", \"apksigner\"].map((name) => [\n `build-tools/${toolchain.buildTools}/${name}`,\n `build-tools;${toolchain.buildTools}`,\n ]),\n ]) {\n if (!(await accessible(join(sdkPath, file!), constants.R_OK))) {\n throw new Error(\n `Android SDK ${sdkPath} is missing ${packageName}. Install this package using the Android CLI: android sdk install '${packageName}'. See https://developer.android.com/tools/agents/android-cli/commands/sdk`,\n );\n }\n }\n const platformProperties = await readFile(\n join(sdkPath, \"platforms\", toolchain.platform, \"source.properties\"),\n \"utf8\",\n );\n if (\n Number(\n /^AndroidVersion.ApiLevel\\s*=\\s*(\\S+)/mu.exec(platformProperties)?.[1],\n ) !== toolchain.compileSdk\n ) {\n throw new Error(\n `Android SDK platform version differs from the required API ${toolchain.compileSdk}: ${sdkPath}`,\n );\n }\n const buildProperties = await readFile(\n join(sdkPath, \"build-tools\", toolchain.buildTools, \"source.properties\"),\n \"utf8\",\n );\n if (\n /^Pkg.Revision\\s*=\\s*(\\S+)/mu.exec(buildProperties)?.[1] !==\n toolchain.buildTools\n ) {\n throw new Error(\n `Android SDK Build Tools version differs from ${toolchain.buildTools}: ${sdkPath}`,\n );\n }\n const javaPath = options.environment.JAVA_HOME\n ? join(options.environment.JAVA_HOME, \"bin/java\")\n : await findExecutable(\"java\", options.environment);\n if (!javaPath || !(await accessible(javaPath, constants.X_OK))) {\n throw new Error(\n `JDK ${toolchain.javaMin}–${toolchain.javaMax} is required. Set JAVA_HOME or add java to PATH.`,\n );\n }\n const versionText = await runAndroidCommand(\n javaPath,\n [\"-version\"],\n componentsDirectory,\n options.environment,\n undefined,\n );\n const javaVersion = Number(\n /(?:openjdk|java) version \"(\\d+)[.\"]/u.exec(versionText)?.[1],\n );\n if (\n !Number.isInteger(javaVersion) ||\n javaVersion < toolchain.javaMin ||\n javaVersion > toolchain.javaMax\n ) {\n throw new Error(\n `JDK ${toolchain.javaMin}–${toolchain.javaMax} is required; detected ${versionText.trim()}.`,\n );\n }\n if (options.prepareGradle) {\n const cache = join(homedir(), \".cache/muon/android\", toolchain.gradle);\n await mkdir(cache, { recursive: true });\n const result = await runAndroidCommand(\n join(componentsDirectory, \"templates/gradlew\"),\n [\"--version\"],\n cache,\n options.environment,\n undefined,\n );\n if (!result.includes(`Gradle ${toolchain.gradle}`))\n throw new Error(\n \"Gradle Wrapper version differs from the shipped toolchain.\",\n );\n }\n return {\n target: \"android\",\n sdkPath,\n javaVersion,\n javaPath,\n toolchain,\n componentsDirectory,\n };\n};\n","// muon - Multi-platform GUI application framework that uses CEF as its backend\n// Copyright (c) Kouji Matsui. (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/muon-ui\n\nimport {\n chmod,\n copyFile,\n cp,\n mkdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport {\n basename,\n dirname,\n isAbsolute,\n join,\n relative,\n resolve,\n} from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { runScriptOnceToText, type FunCityLogEntry } from \"funcity\";\nimport { runAndroidCommand } from \"./command.js\";\nimport { prepareAndroid } from \"./toolchain.js\";\n\n/** ABI shipped by the Muon Android runtime. */\nexport type MuonAndroidAbi = \"arm64-v8a\" | \"x86_64\";\n\n/** Package-owned plugin exposure and built-in function policy. */\nexport type MuonAndroidPluginAccess = {\n /** Whether the trusted main frame may use the bridge. */ readonly enabled: boolean;\n /** Allowed built-in public function paths. */ readonly internalAllow: readonly string[];\n} & (\n | { /** Global API exposure mode. */ readonly mode: \"simple\" }\n | {\n /** Virtual-module exposure mode. */ readonly mode: \"validate\";\n /** Capabilities generated by the same build as the application bundle. */ readonly capabilities: readonly {\n /** Opaque capability id. */ readonly id: string;\n /** Allowed public function paths. */ readonly allow: readonly string[];\n }[];\n }\n);\n\n/** Normalized package-owned plugin entry. */\nexport interface MuonAndroidPackagedPlugin {\n /** Ordinary shared libraries copied beside the plugin for each ABI. */ readonly dependencies?: Readonly<\n Partial<Record<MuonAndroidAbi, readonly string[]>>\n >;\n /** Logical capability name. */ readonly name: string;\n /** Packaged ELF soname. */ readonly soname: string;\n /** Explicitly allowed public function patterns. */ readonly allow: readonly string[];\n /** Producer catalog after applying the allow policy, checked when loading. */ readonly expectedFunctions?: readonly string[];\n /** Application-owned plugin configuration. */ readonly config: readonly {\n readonly key: string;\n readonly value: string;\n }[];\n /** Absolute prebuilt library path for each selected ABI. */ readonly libraries: Readonly<\n Partial<Record<MuonAndroidAbi, string>>\n >;\n}\n\n/** Explicit inputs used by the Android package builder. */\nexport interface MuonAndroidApplicationInput {\n /** Packaged Muon Android components. */ componentsDirectory: string;\n /** Directory containing previously built web assets. */ assetsDirectory: string;\n /** Relative URL path prefix under the trusted asset host. */ assetPath: string;\n /** Trusted HTTPS entry URL. */ startPage: string;\n /** Application identifier independent of Muon's Java namespace. */ applicationId: string;\n /** Launcher label. */ label: string;\n /** Monotonic Android update number. */ versionCode: number;\n /** User-facing version. */ versionName: string;\n /** Selected runtime ABIs. */ abis: readonly MuonAndroidAbi[];\n /** Manifest permissions, already normalized by the public build boundary. */ permissions: readonly string[];\n /** Values returned by muon.environments.getConfigValues. */ values: Readonly<\n Record<string, string>\n >;\n /** Explicit prebuilt plugins; an empty array builds a standard app. */ plugins: readonly MuonAndroidPackagedPlugin[];\n /** Validated common plugin access settings. */ pluginAccess: MuonAndroidPluginAccess;\n /** Optional PNG icon already converted by the public build boundary. */ icon:\n string | undefined;\n /** Owned directory for generated Gradle sources and intermediates. */ projectDirectory: string;\n /** Destination for APKs and result metadata. */ outputDirectory: string;\n /** SDK override. */ sdkPath: string | undefined;\n /** Debug package or unsigned release package. */ variant:\n \"debug\" | \"release\";\n /** Process environment. */ environment: NodeJS.ProcessEnv;\n /** Progress sink, or undefined for a quiet build. */ output:\n ((text: string) => void) | undefined;\n}\n\n/** Android APK result, without desktop launcher or CEF fields. */\nexport interface MuonAndroidApplicationResult {\n /** Target discriminator. */ readonly target: \"android\";\n /** Absolute APK path. */ readonly packagePath: string;\n /** Generated Gradle project for diagnostics. */ readonly projectDirectory: string;\n /** Android variant. */ readonly variant: \"debug\" | \"release\";\n /** Included native ABIs. */ readonly abis: readonly MuonAndroidAbi[];\n /** Application identifier. */ readonly applicationId: string;\n /** Update version. */ readonly versionCode: number;\n /** Display version. */ readonly versionName: string;\n /** Actual signing state. */ readonly signing:\n \"debug\" | \"unsigned\" | \"release\";\n}\n\nconst xml = (value: string): string =>\n value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&apos;\");\n\nconst render = async (\n template: string,\n variables: ReadonlyMap<string, unknown>,\n): Promise<string> => {\n const logs: FunCityLogEntry[] = [];\n const value = await runScriptOnceToText(await readFile(template, \"utf8\"), {\n sourceId: template,\n variables,\n logs,\n });\n if (value === undefined || logs.some((log) => log.type === \"error\")) {\n throw new Error(\n `Cannot render Android template ${template}: ${JSON.stringify(logs)}`,\n );\n }\n return value;\n};\n\n/**\n * Builds a self-contained APK from the shipped AAR and prebuilt web assets.\n * @param input - Explicit application, artifact and toolchain inputs.\n * @returns The APK path and its actual variant/signing state.\n * @remarks Does not run Vite, compile native code or infer a repository root.\n */\nexport const buildAndroidApplication = async (\n input: MuonAndroidApplicationInput,\n): Promise<MuonAndroidApplicationResult> => {\n const tools = await prepareAndroid({\n componentsDirectory: input.componentsDirectory,\n sdkPath: input.sdkPath,\n environment: input.environment,\n prepareGradle: false,\n });\n const project = resolve(input.projectDirectory);\n const assetRelative = relative(resolve(input.assetsDirectory), project);\n if (\n assetRelative === \"\" ||\n (!assetRelative.startsWith(\"..\") && !isAbsolute(assetRelative))\n ) {\n throw new Error(\n \"The generated Android project must be outside the web assets directory.\",\n );\n }\n if (\n isAbsolute(input.assetPath) ||\n input.assetPath\n .split(/[\\\\/]/u)\n .some((part) => part === \"..\" || part === \"muon\")\n ) {\n throw new Error(\n \"Android assetPath must be relative and must not use the reserved muon directory.\",\n );\n }\n await rm(project, { recursive: true, force: true });\n await cp(join(tools.componentsDirectory, \"templates\"), project, {\n recursive: true,\n });\n await chmod(join(project, \"gradlew\"), 0o755);\n const assets = join(project, \"app/src/main/assets\");\n await mkdir(assets, { recursive: true });\n await cp(input.assetsDirectory, join(assets, input.assetPath), {\n recursive: true,\n dereference: true,\n });\n const frameworkAssets = join(assets, \"muon\");\n try {\n await stat(frameworkAssets);\n throw new Error(\"Web assets must not contain the reserved muon directory.\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n await mkdir(frameworkAssets, { recursive: true });\n await cp(\n join(tools.componentsDirectory, \"licenses\"),\n join(frameworkAssets, \"licenses\"),\n { recursive: true },\n );\n await copyFile(\n join(tools.componentsDirectory, \"renderer/renderer.js\"),\n join(frameworkAssets, \"renderer.js\"),\n );\n await writeFile(\n join(frameworkAssets, \"config.json\"),\n JSON.stringify({\n startPage: input.startPage,\n values: input.values,\n plugin: input.pluginAccess,\n }),\n );\n await writeFile(\n join(frameworkAssets, \"plugins.json\"),\n JSON.stringify({\n schemaVersion: 1,\n plugins: input.plugins.map(\n ({ name, soname, allow, config, expectedFunctions }) => ({\n name,\n soname,\n allow,\n config,\n ...(expectedFunctions === undefined ? {} : { expectedFunctions }),\n }),\n ),\n }),\n );\n for (const plugin of input.plugins) {\n for (const abi of input.abis) {\n const library = plugin.libraries[abi];\n if (!library)\n throw new Error(`${plugin.name} is missing its ${abi} library.`);\n const destination = join(\n project,\n \"app/src/main/jniLibs\",\n abi,\n plugin.soname,\n );\n await mkdir(dirname(destination), { recursive: true });\n await copyFile(library, destination);\n for (const dependency of plugin.dependencies?.[abi] ?? [])\n await copyFile(\n dependency,\n join(dirname(destination), basename(dependency)),\n );\n }\n }\n await writeFile(\n join(project, \"muon-build.json\"),\n JSON.stringify(\n {\n ...tools.toolchain,\n applicationId: input.applicationId,\n versionCode: input.versionCode,\n versionName: input.versionName,\n abis: input.abis,\n maven: pathToFileURL(join(tools.componentsDirectory, \"maven\")).href,\n },\n null,\n 2,\n ),\n );\n await writeFile(\n join(project, \"app/src/main/AndroidManifest.xml\"),\n await render(\n join(project, \"AndroidManifest.xml.fc\"),\n new Map([[\"permissions\", input.permissions.map(xml)]]),\n ),\n );\n const resources = join(project, \"app/src/main/res\");\n await mkdir(join(resources, \"values\"), { recursive: true });\n await writeFile(\n join(resources, \"values/strings.xml\"),\n await render(\n join(project, \"strings.xml.fc\"),\n new Map([\n [\n \"label\",\n xml(input.label.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"')),\n ],\n ]),\n ),\n );\n if (input.icon !== undefined) {\n await rm(join(resources, \"mipmap/ic_launcher.xml\"));\n await copyFile(input.icon, join(resources, \"mipmap/ic_launcher.png\"));\n }\n await runAndroidCommand(\n join(project, \"gradlew\"),\n [\n \"--no-daemon\",\n \"--console=plain\",\n input.variant === \"debug\" ? \":app:assembleDebug\" : \":app:assembleRelease\",\n ],\n project,\n {\n ...input.environment,\n ANDROID_HOME: tools.sdkPath,\n ANDROID_SDK_ROOT: tools.sdkPath,\n },\n input.output,\n );\n const name =\n input.variant === \"debug\" ? \"app-debug.apk\" : \"app-release-unsigned.apk\";\n const packagePath = join(\n resolve(input.outputDirectory),\n `${input.applicationId}-${input.versionCode}-${input.variant}.apk`,\n );\n await mkdir(dirname(packagePath), { recursive: true });\n await copyFile(\n join(project, \"app/build/outputs/apk\", input.variant, name),\n packagePath,\n );\n const result: MuonAndroidApplicationResult = {\n target: \"android\",\n packagePath,\n projectDirectory: project,\n variant: input.variant,\n abis: input.abis,\n applicationId: input.applicationId,\n versionCode: input.versionCode,\n versionName: input.versionName,\n signing: input.variant === \"debug\" ? \"debug\" : \"unsigned\",\n };\n await writeFile(\n `${packagePath}.json`,\n JSON.stringify(result, null, 2) + \"\\n\",\n );\n return result;\n};\n"],"mappings":"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"}
@@ -0,0 +1,231 @@
1
+ /*!
2
+ * name: muon-ui
3
+ * version: 0.26.47
4
+ * description: A multi-platform GUI application framework that uses CEF as its backend
5
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
+ * license: MIT
7
+ * repository.url: https://github.com/kekyo/muon-ui.git
8
+ * git.commit.hash: 95dc2712686873c8a022483fd9abf18598019bd7
9
+ */
10
+ import { access, chmod, copyFile, cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
11
+ import { basename, delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
12
+ import { pathToFileURL } from "node:url";
13
+ import { runScriptOnceToText } from "funcity";
14
+ import { spawn } from "node:child_process";
15
+ import { constants } from "node:fs";
16
+ import { homedir } from "node:os";
17
+ //#region android/command.ts
18
+ /**
19
+ * Runs a build tool without a shell and awaits process completion.
20
+ * @param command - Executable path.
21
+ * @param args - Literal argument vector.
22
+ * @param cwd - Working directory.
23
+ * @param environment - Child environment, including any external credentials.
24
+ * @param output - Optional progress sink; undefined captures output only.
25
+ * @returns Captured standard output and standard error.
26
+ */
27
+ var runAndroidCommand = async (command, args, cwd, environment, output) => await new Promise((resolve, reject) => {
28
+ const child = spawn(command, args, {
29
+ cwd,
30
+ env: environment,
31
+ stdio: [
32
+ "ignore",
33
+ "pipe",
34
+ "pipe"
35
+ ]
36
+ });
37
+ let captured = "";
38
+ const receive = (chunk) => {
39
+ const text = chunk.toString();
40
+ captured += text;
41
+ output?.(text);
42
+ };
43
+ child.stdout.on("data", receive);
44
+ child.stderr.on("data", receive);
45
+ child.once("error", reject);
46
+ child.once("close", (code, signal) => {
47
+ if (code === 0) resolve(captured);
48
+ else reject(/* @__PURE__ */ new Error(`${command} failed (${signal ?? code}):\n${captured}`));
49
+ });
50
+ });
51
+ //#endregion
52
+ //#region android/toolchain.ts
53
+ var accessible = async (path, mode) => {
54
+ try {
55
+ await access(path, mode);
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ };
61
+ var findExecutable = async (name, environment) => {
62
+ for (const directory of (environment.PATH ?? "").split(delimiter).filter(Boolean)) {
63
+ const path = join(directory, name);
64
+ if (await accessible(path, constants.X_OK)) return path;
65
+ }
66
+ };
67
+ /**
68
+ * Diagnoses a Linux x64 SDK/JDK installation and optionally prepares Gradle.
69
+ * @param options - Component location, SDK override and process environment.
70
+ * @returns Validated tool paths and pinned versions.
71
+ * @remarks Does not install SDK packages, accept licenses, or require a device.
72
+ */
73
+ var prepareAndroid = async (options) => {
74
+ if (process.platform !== "linux" || process.arch !== "x64") throw new Error("Android builds currently support Linux x64 hosts.");
75
+ const componentsDirectory = resolve(options.componentsDirectory);
76
+ const toolchain = JSON.parse(await readFile(join(componentsDirectory, "toolchain.json"), "utf8"));
77
+ if (toolchain.schemaVersion !== 1) throw new Error("Unsupported Muon Android toolchain schema.");
78
+ const adb = await findExecutable("adb", options.environment);
79
+ const candidate = options.sdkPath ?? options.environment.ANDROID_HOME ?? options.environment.ANDROID_SDK_ROOT ?? (adb === void 0 ? void 0 : dirname(dirname(adb)));
80
+ if (!candidate) throw new Error("Android SDK not found. Set ANDROID_HOME or android.sdkPath.");
81
+ const sdkPath = resolve(candidate);
82
+ for (const [file, packageName] of [[`platforms/${toolchain.platform}/android.jar`, `platforms;${toolchain.platform}`], ...[
83
+ "aapt2",
84
+ "zipalign",
85
+ "apksigner"
86
+ ].map((name) => [`build-tools/${toolchain.buildTools}/${name}`, `build-tools;${toolchain.buildTools}`])]) if (!await accessible(join(sdkPath, file), constants.R_OK)) throw new Error(`Android SDK ${sdkPath} is missing ${packageName}. Install this package using the Android CLI: android sdk install '${packageName}'. See https://developer.android.com/tools/agents/android-cli/commands/sdk`);
87
+ const platformProperties = await readFile(join(sdkPath, "platforms", toolchain.platform, "source.properties"), "utf8");
88
+ if (Number(/^AndroidVersion.ApiLevel\s*=\s*(\S+)/mu.exec(platformProperties)?.[1]) !== toolchain.compileSdk) throw new Error(`Android SDK platform version differs from the required API ${toolchain.compileSdk}: ${sdkPath}`);
89
+ const buildProperties = await readFile(join(sdkPath, "build-tools", toolchain.buildTools, "source.properties"), "utf8");
90
+ if (/^Pkg.Revision\s*=\s*(\S+)/mu.exec(buildProperties)?.[1] !== toolchain.buildTools) throw new Error(`Android SDK Build Tools version differs from ${toolchain.buildTools}: ${sdkPath}`);
91
+ const javaPath = options.environment.JAVA_HOME ? join(options.environment.JAVA_HOME, "bin/java") : await findExecutable("java", options.environment);
92
+ if (!javaPath || !await accessible(javaPath, constants.X_OK)) throw new Error(`JDK ${toolchain.javaMin}–${toolchain.javaMax} is required. Set JAVA_HOME or add java to PATH.`);
93
+ const versionText = await runAndroidCommand(javaPath, ["-version"], componentsDirectory, options.environment, void 0);
94
+ const javaVersion = Number(/(?:openjdk|java) version "(\d+)[."]/u.exec(versionText)?.[1]);
95
+ if (!Number.isInteger(javaVersion) || javaVersion < toolchain.javaMin || javaVersion > toolchain.javaMax) throw new Error(`JDK ${toolchain.javaMin}–${toolchain.javaMax} is required; detected ${versionText.trim()}.`);
96
+ if (options.prepareGradle) {
97
+ const cache = join(homedir(), ".cache/muon/android", toolchain.gradle);
98
+ await mkdir(cache, { recursive: true });
99
+ if (!(await runAndroidCommand(join(componentsDirectory, "templates/gradlew"), ["--version"], cache, options.environment, void 0)).includes(`Gradle ${toolchain.gradle}`)) throw new Error("Gradle Wrapper version differs from the shipped toolchain.");
100
+ }
101
+ return {
102
+ target: "android",
103
+ sdkPath,
104
+ javaVersion,
105
+ javaPath,
106
+ toolchain,
107
+ componentsDirectory
108
+ };
109
+ };
110
+ //#endregion
111
+ //#region android/build.ts
112
+ var xml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
113
+ var render = async (template, variables) => {
114
+ const logs = [];
115
+ const value = await runScriptOnceToText(await readFile(template, "utf8"), {
116
+ sourceId: template,
117
+ variables,
118
+ logs
119
+ });
120
+ if (value === void 0 || logs.some((log) => log.type === "error")) throw new Error(`Cannot render Android template ${template}: ${JSON.stringify(logs)}`);
121
+ return value;
122
+ };
123
+ /**
124
+ * Builds a self-contained APK from the shipped AAR and prebuilt web assets.
125
+ * @param input - Explicit application, artifact and toolchain inputs.
126
+ * @returns The APK path and its actual variant/signing state.
127
+ * @remarks Does not run Vite, compile native code or infer a repository root.
128
+ */
129
+ var buildAndroidApplication = async (input) => {
130
+ const tools = await prepareAndroid({
131
+ componentsDirectory: input.componentsDirectory,
132
+ sdkPath: input.sdkPath,
133
+ environment: input.environment,
134
+ prepareGradle: false
135
+ });
136
+ const project = resolve(input.projectDirectory);
137
+ const assetRelative = relative(resolve(input.assetsDirectory), project);
138
+ if (assetRelative === "" || !assetRelative.startsWith("..") && !isAbsolute(assetRelative)) throw new Error("The generated Android project must be outside the web assets directory.");
139
+ if (isAbsolute(input.assetPath) || input.assetPath.split(/[\\/]/u).some((part) => part === ".." || part === "muon")) throw new Error("Android assetPath must be relative and must not use the reserved muon directory.");
140
+ await rm(project, {
141
+ recursive: true,
142
+ force: true
143
+ });
144
+ await cp(join(tools.componentsDirectory, "templates"), project, { recursive: true });
145
+ await chmod(join(project, "gradlew"), 493);
146
+ const assets = join(project, "app/src/main/assets");
147
+ await mkdir(assets, { recursive: true });
148
+ await cp(input.assetsDirectory, join(assets, input.assetPath), {
149
+ recursive: true,
150
+ dereference: true
151
+ });
152
+ const frameworkAssets = join(assets, "muon");
153
+ try {
154
+ await stat(frameworkAssets);
155
+ throw new Error("Web assets must not contain the reserved muon directory.");
156
+ } catch (error) {
157
+ if (error.code !== "ENOENT") throw error;
158
+ }
159
+ await mkdir(frameworkAssets, { recursive: true });
160
+ await cp(join(tools.componentsDirectory, "licenses"), join(frameworkAssets, "licenses"), { recursive: true });
161
+ await copyFile(join(tools.componentsDirectory, "renderer/renderer.js"), join(frameworkAssets, "renderer.js"));
162
+ await writeFile(join(frameworkAssets, "config.json"), JSON.stringify({
163
+ startPage: input.startPage,
164
+ values: input.values,
165
+ plugin: input.pluginAccess
166
+ }));
167
+ await writeFile(join(frameworkAssets, "plugins.json"), JSON.stringify({
168
+ schemaVersion: 1,
169
+ plugins: input.plugins.map(({ name, soname, allow, config, expectedFunctions }) => ({
170
+ name,
171
+ soname,
172
+ allow,
173
+ config,
174
+ ...expectedFunctions === void 0 ? {} : { expectedFunctions }
175
+ }))
176
+ }));
177
+ for (const plugin of input.plugins) for (const abi of input.abis) {
178
+ const library = plugin.libraries[abi];
179
+ if (!library) throw new Error(`${plugin.name} is missing its ${abi} library.`);
180
+ const destination = join(project, "app/src/main/jniLibs", abi, plugin.soname);
181
+ await mkdir(dirname(destination), { recursive: true });
182
+ await copyFile(library, destination);
183
+ for (const dependency of plugin.dependencies?.[abi] ?? []) await copyFile(dependency, join(dirname(destination), basename(dependency)));
184
+ }
185
+ await writeFile(join(project, "muon-build.json"), JSON.stringify({
186
+ ...tools.toolchain,
187
+ applicationId: input.applicationId,
188
+ versionCode: input.versionCode,
189
+ versionName: input.versionName,
190
+ abis: input.abis,
191
+ maven: pathToFileURL(join(tools.componentsDirectory, "maven")).href
192
+ }, null, 2));
193
+ await writeFile(join(project, "app/src/main/AndroidManifest.xml"), await render(join(project, "AndroidManifest.xml.fc"), /* @__PURE__ */ new Map([["permissions", input.permissions.map(xml)]])));
194
+ const resources = join(project, "app/src/main/res");
195
+ await mkdir(join(resources, "values"), { recursive: true });
196
+ await writeFile(join(resources, "values/strings.xml"), await render(join(project, "strings.xml.fc"), /* @__PURE__ */ new Map([["label", xml(input.label.replaceAll("\\", "\\\\").replaceAll("\"", "\\\""))]])));
197
+ if (input.icon !== void 0) {
198
+ await rm(join(resources, "mipmap/ic_launcher.xml"));
199
+ await copyFile(input.icon, join(resources, "mipmap/ic_launcher.png"));
200
+ }
201
+ await runAndroidCommand(join(project, "gradlew"), [
202
+ "--no-daemon",
203
+ "--console=plain",
204
+ input.variant === "debug" ? ":app:assembleDebug" : ":app:assembleRelease"
205
+ ], project, {
206
+ ...input.environment,
207
+ ANDROID_HOME: tools.sdkPath,
208
+ ANDROID_SDK_ROOT: tools.sdkPath
209
+ }, input.output);
210
+ const name = input.variant === "debug" ? "app-debug.apk" : "app-release-unsigned.apk";
211
+ const packagePath = join(resolve(input.outputDirectory), `${input.applicationId}-${input.versionCode}-${input.variant}.apk`);
212
+ await mkdir(dirname(packagePath), { recursive: true });
213
+ await copyFile(join(project, "app/build/outputs/apk", input.variant, name), packagePath);
214
+ const result = {
215
+ target: "android",
216
+ packagePath,
217
+ projectDirectory: project,
218
+ variant: input.variant,
219
+ abis: input.abis,
220
+ applicationId: input.applicationId,
221
+ versionCode: input.versionCode,
222
+ versionName: input.versionName,
223
+ signing: input.variant === "debug" ? "debug" : "unsigned"
224
+ };
225
+ await writeFile(`${packagePath}.json`, JSON.stringify(result, null, 2) + "\n");
226
+ return result;
227
+ };
228
+ //#endregion
229
+ export { buildAndroidApplication, prepareAndroid };
230
+
231
+ //# sourceMappingURL=index.mjs.map