import https from 'node:https' interface MojangVersionEntry { id: string type: string } interface MojangVersionManifest { versions: MojangVersionEntry[] } const MANIFEST_URL = 'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json' let cachedReleases: string[] | null = null let cachedAt = 0 const CACHE_TTL_MS = 60 * 60 * 1000 export async function fetchReleaseVersions(): Promise { if (cachedReleases && Date.now() - cachedAt < CACHE_TTL_MS) { return cachedReleases } try { const data = await fetchJson(MANIFEST_URL) const releases = data.versions.filter((entry) => entry.type === 'release').map((entry) => entry.id) cachedReleases = releases cachedAt = Date.now() return releases } catch { return cachedReleases ?? FALLBACK_RELEASES } } function fetchJson(url: string): Promise { return new Promise((resolve, reject) => { const request = https.get(url, { timeout: 8000 }, (response) => { if (response.statusCode !== 200) { response.resume() reject(new Error(`Mojang manifest HTTP ${response.statusCode}`)) return } const chunks: Buffer[] = [] response.on('data', (chunk: Buffer) => chunks.push(chunk)) response.on('end', () => { try { resolve(JSON.parse(Buffer.concat(chunks).toString('utf8')) as T) } catch (error) { reject(error as Error) } }) }) request.on('error', reject) request.on('timeout', () => { request.destroy(new Error('Mojang manifest timeout')) }) }) } const FALLBACK_RELEASES = [ '1.21', '1.20.6', '1.20.4', '1.20.2', '1.20.1', '1.19.4', '1.19.2', '1.18.2', '1.17.1', '1.16.5' ]