/** The newest Android release, as the server reports it (GET /app/android/latest). */ export interface AppUpdateDTO { /** e.g. "1.2.0" (the release tag without its "v"). */ version: string; /** Where to download the APK (through our server, so the release host can stay private). */ apkUrl: string; /** Hex SHA-256 of the APK; the phone checks it before installing. */ sha256: string; size: number; /** What's new, from the release notes. */ notes: string | null; publishedAt: string; } /** "1.10.0" → [1, 10, 0]; null for anything that isn't a plain dotted version (an optional leading "v" is fine). */ export function parseVersion(version: string): number[] | null { const m = /^v?(\d+(?:\.\d+)*)$/.exec(version.trim()); return m ? m[1]!.split('.').map(Number) : null; } /** True when `candidate` is a later version than `current`. Unparseable versions are never newer. */ export function isNewerVersion(candidate: string, current: string): boolean { const a = parseVersion(candidate); const b = parseVersion(current); if (!a || !b) return false; for (let i = 0; i < Math.max(a.length, b.length); i++) { const diff = (a[i] ?? 0) - (b[i] ?? 0); if (diff !== 0) return diff > 0; } return false; }