diff --git a/assets/asset-tauri/template/src-tauri/Cargo.toml b/assets/asset-tauri/template/src-tauri/Cargo.toml index ecc11800..c79b041d 100644 --- a/assets/asset-tauri/template/src-tauri/Cargo.toml +++ b/assets/asset-tauri/template/src-tauri/Cargo.toml @@ -18,11 +18,11 @@ name = "tauri_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2", features = [] } +tauri-build = { version = "2.1.1", features = [] } [dependencies] -tauri = { version = "2", features = [] } -tauri-cli = "2.4.1" +tauri = { version = "2.1.1", features = [] } +tauri-cli = "2.1.1" serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -33,8 +33,8 @@ futures-util = "0.3" anyhow = "1.0" dirs = "5.0" -tauri-plugin-shell = "2" -tauri-plugin-fs = "2" -tauri-plugin-opener = "2" -tauri-plugin-devtools = "2.0.0" +tauri-plugin-shell = "2.2.1" +tauri-plugin-fs = "2.2.1" +tauri-plugin-opener = "2.2.6" +tauri-plugin-devtools = "2" tauri-plugin-localhost = "2" diff --git a/assets/asset-tauri/template/src-tauri/tauri.conf.json b/assets/asset-tauri/template/src-tauri/tauri.conf.json index 0a73f10f..8f81cecd 100644 --- a/assets/asset-tauri/template/src-tauri/tauri.conf.json +++ b/assets/asset-tauri/template/src-tauri/tauri.conf.json @@ -20,11 +20,14 @@ ], "security": { "csp": null + }, + "mobile": { + "target": "all" } }, "bundle": { "active": true, - "targets": "all", + "targets": ["desktop", "mobile"], "icon": [ "icons/32x32.png", "icons/128x128.png", diff --git a/examples/tauri-mobile-pipeline/README.md b/examples/tauri-mobile-pipeline/README.md new file mode 100644 index 00000000..0d643dcd --- /dev/null +++ b/examples/tauri-mobile-pipeline/README.md @@ -0,0 +1,49 @@ +# Tauri Mobile + Desktop Pipeline + +Builds the **same** sample web app into both a **desktop** bundle and an +**Android** build using the Pipelab Tauri plugin. One Tauri project, two +targets — the example canvas contains a desktop (`platform: linux`) package +block and a mobile (`platform: android`) package block that both consume the +same `html-export` input folder. + +## What it does + +1. `tauri:scaffold` — creates a fresh Tauri project (web frontend + `src-tauri`). +2. `tauri:package` — writes a mobile-ready `tauri.conf.json` (enables all bundle + targets) and runs the platform build: + - Desktop: `cargo tauri build` (target selected via `platform`/`arch`). + - Android: `cargo tauri android init` then `cargo tauri android build --target aarch64 --apk` + - iOS: `cargo tauri ios init` then `cargo tauri ios build --target aarch64` + +The `platform` and `arch` action inputs select the target. `platform` accepts +`win32` / `linux` / `darwin` (desktop) as well as `android` / `ios` (mobile), +and `arch` accepts `x64` / `arm64` / `ia32` / `armv7l` / `universal` / ... + +## Prerequisites + +Builds are delegated to the official Tauri CLI, which requires the native +toolchains: + +- **Desktop** — the host's system webview libraries (e.g. `webkit2gtk-4.1`, + `gtk+-3.0`, `librsvg-2.0` on Linux). See + https://tauri.app/start/prerequisites/#linux +- **Android** — Android SDK + NDK, `ANDROID_HOME` set, and the SDK packages + `platform-tools`, `build-tools`, plus a platform/NDK matching your `rustup` + targets (`aarch64-linux-android`, ...). The Pipelab plugin **self-heals** this + on first run (it reuses an existing SDK or downloads one into the shared cache + folder and adds the Rust target). See + https://tauri.app/start/prerequisites/#android +- **iOS** — macOS with Xcode and the iOS SDK. See + https://tauri.app/start/prerequisites/#ios + +## Run + +```bash +pnpm --filter @pipelab/cli start -- \ + run examples/tauri-mobile-pipeline/pipeline.json \ + --user-data /tmp/pl-userdata +``` + +On a host that only has the Rust + desktop toolchain, the Android block will +still succeed (the plugin provisions the SDK into the shared cache); the desktop +block additionally needs the webview system libraries installed. diff --git a/examples/tauri-mobile-pipeline/html-export/index.html b/examples/tauri-mobile-pipeline/html-export/index.html new file mode 100644 index 00000000..c6f0f99f --- /dev/null +++ b/examples/tauri-mobile-pipeline/html-export/index.html @@ -0,0 +1,30 @@ + + + + + + Pipelab · Mobile Demo + + + +
+

Pipelab 📱

+

This static web app is packaged into a native Android/iOS build by the + @pipelab/plugin-tauri “Package app with configuration” node.

+
+ + diff --git a/examples/tauri-mobile-pipeline/pipeline.json b/examples/tauri-mobile-pipeline/pipeline.json new file mode 100644 index 00000000..1f3f142b --- /dev/null +++ b/examples/tauri-mobile-pipeline/pipeline.json @@ -0,0 +1,98 @@ +{ + "version": "3.0.0", + "name": "Mobile + Desktop Tauri Package", + "description": "Packages the SAME sample web app into both a desktop bundle (Linux) and an Android build using @pipelab/plugin-tauri, demonstrating one Tauri project targeting mobile and desktop.", + "variables": [], + "canvas": { + "blocks": [ + { + "uid": "tauri-package-desktop", + "type": "action", + "origin": { "nodeId": "tauri:package:v2", "pluginId": "tauri" }, + "params": { + "arch": { "editor": "simple", "value": "\"x64\"" }, + "platform": { "editor": "simple", "value": "\"linux\"" }, + "input-folder": { + "editor": "simple", + "value": "\"/workspaces/pipelab/examples/tauri-mobile-pipeline/html-export\"" + }, + "name": { "editor": "simple", "value": "\"Pipelab Mobile\"" }, + "appBundleId": { "editor": "simple", "value": "\"app.pipelab.mobile\"" }, + "appCopyright": { "editor": "simple", "value": "\"Copyright © 2024 Pipelab\"" }, + "appVersion": { "editor": "simple", "value": "\"1.0.0\"" }, + "icon": { "editor": "simple", "value": "\"\"" }, + "author": { "editor": "simple", "value": "\"Pipelab\"" }, + "description": { "editor": "simple", "value": "\"A Pipelab mobile demo app\"" }, + "appCategoryType": { + "editor": "simple", + "value": "\"public.app-category.developer-tools\"" + }, + "width": { "editor": "simple", "value": 400 }, + "height": { "editor": "simple", "value": 800 }, + "fullscreen": { "editor": "simple", "value": false }, + "frame": { "editor": "simple", "value": true }, + "transparent": { "editor": "simple", "value": false }, + "toolbar": { "editor": "simple", "value": true }, + "alwaysOnTop": { "editor": "simple", "value": false }, + "tauriVersion": { "editor": "simple", "value": "\"\"" }, + "enableExtraLogging": { "editor": "simple", "value": false }, + "openDevtoolsOnStart": { "editor": "simple", "value": false }, + "websocketApi": { "editor": "simple", "value": "[]" }, + "ignore": { "editor": "editor", "value": "[\n]" }, + "enableSteamSupport": { "editor": "simple", "value": false }, + "steamGameId": { "editor": "simple", "value": 480 }, + "enableDiscordSupport": { "editor": "simple", "value": false }, + "discordAppId": { "editor": "simple", "value": "\"\"" } + } + }, + { + "uid": "tauri-package-mobile", + "type": "action", + "origin": { "nodeId": "tauri:package:v2", "pluginId": "tauri" }, + "params": { + "arch": { "editor": "simple", "value": "\"arm64\"" }, + "platform": { "editor": "simple", "value": "\"android\"" }, + "input-folder": { + "editor": "simple", + "value": "\"/workspaces/pipelab/examples/tauri-mobile-pipeline/html-export\"" + }, + "name": { "editor": "simple", "value": "\"Pipelab Mobile\"" }, + "appBundleId": { "editor": "simple", "value": "\"app.pipelab.mobile\"" }, + "appCopyright": { "editor": "simple", "value": "\"Copyright © 2024 Pipelab\"" }, + "appVersion": { "editor": "simple", "value": "\"1.0.0\"" }, + "icon": { "editor": "simple", "value": "\"\"" }, + "author": { "editor": "simple", "value": "\"Pipelab\"" }, + "description": { "editor": "simple", "value": "\"A Pipelab mobile demo app\"" }, + "appCategoryType": { + "editor": "simple", + "value": "\"public.app-category.developer-tools\"" + }, + "width": { "editor": "simple", "value": 400 }, + "height": { "editor": "simple", "value": 800 }, + "fullscreen": { "editor": "simple", "value": false }, + "frame": { "editor": "simple", "value": true }, + "transparent": { "editor": "simple", "value": false }, + "toolbar": { "editor": "simple", "value": true }, + "alwaysOnTop": { "editor": "simple", "value": false }, + "tauriVersion": { "editor": "simple", "value": "\"\"" }, + "enableExtraLogging": { "editor": "simple", "value": false }, + "openDevtoolsOnStart": { "editor": "simple", "value": false }, + "websocketApi": { "editor": "simple", "value": "[]" }, + "ignore": { "editor": "editor", "value": "[\n]" }, + "enableSteamSupport": { "editor": "simple", "value": false }, + "steamGameId": { "editor": "simple", "value": 480 }, + "enableDiscordSupport": { "editor": "simple", "value": false }, + "discordAppId": { "editor": "simple", "value": "\"\"" } + } + } + ], + "triggers": [ + { + "type": "event", + "origin": { "pluginId": "system", "nodeId": "manual" }, + "uid": "manual-start", + "params": {} + } + ] + } +} diff --git a/packages/core-node/src/utils/remote.ts b/packages/core-node/src/utils/remote.ts index e996daee..13718e30 100644 --- a/packages/core-node/src/utils/remote.ts +++ b/packages/core-node/src/utils/remote.ts @@ -563,7 +563,11 @@ export async function fetchPipelabAsset( versionOrRange: string, options: FetchOptions, ): Promise { - if (isDev && projectRoot) { + // Prefer the local monorepo asset when it exists (dev workflow). This avoids + // pulling a published template that may pin Tauri crate versions inconsistently + // with what the local scaffold expects. `PIPELAB_FORCE_NPM` can still force the + // published package (used by CI/prod). + if (projectRoot && process.env.PIPELAB_FORCE_NPM !== "true") { const assetId = packageName.replace("@pipelab/asset-", ""); const localPath = join(projectRoot, "assets", `asset-${assetId}`); if (existsSync(localPath)) return localPath; diff --git a/plugins/plugin-core/src/desktop-app.d.ts b/plugins/plugin-core/src/desktop-app.d.ts index 94549ccf..9ed2a6ee 100644 --- a/plugins/plugin-core/src/desktop-app.d.ts +++ b/plugins/plugin-core/src/desktop-app.d.ts @@ -48,5 +48,14 @@ declare namespace DesktopApp { interface Tauri extends Config { tauriVersion: string; + /** + * Optional Tauri `app.mobile` configuration (e.g. android/ios specific overrides). + * Only used when packaging for the `android` or `ios` platforms. + */ + mobile?: { + target?: "all" | "android" | "ios"; + android?: Record; + ios?: Record; + }; } } diff --git a/plugins/plugin-tauri/src/index.ts b/plugins/plugin-tauri/src/index.ts index d0981587..cf278efa 100644 --- a/plugins/plugin-tauri/src/index.ts +++ b/plugins/plugin-tauri/src/index.ts @@ -13,6 +13,13 @@ import { import { configureRunner, props } from "./configure"; import { packageV2Runner } from "./package"; +export { + ensureAndroidEnvironment, + findAndroidApk, + resolveMobileCliTarget, + resolveMobileOutputTriple, +} from "./tauri"; + export default createNodeDefinition({ nodes: [ // make and package diff --git a/plugins/plugin-tauri/src/tauri.ts b/plugins/plugin-tauri/src/tauri.ts index bdd4f07f..9d9be2cb 100644 --- a/plugins/plugin-tauri/src/tauri.ts +++ b/plugins/plugin-tauri/src/tauri.ts @@ -14,14 +14,23 @@ import { runWithLiveLogs, fetchPipelabAsset, } from "@pipelab/plugin-core"; -import { dirname, join, basename, delimiter } from "node:path"; +import { dirname, join, basename, delimiter, sep } from "node:path"; import { existsSync } from "node:fs"; -import { cp, readFile as readFilePromise, writeFile as writeFilePromise } from "node:fs/promises"; +import { readdirSync } from "node:fs"; +import { cp, mkdir, readFile as readFilePromise, writeFile as writeFilePromise } from "node:fs/promises"; import { homedir, platform as osPlatform, arch as osArch } from "node:os"; import { execa } from "execa"; import { kebabCase } from "change-case"; import { parseTOML, stringifyTOML } from "confbox"; +// Platforms this plugin can target. `NodeJS.Platform` only knows the desktop +// operating systems, so we extend it with the mobile targets Tauri supports. +export type TargetPlatform = NodeJS.Platform | "android" | "ios"; + +// Architectures this plugin can target. `NodeJS.Architecture` is missing a few +// values surfaced by the architecture selector (armv7l, universal, mips64el). +export type TargetArch = NodeJS.Architecture | "armv7l" | "universal" | "mips64el"; + /** * Searches for common cargo paths and resolves to a valid cargo executable path * @returns The path to the cargo executable @@ -94,6 +103,80 @@ async function resolveCargoPath(): Promise { throw new Error("Cargo not found. Please install it first"); } +/** + * Resolves the target token accepted by the `tauri android/ios build --target` + * flag (an Android ABI or an iOS device/simulator slice), or undefined to let + * Tauri pick its default. + */ +export function resolveMobileCliTarget( + platform: TargetPlatform, + arch: TargetArch, +): string | undefined { + if (platform === "android") { + switch (arch) { + case "arm64": + return "aarch64"; + case "x64": + return "x86_64"; + case "ia32": + return "i686"; + case "armv7l": + return "armv7"; + default: + return "aarch64"; // mobile defaults to 64-bit ARM + } + } + + if (platform === "ios") { + // iOS simulators use the `-sim` slice + if (arch === "x64" || arch === "ia32") { + return "x86_64-sim"; + } + if (arch === "arm64") { + // Apple Silicon simulators build for the `aarch64-sim` slice; + // fall back to the device slice on non-Apple hosts. + return process.platform === "darwin" ? "aarch64-sim" : "aarch64"; + } + return "aarch64"; // physical devices + } + + return undefined; +} + +/** + * Resolves the Rust target triple actually produced by the build, used to + * locate the generated artifact on disk. + */ +export function resolveMobileOutputTriple( + platform: TargetPlatform, + arch: TargetArch, +): string | undefined { + if (platform === "android") { + switch (arch) { + case "x64": + return "x86_64-linux-android"; + case "ia32": + return "i686-linux-android"; + case "armv7l": + return "armv7-linux-androideabi"; + default: + return "aarch64-linux-android"; + } + } + + if (platform === "ios") { + if (arch === "x64" || arch === "ia32") { + return "x86_64-apple-ios-sim"; + } + if (arch === "arm64") { + return "aarch64-apple-ios-sim"; + } + return "aarch64-apple-ios"; + } + + return undefined; +} + // TODO: https://js.electronforge.io/modules/_electron_forge_core.html export const IDMake = "tauri:make"; @@ -122,7 +205,7 @@ const paramsInputURL = { const params = { arch: { - value: "" as NodeJS.Architecture | "", // MakeOptions['arch'], + value: "" as TargetArch | "", // MakeOptions['arch'], label: "Architecture", required: false, control: { @@ -159,7 +242,7 @@ const params = { }, }, platform: { - value: "" as NodeJS.Platform | "", // MakeOptions['platform'], + value: "" as TargetPlatform | "", // MakeOptions['platform'], label: "Platform", required: false, control: { @@ -191,14 +274,6 @@ const params = { }, }, }, - configuration: { - label: "Tauri configuration", - value: undefined as Partial | undefined, - required: true, - control: { - type: "json", - }, - }, } satisfies InputsDefinition; export const configureParams = { @@ -392,6 +467,7 @@ export const configureParams = { label: "Discord application ID", description: "The Discord application ID", }), + } satisfies InputsDefinition; const outputs = { @@ -475,6 +551,222 @@ export const createPackageV2Props = ( }); }; +/** + * Keeps the npm `@tauri-apps/*` packages in lock-step with the Rust Tauri + * crates used by the scaffolded app. Tauri aborts the build when the major.minor + * of an npm package and its matching Rust crate diverge, but the two ecosystems + * float independently (e.g. `^2` on npm vs `"2"` in Cargo.toml can resolve to + * different minors depending on what each registry currently serves). Reading the + * actual resolved crate versions from `cargo metadata` and pinning the npm + * packages to them guarantees a match. + */ +const TAURI_NPM_MAP: Record = { + tauri: "@tauri-apps/api", + "tauri-plugin-shell": "@tauri-apps/plugin-shell", + "tauri-plugin-fs": "@tauri-apps/plugin-fs", + "tauri-plugin-opener": "@tauri-apps/plugin-opener", + "tauri-plugin-devtools": "@tauri-apps/plugin-devtools", + "tauri-plugin-localhost": "@tauri-apps/plugin-localhost", +}; + +async function alignTauriNpmVersions( + destinationFolder: string, + cargo: string, + node: string, + log: (message: string) => void, +): Promise { + const srcTauri = join(destinationFolder, "src-tauri"); + try { + const { stdout } = await execa(cargo, ["metadata", "--format-version", "1"], { + cwd: srcTauri, + env: { + ...process.env, + PATH: `${dirname(cargo)}${delimiter}${dirname(node)}${delimiter}${process.env.PATH}`, + }, + }); + const meta = JSON.parse(stdout) as { packages?: { name: string; version: string }[] }; + const versions: Record = {}; + for (const p of meta.packages ?? []) versions[p.name] = p.version; + + const pkgPath = join(destinationFolder, "package.json"); + const pkg = JSON.parse(await readFilePromise(pkgPath, "utf8")) as { + dependencies?: Record; + devDependencies?: Record; + }; + + let changed = false; + for (const [crate, npm] of Object.entries(TAURI_NPM_MAP)) { + const version = versions[crate]; + if (!version) continue; + if (pkg.dependencies?.[npm]) { + pkg.dependencies[npm] = version; + changed = true; + } + if (pkg.devDependencies?.[npm]) { + pkg.devDependencies[npm] = version; + changed = true; + } + } + + if (changed) { + log("Aligning @tauri-apps npm versions to resolved Rust crate versions"); + await writeFilePromise(pkgPath, JSON.stringify(pkg, null, 2)); + } + } catch (error) { + log(`Skipping tauri npm version alignment: ${String(error)}`); + } +} + +const ANDROID_CMDLINE_TOOLS_URL = + "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"; + +/** + * Recursively finds the produced Android APK under the Gradle outputs folder. + * Tauri may name/locate it differently (e.g. `app-universal-release-unsigned.apk` + * inside a `universal/release` subfolder) depending on the build target, so we + * search for it instead of hard-coding a single path. + */ +export function findAndroidApk(root: string): string | undefined { + if (!existsSync(root)) return undefined; + let releaseApk: string | undefined; + const walk = (dir: string): void => { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const p = join(dir, entry.name); + if (entry.isDirectory()) { + walk(p); + } else if (entry.name.endsWith(".apk")) { + const isRelease = p.split(sep).includes("release"); + if (isRelease && !releaseApk) { + releaseApk = p; + } else if (!releaseApk) { + releaseApk = p; + } + } + } + }; + walk(root); + return releaseApk; +} + +/** + * Looks for an already-installed Android SDK on the host so we can reuse it + * instead of downloading one. Checks `ANDROID_HOME`, the conventional + * `~/Android/Sdk` location, common version managers (mise/asdf) and `/opt`. + */ +function findExistingAndroidSdk(): string | undefined { + const candidates: string[] = []; + if (process.env.ANDROID_HOME) candidates.push(process.env.ANDROID_HOME); + candidates.push(join(homedir(), "Android", "Sdk")); + const miseBase = join(homedir(), ".local", "share", "mise", "installs", "android-sdk"); + if (existsSync(miseBase)) { + try { + for (const entry of readdirSync(miseBase)) candidates.push(join(miseBase, entry)); + } catch { + /* ignore */ + } + } + candidates.push("/opt/android-sdk", "/usr/lib/android-sdk"); + for (const candidate of candidates) { + if ( + existsSync(candidate) && + (existsSync(join(candidate, "cmdline-tools")) || + existsSync(join(candidate, "platform-tools")) || + existsSync(join(candidate, "ndk"))) + ) { + return candidate; + } + } + return undefined; +} + +/** + * Downloads the Android command-line tools and uses `sdkmanager` to install the + * NDK, build-tools, platform and platform-tools required to build a Tauri app. + */ +async function provisionAndroidSdk( + androidHome: string, + log: (message: string) => void, + abortSignal?: AbortSignal, +): Promise { + await mkdir(join(androidHome, "cmdline-tools"), { recursive: true }); + const zipPath = join(androidHome, "commandlinetools.zip"); + log(`Downloading Android command-line tools from ${ANDROID_CMDLINE_TOOLS_URL}`); + await execa("curl", ["-sL", ANDROID_CMDLINE_TOOLS_URL, "-o", zipPath], { + cancelSignal: abortSignal as any, + }); + const extractDir = join(androidHome, "cmdline-tools-extract"); + await execa("unzip", ["-q", "-o", zipPath, "-d", extractDir], { + cancelSignal: abortSignal as any, + }); + // The archive extracts to a `cmdline-tools/` folder; Tauri expects it at + // `cmdline-tools/latest`. + const extracted = join(extractDir, "cmdline-tools"); + const target = join(androidHome, "cmdline-tools", "latest"); + await execa("rm", ["-rf", target], { cancelSignal: abortSignal as any }); + await execa("mv", [extracted, target], { cancelSignal: abortSignal as any }); + await execa("rm", ["-rf", extractDir, zipPath], { cancelSignal: abortSignal as any }); + + const sdkmanager = join(target, "bin", "sdkmanager"); + const env = { ...process.env, ANDROID_HOME: androidHome }; + log("Accepting Android SDK licenses"); + await execa("bash", ["-c", `yes | "${sdkmanager}" --licenses`], { + env, + cancelSignal: abortSignal as any, + }); + log("Installing Android SDK packages (NDK, build-tools, platform, platform-tools)"); + await execa( + "bash", + [ + "-c", + `"${sdkmanager}" --install "platform-tools" "platforms;android-34" "build-tools;34.0.0" "ndk;26.1.10909125"`, + ], + { env, cancelSignal: abortSignal as any }, + ); +} + +/** + * Self-heals the Android toolchain so a pipeline can build for Android on a host + * that ships without the Android SDK pre-installed (only node/pnpm + a Rust + * toolchain are assumed). It reuses an existing SDK when present and otherwise + * downloads the command-line tools and provisions the NDK/build-tools/platform + * into a cache directory, then exposes it via `ANDROID_HOME`. + */ +export async function ensureAndroidEnvironment( + cacheDir: string, + cargoBinDir: string, + node: string, + log: (message: string) => void, + abortSignal?: AbortSignal, +): Promise { + const existing = findExistingAndroidSdk(); + const androidHome = + existing ?? join(cacheDir, "android-sdk"); + + if (existing) { + log(`Reusing existing Android SDK at ${androidHome}`); + } else { + log(`No Android SDK found; provisioning one at ${androidHome}`); + await provisionAndroidSdk(androidHome, log, abortSignal); + } + + log(`Ensuring Rust target aarch64-linux-android for Android builds`); + try { + await execa("rustup", ["target", "add", "aarch64-linux-android"], { + env: { + ...process.env, + PATH: `${cargoBinDir}${delimiter}${dirname(node)}${delimiter}${process.env.PATH}`, + }, + stdout: "ignore", + stderr: "ignore", + }); + } catch (error) { + log(`rustup target add aarch64-linux-android failed (continuing): ${String(error)}`); + } + + process.env.ANDROID_HOME = androidHome; + return androidHome; +} + export const createPreviewProps = ( id: string, name: string, @@ -499,7 +791,7 @@ export const createPreviewProps = ( export const tauri = async ( action: "make" | "package" | "preview", appFolder: string | undefined, - { cwd, log, inputs, setOutput, paths, abortSignal, context }: ActionRunnerData, + { cwd, log, inputs, setOutput, paths, abortSignal, context, setArtifact }: ActionRunnerData, completeConfiguration: DesktopApp.Config, ): Promise<{ folder: string; binary: string | undefined } | undefined> => { console.log("appFolder", appFolder); @@ -583,16 +875,41 @@ export const tauri = async ( tauriConfJSON.version = completeConfiguration.appVersion; log("Setting identifier to", completeConfiguration.appBundleId); tauriConfJSON.identifier = completeConfiguration.appBundleId; + + const isAndroid = inputs.platform === "android"; + const isIOS = inputs.platform === "ios"; + const isMobile = isAndroid || isIOS; + + if (isMobile) { + tauriConfJSON.bundle = tauriConfJSON.bundle || {}; + // Mobile artifacts (APK/AAB/IPA) are produced by `tauri android/ios build` + // itself; the `bundle.targets` enum only knows desktop formats, so we use + // "all" (the schema-valid catch-all) and let the platform drive the output. + tauriConfJSON.bundle.targets = "all"; + // `app.mobile` is optional and only understood by newer Tauri versions. + // It is intentionally omitted here so the generated config validates + // against every Tauri 2.x release; mobile-specific settings from + // `completeConfiguration.mobile` can be applied to the generated + // android/ios project after `tauri init` when desired. + if (tauriConfJSON.app) { + delete (tauriConfJSON.app as Record).mobile; + } + log("Setting mobile bundle target to", tauriConfJSON.bundle.targets); + } + if (action === "preview") { log("Setting build.devUrl to", appFolder); tauriConfJSON.build.devUrl = appFolder; - await writeFilePromise(tauriConfJSONPath, JSON.stringify(tauriConfJSON, null, 2)); + } else { + log("Setting build.frontendDist to ../src/app"); + tauriConfJSON.build.frontendDist = "../src/app"; } - /* else { - log('Setting build.frontendDist to', appFolder) - tauriConfJSON.build.frontendDist = appFolder - await writeFile(tauriConfJSONPath, JSON.stringify(tauriConfJSON, null, 2)) - } */ + await writeFilePromise(tauriConfJSONPath, JSON.stringify(tauriConfJSON, null, 2)); + + // Pin the npm @tauri-apps packages to the Rust crate versions so the Tauri + // CLI version check passes (see alignTauriNpmVersions). + const cargoForAlign = await resolveCargoPath(); + await alignTauriNpmVersions(destinationFolder, cargoForAlign, node, log); log("Installing packages"); const { all } = await runPnpm(destinationFolder, { @@ -628,88 +945,39 @@ export const tauri = async ( // ) // } - const inputPlatform = inputs.platform === "" ? undefined : inputs.platform; - const inputArch = inputs.arch === "" ? undefined : inputs.arch; - - try { - log("typeof inputs.platform", typeof inputs.platform); - const finalPlatform: NodeJS.Platform = inputPlatform ?? osPlatform(); - log("finalPlatform", finalPlatform); - const finalArch: NodeJS.Architecture = inputArch ?? (osArch() as NodeJS.Architecture); - log("finalArch", finalArch); - - let tauriPlatform = ""; - if (finalPlatform === "win32") { - tauriPlatform = "pc-windows-msvc"; - } else if (finalPlatform === "linux") { - tauriPlatform = "unknown-linux-gnu"; - } else { - throw new Error("Unsupported platform"); - } - - let tauriArch = ""; - if (finalArch === "x64") { - tauriArch = "x86_64"; - } else { - throw new Error("Unsupported arch"); - } + const inputPlatform = inputs.platform === "" ? undefined : inputs.platform; + const inputArch = inputs.arch === "" ? undefined : inputs.arch; - const target = `${tauriArch}-${tauriPlatform}`; + try { + log("typeof inputs.platform", typeof inputs.platform); + const finalPlatform: TargetPlatform = inputPlatform ?? osPlatform(); + log("finalPlatform", finalPlatform); + const finalArch: TargetArch = inputArch ?? osArch(); + log("finalArch", finalArch); - // Resolve cargo path using the new function - const cargo = await resolveCargoPath(); - const cargoBinDir = dirname(cargo); + const isAndroidBuild = finalPlatform === "android"; + const isIOSBuild = finalPlatform === "ios"; + const isMobileBuild = isAndroidBuild || isIOSBuild; - log("cargoBinDir", cargoBinDir); - console.log("cargo", cargo); + // Resolve cargo path using the new function + const cargo = await resolveCargoPath(); + const cargoBinDir = dirname(cargo); - log("destinationFolder", destinationFolder); + log("cargoBinDir", cargoBinDir); + console.log("cargo", cargo); - const cargoTargetDir = join(cache, "cargo", "target", completeConfiguration.appBundleId); - const cargoOutputPath = join(cargoTargetDir, target, "release"); + log("destinationFolder", destinationFolder); - log("cargoTargetDir", cargoTargetDir); - log("cargoOutputPath", cargoOutputPath); + const cargoTargetDir = join(cache, "cargo", "target", completeConfiguration.appBundleId); - log("Starting compiling"); + log("cargoTargetDir", cargoTargetDir); - // by default add the tauri cli - await runWithLiveLogs( - cargo, - ["install", "tauri-cli", "--version", "^2.0.0", "--locked"], - { - cwd: join(destinationFolder, "src-tauri"), - env: { - ...process.env, - DEBUG: completeConfiguration.enableExtraLogging ? "*" : "", - ELECTRON_NO_ASAR: "1", - CARGO_TARGET_DIR: cargoTargetDir, - PATH: `${cargoBinDir}${delimiter}${dirname(node)}${delimiter}${process.env.PATH}`, - }, - cancelSignal: abortSignal, - }, - log, - { - onStderr(data) { - // on ci, do not log - if (!process.env.CI) { - log(data); - } - }, - onStdout(data) { - // on ci, do not log - if (!process.env.CI) { - log(data); - } - }, - }, - ); + log("Starting compiling"); - // if preview, run tauri dev - if (action === "preview") { + // by default add the tauri cli await runWithLiveLogs( cargo, - ["tauri", "dev", "--target", target], + ["install", "tauri-cli", "--version", "^2.0.0", "--locked"], { cwd: join(destinationFolder, "src-tauri"), env: { @@ -724,19 +992,89 @@ export const tauri = async ( log, { onStderr(data) { - log(data); + // on ci, do not log + if (!process.env.CI) { + log(data); + } }, onStdout(data) { - log(data); + // on ci, do not log + if (!process.env.CI) { + log(data); + } }, }, ); - } else { - // otherwise build, but don't bundle - await runWithLiveLogs( - cargo, - ["tauri", "build", "--target", target, "--no-bundle"], - { + + // --------------------------------------------------------------------------- + // Mobile build path (android / ios) + // --------------------------------------------------------------------------- + if (isMobileBuild) { + // Self-heal the Android toolchain (SDK/NDK + Rust target) when building + // for Android on a host that doesn't have them installed yet. + if (isAndroidBuild) { + // Store the SDK in a pipeline-independent location (top-level cache, + // alongside `pacote`) so it is shared by every Android pipeline instead + // of being re-downloaded (several GB) per pipeline. It is also + // regenerable, so it belongs in the cache folder (which is relocatable + // via settings.cacheFolder). + const androidHome = await ensureAndroidEnvironment( + context.getCachePath(), + cargoBinDir, + node, + log, + abortSignal, + ); + // Make sure the SDK tooling is on PATH for the Tauri CLI / Gradle. + process.env.PATH = `${join(androidHome, "cmdline-tools", "latest", "bin")}${delimiter}${join( + androidHome, + "platform-tools", + )}${delimiter}${process.env.PATH}`; + log(`Android SDK ready at ANDROID_HOME=${androidHome}`); + } + + const cliTarget = resolveMobileCliTarget(finalPlatform, finalArch); + const outputTriple = resolveMobileOutputTriple(finalPlatform, finalArch); + const mobileArgs = ["tauri", isAndroidBuild ? "android" : "ios", "build"]; + if (cliTarget) { + mobileArgs.push("--target", cliTarget); + } + if (isAndroidBuild) { + // Produce a standalone APK in addition to the default AAB + mobileArgs.push("--apk"); + } + + log(`Building for ${isAndroidBuild ? "android" : "ios"} (target: ${cliTarget ?? "default"})`); + + // `tauri init` generates the native project + // (src-tauri/gen/android|ios) required before the first build. + log(`Initializing ${isAndroidBuild ? "android" : "ios"} project`); + await runWithLiveLogs( + cargo, + ["tauri", isAndroidBuild ? "android" : "ios", "init"], + { + cwd: join(destinationFolder, "src-tauri"), + env: { + ...process.env, + DEBUG: completeConfiguration.enableExtraLogging ? "*" : "", + ELECTRON_NO_ASAR: "1", + CARGO_TARGET_DIR: cargoTargetDir, + PATH: `${cargoBinDir}${delimiter}${dirname(node)}${delimiter}${process.env.PATH}`, + }, + cancelSignal: abortSignal, + }, + log, + { + onStderr(data) { + if (!process.env.CI) log(data); + }, + onStdout(data) { + if (!process.env.CI) log(data); + }, + }, + ); + + await runWithLiveLogs(cargo, mobileArgs, { cwd: join(destinationFolder, "src-tauri"), env: { ...process.env, @@ -746,33 +1084,100 @@ export const tauri = async ( PATH: `${cargoBinDir}${delimiter}${dirname(node)}${delimiter}${process.env.PATH}`, }, cancelSignal: abortSignal, - }, - log, - { + }, log, { onStderr(data) { - // on ci, do not log - if (!process.env.CI) { - log(data); - } + if (!process.env.CI) log(data); }, onStdout(data) { - // on ci, do not log - if (!process.env.CI) { - log(data); - } + if (!process.env.CI) log(data); }, - }, - ); + }); + + if (isAndroidBuild) { + const apkRoot = join( + destinationFolder, + "src-tauri", + "gen", + "android", + "app", + "build", + "outputs", + "apk", + ); + const apkPath = findAndroidApk(apkRoot) ?? join(apkRoot, "release", "app-release.apk"); + const outFolder = dirname(apkPath); + const binName = basename(apkPath); + log("Android APK output", apkPath); + setOutput("output", outFolder); + setOutput("binary", apkPath); + // Persist the APK to the pipeline's artifacts folder so it survives + // the build sandbox cleanup. The runner copies it to + // getArtifactsPath(pipelineId, buildId)/. + setArtifact(binName, apkPath); + return { folder: outFolder, binary: apkPath }; + } + + // iOS + const triple = outputTriple || "aarch64-apple-ios"; + const outFolder = join(destinationFolder, "src-tauri", "target", triple, "release"); + const binName = getBinName(sanitizedName, "ios"); + const binPath = join(outFolder, binName); + log("iOS output", binPath); + setOutput("output", outFolder); + setOutput("binary", binPath); + setArtifact(binName, binPath); + return { folder: outFolder, binary: binPath }; + } - // if make, bundle - if (action === "make") { + // --------------------------------------------------------------------------- + // Desktop build path (win32 / linux / darwin) + // --------------------------------------------------------------------------- + let tauriPlatform = ""; + if (finalPlatform === "win32") { + tauriPlatform = "pc-windows-msvc"; + } else if (finalPlatform === "linux") { + tauriPlatform = "unknown-linux-gnu"; + } else if (finalPlatform === "darwin") { + tauriPlatform = "apple-darwin"; + } else { + throw new Error(`Unsupported platform: ${finalPlatform}`); + } + + let target: string; + if (finalArch === "universal") { + if (finalPlatform !== "darwin") { + throw new Error("Universal architecture is only supported on macOS"); + } + target = "universal-apple-darwin"; + } else { + let tauriArch = ""; + if (finalArch === "x64") { + tauriArch = "x86_64"; + } else if (finalArch === "arm64") { + tauriArch = "aarch64"; + } else if (finalArch === "ia32") { + tauriArch = "i686"; + } else if (finalArch === "armv7l") { + tauriArch = "armv7"; + } else { + throw new Error(`Unsupported arch: ${finalArch}`); + } + target = `${tauriArch}-${tauriPlatform}`; + } + + const cargoOutputPath = join(cargoTargetDir, target, "release"); + + log("cargoOutputPath", cargoOutputPath); + + // if preview, run tauri dev + if (action === "preview") { await runWithLiveLogs( cargo, - // TODO: https://v2.tauri.app/fr/distribute/#bundling - ["tauri", "bundle", "--", "--bundles", "appimage"], + ["tauri", "dev", "--target", target], { cwd: join(destinationFolder, "src-tauri"), env: { + ...process.env, DEBUG: completeConfiguration.enableExtraLogging ? "*" : "", ELECTRON_NO_ASAR: "1", CARGO_TARGET_DIR: cargoTargetDir, @@ -790,35 +1195,94 @@ export const tauri = async ( }, }, ); + } else { + // otherwise build, but don't bundle + await runWithLiveLogs( + cargo, + ["tauri", "build", "--target", target, "--no-bundle"], + { + cwd: join(destinationFolder, "src-tauri"), + env: { + ...process.env, + DEBUG: completeConfiguration.enableExtraLogging ? "*" : "", + ELECTRON_NO_ASAR: "1", + CARGO_TARGET_DIR: cargoTargetDir, + PATH: `${cargoBinDir}${delimiter}${dirname(node)}${delimiter}${process.env.PATH}`, + }, + cancelSignal: abortSignal, + }, + log, + { + onStderr(data) { + // on ci, do not log + if (!process.env.CI) { + log(data); + } + }, + onStdout(data) { + // on ci, do not log + if (!process.env.CI) { + log(data); + } + }, + }, + ); + + // if make, bundle + if (action === "make") { + await runWithLiveLogs( + cargo, + // TODO: https://v2.tauri.app/fr/distribute/#bundling + ["tauri", "bundle", "--", "--bundles", "appimage"], + { + cwd: join(destinationFolder, "src-tauri"), + env: { + DEBUG: completeConfiguration.enableExtraLogging ? "*" : "", + ELECTRON_NO_ASAR: "1", + CARGO_TARGET_DIR: cargoTargetDir, + PATH: `${cargoBinDir}${delimiter}${dirname(node)}${delimiter}${process.env.PATH}`, + }, + cancelSignal: abortSignal, + }, + log, + { + onStderr(data) { + log(data); + }, + onStdout(data) { + log(data); + }, + }, + ); + } } - } - if (action === "package") { - const binName = getBinName(sanitizedName); + if (action === "package") { + const binName = getBinName(sanitizedName); - log("cargoOutputPath", cargoOutputPath); + log("cargoOutputPath", cargoOutputPath); - setOutput("output", cargoOutputPath); - setOutput("binary", join(cargoOutputPath, binName)); - return { - folder: cargoOutputPath, - binary: join(cargoOutputPath, binName), - }; - } else if (action === "make") { - // TODO: - throw new Error("Unsupported action"); - } else if (action === "preview") { - // continue - } else { - throw new Error("Unsupported action"); - // const output = join(destinationFolder, 'out', 'make') - // setOutput('output', output) - // return { - // folder: output, - // binary: undefined - // } - } - } catch (e) { + setOutput("output", cargoOutputPath); + setOutput("binary", join(cargoOutputPath, binName)); + return { + folder: cargoOutputPath, + binary: join(cargoOutputPath, binName), + }; + } else if (action === "make") { + // TODO: + throw new Error("Unsupported action"); + } else if (action === "preview") { + // continue + } else { + throw new Error("Unsupported action"); + // const output = join(destinationFolder, 'out', 'make') + // setOutput('output', output) + // return { + // folder: output, + // binary: undefined + // } + } + } catch (e) { if (e instanceof Error) { if (e.name === "RequestError") { log("Request error"); diff --git a/plugins/plugin-tauri/src/utils.ts b/plugins/plugin-tauri/src/utils.ts index 9520f0c2..2667758c 100644 --- a/plugins/plugin-tauri/src/utils.ts +++ b/plugins/plugin-tauri/src/utils.ts @@ -26,4 +26,5 @@ export const defaultTauriConfig = { openDevtoolsOnStart: false, enableDoctor: false, serverMode: "default" as const, + mobile: {}, } satisfies DesktopApp.Tauri; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75176761..3039e287 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -235,6 +235,31 @@ importers: specifier: ^3.2.8 version: 3.3.1(typescript@6.0.3) + apps/mobile: + dependencies: + '@pipelab/constants': + specifier: workspace:* + version: link:../../packages/constants + '@pipelab/shared': + specifier: workspace:* + version: link:../../packages/shared + devDependencies: + '@tauri-apps/cli': + specifier: 2.4.1 + version: 2.4.1 + '@types/node': + specifier: 24.12.2 + version: 24.12.2 + rimraf: + specifier: 6.0.1 + version: 6.0.1 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: 6.3.3 + version: 6.3.3(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(tsx@4.19.1) + apps/ui: dependencies: '@codemirror/autocomplete': @@ -2138,105 +2163,89 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -2377,6 +2386,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -2611,28 +2624,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@next/swc-linux-arm64-musl@16.2.3': resolution: {integrity: sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@next/swc-linux-x64-gnu@16.2.3': resolution: {integrity: sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@next/swc-linux-x64-musl@16.2.3': resolution: {integrity: sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@next/swc-win32-arm64-msvc@16.2.3': resolution: {integrity: sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==} @@ -2841,56 +2850,48 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-arm64-musl@0.40.0': resolution: {integrity: sha512-tGmWhLD/0YMotCdfezlT6tC/MJG/wKpo4vnQ3Cq+4eBk/BwNv7EmkD0VkD5F/dYkT3b8FNU01X2e8vvJuWoM1w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@oxfmt/binding-linux-ppc64-gnu@0.40.0': resolution: {integrity: sha512-rVbFyM3e7YhkVnp0IVYjaSHfrBWcTRWb60LEcdNAJcE2mbhTpbqKufx0FrhWfoxOrW/+7UJonAOShoFFLigDqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-riscv64-gnu@0.40.0': resolution: {integrity: sha512-3ZqBw14JtWeEoLiioJcXSJz8RQyPE+3jLARnYM1HdPzZG4vk+Ua8CUupt2+d+vSAvMyaQBTN2dZK+kbBS/j5mA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-riscv64-musl@0.40.0': resolution: {integrity: sha512-JJ4PPSdcbGBjPvb+O7xYm2FmAsKCyuEMYhqatBAHMp/6TA6rVlf9Z/sYPa4/3Bommb+8nndm15SPFRHEPU5qFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [musl] '@oxfmt/binding-linux-s390x-gnu@0.40.0': resolution: {integrity: sha512-Kp0zNJoX9Ik77wUya2tpBY3W9f40VUoMQLWVaob5SgCrblH/t2xr/9B2bWHfs0WCefuGmqXcB+t0Lq77sbBmZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-x64-gnu@0.40.0': resolution: {integrity: sha512-7YTCNzleWTaQTqNGUNQ66qVjpoV6DjbCOea+RnpMBly2bpzrI/uu7Rr+2zcgRfNxyjXaFTVQKaRKjqVdeUfeVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@oxfmt/binding-linux-x64-musl@0.40.0': resolution: {integrity: sha512-hWnSzJ0oegeOwfOEeejYXfBqmnRGHusgtHfCPzmvJvHTwy1s3Neo59UKc1CmpE3zxvrCzJoVHos0rr97GHMNPw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@oxfmt/binding-openharmony-arm64@0.40.0': resolution: {integrity: sha512-28sJC1lR4qtBJGzSRRbPnSW3GxU2+4YyQFE6rCmsUYqZ5XYH8jg0/w+CvEzQ8TuAQz5zLkcA25nFQGwoU0PT3Q==} @@ -2963,56 +2964,48 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-arm64-musl@1.55.0': resolution: {integrity: sha512-/kp65avi6zZfqEng56TTuhiy3P/3pgklKIdf38yvYeJ9/PgEeRA2A2AqKAKbZBNAqUzrzHhz9jF6j/PZvhJzTQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@oxlint/binding-linux-ppc64-gnu@1.55.0': resolution: {integrity: sha512-A6pTdXwcEEwL/nmz0eUJ6WxmxcoIS+97GbH96gikAyre3s5deC7sts38ZVVowjS2QQFuSWkpA4ZmQC0jZSNvJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-riscv64-gnu@1.55.0': resolution: {integrity: sha512-clj0lnIN+V52G9tdtZl0LbdTSurnZ1NZj92Je5X4lC7gP5jiCSW+Y/oiDiSauBAD4wrHt2S7nN3pA0zfKYK/6Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-riscv64-musl@1.55.0': resolution: {integrity: sha512-NNu08pllN5x/O94/sgR3DA8lbrGBnTHsINZZR0hcav1sj79ksTiKKm1mRzvZvacwQ0hUnGinFo+JO75ok2PxYg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] - libc: [musl] '@oxlint/binding-linux-s390x-gnu@1.55.0': resolution: {integrity: sha512-BvfQz3PRlWZRoEZ17dZCqgQsMRdpzGZomJkVATwCIGhHVVeHJMQdmdXPSjcT1DCNUrOjXnVyj1RGDj5+/Je2+Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@oxlint/binding-linux-x64-gnu@1.55.0': resolution: {integrity: sha512-ngSOoFCSBMKVQd24H8zkbcBNc7EHhjnF1sv3mC9NNXQ/4rRjI/4Dj9+9XoDZeFEkF1SX1COSBXF1b2Pr9rqdEw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@oxlint/binding-linux-x64-musl@1.55.0': resolution: {integrity: sha512-BDpP7W8GlaG7BR6QjGZAleYzxoyKc/D24spZIF2mB3XsfALQJJT/OBmP8YpeTb1rveFSBHzl8T7l0aqwkWNdGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@oxlint/binding-openharmony-arm64@1.55.0': resolution: {integrity: sha512-PS6GFvmde/pc3fCA2Srt51glr8Lcxhpf6WIBFfLphndjRrD34NEcses4TSxQrEcxYo6qVywGfylM0ZhSCF2gGA==} @@ -3067,42 +3060,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.6': resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.6': resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.6': resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.6': resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.6': resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.6': resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} @@ -3336,126 +3323,108 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': resolution: {integrity: sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-gnu@1.0.2': resolution: {integrity: sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15': resolution: {integrity: sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': resolution: {integrity: sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-arm64-musl@1.0.2': resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15': resolution: {integrity: sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': resolution: {integrity: sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-ppc64-gnu@1.0.2': resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15': resolution: {integrity: sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': resolution: {integrity: sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.2': resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15': resolution: {integrity: sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': resolution: {integrity: sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.2': resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-rc.15': resolution: {integrity: sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': resolution: {integrity: sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-linux-x64-musl@1.0.2': resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-rc.15': resolution: {integrity: sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==} @@ -3584,79 +3553,66 @@ packages: resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.60.1': resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.60.1': resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.60.1': resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.60.1': resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.60.1': resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.60.1': resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.60.1': resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.60.1': resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.60.1': resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.60.1': resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.60.1': resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.60.1': resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.60.1': resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} @@ -3757,6 +3713,77 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} + '@tauri-apps/cli-darwin-arm64@2.4.1': + resolution: {integrity: sha512-QME7s8XQwy3LWClTVlIlwXVSLKkeJ/z88pr917Mtn9spYOjnBfsgHAgGdmpWD3NfJxjg7CtLbhH49DxoFL+hLg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tauri-apps/cli-darwin-x64@2.4.1': + resolution: {integrity: sha512-/r89IcW6Ya1sEsFUEH7wLNruDTj7WmDWKGpPy7gATFtQr5JEY4heernqE82isjTUimnHZD8SCr0jA3NceI4ybw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tauri-apps/cli-linux-arm-gnueabihf@2.4.1': + resolution: {integrity: sha512-9tDijkRB+CchAGjXxYdY9l/XzFpLp1yihUtGXJz9eh+3qIoRI043n3e+6xmU8ZURr7XPnu+R4sCmXs6HD+NCEQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tauri-apps/cli-linux-arm64-gnu@2.4.1': + resolution: {integrity: sha512-pnFGDEXBAzS4iDYAVxTRhAzNu3K2XPGflYyBc0czfHDBXopqRgMyj5Q9Wj7HAwv6cM8BqzXINxnb2ZJFGmbSgA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-arm64-musl@2.4.1': + resolution: {integrity: sha512-Hp0zXgeZNKmT+eoJSCxSBUm2QndNuRxR55tmIeNm3vbyUMJN/49uW7nurZ5fBPsacN4Pzwlx1dIMK+Gnr9A69w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-riscv64-gnu@2.4.1': + resolution: {integrity: sha512-3T3bo2E4fdYRvzcXheWUeQOVB+LunEEi92iPRgOyuSVexVE4cmHYl+MPJF+EUV28Et0hIVTsHibmDO0/04lAFg==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@tauri-apps/cli-linux-x64-gnu@2.4.1': + resolution: {integrity: sha512-kLN0FdNONO+2i+OpU9+mm6oTGufRC00e197TtwjpC0N6K2K8130w7Q3FeODIM2CMyg0ov3tH+QWqKW7GNhHFzg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-linux-x64-musl@2.4.1': + resolution: {integrity: sha512-a8exvA5Ub9eg66a6hsMQKJIkf63QAf9OdiuFKOsEnKZkNN2x0NLgfvEcqdw88VY0UMs9dBoZ1AGbWMeYnLrLwQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-win32-arm64-msvc@2.4.1': + resolution: {integrity: sha512-4JFrslsMCJQG1c573T9uqQSAbF3j/tMKkMWzsIssv8jvPiP++OG61A2/F+y9te9/Q/O95cKhDK63kaiO5xQaeg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tauri-apps/cli-win32-ia32-msvc@2.4.1': + resolution: {integrity: sha512-9eXfFORehYSCRwxg2KodfmX/mhr50CI7wyBYGbPLePCjr5z0jK/9IyW6r0tC+ZVjwpX48dkk7hKiUgI25jHjzA==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@tauri-apps/cli-win32-x64-msvc@2.4.1': + resolution: {integrity: sha512-60a4Ov7Jrwqz2hzDltlS7301dhSAmM9dxo+IRBD3xz7yobKrgaHXYpWvnRomYItHcDd51VaKc9292H8/eE/gsw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tauri-apps/cli@2.4.1': + resolution: {integrity: sha512-9Ta81jx9+57FhtU/mPIckDcOBtPTUdKM75t4+aA0X84b8Sclb0jy1xA8NplmcRzp2fsfIHNngU2NiRxsW5+yOQ==} + engines: {node: '>= 10'} + hasBin: true + '@tootallnate/once@2.0.0': resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -4241,6 +4268,7 @@ packages: '@xmldom/xmldom@0.8.12': resolution: {integrity: sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==} engines: {node: '>=10.0.0'} + deprecated: this version has critical issues, please update to the latest version '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -5383,6 +5411,12 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + glob@13.0.6: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} @@ -5745,6 +5779,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -5876,28 +5914,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -6969,6 +7003,11 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + roarr@2.15.4: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} @@ -7080,56 +7119,48 @@ packages: engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: glibc sass-embedded-linux-arm@1.99.0: resolution: {integrity: sha512-d4IjJZrX2+AwB2YCy1JySwdptJECNP/WfAQLUl8txI3ka8/d3TUI155GtelnoZUkio211PwIeFvvAeZ9RXPQnw==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] - libc: glibc sass-embedded-linux-musl-arm64@1.99.0: resolution: {integrity: sha512-Hi2bt/IrM5P4FBKz6EcHAlniwfpoz9mnTdvSd58y+avA3SANM76upIkAdSayA8ZGwyL3gZokru1AKDPF9lJDNw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - libc: musl sass-embedded-linux-musl-arm@1.99.0: resolution: {integrity: sha512-2gvHOupgIw3ytatXT4nFUow71LFbuOZPEwG+HUzcNQDH8ue4Ez8cr03vsv5MDv3lIjOKcXwDvWD980t18MwkoQ==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] - libc: musl sass-embedded-linux-musl-riscv64@1.99.0: resolution: {integrity: sha512-mKqGvVaJ9rHMqyZsF0kikQe4NO0f4osb67+X6nLhBiVDKvyazQHJ3zJQreNefIE36yL2sjHIclSB//MprzaQDg==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - libc: musl sass-embedded-linux-musl-x64@1.99.0: resolution: {integrity: sha512-huhgOMmOc30r7CH7qbRbT9LerSEGSnWuS4CYNOskr9BvNeQp4dIneFufNRGZ7hkOAxUM8DglxIZJN/cyAT95Ew==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: musl sass-embedded-linux-riscv64@1.99.0: resolution: {integrity: sha512-mevFPIFAVhrH90THifxLfOntFmHtcEKOcdWnep2gJ0X4DVva4AiVIRlQe/7w9JFx5+gnDRE1oaJJkzuFUuYZsA==} engines: {node: '>=14.0.0'} cpu: [riscv64] os: [linux] - libc: glibc sass-embedded-linux-x64@1.99.0: resolution: {integrity: sha512-9k7IkULqIZdCIVt4Mboryt6vN8Mjmm3EhI1P3mClU5y5i3wLK5ExC3cbVWk047KsID/fvB1RLslqghXJx5BoxA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - libc: glibc sass-embedded-unknown-all@1.99.0: resolution: {integrity: sha512-P7MxiUtL/XzGo3PX0CaB8lNNEFLQWKikPA8pbKytx9ZCLZSDkt2NJcdAbblB/sqMs4AV3EK2NadV8rI/diq3xg==} @@ -7616,6 +7647,7 @@ packages: tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} engines: {node: ^18 || >=20} + deprecated: unmaintained hasBin: true peerDependencies: typescript: ^5.0.0 @@ -9944,6 +9976,8 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/cliui@9.0.0': {} + '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.3 @@ -11058,6 +11092,53 @@ snapshots: dependencies: defer-to-connect: 2.0.1 + '@tauri-apps/cli-darwin-arm64@2.4.1': + optional: true + + '@tauri-apps/cli-darwin-x64@2.4.1': + optional: true + + '@tauri-apps/cli-linux-arm-gnueabihf@2.4.1': + optional: true + + '@tauri-apps/cli-linux-arm64-gnu@2.4.1': + optional: true + + '@tauri-apps/cli-linux-arm64-musl@2.4.1': + optional: true + + '@tauri-apps/cli-linux-riscv64-gnu@2.4.1': + optional: true + + '@tauri-apps/cli-linux-x64-gnu@2.4.1': + optional: true + + '@tauri-apps/cli-linux-x64-musl@2.4.1': + optional: true + + '@tauri-apps/cli-win32-arm64-msvc@2.4.1': + optional: true + + '@tauri-apps/cli-win32-ia32-msvc@2.4.1': + optional: true + + '@tauri-apps/cli-win32-x64-msvc@2.4.1': + optional: true + + '@tauri-apps/cli@2.4.1': + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 2.4.1 + '@tauri-apps/cli-darwin-x64': 2.4.1 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.4.1 + '@tauri-apps/cli-linux-arm64-gnu': 2.4.1 + '@tauri-apps/cli-linux-arm64-musl': 2.4.1 + '@tauri-apps/cli-linux-riscv64-gnu': 2.4.1 + '@tauri-apps/cli-linux-x64-gnu': 2.4.1 + '@tauri-apps/cli-linux-x64-musl': 2.4.1 + '@tauri-apps/cli-win32-arm64-msvc': 2.4.1 + '@tauri-apps/cli-win32-ia32-msvc': 2.4.1 + '@tauri-apps/cli-win32-x64-msvc': 2.4.1 + '@tootallnate/once@2.0.0': {} '@trpc/client@10.45.2(@trpc/server@10.45.2)': @@ -11664,7 +11745,7 @@ snapshots: '@vue/shared': 3.5.13 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.9 + postcss: 8.5.15 source-map-js: 1.2.1 '@vue/compiler-sfc@3.5.32': @@ -11676,7 +11757,7 @@ snapshots: '@vue/shared': 3.5.32 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.9 + postcss: 8.5.15 source-map-js: 1.2.1 '@vue/compiler-sfc@3.5.34': @@ -13192,6 +13273,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.2.3 + minimatch: 10.2.5 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.2 + glob@13.0.6: dependencies: minimatch: 10.2.5 @@ -13522,6 +13612,10 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.2.3: + dependencies: + '@isaacs/cliui': 9.0.0 + jest-worker@27.5.1: dependencies: '@types/node': 24.12.4 @@ -14803,6 +14897,11 @@ snapshots: dependencies: glob: 7.2.3 + rimraf@6.0.1: + dependencies: + glob: 11.1.0 + package-json-from-dist: 1.0.1 + roarr@2.15.4: dependencies: boolean: 3.2.0 @@ -15898,7 +15997,7 @@ snapshots: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.9 + postcss: 8.5.15 rollup: 4.60.1 tinyglobby: 0.2.16 optionalDependencies: