import { getVersion } from "@tauri-apps/api/app"; // 可选导入:在未注册插件或非 Tauri 环境下,调用时会抛错,外层需做兜底 // 我们按需加载并在运行时捕获错误,避免构建期类型问题 // eslint-disable-next-line @typescript-eslint/consistent-type-imports import type { Update } from "@tauri-apps/plugin-updater"; export type UpdateChannel = "stable" | "beta"; export type UpdaterPhase = | "idle" | "checking" | "available" | "downloading" | "installing" | "restarting" | "upToDate" | "error"; export interface UpdateInfo { currentVersion: string; availableVersion: string; notes?: string; pubDate?: string; } export interface UpdateProgressEvent { event: "Started" | "Progress" | "Finished"; total?: number; downloaded?: number; } export interface UpdateHandle { version: string; notes?: string; date?: string; downloadAndInstall: ( onProgress?: (e: UpdateProgressEvent) => void, ) => Promise; download?: () => Promise; install?: () => Promise; } export interface CheckOptions { timeout?: number; channel?: UpdateChannel; } function mapUpdateHandle(raw: Update): UpdateHandle { return { version: (raw as any).version ?? "", notes: (raw as any).notes, date: (raw as any).date, async downloadAndInstall(onProgress?: (e: UpdateProgressEvent) => void) { await (raw as any).downloadAndInstall((evt: any) => { if (!onProgress) return; const mapped: UpdateProgressEvent = { event: evt?.event, }; if (evt?.event === "Started") { mapped.total = evt?.data?.contentLength ?? 0; mapped.downloaded = 0; } else if (evt?.event === "Progress") { mapped.downloaded = evt?.data?.chunkLength ?? 0; // 累积由调用方完成 } onProgress(mapped); }); }, // 透传可选 API(若插件版本支持) download: (raw as any).download ? async () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call await (raw as any).download(); } : undefined, install: (raw as any).install ? async () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call await (raw as any).install(); } : undefined, }; } export async function getCurrentVersion(): Promise { try { return await getVersion(); } catch { return ""; } } export async function checkForUpdate( opts: CheckOptions = {}, ): Promise< | { status: "up-to-date" } | { status: "available"; info: UpdateInfo; update: UpdateHandle } > { // 动态引入,避免在未安装插件时导致打包期问题 const { check } = await import("@tauri-apps/plugin-updater"); const currentVersion = await getCurrentVersion(); const update = await check({ timeout: opts.timeout ?? 30000 } as any); if (!update) { return { status: "up-to-date" }; } const mapped = mapUpdateHandle(update); const info: UpdateInfo = { currentVersion, availableVersion: mapped.version, notes: mapped.notes, pubDate: mapped.date, }; return { status: "available", info, update: mapped }; } export async function relaunchApp(): Promise { const { relaunch } = await import("@tauri-apps/plugin-process"); await relaunch(); } export async function runUpdateFlow( opts: CheckOptions = {}, ): Promise<{ status: "up-to-date" | "done" }> { const result = await checkForUpdate(opts); if (result.status === "up-to-date") return result; let downloaded = 0; let total = 0; await result.update.downloadAndInstall((e) => { if (e.event === "Started") { total = e.total ?? 0; downloaded = 0; } else if (e.event === "Progress") { downloaded += e.downloaded ?? 0; // 调用方可监听此处并更新 UI(目前设置页仅显示加载态) console.debug("update progress", { downloaded, total }); } }); await relaunchApp(); return { status: "done" }; }