this repo has no description
1import "@moonlight-mod/web-preload";
2import { readConfig, writeConfig } from "@moonlight-mod/core/config";
3import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
4import { getExtensions } from "@moonlight-mod/core/extension";
5import { loadExtensions } from "@moonlight-mod/core/extension/loader";
6import { MoonlightBranch, MoonlightNode } from "@moonlight-mod/types";
7import { IndexedDB } from "@zenfs/dom";
8import { configure } from "@zenfs/core";
9import * as fs from "@zenfs/core/promises";
10
11function getParts(path: string) {
12 if (path.startsWith("/")) path = path.substring(1);
13 return path.split("/");
14}
15
16window._moonlightBrowserInit = async () => {
17 // Set up a virtual filesystem with IndexedDB
18 await configure({
19 mounts: {
20 "/": {
21 backend: IndexedDB,
22 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
23 // @ts-ignore tsc tweaking
24 storeName: "moonlight-fs"
25 }
26 }
27 });
28
29 window.moonlightNodeSandboxed = {
30 fs: {
31 async readFile(path) {
32 return new Uint8Array(await fs.readFile(path));
33 },
34 async readFileString(path) {
35 const file = await this.readFile(path);
36 return new TextDecoder().decode(file);
37 },
38 async writeFile(path, data) {
39 await fs.writeFile(path, data);
40 },
41 async writeFileString(path, data) {
42 const file = new TextEncoder().encode(data);
43 await this.writeFile(path, file);
44 },
45 async unlink(path) {
46 await fs.unlink(path);
47 },
48
49 async readdir(path) {
50 return await fs.readdir(path);
51 },
52 async mkdir(path) {
53 const parts = getParts(path);
54 for (let i = 0; i < parts.length; i++) {
55 const path = this.join(...parts.slice(0, i + 1));
56 if (!(await this.exists(path))) await fs.mkdir(path);
57 }
58 },
59
60 async rmdir(path) {
61 const entries = await this.readdir(path);
62
63 for (const entry of entries) {
64 const fullPath = this.join(path, entry);
65 const isFile = await this.isFile(fullPath);
66 if (isFile) {
67 await this.unlink(fullPath);
68 } else {
69 await this.rmdir(fullPath);
70 }
71 }
72
73 await fs.rmdir(path);
74 },
75
76 async exists(path) {
77 return await fs.exists(path);
78 },
79 async isFile(path) {
80 return (await fs.stat(path)).isFile();
81 },
82
83 join(...parts) {
84 let str = parts.join("/");
85 if (!str.startsWith("/")) str = "/" + str;
86 return str;
87 },
88 dirname(path) {
89 const parts = getParts(path);
90 return "/" + parts.slice(0, parts.length - 1).join("/");
91 }
92 },
93 // TODO
94 addCors(url) {},
95 addBlocked(url) {}
96 };
97
98 // Actual loading begins here
99 const config = await readConfig();
100 initLogger(config);
101
102 const extensions = await getExtensions();
103 const processedExtensions = await loadExtensions(extensions);
104
105 function getConfig(ext: string) {
106 const val = config.extensions[ext];
107 if (val == null || typeof val === "boolean") return undefined;
108 return val.config;
109 }
110
111 const moonlightNode: MoonlightNode = {
112 config,
113 extensions,
114 processedExtensions,
115 nativesCache: {},
116 isBrowser: true,
117
118 version: MOONLIGHT_VERSION,
119 branch: MOONLIGHT_BRANCH as MoonlightBranch,
120
121 getConfig,
122 getConfigOption: <T>(ext: string, name: string) => {
123 const config = getConfig(ext);
124 if (config == null) return undefined;
125 const option = config[name];
126 if (option == null) return undefined;
127 return option as T;
128 },
129 getNatives: () => {},
130 getLogger: (id: string) => {
131 return new Logger(id);
132 },
133
134 getMoonlightDir() {
135 return "/";
136 },
137 getExtensionDir: (ext: string) => {
138 return `/extensions/${ext}`;
139 },
140
141 writeConfig
142 };
143
144 Object.assign(window, {
145 moonlightNode
146 });
147
148 // This is set by web-preload for us
149 await window._moonlightBrowserLoad();
150};