Skip to content

Commit

Permalink
Merge pull request #91 from moonlight-mod/notnite/abstracted-fs
Browse files Browse the repository at this point in the history
Abstract FS for browser
  • Loading branch information
adryd325 authored Oct 8, 2024
2 parents 863e788 + 194f084 commit 1d1fe2b
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 406 deletions.
33 changes: 18 additions & 15 deletions packages/browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import { readConfig, writeConfig } from "@moonlight-mod/core/config";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import { getExtensions } from "@moonlight-mod/core/extension";
import { loadExtensions } from "@moonlight-mod/core/extension/loader";
import {
MoonlightBranch,
MoonlightBrowserFS,
MoonlightNode
} from "@moonlight-mod/types";
import { MoonlightBranch, MoonlightNode } from "@moonlight-mod/types";
import { IndexedDB } from "@zenfs/dom";
import { configure } from "@zenfs/core";
import * as fs from "@zenfs/core/promises";

function getParts(path: string) {
if (path.startsWith("/")) path = path.substring(1);
return path.split("/");
}

window._moonlightBrowserInit = async () => {
// Set up a virtual filesystem with IndexedDB
await configure({
Expand All @@ -25,13 +26,21 @@ window._moonlightBrowserInit = async () => {
}
});

const browserFS: MoonlightBrowserFS = {
window._moonlightBrowserFS = {
async readFile(path) {
return new Uint8Array(await fs.readFile(path));
},
async readFileString(path) {
const file = await this.readFile(path);
return new TextDecoder().decode(file);
},
async writeFile(path, data) {
await fs.writeFile(path, data);
},
async writeFileString(path, data) {
const file = new TextEncoder().encode(data);
await this.writeFile(path, file);
},
async unlink(path) {
await fs.unlink(path);
},
Expand All @@ -40,7 +49,7 @@ window._moonlightBrowserInit = async () => {
return await fs.readdir(path);
},
async mkdir(path) {
const parts = this.parts(path);
const parts = getParts(path);
for (let i = 0; i < parts.length; i++) {
const path = this.join(...parts.slice(0, i + 1));
if (!(await this.exists(path))) await fs.mkdir(path);
Expand Down Expand Up @@ -76,17 +85,10 @@ window._moonlightBrowserInit = async () => {
return str;
},
dirname(path) {
const parts = this.parts(path);
const parts = getParts(path);
return "/" + parts.slice(0, parts.length - 1).join("/");
},
parts(path) {
if (path.startsWith("/")) path = path.substring(1);
return path.split("/");
}
};
Object.assign(window, {
_moonlightBrowserFS: browserFS
});

// Actual loading begins here
const config = await readConfig();
Expand All @@ -106,6 +108,7 @@ window._moonlightBrowserInit = async () => {
extensions,
processedExtensions,
nativesCache: {},
fs: window._moonlightBrowserFS,

version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
Expand Down
3 changes: 0 additions & 3 deletions packages/core-extensions/src/moonbase/consts.ts

This file was deleted.

104 changes: 104 additions & 0 deletions packages/core-extensions/src/moonbase/native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { MoonlightBranch } from "@moonlight-mod/types";
import type { MoonbaseNatives, RepositoryManifest } from "./types";
import extractAsar from "@moonlight-mod/core/asar";
import { repoUrlFile } from "@moonlight-mod/types/constants";

export const githubRepo = "moonlight-mod/moonlight";
export const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref";
export const userAgent = `moonlight/${moonlightNode.version} (https://github.com/moonlight-mod/moonlight)`;

export default function getNatives(): MoonbaseNatives {
const fs = moonlightNode.fs;
const logger = moonlightNode.getLogger("moonbase/natives");

return {
async checkForMoonlightUpdate() {
try {
if (moonlightNode.branch === MoonlightBranch.STABLE) {
const req = await fetch(
`https://api.github.com/repos/${githubRepo}/releases/latest?_=${Date.now()}`,
{
headers: {
"User-Agent": userAgent
}
}
);
const json: { name: string } = await req.json();
return json.name !== moonlightNode.version ? json.name : null;
} else if (moonlightNode.branch === MoonlightBranch.NIGHTLY) {
const req = await fetch(`${nightlyRefUrl}?_=${Date.now()}`, {
headers: {
"User-Agent": userAgent
}
});
const ref = (await req.text()).split("\n")[0];
return ref !== moonlightNode.version ? ref : null;
}

return null;
} catch (e) {
logger.error("Error checking for moonlight update", e);
return null;
}
},

async fetchRepositories(repos) {
const ret: Record<string, RepositoryManifest[]> = {};

for (const repo of repos) {
try {
const req = await fetch(repo, {
headers: {
"User-Agent": userAgent
}
});
const json = await req.json();
ret[repo] = json;
} catch (e) {
logger.error(`Error fetching repository ${repo}`, e);
}
}

return ret;
},

async installExtension(manifest, url, repo) {
const req = await fetch(url, {
headers: {
"User-Agent": userAgent
}
});

const dir = moonlightNode.getExtensionDir(manifest.id);
// remake it in case of updates
if (await fs.exists(dir)) await fs.rmdir(dir);
await fs.mkdir(dir);

const buffer = await req.arrayBuffer();
const files = extractAsar(buffer);
for (const [file, buf] of Object.entries(files)) {
const fullFile = fs.join(dir, file);
const fullDir = fs.dirname(fullFile);

if (!(await fs.exists(fullDir))) await fs.mkdir(fullDir);
await fs.writeFile(fs.join(dir, file), buf);
}

await fs.writeFileString(fs.join(dir, repoUrlFile), repo);
},

async deleteExtension(id) {
const dir = moonlightNode.getExtensionDir(id);
await fs.rmdir(dir);
},

getExtensionConfig(id, key) {
const config = moonlightNode.config.extensions[id];
if (typeof config === "object") {
return config.config?.[key];
}

return undefined;
}
};
}
115 changes: 2 additions & 113 deletions packages/core-extensions/src/moonbase/node.ts
Original file line number Diff line number Diff line change
@@ -1,113 +1,2 @@
import { MoonbaseNatives, RepositoryManifest } from "./types";
import fs from "fs";
import path from "path";
import extractAsar from "@moonlight-mod/core/asar";
import { repoUrlFile } from "@moonlight-mod/types/constants";
import { githubRepo, userAgent, nightlyRefUrl } from "./consts";
import { MoonlightBranch } from "types/src";

const logger = moonlightNode.getLogger("moonbase");

async function checkForMoonlightUpdate() {
try {
if (moonlightNode.branch === MoonlightBranch.STABLE) {
const req = await fetch(
`https://api.github.com/repos/${githubRepo}/releases/latest?_=${Date.now()}`,
{
headers: {
"User-Agent": userAgent
}
}
);
const json: { name: string } = await req.json();
return json.name !== moonlightNode.version ? json.name : null;
} else if (moonlightNode.branch === MoonlightBranch.NIGHTLY) {
const req = await fetch(`${nightlyRefUrl}?_=${Date.now()}`, {
headers: {
"User-Agent": userAgent
}
});
const ref = (await req.text()).split("\n")[0];
return ref !== moonlightNode.version ? ref : null;
}

return null;
} catch (e) {
logger.error("Error checking for moonlight update", e);
return null;
}
}

async function fetchRepositories(repos: string[]) {
const ret: Record<string, RepositoryManifest[]> = {};

for (const repo of repos) {
try {
const req = await fetch(repo, {
headers: {
"User-Agent": userAgent
}
});
const json = await req.json();
ret[repo] = json;
} catch (e) {
logger.error(`Error fetching repository ${repo}`, e);
}
}

return ret;
}

async function installExtension(
manifest: RepositoryManifest,
url: string,
repo: string
) {
const req = await fetch(url, {
headers: {
"User-Agent": userAgent
}
});

const dir = moonlightNode.getExtensionDir(manifest.id);
// remake it in case of updates
if (fs.existsSync(dir)) fs.rmdirSync(dir, { recursive: true });
fs.mkdirSync(dir, { recursive: true });

const buffer = await req.arrayBuffer();
const files = extractAsar(buffer);
for (const [file, buf] of Object.entries(files)) {
const nodeBuf = Buffer.from(buf);
const fullFile = path.join(dir, file);
const fullDir = path.dirname(fullFile);

if (!fs.existsSync(fullDir)) fs.mkdirSync(fullDir, { recursive: true });
fs.writeFileSync(path.join(dir, file), nodeBuf);
}

fs.writeFileSync(path.join(dir, repoUrlFile), repo);
}

async function deleteExtension(id: string) {
const dir = moonlightNode.getExtensionDir(id);
fs.rmdirSync(dir, { recursive: true });
}

function getExtensionConfig(id: string, key: string): any {
const config = moonlightNode.config.extensions[id];
if (typeof config === "object") {
return config.config?.[key];
}

return undefined;
}

const exports: MoonbaseNatives = {
checkForMoonlightUpdate,
fetchRepositories,
installExtension,
deleteExtension,
getExtensionConfig
};

module.exports = exports;
import getNatives from "./native";
module.exports = getNatives();
Loading

0 comments on commit 1d1fe2b

Please sign in to comment.