this repo has no description
at v1.2.1 6.3 kB view raw
1import { MoonlightBranch } from "@moonlight-mod/types"; 2import type { MoonbaseNatives, RepositoryManifest } from "./types"; 3import extractAsar from "@moonlight-mod/core/asar"; 4import { 5 distDir, 6 repoUrlFile, 7 installedVersionFile 8} from "@moonlight-mod/types/constants"; 9import { parseTarGzip } from "nanotar"; 10 11const githubRepo = "moonlight-mod/moonlight"; 12const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`; 13const artifactName = "dist.tar.gz"; 14 15const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref"; 16const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz"; 17 18export const userAgent = `moonlight/${moonlightNode.version} (https://github.com/moonlight-mod/moonlight)`; 19 20async function getStableRelease(): Promise<{ 21 name: string; 22 assets: { 23 name: string; 24 browser_download_url: string; 25 }[]; 26}> { 27 const req = await fetch(githubApiUrl, { 28 cache: "no-store", 29 headers: { 30 "User-Agent": userAgent 31 } 32 }); 33 return await req.json(); 34} 35 36export default function getNatives(): MoonbaseNatives { 37 const logger = moonlightNode.getLogger("moonbase/natives"); 38 39 return { 40 async checkForMoonlightUpdate() { 41 try { 42 if (moonlightNode.branch === MoonlightBranch.STABLE) { 43 const json = await getStableRelease(); 44 return json.name !== moonlightNode.version ? json.name : null; 45 } else if (moonlightNode.branch === MoonlightBranch.NIGHTLY) { 46 const req = await fetch(nightlyRefUrl, { 47 cache: "no-store", 48 headers: { 49 "User-Agent": userAgent 50 } 51 }); 52 const ref = (await req.text()).split("\n")[0]; 53 return ref !== moonlightNode.version ? ref : null; 54 } 55 56 return null; 57 } catch (e) { 58 logger.error("Error checking for moonlight update", e); 59 return null; 60 } 61 }, 62 63 async updateMoonlight() { 64 // Note: this won't do anything on browser, we should probably disable it 65 // entirely when running in browser. 66 async function downloadStable(): Promise<[ArrayBuffer, string]> { 67 const json = await getStableRelease(); 68 const asset = json.assets.find((a) => a.name === artifactName); 69 if (!asset) throw new Error("Artifact not found"); 70 71 logger.debug(`Downloading ${asset.browser_download_url}`); 72 const req = await fetch(asset.browser_download_url, { 73 cache: "no-store", 74 headers: { 75 "User-Agent": userAgent 76 } 77 }); 78 79 return [await req.arrayBuffer(), json.name]; 80 } 81 82 async function downloadNightly(): Promise<[ArrayBuffer, string]> { 83 logger.debug(`Downloading ${nightlyZipUrl}`); 84 const zipReq = await fetch(nightlyZipUrl, { 85 cache: "no-store", 86 headers: { 87 "User-Agent": userAgent 88 } 89 }); 90 91 const refReq = await fetch(nightlyRefUrl, { 92 cache: "no-store", 93 headers: { 94 "User-Agent": userAgent 95 } 96 }); 97 const ref = (await refReq.text()).split("\n")[0]; 98 99 return [await zipReq.arrayBuffer(), ref]; 100 } 101 102 const [tar, ref] = 103 moonlightNode.branch === MoonlightBranch.STABLE 104 ? await downloadStable() 105 : moonlightNode.branch === MoonlightBranch.NIGHTLY 106 ? await downloadNightly() 107 : [null, null]; 108 109 if (!tar || !ref) return; 110 111 const dist = moonlightFS.join(moonlightNode.getMoonlightDir(), distDir); 112 if (await moonlightFS.exists(dist)) await moonlightFS.rmdir(dist); 113 await moonlightFS.mkdir(dist); 114 115 logger.debug("Extracting update"); 116 const files = await parseTarGzip(tar); 117 for (const file of files) { 118 if (!file.data) continue; 119 // @ts-expect-error What do you mean their own types are wrong 120 if (file.type !== "file") continue; 121 122 const fullFile = moonlightFS.join(dist, file.name); 123 const fullDir = moonlightFS.dirname(fullFile); 124 if (!(await moonlightFS.exists(fullDir))) 125 await moonlightFS.mkdir(fullDir); 126 await moonlightFS.writeFile(fullFile, file.data); 127 } 128 129 logger.debug("Writing version file:", ref); 130 const versionFile = moonlightFS.join( 131 moonlightNode.getMoonlightDir(), 132 installedVersionFile 133 ); 134 await moonlightFS.writeFileString(versionFile, ref.trim()); 135 136 logger.debug("Update extracted"); 137 }, 138 139 async fetchRepositories(repos) { 140 const ret: Record<string, RepositoryManifest[]> = {}; 141 142 for (const repo of repos) { 143 try { 144 const req = await fetch(repo, { 145 cache: "no-store", 146 headers: { 147 "User-Agent": userAgent 148 } 149 }); 150 const json = await req.json(); 151 ret[repo] = json; 152 } catch (e) { 153 logger.error(`Error fetching repository ${repo}`, e); 154 } 155 } 156 157 return ret; 158 }, 159 160 async installExtension(manifest, url, repo) { 161 const req = await fetch(url, { 162 headers: { 163 "User-Agent": userAgent 164 } 165 }); 166 167 const dir = moonlightNode.getExtensionDir(manifest.id); 168 // remake it in case of updates 169 if (await moonlightFS.exists(dir)) await moonlightFS.rmdir(dir); 170 await moonlightFS.mkdir(dir); 171 172 const buffer = await req.arrayBuffer(); 173 const files = extractAsar(buffer); 174 for (const [file, buf] of Object.entries(files)) { 175 const fullFile = moonlightFS.join(dir, file); 176 const fullDir = moonlightFS.dirname(fullFile); 177 178 if (!(await moonlightFS.exists(fullDir))) 179 await moonlightFS.mkdir(fullDir); 180 await moonlightFS.writeFile(moonlightFS.join(dir, file), buf); 181 } 182 183 await moonlightFS.writeFileString( 184 moonlightFS.join(dir, repoUrlFile), 185 repo 186 ); 187 }, 188 189 async deleteExtension(id) { 190 const dir = moonlightNode.getExtensionDir(id); 191 await moonlightFS.rmdir(dir); 192 }, 193 194 getExtensionConfig(id, key) { 195 const config = moonlightNode.config.extensions[id]; 196 if (typeof config === "object") { 197 return config.config?.[key]; 198 } 199 200 return undefined; 201 } 202 }; 203}