-57
.eslintrc.json
-57
.eslintrc.json
···
1
-
{
2
-
"extends": [
3
-
"eslint:recommended",
4
-
"plugin:@typescript-eslint/recommended",
5
-
"plugin:prettier/recommended",
6
-
"plugin:react/recommended"
7
-
],
8
-
"plugins": ["@typescript-eslint", "prettier", "react"],
9
-
"parser": "@typescript-eslint/parser",
10
-
"env": {
11
-
"browser": true,
12
-
"node": true
13
-
},
14
-
"parserOptions": {
15
-
"ecmaFeatures": {
16
-
"jsx": true
17
-
},
18
-
"ecmaVersion": "latest",
19
-
"sourceType": "module"
20
-
},
21
-
"rules": {
22
-
"indent": "off",
23
-
"eqeqeq": [
24
-
"error",
25
-
"always",
26
-
{
27
-
"null": "ignore"
28
-
}
29
-
],
30
-
"quotes": [
31
-
"error",
32
-
"double",
33
-
{ "avoidEscape": true, "allowTemplateLiterals": true }
34
-
],
35
-
"@typescript-eslint/no-unused-vars": [
36
-
"error",
37
-
{ "args": "none", "varsIgnorePattern": "^_" }
38
-
],
39
-
// Mostly so we don't forget to leave these in when committing
40
-
"no-console": "error",
41
-
"no-debugger": "error",
42
-
43
-
// Quite honestly we're interacting with so much unknown within Discord that
44
-
// this being enabled is a hinderance
45
-
"@typescript-eslint/no-explicit-any": "off",
46
-
47
-
"@typescript-eslint/no-var-requires": "off",
48
-
49
-
// https://canary.discord.com/channels/1154257010532032512/1154275441788583996/1181760413231230976
50
-
"no-unused-labels": "off"
51
-
},
52
-
"settings": {
53
-
"react": {
54
-
"version": "18.2"
55
-
}
56
-
}
57
-
}
+41
.github/workflows/browser.yml
+41
.github/workflows/browser.yml
···
1
+
name: Browser extension builds
2
+
3
+
on:
4
+
push:
5
+
branches:
6
+
- develop
7
+
8
+
jobs:
9
+
browser:
10
+
name: Browser extension builds
11
+
runs-on: ubuntu-latest
12
+
steps:
13
+
- uses: actions/checkout@v4
14
+
- uses: pnpm/action-setup@v4
15
+
- uses: actions/setup-node@v4
16
+
with:
17
+
node-version: 22
18
+
cache: pnpm
19
+
20
+
- name: Install dependencies
21
+
run: pnpm install --frozen-lockfile
22
+
- name: Build moonlight
23
+
env:
24
+
NODE_ENV: production
25
+
run: pnpm run build
26
+
27
+
- name: Build MV3
28
+
run: pnpm run browser
29
+
- name: Build MV2
30
+
run: pnpm run browser-mv2
31
+
32
+
- name: Upload MV3
33
+
uses: actions/upload-artifact@v4
34
+
with:
35
+
name: browser
36
+
path: ./dist/browser
37
+
- name: Upload MV2
38
+
uses: actions/upload-artifact@v4
39
+
with:
40
+
name: browser-mv2
41
+
path: ./dist/browser-mv2
+4
-8
.github/workflows/lint.yml
+4
-8
.github/workflows/lint.yml
···
9
9
name: Lint commits
10
10
runs-on: ubuntu-latest
11
11
steps:
12
-
- uses: actions/checkout@v3
13
-
14
-
- uses: pnpm/action-setup@v2
15
-
with:
16
-
version: 9
17
-
run_install: false
18
-
- uses: actions/setup-node@v3
12
+
- uses: actions/checkout@v4
13
+
- uses: pnpm/action-setup@v4
14
+
- uses: actions/setup-node@v4
19
15
with:
20
-
node-version: 18
16
+
node-version: 22
21
17
cache: pnpm
22
18
23
19
- name: Install dependencies
+9
-11
.github/workflows/nightly.yml
+9
-11
.github/workflows/nightly.yml
···
15
15
name: Nightly builds on GitHub Pages
16
16
runs-on: ubuntu-latest
17
17
steps:
18
-
- uses: actions/checkout@v3
19
-
20
-
- uses: pnpm/action-setup@v2
21
-
with:
22
-
version: 9
23
-
run_install: false
24
-
- uses: actions/setup-node@v3
18
+
- uses: actions/checkout@v4
19
+
- uses: pnpm/action-setup@v4
20
+
- uses: actions/setup-node@v4
25
21
with:
26
-
node-version: 18
22
+
node-version: 22
27
23
cache: pnpm
28
24
29
25
- name: Install dependencies
···
31
27
- name: Build moonlight
32
28
env:
33
29
NODE_ENV: production
30
+
MOONLIGHT_BRANCH: nightly
31
+
MOONLIGHT_VERSION: ${{ github.sha }}
34
32
run: pnpm run build
35
33
36
34
- name: Write ref/commit to file
···
45
43
echo "$(date +%s)" >> ./dist/ref
46
44
47
45
- name: Setup GitHub Pages
48
-
uses: actions/configure-pages@v3
46
+
uses: actions/configure-pages@v5
49
47
- name: Upload artifact
50
-
uses: actions/upload-pages-artifact@v1
48
+
uses: actions/upload-pages-artifact@v3
51
49
with:
52
50
path: ./dist
53
51
- name: Deploy to GitHub Pages
54
-
uses: actions/deploy-pages@v2
52
+
uses: actions/deploy-pages@v4
+16
.github/workflows/nix.yml
+16
.github/workflows/nix.yml
···
1
+
name: Check Nix flake
2
+
on: [push, pull_request]
3
+
4
+
permissions:
5
+
checks: write
6
+
7
+
jobs:
8
+
nix:
9
+
name: Check Nix flake
10
+
runs-on: ubuntu-latest
11
+
steps:
12
+
- uses: actions/checkout@v4
13
+
- uses: DeterminateSystems/nix-installer-action@main
14
+
15
+
- name: Build default flake output
16
+
run: nix build
+6
-8
.github/workflows/release.yml
+6
-8
.github/workflows/release.yml
···
13
13
name: Release builds to GitHub Releases
14
14
runs-on: ubuntu-latest
15
15
steps:
16
-
- uses: actions/checkout@v3
17
-
18
-
- uses: pnpm/action-setup@v2
19
-
with:
20
-
version: 9
21
-
run_install: false
22
-
- uses: actions/setup-node@v3
16
+
- uses: actions/checkout@v4
17
+
- uses: pnpm/action-setup@v4
18
+
- uses: actions/setup-node@v4
23
19
with:
24
-
node-version: 18
20
+
node-version: 22
25
21
cache: pnpm
26
22
27
23
- name: Install dependencies
···
29
25
- name: Build moonlight
30
26
env:
31
27
NODE_ENV: production
28
+
MOONLIGHT_BRANCH: stable
29
+
MOONLIGHT_VERSION: ${{ github.ref_name }}
32
30
run: pnpm run build
33
31
- name: Create archive
34
32
run: |
+32
.github/workflows/types.yml
+32
.github/workflows/types.yml
···
1
+
name: Publish types on npm
2
+
on: workflow_dispatch
3
+
4
+
permissions:
5
+
contents: read
6
+
pages: write
7
+
id-token: write
8
+
9
+
jobs:
10
+
types:
11
+
name: Publish types on npm
12
+
runs-on: ubuntu-latest
13
+
steps:
14
+
- uses: actions/checkout@v4
15
+
- uses: pnpm/action-setup@v4
16
+
- uses: actions/setup-node@v4
17
+
with:
18
+
node-version: 22
19
+
cache: pnpm
20
+
registry-url: https://registry.npmjs.org
21
+
22
+
- name: Install dependencies
23
+
run: pnpm install --frozen-lockfile
24
+
- name: Build moonlight
25
+
env:
26
+
NODE_ENV: production
27
+
run: pnpm run build
28
+
29
+
- name: Publish types
30
+
run: pnpm publish --filter=./packages/types --access public --no-git-checks
31
+
env:
32
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+5
-1
.gitignore
+5
-1
.gitignore
+4
-4
.prettierrc
+4
-4
.prettierrc
-14
.vscode/tasks.json
-14
.vscode/tasks.json
+4
-1
CHANGELOG.md
+4
-1
CHANGELOG.md
+15
-4
README.md
+15
-4
README.md
···
1
1
<h3 align="center">
2
-
<img src="./img/wordmark.png" alt="moonlight" />
2
+
<picture>
3
+
<source media="(prefers-color-scheme: dark)" srcset="./img/wordmark-light.png">
4
+
<source media="(prefers-color-scheme: light)" srcset="./img/wordmark.png">
5
+
<img src="./img/wordmark.png" alt="moonlight" />
6
+
</picture>
3
7
4
-
<a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
8
+
<a href="https://moonlight-mod.github.io/using/install">Install</a>
9
+
\- <a href="https://moonlight-mod.github.io/ext-dev/getting-started">Docs</a>
10
+
\- <a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
5
11
\- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a>
6
-
\- <a href="https://moonlight-mod.github.io/">Docs</a>
7
12
8
13
<hr />
14
+
15
+
<picture>
16
+
<source media="(prefers-color-scheme: dark)" srcset="https://moonlight-mod.github.io/moonbase.png">
17
+
<source media="(prefers-color-scheme: light)" srcset="https://moonlight-mod.github.io/moonbase-light.png">
18
+
<img src="https://moonlight-mod.github.io/moonbase.png" alt="A screenshot of Moonbase, the moonlight UI" />
19
+
</picture>
9
20
</h3>
10
21
11
22
**moonlight** is yet another Discord client mod, focused on providing a decent user and developer experience.
12
23
13
24
moonlight is heavily inspired by hh3 (a private client mod) and the projects before it that it is inspired by, namely EndPwn. All core code is original or used with permission from their respective authors where not copyleft.
14
25
15
-
**_This is an experimental passion project._** moonlight was not created out of malicious intent nor intended to seriously compete with other mods. Anything and everything is subject to change.
26
+
moonlight is a **_passion project_** - things may break from time to time, but we try our best to keep things working in a timely manner.
16
27
17
28
moonlight is licensed under the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.html) (`LGPL-3.0-or-later`). See [the documentation](https://moonlight-mod.github.io/) for more information.
+127
-41
build.mjs
+127
-41
build.mjs
···
13
13
14
14
const prod = process.env.NODE_ENV === "production";
15
15
const watch = process.argv.includes("--watch");
16
+
const browser = process.argv.includes("--browser");
17
+
const mv2 = process.argv.includes("--mv2");
18
+
const clean = process.argv.includes("--clean");
19
+
20
+
const buildBranch = process.env.MOONLIGHT_BRANCH ?? "dev";
21
+
const buildVersion = process.env.MOONLIGHT_VERSION ?? "dev";
16
22
17
23
const external = [
18
24
"electron",
19
25
"fs",
20
26
"path",
21
27
"module",
22
-
"events",
23
-
"original-fs", // wtf asar?
28
+
"discord", // mappings
24
29
25
30
// Silence an esbuild warning
26
31
"./node-preload.js"
···
65
70
name: "build-log",
66
71
setup(build) {
67
72
build.onEnd((result) => {
68
-
console.log(
69
-
`[${timeFormatter.format(new Date())}] [${tag}] build finished`
70
-
);
73
+
console.log(`[${timeFormatter.format(new Date())}] [${tag}] build finished`);
71
74
});
72
75
}
73
76
});
74
77
75
78
async function build(name, entry) {
76
-
const outfile = path.join("./dist", name + ".js");
79
+
let outfile = path.join("./dist", name + ".js");
80
+
const browserDir = mv2 ? "browser-mv2" : "browser";
81
+
if (name === "browser") outfile = path.join("./dist", browserDir, "index.js");
77
82
78
83
const dropLabels = [];
79
-
if (name !== "injector") dropLabels.push("injector");
80
-
if (name !== "node-preload") dropLabels.push("nodePreload");
81
-
if (name !== "web-preload") dropLabels.push("webPreload");
84
+
const labels = {
85
+
injector: ["injector"],
86
+
nodePreload: ["node-preload"],
87
+
webPreload: ["web-preload"],
88
+
browser: ["browser"],
89
+
90
+
webTarget: ["web-preload", "browser"],
91
+
nodeTarget: ["node-preload", "injector"]
92
+
};
93
+
for (const [label, targets] of Object.entries(labels)) {
94
+
if (!targets.includes(name)) {
95
+
dropLabels.push(label);
96
+
}
97
+
}
82
98
83
99
const define = {
84
100
MOONLIGHT_ENV: `"${name}"`,
85
-
MOONLIGHT_PROD: prod.toString()
101
+
MOONLIGHT_PROD: prod.toString(),
102
+
MOONLIGHT_BRANCH: `"${buildBranch}"`,
103
+
MOONLIGHT_VERSION: `"${buildVersion}"`
86
104
};
87
105
88
-
for (const iterName of Object.keys(config)) {
106
+
for (const iterName of ["injector", "node-preload", "web-preload", "browser"]) {
89
107
const snake = iterName.replace(/-/g, "_").toUpperCase();
90
108
define[`MOONLIGHT_${snake}`] = (name === iterName).toString();
91
109
}
···
93
111
const nodeDependencies = ["glob"];
94
112
const ignoredExternal = name === "web-preload" ? nodeDependencies : [];
95
113
114
+
const plugins = [deduplicatedLogging, taggedBuildLog(name)];
115
+
if (name === "browser") {
116
+
plugins.push(
117
+
copyStaticFiles({
118
+
src: mv2 ? "./packages/browser/manifestv2.json" : "./packages/browser/manifest.json",
119
+
dest: `./dist/${browserDir}/manifest.json`
120
+
})
121
+
);
122
+
123
+
if (!mv2) {
124
+
plugins.push(
125
+
copyStaticFiles({
126
+
src: "./packages/browser/modifyResponseHeaders.json",
127
+
dest: `./dist/${browserDir}/modifyResponseHeaders.json`
128
+
})
129
+
);
130
+
plugins.push(
131
+
copyStaticFiles({
132
+
src: "./packages/browser/blockLoading.json",
133
+
dest: `./dist/${browserDir}/blockLoading.json`
134
+
})
135
+
);
136
+
}
137
+
138
+
plugins.push(
139
+
copyStaticFiles({
140
+
src: mv2 ? "./packages/browser/src/background-mv2.js" : "./packages/browser/src/background.js",
141
+
dest: `./dist/${browserDir}/background.js`
142
+
})
143
+
);
144
+
}
145
+
96
146
/** @type {import("esbuild").BuildOptions} */
97
147
const esbuildConfig = {
98
148
entryPoints: [entry],
99
149
outfile,
100
150
101
-
format: "cjs",
102
-
platform: name === "web-preload" ? "browser" : "node",
151
+
format: "iife",
152
+
globalName: "module.exports",
153
+
154
+
platform: ["web-preload", "browser"].includes(name) ? "browser" : "node",
103
155
104
156
treeShaking: true,
105
157
bundle: true,
···
112
164
dropLabels,
113
165
114
166
logLevel: "silent",
115
-
plugins: [deduplicatedLogging, taggedBuildLog(name)]
167
+
plugins,
168
+
169
+
// https://github.com/evanw/esbuild/issues/3944
170
+
footer:
171
+
name === "web-preload"
172
+
? {
173
+
js: `\n//# sourceURL=${name}.js`
174
+
}
175
+
: undefined
116
176
};
117
177
178
+
if (name === "browser") {
179
+
const coreExtensionsJson = {};
180
+
181
+
function readDir(dir) {
182
+
const files = fs.readdirSync(dir);
183
+
for (const file of files) {
184
+
const filePath = dir + "/" + file;
185
+
const normalizedPath = filePath.replace("./dist/core-extensions/", "");
186
+
if (fs.statSync(filePath).isDirectory()) {
187
+
readDir(filePath);
188
+
} else {
189
+
coreExtensionsJson[normalizedPath] = fs.readFileSync(filePath, "utf8");
190
+
}
191
+
}
192
+
}
193
+
194
+
readDir("./dist/core-extensions");
195
+
196
+
esbuildConfig.banner = {
197
+
js: `window._moonlight_coreExtensionsStr = ${JSON.stringify(JSON.stringify(coreExtensionsJson))};`
198
+
};
199
+
}
200
+
118
201
if (watch) {
119
202
const ctx = await esbuild.context(esbuildConfig);
120
203
await ctx.watch();
···
123
206
}
124
207
}
125
208
126
-
async function buildExt(ext, side, copyManifest, fileExt) {
209
+
async function buildExt(ext, side, fileExt) {
127
210
const outdir = path.join("./dist", "core-extensions", ext);
128
211
if (!fs.existsSync(outdir)) {
129
212
fs.mkdirSync(outdir, { recursive: true });
130
213
}
131
214
132
-
const entryPoints = [
133
-
`packages/core-extensions/src/${ext}/${side}.${fileExt}`
134
-
];
215
+
const entryPoints = [`packages/core-extensions/src/${ext}/${side}.${fileExt}`];
135
216
136
217
const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`;
137
218
if (fs.existsSync(wpModulesDir) && side === "index") {
138
219
const wpModules = fs.opendirSync(wpModulesDir);
139
220
for await (const wpModule of wpModules) {
140
221
if (wpModule.isFile()) {
141
-
entryPoints.push(
142
-
`packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}`
143
-
);
222
+
entryPoints.push(`packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}`);
144
223
} else {
145
224
for (const fileExt of ["ts", "tsx"]) {
146
225
const path = `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}/index.${fileExt}`;
···
168
247
}
169
248
};
170
249
250
+
const styleInput = `packages/core-extensions/src/${ext}/style.css`;
251
+
const styleOutput = `dist/core-extensions/${ext}/style.css`;
252
+
171
253
const esbuildConfig = {
172
254
entryPoints,
173
255
outdir,
174
256
175
-
format: "cjs",
257
+
format: "iife",
258
+
globalName: "module.exports",
176
259
platform: "node",
177
260
178
261
treeShaking: true,
···
186
269
},
187
270
logLevel: "silent",
188
271
plugins: [
189
-
...(copyManifest
272
+
copyStaticFiles({
273
+
src: `./packages/core-extensions/src/${ext}/manifest.json`,
274
+
dest: `./dist/core-extensions/${ext}/manifest.json`
275
+
}),
276
+
...(fs.existsSync(styleInput)
190
277
? [
191
278
copyStaticFiles({
192
-
src: `./packages/core-extensions/src/${ext}/manifest.json`,
193
-
dest: `./dist/core-extensions/${ext}/manifest.json`
279
+
src: styleInput,
280
+
dest: styleOutput
194
281
})
195
282
]
196
283
: []),
···
210
297
211
298
const promises = [];
212
299
213
-
for (const [name, entry] of Object.entries(config)) {
214
-
promises.push(build(name, entry));
215
-
}
216
-
217
-
const coreExtensions = fs.readdirSync("./packages/core-extensions/src");
218
-
for (const ext of coreExtensions) {
219
-
let copiedManifest = false;
300
+
if (clean) {
301
+
fs.rmSync("./dist", { recursive: true, force: true });
302
+
} else if (browser) {
303
+
build("browser", "packages/browser/src/index.ts");
304
+
} else {
305
+
for (const [name, entry] of Object.entries(config)) {
306
+
promises.push(build(name, entry));
307
+
}
220
308
221
-
for (const fileExt of ["ts", "tsx"]) {
222
-
for (const type of ["index", "node", "host"]) {
223
-
if (
224
-
fs.existsSync(
225
-
`./packages/core-extensions/src/${ext}/${type}.${fileExt}`
226
-
)
227
-
) {
228
-
promises.push(buildExt(ext, type, !copiedManifest, fileExt));
229
-
copiedManifest = true;
309
+
const coreExtensions = fs.readdirSync("./packages/core-extensions/src");
310
+
for (const ext of coreExtensions) {
311
+
for (const fileExt of ["ts", "tsx"]) {
312
+
for (const type of ["index", "node", "host"]) {
313
+
if (fs.existsSync(`./packages/core-extensions/src/${ext}/${type}.${fileExt}`)) {
314
+
promises.push(buildExt(ext, type, fileExt));
315
+
}
230
316
}
231
317
}
232
318
}
+25
eslint.config.mjs
+25
eslint.config.mjs
···
1
+
import config from "@moonlight-mod/eslint-config";
2
+
3
+
export default [
4
+
...config,
5
+
{
6
+
rules: {
7
+
// baseUrl being set to ./packages/ makes language server suggest "types/src" instead of "@moonlight-mod/types"
8
+
"no-restricted-imports": [
9
+
"error",
10
+
{
11
+
patterns: [
12
+
{
13
+
group: ["types/*"],
14
+
message: "Use @moonlight-mod/types instead"
15
+
},
16
+
{
17
+
group: ["core/*"],
18
+
message: "Use @moonlight-mod/core instead"
19
+
}
20
+
]
21
+
}
22
+
]
23
+
}
24
+
}
25
+
];
+4
-73
flake.lock
+4
-73
flake.lock
···
18
18
"type": "github"
19
19
}
20
20
},
21
-
"flake-utils_2": {
22
-
"inputs": {
23
-
"systems": "systems_2"
24
-
},
25
-
"locked": {
26
-
"lastModified": 1701680307,
27
-
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
28
-
"owner": "numtide",
29
-
"repo": "flake-utils",
30
-
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
31
-
"type": "github"
32
-
},
33
-
"original": {
34
-
"owner": "numtide",
35
-
"repo": "flake-utils",
36
-
"type": "github"
37
-
}
38
-
},
39
21
"nixpkgs": {
40
22
"locked": {
41
-
"lastModified": 1704295289,
42
-
"narHash": "sha256-9WZDRfpMqCYL6g/HNWVvXF0hxdaAgwgIGeLYiOhmes8=",
23
+
"lastModified": 1744232761,
24
+
"narHash": "sha256-gbl9hE39nQRpZaLjhWKmEu5ejtQsgI5TWYrIVVJn30U=",
43
25
"owner": "NixOS",
44
26
"repo": "nixpkgs",
45
-
"rev": "b0b2c5445c64191fd8d0b31f2b1a34e45a64547d",
27
+
"rev": "f675531bc7e6657c10a18b565cfebd8aa9e24c14",
46
28
"type": "github"
47
29
},
48
30
"original": {
49
31
"owner": "NixOS",
50
-
"ref": "nixos-23.11",
51
-
"repo": "nixpkgs",
52
-
"type": "github"
53
-
}
54
-
},
55
-
"nixpkgs_2": {
56
-
"locked": {
57
-
"lastModified": 1702151865,
58
-
"narHash": "sha256-9VAt19t6yQa7pHZLDbil/QctAgVsA66DLnzdRGqDisg=",
59
-
"owner": "nixos",
60
-
"repo": "nixpkgs",
61
-
"rev": "666fc80e7b2afb570462423cb0e1cf1a3a34fedd",
62
-
"type": "github"
63
-
},
64
-
"original": {
65
-
"owner": "nixos",
66
32
"ref": "nixos-unstable",
67
33
"repo": "nixpkgs",
68
34
"type": "github"
69
35
}
70
36
},
71
-
"pnpm2nix": {
72
-
"inputs": {
73
-
"flake-utils": "flake-utils_2",
74
-
"nixpkgs": "nixpkgs_2"
75
-
},
76
-
"locked": {
77
-
"lastModified": 1709572248,
78
-
"narHash": "sha256-WhaKD4cIvZLbwI2vZTkpH/oEeqGiyMvdW3bLi24P0eU=",
79
-
"owner": "mojotech",
80
-
"repo": "pnpm2nix-nzbr",
81
-
"rev": "c3cfff81ea297cfb9dc18928652f375314dc287d",
82
-
"type": "github"
83
-
},
84
-
"original": {
85
-
"owner": "mojotech",
86
-
"repo": "pnpm2nix-nzbr",
87
-
"type": "github"
88
-
}
89
-
},
90
37
"root": {
91
38
"inputs": {
92
39
"flake-utils": "flake-utils",
93
-
"nixpkgs": "nixpkgs",
94
-
"pnpm2nix": "pnpm2nix"
40
+
"nixpkgs": "nixpkgs"
95
41
}
96
42
},
97
43
"systems": {
98
-
"locked": {
99
-
"lastModified": 1681028828,
100
-
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
101
-
"owner": "nix-systems",
102
-
"repo": "default",
103
-
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
104
-
"type": "github"
105
-
},
106
-
"original": {
107
-
"owner": "nix-systems",
108
-
"repo": "default",
109
-
"type": "github"
110
-
}
111
-
},
112
-
"systems_2": {
113
44
"locked": {
114
45
"lastModified": 1681028828,
115
46
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+5
-89
flake.nix
+5
-89
flake.nix
···
2
2
description = "Yet another Discord mod";
3
3
4
4
inputs = {
5
-
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
5
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6
6
flake-utils.url = "github:numtide/flake-utils";
7
-
pnpm2nix.url = "github:mojotech/pnpm2nix-nzbr";
8
7
};
9
8
10
-
outputs = { self, nixpkgs, flake-utils, pnpm2nix }:
11
-
let
12
-
mkMoonlight = { pkgs, mkPnpmPackage }:
13
-
mkPnpmPackage rec {
14
-
workspace = ./.;
15
-
src = ./.;
16
-
components = [
17
-
"packages/core"
18
-
"packages/core-extensions"
19
-
"packages/injector"
20
-
"packages/node-preload"
21
-
"packages/types"
22
-
"packages/web-preload"
23
-
];
24
-
distDirs = [ "dist" ];
25
-
26
-
copyNodeModules = true;
27
-
buildPhase = "pnpm run build";
28
-
installPhase = "cp -r dist $out";
29
-
30
-
meta = with pkgs.lib; {
31
-
description = "Yet another Discord mod";
32
-
homepage = "https://moonlight-mod.github.io/";
33
-
license = licenses.lgpl3;
34
-
maintainers = with maintainers; [ notnite ];
35
-
};
36
-
};
37
-
38
-
nameTable = {
39
-
discord = "Discord";
40
-
discord-ptb = "DiscordPTB";
41
-
discord-canary = "DiscordCanary";
42
-
discord-development = "DiscordDevelopment";
43
-
};
44
-
45
-
darwinNameTable = {
46
-
discord = "Discord";
47
-
discord-ptb = "Discord PTB";
48
-
discord-canary = "Discord Canary";
49
-
discord-development = "Discord Development";
50
-
};
51
-
52
-
mkOverride = prev: moonlight: name:
53
-
let discord = prev.${name};
54
-
in discord.overrideAttrs (old: {
55
-
installPhase = let
56
-
folderName = nameTable.${name};
57
-
darwinFolderName = darwinNameTable.${name};
58
-
59
-
injected = ''
60
-
require("${moonlight}/injector").inject(
61
-
require("path").join(__dirname, "../_app.asar")
62
-
);
63
-
'';
64
-
65
-
packageJson = ''
66
-
{"name":"discord","main":"./injector.js","private":true}
67
-
'';
68
-
69
-
in old.installPhase + "\n" + ''
70
-
resources="$out/opt/${folderName}/resources"
71
-
if [ ! -d "$resources" ]; then
72
-
resources="$out/Applications/${darwinFolderName}.app/Contents/Resources"
73
-
fi
74
-
75
-
mv "$resources/app.asar" "$resources/_app.asar"
76
-
mkdir -p "$resources/app"
77
-
78
-
cat > "$resources/app/injector.js" <<EOF
79
-
${injected}
80
-
EOF
81
-
82
-
echo '${packageJson}' > "$resources/app/package.json"
83
-
'';
84
-
});
85
-
86
-
overlay = final: prev: rec {
87
-
moonlight-mod = mkMoonlight {
88
-
pkgs = final;
89
-
mkPnpmPackage = pnpm2nix.packages.${final.system}.mkPnpmPackage;
90
-
};
91
-
discord = mkOverride prev moonlight-mod "discord";
92
-
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
93
-
discord-canary = mkOverride prev moonlight-mod "discord-canary";
94
-
discord-development =
95
-
mkOverride prev moonlight-mod "discord-development";
96
-
};
9
+
outputs = { self, nixpkgs, flake-utils }:
10
+
let overlay = import ./nix/overlay.nix { };
97
11
in flake-utils.lib.eachDefaultSystem (system:
98
12
let
99
13
pkgs = import nixpkgs {
···
102
16
overlays = [ overlay ];
103
17
};
104
18
in {
19
+
# Don't use these unless you're testing things
105
20
packages.default = pkgs.moonlight-mod;
106
21
packages.moonlight-mod = pkgs.moonlight-mod;
107
22
···
111
26
packages.discord-development = pkgs.discord-development;
112
27
}) // {
113
28
overlays.default = overlay;
29
+
homeModules.default = ./nix/home-manager.nix;
114
30
};
115
31
}
img/wordmark-light.png
img/wordmark-light.png
This is a binary file and will not be displayed.
+57
nix/default.nix
+57
nix/default.nix
···
1
+
{
2
+
lib,
3
+
stdenv,
4
+
nodejs_22,
5
+
pnpm_10,
6
+
}:
7
+
8
+
stdenv.mkDerivation (finalAttrs: {
9
+
pname = "moonlight";
10
+
version = (builtins.fromJSON (builtins.readFile ./../package.json)).version;
11
+
12
+
src = ./..;
13
+
14
+
outputs = [ "out" "firefox" ];
15
+
16
+
nativeBuildInputs = [
17
+
nodejs_22
18
+
pnpm_10.configHook
19
+
];
20
+
21
+
pnpmDeps = pnpm_10.fetchDeps {
22
+
inherit (finalAttrs) pname version src;
23
+
hash = "sha256-I+zRCUqJabpGJRFBGW0NrM9xzyzeCjioF54zlCpynBU=";
24
+
};
25
+
26
+
env = {
27
+
NODE_ENV = "production";
28
+
MOONLIGHT_VERSION = "v${finalAttrs.version}";
29
+
};
30
+
31
+
buildPhase = ''
32
+
runHook preBuild
33
+
34
+
pnpm run build
35
+
pnpm run browser-mv2
36
+
37
+
runHook postBuild
38
+
'';
39
+
40
+
installPhase = ''
41
+
runHook preInstall
42
+
43
+
cp -r dist $out
44
+
45
+
mkdir -p $firefox/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/
46
+
mv $out/browser-mv2 $firefox/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/{0fb6d66f-f22d-4555-a87b-34ef4bea5e2a}
47
+
48
+
runHook postInstall
49
+
'';
50
+
51
+
meta = with lib; {
52
+
description = "Yet another Discord mod";
53
+
homepage = "https://moonlight-mod.github.io/";
54
+
license = licenses.lgpl3;
55
+
maintainers = with maintainers; [ notnite ];
56
+
};
57
+
})
+56
nix/home-manager.nix
+56
nix/home-manager.nix
···
1
+
{ config, lib, pkgs, ... }:
2
+
3
+
let cfg = config.programs.moonlight-mod;
4
+
in {
5
+
options.programs.moonlight-mod = {
6
+
enable = lib.mkEnableOption "Yet another Discord mod";
7
+
8
+
configs = let
9
+
# TODO: type this
10
+
type = lib.types.nullOr (lib.types.attrs);
11
+
default = null;
12
+
in {
13
+
stable = lib.mkOption {
14
+
inherit type default;
15
+
description = "Configuration for Discord Stable";
16
+
};
17
+
18
+
ptb = lib.mkOption {
19
+
inherit type default;
20
+
description = "Configuration for Discord PTB";
21
+
};
22
+
23
+
canary = lib.mkOption {
24
+
inherit type default;
25
+
description = "Configuration for Discord Canary";
26
+
};
27
+
28
+
development = lib.mkOption {
29
+
inherit type default;
30
+
description = "Configuration for Discord Development";
31
+
};
32
+
};
33
+
};
34
+
35
+
config = lib.mkIf cfg.enable {
36
+
xdg.configFile."moonlight-mod/stable.json" =
37
+
lib.mkIf (cfg.configs.stable != null) {
38
+
text = builtins.toJSON cfg.configs.stable;
39
+
};
40
+
41
+
xdg.configFile."moonlight-mod/ptb.json" =
42
+
lib.mkIf (cfg.configs.ptb != null) {
43
+
text = builtins.toJSON cfg.configs.ptb;
44
+
};
45
+
46
+
xdg.configFile."moonlight-mod/canary.json" =
47
+
lib.mkIf (cfg.configs.canary != null) {
48
+
text = builtins.toJSON cfg.configs.canary;
49
+
};
50
+
51
+
xdg.configFile."moonlight-mod/development.json" =
52
+
lib.mkIf (cfg.configs.development != null) {
53
+
text = builtins.toJSON cfg.configs.development;
54
+
};
55
+
};
56
+
}
+57
nix/overlay.nix
+57
nix/overlay.nix
···
1
+
{ ... }:
2
+
3
+
let
4
+
nameTable = {
5
+
discord = "Discord";
6
+
discord-ptb = "DiscordPTB";
7
+
discord-canary = "DiscordCanary";
8
+
discord-development = "DiscordDevelopment";
9
+
};
10
+
11
+
darwinNameTable = {
12
+
discord = "Discord";
13
+
discord-ptb = "Discord PTB";
14
+
discord-canary = "Discord Canary";
15
+
discord-development = "Discord Development";
16
+
};
17
+
18
+
mkOverride = prev: moonlight: name:
19
+
let discord = prev.${name};
20
+
in discord.overrideAttrs (old: {
21
+
installPhase = let
22
+
folderName = nameTable.${name};
23
+
darwinFolderName = darwinNameTable.${name};
24
+
25
+
injected = ''
26
+
require("${moonlight}/injector").inject(
27
+
require("path").join(__dirname, "../_app.asar")
28
+
);
29
+
'';
30
+
31
+
packageJson = ''
32
+
{"name":"${name}","main":"./injector.js","private":true}
33
+
'';
34
+
35
+
in old.installPhase + "\n" + ''
36
+
resources="$out/opt/${folderName}/resources"
37
+
if [ ! -d "$resources" ]; then
38
+
resources="$out/Applications/${darwinFolderName}.app/Contents/Resources"
39
+
fi
40
+
41
+
mv "$resources/app.asar" "$resources/_app.asar"
42
+
mkdir -p "$resources/app"
43
+
44
+
cat > "$resources/app/injector.js" <<EOF
45
+
${injected}
46
+
EOF
47
+
48
+
echo '${packageJson}' > "$resources/app/package.json"
49
+
'';
50
+
});
51
+
in final: prev: rec {
52
+
moonlight-mod = final.callPackage ./default.nix { };
53
+
discord = mkOverride prev moonlight-mod "discord";
54
+
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
55
+
discord-canary = mkOverride prev moonlight-mod "discord-canary";
56
+
discord-development = mkOverride prev moonlight-mod "discord-development";
57
+
}
+26
-16
package.json
+26
-16
package.json
···
1
1
{
2
2
"name": "moonlight",
3
-
"version": "1.0.10",
3
+
"version": "1.3.14",
4
+
"packageManager": "pnpm@10.7.1",
4
5
"description": "Yet another Discord mod",
5
-
"homepage": "https://moonlight-mod.github.io/",
6
6
"license": "LGPL-3.0-or-later",
7
+
"homepage": "https://moonlight-mod.github.io/",
7
8
"repository": {
8
9
"type": "git",
9
10
"url": "git+https://github.com/moonlight-mod/moonlight.git"
···
11
12
"bugs": {
12
13
"url": "https://github.com/moonlight-mod/moonlight/issues"
13
14
},
15
+
"engineStrict": true,
16
+
"engines": {
17
+
"node": ">=22",
18
+
"pnpm": ">=10",
19
+
"npm": "pnpm",
20
+
"yarn": "pnpm"
21
+
},
14
22
"scripts": {
15
23
"build": "node build.mjs",
16
24
"dev": "node build.mjs --watch",
25
+
"clean": "node build.mjs --clean",
26
+
"browser": "node build.mjs --browser",
27
+
"browser-mv2": "node build.mjs --browser --mv2",
17
28
"lint": "eslint packages",
18
-
"lint:fix": "eslint packages",
19
-
"lint:report": "eslint --output-file eslint_report.json --format json packages",
29
+
"lint:fix": "pnpm lint --fix",
30
+
"lint:report": "pnpm lint --output-file eslint_report.json --format json",
20
31
"typecheck": "tsc --noEmit",
21
32
"check": "pnpm run lint && pnpm run typecheck",
22
-
"prepare": "husky install"
33
+
"prepare": "husky install",
34
+
"updates": "pnpm taze -r"
23
35
},
24
36
"devDependencies": {
25
-
"@typescript-eslint/eslint-plugin": "^6.13.2",
26
-
"@typescript-eslint/parser": "^6.13.2",
27
-
"esbuild": "^0.19.3",
28
-
"esbuild-copy-static-files": "^0.1.0",
29
-
"eslint": "^8.55.0",
30
-
"eslint-config-prettier": "^9.1.0",
31
-
"eslint-plugin-prettier": "^5.0.1",
32
-
"eslint-plugin-react": "^7.33.2",
33
-
"husky": "^8.0.3",
34
-
"prettier": "^3.1.0",
35
-
"typescript": "^5.3.2"
37
+
"@moonlight-mod/eslint-config": "catalog:dev",
38
+
"@types/node": "catalog:dev",
39
+
"esbuild": "catalog:dev",
40
+
"esbuild-copy-static-files": "catalog:dev",
41
+
"eslint": "catalog:dev",
42
+
"husky": "catalog:dev",
43
+
"prettier": "catalog:dev",
44
+
"taze": "catalog:dev",
45
+
"typescript": "catalog:dev"
36
46
}
37
47
}
+14
packages/browser/blockLoading.json
+14
packages/browser/blockLoading.json
+46
packages/browser/manifest.json
+46
packages/browser/manifest.json
···
1
+
{
2
+
"$schema": "https://json.schemastore.org/chrome-manifest",
3
+
"manifest_version": 3,
4
+
"name": "moonlight",
5
+
"description": "Yet another Discord mod",
6
+
"version": "1.3.14",
7
+
"permissions": ["declarativeNetRequestWithHostAccess", "webRequest", "scripting", "webNavigation"],
8
+
"host_permissions": [
9
+
"https://moonlight-mod.github.io/*",
10
+
"https://api.github.com/*",
11
+
"https://*.discord.com/*",
12
+
"https://*.discordapp.com/*"
13
+
],
14
+
"content_scripts": [
15
+
{
16
+
"js": ["index.js"],
17
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"],
18
+
"run_at": "document_start",
19
+
"world": "MAIN"
20
+
}
21
+
],
22
+
"declarative_net_request": {
23
+
"rule_resources": [
24
+
{
25
+
"id": "modifyResponseHeaders",
26
+
"enabled": true,
27
+
"path": "modifyResponseHeaders.json"
28
+
},
29
+
{
30
+
"id": "blockLoading",
31
+
"enabled": true,
32
+
"path": "blockLoading.json"
33
+
}
34
+
]
35
+
},
36
+
"background": {
37
+
"service_worker": "background.js",
38
+
"type": "module"
39
+
},
40
+
"web_accessible_resources": [
41
+
{
42
+
"resources": ["index.js"],
43
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"]
44
+
}
45
+
]
46
+
}
+33
packages/browser/manifestv2.json
+33
packages/browser/manifestv2.json
···
1
+
{
2
+
"$schema": "https://json.schemastore.org/chrome-manifest",
3
+
"manifest_version": 2,
4
+
"name": "moonlight",
5
+
"description": "Yet another Discord mod",
6
+
"version": "1.3.14",
7
+
"permissions": [
8
+
"webRequest",
9
+
"webRequestBlocking",
10
+
"scripting",
11
+
"webNavigation",
12
+
"https://*.discord.com/*",
13
+
"https://*.discordapp.com/*",
14
+
"https://moonlight-mod.github.io/*",
15
+
"https://api.github.com/*"
16
+
],
17
+
"background": {
18
+
"scripts": ["background.js"]
19
+
},
20
+
"content_scripts": [
21
+
{
22
+
"js": ["index.js"],
23
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"],
24
+
"run_at": "document_start",
25
+
"world": "MAIN"
26
+
}
27
+
],
28
+
"browser_specific_settings": {
29
+
"gecko": {
30
+
"id": "{0fb6d66f-f22d-4555-a87b-34ef4bea5e2a}"
31
+
}
32
+
}
33
+
}
+19
packages/browser/modifyResponseHeaders.json
+19
packages/browser/modifyResponseHeaders.json
···
1
+
[
2
+
{
3
+
"id": 1,
4
+
"priority": 2,
5
+
"action": {
6
+
"type": "modifyHeaders",
7
+
"responseHeaders": [
8
+
{
9
+
"header": "Content-Security-Policy",
10
+
"operation": "remove"
11
+
}
12
+
]
13
+
},
14
+
"condition": {
15
+
"resourceTypes": ["main_frame"],
16
+
"requestDomains": ["discord.com"]
17
+
}
18
+
}
19
+
]
+21
packages/browser/package.json
+21
packages/browser/package.json
···
1
+
{
2
+
"name": "@moonlight-mod/browser",
3
+
"private": true,
4
+
"engines": {
5
+
"node": ">=22",
6
+
"pnpm": ">=10",
7
+
"npm": "pnpm",
8
+
"yarn": "pnpm"
9
+
},
10
+
"dependencies": {
11
+
"@moonlight-mod/core": "workspace:*",
12
+
"@moonlight-mod/types": "workspace:*",
13
+
"@moonlight-mod/web-preload": "workspace:*",
14
+
"@zenfs/core": "catalog:prod",
15
+
"@zenfs/dom": "catalog:prod"
16
+
},
17
+
"engineStrict": true,
18
+
"devDependencies": {
19
+
"@types/chrome": "catalog:dev"
20
+
}
21
+
}
+84
packages/browser/src/background-mv2.js
+84
packages/browser/src/background-mv2.js
···
1
+
/* eslint-disable no-console */
2
+
/* eslint-disable no-undef */
3
+
4
+
const scriptUrls = ["web.", "sentry."];
5
+
let blockedScripts = new Set();
6
+
7
+
chrome.webRequest.onBeforeRequest.addListener(
8
+
async (details) => {
9
+
if (details.tabId === -1) return;
10
+
11
+
const url = new URL(details.url);
12
+
const hasUrl = scriptUrls.some((scriptUrl) => {
13
+
return (
14
+
details.url.includes(scriptUrl) &&
15
+
!url.searchParams.has("inj") &&
16
+
(url.host.endsWith("discord.com") || url.host.endsWith("discordapp.com"))
17
+
);
18
+
});
19
+
if (hasUrl) blockedScripts.add(details.url);
20
+
21
+
if (blockedScripts.size === scriptUrls.length) {
22
+
const blockedScriptsCopy = Array.from(blockedScripts);
23
+
blockedScripts.clear();
24
+
25
+
setTimeout(async () => {
26
+
console.log("Starting moonlight");
27
+
await chrome.scripting.executeScript({
28
+
target: { tabId: details.tabId },
29
+
world: "MAIN",
30
+
args: [blockedScriptsCopy],
31
+
func: async (blockedScripts) => {
32
+
console.log("Initializing moonlight");
33
+
try {
34
+
await window._moonlightBrowserInit();
35
+
} catch (e) {
36
+
console.error(e);
37
+
}
38
+
39
+
console.log("Readding scripts");
40
+
try {
41
+
const scripts = [...document.querySelectorAll("script")].filter(
42
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
43
+
);
44
+
45
+
blockedScripts.reverse();
46
+
for (const url of blockedScripts) {
47
+
if (url.includes("/sentry.")) continue;
48
+
49
+
const script = scripts.find((script) => url.includes(script.src));
50
+
const newScript = document.createElement("script");
51
+
for (const attr of script.attributes) {
52
+
if (attr.name === "src") attr.value += "?inj";
53
+
newScript.setAttribute(attr.name, attr.value);
54
+
}
55
+
script.remove();
56
+
document.documentElement.appendChild(newScript);
57
+
}
58
+
} catch (e) {
59
+
console.error(e);
60
+
}
61
+
}
62
+
});
63
+
}, 0);
64
+
}
65
+
66
+
if (hasUrl) return { cancel: true };
67
+
},
68
+
{
69
+
urls: ["https://*.discord.com/assets/*.js", "https://*.discordapp.com/assets/*.js"]
70
+
},
71
+
["blocking"]
72
+
);
73
+
74
+
chrome.webRequest.onHeadersReceived.addListener(
75
+
(details) => {
76
+
return {
77
+
responseHeaders: details.responseHeaders.filter(
78
+
(header) => header.name.toLowerCase() !== "content-security-policy"
79
+
)
80
+
};
81
+
},
82
+
{ urls: ["https://*.discord.com/*", "https://*.discordapp.com/*"] },
83
+
["blocking", "responseHeaders"]
84
+
);
+111
packages/browser/src/background.js
+111
packages/browser/src/background.js
···
1
+
/* eslint-disable no-console */
2
+
/* eslint-disable no-undef */
3
+
4
+
const scriptUrls = ["web.", "sentry."];
5
+
let blockedScripts = new Set();
6
+
7
+
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
8
+
const url = new URL(details.url);
9
+
if (
10
+
!url.searchParams.has("inj") &&
11
+
(url.hostname.endsWith("discord.com") || url.hostname.endsWith("discordapp.com"))
12
+
) {
13
+
console.log("Enabling block ruleset");
14
+
await chrome.declarativeNetRequest.updateEnabledRulesets({
15
+
enableRulesetIds: ["modifyResponseHeaders", "blockLoading"]
16
+
});
17
+
}
18
+
});
19
+
20
+
chrome.webRequest.onBeforeRequest.addListener(
21
+
async (details) => {
22
+
if (details.tabId === -1) return;
23
+
24
+
const url = new URL(details.url);
25
+
const hasUrl = scriptUrls.some((scriptUrl) => {
26
+
return (
27
+
details.url.includes(scriptUrl) &&
28
+
!url.searchParams.has("inj") &&
29
+
(url.hostname.endsWith("discord.com") || url.hostname.endsWith("discordapp.com"))
30
+
);
31
+
});
32
+
33
+
if (hasUrl) blockedScripts.add(details.url);
34
+
35
+
if (blockedScripts.size === scriptUrls.length) {
36
+
const blockedScriptsCopy = Array.from(blockedScripts);
37
+
blockedScripts.clear();
38
+
39
+
console.log("Running moonlight script");
40
+
try {
41
+
await chrome.scripting.executeScript({
42
+
target: { tabId: details.tabId },
43
+
world: "MAIN",
44
+
files: ["index.js"]
45
+
});
46
+
} catch (e) {
47
+
console.error(e);
48
+
}
49
+
50
+
console.log("Initializing moonlight");
51
+
try {
52
+
await chrome.scripting.executeScript({
53
+
target: { tabId: details.tabId },
54
+
world: "MAIN",
55
+
func: async () => {
56
+
try {
57
+
await window._moonlightBrowserInit();
58
+
} catch (e) {
59
+
console.error(e);
60
+
}
61
+
}
62
+
});
63
+
} catch (e) {
64
+
console.log(e);
65
+
}
66
+
67
+
console.log("Disabling block ruleset");
68
+
try {
69
+
await chrome.declarativeNetRequest.updateEnabledRulesets({
70
+
disableRulesetIds: ["blockLoading"],
71
+
enableRulesetIds: ["modifyResponseHeaders"]
72
+
});
73
+
} catch (e) {
74
+
console.error(e);
75
+
}
76
+
77
+
console.log("Readding scripts");
78
+
try {
79
+
await chrome.scripting.executeScript({
80
+
target: { tabId: details.tabId },
81
+
world: "MAIN",
82
+
args: [blockedScriptsCopy],
83
+
func: async (blockedScripts) => {
84
+
const scripts = [...document.querySelectorAll("script")].filter(
85
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
86
+
);
87
+
88
+
blockedScripts.reverse();
89
+
for (const url of blockedScripts) {
90
+
if (url.includes("/sentry.")) continue;
91
+
92
+
const script = scripts.find((script) => url.includes(script.src));
93
+
const newScript = document.createElement("script");
94
+
for (const attr of script.attributes) {
95
+
if (attr.name === "src") attr.value += "?inj";
96
+
newScript.setAttribute(attr.name, attr.value);
97
+
}
98
+
script.remove();
99
+
document.documentElement.appendChild(newScript);
100
+
}
101
+
}
102
+
});
103
+
} catch (e) {
104
+
console.error(e);
105
+
}
106
+
}
107
+
},
108
+
{
109
+
urls: ["*://*.discord.com/assets/*.js", "*://*.discordapp.com/assets/*.js"]
110
+
}
111
+
);
+161
packages/browser/src/index.ts
+161
packages/browser/src/index.ts
···
1
+
import "@moonlight-mod/web-preload";
2
+
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
3
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
4
+
import { getExtensions } from "@moonlight-mod/core/extension";
5
+
import { loadExtensions } from "@moonlight-mod/core/extension/loader";
6
+
import { MoonlightBranch, MoonlightNode } from "@moonlight-mod/types";
7
+
import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
8
+
import { IndexedDB } from "@zenfs/dom";
9
+
import { configureSingle } from "@zenfs/core";
10
+
import * as fs from "@zenfs/core/promises";
11
+
import { NodeEventPayloads, NodeEventType } from "@moonlight-mod/types/core/event";
12
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
13
+
14
+
function getParts(path: string) {
15
+
if (path.startsWith("/")) path = path.substring(1);
16
+
return path.split("/");
17
+
}
18
+
19
+
window._moonlightBrowserInit = async () => {
20
+
delete window._moonlightBrowserInit;
21
+
22
+
// Set up a virtual filesystem with IndexedDB
23
+
await configureSingle({
24
+
backend: IndexedDB,
25
+
storeName: "moonlight-fs"
26
+
});
27
+
28
+
window.moonlightNodeSandboxed = {
29
+
fs: {
30
+
async readFile(path) {
31
+
return new Uint8Array(await fs.readFile(path));
32
+
},
33
+
async readFileString(path) {
34
+
const file = await this.readFile(path);
35
+
return new TextDecoder().decode(file);
36
+
},
37
+
async writeFile(path, data) {
38
+
await fs.writeFile(path, data);
39
+
},
40
+
async writeFileString(path, data) {
41
+
const file = new TextEncoder().encode(data);
42
+
await this.writeFile(path, file);
43
+
},
44
+
async unlink(path) {
45
+
await fs.unlink(path);
46
+
},
47
+
48
+
async readdir(path) {
49
+
return await fs.readdir(path);
50
+
},
51
+
async mkdir(path) {
52
+
const parts = getParts(path);
53
+
for (let i = 0; i < parts.length; i++) {
54
+
const path = this.join(...parts.slice(0, i + 1));
55
+
if (!(await this.exists(path))) await fs.mkdir(path);
56
+
}
57
+
},
58
+
59
+
async rmdir(path) {
60
+
const entries = await this.readdir(path);
61
+
62
+
for (const entry of entries) {
63
+
const fullPath = this.join(path, entry);
64
+
const isFile = await this.isFile(fullPath);
65
+
if (isFile) {
66
+
await this.unlink(fullPath);
67
+
} else {
68
+
await this.rmdir(fullPath);
69
+
}
70
+
}
71
+
72
+
await fs.rmdir(path);
73
+
},
74
+
75
+
async exists(path) {
76
+
return await fs.exists(path);
77
+
},
78
+
async isFile(path) {
79
+
return (await fs.stat(path)).isFile();
80
+
},
81
+
async isDir(path) {
82
+
return (await fs.stat(path)).isDirectory();
83
+
},
84
+
85
+
join(...parts) {
86
+
let str = parts.join("/");
87
+
if (!str.startsWith("/")) str = "/" + str;
88
+
return str;
89
+
},
90
+
dirname(path) {
91
+
const parts = getParts(path);
92
+
return "/" + parts.slice(0, parts.length - 1).join("/");
93
+
},
94
+
basename(path) {
95
+
const parts = getParts(path);
96
+
return parts[parts.length - 1];
97
+
}
98
+
},
99
+
// TODO
100
+
addCors(url) {},
101
+
addBlocked(url) {}
102
+
};
103
+
104
+
// Actual loading begins here
105
+
let config = await readConfig();
106
+
initLogger(config);
107
+
108
+
const extensions = await getExtensions();
109
+
const processedExtensions = await loadExtensions(extensions);
110
+
111
+
const moonlightNode: MoonlightNode = {
112
+
get config() {
113
+
return config;
114
+
},
115
+
extensions,
116
+
processedExtensions,
117
+
nativesCache: {},
118
+
isBrowser: true,
119
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
120
+
121
+
version: MOONLIGHT_VERSION,
122
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
123
+
124
+
getConfig(ext) {
125
+
return getConfig(ext, config);
126
+
},
127
+
getConfigOption(ext, name) {
128
+
const manifest = getManifest(extensions, ext);
129
+
return getConfigOption(ext, name, config, manifest?.settings);
130
+
},
131
+
async setConfigOption(ext, name, value) {
132
+
setConfigOption(config, ext, name, value);
133
+
await this.writeConfig(config);
134
+
},
135
+
136
+
getNatives: () => {},
137
+
getLogger: (id: string) => {
138
+
return new Logger(id);
139
+
},
140
+
141
+
getMoonlightDir() {
142
+
return "/";
143
+
},
144
+
getExtensionDir: (ext: string) => {
145
+
return `/extensions/${ext}`;
146
+
},
147
+
148
+
async writeConfig(newConfig) {
149
+
await writeConfig(newConfig);
150
+
config = newConfig;
151
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
152
+
}
153
+
};
154
+
155
+
Object.assign(window, {
156
+
moonlightNode
157
+
});
158
+
159
+
// This is set by web-preload for us
160
+
await window._moonlightWebLoad!();
161
+
};
+7
packages/browser/tsconfig.json
+7
packages/browser/tsconfig.json
+7
packages/core/package.json
+7
packages/core/package.json
+57
packages/core/src/asar.ts
+57
packages/core/src/asar.ts
···
1
+
// https://github.com/electron/asar
2
+
// http://formats.kaitai.io/python_pickle/
3
+
import { BinaryReader } from "./util/binary";
4
+
5
+
/*
6
+
The asar format is kinda bad, especially because it uses multiple pickle
7
+
entries. It spams sizes, expecting us to read small buffers and parse those,
8
+
but we can just take it all through at once without having to create multiple
9
+
BinaryReaders. This implementation might be wrong, though.
10
+
11
+
This either has size/offset or files but I can't get the type to cooperate,
12
+
so pretend this is a union.
13
+
*/
14
+
15
+
type AsarEntry = {
16
+
size: number;
17
+
offset: `${number}`; // who designed this
18
+
19
+
files?: Record<string, AsarEntry>;
20
+
};
21
+
22
+
export default function extractAsar(file: ArrayBuffer) {
23
+
const array = new Uint8Array(file);
24
+
const br = new BinaryReader(array);
25
+
26
+
// two uints, one containing the number '4', to signify that the other uint takes up 4 bytes
27
+
// bravo, electron, bravo
28
+
const _payloadSize = br.readUInt32();
29
+
const _headerSize = br.readInt32();
30
+
31
+
const headerStringStart = br.position;
32
+
const headerStringSize = br.readUInt32(); // How big the block is
33
+
const actualStringSize = br.readUInt32(); // How big the string in that block is
34
+
35
+
const base = headerStringStart + headerStringSize + 4;
36
+
37
+
const string = br.readString(actualStringSize);
38
+
const header: AsarEntry = JSON.parse(string);
39
+
40
+
const ret: Record<string, Uint8Array> = {};
41
+
function addDirectory(dir: AsarEntry, path: string) {
42
+
for (const [name, data] of Object.entries(dir.files!)) {
43
+
const fullName = path + "/" + name;
44
+
if (data.files != null) {
45
+
addDirectory(data, fullName);
46
+
} else {
47
+
br.position = base + parseInt(data.offset);
48
+
const file = br.read(data.size);
49
+
ret[fullName] = file;
50
+
}
51
+
}
52
+
}
53
+
54
+
addDirectory(header, "");
55
+
56
+
return ret;
57
+
}
+31
-31
packages/core/src/config.ts
+31
-31
packages/core/src/config.ts
···
1
1
import { Config } from "@moonlight-mod/types";
2
-
import requireImport from "./util/import";
3
2
import { getConfigPath } from "./util/data";
3
+
import * as constants from "@moonlight-mod/types/constants";
4
+
import Logger from "./util/logger";
5
+
6
+
const logger = new Logger("core/config");
4
7
5
8
const defaultConfig: Config = {
9
+
// If you're updating this, update `builtinExtensions` in constants as well
6
10
extensions: {
7
11
moonbase: true,
8
12
disableSentry: true,
9
13
noTrack: true,
10
14
noHideToken: true
11
15
},
12
-
repositories: ["https://moonlight-mod.github.io/extensions-dist/repo.json"]
16
+
repositories: [constants.mainRepo]
13
17
};
14
18
15
-
export function writeConfig(config: Config) {
16
-
const fs = requireImport("fs");
17
-
const configPath = getConfigPath();
18
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
19
-
}
20
-
21
-
function readConfigNode(): Config {
22
-
const fs = requireImport("fs");
23
-
const configPath = getConfigPath();
24
-
25
-
if (!fs.existsSync(configPath)) {
26
-
writeConfig(defaultConfig);
27
-
return defaultConfig;
19
+
export async function writeConfig(config: Config) {
20
+
try {
21
+
const configPath = await getConfigPath();
22
+
await moonlightNodeSandboxed.fs.writeFileString(configPath, JSON.stringify(config, null, 2));
23
+
} catch (e) {
24
+
logger.error("Failed to write config", e);
28
25
}
29
-
30
-
let config: Config = JSON.parse(fs.readFileSync(configPath, "utf8"));
31
-
32
-
// Assign the default values if they don't exist (newly added)
33
-
config = { ...defaultConfig, ...config };
34
-
writeConfig(config);
35
-
36
-
return config;
37
26
}
38
27
39
-
export function readConfig(): Config {
28
+
export async function readConfig(): Promise<Config> {
40
29
webPreload: {
41
30
return moonlightNode.config;
42
31
}
43
32
44
-
nodePreload: {
45
-
return readConfigNode();
46
-
}
33
+
const configPath = await getConfigPath();
34
+
if (!(await moonlightNodeSandboxed.fs.exists(configPath))) {
35
+
await writeConfig(defaultConfig);
36
+
return defaultConfig;
37
+
} else {
38
+
try {
39
+
let config: Config = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(configPath));
40
+
// Assign the default values if they don't exist (newly added)
41
+
config = { ...defaultConfig, ...config };
42
+
await writeConfig(config);
47
43
48
-
injector: {
49
-
return readConfigNode();
44
+
return config;
45
+
} catch (e) {
46
+
logger.error("Failed to read config, falling back to defaults", e);
47
+
// We don't want to write the default config here - if a user is manually
48
+
// editing their config and messes it up, we'll delete it all instead of
49
+
// letting them fix it
50
+
return defaultConfig;
51
+
}
50
52
}
51
-
52
-
throw new Error("Called readConfig() in an impossible environment");
53
53
}
+17
packages/core/src/cors.ts
+17
packages/core/src/cors.ts
···
1
+
const cors: string[] = [];
2
+
const blocked: string[] = [];
3
+
4
+
export function registerCors(url: string) {
5
+
cors.push(url);
6
+
}
7
+
8
+
export function registerBlocked(url: string) {
9
+
blocked.push(url);
10
+
}
11
+
12
+
export function getDynamicCors() {
13
+
return {
14
+
cors,
15
+
blocked
16
+
};
17
+
}
+111
-70
packages/core/src/extension/loader.ts
+111
-70
packages/core/src/extension/loader.ts
···
2
2
ExtensionWebExports,
3
3
DetectedExtension,
4
4
ProcessedExtensions,
5
-
WebpackModuleFunc
5
+
WebpackModuleFunc,
6
+
constants,
7
+
ExtensionManifest,
8
+
ExtensionEnvironment
6
9
} from "@moonlight-mod/types";
7
10
import { readConfig } from "../config";
8
11
import Logger from "../util/logger";
···
10
13
import calculateDependencies from "../util/dependency";
11
14
import { createEventEmitter } from "../util/event";
12
15
import { registerStyles } from "../styles";
16
+
import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event";
13
17
14
18
const logger = new Logger("core/extension/loader");
15
19
16
-
async function loadExt(ext: DetectedExtension) {
17
-
webPreload: {
18
-
if (ext.scripts.web != null) {
19
-
const source =
20
-
ext.scripts.web + "\n//# sourceURL=file:///" + ext.scripts.webPath;
21
-
const fn = new Function("require", "module", "exports", source);
20
+
function evalIIFE(id: string, source: string): ExtensionWebExports {
21
+
const fn = new Function("require", "module", "exports", source);
22
22
23
-
const module = { id: ext.id, exports: {} };
24
-
fn.apply(window, [
25
-
() => {
26
-
logger.warn("Attempted to require() from web");
27
-
},
28
-
module,
29
-
module.exports
30
-
]);
23
+
const module = { id, exports: {} };
24
+
fn.apply(window, [
25
+
() => {
26
+
logger.warn("Attempted to require() from web");
27
+
},
28
+
module,
29
+
module.exports
30
+
]);
31
+
32
+
return module.exports;
33
+
}
34
+
35
+
async function evalEsm(source: string): Promise<ExtensionWebExports> {
36
+
// Data URLs (`data:`) don't seem to work under the CSP, but object URLs do
37
+
const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
38
+
39
+
const module = await import(url);
40
+
41
+
URL.revokeObjectURL(url);
42
+
43
+
return module;
44
+
}
45
+
46
+
async function loadExtWeb(ext: DetectedExtension) {
47
+
if (ext.scripts.web != null) {
48
+
const source = ext.scripts.web + `\n//# sourceURL=${ext.id}/web.js`;
49
+
50
+
let exports: ExtensionWebExports;
31
51
32
-
const exports: ExtensionWebExports = module.exports;
33
-
if (exports.patches != null) {
34
-
let idx = 0;
35
-
for (const patch of exports.patches) {
36
-
if (Array.isArray(patch.replace)) {
37
-
for (const replacement of patch.replace) {
38
-
const newPatch = Object.assign({}, patch, {
39
-
replace: replacement
40
-
});
52
+
try {
53
+
exports = evalIIFE(ext.id, source);
54
+
} catch {
55
+
logger.trace(`Failed to load IIFE for extension ${ext.id}, trying ESM loading`);
56
+
exports = await evalEsm(source);
57
+
}
41
58
42
-
registerPatch({ ...newPatch, ext: ext.id, id: idx });
43
-
idx++;
44
-
}
45
-
} else {
46
-
registerPatch({ ...patch, ext: ext.id, id: idx });
47
-
idx++;
48
-
}
59
+
if (exports.patches != null) {
60
+
let idx = 0;
61
+
for (const patch of exports.patches) {
62
+
if (Array.isArray(patch.replace)) {
63
+
registerPatch({ ...patch, ext: ext.id, id: idx });
64
+
} else {
65
+
registerPatch({ ...patch, replace: [patch.replace], ext: ext.id, id: idx });
49
66
}
67
+
idx++;
50
68
}
69
+
}
51
70
52
-
if (exports.webpackModules != null) {
53
-
for (const [name, wp] of Object.entries(exports.webpackModules)) {
54
-
if (wp.run == null && ext.scripts.webpackModules?.[name] != null) {
55
-
const func = new Function(
56
-
"module",
57
-
"exports",
58
-
"require",
59
-
ext.scripts.webpackModules[name]!
60
-
) as WebpackModuleFunc;
61
-
registerWebpackModule({
62
-
...wp,
63
-
ext: ext.id,
64
-
id: name,
65
-
run: func
66
-
});
67
-
} else {
68
-
registerWebpackModule({ ...wp, ext: ext.id, id: name });
69
-
}
71
+
if (exports.webpackModules != null) {
72
+
for (const [name, wp] of Object.entries(exports.webpackModules)) {
73
+
if (wp.run == null && ext.scripts.webpackModules?.[name] != null) {
74
+
const source = ext.scripts.webpackModules[name]! + `\n//# sourceURL=${ext.id}/webpackModules/${name}.js`;
75
+
const func = new Function("module", "exports", "require", source) as WebpackModuleFunc;
76
+
registerWebpackModule({
77
+
...wp,
78
+
ext: ext.id,
79
+
id: name,
80
+
run: func
81
+
});
82
+
} else {
83
+
registerWebpackModule({ ...wp, ext: ext.id, id: name });
70
84
}
71
85
}
86
+
}
72
87
73
-
if (exports.styles != null) {
74
-
registerStyles(
75
-
exports.styles.map((style, i) => `/* ${ext.id}#${i} */ ${style}`)
76
-
);
77
-
}
88
+
if (exports.styles != null) {
89
+
registerStyles(exports.styles.map((style, i) => `/* ${ext.id}#${i} */ ${style}`));
90
+
}
91
+
if (ext.scripts.style != null) {
92
+
registerStyles([`/* ${ext.id}#style.css */ ${ext.scripts.style}`]);
93
+
}
94
+
}
95
+
}
96
+
97
+
async function loadExt(ext: DetectedExtension) {
98
+
webTarget: {
99
+
try {
100
+
await loadExtWeb(ext);
101
+
} catch (e) {
102
+
logger.error(`Failed to load extension "${ext.id}"`, e);
78
103
}
79
104
}
80
105
···
100
125
}
101
126
}
102
127
128
+
export enum ExtensionCompat {
129
+
Compatible,
130
+
InvalidApiLevel,
131
+
InvalidEnvironment
132
+
}
133
+
134
+
export function checkExtensionCompat(manifest: ExtensionManifest): ExtensionCompat {
135
+
let environment;
136
+
webTarget: {
137
+
environment = ExtensionEnvironment.Web;
138
+
}
139
+
nodeTarget: {
140
+
environment = ExtensionEnvironment.Desktop;
141
+
}
142
+
143
+
if (manifest.apiLevel !== constants.apiLevel) return ExtensionCompat.InvalidApiLevel;
144
+
if ((manifest.environment ?? "both") !== "both" && manifest.environment !== environment)
145
+
return ExtensionCompat.InvalidEnvironment;
146
+
return ExtensionCompat.Compatible;
147
+
}
148
+
103
149
/*
104
150
This function resolves extensions and loads them, split into a few stages:
105
151
···
114
160
extensions fires an event on completion, which allows us to await the loading
115
161
of another extension, resolving dependencies & load order effectively.
116
162
*/
117
-
export async function loadExtensions(
118
-
exts: DetectedExtension[]
119
-
): Promise<ProcessedExtensions> {
120
-
const config = readConfig();
163
+
export async function loadExtensions(exts: DetectedExtension[]): Promise<ProcessedExtensions> {
164
+
exts = exts.filter((ext) => checkExtensionCompat(ext.manifest) === ExtensionCompat.Compatible);
165
+
166
+
const config = await readConfig();
121
167
const items = exts
122
168
.map((ext) => {
123
169
return {
···
155
201
};
156
202
}
157
203
158
-
export async function loadProcessedExtensions({
159
-
extensions,
160
-
dependencyGraph
161
-
}: ProcessedExtensions) {
162
-
const eventEmitter = createEventEmitter();
204
+
export async function loadProcessedExtensions({ extensions, dependencyGraph }: ProcessedExtensions) {
205
+
const eventEmitter = createEventEmitter<WebEventType, WebEventPayloads>();
163
206
const finished: Set<string> = new Set();
164
207
165
208
logger.trace(
···
181
224
}
182
225
183
226
function done() {
184
-
eventEmitter.removeEventListener("ext-ready", cb);
227
+
eventEmitter.removeEventListener(WebEventType.ExtensionLoad, cb);
185
228
r();
186
229
}
187
230
188
-
eventEmitter.addEventListener("ext-ready", cb);
231
+
eventEmitter.addEventListener(WebEventType.ExtensionLoad, cb);
189
232
if (finished.has(dep)) done();
190
233
})
191
234
);
192
235
193
236
if (waitPromises.length > 0) {
194
-
logger.debug(
195
-
`Waiting on ${waitPromises.length} dependencies for "${ext.id}"`
196
-
);
237
+
logger.debug(`Waiting on ${waitPromises.length} dependencies for "${ext.id}"`);
197
238
await Promise.all(waitPromises);
198
239
}
199
240
···
201
242
await loadExt(ext);
202
243
203
244
finished.add(ext.id);
204
-
eventEmitter.dispatchEvent("ext-ready", ext.id);
245
+
eventEmitter.dispatchEvent(WebEventType.ExtensionLoad, ext.id);
205
246
logger.debug(`Loaded "${ext.id}"`);
206
247
}
207
248
208
-
webPreload: {
249
+
webTarget: {
209
250
for (const ext of extensions) {
210
251
moonlight.enabledExtensions.add(ext.id);
211
252
}
+128
-85
packages/core/src/extension.ts
+128
-85
packages/core/src/extension.ts
···
1
-
import {
2
-
ExtensionManifest,
3
-
DetectedExtension,
4
-
ExtensionLoadSource,
5
-
constants
6
-
} from "@moonlight-mod/types";
1
+
import { ExtensionManifest, DetectedExtension, ExtensionLoadSource, constants } from "@moonlight-mod/types";
7
2
import { readConfig } from "./config";
8
-
import requireImport from "./util/import";
9
3
import { getCoreExtensionsPath, getExtensionsPath } from "./util/data";
4
+
import Logger from "./util/logger";
5
+
6
+
const logger = new Logger("core/extension");
10
7
11
-
function findManifests(dir: string): string[] {
12
-
const fs = requireImport("fs");
13
-
const path = requireImport("path");
8
+
async function findManifests(dir: string): Promise<string[]> {
14
9
const ret = [];
15
10
16
-
if (fs.existsSync(dir)) {
17
-
for (const file of fs.readdirSync(dir)) {
11
+
if (await moonlightNodeSandboxed.fs.exists(dir)) {
12
+
for (const file of await moonlightNodeSandboxed.fs.readdir(dir)) {
13
+
const path = moonlightNodeSandboxed.fs.join(dir, file);
18
14
if (file === "manifest.json") {
19
-
ret.push(path.join(dir, file));
15
+
ret.push(path);
20
16
}
21
17
22
-
if (fs.statSync(path.join(dir, file)).isDirectory()) {
23
-
ret.push(...findManifests(path.join(dir, file)));
18
+
if (!(await moonlightNodeSandboxed.fs.isFile(path))) {
19
+
ret.push(...(await findManifests(path)));
24
20
}
25
21
}
26
22
}
···
28
24
return ret;
29
25
}
30
26
31
-
function loadDetectedExtensions(
27
+
async function loadDetectedExtensions(
32
28
dir: string,
33
-
type: ExtensionLoadSource
34
-
): DetectedExtension[] {
35
-
const fs = requireImport("fs");
36
-
const path = requireImport("path");
29
+
type: ExtensionLoadSource,
30
+
seen: Set<string>
31
+
): Promise<DetectedExtension[]> {
37
32
const ret: DetectedExtension[] = [];
38
33
39
-
const manifests = findManifests(dir);
34
+
const manifests = await findManifests(dir);
40
35
for (const manifestPath of manifests) {
41
-
if (!fs.existsSync(manifestPath)) continue;
42
-
const dir = path.dirname(manifestPath);
36
+
try {
37
+
if (!(await moonlightNodeSandboxed.fs.exists(manifestPath))) continue;
38
+
const dir = moonlightNodeSandboxed.fs.dirname(manifestPath);
39
+
40
+
const manifest: ExtensionManifest = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(manifestPath));
41
+
if (seen.has(manifest.id)) {
42
+
logger.warn(`Duplicate extension found, skipping: ${manifest.id}`);
43
+
continue;
44
+
}
45
+
seen.add(manifest.id);
46
+
47
+
const webPath = moonlightNodeSandboxed.fs.join(dir, "index.js");
48
+
const nodePath = moonlightNodeSandboxed.fs.join(dir, "node.js");
49
+
const hostPath = moonlightNodeSandboxed.fs.join(dir, "host.js");
50
+
51
+
// if none exist (empty manifest) don't give a shit
52
+
if (
53
+
!moonlightNodeSandboxed.fs.exists(webPath) &&
54
+
!moonlightNodeSandboxed.fs.exists(nodePath) &&
55
+
!moonlightNodeSandboxed.fs.exists(hostPath)
56
+
) {
57
+
continue;
58
+
}
59
+
60
+
const web = (await moonlightNodeSandboxed.fs.exists(webPath))
61
+
? await moonlightNodeSandboxed.fs.readFileString(webPath)
62
+
: undefined;
63
+
64
+
let url: string | undefined = undefined;
65
+
const urlPath = moonlightNodeSandboxed.fs.join(dir, constants.repoUrlFile);
66
+
if (type === ExtensionLoadSource.Normal && (await moonlightNodeSandboxed.fs.exists(urlPath))) {
67
+
url = await moonlightNodeSandboxed.fs.readFileString(urlPath);
68
+
}
69
+
70
+
const wpModules: Record<string, string> = {};
71
+
const wpModulesPath = moonlightNodeSandboxed.fs.join(dir, "webpackModules");
72
+
if (await moonlightNodeSandboxed.fs.exists(wpModulesPath)) {
73
+
const wpModulesFile = await moonlightNodeSandboxed.fs.readdir(wpModulesPath);
43
74
44
-
const manifest: ExtensionManifest = JSON.parse(
45
-
fs.readFileSync(manifestPath, "utf8")
46
-
);
75
+
for (const wpModuleFile of wpModulesFile) {
76
+
if (wpModuleFile.endsWith(".js")) {
77
+
wpModules[wpModuleFile.replace(".js", "")] = await moonlightNodeSandboxed.fs.readFileString(
78
+
moonlightNodeSandboxed.fs.join(wpModulesPath, wpModuleFile)
79
+
);
80
+
}
81
+
}
82
+
}
47
83
48
-
const webPath = path.join(dir, "index.js");
49
-
const nodePath = path.join(dir, "node.js");
50
-
const hostPath = path.join(dir, "host.js");
84
+
const stylePath = moonlightNodeSandboxed.fs.join(dir, "style.css");
51
85
52
-
// if none exist (empty manifest) don't give a shit
53
-
if (
54
-
!fs.existsSync(webPath) &&
55
-
!fs.existsSync(nodePath) &&
56
-
!fs.existsSync(hostPath)
57
-
) {
58
-
continue;
86
+
ret.push({
87
+
id: manifest.id,
88
+
manifest,
89
+
source: {
90
+
type,
91
+
url
92
+
},
93
+
scripts: {
94
+
web,
95
+
webPath: web != null ? webPath : undefined,
96
+
webpackModules: wpModules,
97
+
nodePath: (await moonlightNodeSandboxed.fs.exists(nodePath)) ? nodePath : undefined,
98
+
hostPath: (await moonlightNodeSandboxed.fs.exists(hostPath)) ? hostPath : undefined,
99
+
style: (await moonlightNodeSandboxed.fs.exists(stylePath))
100
+
? await moonlightNodeSandboxed.fs.readFileString(stylePath)
101
+
: undefined
102
+
}
103
+
});
104
+
} catch (err) {
105
+
logger.error(`Failed to load extension from "${manifestPath}":`, err);
59
106
}
107
+
}
108
+
109
+
return ret;
110
+
}
111
+
112
+
async function getExtensionsNative(): Promise<DetectedExtension[]> {
113
+
const config = await readConfig();
114
+
const res = [];
115
+
const seen = new Set<string>();
116
+
117
+
res.push(...(await loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core, seen)));
118
+
119
+
for (const devSearchPath of config.devSearchPaths ?? []) {
120
+
res.push(...(await loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer, seen)));
121
+
}
60
122
61
-
const web = fs.existsSync(webPath)
62
-
? fs.readFileSync(webPath, "utf8")
63
-
: undefined;
123
+
res.push(...(await loadDetectedExtensions(await getExtensionsPath(), ExtensionLoadSource.Normal, seen)));
64
124
65
-
let url: string | undefined = undefined;
66
-
const urlPath = path.join(dir, constants.repoUrlFile);
67
-
if (type === ExtensionLoadSource.Normal && fs.existsSync(urlPath)) {
68
-
url = fs.readFileSync(urlPath, "utf8");
69
-
}
125
+
return res;
126
+
}
70
127
71
-
const wpModules: Record<string, string> = {};
72
-
const wpModulesPath = path.join(dir, "webpackModules");
73
-
if (fs.existsSync(wpModulesPath)) {
74
-
const wpModulesFile = fs.readdirSync(wpModulesPath);
128
+
async function getExtensionsBrowser(): Promise<DetectedExtension[]> {
129
+
const ret: DetectedExtension[] = [];
130
+
const seen = new Set<string>();
75
131
76
-
for (const wpModuleFile of wpModulesFile) {
77
-
if (wpModuleFile.endsWith(".js")) {
78
-
wpModules[wpModuleFile.replace(".js", "")] = fs.readFileSync(
79
-
path.join(wpModulesPath, wpModuleFile),
80
-
"utf8"
81
-
);
82
-
}
132
+
const coreExtensionsFs: Record<string, string> = JSON.parse(_moonlight_coreExtensionsStr);
133
+
const coreExtensions = Array.from(new Set(Object.keys(coreExtensionsFs).map((x) => x.split("/")[0])));
134
+
135
+
for (const ext of coreExtensions) {
136
+
if (!coreExtensionsFs[`${ext}/index.js`]) continue;
137
+
const manifest = JSON.parse(coreExtensionsFs[`${ext}/manifest.json`]);
138
+
const web = coreExtensionsFs[`${ext}/index.js`];
139
+
140
+
const wpModules: Record<string, string> = {};
141
+
const wpModulesPath = `${ext}/webpackModules`;
142
+
for (const wpModuleFile of Object.keys(coreExtensionsFs)) {
143
+
if (wpModuleFile.startsWith(wpModulesPath)) {
144
+
wpModules[wpModuleFile.replace(wpModulesPath + "/", "").replace(".js", "")] = coreExtensionsFs[wpModuleFile];
83
145
}
84
146
}
85
147
···
87
149
id: manifest.id,
88
150
manifest,
89
151
source: {
90
-
type,
91
-
url
152
+
type: ExtensionLoadSource.Core
92
153
},
93
154
scripts: {
94
155
web,
95
-
webPath: web != null ? webPath : undefined,
96
156
webpackModules: wpModules,
97
-
nodePath: fs.existsSync(nodePath) ? nodePath : undefined,
98
-
hostPath: fs.existsSync(hostPath) ? hostPath : undefined
157
+
style: coreExtensionsFs[`${ext}/style.css`]
99
158
}
100
159
});
160
+
seen.add(manifest.id);
101
161
}
102
162
103
-
return ret;
104
-
}
105
-
106
-
function getExtensionsNative(): DetectedExtension[] {
107
-
const config = readConfig();
108
-
const res = [];
109
-
110
-
res.push(
111
-
...loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core)
112
-
);
113
-
114
-
res.push(
115
-
...loadDetectedExtensions(getExtensionsPath(), ExtensionLoadSource.Normal)
116
-
);
117
-
118
-
for (const devSearchPath of config.devSearchPaths ?? []) {
119
-
res.push(
120
-
...loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer)
121
-
);
163
+
if (await moonlightNodeSandboxed.fs.exists("/extensions")) {
164
+
ret.push(...(await loadDetectedExtensions("/extensions", ExtensionLoadSource.Normal, seen)));
122
165
}
123
166
124
-
return res;
167
+
return ret;
125
168
}
126
169
127
-
export function getExtensions(): DetectedExtension[] {
170
+
export async function getExtensions(): Promise<DetectedExtension[]> {
128
171
webPreload: {
129
172
return moonlightNode.extensions;
130
173
}
131
174
132
-
nodePreload: {
133
-
return getExtensionsNative();
175
+
browser: {
176
+
return await getExtensionsBrowser();
134
177
}
135
178
136
-
injector: {
137
-
return getExtensionsNative();
179
+
nodeTarget: {
180
+
return await getExtensionsNative();
138
181
}
139
182
140
183
throw new Error("Called getExtensions() outside of node-preload/web-preload");
+56
packages/core/src/fs.ts
+56
packages/core/src/fs.ts
···
1
+
import type { MoonlightFS } from "@moonlight-mod/types";
2
+
import requireImport from "./util/import";
3
+
4
+
export default function createFS(): MoonlightFS {
5
+
const fs = requireImport("fs");
6
+
const path = requireImport("path");
7
+
8
+
return {
9
+
async readFile(path) {
10
+
const file = fs.readFileSync(path);
11
+
return new Uint8Array(file);
12
+
},
13
+
async readFileString(path) {
14
+
return fs.readFileSync(path, "utf8");
15
+
},
16
+
async writeFile(path, data) {
17
+
fs.writeFileSync(path, Buffer.from(data));
18
+
},
19
+
async writeFileString(path, data) {
20
+
fs.writeFileSync(path, data, "utf8");
21
+
},
22
+
async unlink(path) {
23
+
fs.unlinkSync(path);
24
+
},
25
+
26
+
async readdir(path) {
27
+
return fs.readdirSync(path);
28
+
},
29
+
async mkdir(path) {
30
+
fs.mkdirSync(path, { recursive: true });
31
+
},
32
+
async rmdir(path) {
33
+
fs.rmSync(path, { recursive: true });
34
+
},
35
+
36
+
async exists(path) {
37
+
return fs.existsSync(path);
38
+
},
39
+
async isFile(path) {
40
+
return fs.statSync(path).isFile();
41
+
},
42
+
async isDir(path) {
43
+
return fs.statSync(path).isDirectory();
44
+
},
45
+
46
+
join(...parts) {
47
+
return path.join(...parts);
48
+
},
49
+
dirname(dir) {
50
+
return path.dirname(dir);
51
+
},
52
+
basename(dir) {
53
+
return path.basename(dir);
54
+
}
55
+
};
56
+
}
+238
-106
packages/core/src/patch.ts
+238
-106
packages/core/src/patch.ts
···
6
6
IdentifiedWebpackModule,
7
7
WebpackJsonp,
8
8
WebpackJsonpEntry,
9
-
WebpackModuleFunc
9
+
WebpackModuleFunc,
10
+
WebpackRequireType
10
11
} from "@moonlight-mod/types";
11
12
import Logger from "./util/logger";
12
13
import calculateDependencies, { Dependency } from "./util/dependency";
13
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
14
+
import { WebEventType } from "@moonlight-mod/types/core/event";
15
+
import { processFind, processReplace, testFind } from "./util/patch";
14
16
15
17
const logger = new Logger("core/patch");
16
18
17
19
// Can't be Set because we need splice
18
20
const patches: IdentifiedPatch[] = [];
19
21
let webpackModules: Set<IdentifiedWebpackModule> = new Set();
22
+
let webpackRequire: WebpackRequireType | null = null;
23
+
24
+
const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = new Map();
20
25
21
26
export function registerPatch(patch: IdentifiedPatch) {
27
+
patch.find = processFind(patch.find);
28
+
processReplace(patch.replace);
29
+
22
30
patches.push(patch);
23
31
moonlight.unpatched.add(patch);
24
32
}
···
30
38
}
31
39
}
32
40
41
+
export function onModuleLoad(module: string | string[], callback: (moduleId: string) => void): void {
42
+
let moduleIds = module;
43
+
44
+
if (typeof module === "string") {
45
+
moduleIds = [module];
46
+
}
47
+
48
+
for (const moduleId of moduleIds) {
49
+
if (moduleLoadSubscriptions.has(moduleId)) {
50
+
moduleLoadSubscriptions.get(moduleId)?.push(callback);
51
+
} else {
52
+
moduleLoadSubscriptions.set(moduleId, [callback]);
53
+
}
54
+
}
55
+
}
56
+
33
57
/*
34
58
The patching system functions by matching a string or regex against the
35
59
.toString()'d copy of a Webpack module. When a patch happens, we reconstruct
···
42
66
const moduleCache: Record<string, string> = {};
43
67
const patched: Record<string, Array<string>> = {};
44
68
69
+
function createSourceURL(id: string) {
70
+
const remapped = Object.entries(moonlight.moonmap.modules).find((m) => m[1] === id)?.[0];
71
+
72
+
if (remapped) {
73
+
return `// Webpack Module: ${id}\n//# sourceURL=${remapped}`;
74
+
}
75
+
76
+
return `//# sourceURL=Webpack-Module/${id.slice(0, 3)}/${id}`;
77
+
}
78
+
79
+
function patchModule(id: string, patchId: string, replaced: string, entry: WebpackJsonpEntry[1]) {
80
+
// Store what extensions patched what modules for easier debugging
81
+
patched[id] = patched[id] ?? [];
82
+
patched[id].push(patchId);
83
+
84
+
// Webpack module arguments are minified, so we replace them with consistent names
85
+
// We have to wrap it so things don't break, though
86
+
const patchedStr = patched[id].sort().join(", ");
87
+
88
+
const wrapped =
89
+
`(${replaced}).apply(this, arguments)\n` + `// Patched by moonlight: ${patchedStr}\n` + createSourceURL(id);
90
+
91
+
try {
92
+
const func = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
93
+
entry[id] = func;
94
+
entry[id].__moonlight = true;
95
+
return true;
96
+
} catch (e) {
97
+
logger.warn("Error constructing function for patch", patchId, e);
98
+
patched[id].pop();
99
+
return false;
100
+
}
101
+
}
102
+
45
103
function patchModules(entry: WebpackJsonpEntry[1]) {
104
+
// Populate the module cache
46
105
for (const [id, func] of Object.entries(entry)) {
47
-
let moduleString = Object.prototype.hasOwnProperty.call(moduleCache, id)
48
-
? moduleCache[id]
49
-
: func.toString().replace(/\n/g, "");
106
+
if (!Object.hasOwn(moduleCache, id) && func.__moonlight !== true) {
107
+
moduleCache[id] = func.toString().replace(/\n/g, "");
108
+
moonlight.moonmap.parseScript(id, moduleCache[id]);
109
+
}
110
+
}
111
+
112
+
for (const [id, func] of Object.entries(entry)) {
113
+
if (func.__moonlight === true) continue;
114
+
115
+
// Clone the module string so finds don't get messed up by other extensions
116
+
const origModuleString = moduleCache[id];
117
+
let moduleString = origModuleString;
118
+
const patchedStr = [];
119
+
const mappedName = Object.entries(moonlight.moonmap.modules).find((m) => m[1] === id)?.[0];
120
+
let modified = false;
121
+
let swappedModule = false;
122
+
123
+
const exts = new Set<string>();
50
124
51
125
for (let i = 0; i < patches.length; i++) {
52
126
const patch = patches[i];
53
127
if (patch.prerequisite != null && !patch.prerequisite()) {
128
+
moonlight.unpatched.delete(patch);
54
129
continue;
55
130
}
56
131
···
59
134
patch.find.lastIndex = 0;
60
135
}
61
136
62
-
// indexOf is faster than includes by 0.25% lmao
63
-
const match =
64
-
typeof patch.find === "string"
65
-
? moduleString.indexOf(patch.find) !== -1
66
-
: patch.find.test(moduleString);
137
+
const match = testFind(origModuleString, patch.find) || patch.find === mappedName;
67
138
68
139
// Global regexes apply to all modules
69
-
const shouldRemove =
70
-
typeof patch.find === "string" ? true : !patch.find.global;
140
+
const shouldRemove = typeof patch.find === "string" ? true : !patch.find.global;
71
141
142
+
let replaced = moduleString;
143
+
let hardFailed = false;
72
144
if (match) {
73
-
moonlight.unpatched.delete(patch);
145
+
// We ensured normal PatchReplace objects get turned into arrays on register
146
+
const replaces = patch.replace as PatchReplace[];
74
147
75
-
// We ensured all arrays get turned into normal PatchReplace objects on register
76
-
const replace = patch.replace as PatchReplace;
148
+
let isPatched = true;
149
+
for (let i = 0; i < replaces.length; i++) {
150
+
const replace = replaces[i];
151
+
let patchId = `${patch.ext}#${patch.id}`;
152
+
if (replaces.length > 1) patchId += `#${i}`;
153
+
patchedStr.push(patchId);
77
154
78
-
if (
79
-
replace.type === undefined ||
80
-
replace.type === PatchReplaceType.Normal
81
-
) {
82
-
// Add support for \i to match rspack's minified names
83
-
if (typeof replace.match !== "string") {
84
-
replace.match = new RegExp(
85
-
replace.match.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"),
86
-
replace.match.flags
87
-
);
88
-
}
89
-
// tsc fails to detect the overloads for this, so I'll just do this
90
-
// Verbose, but it works
91
-
let replaced;
92
-
if (typeof replace.replacement === "string") {
93
-
replaced = moduleString.replace(replace.match, replace.replacement);
94
-
} else {
95
-
replaced = moduleString.replace(replace.match, replace.replacement);
96
-
}
155
+
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
156
+
// tsc fails to detect the overloads for this, so I'll just do this
157
+
// Verbose, but it works
158
+
if (typeof replace.replacement === "string") {
159
+
replaced = replaced.replace(replace.match, replace.replacement);
160
+
} else {
161
+
replaced = replaced.replace(replace.match, replace.replacement);
162
+
}
97
163
98
-
if (replaced === moduleString) {
99
-
logger.warn("Patch replacement failed", id, patch);
100
-
continue;
164
+
if (replaced === moduleString) {
165
+
logger.warn("Patch replacement failed", id, patchId, patch);
166
+
isPatched = false;
167
+
if (patch.hardFail) {
168
+
hardFailed = true;
169
+
break;
170
+
} else {
171
+
continue;
172
+
}
173
+
}
174
+
} else if (replace.type === PatchReplaceType.Module) {
175
+
// Directly replace the module with a new one
176
+
const newModule = replace.replacement(replaced);
177
+
entry[id] = newModule;
178
+
entry[id].__moonlight = true;
179
+
replaced = newModule.toString().replace(/\n/g, "");
180
+
swappedModule = true;
101
181
}
182
+
}
102
183
103
-
// Store what extensions patched what modules for easier debugging
104
-
patched[id] = patched[id] || [];
105
-
patched[id].push(`${patch.ext}#${patch.id}`);
184
+
if (!hardFailed) {
185
+
moduleString = replaced;
186
+
modified = true;
187
+
exts.add(patch.ext);
188
+
}
106
189
107
-
// Webpack module arguments are minified, so we replace them with consistent names
108
-
// We have to wrap it so things don't break, though
109
-
const patchedStr = patched[id].sort().join(", ");
190
+
if (isPatched) moonlight.unpatched.delete(patch);
191
+
if (shouldRemove) patches.splice(i--, 1);
192
+
}
193
+
}
110
194
111
-
const wrapped =
112
-
`(${replaced}).apply(this, arguments)\n` +
113
-
`// Patched by moonlight: ${patchedStr}\n` +
114
-
`//# sourceURL=Webpack-Module-${id}`;
195
+
if (modified) {
196
+
let shouldCache = true;
197
+
if (!swappedModule) shouldCache = patchModule(id, patchedStr.join(", "), moduleString, entry);
198
+
if (shouldCache) moduleCache[id] = moduleString;
199
+
moonlight.patched.set(id, exts);
200
+
}
115
201
116
-
try {
117
-
const func = new Function(
118
-
"module",
119
-
"exports",
120
-
"require",
121
-
wrapped
122
-
) as WebpackModuleFunc;
123
-
entry[id] = func;
124
-
entry[id].__moonlight = true;
125
-
moduleString = replaced;
126
-
} catch (e) {
127
-
logger.warn("Error constructing function for patch", patch, e);
128
-
patched[id].pop();
202
+
try {
203
+
const parsed = moonlight.lunast.parseScript(id, moduleString);
204
+
if (parsed != null) {
205
+
for (const [parsedId, parsedScript] of Object.entries(parsed)) {
206
+
if (patchModule(parsedId, "lunast", parsedScript, entry)) {
207
+
moduleCache[parsedId] = parsedScript;
129
208
}
130
-
} else if (replace.type === PatchReplaceType.Module) {
131
-
// Directly replace the module with a new one
132
-
const newModule = replace.replacement(moduleString);
133
-
entry[id] = newModule;
134
-
entry[id].__moonlight = true;
135
-
moduleString =
136
-
newModule.toString().replace(/\n/g, "") +
137
-
`//# sourceURL=Webpack-Module-${id}`;
138
-
}
139
-
140
-
if (shouldRemove) {
141
-
patches.splice(i--, 1);
142
209
}
143
210
}
211
+
} catch (e) {
212
+
logger.error("Failed to parse script for LunAST", id, e);
144
213
}
145
214
146
215
if (moonlightNode.config.patchAll === true) {
147
-
if (
148
-
(typeof id !== "string" || !id.includes("_")) &&
149
-
!entry[id].__moonlight
150
-
) {
151
-
const wrapped =
152
-
`(${moduleString}).apply(this, arguments)\n` +
153
-
`//# sourceURL=Webpack-Module-${id}`;
154
-
entry[id] = new Function(
155
-
"module",
156
-
"exports",
157
-
"require",
158
-
wrapped
159
-
) as WebpackModuleFunc;
216
+
if ((typeof id !== "string" || !id.includes("_")) && !entry[id].__moonlight) {
217
+
const wrapped = `(${moduleCache[id]}).apply(this, arguments)\n` + createSourceURL(id);
218
+
entry[id] = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
160
219
entry[id].__moonlight = true;
161
220
}
162
221
}
163
222
223
+
// Dispatch module load event subscription
224
+
if (moduleLoadSubscriptions.has(id)) {
225
+
const loadCallbacks = moduleLoadSubscriptions.get(id)!;
226
+
for (const callback of loadCallbacks) {
227
+
try {
228
+
callback(id);
229
+
} catch (e) {
230
+
logger.error("Error in module load subscription: " + e);
231
+
}
232
+
}
233
+
moduleLoadSubscriptions.delete(id);
234
+
}
235
+
164
236
moduleCache[id] = moduleString;
165
237
}
166
238
}
···
172
244
*/
173
245
let chunkId = Number.MAX_SAFE_INTEGER;
174
246
247
+
function depToString(x: ExplicitExtensionDependency) {
248
+
return x.ext != null ? `${x.ext}_${x.id}` : x.id;
249
+
}
250
+
175
251
function handleModuleDependencies() {
176
252
const modules = Array.from(webpackModules.values());
177
253
178
-
const dependencies: Dependency<string, IdentifiedWebpackModule>[] =
179
-
modules.map((wp) => {
180
-
return {
181
-
id: `${wp.ext}_${wp.id}`,
182
-
data: wp
183
-
};
184
-
});
254
+
const dependencies: Dependency<string, IdentifiedWebpackModule>[] = modules.map((wp) => {
255
+
return {
256
+
id: depToString(wp),
257
+
data: wp
258
+
};
259
+
});
185
260
186
261
const [sorted, _] = calculateDependencies(dependencies, {
187
262
fetchDep: (id) => {
188
-
return modules.find((x) => id === `${x.ext}_${x.id}`) ?? null;
263
+
return modules.find((x) => id === depToString(x)) ?? null;
189
264
},
190
265
191
266
getDeps: (item) => {
192
267
const deps = item.data?.dependencies ?? [];
193
268
return (
194
269
deps.filter(
195
-
(dep) => !(dep instanceof RegExp || typeof dep === "string")
270
+
(dep) => !(dep instanceof RegExp || typeof dep === "string") && dep.ext != null
196
271
) as ExplicitExtensionDependency[]
197
-
).map((x) => `${x.ext}_${x.id}`);
272
+
).map(depToString);
198
273
}
199
274
});
200
275
···
210
285
for (const [_modId, mod] of Object.entries(entry)) {
211
286
const modStr = mod.toString();
212
287
for (const wpModule of webpackModules) {
213
-
const id = wpModule.ext + "_" + wpModule.id;
288
+
const id = depToString(wpModule);
214
289
if (wpModule.dependencies) {
215
290
const deps = new Set(wpModule.dependencies);
216
291
···
223
298
} else if (dep instanceof RegExp) {
224
299
if (dep.test(modStr)) deps.delete(dep);
225
300
} else if (
226
-
injectedWpModules.find(
227
-
(x) => x.ext === dep.ext && x.id === dep.id
228
-
)
301
+
dep.ext != null
302
+
? injectedWpModules.find((x) => x.ext === dep.ext && x.id === dep.id)
303
+
: injectedWpModules.find((x) => x.id === dep.id)
229
304
) {
230
305
deps.delete(dep);
231
306
}
232
307
}
233
308
309
+
wpModule.dependencies = Array.from(deps);
234
310
if (deps.size !== 0) {
235
-
wpModule.dependencies = Array.from(deps);
236
311
continue;
237
312
}
238
-
239
-
wpModule.dependencies = Array.from(deps);
240
313
}
241
314
}
242
315
···
249
322
if (wpModule.run) {
250
323
modules[id] = wpModule.run;
251
324
wpModule.run.__moonlight = true;
325
+
// @ts-expect-error hacks
326
+
wpModule.run.call = function (self, module, exports, require) {
327
+
try {
328
+
wpModule.run!.apply(self, [module, exports, require]);
329
+
} catch (err) {
330
+
logger.error(`Failed to run module "${id}":`, err);
331
+
}
332
+
};
333
+
if (wpModule.entrypoint) entrypoints.push(id);
252
334
}
253
-
if (wpModule.entrypoint) entrypoints.push(id);
254
335
}
255
336
if (!webpackModules.size) break;
337
+
}
338
+
339
+
for (const [name, func] of Object.entries(moonlight.moonmap.getWebpackModules("window.moonlight.moonmap"))) {
340
+
// @ts-expect-error probably should fix the type on this idk
341
+
func.__moonlight = true;
342
+
injectedWpModules.push({ id: name, run: func });
343
+
modules[name] = func;
344
+
inject = true;
345
+
}
346
+
347
+
if (webpackRequire != null) {
348
+
for (const id of moonlight.moonmap.getLazyModules()) {
349
+
webpackRequire.e(id);
350
+
}
256
351
}
257
352
258
353
if (inject) {
···
260
355
window.webpackChunkdiscord_app.push([
261
356
[--chunkId],
262
357
modules,
263
-
(require: typeof WebpackRequire) => entrypoints.map(require)
358
+
(require: WebpackRequireType) =>
359
+
entrypoints.map((id) => {
360
+
try {
361
+
if (require.m[id] == null) {
362
+
logger.error(`Failing to load entrypoint module "${id}" because it's not found in Webpack.`);
363
+
} else {
364
+
require(id);
365
+
}
366
+
} catch (err) {
367
+
logger.error(`Failed to load entrypoint module "${id}":`, err);
368
+
}
369
+
})
264
370
]);
265
371
}
266
372
}
···
269
375
interface Window {
270
376
webpackChunkdiscord_app: WebpackJsonp;
271
377
}
378
+
}
379
+
380
+
function moduleSourceGetter(id: string) {
381
+
return moduleCache[id] ?? null;
272
382
}
273
383
274
384
/*
···
282
392
export async function installWebpackPatcher() {
283
393
await handleModuleDependencies();
284
394
395
+
moonlight.lunast.setModuleSourceGetter(moduleSourceGetter);
396
+
moonlight.moonmap.setModuleSourceGetter(moduleSourceGetter);
397
+
398
+
const wpRequireFetcher: WebpackModuleFunc = (module, exports, require) => {
399
+
webpackRequire = require;
400
+
};
401
+
wpRequireFetcher.__moonlight = true;
402
+
webpackModules.add({
403
+
id: "moonlight",
404
+
entrypoint: true,
405
+
run: wpRequireFetcher
406
+
});
407
+
285
408
let realWebpackJsonp: WebpackJsonp | null = null;
286
409
Object.defineProperty(window, "webpackChunkdiscord_app", {
287
410
set: (jsonp: WebpackJsonp) => {
···
293
416
const realPush = jsonp.push;
294
417
if (jsonp.push.__moonlight !== true) {
295
418
jsonp.push = (items) => {
419
+
moonlight.events.dispatchEvent(WebEventType.ChunkLoad, {
420
+
chunkId: items[0],
421
+
modules: items[1],
422
+
require: items[2]
423
+
});
424
+
296
425
patchModules(items[1]);
297
426
298
427
try {
···
335
464
set(modules: any) {
336
465
const { stack } = new Error();
337
466
if (stack!.includes("/assets/") && !Array.isArray(modules)) {
467
+
moonlight.events.dispatchEvent(WebEventType.ChunkLoad, {
468
+
modules: modules
469
+
});
338
470
patchModules(modules);
339
-
if (!window.webpackChunkdiscord_app)
340
-
window.webpackChunkdiscord_app = [];
471
+
472
+
if (!window.webpackChunkdiscord_app) window.webpackChunkdiscord_app = [];
341
473
injectModules(modules);
342
474
}
343
475
+49
packages/core/src/persist.ts
+49
packages/core/src/persist.ts
···
1
+
import { join, dirname } from "node:path";
2
+
import { mkdirSync, renameSync, existsSync, copyFileSync, readdirSync } from "node:fs";
3
+
import Logger from "./util/logger";
4
+
5
+
const logger = new Logger("core/persist");
6
+
7
+
export default function persist(asarPath: string) {
8
+
try {
9
+
if (process.platform === "win32") {
10
+
persistWin32(asarPath);
11
+
}
12
+
} catch (e) {
13
+
logger.error(`Failed to persist moonlight: ${e}`);
14
+
}
15
+
}
16
+
17
+
function persistWin32(asarPath: string) {
18
+
const updaterModule = require(join(asarPath, "common", "updater"));
19
+
const updater = updaterModule.Updater;
20
+
21
+
const currentAppDir = join(dirname(asarPath), "app");
22
+
23
+
const realEmit = updater.prototype.emit;
24
+
updater.prototype.emit = function (event: string, ...args: any[]) {
25
+
if (event === "host-updated") {
26
+
const versions = this.queryCurrentVersionsSync();
27
+
28
+
const newRootDir = join(this.rootPath, "app-" + versions.current_host.map((v: number) => v.toString()).join("."));
29
+
logger.info(`Persisting moonlight - new root dir: ${newRootDir}`);
30
+
31
+
const newResources = join(newRootDir, "resources");
32
+
33
+
// app.asar -> _app.asar
34
+
const newAsar = join(newResources, "app.asar");
35
+
const newRenamedAsar = join(newResources, "_app.asar");
36
+
if (!existsSync(newRenamedAsar)) renameSync(newAsar, newRenamedAsar);
37
+
38
+
// copy the already existing app dir so we don't have to figure out the moonlight dir
39
+
const newAppDir = join(newResources, "app");
40
+
if (!existsSync(newAppDir)) mkdirSync(newAppDir);
41
+
42
+
for (const file of readdirSync(currentAppDir)) {
43
+
copyFileSync(join(currentAppDir, file), join(newAppDir, file));
44
+
}
45
+
}
46
+
47
+
return realEmit.call(this, event, ...args);
48
+
};
49
+
}
+63
packages/core/src/util/binary.ts
+63
packages/core/src/util/binary.ts
···
1
+
// https://github.com/NotNite/brc-save-editor/blob/main/src/lib/binary.ts
2
+
export interface BinaryInterface {
3
+
data: Uint8Array;
4
+
view: DataView;
5
+
length: number;
6
+
position: number;
7
+
}
8
+
9
+
export class BinaryReader implements BinaryInterface {
10
+
data: Uint8Array;
11
+
view: DataView;
12
+
length: number;
13
+
position: number;
14
+
15
+
constructor(data: Uint8Array) {
16
+
this.data = data;
17
+
this.view = new DataView(data.buffer);
18
+
19
+
this.length = data.length;
20
+
this.position = 0;
21
+
}
22
+
23
+
readByte() {
24
+
return this._read(this.view.getInt8, 1);
25
+
}
26
+
27
+
readBoolean() {
28
+
return this.readByte() !== 0;
29
+
}
30
+
31
+
readInt32() {
32
+
return this._read(this.view.getInt32, 4);
33
+
}
34
+
35
+
readUInt32() {
36
+
return this._read(this.view.getUint32, 4);
37
+
}
38
+
39
+
readSingle() {
40
+
return this._read(this.view.getFloat32, 4);
41
+
}
42
+
43
+
readInt64() {
44
+
return this._read(this.view.getBigInt64, 8);
45
+
}
46
+
47
+
readString(length: number) {
48
+
const result = this.read(length);
49
+
return new TextDecoder().decode(result);
50
+
}
51
+
52
+
read(length: number) {
53
+
const data = this.data.subarray(this.position, this.position + length);
54
+
this.position += length;
55
+
return data;
56
+
}
57
+
58
+
private _read<T>(func: (position: number, littleEndian?: boolean) => T, length: number): T {
59
+
const result = func.call(this.view, this.position, true);
60
+
this.position += length;
61
+
return result;
62
+
}
63
+
}
packages/core/src/util/clone.ts
packages/core/src/util/clone.ts
This is a binary file and will not be displayed.
+39
packages/core/src/util/config.ts
+39
packages/core/src/util/config.ts
···
1
+
import type { Config, DetectedExtension, ExtensionManifest } from "@moonlight-mod/types";
2
+
3
+
export function getManifest(extensions: DetectedExtension[], ext: string) {
4
+
return extensions.find((x) => x.id === ext)?.manifest;
5
+
}
6
+
7
+
export function getConfig(ext: string, config: Config) {
8
+
const val = config.extensions[ext];
9
+
if (val == null || typeof val === "boolean") return undefined;
10
+
return val.config;
11
+
}
12
+
13
+
export function getConfigOption<T>(
14
+
ext: string,
15
+
key: string,
16
+
config: Config,
17
+
settings?: ExtensionManifest["settings"]
18
+
): T | undefined {
19
+
const defaultValue: T | undefined = structuredClone(settings?.[key]?.default);
20
+
const cfg = getConfig(ext, config);
21
+
if (cfg == null || typeof cfg === "boolean") return defaultValue;
22
+
return cfg?.[key] ?? defaultValue;
23
+
}
24
+
25
+
export function setConfigOption<T>(config: Config, ext: string, key: string, value: T) {
26
+
const oldConfig = config.extensions[ext];
27
+
const newConfig =
28
+
typeof oldConfig === "boolean"
29
+
? {
30
+
enabled: oldConfig,
31
+
config: { [key]: value }
32
+
}
33
+
: {
34
+
...oldConfig,
35
+
config: { ...(oldConfig?.config ?? {}), [key]: value }
36
+
};
37
+
38
+
config.extensions[ext] = newConfig;
39
+
}
+25
-28
packages/core/src/util/data.ts
+25
-28
packages/core/src/util/data.ts
···
1
1
import { constants } from "@moonlight-mod/types";
2
-
import requireImport from "./import";
2
+
3
+
export async function getMoonlightDir() {
4
+
browser: {
5
+
return "/";
6
+
}
3
7
4
-
export function getMoonlightDir(): string {
5
8
const electron = require("electron");
6
-
const fs = requireImport("fs");
7
-
const path = requireImport("path");
8
9
9
10
let appData = "";
10
11
injector: {
···
15
16
appData = electron.ipcRenderer.sendSync(constants.ipcGetAppData);
16
17
}
17
18
18
-
const dir = path.join(appData, "moonlight-mod");
19
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
19
+
const dir = moonlightNodeSandboxed.fs.join(appData, "moonlight-mod");
20
+
if (!(await moonlightNodeSandboxed.fs.exists(dir))) await moonlightNodeSandboxed.fs.mkdir(dir);
20
21
21
22
return dir;
22
23
}
···
26
27
version: string;
27
28
};
28
29
29
-
export function getConfigPath(): string {
30
-
const dir = getMoonlightDir();
31
-
const fs = requireImport("fs");
32
-
const path = requireImport("path");
30
+
export async function getConfigPath() {
31
+
browser: {
32
+
return "/config.json";
33
+
}
34
+
35
+
const dir = await getMoonlightDir();
33
36
34
37
let configPath = "";
35
38
36
-
const buildInfoPath = path.join(process.resourcesPath, "build_info.json");
37
-
if (!fs.existsSync(buildInfoPath)) {
38
-
configPath = path.join(dir, "desktop.json");
39
+
const buildInfoPath = moonlightNodeSandboxed.fs.join(process.resourcesPath, "build_info.json");
40
+
if (!(await moonlightNodeSandboxed.fs.exists(buildInfoPath))) {
41
+
configPath = moonlightNodeSandboxed.fs.join(dir, "desktop.json");
39
42
} else {
40
-
const buildInfo: BuildInfo = JSON.parse(
41
-
fs.readFileSync(buildInfoPath, "utf8")
42
-
);
43
-
configPath = path.join(dir, buildInfo.releaseChannel + ".json");
43
+
const buildInfo: BuildInfo = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(buildInfoPath));
44
+
configPath = moonlightNodeSandboxed.fs.join(dir, buildInfo.releaseChannel + ".json");
44
45
}
45
46
46
47
return configPath;
47
48
}
48
49
49
-
function getPathFromMoonlight(...names: string[]): string {
50
-
const dir = getMoonlightDir();
51
-
const fs = requireImport("fs");
52
-
const path = requireImport("path");
50
+
async function getPathFromMoonlight(...names: string[]) {
51
+
const dir = await getMoonlightDir();
53
52
54
-
const target = path.join(dir, ...names);
55
-
if (!fs.existsSync(target)) fs.mkdirSync(target);
53
+
const target = moonlightNodeSandboxed.fs.join(dir, ...names);
54
+
if (!(await moonlightNodeSandboxed.fs.exists(target))) await moonlightNodeSandboxed.fs.mkdir(target);
56
55
57
56
return target;
58
57
}
59
58
60
-
export function getExtensionsPath(): string {
61
-
return getPathFromMoonlight(constants.extensionsDir);
59
+
export async function getExtensionsPath() {
60
+
return await getPathFromMoonlight(constants.extensionsDir);
62
61
}
63
62
64
63
export function getCoreExtensionsPath(): string {
65
-
const path = requireImport("path");
66
-
const a = path.join(__dirname, constants.coreExtensionsDir);
67
-
return a;
64
+
return moonlightNodeSandboxed.fs.join(__dirname, constants.coreExtensionsDir);
68
65
}
+4
-14
packages/core/src/util/dependency.ts
+4
-14
packages/core/src/util/dependency.ts
···
35
35
const fullDeps: Set<T> = new Set();
36
36
let failed = false;
37
37
38
-
// eslint-disable-next-line no-inner-declarations
39
38
function resolveDeps(id: T, root: boolean) {
40
39
if (id === item.id && !root) {
41
40
logger.warn(`Circular dependency detected: "${item.id}"`);
···
113
112
logger.trace("Enabled stage", itemsOrig);
114
113
const implicitlyEnabled: T[] = [];
115
114
116
-
// eslint-disable-next-line no-inner-declarations
117
115
function validateDeps(dep: Dependency<T, D>) {
118
116
if (getEnabled!(dep)) {
119
117
const deps = dependencyGraphOrig.get(dep.id)!;
···
122
120
validateDeps({ id, data });
123
121
}
124
122
} else {
125
-
const dependsOnMe = Array.from(dependencyGraphOrig.entries()).filter(
126
-
([, v]) => v?.has(dep.id)
127
-
);
123
+
const dependsOnMe = Array.from(dependencyGraphOrig.entries()).filter(([, v]) => v?.has(dep.id));
128
124
129
125
if (dependsOnMe.length > 0) {
130
126
logger.debug("Implicitly enabling dependency", dep.id);
···
134
130
}
135
131
136
132
for (const dep of itemsOrig) validateDeps(dep);
137
-
itemsOrig = itemsOrig.filter(
138
-
(x) => getEnabled(x) || implicitlyEnabled.includes(x.id)
139
-
);
133
+
itemsOrig = itemsOrig.filter((x) => getEnabled(x) || implicitlyEnabled.includes(x.id));
140
134
}
141
135
142
136
if (getIncompatible != null) {
···
176
170
dependencyGraph.set(item.id, new Set(dependencyGraph.get(item.id)));
177
171
}
178
172
179
-
while (
180
-
Array.from(dependencyGraph.values()).filter((x) => x != null).length > 0
181
-
) {
182
-
const noDependents = items.filter(
183
-
(e) => dependencyGraph.get(e.id)?.size === 0
184
-
);
173
+
while (Array.from(dependencyGraph.values()).filter((x) => x != null).length > 0) {
174
+
const noDependents = items.filter((e) => dependencyGraph.get(e.id)?.size === 0);
185
175
186
176
if (noDependents.length === 0) {
187
177
logger.warn("Stuck dependency graph detected", dependencyGraph);
+48
-54
packages/core/src/util/event.ts
+48
-54
packages/core/src/util/event.ts
···
1
-
export type MoonlightEventCallback = (data: string) => void;
1
+
import { MoonlightEventEmitter } from "@moonlight-mod/types/core/event";
2
2
3
-
export interface MoonlightEventEmitter {
4
-
dispatchEvent: (id: string, data: string) => void;
5
-
addEventListener: (id: string, cb: MoonlightEventCallback) => void;
6
-
removeEventListener: (id: string, cb: MoonlightEventCallback) => void;
7
-
}
3
+
export function createEventEmitter<
4
+
EventId extends string = string,
5
+
EventData = Record<EventId, any>
6
+
>(): MoonlightEventEmitter<EventId, EventData> {
7
+
webTarget: {
8
+
const eventEmitter = new EventTarget();
9
+
const listeners = new Map<(data: EventData) => void, (e: Event) => void>();
8
10
9
-
function nodeMethod(): MoonlightEventEmitter {
10
-
const EventEmitter = require("events");
11
-
const eventEmitter = new EventEmitter();
12
-
const listeners = new Map<MoonlightEventCallback, (...args: any[]) => void>();
11
+
return {
12
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => {
13
+
eventEmitter.dispatchEvent(new CustomEvent(id as string, { detail: data }));
14
+
},
13
15
14
-
return {
15
-
dispatchEvent: (id: string, data: string) => {
16
-
eventEmitter.emit(id, data);
17
-
},
16
+
addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
17
+
const untyped = cb as (data: EventData) => void;
18
+
if (listeners.has(untyped)) return;
18
19
19
-
addEventListener: (id: string, cb: (data: string) => void) => {
20
-
if (listeners.has(cb)) return;
20
+
function listener(e: Event) {
21
+
const event = e as CustomEvent<string>;
22
+
cb(event.detail as EventData[Id]);
23
+
}
21
24
22
-
function listener(data: string) {
23
-
cb(data);
24
-
}
25
+
listeners.set(untyped, listener);
26
+
eventEmitter.addEventListener(id as string, listener);
27
+
},
25
28
26
-
listeners.set(cb, listener);
27
-
eventEmitter.on(id, listener);
28
-
},
29
-
30
-
removeEventListener: (id: string, cb: (data: string) => void) => {
31
-
const listener = listeners.get(cb);
32
-
if (listener == null) return;
33
-
listeners.delete(cb);
34
-
eventEmitter.off(id, listener);
35
-
}
36
-
};
37
-
}
29
+
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
30
+
const untyped = cb as (data: EventData) => void;
31
+
const listener = listeners.get(untyped);
32
+
if (listener == null) return;
33
+
listeners.delete(untyped);
34
+
eventEmitter.removeEventListener(id as string, listener);
35
+
}
36
+
};
37
+
}
38
38
39
-
export function createEventEmitter(): MoonlightEventEmitter {
40
-
webPreload: {
41
-
const eventEmitter = new EventTarget();
42
-
const listeners = new Map<MoonlightEventCallback, (e: Event) => void>();
39
+
nodeTarget: {
40
+
const EventEmitter = require("events");
41
+
const eventEmitter = new EventEmitter();
42
+
const listeners = new Map<(data: EventData) => void, (e: Event) => void>();
43
43
44
44
return {
45
-
dispatchEvent: (id: string, data: string) => {
46
-
eventEmitter.dispatchEvent(new CustomEvent(id, { detail: data }));
45
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => {
46
+
eventEmitter.emit(id as string, data);
47
47
},
48
48
49
-
addEventListener: (id: string, cb: (data: string) => void) => {
50
-
if (listeners.has(cb)) return;
49
+
addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
50
+
const untyped = cb as (data: EventData) => void;
51
+
if (listeners.has(untyped)) return;
51
52
52
53
function listener(e: Event) {
53
54
const event = e as CustomEvent<string>;
54
-
cb(event.detail);
55
+
cb(event as EventData[Id]);
55
56
}
56
57
57
-
listeners.set(cb, listener);
58
-
eventEmitter.addEventListener(id, listener);
58
+
listeners.set(untyped, listener);
59
+
eventEmitter.on(id as string, listener);
59
60
},
60
61
61
-
removeEventListener: (id: string, cb: (data: string) => void) => {
62
-
const listener = listeners.get(cb);
62
+
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
63
+
const untyped = cb as (data: EventData) => void;
64
+
const listener = listeners.get(untyped);
63
65
if (listener == null) return;
64
-
listeners.delete(cb);
65
-
eventEmitter.removeEventListener(id, listener);
66
+
listeners.delete(untyped);
67
+
eventEmitter.off(id as string, listener);
66
68
}
67
69
};
68
-
}
69
-
70
-
nodePreload: {
71
-
return nodeMethod();
72
-
}
73
-
74
-
injector: {
75
-
return nodeMethod();
76
70
}
77
71
78
72
throw new Error("Called createEventEmitter() in an impossible environment");
+3
-5
packages/core/src/util/import.ts
+3
-5
packages/core/src/util/import.ts
···
9
9
cemented if import is passed a string literal.
10
10
*/
11
11
12
-
const canRequire = ["path", "fs"] as const;
13
-
type CanRequire = (typeof canRequire)[number];
12
+
const _canRequire = ["path", "fs"] as const;
13
+
type CanRequire = (typeof _canRequire)[number];
14
14
15
15
type ImportTypes = {
16
16
path: typeof import("path");
17
17
fs: typeof import("fs");
18
18
};
19
19
20
-
export default function requireImport<T extends CanRequire>(
21
-
type: T
22
-
): Awaited<ImportTypes[T]> {
20
+
export default function requireImport<T extends CanRequire>(type: T): Awaited<ImportTypes[T]> {
23
21
return require(type);
24
22
}
+12
-16
packages/core/src/util/logger.ts
+12
-16
packages/core/src/util/logger.ts
···
1
1
/* eslint-disable no-console */
2
2
import { LogLevel } from "@moonlight-mod/types/logger";
3
-
import { readConfig } from "../config";
3
+
import { Config } from "@moonlight-mod/types";
4
4
5
5
const colors = {
6
6
[LogLevel.SILLY]: "#EDD3E9",
···
11
11
[LogLevel.ERROR]: "#FF0000"
12
12
};
13
13
14
-
const config = readConfig();
15
14
let maxLevel = LogLevel.INFO;
16
-
if (config.loggerLevel != null) {
17
-
const enumValue =
18
-
LogLevel[config.loggerLevel.toUpperCase() as keyof typeof LogLevel];
19
-
if (enumValue != null) {
20
-
maxLevel = enumValue;
21
-
}
22
-
}
23
15
24
16
export default class Logger {
25
17
private name: string;
···
57
49
const logLevel = LogLevel[level].toUpperCase();
58
50
if (maxLevel > level) return;
59
51
60
-
if (MOONLIGHT_WEB_PRELOAD) {
61
-
args = [
62
-
`%c[${logLevel}]`,
63
-
`background-color: ${colors[level]}; color: #FFFFFF;`,
64
-
`[${this.name}]`,
65
-
...obj
66
-
];
52
+
if (MOONLIGHT_WEB_PRELOAD || MOONLIGHT_BROWSER) {
53
+
args = [`%c[${logLevel}]`, `background-color: ${colors[level]}; color: #FFFFFF;`, `[${this.name}]`, ...obj];
67
54
} else {
68
55
args = [`[${logLevel}]`, `[${this.name}]`, ...obj];
69
56
}
···
92
79
}
93
80
}
94
81
}
82
+
83
+
export function initLogger(config: Config) {
84
+
if (config.loggerLevel != null) {
85
+
const enumValue = LogLevel[config.loggerLevel.toUpperCase() as keyof typeof LogLevel];
86
+
if (enumValue != null) {
87
+
maxLevel = enumValue;
88
+
}
89
+
}
90
+
}
+30
packages/core/src/util/patch.ts
+30
packages/core/src/util/patch.ts
···
1
+
import { PatchReplace, PatchReplaceType } from "@moonlight-mod/types";
2
+
3
+
type SingleFind = string | RegExp;
4
+
type Find = SingleFind | SingleFind[];
5
+
6
+
export function processFind<T extends Find>(find: T): T {
7
+
if (Array.isArray(find)) {
8
+
return find.map(processFind) as T;
9
+
} else if (find instanceof RegExp) {
10
+
// Add support for \i to match rspack's minified names
11
+
return new RegExp(find.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"), find.flags) as T;
12
+
} else {
13
+
return find;
14
+
}
15
+
}
16
+
17
+
export function processReplace(replace: PatchReplace | PatchReplace[]) {
18
+
if (Array.isArray(replace)) {
19
+
replace.forEach(processReplace);
20
+
} else {
21
+
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
22
+
replace.match = processFind(replace.match);
23
+
}
24
+
}
25
+
}
26
+
27
+
export function testFind(src: string, find: SingleFind) {
28
+
// indexOf is faster than includes by 0.25% lmao
29
+
return typeof find === "string" ? src.indexOf(find) !== -1 : find.test(src);
30
+
}
+4
-1
packages/core/tsconfig.json
+4
-1
packages/core/tsconfig.json
+11
-2
packages/core-extensions/package.json
+11
-2
packages/core-extensions/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/core-extensions",
3
3
"private": true,
4
+
"engineStrict": true,
5
+
"engines": {
6
+
"node": ">=22",
7
+
"pnpm": ">=10",
8
+
"npm": "pnpm",
9
+
"yarn": "pnpm"
10
+
},
4
11
"dependencies": {
5
-
"@electron/asar": "^3.2.5",
6
-
"@moonlight-mod/types": "workspace:*"
12
+
"@moonlight-mod/core": "workspace:*",
13
+
"@moonlight-mod/types": "workspace:*",
14
+
"microdiff": "catalog:prod",
15
+
"nanotar": "catalog:prod"
7
16
}
8
17
}
+19
packages/core-extensions/src/appPanels/index.ts
+19
packages/core-extensions/src/appPanels/index.ts
···
1
+
import type { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
{
5
+
find: 'setProperty("--custom-app-panels-height"',
6
+
replace: [
7
+
{
8
+
match: /\(0,.\.jsx\)\((.\..),{section:/,
9
+
replacement: (prev, el) => `...require("appPanels_appPanels").default.getPanels(${el}),${prev}`
10
+
}
11
+
]
12
+
}
13
+
];
14
+
15
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
16
+
appPanels: {
17
+
dependencies: [{ id: "react" }]
18
+
}
19
+
};
+11
packages/core-extensions/src/appPanels/manifest.json
+11
packages/core-extensions/src/appPanels/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "appPanels",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "App Panels",
7
+
"tagline": "An API for adding panels around the user/voice controls",
8
+
"authors": ["NotNite"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+23
packages/core-extensions/src/appPanels/webpackModules/appPanels.ts
+23
packages/core-extensions/src/appPanels/webpackModules/appPanels.ts
···
1
+
import type { AppPanels as AppPanelsType } from "@moonlight-mod/types/coreExtensions/appPanels";
2
+
import React from "@moonlight-mod/wp/react";
3
+
4
+
const panels: Record<string, React.FC<any>> = {};
5
+
6
+
export const AppPanels: AppPanelsType = {
7
+
addPanel(section, element) {
8
+
panels[section] = element;
9
+
},
10
+
getPanels(panel) {
11
+
return Object.entries(panels).map(([section, element]) =>
12
+
React.createElement(
13
+
panel,
14
+
{
15
+
section
16
+
},
17
+
React.createElement(element)
18
+
)
19
+
);
20
+
}
21
+
};
22
+
23
+
export default AppPanels;
+85
packages/core-extensions/src/commands/index.ts
+85
packages/core-extensions/src/commands/index.ts
···
1
+
import { Patch, ExtensionWebpackModule } from "@moonlight-mod/types";
2
+
import { APPLICATION_ID } from "@moonlight-mod/types/coreExtensions/commands";
3
+
4
+
export const patches: Patch[] = [
5
+
{
6
+
find: ".fI5MTU)", // COMMAND_SECTION_BUILT_IN_NAME
7
+
replace: [
8
+
// inject commands
9
+
{
10
+
match: /return (\i)\.filter/,
11
+
replacement: (orig, commands) =>
12
+
`return [...${commands},...require("commands_commands").default._getCommands()].filter`
13
+
},
14
+
15
+
// section
16
+
{
17
+
match: /(?<=\i={)(?=\[\i\.\i\.BUILT_IN]:{id:\i\.\i\.BUILT_IN,type:(\i.\i\.BUILT_IN))/,
18
+
replacement: (_, type) =>
19
+
`"${APPLICATION_ID}":{id:"${APPLICATION_ID}",type:${type},get name(){return "moonlight"}},`
20
+
}
21
+
]
22
+
},
23
+
24
+
// index our section
25
+
{
26
+
find: '"ApplicationCommandIndexStore"',
27
+
replace: {
28
+
match: /(?<=let \i=(\i)\((\i\.\i)\[\i\.\i\.BUILT_IN\],(\i),!0,!0,(\i)\);)null!=(\i)&&(\i)\.push\(\i\)/,
29
+
replacement: (_, createSection, sections, deny, props, section, commands) =>
30
+
`null!=${section}&&(${section}.data=${section}.data.filter(c=>c.applicationId=="-1"));
31
+
null!=${section}&&${commands}.push(${section});
32
+
const moonlightCommands=${createSection}(${sections}["${APPLICATION_ID}"],${deny},!0,!0,${props});
33
+
null!=moonlightCommands&&(moonlightCommands.data=moonlightCommands.data.filter(c=>c.applicationId=="${APPLICATION_ID}"));
34
+
null!=moonlightCommands&&${commands}.push(moonlightCommands)`
35
+
}
36
+
},
37
+
38
+
// grab legacy commands (needed for adding actions that act like sed/plus reacting)
39
+
{
40
+
find: "={tts:{action:",
41
+
replace: {
42
+
match: /Object\.setPrototypeOf\((\i),null\)/,
43
+
replacement: (_, legacyCommands) => `require("commands_commands")._getLegacyCommands(${legacyCommands})`
44
+
}
45
+
},
46
+
47
+
// add icon
48
+
{
49
+
find: ",hasSpaceTerminator:",
50
+
replace: {
51
+
match: /(\i)\.type===/,
52
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
53
+
}
54
+
},
55
+
{
56
+
find: ".icon,bot:null==",
57
+
replace: {
58
+
match: /(\.useMemo\(\(\)=>{(var \i;)?)((return |if\()(\i)\.type)/,
59
+
replacement: (_, before, beforeVar, after, afterIf, section) => `${before}
60
+
if (${section}.id==="${APPLICATION_ID}") return "https://moonlight-mod.github.io/favicon.png";
61
+
${after}`
62
+
}
63
+
},
64
+
// fix icon sizing because they expect built in to be 24 and others to be 32
65
+
{
66
+
find: ".builtInSeparator}):null]",
67
+
replace: {
68
+
match: /(\i)\.type===\i\.\i\.BUILT_IN/,
69
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
70
+
}
71
+
},
72
+
73
+
// tell it this app id is authorized
74
+
{
75
+
find: /let{customInstallUrl:\i,installParams:\i,integrationTypesConfig:\i}/,
76
+
replace: {
77
+
match: /\|\|(\i)===\i\.\i\.BUILT_IN/,
78
+
replacement: (orig, id) => `${orig}||${id}==="${APPLICATION_ID}"`
79
+
}
80
+
}
81
+
];
82
+
83
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
84
+
commands: {}
85
+
};
+11
packages/core-extensions/src/commands/manifest.json
+11
packages/core-extensions/src/commands/manifest.json
+71
packages/core-extensions/src/commands/webpackModules/commands.ts
+71
packages/core-extensions/src/commands/webpackModules/commands.ts
···
1
+
import {
2
+
APPLICATION_ID,
3
+
Commands,
4
+
LegacyCommand,
5
+
RegisteredCommand
6
+
} from "@moonlight-mod/types/coreExtensions/commands";
7
+
8
+
type LegacyCommands = Record<string, LegacyCommand>;
9
+
let legacyCommands: LegacyCommands | undefined;
10
+
let queuedLegacyCommands: Record<string, LegacyCommand> | null = {};
11
+
12
+
const registeredCommands: RegisteredCommand[] = [];
13
+
14
+
export function _getLegacyCommands(commands: LegacyCommands) {
15
+
legacyCommands = commands;
16
+
if (queuedLegacyCommands != null) {
17
+
for (const [key, value] of Object.entries(queuedLegacyCommands)) {
18
+
legacyCommands[key] = value;
19
+
}
20
+
queuedLegacyCommands = null;
21
+
}
22
+
}
23
+
24
+
export const commands: Commands = {
25
+
registerCommand(command) {
26
+
const registered: RegisteredCommand = {
27
+
...command,
28
+
untranslatedName: command.id,
29
+
displayName: command.id,
30
+
applicationId: APPLICATION_ID,
31
+
untranslatedDescription: command.description,
32
+
displayDescription: command.description,
33
+
options: command.options?.map((o) => ({
34
+
...o,
35
+
displayName: o.name,
36
+
displayDescription: o.description
37
+
}))
38
+
};
39
+
registeredCommands.push(registered);
40
+
},
41
+
42
+
registerLegacyCommand(id, command) {
43
+
if (command.match) {
44
+
if (command.match instanceof RegExp) {
45
+
command.match = this.anyScopeRegex(command.match);
46
+
} else if (command.match.regex && typeof command.match !== "function") {
47
+
command.match = this.anyScopeRegex(command.match.regex);
48
+
}
49
+
}
50
+
51
+
if (!legacyCommands) {
52
+
queuedLegacyCommands![id] = command;
53
+
} else {
54
+
legacyCommands[id] = command;
55
+
}
56
+
},
57
+
58
+
anyScopeRegex(regex) {
59
+
const out = function (str: string) {
60
+
return regex.exec(str);
61
+
};
62
+
out.regex = regex;
63
+
return out;
64
+
},
65
+
66
+
_getCommands() {
67
+
return [...registeredCommands];
68
+
}
69
+
};
70
+
71
+
export default commands;
+6
-29
packages/core-extensions/src/common/index.ts
+6
-29
packages/core-extensions/src/common/index.ts
···
1
1
import { ExtensionWebExports } from "@moonlight-mod/types";
2
2
3
3
export const webpackModules: ExtensionWebExports["webpackModules"] = {
4
-
components: {
5
-
dependencies: [
6
-
{ ext: "spacepack", id: "spacepack" },
7
-
"MasonryList:",
8
-
".flexGutterSmall,"
9
-
]
4
+
stores: {
5
+
dependencies: [{ id: "discord/packages/flux" }]
10
6
},
11
-
12
-
flux: {
13
-
dependencies: [{ ext: "spacepack", id: "spacepack" }, "connectStores:"]
7
+
ErrorBoundary: {
8
+
dependencies: [{ id: "react" }]
14
9
},
15
-
16
-
fluxDispatcher: {
17
-
dependencies: [
18
-
{ ext: "spacepack", id: "spacepack" },
19
-
"isDispatching",
20
-
"dispatch"
21
-
]
22
-
},
23
-
24
-
react: {
25
-
dependencies: [
26
-
{ ext: "spacepack", id: "spacepack" },
27
-
"__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
28
-
/\.?version(?:=|:)/,
29
-
/\.?createElement(?:=|:)/
30
-
]
31
-
},
32
-
33
-
stores: {
34
-
dependencies: [{ ext: "common", id: "flux" }]
10
+
icons: {
11
+
dependencies: [{ id: "react" }, { id: "discord/components/common/index" }]
35
12
}
36
13
};
+3
-1
packages/core-extensions/src/common/manifest.json
+3
-1
packages/core-extensions/src/common/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "common",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Common",
5
-
"tagline": "A *lot* of common clientmodding utilities from the Discord client",
7
+
"tagline": "Common client modding utilities for the Discord client",
6
8
"authors": ["Cynosphere", "NotNite"],
7
9
"tags": ["library"]
8
10
},
+27
packages/core-extensions/src/common/style.css
+27
packages/core-extensions/src/common/style.css
···
1
+
.moonlight-error-boundary {
2
+
margin: 0 0 15px;
3
+
padding: 10px;
4
+
border-radius: 5px;
5
+
font-size: 1rem;
6
+
font-weight: 300;
7
+
line-height: 22px;
8
+
color: var(--text-normal, white);
9
+
background: hsl(var(--red-400-hsl) / 0.1);
10
+
border: 2px solid hsl(var(--red-400-hsl) / 0.5);
11
+
12
+
.theme-light & {
13
+
color: var(--text-normal, black) !important;
14
+
}
15
+
16
+
& > h3 {
17
+
margin-bottom: 0.25rem;
18
+
}
19
+
20
+
& > .hljs {
21
+
background: var(--background-secondary);
22
+
border: 1px solid var(--background-tertiary);
23
+
white-space: pre-wrap;
24
+
font-family: var(--font-code);
25
+
user-select: text;
26
+
}
27
+
}
+47
packages/core-extensions/src/common/webpackModules/ErrorBoundary.tsx
+47
packages/core-extensions/src/common/webpackModules/ErrorBoundary.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import { ErrorBoundaryProps, ErrorBoundaryState } from "@moonlight-mod/types/coreExtensions/common";
3
+
4
+
const logger = moonlight.getLogger("ErrorBoundary");
5
+
6
+
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
7
+
constructor(props: ErrorBoundaryProps) {
8
+
super(props);
9
+
this.state = {
10
+
errored: false,
11
+
error: undefined,
12
+
componentStack: undefined
13
+
};
14
+
}
15
+
16
+
static getDerivedStateFromError(error: Error) {
17
+
return {
18
+
errored: true,
19
+
error
20
+
};
21
+
}
22
+
23
+
componentDidCatch(error: Error, { componentStack }: { componentStack: string }) {
24
+
logger.error(`${error}\n\nComponent stack:\n${componentStack}`);
25
+
this.setState({ error, componentStack });
26
+
}
27
+
28
+
render() {
29
+
const { noop, fallback: FallbackComponent, children, message } = this.props;
30
+
const { errored, error, componentStack } = this.state;
31
+
32
+
if (FallbackComponent) return <FallbackComponent children={children} {...this.state} />;
33
+
34
+
if (errored) {
35
+
return noop ? null : (
36
+
<div className={`moonlight-error-boundary`}>
37
+
<h3>{message ?? "An error occurred rendering this component:"}</h3>
38
+
<code className="hljs">{`${error}\n\nComponent stack:\n${componentStack}`}</code>
39
+
</div>
40
+
);
41
+
}
42
+
43
+
return children;
44
+
}
45
+
}
46
+
47
+
export default ErrorBoundary;
-41
packages/core-extensions/src/common/webpackModules/components.ts
-41
packages/core-extensions/src/common/webpackModules/components.ts
···
1
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
-
3
-
const Components = spacepack.findByCode("MasonryList:function")[0].exports;
4
-
const MarkdownParser = spacepack.findByCode(
5
-
"parseAutoModerationSystemMessage:"
6
-
)[0].exports.Z;
7
-
const LegacyText = spacepack.findByCode(".selectable", ".colorStandard")[0]
8
-
.exports.default;
9
-
const Flex = Object.values(
10
-
spacepack.findByCode(".flex" + "GutterSmall,")[0].exports
11
-
)[0];
12
-
13
-
const CardClasses = {};
14
-
spacepack
15
-
.lazyLoad(
16
-
"renderArtisanalHack",
17
-
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"ChannelSettings"/,
18
-
/webpackId:(\d+),name:"ChannelSettings"/
19
-
)
20
-
.then(() =>
21
-
Object.assign(
22
-
CardClasses,
23
-
spacepack.findByExports("card", "cardHeader", "inModal")[0].exports
24
-
)
25
-
);
26
-
27
-
const ControlClasses = spacepack.findByCode(
28
-
"title",
29
-
"titleDefault",
30
-
"dividerDefault"
31
-
)[0].exports;
32
-
33
-
// We use CJS export here because merging the exports from Components is annoying as shit
34
-
module.exports = {
35
-
...Components,
36
-
MarkdownParser,
37
-
LegacyText,
38
-
Flex,
39
-
CardClasses,
40
-
ControlClasses
41
-
};
-28
packages/core-extensions/src/common/webpackModules/flux.ts
-28
packages/core-extensions/src/common/webpackModules/flux.ts
···
1
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
-
3
-
const mod = spacepack.findByCode("connectStores:")[0].exports;
4
-
5
-
const useStateFromStores = spacepack.findFunctionByStrings(
6
-
mod,
7
-
'"useStateFromStores"'
8
-
)!;
9
-
10
-
module.exports = {
11
-
BatchedStoreListener: spacepack.findFunctionByStrings(
12
-
mod,
13
-
" tried to load a non-existent store."
14
-
),
15
-
Dispatcher: spacepack.findFunctionByStrings(mod, "_dispatchWithDevtools("),
16
-
Store: spacepack.findFunctionByStrings(mod, "registerActionHandlers("),
17
-
default: mod.ZP,
18
-
statesWillNeverBeEqual: spacepack.findFunctionByStrings(mod, "return!1"),
19
-
useStateFromStores,
20
-
useStateFromStoresArray: spacepack.findFunctionByStrings(
21
-
mod,
22
-
new RegExp(`return ${useStateFromStores.name}\\(.+?\\.[^Z]\\)`)
23
-
),
24
-
useStateFromStoresObject: spacepack.findFunctionByStrings(
25
-
mod,
26
-
new RegExp(`return ${useStateFromStores.name}\\(.+?\\.Z\\)`)
27
-
)
28
-
};
-6
packages/core-extensions/src/common/webpackModules/fluxDispatcher.ts
-6
packages/core-extensions/src/common/webpackModules/fluxDispatcher.ts
+31
packages/core-extensions/src/common/webpackModules/icons.ts
+31
packages/core-extensions/src/common/webpackModules/icons.ts
···
1
+
import { Icons, IconSize } from "@moonlight-mod/types/coreExtensions/common";
2
+
import { tokens } from "@moonlight-mod/wp/discord/components/common/index";
3
+
4
+
// This is defined in a Webpack module but we copy it here to be less breakage-prone
5
+
const sizes: Partial<Record<IconSize, number>> = {
6
+
xxs: 12,
7
+
xs: 16,
8
+
sm: 18,
9
+
md: 24,
10
+
lg: 32,
11
+
refresh_sm: 20
12
+
};
13
+
14
+
export const icons: Icons = {
15
+
parseProps(props) {
16
+
// NOTE: var() fallback is non-standard behavior, just for safety reasons
17
+
const color = props?.color ?? tokens?.colors?.["INTERACTIVE_NORMAL"] ?? "var(--interactive-normal)";
18
+
19
+
const size = sizes[props?.size ?? "md"];
20
+
21
+
return {
22
+
// note: this default size is also non-standard behavior, just for safety
23
+
width: size ?? props?.width ?? sizes.md!,
24
+
height: size ?? props?.width ?? sizes.md!,
25
+
26
+
fill: typeof color === "string" ? color : color.css,
27
+
className: props?.colorClass ?? ""
28
+
};
29
+
}
30
+
};
31
+
export default icons;
-7
packages/core-extensions/src/common/webpackModules/react.ts
-7
packages/core-extensions/src/common/webpackModules/react.ts
+2
-2
packages/core-extensions/src/common/webpackModules/stores.ts
+2
-2
packages/core-extensions/src/common/webpackModules/stores.ts
···
1
-
import Flux from "@moonlight-mod/wp/common_flux";
1
+
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
2
2
3
3
module.exports = new Proxy(
4
4
{},
5
5
{
6
6
get: function (target, key, receiver) {
7
-
const allStores = Flux.Store.getAll();
7
+
const allStores = Store.getAll();
8
8
9
9
let targetStore;
10
10
for (const store of allStores) {
+84
packages/core-extensions/src/componentEditor/index.ts
+84
packages/core-extensions/src/componentEditor/index.ts
···
1
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
// dm list
5
+
{
6
+
find: ".interactiveSystemDM]:",
7
+
replace: [
8
+
{
9
+
match: /decorators:(\i\.isSystemDM\(\)\?\(0,\i\.jsx\)\(.+?verified:!0}\):null)/,
10
+
replacement: (_, decorators) =>
11
+
`decorators:require("componentEditor_dmList").default._patchDecorators([${decorators}],arguments[0])`
12
+
},
13
+
{
14
+
match: /(?<=selected:\i,)children:\[/,
15
+
replacement: 'children:require("componentEditor_dmList").default._patchItems(['
16
+
},
17
+
{
18
+
match: /(?<=(onMouseDown|nameplate):\i}\))]/,
19
+
replacement: "],arguments[0])"
20
+
}
21
+
],
22
+
hardFail: true
23
+
},
24
+
25
+
// member list
26
+
{
27
+
find: ".lostPermission",
28
+
replace: [
29
+
{
30
+
match:
31
+
/(?<=\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[\(0,\i\.jsx\)\(\i,{user:\i}\),.+?onClickPremiumGuildIcon:\i}\)])/,
32
+
replacement: (_, decorators) =>
33
+
`children:require("componentEditor_memberList").default._patchDecorators(${decorators},arguments[0])`
34
+
},
35
+
{
36
+
match: /name:null==\i\?\(0,\i\.jsx\)\("span"/,
37
+
replacement: (orig: string) =>
38
+
`children:require("componentEditor_memberList").default._patchItems([],arguments[0]),${orig}`
39
+
}
40
+
]
41
+
},
42
+
43
+
// messages
44
+
{
45
+
find: '},"new-member")),',
46
+
replace: [
47
+
{
48
+
match: /(?<=\.BADGES](=|:))(\i)(;|})/,
49
+
replacement: (_, leading, badges, trailing) =>
50
+
`require("componentEditor_messages").default._patchUsernameBadges(${badges},arguments[0])${trailing}`
51
+
},
52
+
{
53
+
match: /(?<=className:\i,)badges:(\i)/,
54
+
replacement: (_, badges) =>
55
+
`badges:require("componentEditor_messages").default._patchBadges(${badges},arguments[0])`
56
+
},
57
+
{
58
+
match: /(?<=username:\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[.+?])}\),usernameSpanId:/,
59
+
replacement: (_, elements) =>
60
+
`children:require("componentEditor_messages").default._patchUsername(${elements},arguments[0])}),usernameSpanId:`
61
+
}
62
+
]
63
+
},
64
+
{
65
+
find: '.provider&&"Discord"===',
66
+
replace: {
67
+
match: /(?<=\.container\),)children:(\[.+?this\.renderSuppressConfirmModal\(\),.+?\])}\)/,
68
+
replacement: (_, elements) =>
69
+
`children:require("componentEditor_messages").default._patchAccessories(${elements},this.props)})`
70
+
}
71
+
}
72
+
];
73
+
74
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
75
+
dmList: {
76
+
dependencies: [{ id: "react" }]
77
+
},
78
+
memberList: {
79
+
dependencies: [{ id: "react" }]
80
+
},
81
+
messages: {
82
+
dependencies: [{ id: "react" }]
83
+
}
84
+
};
+11
packages/core-extensions/src/componentEditor/manifest.json
+11
packages/core-extensions/src/componentEditor/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "componentEditor",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "Component Editor",
7
+
"tagline": "A library to add to commonly patched components",
8
+
"authors": ["Cynosphere"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+61
packages/core-extensions/src/componentEditor/webpackModules/dmList.tsx
+61
packages/core-extensions/src/componentEditor/webpackModules/dmList.tsx
···
1
+
import {
2
+
DMList,
3
+
DMListItem,
4
+
DMListDecorator,
5
+
DMListAnchorIndicies,
6
+
DMListDecoratorAnchorIndicies
7
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
8
+
import React from "@moonlight-mod/wp/react";
9
+
10
+
const items: Record<string, DMListItem> = {};
11
+
const decorators: Record<string, DMListDecorator> = {};
12
+
13
+
function addEntries(
14
+
elements: React.ReactNode[],
15
+
entries: Record<string, DMListItem | DMListDecorator>,
16
+
indicies: Partial<Record<keyof typeof DMListAnchorIndicies | keyof typeof DMListDecoratorAnchorIndicies, number>>,
17
+
props: any
18
+
) {
19
+
const originalElements = [...elements];
20
+
for (const [id, entry] of Object.entries(entries)) {
21
+
const component = <entry.component {...props} key={id} />;
22
+
23
+
if (entry.anchor === undefined) {
24
+
if (entry.before) {
25
+
elements.splice(0, 0, component);
26
+
} else {
27
+
elements.push(component);
28
+
}
29
+
} else {
30
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
31
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
32
+
}
33
+
}
34
+
}
35
+
36
+
export const dmList: DMList = {
37
+
addItem(id, component, anchor, before = false) {
38
+
items[id] = {
39
+
component,
40
+
anchor,
41
+
before
42
+
};
43
+
},
44
+
addDecorator(id, component, anchor, before = false) {
45
+
decorators[id] = {
46
+
component,
47
+
anchor,
48
+
before
49
+
};
50
+
},
51
+
_patchItems(elements, props) {
52
+
addEntries(elements, items, DMListAnchorIndicies, props);
53
+
return elements;
54
+
},
55
+
_patchDecorators(elements, props) {
56
+
addEntries(elements, decorators, DMListDecoratorAnchorIndicies, props);
57
+
return elements;
58
+
}
59
+
};
60
+
61
+
export default dmList;
+50
packages/core-extensions/src/componentEditor/webpackModules/memberList.tsx
+50
packages/core-extensions/src/componentEditor/webpackModules/memberList.tsx
···
1
+
import {
2
+
MemberList,
3
+
MemberListDecorator,
4
+
MemberListDecoratorAnchorIndicies
5
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
6
+
import React from "@moonlight-mod/wp/react";
7
+
8
+
const items: Record<string, React.FC<any>> = {};
9
+
const decorators: Record<string, MemberListDecorator> = {};
10
+
11
+
export const memberList: MemberList = {
12
+
addItem(id, component) {
13
+
items[id] = component;
14
+
},
15
+
addDecorator(id, component, anchor, before = false) {
16
+
decorators[id] = {
17
+
component,
18
+
anchor,
19
+
before
20
+
};
21
+
},
22
+
_patchItems(elements, props) {
23
+
for (const [id, Component] of Object.entries(items)) {
24
+
elements.push(<Component {...props} key={id} />);
25
+
}
26
+
27
+
return elements;
28
+
},
29
+
_patchDecorators(elements, props) {
30
+
const originalElements = [...elements];
31
+
for (const [id, entry] of Object.entries(decorators)) {
32
+
const component = <entry.component {...props} key={id} />;
33
+
34
+
if (entry.anchor === undefined) {
35
+
if (entry.before) {
36
+
elements.splice(0, 0, component);
37
+
} else {
38
+
elements.push(component);
39
+
}
40
+
} else {
41
+
const index = elements.indexOf(originalElements[MemberListDecoratorAnchorIndicies[entry.anchor]!]);
42
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
43
+
}
44
+
}
45
+
46
+
return elements;
47
+
}
48
+
};
49
+
50
+
export default memberList;
+97
packages/core-extensions/src/componentEditor/webpackModules/messages.tsx
+97
packages/core-extensions/src/componentEditor/webpackModules/messages.tsx
···
1
+
import {
2
+
MessageBadge,
3
+
MessageBadgeIndicies,
4
+
Messages,
5
+
MessageUsername,
6
+
MessageUsernameBadge,
7
+
MessageUsernameBadgeIndicies,
8
+
MessageUsernameIndicies
9
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
10
+
import React from "@moonlight-mod/wp/react";
11
+
12
+
const username: Record<string, MessageUsername> = {};
13
+
const usernameBadges: Record<string, MessageUsernameBadge> = {};
14
+
const badges: Record<string, MessageBadge> = {};
15
+
const accessories: Record<string, React.FC<any>> = {};
16
+
17
+
function addEntries(
18
+
elements: React.ReactNode[],
19
+
entries: Record<string, MessageUsername | MessageUsernameBadge | MessageBadge>,
20
+
indicies: Partial<
21
+
Record<
22
+
| keyof typeof MessageUsernameIndicies
23
+
| keyof typeof MessageUsernameBadgeIndicies
24
+
| keyof typeof MessageBadgeIndicies,
25
+
number
26
+
>
27
+
>,
28
+
props: any
29
+
) {
30
+
const originalElements = [...elements];
31
+
for (const [id, entry] of Object.entries(entries)) {
32
+
const component = <entry.component {...props} key={id} />;
33
+
34
+
if (entry.anchor === undefined) {
35
+
if (entry.before) {
36
+
elements.splice(0, 0, component);
37
+
} else {
38
+
elements.push(component);
39
+
}
40
+
} else {
41
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
42
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
43
+
}
44
+
}
45
+
}
46
+
47
+
function addComponents(elements: React.ReactNode[], components: Record<string, React.FC<any>>, props: any) {
48
+
for (const [id, Component] of Object.entries(components)) {
49
+
const component = <Component {...props} key={id} />;
50
+
elements.push(component);
51
+
}
52
+
}
53
+
54
+
export const messages: Messages = {
55
+
addToUsername(id, component, anchor, before = false) {
56
+
username[id] = {
57
+
component,
58
+
anchor,
59
+
before
60
+
};
61
+
},
62
+
addUsernameBadge(id, component, anchor, before = false) {
63
+
usernameBadges[id] = {
64
+
component,
65
+
anchor,
66
+
before
67
+
};
68
+
},
69
+
addBadge(id, component, anchor, before = false) {
70
+
badges[id] = {
71
+
component,
72
+
anchor,
73
+
before
74
+
};
75
+
},
76
+
addAccessory(id, component) {
77
+
accessories[id] = component;
78
+
},
79
+
_patchUsername(elements, props) {
80
+
addEntries(elements, username, MessageUsernameIndicies, props);
81
+
return elements;
82
+
},
83
+
_patchUsernameBadges(elements, props) {
84
+
addEntries(elements, usernameBadges, MessageUsernameBadgeIndicies, props);
85
+
return elements;
86
+
},
87
+
_patchBadges(elements, props) {
88
+
addEntries(elements, badges, MessageBadgeIndicies, props);
89
+
return elements;
90
+
},
91
+
_patchAccessories(elements, props) {
92
+
addComponents(elements, accessories, props);
93
+
return elements;
94
+
}
95
+
};
96
+
97
+
export default messages;
+5
-13
packages/core-extensions/src/contextMenu/index.tsx
+5
-13
packages/core-extensions/src/contextMenu/index.tsx
···
5
5
find: "Menu API only allows Items and groups of Items as children.",
6
6
replace: [
7
7
{
8
-
match: /(?<=let{navId[^}]+?}=(.),(.)=.\(.\))/,
9
-
replacement: (_, props, items) =>
10
-
`,__contextMenu=!${props}.__contextMenu_evilMenu&&require("contextMenu_contextMenu")._patchMenu(${props}, ${items})`
8
+
match: /(?<=let{navId[^}]+?}=(.),.=).+?(?=,)/,
9
+
replacement: (items, props) => `require("contextMenu_contextMenu")._patchMenu(${props},${items})`
11
10
}
12
11
]
13
12
},
···
16
15
replace: [
17
16
{
18
17
match: /(?<=let\{[^}]+?\}=.;return ).\({[^}]+?}\)/,
19
-
replacement: (render) =>
20
-
`require("contextMenu_contextMenu")._saveProps(this,${render})`
18
+
replacement: (render) => `require("contextMenu_contextMenu")._saveProps(this,${render})`
21
19
}
22
20
]
23
21
}
···
25
23
26
24
export const webpackModules: Record<string, ExtensionWebpackModule> = {
27
25
contextMenu: {
28
-
dependencies: [
29
-
{ ext: "spacepack", id: "spacepack" },
30
-
"Menu API only allows Items and groups of Items as children."
31
-
]
26
+
dependencies: [{ ext: "spacepack", id: "spacepack" }, "Menu API only allows Items and groups of Items as children."]
32
27
},
33
28
evilMenu: {
34
-
dependencies: [
35
-
{ ext: "spacepack", id: "spacepack" },
36
-
"Menu API only allows Items and groups of Items as children."
37
-
]
29
+
dependencies: [{ ext: "spacepack", id: "spacepack" }, "Menu API only allows Items and groups of Items as children."]
38
30
}
39
31
};
+2
packages/core-extensions/src/contextMenu/manifest.json
+2
packages/core-extensions/src/contextMenu/manifest.json
+25
-30
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
+25
-30
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
···
1
-
import {
2
-
InternalItem,
3
-
MenuElement,
4
-
MenuProps
5
-
} from "@moonlight-mod/types/coreExtensions/contextMenu";
1
+
import { InternalItem, Menu, MenuElement } from "@moonlight-mod/types/coreExtensions/contextMenu";
6
2
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
7
3
import parser from "@moonlight-mod/wp/contextMenu_evilMenu";
8
4
5
+
// NOTE: We originally had item as a function that returned this, but it didn't
6
+
// quite know how to work out the type and thought it was a JSX element (it
7
+
// *technically* was). This has less type safety, but a @ts-expect-error has
8
+
// zero, so it's better than nothing.
9
+
type ReturnType = MenuElement | MenuElement[];
10
+
9
11
type Patch = {
10
12
navId: string;
11
-
item: (
12
-
props: any
13
-
) =>
14
-
| React.ReactComponentElement<MenuElement>
15
-
| React.ReactComponentElement<MenuElement>[];
16
-
anchorId: string;
13
+
item: React.FC<any>;
14
+
anchor: string | RegExp;
17
15
before: boolean;
18
16
};
19
17
20
-
export function addItem<T>(
21
-
navId: string,
22
-
item: (
23
-
props: T
24
-
) =>
25
-
| React.ReactComponentElement<MenuElement>
26
-
| React.ReactComponentElement<MenuElement>[],
27
-
anchorId: string,
28
-
before = false
29
-
) {
30
-
patches.push({ navId, item, anchorId, before });
18
+
function addItem<T = any>(navId: string, item: React.FC<T>, anchor: string | RegExp, before = false) {
19
+
if (anchor instanceof RegExp && anchor.flags.includes("g"))
20
+
throw new Error("anchor regular expression should not be global");
21
+
patches.push({ navId, item, anchor, before });
31
22
}
32
23
33
-
export const patches: Patch[] = [];
34
-
function _patchMenu(props: MenuProps, items: InternalItem[]) {
24
+
const patches: Patch[] = [];
25
+
function _patchMenu(props: React.ComponentProps<Menu>, items: InternalItem[]) {
35
26
const matches = patches.filter((p) => p.navId === props.navId);
36
-
if (!matches.length) return;
27
+
if (!matches.length) return items;
37
28
38
29
for (const patch of matches) {
39
-
const idx = items.findIndex((i) => i.key === patch.anchorId);
30
+
const idx = items.findIndex((i) =>
31
+
typeof patch.anchor === "string" ? i.key === patch.anchor : patch.anchor.test(i.key!)
32
+
);
40
33
if (idx === -1) continue;
41
-
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps)));
34
+
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps) as ReturnType));
42
35
}
36
+
37
+
return items;
43
38
}
44
39
45
40
let menuProps: any;
···
56
51
}
57
52
58
53
module.exports = {
54
+
patches,
59
55
addItem,
60
56
_patchMenu,
61
57
_saveProps
62
58
};
63
59
64
60
// Unmangle Menu elements
61
+
// spacepack.require.m[moonlight.moonmap.modules["discord/modules/menus/web/Menu"]].toString();
65
62
const code =
66
63
spacepack.require.m[
67
-
spacepack.findByCode(
68
-
"Menu API only allows Items and groups of Items as children."
69
-
)[0].id
64
+
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
70
65
].toString();
71
66
72
67
let MangledMenu;
+11
-20
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
+11
-20
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
···
1
1
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
2
3
+
// spacepack.require.m[moonlight.moonmap.modules["discord/modules/menus/web/Menu"]].toString();
3
4
let code =
4
5
spacepack.require.m[
5
-
spacepack.findByCode(
6
-
"Menu API only allows Items and groups of Items as children."
7
-
)[0].id
6
+
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
8
7
].toString();
9
-
code = code.replace(
10
-
/onSelect:(.)}=(.),.=(.\(.\)),/,
11
-
`onSelect:$1}=$2;return $3;let `
12
-
);
13
-
const mod = new Function(
14
-
"module",
15
-
"exports",
16
-
"require",
17
-
`(${code}).apply(this, arguments)`
18
-
);
8
+
9
+
const parserSym = code.match(/(?<=_patchMenu\(.,).+?(?=\()/)![0];
10
+
11
+
code = code.replace(/{(.):\(\)=>./, (orig, e) => `{${e}:()=>${parserSym}`);
12
+
const mod = new Function("module", "exports", "require", `(${code}).apply(this, arguments)`);
13
+
19
14
const exp: any = {};
20
15
mod({}, exp, require);
21
-
const Menu = spacepack.findFunctionByStrings(exp, "isUsingKeyboardNavigation")!;
22
-
module.exports = (el: any) => {
23
-
return Menu({
24
-
children: el,
25
-
__contextMenu_evilMenu: true
26
-
});
27
-
};
16
+
17
+
const parser = spacepack.findFunctionByStrings(exp, "Menu API only allows Items and groups of Items as children.")!;
18
+
module.exports = parser;
+19
packages/core-extensions/src/devToolsExtensions/host.ts
+19
packages/core-extensions/src/devToolsExtensions/host.ts
···
1
+
import { app, session } from "electron";
2
+
import { resolve } from "node:path";
3
+
import Logger from "@moonlight-mod/core/util/logger";
4
+
5
+
const logger = new Logger("DevTools Extensions");
6
+
7
+
app.whenReady().then(async () => {
8
+
const paths = moonlightHost.getConfigOption<string[]>("devToolsExtensions", "paths") ?? [];
9
+
10
+
for (const path of paths) {
11
+
const resolved = resolve(path);
12
+
13
+
try {
14
+
await session.defaultSession.loadExtension(resolved);
15
+
} catch (err) {
16
+
logger.error(`Failed to load an extension in "${resolved}":`, err);
17
+
}
18
+
}
19
+
});
+22
packages/core-extensions/src/devToolsExtensions/manifest.json
+22
packages/core-extensions/src/devToolsExtensions/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "devToolsExtensions",
4
+
"meta": {
5
+
"name": "DevTools Extensions",
6
+
"tagline": "Loads Chrome extensions into Electron DevTools",
7
+
"authors": [
8
+
"Cynosphere"
9
+
],
10
+
"tags": [
11
+
"development"
12
+
]
13
+
},
14
+
"settings": {
15
+
"paths": {
16
+
"advice": "restart",
17
+
"displayName": "Extension Paths",
18
+
"type": "list"
19
+
}
20
+
},
21
+
"apiLevel": 2
22
+
}
+4
-25
packages/core-extensions/src/disableSentry/host.ts
+4
-25
packages/core-extensions/src/disableSentry/host.ts
···
1
1
import { join } from "node:path";
2
2
import { Module } from "node:module";
3
-
import { BrowserWindow } from "electron";
4
3
5
4
const logger = moonlightHost.getLogger("disableSentry");
6
5
7
6
if (moonlightHost.asarPath !== "moonlightDesktop") {
8
7
try {
9
-
const hostSentryPath = require.resolve(
10
-
join(moonlightHost.asarPath, "node_modules", "@sentry", "electron")
11
-
);
12
-
require.cache[hostSentryPath] = new Module(
13
-
hostSentryPath,
14
-
require.cache[require.resolve(moonlightHost.asarPath)]
15
-
);
8
+
const hostSentryPath = require.resolve(join(moonlightHost.asarPath, "node_modules", "@sentry", "electron"));
9
+
require.cache[hostSentryPath] = new Module(hostSentryPath, require.cache[require.resolve(moonlightHost.asarPath)]);
16
10
require.cache[hostSentryPath]!.exports = {
17
11
init: () => {},
18
12
captureException: () => {},
19
13
setTag: () => {},
20
-
setUser: () => {}
14
+
setUser: () => {},
15
+
captureMessage: () => {}
21
16
};
22
17
logger.debug("Stubbed Sentry host side!");
23
18
} catch (err) {
24
19
logger.error("Failed to stub Sentry host side:", err);
25
20
}
26
21
}
27
-
28
-
moonlightHost.events.on("window-created", (window: BrowserWindow) => {
29
-
window.webContents.session.webRequest.onBeforeRequest(
30
-
{
31
-
urls: [
32
-
"https://*.sentry.io/*",
33
-
"https://*.discord.com/error-reporting-proxy/*",
34
-
"https://discord.com/assets/sentry.*.js",
35
-
"https://*.discord.com/assets/sentry.*.js"
36
-
]
37
-
},
38
-
function (details, callback) {
39
-
callback({ cancel: true });
40
-
}
41
-
);
42
-
});
+5
-6
packages/core-extensions/src/disableSentry/index.ts
+5
-6
packages/core-extensions/src/disableSentry/index.ts
···
6
6
find: "profiledRootComponent:",
7
7
replace: {
8
8
type: PatchReplaceType.Normal,
9
-
match: /(?<=\.Z=){.+?}}/,
10
-
replacement: 'require("disableSentry_stub").proxy()'
9
+
match: /Z:\(\)=>\i/,
10
+
replacement: 'Z:()=>require("disableSentry_stub").proxy()'
11
11
}
12
12
},
13
13
{
14
-
find: "window.DiscordSentry.addBreadcrumb",
14
+
find: "this._sentryUtils=",
15
15
replace: {
16
16
type: PatchReplaceType.Normal,
17
-
match: /Z:function\(\){return .}/,
18
-
replacement:
19
-
'default:function(){return (...args)=>{moonlight.getLogger("disableSentry").debug("Sentry calling addBreadcrumb passthrough:", ...args);}}'
17
+
match: /(?<=this._sentryUtils=)./,
18
+
replacement: "undefined"
20
19
}
21
20
},
22
21
{
+12
-1
packages/core-extensions/src/disableSentry/manifest.json
+12
-1
packages/core-extensions/src/disableSentry/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "disableSentry",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Disable Sentry",
5
7
"tagline": "Turns off Discord's error reporting systems",
6
8
"authors": ["Cynosphere", "NotNite"],
7
9
"tags": ["privacy"]
8
-
}
10
+
},
11
+
"blocked": [
12
+
"https://*.sentry.io/*",
13
+
"https://*.discord.com/error-reporting-proxy/*",
14
+
"https://discord.com/assets/sentry.*.js",
15
+
"https://*.discord.com/assets/sentry.*.js",
16
+
"https://*.discordapp.com/error-reporting-proxy/*",
17
+
"https://discordapp.com/assets/sentry.*.js",
18
+
"https://*.discordapp.com/assets/sentry.*.js"
19
+
]
9
20
}
+13
-19
packages/core-extensions/src/disableSentry/node.ts
+13
-19
packages/core-extensions/src/disableSentry/node.ts
···
5
5
6
6
const logger = moonlightNode.getLogger("disableSentry");
7
7
8
-
if (!ipcRenderer.sendSync(constants.ipcGetIsMoonlightDesktop)) {
9
-
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
10
-
try {
11
-
const sentryPath = require.resolve(
12
-
resolve(preloadPath, "..", "node_modules", "@sentry", "electron")
13
-
);
14
-
require.cache[sentryPath] = new Module(
15
-
sentryPath,
16
-
require.cache[require.resolve(preloadPath)]
17
-
);
18
-
require.cache[sentryPath]!.exports = {
19
-
init: () => {},
20
-
setTag: () => {},
21
-
setUser: () => {}
22
-
};
23
-
logger.debug("Stubbed Sentry node side!");
24
-
} catch (err) {
25
-
logger.error("Failed to stub Sentry:", err);
26
-
}
8
+
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
9
+
try {
10
+
const sentryPath = require.resolve(resolve(preloadPath, "..", "node_modules", "@sentry", "electron"));
11
+
require.cache[sentryPath] = new Module(sentryPath, require.cache[require.resolve(preloadPath)]);
12
+
require.cache[sentryPath]!.exports = {
13
+
init: () => {},
14
+
setTag: () => {},
15
+
setUser: () => {},
16
+
captureMessage: () => {}
17
+
};
18
+
logger.debug("Stubbed Sentry node side!");
19
+
} catch (err) {
20
+
logger.error("Failed to stub Sentry:", err);
27
21
}
+1
-2
packages/core-extensions/src/disableSentry/webpackModules/stub.ts
+1
-2
packages/core-extensions/src/disableSentry/webpackModules/stub.ts
···
23
23
throw Error("crash");
24
24
};
25
25
} else if (keys.includes(prop.toString())) {
26
-
return (...args: any[]) =>
27
-
logger.debug(`Sentry calling "${prop.toString()}":`, ...args);
26
+
return (...args: any[]) => logger.debug(`Sentry calling "${prop.toString()}":`, ...args);
28
27
} else {
29
28
return undefined;
30
29
}
+39
-2
packages/core-extensions/src/experiments/index.ts
+39
-2
packages/core-extensions/src/experiments/index.ts
···
11
11
{
12
12
find: '"scientist:triggered"', // Scientist? Triggered.
13
13
replace: {
14
-
match: /(?<=personal_connection_id\|\|)!1/,
15
-
replacement: "!0"
14
+
match: ".personal_connection_id",
15
+
replacement: ".personal_connection_id || true"
16
+
}
17
+
},
18
+
19
+
// Enable staff help menu
20
+
{
21
+
find: ".HEADER_BAR)",
22
+
replace: {
23
+
match: /&&\((\i)\?\(0,/,
24
+
replacement: (_, isStaff) =>
25
+
`&&(((moonlight.getConfigOption("experiments","devtools")??false)?true:${isStaff})?(0,`
26
+
}
27
+
},
28
+
// staff help menu - visual refresh
29
+
{
30
+
find: '("AppTitleBar")',
31
+
replace: {
32
+
match: /{hasBugReporterAccess:(\i)}=\i\.\i\.useExperiment\({location:"HeaderBar"},{autoTrackExposure:!1}\);/,
33
+
replacement: (orig, isStaff) =>
34
+
`${orig}if(moonlight.getConfigOption("experiments","devtools")??false)${isStaff}=true;`
35
+
}
36
+
},
37
+
{
38
+
find: 'navId:"staff-help-popout",',
39
+
replace: {
40
+
match: /isDiscordDeveloper:(\i)}\),/,
41
+
replacement: (_, isStaff) =>
42
+
`isDiscordDeveloper:(moonlight.getConfigOption("experiments","devtools")??false)||${isStaff}}),`
43
+
}
44
+
},
45
+
46
+
// Enable further staff-locked options
47
+
{
48
+
find: "shouldShowLurkerModeUpsellPopout:",
49
+
replace: {
50
+
match: /\.useReducedMotion,isStaff:(\i)(,|})/,
51
+
replacement: (_, isStaff, trail) =>
52
+
`.useReducedMotion,isStaff:(moonlight.getConfigOption("experiments","staffSettings")??false)?true:${isStaff}${trail}`
16
53
}
17
54
}
18
55
];
+12
-2
packages/core-extensions/src/experiments/manifest.json
+12
-2
packages/core-extensions/src/experiments/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "experiments",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Experiments",
5
7
"tagline": "Allows you to configure Discord's internal A/B testing features",
···
7
9
"tags": ["dangerZone"]
8
10
},
9
11
"settings": {
10
-
"sections": {
12
+
"devtools": {
13
+
"advice": "reload",
14
+
"displayName": "Enable staff help menu (DevTools)",
15
+
"type": "boolean",
16
+
"default": false
17
+
},
18
+
"staffSettings": {
19
+
"advice": "reload",
11
20
"displayName": "Allow access to other staff settings elsewhere",
12
-
"type": "boolean"
21
+
"type": "boolean",
22
+
"default": false
13
23
}
14
24
}
15
25
}
+7
-19
packages/core-extensions/src/markdown/index.ts
+7
-19
packages/core-extensions/src/markdown/index.ts
···
6
6
replace: [
7
7
{
8
8
match: /={newline:(.+?)},(.{1,2})=\(0,/,
9
-
replacement: (_, rules, RULES) =>
10
-
`=require("markdown_markdown")._addRules({newline:${rules}}),${RULES}=(0,`
9
+
replacement: (_, rules, RULES) => `=require("markdown_markdown")._addRules({newline:${rules}}),${RULES}=(0,`
11
10
},
12
11
{
13
-
match: /(?<=var (.{1,2})={RULES:.+?})/,
14
-
replacement: (_, rulesets) =>
15
-
`;require("markdown_markdown")._applyRulesetBlacklist(${rulesets});`
12
+
match: /(?<=;(.{1,2}\.Z)={RULES:.+?})/,
13
+
replacement: (_, rulesets) => `;require("markdown_markdown")._applyRulesetBlacklist(${rulesets});`
16
14
}
17
15
]
18
16
},
···
25
23
`__slateRules,${rulesDef}=__slateRules=require("markdown_markdown")._addSlateRules({link:{${rules}}),${syntaxBefore}=new Set`
26
24
},
27
25
{
28
-
match:
29
-
/(originalMatch:.}=(.);)(.+?)case"emoticon":(return .+?;)(.+?)case"link":{(.+?)}default:/,
30
-
replacement: (
31
-
_,
32
-
start,
33
-
rule,
34
-
body,
35
-
plaintextReturn,
36
-
otherRules,
37
-
inlineStyleBody
38
-
) =>
39
-
`${start}if(${rule}.type.startsWith("__moonlight_")){if(__slateRules[${rule}.type].type=="inlineStyle"){${inlineStyleBody}}else{${plaintextReturn}}}${body}case"emoticon":${plaintextReturn}${otherRules}case"link":{${inlineStyleBody}}default:`
26
+
match: /(originalMatch:.}=(.);)(.+?)case"emoticon":(return .+?;)(.+?)case"subtext":{(.+?)}default:/,
27
+
replacement: (_, start, rule, body, plaintextReturn, otherRules, inlineStyleBody) =>
28
+
`${start}if(${rule}.type.startsWith("__moonlight_")){if(__slateRules[${rule}.type].type=="inlineStyle"){${inlineStyleBody}}else{${plaintextReturn}}}${body}case"emoticon":${plaintextReturn}${otherRules}case"subtext":{${inlineStyleBody}}default:`
40
29
}
41
30
]
42
31
},
···
44
33
find: '"Slate: Unknown decoration attribute: "',
45
34
replace: {
46
35
match: /=({strong:.+?});/,
47
-
replacement: (_, rules) =>
48
-
`=require("markdown_markdown")._addSlateDecorators(${rules});`
36
+
replacement: (_, rules) => `=require("markdown_markdown")._addSlateDecorators(${rules});`
49
37
}
50
38
}
51
39
];
+2
packages/core-extensions/src/markdown/manifest.json
+2
packages/core-extensions/src/markdown/manifest.json
+4
-17
packages/core-extensions/src/markdown/webpackModules/markdown.ts
+4
-17
packages/core-extensions/src/markdown/webpackModules/markdown.ts
···
1
-
/* eslint-disable no-console */
2
-
import {
3
-
MarkdownRule,
4
-
Ruleset,
5
-
SlateRule
6
-
} from "@moonlight-mod/types/coreExtensions/markdown";
1
+
import { MarkdownRule, Ruleset, SlateRule } from "@moonlight-mod/types/coreExtensions/markdown";
7
2
8
-
export const rules: Record<
9
-
string,
10
-
(rules: Record<string, MarkdownRule>) => MarkdownRule
11
-
> = {};
12
-
export const slateRules: Record<
13
-
string,
14
-
(rules: Record<string, SlateRule>) => SlateRule
15
-
> = {};
3
+
export const rules: Record<string, (rules: Record<string, MarkdownRule>) => MarkdownRule> = {};
4
+
export const slateRules: Record<string, (rules: Record<string, SlateRule>) => SlateRule> = {};
16
5
export const slateDecorators: Record<string, string> = {};
17
6
export const ruleBlacklists: Record<Ruleset, Record<string, boolean>> = {
18
7
RULES: {},
···
67
56
return originalRules;
68
57
}
69
58
70
-
export function _applyRulesetBlacklist(
71
-
rulesets: Record<Ruleset, Record<string, MarkdownRule>>
72
-
) {
59
+
export function _applyRulesetBlacklist(rulesets: Record<Ruleset, Record<string, MarkdownRule>>) {
73
60
for (const ruleset of Object.keys(rulesets) as Ruleset[]) {
74
61
if (ruleset === "RULES") continue;
75
62
+108
packages/core-extensions/src/moonbase/host.ts
+108
packages/core-extensions/src/moonbase/host.ts
···
1
+
import * as electron from "electron";
2
+
import * as fs from "node:fs/promises";
3
+
import * as path from "node:path";
4
+
import getNatives from "./native";
5
+
import { MoonlightBranch } from "@moonlight-mod/types";
6
+
7
+
const natives = getNatives();
8
+
9
+
const confirm = (action: string) =>
10
+
electron.dialog
11
+
.showMessageBox({
12
+
title: "Are you sure?",
13
+
message: `Are you sure? This will ${action} and restart Discord.`,
14
+
type: "warning",
15
+
buttons: ["OK", "Cancel"]
16
+
})
17
+
.then((r) => r.response === 0);
18
+
19
+
async function updateAndRestart() {
20
+
if (!(await confirm("update moonlight"))) return;
21
+
const newVersion = await natives.checkForMoonlightUpdate();
22
+
23
+
if (newVersion === null) {
24
+
electron.dialog.showMessageBox({ message: "You are already on the latest version of moonlight." });
25
+
return;
26
+
}
27
+
28
+
try {
29
+
await natives.updateMoonlight();
30
+
await electron.dialog.showMessageBox({ message: "Update successful, restarting Discord." });
31
+
electron.app.relaunch();
32
+
electron.app.exit(0);
33
+
} catch {
34
+
await electron.dialog.showMessageBox({
35
+
message: "Failed to update moonlight. Please use the installer instead.",
36
+
type: "error"
37
+
});
38
+
}
39
+
}
40
+
41
+
async function resetConfig() {
42
+
if (!(await confirm("reset your configuration"))) return;
43
+
44
+
const config = await moonlightHost.getConfigPath();
45
+
const dir = path.dirname(config);
46
+
const branch = path.basename(config, ".json");
47
+
await fs.rename(config, path.join(dir, `${branch}-backup-${Math.floor(Date.now() / 1000)}.json`));
48
+
49
+
await electron.dialog.showMessageBox({ message: "Configuration reset, restarting Discord." });
50
+
electron.app.relaunch();
51
+
electron.app.exit(0);
52
+
}
53
+
54
+
async function changeBranch(branch: MoonlightBranch) {
55
+
if (moonlightHost.branch === branch) return;
56
+
if (!(await confirm("switch branches"))) return;
57
+
try {
58
+
await natives.updateMoonlight(branch);
59
+
await electron.dialog.showMessageBox({ message: "Branch switch successful, restarting Discord." });
60
+
electron.app.relaunch();
61
+
electron.app.exit(0);
62
+
} catch (e) {
63
+
await electron.dialog.showMessageBox({ message: "Failed to switch branches:\n" + e, type: "error" });
64
+
}
65
+
}
66
+
67
+
function showAbout() {
68
+
electron.dialog.showMessageBox({
69
+
title: "About moonlight",
70
+
message: `moonlight ${moonlightHost.branch} ${moonlightHost.version}`
71
+
});
72
+
}
73
+
74
+
electron.app.whenReady().then(() => {
75
+
const original = electron.Menu.buildFromTemplate;
76
+
electron.Menu.buildFromTemplate = function (entries) {
77
+
const i = entries.findIndex((e) => e.label === "Check for Updates...");
78
+
if (i === -1) return original.call(this, entries);
79
+
80
+
if (!entries.find((e) => e.label === "moonlight")) {
81
+
const options: Electron.MenuItemConstructorOptions[] = [
82
+
{ label: "Update and restart", click: updateAndRestart },
83
+
{ label: "Reset config", click: resetConfig }
84
+
];
85
+
86
+
if (moonlightHost.branch !== MoonlightBranch.DEV) {
87
+
options.push({
88
+
label: "Switch branch",
89
+
submenu: [MoonlightBranch.STABLE, MoonlightBranch.NIGHTLY].map((branch) => ({
90
+
label: branch,
91
+
type: "radio",
92
+
checked: moonlightHost.branch === branch,
93
+
click: () => changeBranch(branch)
94
+
}))
95
+
});
96
+
}
97
+
98
+
options.push({ label: "About", click: showAbout });
99
+
100
+
entries.splice(i + 1, 0, {
101
+
label: "moonlight",
102
+
submenu: options
103
+
});
104
+
}
105
+
106
+
return original.call(this, entries);
107
+
};
108
+
});
+80
-17
packages/core-extensions/src/moonbase/index.tsx
+80
-17
packages/core-extensions/src/moonbase/index.tsx
···
1
-
import { ExtensionWebExports } from "@moonlight-mod/types";
1
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
{
5
+
find: "window.DiscordErrors=",
6
+
replace: [
7
+
// replace reporting line with update status
8
+
{
9
+
// CvQlAA mapped to ERRORS_ACTION_TO_TAKE
10
+
// FIXME: Better patch find?
11
+
match: /,(\(0,(\i)\.jsx\))\("p",{children:\i\.\i\.string\(\i\.\i\.CvQlAA\)}\)/,
12
+
replacement: (_, createElement, ReactJSX) =>
13
+
`,${createElement}(require("moonbase_crashScreen")?.UpdateText??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
14
+
},
15
+
16
+
// wrap actions field to display error details
17
+
{
18
+
match: /(?<=return(\(0,(\i)\.jsx\))\(.+?,)action:(\i),className:/,
19
+
replacement: (_, createElement, ReactJSX, action) =>
20
+
`action:require("moonbase_crashScreen")?.wrapAction?${createElement}(require("moonbase_crashScreen").wrapAction,{action:${action},state:this.state}):${action},className:`
21
+
},
22
+
23
+
// add update button
24
+
// +hivLS -> ERRORS_RELOAD
25
+
{
26
+
match: /(?<=\["\+hivLS"\]\)}\),(\(0,(\i)\.jsx\))\(\i,{}\))/,
27
+
replacement: (_, createElement, ReactJSX) =>
28
+
`,${createElement}(require("moonbase_crashScreen")?.UpdateButton??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
29
+
}
30
+
]
31
+
}
32
+
];
2
33
3
-
export const webpackModules: ExtensionWebExports["webpackModules"] = {
34
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
4
35
stores: {
5
-
dependencies: [
6
-
{ ext: "common", id: "flux" },
7
-
{ ext: "common", id: "fluxDispatcher" }
8
-
]
36
+
dependencies: [{ id: "discord/packages/flux" }, { id: "discord/Dispatcher" }]
9
37
},
10
38
11
39
ui: {
12
40
dependencies: [
13
41
{ ext: "spacepack", id: "spacepack" },
14
-
{ ext: "common", id: "react" },
15
-
{ ext: "common", id: "components" },
42
+
{ id: "react" },
43
+
{ id: "discord/components/common/index" },
16
44
{ ext: "moonbase", id: "stores" },
45
+
{ ext: "moonbase", id: "ThemeDarkIcon" },
46
+
{ id: "discord/modules/guild_settings/web/AppCard.css" },
47
+
{ ext: "contextMenu", id: "contextMenu" },
48
+
{ id: "discord/modules/modals/Modals" },
17
49
"Masks.PANEL_BUTTON",
18
-
"renderArtisanalHack(){",
19
50
'"Missing channel in Channel.openChannelContextMenu"',
20
51
".forumOrHome]:"
21
52
]
22
53
},
23
54
24
-
moonbase: {
55
+
ThemeDarkIcon: {
56
+
dependencies: [{ ext: "common", id: "icons" }, { id: "react" }]
57
+
},
58
+
59
+
settings: {
25
60
dependencies: [
26
61
{ ext: "spacepack", id: "spacepack" },
27
62
{ ext: "settings", id: "settings" },
28
-
{ ext: "common", id: "react" },
29
-
{ ext: "moonbase", id: "ui" }
63
+
{ id: "react" },
64
+
{ ext: "moonbase", id: "ui" },
65
+
{ ext: "contextMenu", id: "contextMenu" },
66
+
':"USER_SETTINGS_MODAL_SET_SECTION"'
30
67
],
31
68
entrypoint: true
69
+
},
70
+
71
+
updates: {
72
+
dependencies: [
73
+
{ id: "react" },
74
+
{ ext: "moonbase", id: "stores" },
75
+
{ ext: "moonbase", id: "ThemeDarkIcon" },
76
+
{ ext: "notices", id: "notices" },
77
+
{
78
+
ext: "spacepack",
79
+
id: "spacepack"
80
+
},
81
+
{ id: "discord/Constants" },
82
+
{ id: "discord/components/common/index" }
83
+
],
84
+
entrypoint: true
85
+
},
86
+
87
+
moonbase: {
88
+
dependencies: [{ ext: "moonbase", id: "stores" }]
89
+
},
90
+
91
+
crashScreen: {
92
+
dependencies: [
93
+
{ ext: "spacepack", id: "spacepack" },
94
+
{ id: "react" },
95
+
{ ext: "moonbase", id: "stores" },
96
+
{ id: "discord/packages/flux" },
97
+
{ id: "discord/components/common/index" },
98
+
/tabBar:"tabBar_[a-z0-9]+",tabBarItem:"tabBarItem_[a-z0-9]+"/
99
+
]
32
100
}
33
101
};
34
-
35
-
export const styles = [
36
-
".moonbase-settings > :first-child { margin-top: 0px; }",
37
-
"textarea.moonbase-resizeable { resize: vertical }"
38
-
];
+35
-5
packages/core-extensions/src/moonbase/manifest.json
+35
-5
packages/core-extensions/src/moonbase/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "moonbase",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Moonbase",
5
7
"tagline": "The official settings UI for moonlight",
6
-
"authors": ["Cynosphere", "NotNite"]
8
+
"authors": ["Cynosphere", "NotNite", "redstonekasi"]
7
9
},
8
-
"dependencies": ["spacepack", "settings", "common"],
10
+
"dependencies": ["spacepack", "settings", "common", "notices", "contextMenu"],
9
11
"settings": {
10
12
"sections": {
13
+
"advice": "reload",
11
14
"displayName": "Split into sections",
12
15
"description": "Show the Moonbase tabs as separate sections",
13
-
"type": "boolean"
16
+
"type": "boolean",
17
+
"default": false
18
+
},
19
+
"oldLocation": {
20
+
"advice": "reload",
21
+
"displayName": "Put Moonbase back at the bottom",
22
+
"type": "boolean",
23
+
"default": false
14
24
},
15
25
"saveFilter": {
26
+
"advice": "none",
16
27
"displayName": "Persist filter",
17
28
"description": "Save extension filter in config",
18
-
"type": "boolean"
29
+
"type": "boolean",
30
+
"default": false
31
+
},
32
+
"updateChecking": {
33
+
"advice": "none",
34
+
"displayName": "Automatic update checking",
35
+
"description": "Checks for updates to moonlight",
36
+
"type": "boolean",
37
+
"default": true
38
+
},
39
+
"updateBanner": {
40
+
"advice": "none",
41
+
"displayName": "Show update banner",
42
+
"description": "Shows a banner for moonlight and extension updates",
43
+
"type": "boolean",
44
+
"default": true
19
45
}
20
-
}
46
+
},
47
+
"cors": [
48
+
"https://github.com/moonlight-mod/moonlight/releases/download/",
49
+
"https://objects.githubusercontent.com/github-production-release-asset-"
50
+
]
21
51
}
+178
packages/core-extensions/src/moonbase/native.ts
+178
packages/core-extensions/src/moonbase/native.ts
···
1
+
import { MoonlightBranch } from "@moonlight-mod/types";
2
+
import type { MoonbaseNatives, RepositoryManifest } from "./types";
3
+
import extractAsar from "@moonlight-mod/core/asar";
4
+
import { distDir, repoUrlFile, installedVersionFile } from "@moonlight-mod/types/constants";
5
+
import { parseTarGzip } from "nanotar";
6
+
7
+
const moonlightGlobal = globalThis.moonlightHost ?? globalThis.moonlightNode;
8
+
9
+
const githubRepo = "moonlight-mod/moonlight";
10
+
const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`;
11
+
const artifactName = "dist.tar.gz";
12
+
13
+
const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref";
14
+
const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz";
15
+
16
+
export const userAgent = `moonlight/${moonlightGlobal.version} (https://github.com/moonlight-mod/moonlight)`;
17
+
18
+
// User-Agent header causes trouble on Firefox
19
+
const isBrowser = globalThis.moonlightNode != null && globalThis.moonlightNode.isBrowser;
20
+
const sharedHeaders: Record<string, string> = {};
21
+
if (!isBrowser) sharedHeaders["User-Agent"] = userAgent;
22
+
23
+
async function getStableRelease(): Promise<{
24
+
name: string;
25
+
assets: {
26
+
name: string;
27
+
browser_download_url: string;
28
+
}[];
29
+
}> {
30
+
const req = await fetch(githubApiUrl, {
31
+
cache: "no-store",
32
+
headers: sharedHeaders
33
+
});
34
+
return await req.json();
35
+
}
36
+
37
+
export default function getNatives(): MoonbaseNatives {
38
+
const logger = moonlightGlobal.getLogger("moonbase/natives");
39
+
40
+
return {
41
+
async checkForMoonlightUpdate() {
42
+
try {
43
+
if (moonlightGlobal.branch === MoonlightBranch.STABLE) {
44
+
const json = await getStableRelease();
45
+
return json.name !== moonlightGlobal.version ? json.name : null;
46
+
} else if (moonlightGlobal.branch === MoonlightBranch.NIGHTLY) {
47
+
const req = await fetch(nightlyRefUrl, {
48
+
cache: "no-store",
49
+
headers: sharedHeaders
50
+
});
51
+
const ref = (await req.text()).split("\n")[0];
52
+
return ref !== moonlightGlobal.version ? ref : null;
53
+
}
54
+
55
+
return null;
56
+
} catch (e) {
57
+
logger.error("Error checking for moonlight update", e);
58
+
return null;
59
+
}
60
+
},
61
+
62
+
async updateMoonlight(overrideBranch?: MoonlightBranch) {
63
+
const branch = overrideBranch ?? moonlightGlobal.branch;
64
+
65
+
// Note: this won't do anything on browser, we should probably disable it
66
+
// entirely when running in browser.
67
+
async function downloadStable(): Promise<[ArrayBuffer, string]> {
68
+
const json = await getStableRelease();
69
+
const asset = json.assets.find((a) => a.name === artifactName);
70
+
if (!asset) throw new Error("Artifact not found");
71
+
72
+
logger.debug(`Downloading ${asset.browser_download_url}`);
73
+
const req = await fetch(asset.browser_download_url, {
74
+
cache: "no-store",
75
+
headers: sharedHeaders
76
+
});
77
+
78
+
return [await req.arrayBuffer(), json.name];
79
+
}
80
+
81
+
async function downloadNightly(): Promise<[ArrayBuffer, string]> {
82
+
logger.debug(`Downloading ${nightlyZipUrl}`);
83
+
const zipReq = await fetch(nightlyZipUrl, {
84
+
cache: "no-store",
85
+
headers: sharedHeaders
86
+
});
87
+
88
+
const refReq = await fetch(nightlyRefUrl, {
89
+
cache: "no-store",
90
+
headers: sharedHeaders
91
+
});
92
+
const ref = (await refReq.text()).split("\n")[0];
93
+
94
+
return [await zipReq.arrayBuffer(), ref];
95
+
}
96
+
97
+
const [tar, ref] =
98
+
branch === MoonlightBranch.STABLE
99
+
? await downloadStable()
100
+
: branch === MoonlightBranch.NIGHTLY
101
+
? await downloadNightly()
102
+
: [null, null];
103
+
104
+
if (!tar || !ref) return;
105
+
106
+
const dist = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), distDir);
107
+
if (await moonlightNodeSandboxed.fs.exists(dist)) await moonlightNodeSandboxed.fs.rmdir(dist);
108
+
await moonlightNodeSandboxed.fs.mkdir(dist);
109
+
110
+
logger.debug("Extracting update");
111
+
const files = await parseTarGzip(tar);
112
+
for (const file of files) {
113
+
if (!file.data) continue;
114
+
// @ts-expect-error What do you mean their own types are wrong
115
+
if (file.type !== "file") continue;
116
+
117
+
const fullFile = moonlightNodeSandboxed.fs.join(dist, file.name);
118
+
const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile);
119
+
if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir);
120
+
await moonlightNodeSandboxed.fs.writeFile(fullFile, file.data);
121
+
}
122
+
123
+
logger.debug("Writing version file:", ref);
124
+
const versionFile = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), installedVersionFile);
125
+
await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim());
126
+
127
+
logger.debug("Update extracted");
128
+
},
129
+
130
+
async fetchRepositories(repos) {
131
+
const ret: Record<string, RepositoryManifest[]> = {};
132
+
133
+
for (const repo of repos) {
134
+
try {
135
+
const req = await fetch(repo, {
136
+
cache: "no-store",
137
+
headers: sharedHeaders
138
+
});
139
+
const json = await req.json();
140
+
ret[repo] = json;
141
+
} catch (e) {
142
+
logger.error(`Error fetching repository ${repo}`, e);
143
+
}
144
+
}
145
+
146
+
return ret;
147
+
},
148
+
149
+
async installExtension(manifest, url, repo) {
150
+
const req = await fetch(url, {
151
+
cache: "no-store",
152
+
headers: sharedHeaders
153
+
});
154
+
155
+
const dir = moonlightGlobal.getExtensionDir(manifest.id);
156
+
// remake it in case of updates
157
+
if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir);
158
+
await moonlightNodeSandboxed.fs.mkdir(dir);
159
+
160
+
const buffer = await req.arrayBuffer();
161
+
const files = extractAsar(buffer);
162
+
for (const [file, buf] of Object.entries(files)) {
163
+
const fullFile = moonlightNodeSandboxed.fs.join(dir, file);
164
+
const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile);
165
+
166
+
if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir);
167
+
await moonlightNodeSandboxed.fs.writeFile(moonlightNodeSandboxed.fs.join(dir, file), buf);
168
+
}
169
+
170
+
await moonlightNodeSandboxed.fs.writeFileString(moonlightNodeSandboxed.fs.join(dir, repoUrlFile), repo);
171
+
},
172
+
173
+
async deleteExtension(id) {
174
+
const dir = moonlightGlobal.getExtensionDir(id);
175
+
await moonlightNodeSandboxed.fs.rmdir(dir);
176
+
}
177
+
};
178
+
}
+2
-69
packages/core-extensions/src/moonbase/node.ts
+2
-69
packages/core-extensions/src/moonbase/node.ts
···
1
-
import { MoonbaseNatives, RepositoryManifest } from "./types";
2
-
import asar from "@electron/asar";
3
-
import fs from "fs";
4
-
import path from "path";
5
-
import os from "os";
6
-
import { repoUrlFile } from "types/src/constants";
7
-
8
-
const logger = moonlightNode.getLogger("moonbase");
9
-
10
-
async function fetchRepositories(repos: string[]) {
11
-
const ret: Record<string, RepositoryManifest[]> = {};
12
-
13
-
for (const repo of repos) {
14
-
try {
15
-
const req = await fetch(repo);
16
-
const json = await req.json();
17
-
ret[repo] = json;
18
-
} catch (e) {
19
-
logger.error(`Error fetching repository ${repo}`, e);
20
-
}
21
-
}
22
-
23
-
return ret;
24
-
}
25
-
26
-
async function installExtension(
27
-
manifest: RepositoryManifest,
28
-
url: string,
29
-
repo: string
30
-
) {
31
-
const req = await fetch(url);
32
-
33
-
const dir = moonlightNode.getExtensionDir(manifest.id);
34
-
// remake it in case of updates
35
-
if (fs.existsSync(dir)) fs.rmdirSync(dir, { recursive: true });
36
-
fs.mkdirSync(dir, { recursive: true });
37
-
38
-
// for some reason i just can't .writeFileSync() a file that ends in .asar???
39
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "moonlight-"));
40
-
const tempFile = path.join(tempDir, "extension");
41
-
const buffer = await req.arrayBuffer();
42
-
fs.writeFileSync(tempFile, Buffer.from(buffer));
43
-
44
-
asar.extractAll(tempFile, dir);
45
-
fs.writeFileSync(path.join(dir, repoUrlFile), repo);
46
-
}
47
-
48
-
async function deleteExtension(id: string) {
49
-
const dir = moonlightNode.getExtensionDir(id);
50
-
fs.rmdirSync(dir, { recursive: true });
51
-
}
52
-
53
-
function getExtensionConfig(id: string, key: string): any {
54
-
const config = moonlightNode.config.extensions[id];
55
-
if (typeof config === "object") {
56
-
return config.config?.[key];
57
-
}
58
-
59
-
return undefined;
60
-
}
61
-
62
-
const exports: MoonbaseNatives = {
63
-
fetchRepositories,
64
-
installExtension,
65
-
deleteExtension,
66
-
getExtensionConfig
67
-
};
68
-
69
-
module.exports = exports;
1
+
import getNatives from "./native";
2
+
module.exports = getNatives();
+269
packages/core-extensions/src/moonbase/style.css
+269
packages/core-extensions/src/moonbase/style.css
···
1
+
:root {
2
+
--moonbase-bg: #222034;
3
+
--moonbase-fg: #fffba6;
4
+
}
5
+
6
+
.moonbase-settings > :first-child {
7
+
margin-top: 0px;
8
+
}
9
+
10
+
.moonbase-retry-button {
11
+
padding: 8px;
12
+
margin-right: 8px;
13
+
}
14
+
15
+
textarea.moonbase-resizeable {
16
+
resize: vertical;
17
+
}
18
+
19
+
.moonbase-link-buttons {
20
+
border-bottom: 2px solid var(--background-modifier-accent);
21
+
margin-bottom: -2px;
22
+
margin-left: 0 !important;
23
+
padding-right: 20px;
24
+
gap: 1rem;
25
+
}
26
+
27
+
.moonbase-speen {
28
+
animation: moonbase-speen-animation 0.25s linear infinite;
29
+
}
30
+
31
+
@keyframes moonbase-speen-animation {
32
+
from {
33
+
transform: rotate(0deg);
34
+
}
35
+
to {
36
+
transform: rotate(360deg);
37
+
}
38
+
}
39
+
40
+
/* Update notice at the top of the client */
41
+
.moonbase-updates-notice {
42
+
background-color: var(--moonbase-bg);
43
+
color: var(--moonbase-fg);
44
+
--custom-notice-text: var(--moonbase-fg);
45
+
line-height: unset;
46
+
height: 36px;
47
+
}
48
+
49
+
.moonbase-updates-notice button {
50
+
color: var(--moonbase-fg);
51
+
border-color: var(--moonbase-fg);
52
+
}
53
+
54
+
.moonbase-updates-notice_text-wrapper {
55
+
display: inline-flex;
56
+
align-items: center;
57
+
line-height: 36px;
58
+
gap: 2px;
59
+
}
60
+
61
+
/* Help messages in Moonbase UI */
62
+
.moonbase-help-message {
63
+
display: flex;
64
+
flex-direction: row;
65
+
justify-content: space-between;
66
+
}
67
+
68
+
.moonbase-help-message-sticky {
69
+
position: sticky;
70
+
top: 24px;
71
+
z-index: 10;
72
+
background-color: var(--background-primary);
73
+
}
74
+
75
+
.moonbase-extension-update-section {
76
+
margin-top: 15px;
77
+
}
78
+
79
+
.moonbase-update-section {
80
+
background-color: var(--moonbase-bg);
81
+
--info-help-foreground: var(--moonbase-fg);
82
+
border: none !important;
83
+
color: var(--moonbase-fg);
84
+
}
85
+
86
+
.moonbase-update-section button {
87
+
--info-help-foreground: var(--moonbase-fg);
88
+
color: var(--moonbase-fg);
89
+
background-color: transparent;
90
+
border-color: var(--moonbase-fg);
91
+
}
92
+
93
+
.moonbase-help-message-buttons {
94
+
display: flex;
95
+
flex-direction: row;
96
+
gap: 8px;
97
+
align-items: center;
98
+
}
99
+
100
+
.moonbase-update-divider {
101
+
margin: 32px 0;
102
+
}
103
+
104
+
.moonlight-card-info-header {
105
+
margin-bottom: 0.25rem;
106
+
}
107
+
108
+
.moonlight-card-badge {
109
+
border-radius: 0.1875rem;
110
+
padding: 0 0.275rem;
111
+
margin-right: 0.4em;
112
+
background-color: var(--badge-color, var(--bg-mod-strong));
113
+
}
114
+
115
+
/* Crash screen */
116
+
.moonbase-crash-wrapper > [class^="buttons_"] {
117
+
gap: 1rem;
118
+
}
119
+
120
+
.moonbase-crash-wrapper {
121
+
display: flex;
122
+
flex-direction: column;
123
+
align-items: center;
124
+
gap: 1rem;
125
+
height: 50%;
126
+
width: 50vw;
127
+
max-height: 50%;
128
+
max-width: 50vw;
129
+
}
130
+
131
+
.moonbase-crash-tabs {
132
+
width: 100%;
133
+
}
134
+
135
+
.moonbase-crash-details-wrapper {
136
+
overflow-y: scroll;
137
+
color: var(--text-normal);
138
+
background: var(--background-secondary);
139
+
border: 1px solid var(--background-tertiary);
140
+
border-radius: 4px;
141
+
padding: 0.5em;
142
+
143
+
&::-webkit-scrollbar {
144
+
width: 8px;
145
+
height: 8px;
146
+
}
147
+
148
+
&::-webkit-scrollbar-thumb {
149
+
background-clip: padding-box;
150
+
border: 2px solid transparent;
151
+
border-radius: 4px;
152
+
background-color: var(--scrollbar-thin-thumb);
153
+
min-height: 40px;
154
+
}
155
+
156
+
&::-webkit-scrollbar-track {
157
+
border: 2px solid var(--scrollbar-thin-track);
158
+
background-color: var(--scrollbar-thin-track);
159
+
border-color: var(--scrollbar-thin-track);
160
+
}
161
+
}
162
+
163
+
.moonbase-crash-details {
164
+
box-sizing: border-box;
165
+
padding: 0;
166
+
font-family: var(--font-code);
167
+
font-size: 0.75rem;
168
+
line-height: 1rem;
169
+
margin: 6px;
170
+
white-space: pre-wrap;
171
+
background-clip: border-box;
172
+
173
+
& > code {
174
+
font-size: 0.875rem;
175
+
line-height: 1.125rem;
176
+
text-indent: 0;
177
+
white-space: pre-wrap;
178
+
text-size-adjust: none;
179
+
display: block;
180
+
user-select: text;
181
+
}
182
+
}
183
+
184
+
.moonbase-crash-extensions {
185
+
overflow-y: scroll;
186
+
display: grid;
187
+
grid-auto-columns: 25vw;
188
+
gap: 8px;
189
+
190
+
&::-webkit-scrollbar {
191
+
width: 8px;
192
+
height: 8px;
193
+
}
194
+
195
+
&::-webkit-scrollbar-thumb {
196
+
background-clip: padding-box;
197
+
border: 2px solid transparent;
198
+
border-radius: 4px;
199
+
background-color: var(--scrollbar-thin-thumb);
200
+
min-height: 40px;
201
+
}
202
+
203
+
&::-webkit-scrollbar-track {
204
+
border: 2px solid var(--scrollbar-thin-track);
205
+
background-color: var(--scrollbar-thin-track);
206
+
border-color: var(--scrollbar-thin-track);
207
+
}
208
+
}
209
+
210
+
.moonbase-crash-extensionCard {
211
+
color: var(--text-normal);
212
+
background: var(--background-secondary);
213
+
border: 1px solid var(--background-tertiary);
214
+
border-radius: 4px;
215
+
padding: 0.5em;
216
+
display: flex;
217
+
}
218
+
219
+
.moonbase-crash-extensionCard-meta {
220
+
display: flex;
221
+
flex-direction: column;
222
+
flex-grow: 1;
223
+
}
224
+
225
+
.moonbase-crash-extensionCard-title {
226
+
color: var(--text-normal);
227
+
font-family: var(--font-primary);
228
+
font-size: 16px;
229
+
line-height: 1.25;
230
+
font-weight: 600;
231
+
}
232
+
233
+
.moonbase-crash-extensionCard-version {
234
+
color: var(--text-muted);
235
+
font-family: var(--font-primary);
236
+
font-size: 14px;
237
+
line-height: 1.286;
238
+
font-weight: 400;
239
+
}
240
+
241
+
/* About page */
242
+
.moonbase-wordmark {
243
+
width: 100%;
244
+
}
245
+
246
+
.moonbase-devs {
247
+
width: 100%;
248
+
display: flex;
249
+
justify-content: center;
250
+
gap: 0rem 0.5rem;
251
+
padding-top: 0.5rem;
252
+
}
253
+
254
+
.moonbase-dev {
255
+
height: 4rem;
256
+
}
257
+
258
+
.moonbase-dev-avatar {
259
+
width: 2rem;
260
+
border-radius: 50%;
261
+
}
262
+
263
+
.moonbase-gap {
264
+
gap: 0.5rem;
265
+
}
266
+
267
+
.moonbase-about-page {
268
+
gap: 1rem;
269
+
}
+26
-10
packages/core-extensions/src/moonbase/types.ts
+26
-10
packages/core-extensions/src/moonbase/types.ts
···
1
-
import { DetectedExtension, ExtensionManifest } from "types/src";
1
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
2
+
import { DetectedExtension, ExtensionManifest, MoonlightBranch } from "@moonlight-mod/types";
2
3
3
4
export type MoonbaseNatives = {
4
-
fetchRepositories(
5
-
repos: string[]
6
-
): Promise<Record<string, RepositoryManifest[]>>;
7
-
installExtension(
8
-
manifest: RepositoryManifest,
9
-
url: string,
10
-
repo: string
11
-
): Promise<void>;
5
+
checkForMoonlightUpdate(): Promise<string | null>;
6
+
updateMoonlight(overrideBranch?: MoonlightBranch): Promise<void>;
7
+
8
+
fetchRepositories(repos: string[]): Promise<Record<string, RepositoryManifest[]>>;
9
+
installExtension(manifest: RepositoryManifest, url: string, repo: string): Promise<void>;
12
10
deleteExtension(id: string): Promise<void>;
13
-
getExtensionConfig(id: string, key: string): any;
14
11
};
15
12
16
13
export type RepositoryManifest = ExtensionManifest & {
···
29
26
manifest: ExtensionManifest | RepositoryManifest;
30
27
source: DetectedExtension["source"];
31
28
state: ExtensionState;
29
+
compat: ExtensionCompat;
30
+
hasUpdate: boolean;
31
+
changelog?: string;
32
+
settingsOverride?: ExtensionManifest["settings"];
32
33
};
34
+
35
+
export enum UpdateState {
36
+
Ready,
37
+
Working,
38
+
Installed,
39
+
Failed
40
+
}
41
+
42
+
// Ordered in terms of priority
43
+
export enum RestartAdvice {
44
+
NotNeeded, // No action is needed
45
+
ReloadSuggested, // A reload might be needed
46
+
ReloadNeeded, // A reload is needed
47
+
RestartNeeded // A restart is needed
48
+
}
+36
packages/core-extensions/src/moonbase/webpackModules/ThemeDarkIcon.tsx
+36
packages/core-extensions/src/moonbase/webpackModules/ThemeDarkIcon.tsx
···
1
+
// RIP to ThemeDarkIcon ????-2025
2
+
// <Cynthia> Failed to remap "ThemeDarkIcon" in "discord/components/common/index"
3
+
// <NotNite> bro are you fucking kidding me
4
+
// <NotNite> that's literally the icon we use for the update banner
5
+
6
+
import React from "@moonlight-mod/wp/react";
7
+
import icons from "@moonlight-mod/wp/common_icons";
8
+
import type { IconProps } from "@moonlight-mod/types/coreExtensions/common";
9
+
10
+
export default function ThemeDarkIcon(props?: IconProps) {
11
+
const parsed = icons.parseProps(props);
12
+
13
+
return (
14
+
<svg
15
+
aria-hidden="true"
16
+
role="img"
17
+
xmlns="http://www.w3.org/2000/svg"
18
+
width={parsed.width}
19
+
height={parsed.height}
20
+
fill="none"
21
+
viewBox="0 0 24 24"
22
+
>
23
+
<path
24
+
fill={parsed.fill}
25
+
className={parsed.className}
26
+
d="M20.52 18.96c.32-.4-.01-.96-.52-.96A11 11 0 0 1 9.77 2.94c.31-.78-.3-1.68-1.1-1.43a11 11 0 1 0 11.85 17.45Z"
27
+
/>
28
+
29
+
<path
30
+
fill={parsed.fill}
31
+
className={parsed.className}
32
+
d="m17.73 9.27-.76-2.02a.5.5 0 0 0-.94 0l-.76 2.02-2.02.76a.5.5 0 0 0 0 .94l2.02.76.76 2.02a.5.5 0 0 0 .94 0l.76-2.02 2.02-.76a.5.5 0 0 0 0-.94l-2.02-.76ZM19.73 2.62l.45 1.2 1.2.45c.21.08.21.38 0 .46l-1.2.45-.45 1.2a.25.25 0 0 1-.46 0l-.45-1.2-1.2-.45a.25.25 0 0 1 0-.46l1.2-.45.45-1.2a.25.25 0 0 1 .46 0Z"
33
+
/>
34
+
</svg>
35
+
);
36
+
}
+263
packages/core-extensions/src/moonbase/webpackModules/crashScreen.tsx
+263
packages/core-extensions/src/moonbase/webpackModules/crashScreen.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import { Button, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
3
+
import { useStateFromStores, useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
4
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
5
+
import { RepositoryManifest, UpdateState } from "../types";
6
+
import { ConfigExtension, DetectedExtension } from "@moonlight-mod/types";
7
+
import DiscoveryClasses from "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css";
8
+
9
+
const MODULE_REGEX = /Webpack-Module\/(\d+)\/(\d+)/g;
10
+
11
+
const logger = moonlight.getLogger("moonbase/crashScreen");
12
+
13
+
type ErrorState = {
14
+
error: Error;
15
+
info: {
16
+
componentStack: string;
17
+
};
18
+
__moonlight_update?: UpdateState;
19
+
};
20
+
21
+
type WrapperProps = {
22
+
action: React.ReactNode;
23
+
state: ErrorState;
24
+
};
25
+
26
+
type UpdateCardProps = {
27
+
id: number;
28
+
ext: {
29
+
version: string;
30
+
download: string;
31
+
updateManifest: RepositoryManifest;
32
+
};
33
+
};
34
+
35
+
const updateStrings: Record<UpdateState, string> = {
36
+
[UpdateState.Ready]: "A new version of moonlight is available.",
37
+
[UpdateState.Working]: "Updating moonlight...",
38
+
[UpdateState.Installed]: "Updated moonlight. Click Reload to apply changes.",
39
+
[UpdateState.Failed]: "Failed to update moonlight. Please use the installer."
40
+
};
41
+
const buttonStrings: Record<UpdateState, string> = {
42
+
[UpdateState.Ready]: "Update moonlight",
43
+
[UpdateState.Working]: "Updating moonlight...",
44
+
[UpdateState.Installed]: "",
45
+
[UpdateState.Failed]: "Update failed"
46
+
};
47
+
const extensionButtonStrings: Record<UpdateState, string> = {
48
+
[UpdateState.Ready]: "Update",
49
+
[UpdateState.Working]: "Updating...",
50
+
[UpdateState.Installed]: "Updated",
51
+
[UpdateState.Failed]: "Update failed"
52
+
};
53
+
54
+
function ExtensionUpdateCard({ id, ext }: UpdateCardProps) {
55
+
const [state, setState] = React.useState(UpdateState.Ready);
56
+
const installed = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.getExtension(id), [id]);
57
+
58
+
return (
59
+
<div className="moonbase-crash-extensionCard">
60
+
<div className="moonbase-crash-extensionCard-meta">
61
+
<div className="moonbase-crash-extensionCard-title">
62
+
{ext.updateManifest.meta?.name ?? ext.updateManifest.id}
63
+
</div>
64
+
<div className="moonbase-crash-extensionCard-version">{`v${installed?.manifest?.version ?? "???"} -> v${
65
+
ext.version
66
+
}`}</div>
67
+
</div>
68
+
<div className="moonbase-crash-extensionCard-button">
69
+
<Button
70
+
color={Button.Colors.GREEN}
71
+
disabled={state !== UpdateState.Ready}
72
+
onClick={() => {
73
+
setState(UpdateState.Working);
74
+
MoonbaseSettingsStore.installExtension(id)
75
+
.then(() => setState(UpdateState.Installed))
76
+
.catch(() => setState(UpdateState.Failed));
77
+
}}
78
+
>
79
+
{extensionButtonStrings[state]}
80
+
</Button>
81
+
</div>
82
+
</div>
83
+
);
84
+
}
85
+
86
+
function ExtensionDisableCard({ ext }: { ext: DetectedExtension }) {
87
+
async function disableWithDependents() {
88
+
const disable = new Set<string>();
89
+
disable.add(ext.id);
90
+
for (const [id, dependencies] of moonlightNode.processedExtensions.dependencyGraph) {
91
+
if (dependencies?.has(ext.id)) disable.add(id);
92
+
}
93
+
94
+
const config = structuredClone(moonlightNode.config);
95
+
for (const id in config.extensions) {
96
+
if (!disable.has(id)) continue;
97
+
if (typeof config.extensions[id] === "boolean") config.extensions[id] = false;
98
+
else (config.extensions[id] as ConfigExtension).enabled = false;
99
+
}
100
+
101
+
let msg = `Are you sure you want to disable "${ext.manifest.meta?.name ?? ext.id}"`;
102
+
if (disable.size > 1) {
103
+
msg += ` and its ${disable.size - 1} dependent${disable.size - 1 === 1 ? "" : "s"}`;
104
+
}
105
+
msg += "?";
106
+
107
+
if (confirm(msg)) {
108
+
await moonlightNode.writeConfig(config);
109
+
window.location.reload();
110
+
}
111
+
}
112
+
113
+
return (
114
+
<div className="moonbase-crash-extensionCard">
115
+
<div className="moonbase-crash-extensionCard-meta">
116
+
<div className="moonbase-crash-extensionCard-title">{ext.manifest.meta?.name ?? ext.id}</div>
117
+
<div className="moonbase-crash-extensionCard-version">{`v${ext.manifest.version ?? "???"}`}</div>
118
+
</div>
119
+
<div className="moonbase-crash-extensionCard-button">
120
+
<Button color={Button.Colors.RED} onClick={disableWithDependents}>
121
+
Disable
122
+
</Button>
123
+
</div>
124
+
</div>
125
+
);
126
+
}
127
+
128
+
export function wrapAction({ action, state }: WrapperProps) {
129
+
const [tab, setTab] = React.useState("crash");
130
+
131
+
const { updates, updateCount } = useStateFromStoresObject([MoonbaseSettingsStore], () => {
132
+
const { updates } = MoonbaseSettingsStore;
133
+
return {
134
+
updates: Object.entries(updates),
135
+
updateCount: Object.keys(updates).length
136
+
};
137
+
});
138
+
139
+
const causes = React.useMemo(() => {
140
+
const causes = new Set<string>();
141
+
if (state.error.stack) {
142
+
for (const [, , id] of state.error.stack.matchAll(MODULE_REGEX))
143
+
for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
144
+
}
145
+
for (const [, , id] of state.info.componentStack.matchAll(MODULE_REGEX))
146
+
for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
147
+
148
+
for (const [path, id] of Object.entries(moonlight.moonmap.modules)) {
149
+
const MAPPING_REGEX = new RegExp(
150
+
// @ts-expect-error Only Firefox has RegExp.escape
151
+
`(${RegExp.escape ? RegExp.escape(path) : path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`,
152
+
"g"
153
+
);
154
+
155
+
if (state.error.stack) {
156
+
for (const match of state.error.stack.matchAll(MAPPING_REGEX))
157
+
if (match) for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
158
+
}
159
+
for (const match of state.info.componentStack.matchAll(MAPPING_REGEX))
160
+
if (match) for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
161
+
}
162
+
163
+
return [...causes];
164
+
}, []);
165
+
166
+
return (
167
+
<div className="moonbase-crash-wrapper">
168
+
{action}
169
+
<TabBar
170
+
className={`${DiscoveryClasses.tabBar} moonbase-crash-tabs`}
171
+
type="top"
172
+
selectedItem={tab}
173
+
onItemSelect={(v) => setTab(v)}
174
+
>
175
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="crash">
176
+
Crash details
177
+
</TabBar.Item>
178
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="extensions" disabled={updateCount === 0}>
179
+
{`Extension updates (${updateCount})`}
180
+
</TabBar.Item>
181
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="causes" disabled={causes.length === 0}>
182
+
{`Possible causes (${causes.length})`}
183
+
</TabBar.Item>
184
+
</TabBar>
185
+
{tab === "crash" ? (
186
+
<div className="moonbase-crash-details-wrapper">
187
+
<pre className="moonbase-crash-details">
188
+
<code>
189
+
{state.error.stack}
190
+
{"\n\nComponent stack:"}
191
+
{state.info.componentStack}
192
+
</code>
193
+
</pre>
194
+
</div>
195
+
) : null}
196
+
{tab === "extensions" ? (
197
+
<div className="moonbase-crash-extensions">
198
+
{updates.map(([id, ext]) => (
199
+
<ExtensionUpdateCard id={Number(id)} ext={ext} />
200
+
))}
201
+
</div>
202
+
) : null}
203
+
{tab === "causes" ? (
204
+
<div className="moonbase-crash-extensions">
205
+
{causes
206
+
.map((ext) => moonlightNode.extensions.find((e) => e.id === ext)!)
207
+
.map((ext) => (
208
+
<ExtensionDisableCard ext={ext} />
209
+
))}
210
+
</div>
211
+
) : null}
212
+
</div>
213
+
);
214
+
}
215
+
216
+
export function UpdateText({ state, setState }: { state: ErrorState; setState: (state: ErrorState) => void }) {
217
+
if (!state.__moonlight_update) {
218
+
setState({
219
+
...state,
220
+
__moonlight_update: UpdateState.Ready
221
+
});
222
+
}
223
+
const newVersion = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.newVersion);
224
+
225
+
return newVersion == null ? null : (
226
+
<p>{state.__moonlight_update !== undefined ? updateStrings[state.__moonlight_update] : ""}</p>
227
+
);
228
+
}
229
+
230
+
export function UpdateButton({ state, setState }: { state: ErrorState; setState: (state: ErrorState) => void }) {
231
+
const newVersion = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.newVersion);
232
+
return newVersion == null ||
233
+
state.__moonlight_update === UpdateState.Installed ||
234
+
state.__moonlight_update === undefined ? null : (
235
+
<Button
236
+
size={Button.Sizes.LARGE}
237
+
disabled={state.__moonlight_update !== UpdateState.Ready}
238
+
onClick={() => {
239
+
setState({
240
+
...state,
241
+
__moonlight_update: UpdateState.Working
242
+
});
243
+
244
+
MoonbaseSettingsStore.updateMoonlight()
245
+
.then(() => {
246
+
setState({
247
+
...state,
248
+
__moonlight_update: UpdateState.Installed
249
+
});
250
+
})
251
+
.catch((e) => {
252
+
logger.error(e);
253
+
setState({
254
+
...state,
255
+
__moonlight_update: UpdateState.Failed
256
+
});
257
+
});
258
+
}}
259
+
>
260
+
{state.__moonlight_update !== undefined ? buttonStrings[state.__moonlight_update] : ""}
261
+
</Button>
262
+
);
263
+
}
+10
packages/core-extensions/src/moonbase/webpackModules/moonbase.ts
+10
packages/core-extensions/src/moonbase/webpackModules/moonbase.ts
···
1
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
2
+
import type { Moonbase } from "@moonlight-mod/types/coreExtensions/moonbase";
3
+
4
+
export const moonbase: Moonbase = {
5
+
registerConfigComponent(ext, option, component) {
6
+
MoonbaseSettingsStore.registerConfigComponent(ext, option, component);
7
+
}
8
+
};
9
+
10
+
export default moonbase;
-44
packages/core-extensions/src/moonbase/webpackModules/moonbase.tsx
-44
packages/core-extensions/src/moonbase/webpackModules/moonbase.tsx
···
1
-
import settings from "@moonlight-mod/wp/settings_settings";
2
-
import React from "@moonlight-mod/wp/common_react";
3
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
-
import { Moonbase, pages } from "@moonlight-mod/wp/moonbase_ui";
5
-
6
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
7
-
import { MenuItem } from "@moonlight-mod/wp/common_components";
8
-
9
-
const { open } = spacepack.findByExports("setSection", "clearSubsection")[0]
10
-
.exports.Z;
11
-
12
-
settings.addSection("moonbase", "Moonbase", Moonbase, null, -2, {
13
-
stores: [MoonbaseSettingsStore],
14
-
element: () => {
15
-
// Require it here because lazy loading SUX
16
-
const SettingsNotice = spacepack.findByCode(
17
-
"onSaveButtonColor",
18
-
"FocusRingScope"
19
-
)[0].exports.Z;
20
-
return (
21
-
<SettingsNotice
22
-
submitting={MoonbaseSettingsStore.submitting}
23
-
onReset={() => {
24
-
MoonbaseSettingsStore.reset();
25
-
}}
26
-
onSave={() => {
27
-
MoonbaseSettingsStore.writeConfig();
28
-
}}
29
-
/>
30
-
);
31
-
}
32
-
});
33
-
34
-
settings.addSectionMenuItems(
35
-
"moonbase",
36
-
...pages.map((page, i) => (
37
-
<MenuItem
38
-
key={page.id}
39
-
id={`moonbase-${page.id}`}
40
-
label={page.name}
41
-
action={() => open("moonbase", i)}
42
-
/>
43
-
))
44
-
);
+100
packages/core-extensions/src/moonbase/webpackModules/settings.tsx
+100
packages/core-extensions/src/moonbase/webpackModules/settings.tsx
···
1
+
import settings from "@moonlight-mod/wp/settings_settings";
2
+
import React from "@moonlight-mod/wp/react";
3
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
+
import { Moonbase, pages, RestartAdviceMessage, Update } from "@moonlight-mod/wp/moonbase_ui";
5
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
6
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
7
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
8
+
import { Text, Breadcrumbs } from "@moonlight-mod/wp/discord/components/common/index";
9
+
import { MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu";
10
+
11
+
const notice = {
12
+
stores: [MoonbaseSettingsStore],
13
+
element: () => {
14
+
// Require it here because lazy loading SUX
15
+
const SettingsNotice = spacepack.require("discord/components/common/SettingsNotice").default;
16
+
return (
17
+
<SettingsNotice
18
+
submitting={MoonbaseSettingsStore.submitting}
19
+
onReset={() => {
20
+
MoonbaseSettingsStore.reset();
21
+
}}
22
+
onSave={async () => {
23
+
await MoonbaseSettingsStore.writeConfig();
24
+
}}
25
+
/>
26
+
);
27
+
}
28
+
};
29
+
30
+
const oldLocation = MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "oldLocation", false);
31
+
const position = oldLocation ? -2 : -9999;
32
+
33
+
function addSection(id: string, name: string, element: React.FunctionComponent) {
34
+
settings.addSection(`moonbase-${id}`, name, element, null, position, notice);
35
+
}
36
+
37
+
// FIXME: move to component types
38
+
type Breadcrumb = {
39
+
id: string;
40
+
label: string;
41
+
};
42
+
43
+
function renderBreadcrumb(crumb: Breadcrumb, last: boolean) {
44
+
return (
45
+
<Text variant="heading-lg/semibold" tag="h2" color={last ? "header-primary" : "header-secondary"}>
46
+
{crumb.label}
47
+
</Text>
48
+
);
49
+
}
50
+
51
+
if (!oldLocation) {
52
+
settings.addDivider(position);
53
+
}
54
+
55
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
56
+
if (oldLocation) settings.addHeader("Moonbase", position);
57
+
58
+
const _pages = oldLocation ? pages : pages.reverse();
59
+
for (const page of _pages) {
60
+
addSection(page.id, page.name, () => {
61
+
const breadcrumbs = [
62
+
{ id: "moonbase", label: "Moonbase" },
63
+
{ id: page.id, label: page.name }
64
+
];
65
+
return (
66
+
<>
67
+
<Breadcrumbs
68
+
className={Margins.marginBottom20}
69
+
renderCustomBreadcrumb={renderBreadcrumb}
70
+
breadcrumbs={breadcrumbs}
71
+
activeId={page.id}
72
+
>
73
+
{page.name}
74
+
</Breadcrumbs>
75
+
76
+
<RestartAdviceMessage />
77
+
<Update />
78
+
79
+
<page.element />
80
+
</>
81
+
);
82
+
});
83
+
}
84
+
85
+
if (!oldLocation) settings.addHeader("Moonbase", position);
86
+
} else {
87
+
settings.addSection("moonbase", "Moonbase", Moonbase, null, position, notice);
88
+
89
+
settings.addSectionMenuItems(
90
+
"moonbase",
91
+
...pages.map((page, i) => (
92
+
<MenuItem
93
+
key={page.id}
94
+
id={`moonbase-${page.id}`}
95
+
label={page.name}
96
+
action={() => UserSettingsModalActionCreators.open("moonbase", i.toString())}
97
+
/>
98
+
))
99
+
);
100
+
}
+362
-91
packages/core-extensions/src/moonbase/webpackModules/stores.ts
+362
-91
packages/core-extensions/src/moonbase/webpackModules/stores.ts
···
1
-
import { Config, ExtensionLoadSource } from "@moonlight-mod/types";
2
-
import { ExtensionState, MoonbaseExtension, MoonbaseNatives } from "../types";
3
-
import Flux from "@moonlight-mod/wp/common_flux";
4
-
import Dispatcher from "@moonlight-mod/wp/common_fluxDispatcher";
1
+
import { Config, ExtensionEnvironment, ExtensionLoadSource, ExtensionSettingsAdvice } from "@moonlight-mod/types";
2
+
import {
3
+
ExtensionState,
4
+
MoonbaseExtension,
5
+
MoonbaseNatives,
6
+
RepositoryManifest,
7
+
RestartAdvice,
8
+
UpdateState
9
+
} from "../types";
10
+
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
11
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
12
+
import getNatives from "../native";
13
+
import { mainRepo } from "@moonlight-mod/types/constants";
14
+
import { checkExtensionCompat, ExtensionCompat } from "@moonlight-mod/core/extension/loader";
15
+
import { CustomComponent } from "@moonlight-mod/types/coreExtensions/moonbase";
16
+
import { NodeEventType } from "@moonlight-mod/types/core/event";
17
+
import { getConfigOption, setConfigOption } from "@moonlight-mod/core/util/config";
18
+
import diff from "microdiff";
5
19
6
-
const natives: MoonbaseNatives = moonlight.getNatives("moonbase");
7
20
const logger = moonlight.getLogger("moonbase");
8
21
9
-
class MoonbaseSettingsStore extends Flux.Store<any> {
10
-
private origConfig: Config;
22
+
let natives: MoonbaseNatives = moonlight.getNatives("moonbase");
23
+
if (moonlightNode.isBrowser) natives = getNatives();
24
+
25
+
class MoonbaseSettingsStore extends Store<any> {
26
+
private initialConfig: Config;
27
+
private savedConfig: Config;
11
28
private config: Config;
12
29
private extensionIndex: number;
30
+
private configComponents: Record<string, Record<string, CustomComponent>> = {};
13
31
14
32
modified: boolean;
15
33
submitting: boolean;
16
34
installing: boolean;
17
35
36
+
#updateState = UpdateState.Ready;
37
+
get updateState() {
38
+
return this.#updateState;
39
+
}
40
+
newVersion: string | null;
41
+
shouldShowNotice: boolean;
42
+
43
+
restartAdvice = RestartAdvice.NotNeeded;
44
+
18
45
extensions: { [id: number]: MoonbaseExtension };
19
-
updates: { [id: number]: { version: string; download: string } };
46
+
updates: {
47
+
[id: number]: {
48
+
version: string;
49
+
download: string;
50
+
updateManifest: RepositoryManifest;
51
+
};
52
+
};
20
53
21
54
constructor() {
22
55
super(Dispatcher);
23
56
24
-
this.origConfig = moonlightNode.config;
25
-
this.config = this.clone(this.origConfig);
57
+
this.initialConfig = moonlightNode.config;
58
+
this.savedConfig = moonlightNode.config;
59
+
this.config = this.clone(this.savedConfig);
26
60
this.extensionIndex = 0;
27
61
28
62
this.modified = false;
29
63
this.submitting = false;
30
64
this.installing = false;
65
+
66
+
this.newVersion = null;
67
+
this.shouldShowNotice = false;
31
68
32
69
this.extensions = {};
33
70
this.updates = {};
···
36
73
this.extensions[uniqueId] = {
37
74
...ext,
38
75
uniqueId,
39
-
state: moonlight.enabledExtensions.has(ext.id)
40
-
? ExtensionState.Enabled
41
-
: ExtensionState.Disabled
76
+
state: moonlight.enabledExtensions.has(ext.id) ? ExtensionState.Enabled : ExtensionState.Disabled,
77
+
compat: checkExtensionCompat(ext.manifest),
78
+
hasUpdate: false
42
79
};
43
80
}
44
81
45
-
natives.fetchRepositories(this.config.repositories).then((ret) => {
46
-
for (const [repo, exts] of Object.entries(ret)) {
47
-
try {
48
-
for (const ext of exts) {
49
-
const uniqueId = this.extensionIndex++;
50
-
const extensionData = {
51
-
id: ext.id,
52
-
uniqueId,
53
-
manifest: ext,
54
-
source: { type: ExtensionLoadSource.Normal, url: repo },
55
-
state: ExtensionState.NotDownloaded
56
-
};
82
+
// This is async but we're calling it without
83
+
this.checkUpdates();
84
+
85
+
// Update our state if another extension edited the config programatically
86
+
moonlightNode.events.addEventListener(NodeEventType.ConfigSaved, (config) => {
87
+
if (!this.submitting) {
88
+
this.config = this.clone(config);
89
+
// NOTE: This is also async but we're calling it without
90
+
this.processConfigChanged();
91
+
}
92
+
});
93
+
}
94
+
95
+
async checkUpdates() {
96
+
await Promise.all([this.checkExtensionUpdates(), this.checkMoonlightUpdates()]);
97
+
this.shouldShowNotice = this.newVersion != null || Object.keys(this.updates).length > 0;
98
+
this.emitChange();
99
+
}
100
+
101
+
private async checkExtensionUpdates() {
102
+
const repositories = await natives!.fetchRepositories(this.savedConfig.repositories);
103
+
104
+
// Reset update state
105
+
for (const id in this.extensions) {
106
+
const ext = this.extensions[id];
107
+
ext.hasUpdate = false;
108
+
ext.changelog = undefined;
109
+
}
110
+
this.updates = {};
111
+
112
+
for (const [repo, exts] of Object.entries(repositories)) {
113
+
for (const ext of exts) {
114
+
const uniqueId = this.extensionIndex++;
115
+
const extensionData = {
116
+
id: ext.id,
117
+
uniqueId,
118
+
manifest: ext,
119
+
source: { type: ExtensionLoadSource.Normal, url: repo },
120
+
state: ExtensionState.NotDownloaded,
121
+
compat: ExtensionCompat.Compatible,
122
+
hasUpdate: false
123
+
};
57
124
58
-
if (this.alreadyExists(extensionData)) {
59
-
if (this.hasUpdate(extensionData)) {
60
-
this.updates[uniqueId] = {
61
-
version: ext.version!,
62
-
download: ext.download
63
-
};
64
-
}
125
+
// Don't present incompatible updates
126
+
if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue;
65
127
66
-
continue;
67
-
}
128
+
const existing = this.getExisting(extensionData);
129
+
if (existing != null) {
130
+
// Make sure the download URL is properly updated
131
+
existing.manifest = {
132
+
...existing.manifest,
133
+
download: ext.download
134
+
};
68
135
69
-
this.extensions[uniqueId] = extensionData;
136
+
if (this.hasUpdate(extensionData)) {
137
+
this.updates[existing.uniqueId] = {
138
+
version: ext.version!,
139
+
download: ext.download,
140
+
updateManifest: ext
141
+
};
142
+
existing.hasUpdate = true;
143
+
existing.changelog = ext.meta?.changelog;
70
144
}
71
-
} catch (e) {
72
-
logger.error(`Error processing repository ${repo}`, e);
145
+
} else {
146
+
this.extensions[uniqueId] = extensionData;
73
147
}
74
148
}
149
+
}
150
+
}
75
151
76
-
this.emitChange();
77
-
});
152
+
private async checkMoonlightUpdates() {
153
+
this.newVersion = this.getExtensionConfigRaw("moonbase", "updateChecking", true)
154
+
? await natives!.checkForMoonlightUpdate()
155
+
: null;
78
156
}
79
157
80
-
private alreadyExists(ext: MoonbaseExtension) {
81
-
return Object.values(this.extensions).some(
82
-
(e) => e.id === ext.id && e.source.url === ext.source.url
83
-
);
158
+
private getExisting(ext: MoonbaseExtension) {
159
+
return Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url);
84
160
}
85
161
86
162
private hasUpdate(ext: MoonbaseExtension) {
87
-
const existing = Object.values(this.extensions).find(
88
-
(e) => e.id === ext.id && e.source.url === ext.source.url
89
-
);
163
+
const existing = Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url);
90
164
if (existing == null) return false;
91
165
92
-
return (
93
-
existing.manifest.version !== ext.manifest.version &&
94
-
existing.state !== ExtensionState.NotDownloaded
95
-
);
166
+
return existing.manifest.version !== ext.manifest.version && existing.state !== ExtensionState.NotDownloaded;
96
167
}
97
168
98
169
// Jank
99
170
private isModified() {
100
-
const orig = JSON.stringify(this.origConfig);
171
+
const orig = JSON.stringify(this.savedConfig);
101
172
const curr = JSON.stringify(this.config);
102
173
return orig !== curr;
103
174
}
···
106
177
return this.submitting || this.installing;
107
178
}
108
179
180
+
// Required for the settings store contract
109
181
showNotice() {
110
182
return this.modified;
111
183
}
···
115
187
}
116
188
117
189
getExtensionUniqueId(id: string) {
118
-
return Object.values(this.extensions).find((ext) => ext.id === id)
119
-
?.uniqueId;
190
+
return Object.values(this.extensions).find((ext) => ext.id === id)?.uniqueId;
120
191
}
121
192
122
193
getExtensionConflicting(uniqueId: number) {
123
194
const ext = this.getExtension(uniqueId);
124
195
if (ext.state !== ExtensionState.NotDownloaded) return false;
125
196
return Object.values(this.extensions).some(
126
-
(e) =>
127
-
e.id === ext.id &&
128
-
e.uniqueId !== uniqueId &&
129
-
e.state !== ExtensionState.NotDownloaded
197
+
(e) => e.id === ext.id && e.uniqueId !== uniqueId && e.state !== ExtensionState.NotDownloaded
130
198
);
131
199
}
132
200
···
149
217
150
218
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
151
219
const ext = this.getExtension(uniqueId);
152
-
const defaultValue = ext.manifest.settings?.[key]?.default;
153
-
const clonedDefaultValue = this.clone(defaultValue);
154
-
const cfg = this.config.extensions[ext.id];
220
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
221
+
return getConfigOption(ext.id, key, this.config, settings);
222
+
}
155
223
156
-
if (cfg == null || typeof cfg === "boolean") return clonedDefaultValue;
157
-
return cfg.config?.[key] ?? clonedDefaultValue;
224
+
getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined {
225
+
const cfg = this.config.extensions[id];
226
+
if (cfg == null || typeof cfg === "boolean") return defaultValue;
227
+
return cfg.config?.[key] ?? defaultValue;
158
228
}
159
229
160
230
getExtensionConfigName(uniqueId: number, key: string) {
161
231
const ext = this.getExtension(uniqueId);
162
-
return ext.manifest.settings?.[key]?.displayName ?? key;
232
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
233
+
return settings?.[key]?.displayName ?? key;
163
234
}
164
235
165
236
getExtensionConfigDescription(uniqueId: number, key: string) {
166
237
const ext = this.getExtension(uniqueId);
167
-
return ext.manifest.settings?.[key]?.description;
238
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
239
+
return settings?.[key]?.description;
168
240
}
169
241
170
-
setExtensionConfig(uniqueId: number, key: string, value: any) {
171
-
const ext = this.getExtension(uniqueId);
172
-
const oldConfig = this.config.extensions[ext.id];
173
-
const newConfig =
174
-
typeof oldConfig === "boolean"
175
-
? {
176
-
enabled: oldConfig,
177
-
config: { [key]: value }
178
-
}
179
-
: {
180
-
...oldConfig,
181
-
config: { ...(oldConfig?.config ?? {}), [key]: value }
182
-
};
183
-
184
-
this.config.extensions[ext.id] = newConfig;
242
+
setExtensionConfig(id: string, key: string, value: any) {
243
+
setConfigOption(this.config, id, key, value);
185
244
this.modified = this.isModified();
186
245
this.emitChange();
187
246
}
···
191
250
let val = this.config.extensions[ext.id];
192
251
193
252
if (val == null) {
194
-
this.config.extensions[ext.id] = { enabled };
253
+
this.config.extensions[ext.id] = enabled;
195
254
this.modified = this.isModified();
196
255
this.emitChange();
197
256
return;
···
208
267
this.emitChange();
209
268
}
210
269
270
+
dismissAllExtensionUpdates() {
271
+
for (const id in this.extensions) {
272
+
this.extensions[id].hasUpdate = false;
273
+
}
274
+
this.emitChange();
275
+
}
276
+
277
+
async updateAllExtensions() {
278
+
for (const id of Object.keys(this.updates)) {
279
+
try {
280
+
await this.installExtension(parseInt(id));
281
+
} catch (e) {
282
+
logger.error("Error bulk updating extension", id, e);
283
+
}
284
+
}
285
+
}
286
+
211
287
async installExtension(uniqueId: number) {
212
288
const ext = this.getExtension(uniqueId);
213
289
if (!("download" in ext.manifest)) {
···
216
292
217
293
this.installing = true;
218
294
try {
219
-
const url = this.updates[uniqueId]?.download ?? ext.manifest.download;
220
-
await natives.installExtension(ext.manifest, url, ext.source.url!);
295
+
const update = this.updates[uniqueId];
296
+
const url = update?.download ?? ext.manifest.download;
297
+
await natives!.installExtension(ext.manifest, url, ext.source.url!);
221
298
if (ext.state === ExtensionState.NotDownloaded) {
222
299
this.extensions[uniqueId].state = ExtensionState.Disabled;
223
300
}
224
301
302
+
if (update != null) {
303
+
const existing = this.extensions[uniqueId];
304
+
existing.settingsOverride = update.updateManifest.settings;
305
+
existing.compat = checkExtensionCompat(update.updateManifest);
306
+
existing.manifest = update.updateManifest;
307
+
existing.changelog = update.updateManifest.meta?.changelog;
308
+
}
309
+
225
310
delete this.updates[uniqueId];
226
311
} catch (e) {
227
312
logger.error("Error installing extension:", e);
228
313
}
229
314
230
315
this.installing = false;
316
+
this.restartAdvice = this.#computeRestartAdvice();
231
317
this.emitChange();
232
318
}
233
319
320
+
private getRank(ext: MoonbaseExtension) {
321
+
if (ext.source.type === ExtensionLoadSource.Developer) return 3;
322
+
if (ext.source.type === ExtensionLoadSource.Core) return 2;
323
+
if (ext.source.url === mainRepo) return 1;
324
+
return 0;
325
+
}
326
+
327
+
async getDependencies(uniqueId: number) {
328
+
const ext = this.getExtension(uniqueId);
329
+
330
+
const missingDeps = [];
331
+
for (const dep of ext.manifest.dependencies ?? []) {
332
+
const anyInstalled = Object.values(this.extensions).some(
333
+
(e) => e.id === dep && e.state !== ExtensionState.NotDownloaded
334
+
);
335
+
if (!anyInstalled) missingDeps.push(dep);
336
+
}
337
+
338
+
if (missingDeps.length === 0) return null;
339
+
340
+
const deps: Record<string, MoonbaseExtension[]> = {};
341
+
for (const dep of missingDeps) {
342
+
const candidates = Object.values(this.extensions).filter((e) => e.id === dep);
343
+
344
+
deps[dep] = candidates.sort((a, b) => {
345
+
const aRank = this.getRank(a);
346
+
const bRank = this.getRank(b);
347
+
if (aRank === bRank) {
348
+
const repoIndex = this.savedConfig.repositories.indexOf(a.source.url!);
349
+
const otherRepoIndex = this.savedConfig.repositories.indexOf(b.source.url!);
350
+
return repoIndex - otherRepoIndex;
351
+
} else {
352
+
return bRank - aRank;
353
+
}
354
+
});
355
+
}
356
+
357
+
return deps;
358
+
}
359
+
234
360
async deleteExtension(uniqueId: number) {
235
361
const ext = this.getExtension(uniqueId);
236
362
if (ext == null) return;
237
363
238
364
this.installing = true;
239
365
try {
240
-
await natives.deleteExtension(ext.id);
366
+
await natives!.deleteExtension(ext.id);
241
367
this.extensions[uniqueId].state = ExtensionState.NotDownloaded;
242
368
} catch (e) {
243
369
logger.error("Error deleting extension:", e);
244
370
}
245
371
246
372
this.installing = false;
373
+
this.restartAdvice = this.#computeRestartAdvice();
374
+
this.emitChange();
375
+
}
376
+
377
+
async updateMoonlight() {
378
+
this.#updateState = UpdateState.Working;
379
+
this.emitChange();
380
+
381
+
await natives
382
+
.updateMoonlight()
383
+
.then(() => (this.#updateState = UpdateState.Installed))
384
+
.catch((e) => {
385
+
logger.error(e);
386
+
this.#updateState = UpdateState.Failed;
387
+
});
388
+
247
389
this.emitChange();
248
390
}
249
391
···
257
399
this.emitChange();
258
400
}
259
401
260
-
writeConfig() {
261
-
this.submitting = true;
402
+
tryGetExtensionName(id: string) {
403
+
const uniqueId = this.getExtensionUniqueId(id);
404
+
return (uniqueId != null ? this.getExtensionName(uniqueId) : null) ?? id;
405
+
}
262
406
407
+
registerConfigComponent(ext: string, name: string, component: CustomComponent) {
408
+
if (!(ext in this.configComponents)) this.configComponents[ext] = {};
409
+
this.configComponents[ext][name] = component;
410
+
}
411
+
412
+
getExtensionConfigComponent(ext: string, name: string) {
413
+
return this.configComponents[ext]?.[name];
414
+
}
415
+
416
+
#computeRestartAdvice() {
417
+
// If moonlight update needs a restart, always hide advice.
418
+
if (this.#updateState === UpdateState.Installed) return RestartAdvice.NotNeeded;
419
+
420
+
const i = this.initialConfig; // Initial config, from startup
421
+
const n = this.config; // New config about to be saved
422
+
423
+
let returnedAdvice = RestartAdvice.NotNeeded;
424
+
const updateAdvice = (r: RestartAdvice) => (returnedAdvice < r ? (returnedAdvice = r) : returnedAdvice);
425
+
426
+
// Top-level keys, repositories is not needed here because Moonbase handles it.
427
+
if (i.patchAll !== n.patchAll) updateAdvice(RestartAdvice.ReloadNeeded);
428
+
if (i.loggerLevel !== n.loggerLevel) updateAdvice(RestartAdvice.ReloadNeeded);
429
+
if (diff(i.devSearchPaths ?? [], n.devSearchPaths ?? [], { cyclesFix: false }).length !== 0)
430
+
return updateAdvice(RestartAdvice.RestartNeeded);
431
+
432
+
// Extension specific logic
433
+
for (const id in n.extensions) {
434
+
// Installed extension (might not be detected yet)
435
+
const ext = Object.values(this.extensions).find((e) => e.id === id && e.state !== ExtensionState.NotDownloaded);
436
+
// Installed and detected extension
437
+
const detected = moonlightNode.extensions.find((e) => e.id === id);
438
+
439
+
// If it's not installed at all, we don't care
440
+
if (!ext) continue;
441
+
442
+
const initState = i.extensions[id];
443
+
const newState = n.extensions[id];
444
+
445
+
const newEnabled = typeof newState === "boolean" ? newState : newState.enabled;
446
+
// If it's enabled but not detected yet, restart.
447
+
if (newEnabled && !detected) {
448
+
return updateAdvice(RestartAdvice.RestartNeeded);
449
+
}
450
+
451
+
// Toggling extensions specifically wants to rely on the initial state,
452
+
// that's what was considered when loading extensions.
453
+
const initEnabled = initState && (typeof initState === "boolean" ? initState : initState.enabled);
454
+
if (initEnabled !== newEnabled || detected?.manifest.version !== ext.manifest.version) {
455
+
// If we have the extension locally, we confidently know if it has host/preload scripts.
456
+
// If not, we have to respect the environment specified in the manifest.
457
+
// If that is the default, we can't know what's needed.
458
+
459
+
if (detected?.scripts.hostPath || detected?.scripts.nodePath) {
460
+
return updateAdvice(RestartAdvice.RestartNeeded);
461
+
}
462
+
463
+
switch (ext.manifest.environment) {
464
+
case ExtensionEnvironment.Both:
465
+
case ExtensionEnvironment.Web:
466
+
updateAdvice(RestartAdvice.ReloadNeeded);
467
+
continue;
468
+
case ExtensionEnvironment.Desktop:
469
+
return updateAdvice(RestartAdvice.RestartNeeded);
470
+
default:
471
+
updateAdvice(RestartAdvice.ReloadNeeded);
472
+
continue;
473
+
}
474
+
}
475
+
476
+
const initConfig = typeof initState === "boolean" ? {} : { ...initState?.config };
477
+
const newConfig = typeof newState === "boolean" ? {} : { ...newState?.config };
478
+
479
+
const def = ext.manifest.settings;
480
+
if (!def) continue;
481
+
482
+
for (const key in def) {
483
+
const defaultValue = def[key].default;
484
+
485
+
initConfig[key] ??= defaultValue;
486
+
newConfig[key] ??= defaultValue;
487
+
}
488
+
489
+
const changedKeys = diff(initConfig, newConfig, { cyclesFix: false }).map((c) => c.path[0]);
490
+
for (const key in def) {
491
+
if (!changedKeys.includes(key)) continue;
492
+
493
+
const advice = def[key].advice;
494
+
switch (advice) {
495
+
case ExtensionSettingsAdvice.None:
496
+
updateAdvice(RestartAdvice.NotNeeded);
497
+
continue;
498
+
case ExtensionSettingsAdvice.Reload:
499
+
updateAdvice(RestartAdvice.ReloadNeeded);
500
+
continue;
501
+
case ExtensionSettingsAdvice.Restart:
502
+
updateAdvice(RestartAdvice.RestartNeeded);
503
+
continue;
504
+
default:
505
+
updateAdvice(RestartAdvice.ReloadSuggested);
506
+
}
507
+
}
508
+
}
509
+
510
+
return returnedAdvice;
511
+
}
512
+
513
+
async writeConfig() {
263
514
try {
264
-
moonlightNode.writeConfig(this.config);
265
-
this.origConfig = this.clone(this.config);
266
-
} catch (e) {
267
-
logger.error("Error writing config", e);
515
+
this.submitting = true;
516
+
this.emitChange();
517
+
518
+
await moonlightNode.writeConfig(this.config);
519
+
await this.processConfigChanged();
520
+
} finally {
521
+
this.submitting = false;
522
+
this.emitChange();
268
523
}
524
+
}
269
525
270
-
this.submitting = false;
526
+
private async processConfigChanged() {
527
+
this.savedConfig = this.clone(this.config);
528
+
this.restartAdvice = this.#computeRestartAdvice();
271
529
this.modified = false;
530
+
531
+
const modifiedRepos = diff(this.savedConfig.repositories, this.config.repositories);
532
+
if (modifiedRepos.length !== 0) await this.checkUpdates();
533
+
272
534
this.emitChange();
273
535
}
274
536
275
537
reset() {
276
538
this.submitting = false;
277
539
this.modified = false;
278
-
this.config = this.clone(this.origConfig);
540
+
this.config = this.clone(this.savedConfig);
279
541
this.emitChange();
542
+
}
543
+
544
+
restartDiscord() {
545
+
if (moonlightNode.isBrowser) {
546
+
window.location.reload();
547
+
} else {
548
+
// @ts-expect-error TODO: DiscordNative
549
+
window.DiscordNative.app.relaunch();
550
+
}
280
551
}
281
552
282
553
// Required because electron likes to make it immutable sometimes.
+47
packages/core-extensions/src/moonbase/webpackModules/ui/HelpMessage.tsx
+47
packages/core-extensions/src/moonbase/webpackModules/ui/HelpMessage.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
3
+
import { Text } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import HelpMessageClasses from "@moonlight-mod/wp/discord/components/common/HelpMessage.css";
5
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
6
+
7
+
// reimpl of HelpMessage but with a custom icon
8
+
export default function HelpMessage({
9
+
className,
10
+
text,
11
+
icon,
12
+
children,
13
+
type = "info"
14
+
}: {
15
+
className?: string;
16
+
text: string;
17
+
icon: React.ComponentType<any>;
18
+
type?: "warning" | "positive" | "error" | "info";
19
+
children?: React.ReactNode;
20
+
}) {
21
+
return (
22
+
<div
23
+
className={`${Margins.marginBottom20} ${HelpMessageClasses[type]} ${HelpMessageClasses.container} moonbase-help-message ${className}`}
24
+
>
25
+
<Flex direction={Flex.Direction.HORIZONTAL}>
26
+
<div
27
+
className={HelpMessageClasses.iconDiv}
28
+
style={{
29
+
alignItems: "center"
30
+
}}
31
+
>
32
+
{React.createElement(icon, {
33
+
size: "sm",
34
+
color: "currentColor",
35
+
className: HelpMessageClasses.icon
36
+
})}
37
+
</div>
38
+
39
+
<Text variant="text-sm/medium" color="currentColor" className={HelpMessageClasses.text}>
40
+
{text}
41
+
</Text>
42
+
43
+
{children}
44
+
</Flex>
45
+
</div>
46
+
);
47
+
}
+43
packages/core-extensions/src/moonbase/webpackModules/ui/RestartAdvice.tsx
+43
packages/core-extensions/src/moonbase/webpackModules/ui/RestartAdvice.tsx
···
1
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
2
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
3
+
import { Button, CircleWarningIcon } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import React from "@moonlight-mod/wp/react";
5
+
import { RestartAdvice } from "../../types";
6
+
import HelpMessage from "./HelpMessage";
7
+
8
+
const strings: Record<RestartAdvice, string> = {
9
+
[RestartAdvice.NotNeeded]: "how did you even",
10
+
[RestartAdvice.ReloadSuggested]: "A reload might be needed to apply some of the changed options.",
11
+
[RestartAdvice.ReloadNeeded]: "A reload is needed to apply some of the changed options.",
12
+
[RestartAdvice.RestartNeeded]: "A restart is needed to apply some of the changed options."
13
+
};
14
+
15
+
const buttonStrings: Record<RestartAdvice, string> = {
16
+
[RestartAdvice.NotNeeded]: "huh?",
17
+
[RestartAdvice.ReloadSuggested]: "Reload",
18
+
[RestartAdvice.ReloadNeeded]: "Reload",
19
+
[RestartAdvice.RestartNeeded]: "Restart"
20
+
};
21
+
22
+
const actions: Record<RestartAdvice, () => void> = {
23
+
[RestartAdvice.NotNeeded]: () => {},
24
+
[RestartAdvice.ReloadSuggested]: () => window.location.reload(),
25
+
[RestartAdvice.ReloadNeeded]: () => window.location.reload(),
26
+
[RestartAdvice.RestartNeeded]: () => MoonbaseSettingsStore.restartDiscord()
27
+
};
28
+
29
+
export default function RestartAdviceMessage() {
30
+
const restartAdvice = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.restartAdvice);
31
+
32
+
if (restartAdvice === RestartAdvice.NotNeeded) return null;
33
+
34
+
return (
35
+
<div className="moonbase-help-message-sticky">
36
+
<HelpMessage text={strings[restartAdvice]} icon={CircleWarningIcon} type="warning">
37
+
<Button color={Button.Colors.YELLOW} size={Button.Sizes.TINY} onClick={actions[restartAdvice]}>
38
+
{buttonStrings[restartAdvice]}
39
+
</Button>
40
+
</HelpMessage>
41
+
</div>
42
+
);
43
+
}
+110
packages/core-extensions/src/moonbase/webpackModules/ui/about.tsx
+110
packages/core-extensions/src/moonbase/webpackModules/ui/about.tsx
···
1
+
import {
2
+
Text,
3
+
useThemeContext,
4
+
Button,
5
+
AngleBracketsIcon,
6
+
BookCheckIcon,
7
+
ClydeIcon
8
+
} from "@moonlight-mod/wp/discord/components/common/index";
9
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
10
+
import React from "@moonlight-mod/wp/react";
11
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
12
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
13
+
14
+
const wordmark = "https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark.png";
15
+
const wordmarkLight =
16
+
"https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark-light.png";
17
+
18
+
function parse(str: string) {
19
+
return MarkupUtils.parse(str, true, {
20
+
allowHeading: true,
21
+
allowLinks: true,
22
+
allowList: true
23
+
});
24
+
}
25
+
26
+
function Dev({ name, picture, link }: { name: string; picture: string; link: string }) {
27
+
return (
28
+
<Button onClick={() => window.open(link)} color={Button.Colors.PRIMARY} className="moonbase-dev">
29
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
30
+
<img src={picture} alt={name} className="moonbase-dev-avatar" />
31
+
32
+
<Text variant="text-md/semibold">{name}</Text>
33
+
</Flex>
34
+
</Button>
35
+
);
36
+
}
37
+
38
+
function IconButton({
39
+
text,
40
+
link,
41
+
icon,
42
+
openInClient
43
+
}: {
44
+
text: string;
45
+
link: string;
46
+
icon: React.FC<any>;
47
+
openInClient?: boolean;
48
+
}) {
49
+
return (
50
+
<Button
51
+
onClick={() => {
52
+
if (openInClient) {
53
+
try {
54
+
const { handleClick } = spacepack.require("discord/utils/MaskedLinkUtils");
55
+
handleClick({ href: link });
56
+
} catch {
57
+
window.open(link);
58
+
}
59
+
} else {
60
+
// Will open externally in the user's browser
61
+
window.open(link);
62
+
}
63
+
}}
64
+
>
65
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
66
+
{React.createElement(icon, {
67
+
size: "sm",
68
+
color: "currentColor"
69
+
})}
70
+
{text}
71
+
</Flex>
72
+
</Button>
73
+
);
74
+
}
75
+
76
+
export default function AboutPage() {
77
+
const darkTheme = useThemeContext()?.theme !== "light";
78
+
79
+
return (
80
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.CENTER} className="moonbase-about-page">
81
+
<img src={darkTheme ? wordmarkLight : wordmark} alt="moonlight wordmark" className="moonbase-wordmark" />
82
+
83
+
<Text variant="heading-lg/medium">created by:</Text>
84
+
<div className="moonbase-devs">
85
+
<Dev name="Cynosphere" picture="https://github.com/Cynosphere.png" link="https://github.com/Cynosphere" />
86
+
<Dev name="NotNite" picture="https://github.com/NotNite.png" link="https://github.com/NotNite" />
87
+
<Dev name="adryd" picture="https://github.com/adryd325.png" link="https://github.com/adryd325" />
88
+
<Dev name="redstonekasi" picture="https://github.com/redstonekasi.png" link="https://github.com/redstonekasi" />
89
+
</div>
90
+
91
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
92
+
<IconButton text="View source" icon={AngleBracketsIcon} link="https://github.com/moonlight-mod/moonlight" />
93
+
<IconButton text="Open the docs" icon={BookCheckIcon} link="https://moonlight-mod.github.io/" />
94
+
<IconButton text="Join the server" icon={ClydeIcon} link="https://discord.gg/FdZBTFCP6F" openInClient={true} />
95
+
</Flex>
96
+
97
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.START}>
98
+
<Text variant="text-sm/normal">
99
+
{parse(`moonlight \`${window.moonlight.version}\` on \`${window.moonlight.branch}\``)}
100
+
</Text>
101
+
102
+
<Text variant="text-sm/normal">
103
+
{parse(
104
+
"moonlight is licensed under the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.html) (`LGPL-3.0-or-later`)."
105
+
)}
106
+
</Text>
107
+
</Flex>
108
+
</Flex>
109
+
);
110
+
}
+29
-34
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
+29
-34
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
···
1
1
import { LogLevel } from "@moonlight-mod/types";
2
2
3
-
const logLevels = Object.values(LogLevel).filter(
4
-
(v) => typeof v === "string"
5
-
) as string[];
3
+
const logLevels = Object.values(LogLevel).filter((v) => typeof v === "string") as string[];
6
4
7
-
import React from "@moonlight-mod/wp/common_react";
5
+
import React from "@moonlight-mod/wp/react";
8
6
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
9
7
import {
10
8
FormDivider,
···
12
10
FormText,
13
11
FormSwitch,
14
12
TextInput,
15
-
Flex,
16
13
Button,
17
14
SingleSelect,
18
15
Tooltip,
19
16
Clickable
20
-
} from "@moonlight-mod/wp/common_components";
21
-
import CommonComponents from "@moonlight-mod/wp/common_components";
17
+
} from "@moonlight-mod/wp/discord/components/common/index";
18
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
19
+
import { CircleXIcon } from "@moonlight-mod/wp/discord/components/common/index";
20
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
21
+
import FormSwitchClasses from "@moonlight-mod/wp/discord/components/common/FormSwitch.css";
22
22
23
23
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
24
24
25
-
const FormClasses = spacepack.findByCode("dividerDefault:")[0].exports;
26
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
27
-
28
-
let RemoveButtonClasses: any;
25
+
let GuildSettingsRoleEditClasses: any;
29
26
spacepack
30
27
.lazyLoad(
31
28
"renderArtisanalHack",
···
34
31
)
35
32
.then(
36
33
() =>
37
-
(RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0]
38
-
.exports)
34
+
(GuildSettingsRoleEditClasses = spacepack.require(
35
+
"discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"
36
+
))
39
37
);
40
38
41
-
const { CircleXIcon } = CommonComponents;
42
39
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
43
40
return (
44
-
<div className={RemoveButtonClasses.removeButtonContainer}>
41
+
<div className={GuildSettingsRoleEditClasses.removeButtonContainer}>
45
42
<Tooltip text="Remove entry" position="top">
46
43
{(props: any) => (
47
-
<Clickable
48
-
{...props}
49
-
className={RemoveButtonClasses.removeButton}
50
-
onClick={onClick}
51
-
>
44
+
<Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}>
52
45
<CircleXIcon width={24} height={24} />
53
46
</Clickable>
54
47
)}
···
57
50
);
58
51
}
59
52
60
-
function ArrayFormItem({
61
-
config
62
-
}: {
63
-
config: "repositories" | "devSearchPaths";
64
-
}) {
53
+
function ArrayFormItem({ config }: { config: "repositories" | "devSearchPaths" }) {
65
54
const items = MoonbaseSettingsStore.getConfigOption(config) ?? [];
66
55
return (
67
56
<Flex
···
119
108
export default function ConfigPage() {
120
109
return (
121
110
<>
111
+
<FormSwitch
112
+
className={Margins.marginTop20}
113
+
value={MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "updateChecking", true) ?? true}
114
+
onChange={(value: boolean) => {
115
+
MoonbaseSettingsStore.setExtensionConfig("moonbase", "updateChecking", value);
116
+
}}
117
+
note="Checks for updates to moonlight"
118
+
>
119
+
Automatic update checking
120
+
</FormSwitch>
122
121
<FormItem title="Repositories">
123
-
<FormText className={Margins.marginBottom4}>
124
-
A list of remote repositories to display extensions from
125
-
</FormText>
122
+
<FormText className={Margins.marginBottom4}>A list of remote repositories to display extensions from</FormText>
126
123
<ArrayFormItem config="repositories" />
127
124
</FormItem>
128
-
<FormDivider className={FormClasses.dividerDefault} />
125
+
<FormDivider className={FormSwitchClasses.dividerDefault} />
129
126
<FormItem title="Extension search paths" className={Margins.marginTop20}>
130
127
<FormText className={Margins.marginBottom4}>
131
128
A list of local directories to search for built extensions
132
129
</FormText>
133
130
<ArrayFormItem config="devSearchPaths" />
134
131
</FormItem>
135
-
<FormDivider className={FormClasses.dividerDefault} />
132
+
<FormDivider className={FormSwitchClasses.dividerDefault} />
136
133
<FormSwitch
137
134
className={Margins.marginTop20}
138
-
value={MoonbaseSettingsStore.getConfigOption("patchAll")}
135
+
value={MoonbaseSettingsStore.getConfigOption("patchAll") ?? false}
139
136
onChange={(value: boolean) => {
140
137
MoonbaseSettingsStore.setConfigOption("patchAll", value);
141
138
}}
···
152
149
value: o.toLowerCase(),
153
150
label: o[0] + o.slice(1).toLowerCase()
154
151
}))}
155
-
onChange={(v) =>
156
-
MoonbaseSettingsStore.setConfigOption("loggerLevel", v)
157
-
}
152
+
onChange={(v) => MoonbaseSettingsStore.setConfigOption("loggerLevel", v)}
158
153
/>
159
154
</FormItem>
160
155
</>
+285
-156
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
+285
-156
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
···
1
1
import { ExtensionState } from "../../../types";
2
-
import { ExtensionLoadSource } from "@moonlight-mod/types";
3
-
4
-
import React from "@moonlight-mod/wp/common_react";
5
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
6
-
import CommonComponents from "@moonlight-mod/wp/common_components";
7
-
import * as Flux from "@moonlight-mod/wp/common_flux";
2
+
import { constants, ExtensionLoadSource, ExtensionTag } from "@moonlight-mod/types";
8
3
4
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
5
+
import {
6
+
ScienceIcon,
7
+
DownloadIcon,
8
+
TrashIcon,
9
+
AngleBracketsIcon,
10
+
Tooltip,
11
+
Card,
12
+
Text,
13
+
FormSwitch,
14
+
TabBar,
15
+
Button,
16
+
ChannelListIcon,
17
+
HeartIcon,
18
+
WindowTopOutlineIcon,
19
+
WarningIcon
20
+
} from "@moonlight-mod/wp/discord/components/common/index";
21
+
import React from "@moonlight-mod/wp/react";
22
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
23
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
24
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
25
+
import AppCardClasses from "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCard.css";
26
+
import PanelButton from "@moonlight-mod/wp/discord/components/common/PanelButton";
27
+
import DiscoveryClasses from "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css";
28
+
import MarkupClasses from "@moonlight-mod/wp/discord/modules/messages/web/Markup.css";
29
+
import BuildOverrideClasses from "@moonlight-mod/wp/discord/modules/build_overrides/web/BuildOverride.css";
30
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
31
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
9
32
import ExtensionInfo from "./info";
10
33
import Settings from "./settings";
34
+
import { doGenericExtensionPopup, doMissingExtensionPopup } from "./popup";
11
35
12
36
export enum ExtensionPage {
13
37
Info,
14
38
Description,
39
+
Changelog,
15
40
Settings
16
41
}
17
42
18
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
43
+
const COMPAT_TEXT_MAP: Record<ExtensionCompat, string> = {
44
+
[ExtensionCompat.Compatible]: "huh?",
45
+
[ExtensionCompat.InvalidApiLevel]: "Incompatible API level",
46
+
[ExtensionCompat.InvalidEnvironment]: "Incompatible platform"
47
+
};
48
+
const CONFLICTING_TEXT = "This extension is already installed from another source.";
49
+
50
+
function PanelLinkButton({ icon, tooltip, link }: { icon: React.ReactNode; tooltip: string; link: string }) {
51
+
return (
52
+
<PanelButton
53
+
icon={icon}
54
+
tooltipText={tooltip}
55
+
onClick={() => {
56
+
window.open(link);
57
+
}}
58
+
/>
59
+
);
60
+
}
61
+
62
+
export default function ExtensionCard({ uniqueId, selectTag }: { uniqueId: number; selectTag: (tag: string) => void }) {
63
+
const { ext, enabled, busy, update, conflicting } = useStateFromStores([MoonbaseSettingsStore], () => {
64
+
return {
65
+
ext: MoonbaseSettingsStore.getExtension(uniqueId),
66
+
enabled: MoonbaseSettingsStore.getExtensionEnabled(uniqueId),
67
+
busy: MoonbaseSettingsStore.busy,
68
+
update: MoonbaseSettingsStore.getExtensionUpdate(uniqueId),
69
+
conflicting: MoonbaseSettingsStore.getExtensionConflicting(uniqueId)
70
+
};
71
+
});
19
72
20
-
const { DownloadIcon, TrashIcon, CircleWarningIcon } = CommonComponents;
73
+
const [tab, setTab] = React.useState(
74
+
update != null && ext?.changelog != null ? ExtensionPage.Changelog : ExtensionPage.Info
75
+
);
21
76
22
-
const PanelButton = spacepack.findByCode("Masks.PANEL_BUTTON")[0].exports.Z;
23
-
const TabBarClasses = spacepack.findByExports(
24
-
"tabBar",
25
-
"tabBarItem",
26
-
"headerContentWrapper"
27
-
)[0].exports;
77
+
const tagline = ext.manifest?.meta?.tagline;
78
+
const settings = ext.settingsOverride ?? ext.manifest?.settings;
79
+
const description = ext.manifest?.meta?.description;
80
+
const changelog = ext.changelog;
81
+
const linkButtons = [
82
+
ext?.manifest?.meta?.source && (
83
+
<PanelLinkButton icon={<AngleBracketsIcon />} tooltip="View source" link={ext.manifest.meta.source} />
84
+
),
85
+
ext?.source?.url && <PanelLinkButton icon={<ChannelListIcon />} tooltip="View repository" link={ext.source.url} />,
86
+
ext?.manifest?.meta?.donate && (
87
+
<PanelLinkButton icon={<HeartIcon />} tooltip="Donate" link={ext.manifest.meta.donate} />
88
+
)
89
+
].filter((x) => x != null);
28
90
29
-
export default function ExtensionCard({ uniqueId }: { uniqueId: number }) {
30
-
const [tab, setTab] = React.useState(ExtensionPage.Info);
31
-
const [restartNeeded, setRestartNeeded] = React.useState(false);
91
+
const enabledDependants = useStateFromStores([MoonbaseSettingsStore], () =>
92
+
Object.keys(MoonbaseSettingsStore.extensions)
93
+
.filter((uniqueId) => {
94
+
const potentialDependant = MoonbaseSettingsStore.getExtension(parseInt(uniqueId));
32
95
33
-
const { ext, enabled, busy, update, conflicting } = Flux.useStateFromStores(
34
-
[MoonbaseSettingsStore],
35
-
() => {
36
-
return {
37
-
ext: MoonbaseSettingsStore.getExtension(uniqueId),
38
-
enabled: MoonbaseSettingsStore.getExtensionEnabled(uniqueId),
39
-
busy: MoonbaseSettingsStore.busy,
40
-
update: MoonbaseSettingsStore.getExtensionUpdate(uniqueId),
41
-
conflicting: MoonbaseSettingsStore.getExtensionConflicting(uniqueId)
42
-
};
43
-
}
96
+
return (
97
+
potentialDependant.manifest.dependencies?.includes(ext?.id) &&
98
+
MoonbaseSettingsStore.getExtensionEnabled(parseInt(uniqueId))
99
+
);
100
+
})
101
+
.map((a) => MoonbaseSettingsStore.getExtension(parseInt(a)))
44
102
);
103
+
const implicitlyEnabled = enabledDependants.length > 0;
45
104
46
-
// Why it work like that :sob:
47
-
if (ext == null) return <></>;
105
+
const hasDuplicateEntry = useStateFromStores([MoonbaseSettingsStore], () =>
106
+
Object.entries(MoonbaseSettingsStore.extensions).some(
107
+
([otherUniqueId, otherExt]) =>
108
+
otherExt != null && otherExt?.id === ext?.id && parseInt(otherUniqueId) !== uniqueId
109
+
)
110
+
);
48
111
49
-
const {
50
-
Card,
51
-
CardClasses,
52
-
Flex,
53
-
Text,
54
-
MarkdownParser,
55
-
Switch,
56
-
TabBar,
57
-
Button
58
-
} = CommonComponents;
112
+
return ext == null ? (
113
+
<></>
114
+
) : (
115
+
<Card editable={true} className={AppCardClasses.card}>
116
+
<div className={AppCardClasses.cardHeader}>
117
+
<Flex direction={Flex.Direction.VERTICAL}>
118
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER}>
119
+
<Text variant="text-md/semibold">{ext.manifest?.meta?.name ?? ext.id}</Text>
120
+
{ext.source.type === ExtensionLoadSource.Developer && (
121
+
<Tooltip text="This is a local extension" position="top">
122
+
{(props: any) => <ScienceIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
123
+
</Tooltip>
124
+
)}
59
125
60
-
const tagline = ext.manifest?.meta?.tagline;
61
-
const settings = ext.manifest?.settings;
62
-
const description = ext.manifest?.meta?.description;
126
+
{hasDuplicateEntry && ext?.source?.url && (
127
+
<Tooltip text={`This extension is from the following repository: ${ext.source.url}`} position="top">
128
+
{(props: any) => <WindowTopOutlineIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
129
+
</Tooltip>
130
+
)}
63
131
64
-
return (
65
-
<Card editable={true} className={CardClasses.card}>
66
-
<div className={CardClasses.cardHeader}>
67
-
<Flex direction={Flex.Direction.VERTICAL}>
68
-
<Flex direction={Flex.Direction.HORIZONTAL}>
69
-
<Text variant="text-md/semibold">
70
-
{ext.manifest?.meta?.name ?? ext.id}
71
-
</Text>
132
+
{ext.manifest?.meta?.deprecated && (
133
+
<Tooltip text="This extension is deprecated" position="top">
134
+
{(props: any) => <WarningIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
135
+
</Tooltip>
136
+
)}
72
137
</Flex>
73
138
74
-
{tagline != null && (
75
-
<Text variant="text-sm/normal">
76
-
{MarkdownParser.parse(tagline)}
77
-
</Text>
78
-
)}
139
+
{tagline != null && <Text variant="text-sm/normal">{MarkupUtils.parse(tagline)}</Text>}
140
+
</Flex>
141
+
142
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.END} justify={Flex.Justify.END}>
143
+
<div
144
+
// too lazy to learn how <Flex /> works lmao
145
+
style={{
146
+
display: "flex",
147
+
alignItems: "center",
148
+
gap: "1rem"
149
+
}}
150
+
>
151
+
{ext.state === ExtensionState.NotDownloaded ? (
152
+
<Tooltip
153
+
text={conflicting ? CONFLICTING_TEXT : COMPAT_TEXT_MAP[ext.compat]}
154
+
shouldShow={conflicting || ext.compat !== ExtensionCompat.Compatible}
155
+
>
156
+
{(props: any) => (
157
+
<Button
158
+
{...props}
159
+
color={Button.Colors.BRAND}
160
+
submitting={busy}
161
+
disabled={ext.compat !== ExtensionCompat.Compatible || conflicting}
162
+
onClick={async () => {
163
+
await MoonbaseSettingsStore.installExtension(uniqueId);
164
+
const deps = await MoonbaseSettingsStore.getDependencies(uniqueId);
165
+
if (deps != null) {
166
+
await doMissingExtensionPopup(deps);
167
+
}
168
+
169
+
// Don't auto enable dangerous extensions
170
+
if (!ext.manifest?.meta?.tags?.includes(ExtensionTag.DangerZone)) {
171
+
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, true);
172
+
}
173
+
}}
174
+
>
175
+
Install
176
+
</Button>
177
+
)}
178
+
</Tooltip>
179
+
) : (
180
+
<>
181
+
{ext.source.type === ExtensionLoadSource.Normal && (
182
+
<PanelButton
183
+
icon={TrashIcon}
184
+
tooltipText="Delete"
185
+
onClick={() => {
186
+
MoonbaseSettingsStore.deleteExtension(uniqueId);
187
+
}}
188
+
/>
189
+
)}
190
+
191
+
{update != null && (
192
+
<PanelButton
193
+
icon={DownloadIcon}
194
+
tooltipText="Update"
195
+
onClick={() => {
196
+
MoonbaseSettingsStore.installExtension(uniqueId);
197
+
}}
198
+
/>
199
+
)}
200
+
201
+
<FormSwitch
202
+
value={ext.compat === ExtensionCompat.Compatible && (enabled || implicitlyEnabled)}
203
+
disabled={implicitlyEnabled || ext.compat !== ExtensionCompat.Compatible}
204
+
hideBorder={true}
205
+
style={{ marginBottom: "0px" }}
206
+
// @ts-expect-error fix type later
207
+
tooltipNote={
208
+
ext.compat !== ExtensionCompat.Compatible ? (
209
+
COMPAT_TEXT_MAP[ext.compat]
210
+
) : implicitlyEnabled ? (
211
+
<div style={{ display: "flex", flexDirection: "column" }}>
212
+
<div>{`This extension is a dependency of the following enabled extension${
213
+
enabledDependants.length > 1 ? "s" : ""
214
+
}:`}</div>
215
+
{enabledDependants.map((dep) => (
216
+
<div>{"โข " + (dep.manifest.meta?.name ?? dep.id)}</div>
217
+
))}
218
+
</div>
219
+
) : undefined
220
+
}
221
+
onChange={() => {
222
+
const toggle = () => {
223
+
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled);
224
+
};
225
+
226
+
if (enabled && constants.builtinExtensions.includes(ext.id)) {
227
+
doGenericExtensionPopup(
228
+
"Built in extension",
229
+
"This extension is enabled by default. Disabling it might have consequences. Are you sure you want to disable it?",
230
+
uniqueId,
231
+
toggle
232
+
);
233
+
} else if (!enabled && ext.manifest?.meta?.tags?.includes(ExtensionTag.DangerZone)) {
234
+
doGenericExtensionPopup(
235
+
"Dangerous extension",
236
+
"This extension is marked as dangerous. Enabling it might have consequences. Are you sure you want to enable it?",
237
+
uniqueId,
238
+
toggle
239
+
);
240
+
} else {
241
+
toggle();
242
+
}
243
+
}}
244
+
/>
245
+
</>
246
+
)}
247
+
</div>
79
248
</Flex>
249
+
</div>
80
250
81
-
<Flex
82
-
direction={Flex.Direction.HORIZONTAL}
83
-
align={Flex.Align.END}
84
-
justify={Flex.Justify.END}
85
-
>
86
-
{ext.state === ExtensionState.NotDownloaded ? (
87
-
<Button
88
-
color={Button.Colors.BRAND}
89
-
submitting={busy}
90
-
disabled={conflicting}
91
-
onClick={() => {
92
-
MoonbaseSettingsStore.installExtension(uniqueId);
93
-
}}
94
-
>
95
-
Install
96
-
</Button>
97
-
) : (
98
-
<div
99
-
// too lazy to learn how <Flex /> works lmao
251
+
<div>
252
+
{(description != null || changelog != null || settings != null || linkButtons.length > 0) && (
253
+
<Flex>
254
+
<TabBar
255
+
selectedItem={tab}
256
+
type="top"
257
+
onItemSelect={setTab}
258
+
className={DiscoveryClasses.tabBar}
100
259
style={{
101
-
display: "flex",
102
-
alignItems: "center",
103
-
gap: "1rem"
260
+
padding: "0 20px"
104
261
}}
105
262
>
106
-
{ext.source.type === ExtensionLoadSource.Normal && (
107
-
<PanelButton
108
-
icon={TrashIcon}
109
-
tooltipText="Delete"
110
-
onClick={() => {
111
-
MoonbaseSettingsStore.deleteExtension(uniqueId);
112
-
}}
113
-
/>
114
-
)}
263
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Info}>
264
+
Info
265
+
</TabBar.Item>
115
266
116
-
{update != null && (
117
-
<PanelButton
118
-
icon={DownloadIcon}
119
-
tooltipText="Update"
120
-
onClick={() => {
121
-
MoonbaseSettingsStore.installExtension(uniqueId);
122
-
}}
123
-
/>
267
+
{description != null && (
268
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Description}>
269
+
Description
270
+
</TabBar.Item>
124
271
)}
125
272
126
-
{restartNeeded && (
127
-
<PanelButton
128
-
icon={() => (
129
-
<CircleWarningIcon
130
-
color={CommonComponents.tokens.colors.STATUS_DANGER}
131
-
/>
132
-
)}
133
-
onClick={() => window.location.reload()}
134
-
tooltipText="You will need to reload/restart your client for this extension to work properly."
135
-
/>
273
+
{changelog != null && (
274
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Changelog}>
275
+
Changelog
276
+
</TabBar.Item>
136
277
)}
137
278
138
-
<Switch
139
-
checked={enabled}
140
-
onChange={() => {
141
-
setRestartNeeded(true);
142
-
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled);
143
-
}}
144
-
/>
145
-
</div>
146
-
)}
147
-
</Flex>
148
-
</div>
279
+
{settings != null && (
280
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Settings}>
281
+
Settings
282
+
</TabBar.Item>
283
+
)}
284
+
</TabBar>
149
285
150
-
<div>
151
-
{(description != null || settings != null) && (
152
-
<TabBar
153
-
selectedItem={tab}
154
-
type="top"
155
-
onItemSelect={setTab}
156
-
className={TabBarClasses.tabBar}
157
-
style={{
158
-
padding: "0 20px"
159
-
}}
160
-
>
161
-
<TabBar.Item
162
-
className={TabBarClasses.tabBarItem}
163
-
id={ExtensionPage.Info}
286
+
<Flex
287
+
align={Flex.Align.CENTER}
288
+
justify={Flex.Justify.END}
289
+
direction={Flex.Direction.HORIZONTAL}
290
+
grow={1}
291
+
className="moonbase-link-buttons"
164
292
>
165
-
Info
166
-
</TabBar.Item>
167
-
168
-
{description != null && (
169
-
<TabBar.Item
170
-
className={TabBarClasses.tabBarItem}
171
-
id={ExtensionPage.Description}
172
-
>
173
-
Description
174
-
</TabBar.Item>
175
-
)}
176
-
177
-
{settings != null && (
178
-
<TabBar.Item
179
-
className={TabBarClasses.tabBarItem}
180
-
id={ExtensionPage.Settings}
181
-
>
182
-
Settings
183
-
</TabBar.Item>
184
-
)}
185
-
</TabBar>
293
+
{linkButtons.length > 0 && linkButtons}
294
+
</Flex>
295
+
</Flex>
186
296
)}
187
297
188
298
<Flex
189
299
justify={Flex.Justify.START}
190
300
wrap={Flex.Wrap.WRAP}
191
301
style={{
192
-
padding: "16px 16px"
302
+
padding: "16px 16px",
303
+
// This looks wonky in the settings tab
304
+
rowGap: tab === ExtensionPage.Info ? "16px" : undefined
193
305
}}
194
306
>
195
-
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} />}
307
+
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} selectTag={selectTag} />}
196
308
{tab === ExtensionPage.Description && (
197
-
<Text variant="text-md/normal">
198
-
{MarkdownParser.parse(description ?? "*No description*")}
309
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
310
+
{MarkupUtils.parse(description ?? "*No description*", true, {
311
+
allowHeading: true,
312
+
allowLinks: true,
313
+
allowList: true
314
+
})}
315
+
</Text>
316
+
)}
317
+
{tab === ExtensionPage.Changelog && (
318
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
319
+
{MarkupUtils.parse(changelog ?? "*No changelog*", true, {
320
+
allowHeading: true,
321
+
allowLinks: true,
322
+
allowList: true
323
+
})}
199
324
</Text>
200
325
)}
201
-
{tab === ExtensionPage.Settings && <Settings ext={ext} />}
326
+
{tab === ExtensionPage.Settings && (
327
+
<ErrorBoundary>
328
+
<Settings ext={ext} />
329
+
</ErrorBoundary>
330
+
)}
202
331
</Flex>
203
332
</div>
204
333
</Card>
+119
-119
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
+119
-119
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
···
1
1
import { tagNames } from "./info";
2
-
3
2
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
-
import React from "@moonlight-mod/wp/common_react";
5
-
import * as Flux from "@moonlight-mod/wp/common_flux";
3
+
import * as React from "@moonlight-mod/wp/react";
4
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
6
5
import { WindowStore } from "@moonlight-mod/wp/common_stores";
7
6
import {
8
7
Button,
···
11
10
Popout,
12
11
Dialog,
13
12
Menu,
14
-
MenuGroup,
15
-
MenuCheckboxItem,
16
-
MenuItem
17
-
} from "@moonlight-mod/wp/common_components";
18
-
import CommonComponents from "@moonlight-mod/wp/common_components";
13
+
ChevronSmallDownIcon,
14
+
ChevronSmallUpIcon,
15
+
ArrowsUpDownIcon,
16
+
RetryIcon,
17
+
Tooltip
18
+
} from "@moonlight-mod/wp/discord/components/common/index";
19
+
import { MenuGroup, MenuCheckboxItem, MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu";
20
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
21
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
22
+
import TagItem from "@moonlight-mod/wp/discord/modules/forums/web/Tag";
19
23
20
24
export enum Filter {
21
25
Core = 1 << 0,
···
24
28
Enabled = 1 << 3,
25
29
Disabled = 1 << 4,
26
30
Installed = 1 << 5,
27
-
Repository = 1 << 6
31
+
Repository = 1 << 6,
32
+
Incompatible = 1 << 7,
33
+
Deprecated = 1 << 8
28
34
}
29
-
export const defaultFilter = ~(~0 << 7);
30
-
31
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
32
-
const SortMenuClasses = spacepack.findByCode("container:", "clearText:")[0]
33
-
.exports;
35
+
export const defaultFilter = 127 as Filter;
34
36
35
-
let FilterDialogClasses: any;
36
-
let FilterBarClasses: any;
37
+
let HeaderClasses: any;
38
+
let ForumsClasses: any;
39
+
let SortMenuClasses: any;
37
40
spacepack
38
-
.lazyLoad(
39
-
'"Missing channel in Channel.openChannelContextMenu"',
40
-
/e\("(\d+)"\)/g,
41
-
/webpackId:(\d+?),/
42
-
)
41
+
.lazyLoad('"Missing channel in Channel.openChannelContextMenu"', /e\("(\d+)"\)/g, /webpackId:(\d+?),/)
43
42
.then(() => {
44
-
FilterBarClasses = spacepack.findByCode("tagsButtonWithCount:")[0].exports;
45
-
FilterDialogClasses = spacepack.findByCode(
46
-
"countContainer:",
47
-
"tagContainer:"
48
-
)[0].exports;
43
+
ForumsClasses = spacepack.require("discord/modules/forums/web/Forums.css");
44
+
HeaderClasses = spacepack.require("discord/modules/forums/web/Header.css");
45
+
SortMenuClasses = spacepack.require("discord/modules/forums/web/SortMenu.css");
49
46
});
50
47
51
-
const TagItem = spacepack.findByCode(".FORUM_TAG_A11Y_FILTER_BY_TAG")[0].exports
52
-
.Z;
53
-
54
-
const { ChevronSmallDownIcon, ChevronSmallUpIcon, ArrowsUpDownIcon } =
55
-
CommonComponents;
56
-
57
-
function toggleTag(
58
-
selectedTags: Set<string>,
59
-
setSelectedTags: (tags: Set<string>) => void,
60
-
tag: string
61
-
) {
48
+
function toggleTag(selectedTags: Set<string>, setSelectedTags: (tags: Set<string>) => void, tag: string) {
62
49
const newState = new Set(selectedTags);
63
50
if (newState.has(tag)) newState.delete(tag);
64
51
else newState.add(tag);
···
74
61
setFilter: (filter: Filter) => void;
75
62
closePopout: () => void;
76
63
}) {
77
-
const toggleFilter = (set: Filter) =>
78
-
setFilter(filter & set ? filter & ~set : filter | set);
64
+
const toggleFilter = (set: Filter) => setFilter(filter & set ? filter & ~set : filter | set);
79
65
80
66
return (
81
67
<div className={SortMenuClasses.container}>
82
-
<Menu navId="sort-filter" hideScrollbar={true} onClose={closePopout}>
68
+
<Menu navId="sort-filter" hideScroller={true} onClose={closePopout}>
83
69
<MenuGroup label="Type">
84
70
<MenuCheckboxItem
85
71
id="t-core"
86
72
label="Core"
87
-
checked={filter & Filter.Core}
73
+
checked={(filter & Filter.Core) === Filter.Core}
88
74
action={() => toggleFilter(Filter.Core)}
89
75
/>
90
76
<MenuCheckboxItem
91
77
id="t-normal"
92
78
label="Normal"
93
-
checked={filter & Filter.Normal}
79
+
checked={(filter & Filter.Normal) === Filter.Normal}
94
80
action={() => toggleFilter(Filter.Normal)}
95
81
/>
96
82
<MenuCheckboxItem
97
83
id="t-developer"
98
84
label="Developer"
99
-
checked={filter & Filter.Developer}
85
+
checked={(filter & Filter.Developer) === Filter.Developer}
100
86
action={() => toggleFilter(Filter.Developer)}
101
87
/>
102
88
</MenuGroup>
···
104
90
<MenuCheckboxItem
105
91
id="s-enabled"
106
92
label="Enabled"
107
-
checked={filter & Filter.Enabled}
93
+
checked={(filter & Filter.Enabled) === Filter.Enabled}
108
94
action={() => toggleFilter(Filter.Enabled)}
109
95
/>
110
96
<MenuCheckboxItem
111
97
id="s-disabled"
112
98
label="Disabled"
113
-
checked={filter & Filter.Disabled}
99
+
checked={(filter & Filter.Disabled) === Filter.Disabled}
114
100
action={() => toggleFilter(Filter.Disabled)}
115
101
/>
116
102
</MenuGroup>
···
118
104
<MenuCheckboxItem
119
105
id="l-installed"
120
106
label="Installed"
121
-
checked={filter & Filter.Installed}
107
+
checked={(filter & Filter.Installed) === Filter.Installed}
122
108
action={() => toggleFilter(Filter.Installed)}
123
109
/>
124
110
<MenuCheckboxItem
125
111
id="l-repository"
126
112
label="Repository"
127
-
checked={filter & Filter.Repository}
113
+
checked={(filter & Filter.Repository) === Filter.Repository}
128
114
action={() => toggleFilter(Filter.Repository)}
129
115
/>
130
116
</MenuGroup>
131
117
<MenuGroup>
118
+
<MenuCheckboxItem
119
+
id="l-incompatible"
120
+
label="Show incompatible"
121
+
checked={(filter & Filter.Incompatible) === Filter.Incompatible}
122
+
action={() => toggleFilter(Filter.Incompatible)}
123
+
/>
124
+
<MenuCheckboxItem
125
+
id="l-deprecated"
126
+
label="Show deprecated"
127
+
checked={(filter & Filter.Deprecated) === Filter.Deprecated}
128
+
action={() => toggleFilter(Filter.Deprecated)}
129
+
/>
132
130
<MenuItem
133
131
id="reset-all"
134
132
className={SortMenuClasses.clearText}
135
-
label={
136
-
<Text variant="text-sm/medium" color="none">
137
-
Reset to default
138
-
</Text>
139
-
}
133
+
label="Reset to default"
140
134
action={() => {
141
135
setFilter(defaultFilter);
142
136
closePopout();
···
148
142
);
149
143
}
150
144
151
-
function TagButtonPopout({
152
-
selectedTags,
153
-
setSelectedTags,
154
-
setPopoutRef,
155
-
closePopout
156
-
}: any) {
145
+
function TagButtonPopout({ selectedTags, setSelectedTags, setPopoutRef, closePopout }: any) {
157
146
return (
158
-
<Dialog ref={setPopoutRef} className={FilterDialogClasses.container}>
159
-
<div className={FilterDialogClasses.header}>
160
-
<div className={FilterDialogClasses.headerLeft}>
161
-
<Heading
162
-
color="interactive-normal"
163
-
variant="text-xs/bold"
164
-
className={FilterDialogClasses.headerText}
165
-
>
147
+
<Dialog ref={setPopoutRef} className={HeaderClasses.container}>
148
+
<div className={HeaderClasses.header}>
149
+
<div className={HeaderClasses.headerLeft}>
150
+
<Heading color="interactive-normal" variant="text-xs/bold" className={HeaderClasses.headerText}>
166
151
Select tags
167
152
</Heading>
168
-
<div className={FilterDialogClasses.countContainer}>
169
-
<Text
170
-
className={FilterDialogClasses.countText}
171
-
color="none"
172
-
variant="text-xs/medium"
173
-
>
153
+
<div className={HeaderClasses.countContainer}>
154
+
<Text className={HeaderClasses.countText} color="none" variant="text-xs/medium">
174
155
{selectedTags.size}
175
156
</Text>
176
157
</div>
177
158
</div>
178
159
</div>
179
-
<div className={FilterDialogClasses.tagContainer}>
160
+
<div className={HeaderClasses.tagContainer}>
180
161
{Object.keys(tagNames).map((tag) => (
181
162
<TagItem
182
163
key={tag}
183
-
className={FilterDialogClasses.tag}
184
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
164
+
className={HeaderClasses.tag}
165
+
tag={{ name: tagNames[tag as keyof typeof tagNames], id: tagNames[tag as keyof typeof tagNames] }}
185
166
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
186
167
selected={selectedTags.has(tag)}
187
168
/>
188
169
))}
189
170
</div>
190
-
<div className={FilterDialogClasses.separator} />
171
+
<div className={HeaderClasses.separator} />
191
172
<Button
192
173
look={Button.Looks.LINK}
193
174
size={Button.Sizes.MIN}
194
175
color={Button.Colors.CUSTOM}
195
-
className={FilterDialogClasses.clear}
176
+
className={HeaderClasses.clear}
196
177
onClick={() => {
197
178
setSelectedTags(new Set());
198
179
closePopout();
···
217
198
selectedTags: Set<string>;
218
199
setSelectedTags: (tags: Set<string>) => void;
219
200
}) {
220
-
const windowSize = Flux.useStateFromStores([WindowStore], () =>
221
-
WindowStore.windowSize()
222
-
);
201
+
const windowSize = useStateFromStores([WindowStore], () => WindowStore.windowSize());
223
202
224
203
const tagsContainer = React.useRef<HTMLDivElement>(null);
225
204
const tagListInner = React.useRef<HTMLDivElement>(null);
226
205
const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0);
206
+
const [checkingUpdates, setCheckingUpdates] = React.useState(false);
207
+
227
208
React.useLayoutEffect(() => {
228
209
if (tagsContainer.current === null || tagListInner.current === null) return;
229
-
const { left: containerX, top: containerY } =
230
-
tagsContainer.current.getBoundingClientRect();
210
+
const { left: containerX, top: containerY } = tagsContainer.current.getBoundingClientRect();
231
211
let offset = 0;
232
212
for (const child of tagListInner.current.children) {
233
-
const {
234
-
right: childX,
235
-
top: childY,
236
-
height
237
-
} = child.getBoundingClientRect();
213
+
const { right: childX, top: childY, height } = child.getBoundingClientRect();
238
214
if (childY - containerY > height) break;
239
215
const newOffset = childX - containerX;
240
216
if (newOffset > offset) {
···
242
218
}
243
219
}
244
220
setTagsButtonOffset(offset);
245
-
}, [windowSize]);
221
+
}, [windowSize, tagsContainer.current, tagListInner.current, tagListInner.current?.getBoundingClientRect()?.width]);
246
222
247
223
return (
248
224
<div
···
250
226
style={{
251
227
paddingTop: "12px"
252
228
}}
253
-
className={`${FilterBarClasses.tagsContainer} ${Margins.marginBottom8}`}
229
+
className={`${ForumsClasses.tagsContainer} ${Margins.marginBottom8}`}
254
230
>
231
+
<Tooltip text="Refresh updates" position="top">
232
+
{(props: any) => (
233
+
<Button
234
+
{...props}
235
+
size={Button.Sizes.MIN}
236
+
color={Button.Colors.CUSTOM}
237
+
className={`${ForumsClasses.sortDropdown} moonbase-retry-button`}
238
+
innerClassName={ForumsClasses.sortDropdownInner}
239
+
onClick={() => {
240
+
(async () => {
241
+
try {
242
+
setCheckingUpdates(true);
243
+
await MoonbaseSettingsStore.checkUpdates();
244
+
} finally {
245
+
// artificial delay because the spin is fun
246
+
await new Promise((r) => setTimeout(r, 500));
247
+
setCheckingUpdates(false);
248
+
}
249
+
})();
250
+
}}
251
+
>
252
+
<RetryIcon size={"custom"} width={16} className={checkingUpdates ? "moonbase-speen" : ""} />
253
+
</Button>
254
+
)}
255
+
</Tooltip>
255
256
<Popout
256
257
renderPopout={({ closePopout }: any) => (
257
-
<FilterButtonPopout
258
-
filter={filter}
259
-
setFilter={setFilter}
260
-
closePopout={closePopout}
261
-
/>
258
+
<FilterButtonPopout filter={filter} setFilter={setFilter} closePopout={closePopout} />
262
259
)}
263
260
position="bottom"
264
261
align="left"
···
268
265
{...props}
269
266
size={Button.Sizes.MIN}
270
267
color={Button.Colors.CUSTOM}
271
-
className={FilterBarClasses.sortDropdown}
272
-
innerClassName={FilterBarClasses.sortDropdownInner}
268
+
className={ForumsClasses.sortDropdown}
269
+
innerClassName={ForumsClasses.sortDropdownInner}
273
270
>
274
271
<ArrowsUpDownIcon size="xs" />
275
-
<Text
276
-
className={FilterBarClasses.sortDropdownText}
277
-
variant="text-sm/medium"
278
-
color="interactive-normal"
279
-
>
272
+
<Text className={ForumsClasses.sortDropdownText} variant="text-sm/medium" color="interactive-normal">
280
273
Sort & filter
281
274
</Text>
282
275
{isShown ? (
···
287
280
</Button>
288
281
)}
289
282
</Popout>
290
-
<div className={FilterBarClasses.divider} />
291
-
<div className={FilterBarClasses.tagList}>
292
-
<div ref={tagListInner} className={FilterBarClasses.tagListInner}>
283
+
<div className={ForumsClasses.divider} />
284
+
<div className={ForumsClasses.tagList}>
285
+
<div ref={tagListInner} className={ForumsClasses.tagListInner}>
293
286
{Object.keys(tagNames).map((tag) => (
294
287
<TagItem
295
288
key={tag}
296
-
className={FilterBarClasses.tag}
297
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
289
+
className={ForumsClasses.tag}
290
+
tag={{ name: tagNames[tag as keyof typeof tagNames], id: tag }}
298
291
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
299
292
selected={selectedTags.has(tag)}
300
293
/>
···
322
315
left: tagsButtonOffset
323
316
}}
324
317
// TODO: Use Discord's class name utility
325
-
className={`${FilterBarClasses.tagsButton} ${
326
-
selectedTags.size > 0 ? FilterBarClasses.tagsButtonWithCount : ""
327
-
}`}
328
-
innerClassName={FilterBarClasses.tagsButtonInner}
318
+
className={`${ForumsClasses.tagsButton} ${selectedTags.size > 0 ? ForumsClasses.tagsButtonWithCount : ""}`}
319
+
innerClassName={ForumsClasses.tagsButtonInner}
329
320
>
330
321
{selectedTags.size > 0 ? (
331
-
<div
332
-
style={{ boxSizing: "content-box" }}
333
-
className={FilterBarClasses.countContainer}
334
-
>
335
-
<Text
336
-
className={FilterBarClasses.countText}
337
-
color="none"
338
-
variant="text-xs/medium"
339
-
>
322
+
<div style={{ boxSizing: "content-box" }} className={ForumsClasses.countContainer}>
323
+
<Text className={ForumsClasses.countText} color="none" variant="text-xs/medium">
340
324
{selectedTags.size}
341
325
</Text>
342
326
</div>
···
351
335
</Button>
352
336
)}
353
337
</Popout>
338
+
<Button
339
+
size={Button.Sizes.MIN}
340
+
color={Button.Colors.CUSTOM}
341
+
className={`${ForumsClasses.tagsButton} ${ForumsClasses.tagsButtonPlaceholder}`}
342
+
innerClassName={ForumsClasses.tagsButtonInner}
343
+
>
344
+
{selectedTags.size > 0 ? (
345
+
<div style={{ boxSizing: "content-box" }} className={ForumsClasses.countContainer}>
346
+
<Text className={ForumsClasses.countText} color="none" variant="text-xs/medium">
347
+
{selectedTags.size}
348
+
</Text>
349
+
</div>
350
+
) : null}
351
+
352
+
<ChevronSmallUpIcon size={"custom"} width={20} />
353
+
</Button>
354
354
</div>
355
355
);
356
356
}
+110
-51
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
+110
-51
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
···
3
3
import FilterBar, { Filter, defaultFilter } from "./filterBar";
4
4
import ExtensionCard from "./card";
5
5
6
-
import React from "@moonlight-mod/wp/common_react";
6
+
import React from "@moonlight-mod/wp/react";
7
7
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
8
-
import * as Flux from "@moonlight-mod/wp/common_flux";
8
+
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
9
+
import {
10
+
FormDivider,
11
+
CircleInformationIcon,
12
+
XSmallIcon,
13
+
Button
14
+
} from "@moonlight-mod/wp/discord/components/common/index";
15
+
import PanelButton from "@moonlight-mod/wp/discord/components/common/PanelButton";
9
16
10
17
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
18
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
19
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
20
+
import HelpMessage from "../HelpMessage";
11
21
12
-
const SearchBar: any = Object.values(
13
-
spacepack.findByCode("Messages.SEARCH", "hideSearchIcon")[0].exports
14
-
)[0];
22
+
const SearchBar = spacepack.require("discord/uikit/search/SearchBar").default;
23
+
24
+
const validTags: string[] = Object.values(ExtensionTag);
15
25
16
26
export default function ExtensionsPage() {
17
-
const moonbaseId = MoonbaseSettingsStore.getExtensionUniqueId("moonbase")!;
18
-
const { extensions, savedFilter } = Flux.useStateFromStoresObject(
19
-
[MoonbaseSettingsStore],
20
-
() => {
21
-
return {
22
-
extensions: MoonbaseSettingsStore.extensions,
23
-
savedFilter: MoonbaseSettingsStore.getExtensionConfig(
24
-
moonbaseId,
25
-
"filter"
26
-
)
27
-
};
28
-
}
29
-
);
27
+
const { extensions, savedFilter } = useStateFromStoresObject([MoonbaseSettingsStore], () => {
28
+
return {
29
+
extensions: MoonbaseSettingsStore.extensions,
30
+
savedFilter: MoonbaseSettingsStore.getExtensionConfigRaw<number>("moonbase", "filter", defaultFilter)
31
+
};
32
+
});
30
33
31
34
const [query, setQuery] = React.useState("");
35
+
const [hitUpdateAll, setHitUpdateAll] = React.useState(false);
36
+
37
+
const filterState = React.useState(defaultFilter);
32
38
33
39
let filter: Filter, setFilter: (filter: Filter) => void;
34
-
if (moonlight.getConfigOption<boolean>("moonbase", "saveFilter")) {
40
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "saveFilter", false)) {
35
41
filter = savedFilter ?? defaultFilter;
36
-
setFilter = (filter) =>
37
-
MoonbaseSettingsStore.setExtensionConfig(moonbaseId, "filter", filter);
42
+
setFilter = (filter) => MoonbaseSettingsStore.setExtensionConfig("moonbase", "filter", filter);
38
43
} else {
39
-
const state = React.useState(defaultFilter);
40
-
filter = state[0];
41
-
setFilter = state[1];
44
+
filter = filterState[0];
45
+
setFilter = filterState[1];
42
46
}
47
+
43
48
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
49
+
const selectTag = React.useCallback(
50
+
(tag: string) => {
51
+
const newState = new Set(selectedTags);
52
+
if (validTags.includes(tag)) newState.add(tag);
53
+
setSelectedTags(newState);
54
+
},
55
+
[selectedTags]
56
+
);
57
+
44
58
const sorted = Object.values(extensions).sort((a, b) => {
45
59
const aName = a.manifest.meta?.name ?? a.id;
46
60
const bName = b.manifest.meta?.name ?? b.id;
···
49
63
50
64
const filtered = sorted.filter(
51
65
(ext) =>
52
-
(ext.manifest.meta?.name?.toLowerCase().includes(query) ||
66
+
(query === "" ||
67
+
ext.manifest.id?.toLowerCase().includes(query) ||
68
+
ext.manifest.meta?.name?.toLowerCase().includes(query) ||
53
69
ext.manifest.meta?.tagline?.toLowerCase().includes(query) ||
70
+
(ext.manifest?.settings != null &&
71
+
Object.entries(ext.manifest.settings).some(([key, setting]) =>
72
+
(setting.displayName ?? key).toLowerCase().includes(query)
73
+
)) ||
74
+
(ext.manifest?.meta?.authors != null &&
75
+
ext.manifest.meta.authors.some((author) =>
76
+
(typeof author === "string" ? author : author.name).toLowerCase().includes(query)
77
+
)) ||
54
78
ext.manifest.meta?.description?.toLowerCase().includes(query)) &&
55
-
[...selectedTags.values()].every(
56
-
(tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)
57
-
) &&
79
+
[...selectedTags.values()].every((tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)) &&
58
80
// This seems very bad, sorry
59
81
!(
60
-
(!(filter & Filter.Core) &&
61
-
ext.source.type === ExtensionLoadSource.Core) ||
62
-
(!(filter & Filter.Normal) &&
63
-
ext.source.type === ExtensionLoadSource.Normal) ||
64
-
(!(filter & Filter.Developer) &&
65
-
ext.source.type === ExtensionLoadSource.Developer) ||
66
-
(!(filter & Filter.Enabled) &&
67
-
MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
68
-
(!(filter & Filter.Disabled) &&
69
-
!MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
70
-
(!(filter & Filter.Installed) &&
71
-
ext.state !== ExtensionState.NotDownloaded) ||
72
-
(!(filter & Filter.Repository) &&
73
-
ext.state === ExtensionState.NotDownloaded)
74
-
)
82
+
(!(filter & Filter.Core) && ext.source.type === ExtensionLoadSource.Core) ||
83
+
(!(filter & Filter.Normal) && ext.source.type === ExtensionLoadSource.Normal) ||
84
+
(!(filter & Filter.Developer) && ext.source.type === ExtensionLoadSource.Developer) ||
85
+
(!(filter & Filter.Enabled) && MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
86
+
(!(filter & Filter.Disabled) && !MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
87
+
(!(filter & Filter.Installed) && ext.state !== ExtensionState.NotDownloaded) ||
88
+
(!(filter & Filter.Repository) && ext.state === ExtensionState.NotDownloaded)
89
+
) &&
90
+
(filter & Filter.Incompatible ||
91
+
ext.compat === ExtensionCompat.Compatible ||
92
+
(ext.compat === ExtensionCompat.InvalidApiLevel && ext.hasUpdate)) &&
93
+
(filter & Filter.Deprecated ||
94
+
ext.manifest?.meta?.deprecated !== true ||
95
+
ext.state !== ExtensionState.NotDownloaded)
75
96
);
97
+
98
+
// Prioritize extensions with updates
99
+
const filteredWithUpdates = filtered.filter((ext) => ext!.hasUpdate);
100
+
const filteredWithoutUpdates = filtered.filter((ext) => !ext!.hasUpdate);
76
101
77
102
return (
78
103
<>
···
89
114
spellCheck: "false"
90
115
}}
91
116
/>
92
-
<FilterBar
93
-
filter={filter}
94
-
setFilter={setFilter}
95
-
selectedTags={selectedTags}
96
-
setSelectedTags={setSelectedTags}
97
-
/>
98
-
{filtered.map((ext) => (
99
-
<ExtensionCard uniqueId={ext.uniqueId} key={ext.id} />
117
+
<FilterBar filter={filter} setFilter={setFilter} selectedTags={selectedTags} setSelectedTags={setSelectedTags} />
118
+
119
+
{filteredWithUpdates.length > 0 && (
120
+
<HelpMessage
121
+
icon={CircleInformationIcon}
122
+
text="Extension updates are available"
123
+
className="moonbase-extension-update-section"
124
+
>
125
+
<div className="moonbase-help-message-buttons">
126
+
<Button
127
+
color={Button.Colors.BRAND}
128
+
size={Button.Sizes.TINY}
129
+
disabled={hitUpdateAll}
130
+
onClick={() => {
131
+
setHitUpdateAll(true);
132
+
MoonbaseSettingsStore.updateAllExtensions();
133
+
}}
134
+
>
135
+
Update all
136
+
</Button>
137
+
<PanelButton
138
+
icon={XSmallIcon}
139
+
onClick={() => {
140
+
MoonbaseSettingsStore.dismissAllExtensionUpdates();
141
+
}}
142
+
/>
143
+
</div>
144
+
</HelpMessage>
145
+
)}
146
+
147
+
{filteredWithUpdates.map((ext) => (
148
+
<ErrorBoundary>
149
+
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} selectTag={selectTag} />
150
+
</ErrorBoundary>
151
+
))}
152
+
{filteredWithUpdates.length > 0 && filteredWithoutUpdates.length > 0 && (
153
+
<FormDivider className="moonbase-update-divider" />
154
+
)}
155
+
{filteredWithoutUpdates.map((ext) => (
156
+
<ErrorBoundary>
157
+
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} selectTag={selectTag} />
158
+
</ErrorBoundary>
100
159
))}
101
160
</>
102
161
);
+55
-54
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
+55
-54
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
···
1
1
import { ExtensionTag } from "@moonlight-mod/types";
2
2
import { MoonbaseExtension } from "../../../types";
3
3
4
-
import React from "@moonlight-mod/wp/common_react";
5
-
import CommonComponents from "@moonlight-mod/wp/common_components";
6
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
+
import React from "@moonlight-mod/wp/react";
5
+
import { Text } from "@moonlight-mod/wp/discord/components/common/index";
7
6
8
7
type Dependency = {
9
8
id: string;
···
34
33
[ExtensionTag.Library]: "Library"
35
34
};
36
35
37
-
const UserInfoClasses = spacepack.findByCode(
38
-
"infoScroller",
39
-
"userInfoSection",
40
-
"userInfoSectionHeader"
41
-
)[0].exports;
42
-
43
36
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
44
37
45
-
function InfoSection({
46
-
title,
47
-
children
48
-
}: {
49
-
title: string;
50
-
children: React.ReactNode;
51
-
}) {
38
+
function InfoSection({ title, children }: { title: string; children: React.ReactNode }) {
52
39
return (
53
40
<div
54
41
style={{
55
42
marginRight: "1em"
56
43
}}
57
44
>
58
-
<CommonComponents.Text
59
-
variant="eyebrow"
60
-
className={UserInfoClasses.userInfoSectionHeader}
61
-
>
45
+
<Text variant="eyebrow" className="moonlight-card-info-header">
62
46
{title}
63
-
</CommonComponents.Text>
47
+
</Text>
64
48
65
-
<CommonComponents.Text variant="text-sm/normal">
66
-
{children}
67
-
</CommonComponents.Text>
49
+
<Text variant="text-sm/normal">{children}</Text>
68
50
</div>
69
51
);
70
52
}
71
53
72
54
function Badge({
73
55
color,
74
-
children
56
+
children,
57
+
style = {},
58
+
onClick
75
59
}: {
76
60
color: string;
77
61
children: React.ReactNode;
62
+
style?: React.CSSProperties;
63
+
onClick?: () => void;
78
64
}) {
65
+
if (onClick) style.cursor ??= "pointer";
79
66
return (
80
67
<span
81
-
style={{
82
-
borderRadius: ".1875rem",
83
-
padding: "0 0.275rem",
84
-
marginRight: "0.4em",
85
-
backgroundColor: color,
86
-
color: "#fff"
87
-
}}
68
+
className="moonlight-card-badge"
69
+
style={
70
+
{
71
+
"--badge-color": color,
72
+
...style
73
+
} as React.CSSProperties
74
+
}
75
+
onClick={onClick}
88
76
>
89
77
{children}
90
78
</span>
91
79
);
92
80
}
93
81
94
-
export default function ExtensionInfo({ ext }: { ext: MoonbaseExtension }) {
82
+
export default function ExtensionInfo({
83
+
ext,
84
+
selectTag
85
+
}: {
86
+
ext: MoonbaseExtension;
87
+
selectTag: (tag: string) => void;
88
+
}) {
95
89
const authors = ext.manifest?.meta?.authors;
96
90
const tags = ext.manifest?.meta?.tags;
97
91
const version = ext.manifest?.version;
98
92
99
93
const dependencies: Dependency[] = [];
94
+
const incompatible: Dependency[] = [];
95
+
100
96
if (ext.manifest.dependencies != null) {
101
97
dependencies.push(
102
98
...ext.manifest.dependencies.map((dep) => ({
···
116
112
}
117
113
118
114
if (ext.manifest.incompatible != null) {
119
-
dependencies.push(
115
+
incompatible.push(
120
116
...ext.manifest.incompatible.map((dep) => ({
121
117
id: dep,
122
118
type: DependencyType.Incompatible
···
154
150
<InfoSection title="Tags">
155
151
{tags.map((tag, i) => {
156
152
const name = tagNames[tag];
153
+
let color = "var(--bg-mod-strong)";
154
+
let style;
155
+
if (tag === ExtensionTag.DangerZone) {
156
+
color = "var(--red-460)";
157
+
style = { color: "var(--primary-230)" };
158
+
}
157
159
158
160
return (
159
-
<Badge
160
-
key={i}
161
-
color={
162
-
tag === ExtensionTag.DangerZone
163
-
? "var(--red-400)"
164
-
: "var(--brand-500)"
165
-
}
166
-
>
161
+
<Badge key={i} color={color} style={style} onClick={() => selectTag(tag)}>
167
162
{name}
168
163
</Badge>
169
164
);
···
174
169
{dependencies.length > 0 && (
175
170
<InfoSection title="Dependencies">
176
171
{dependencies.map((dep) => {
177
-
const colors = {
178
-
[DependencyType.Dependency]: "var(--brand-500)",
179
-
[DependencyType.Optional]: "var(--orange-400)",
180
-
[DependencyType.Incompatible]: "var(--red-400)"
181
-
};
182
-
const color = colors[dep.type];
183
-
const id = MoonbaseSettingsStore.getExtensionUniqueId(dep.id);
184
-
const name =
185
-
(id !== null
186
-
? MoonbaseSettingsStore.getExtensionName(id!)
187
-
: null) ?? dep.id;
172
+
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
173
+
174
+
// TODO: figure out a decent way to distinguish suggested
175
+
return (
176
+
<Badge color="var(--bg-mod-strong)" key={dep.id}>
177
+
{name}
178
+
</Badge>
179
+
);
180
+
})}
181
+
</InfoSection>
182
+
)}
183
+
184
+
{incompatible.length > 0 && (
185
+
<InfoSection title="Incompatible">
186
+
{incompatible.map((dep) => {
187
+
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
188
+
188
189
return (
189
-
<Badge color={color} key={dep.id}>
190
+
<Badge color="var(--bg-mod-strong)" key={dep.id}>
190
191
{name}
191
192
</Badge>
192
193
);
+211
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
+211
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
···
1
+
// TODO: clean up the styling here
2
+
import React from "@moonlight-mod/wp/react";
3
+
import { MoonbaseExtension } from "core-extensions/src/moonbase/types";
4
+
import { openModalLazy, useModalsStore, closeModal } from "@moonlight-mod/wp/discord/modules/modals/Modals";
5
+
import { SingleSelect, Text } from "@moonlight-mod/wp/discord/components/common/index";
6
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
7
+
import { ExtensionLoadSource } from "@moonlight-mod/types";
8
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
9
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
10
+
11
+
let ConfirmModal: typeof import("@moonlight-mod/wp/discord/components/modals/ConfirmModal").default;
12
+
13
+
function close() {
14
+
const ModalStore = useModalsStore.getState();
15
+
closeModal(ModalStore.default[0].key);
16
+
}
17
+
18
+
// do this to avoid a hard dependency
19
+
function lazyLoad() {
20
+
if (!ConfirmModal) {
21
+
ConfirmModal = spacepack.require("discord/components/modals/ConfirmModal").default;
22
+
}
23
+
}
24
+
25
+
const presentableLoadSources: Record<ExtensionLoadSource, string> = {
26
+
[ExtensionLoadSource.Developer]: "Local extension", // should never show up
27
+
[ExtensionLoadSource.Core]: "Core extension",
28
+
[ExtensionLoadSource.Normal]: "Extension repository"
29
+
};
30
+
31
+
function ExtensionSelect({
32
+
id,
33
+
candidates,
34
+
option,
35
+
setOption
36
+
}: {
37
+
id: string;
38
+
candidates: MoonbaseExtension[];
39
+
option: string | undefined;
40
+
setOption: (pick: string | undefined) => void;
41
+
}) {
42
+
return (
43
+
<SingleSelect
44
+
key={id}
45
+
autofocus={false}
46
+
value={option}
47
+
options={candidates.map((candidate) => {
48
+
return {
49
+
value: candidate.uniqueId.toString(),
50
+
label:
51
+
candidate.source.url ?? presentableLoadSources[candidate.source.type] ?? candidate.manifest.version ?? ""
52
+
};
53
+
})}
54
+
onChange={(value: string) => {
55
+
setOption(value);
56
+
}}
57
+
placeholder="Missing extension"
58
+
/>
59
+
);
60
+
}
61
+
62
+
function MissingExtensionPopup({
63
+
deps,
64
+
transitionState
65
+
}: {
66
+
deps: Record<string, MoonbaseExtension[]>;
67
+
transitionState: number | null;
68
+
}) {
69
+
lazyLoad();
70
+
const amountNotAvailable = Object.values(deps).filter((candidates) => candidates.length === 0).length;
71
+
72
+
const [options, setOptions] = React.useState<Record<string, string | undefined>>(
73
+
Object.fromEntries(
74
+
Object.entries(deps).map(([id, candidates]) => [
75
+
id,
76
+
candidates.length > 0 ? candidates[0].uniqueId.toString() : undefined
77
+
])
78
+
)
79
+
);
80
+
81
+
return (
82
+
<ConfirmModal
83
+
body={
84
+
<Flex
85
+
style={{
86
+
gap: "20px"
87
+
}}
88
+
direction={Flex.Direction.VERTICAL}
89
+
>
90
+
<Text variant="text-md/normal">
91
+
This extension depends on other extensions which are not downloaded. Choose which extensions to download.
92
+
</Text>
93
+
94
+
{amountNotAvailable > 0 && (
95
+
<Text variant="text-md/normal">
96
+
{amountNotAvailable} extension
97
+
{amountNotAvailable > 1 ? "s" : ""} could not be found, and must be installed manually.
98
+
</Text>
99
+
)}
100
+
101
+
<div
102
+
style={{
103
+
display: "grid",
104
+
gridTemplateColumns: "1fr 2fr",
105
+
gap: "10px"
106
+
}}
107
+
>
108
+
{Object.entries(deps).map(([id, candidates], i) => (
109
+
<>
110
+
<Text
111
+
variant="text-md/normal"
112
+
style={{
113
+
alignSelf: "center",
114
+
wordBreak: "break-word"
115
+
}}
116
+
>
117
+
{MoonbaseSettingsStore.tryGetExtensionName(id)}
118
+
</Text>
119
+
120
+
<ExtensionSelect
121
+
id={id}
122
+
candidates={candidates}
123
+
option={options[id]}
124
+
setOption={(pick) =>
125
+
setOptions((prev) => ({
126
+
...prev,
127
+
[id]: pick
128
+
}))
129
+
}
130
+
/>
131
+
</>
132
+
))}
133
+
</div>
134
+
</Flex>
135
+
}
136
+
cancelText="Cancel"
137
+
confirmText="Install"
138
+
onCancel={close}
139
+
onConfirm={() => {
140
+
close();
141
+
142
+
for (const pick of Object.values(options)) {
143
+
if (pick != null) {
144
+
MoonbaseSettingsStore.installExtension(parseInt(pick));
145
+
}
146
+
}
147
+
}}
148
+
title="Extension dependencies"
149
+
transitionState={transitionState}
150
+
/>
151
+
);
152
+
}
153
+
154
+
export async function doMissingExtensionPopup(deps: Record<string, MoonbaseExtension[]>) {
155
+
await openModalLazy(async () => {
156
+
return ({ transitionState }: { transitionState: number | null }) => {
157
+
return <MissingExtensionPopup transitionState={transitionState} deps={deps} />;
158
+
};
159
+
});
160
+
}
161
+
162
+
function GenericExtensionPopup({
163
+
title,
164
+
content,
165
+
transitionState,
166
+
uniqueId,
167
+
cb
168
+
}: {
169
+
title: string;
170
+
content: string;
171
+
transitionState: number | null;
172
+
uniqueId: number;
173
+
cb: () => void;
174
+
}) {
175
+
lazyLoad();
176
+
177
+
return (
178
+
<ConfirmModal
179
+
title={title}
180
+
body={
181
+
<Flex>
182
+
<Text variant="text-md/normal">{content}</Text>
183
+
</Flex>
184
+
}
185
+
confirmText="Yes"
186
+
cancelText="No"
187
+
onCancel={close}
188
+
onConfirm={() => {
189
+
close();
190
+
cb();
191
+
}}
192
+
transitionState={transitionState}
193
+
/>
194
+
);
195
+
}
196
+
197
+
export async function doGenericExtensionPopup(title: string, content: string, uniqueId: number, cb: () => void) {
198
+
await openModalLazy(async () => {
199
+
return ({ transitionState }: { transitionState: number | null }) => {
200
+
return (
201
+
<GenericExtensionPopup
202
+
title={title}
203
+
content={content}
204
+
transitionState={transitionState}
205
+
uniqueId={uniqueId}
206
+
cb={cb}
207
+
/>
208
+
);
209
+
};
210
+
});
211
+
}
+133
-138
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
+133
-138
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
···
9
9
10
10
import { ExtensionState, MoonbaseExtension } from "../../../types";
11
11
12
-
import React from "@moonlight-mod/wp/common_react";
13
-
import CommonComponents from "@moonlight-mod/wp/common_components";
14
-
import * as Flux from "@moonlight-mod/wp/common_flux";
15
12
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
13
+
import React from "@moonlight-mod/wp/react";
14
+
import {
15
+
FormSwitch,
16
+
FormItem,
17
+
FormText,
18
+
TextInput,
19
+
Slider,
20
+
TextArea,
21
+
Tooltip,
22
+
Clickable,
23
+
CircleXIcon,
24
+
Text,
25
+
SingleSelect,
26
+
Button,
27
+
useVariableSelect,
28
+
multiSelect,
29
+
Select as DiscordSelect,
30
+
NumberInputStepper
31
+
} from "@moonlight-mod/wp/discord/components/common/index";
32
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
33
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
34
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
35
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
36
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
37
+
38
+
let GuildSettingsRoleEditClasses: any;
39
+
spacepack
40
+
.lazyLoad(
41
+
"renderArtisanalHack",
42
+
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
43
+
/webpackId:(\d+),name:"GuildSettings"/
44
+
)
45
+
.then(
46
+
() =>
47
+
(GuildSettingsRoleEditClasses = spacepack.require(
48
+
"discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"
49
+
))
50
+
);
16
51
17
52
type SettingsProps = {
18
53
ext: MoonbaseExtension;
···
20
55
setting: ExtensionSettingsManifest;
21
56
disabled: boolean;
22
57
};
23
-
24
58
type SettingsComponent = React.ComponentType<SettingsProps>;
25
59
26
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
60
+
const Margins = spacepack.require("discord/styles/shared/Margins.css");
27
61
28
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
62
+
function markdownify(str: string) {
63
+
return MarkupUtils.parse(str, true, {
64
+
hideSimpleEmbedContent: true,
65
+
allowLinks: true
66
+
});
67
+
}
29
68
30
69
function useConfigEntry<T>(uniqueId: number, name: string) {
31
-
return Flux.useStateFromStores(
70
+
return useStateFromStores(
32
71
[MoonbaseSettingsStore],
33
72
() => {
34
73
return {
35
74
value: MoonbaseSettingsStore.getExtensionConfig<T>(uniqueId, name),
36
-
displayName: MoonbaseSettingsStore.getExtensionConfigName(
37
-
uniqueId,
38
-
name
39
-
),
40
-
description: MoonbaseSettingsStore.getExtensionConfigDescription(
41
-
uniqueId,
42
-
name
43
-
)
75
+
displayName: MoonbaseSettingsStore.getExtensionConfigName(uniqueId, name),
76
+
description: MoonbaseSettingsStore.getExtensionConfigDescription(uniqueId, name)
44
77
};
45
78
},
46
79
[uniqueId, name]
···
48
81
}
49
82
50
83
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
51
-
const { FormSwitch } = CommonComponents;
52
-
const { value, displayName, description } = useConfigEntry<boolean>(
53
-
ext.uniqueId,
54
-
name
55
-
);
84
+
const { value, displayName, description } = useConfigEntry<boolean>(ext.uniqueId, name);
56
85
57
86
return (
58
87
<FormSwitch
···
60
89
hideBorder={true}
61
90
disabled={disabled}
62
91
onChange={(value: boolean) => {
63
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
92
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
64
93
}}
65
-
note={description}
94
+
note={description != null ? markdownify(description) : undefined}
66
95
className={`${Margins.marginReset} ${Margins.marginTop20}`}
67
96
>
68
97
{displayName}
···
71
100
}
72
101
73
102
function Number({ ext, name, setting, disabled }: SettingsProps) {
74
-
const { FormItem, FormText, Slider } = CommonComponents;
75
-
const { value, displayName, description } = useConfigEntry<number>(
76
-
ext.uniqueId,
77
-
name
78
-
);
103
+
const { value, displayName, description } = useConfigEntry<number>(ext.uniqueId, name);
79
104
80
105
const castedSetting = setting as NumberSettingType;
81
-
const min = castedSetting.min ?? 0;
82
-
const max = castedSetting.max ?? 100;
106
+
const min = castedSetting.min;
107
+
const max = castedSetting.max;
108
+
109
+
const onChange = (value: number) => {
110
+
const rounded = min == null || max == null ? Math.round(value) : Math.max(min, Math.min(max, Math.round(value)));
111
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded);
112
+
};
83
113
84
114
return (
85
115
<FormItem className={Margins.marginTop20} title={displayName}>
86
-
{description && <FormText>{description}</FormText>}
87
-
<Slider
88
-
initialValue={value ?? 0}
89
-
disabled={disabled}
90
-
minValue={castedSetting.min ?? 0}
91
-
maxValue={castedSetting.max ?? 100}
92
-
onValueChange={(value: number) => {
93
-
const rounded = Math.max(min, Math.min(max, Math.round(value)));
94
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, rounded);
95
-
}}
96
-
/>
116
+
{min == null || max == null ? (
117
+
<Flex justify={Flex.Justify.BETWEEN} direction={Flex.Direction.HORIZONTAL}>
118
+
{description && <FormText>{markdownify(description)}</FormText>}
119
+
<NumberInputStepper value={value ?? 0} onChange={onChange} />
120
+
</Flex>
121
+
) : (
122
+
<>
123
+
{description && <FormText>{markdownify(description)}</FormText>}
124
+
<Slider
125
+
initialValue={value ?? 0}
126
+
disabled={disabled}
127
+
minValue={min}
128
+
maxValue={max}
129
+
onValueChange={onChange}
130
+
onValueRender={(value: number) => `${Math.round(value)}`}
131
+
/>
132
+
</>
133
+
)}
97
134
</FormItem>
98
135
);
99
136
}
100
137
101
138
function String({ ext, name, setting, disabled }: SettingsProps) {
102
-
const { FormItem, FormText, TextInput } = CommonComponents;
103
-
const { value, displayName, description } = useConfigEntry<string>(
104
-
ext.uniqueId,
105
-
name
106
-
);
139
+
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
107
140
108
141
return (
109
142
<FormItem className={Margins.marginTop20} title={displayName}>
110
-
{description && (
111
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
112
-
)}
143
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
113
144
<TextInput
114
145
value={value ?? ""}
115
-
onChange={(value: string) => {
116
-
if (disabled) return;
117
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
118
-
}}
146
+
disabled={disabled}
147
+
onChange={(value: string) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)}
119
148
/>
120
149
</FormItem>
121
150
);
122
151
}
123
152
124
153
function MultilineString({ ext, name, setting, disabled }: SettingsProps) {
125
-
const { FormItem, FormText, TextArea } = CommonComponents;
126
-
const { value, displayName, description } = useConfigEntry<string>(
127
-
ext.uniqueId,
128
-
name
129
-
);
154
+
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
130
155
131
156
return (
132
157
<FormItem className={Margins.marginTop20} title={displayName}>
133
-
{description && (
134
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
135
-
)}
158
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
136
159
<TextArea
137
160
rows={5}
138
161
value={value ?? ""}
162
+
disabled={disabled}
139
163
className={"moonbase-resizeable"}
140
-
onChange={(value: string) => {
141
-
if (disabled) return;
142
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
143
-
}}
164
+
onChange={(value: string) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)}
144
165
/>
145
166
</FormItem>
146
167
);
147
168
}
148
169
149
170
function Select({ ext, name, setting, disabled }: SettingsProps) {
150
-
const { FormItem, FormText, SingleSelect } = CommonComponents;
151
-
const { value, displayName, description } = useConfigEntry<string>(
152
-
ext.uniqueId,
153
-
name
154
-
);
171
+
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
155
172
156
173
const castedSetting = setting as SelectSettingType;
157
174
const options = castedSetting.options;
158
175
159
176
return (
160
177
<FormItem className={Margins.marginTop20} title={displayName}>
161
-
{description && (
162
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
163
-
)}
178
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
164
179
<SingleSelect
165
180
autofocus={false}
166
181
clearable={false}
167
182
value={value ?? ""}
168
-
options={options.map((o: SelectOption) =>
169
-
typeof o === "string" ? { value: o, label: o } : o
170
-
)}
183
+
options={options.map((o: SelectOption) => (typeof o === "string" ? { value: o, label: o } : o))}
171
184
onChange={(value: string) => {
172
185
if (disabled) return;
173
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
186
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
174
187
}}
175
188
/>
176
189
</FormItem>
···
178
191
}
179
192
180
193
function MultiSelect({ ext, name, setting, disabled }: SettingsProps) {
181
-
const { FormItem, FormText, Select, useVariableSelect, multiSelect } =
182
-
CommonComponents;
183
-
const { value, displayName, description } = useConfigEntry<string | string[]>(
184
-
ext.uniqueId,
185
-
name
186
-
);
194
+
const { value, displayName, description } = useConfigEntry<string | string[]>(ext.uniqueId, name);
187
195
188
196
const castedSetting = setting as MultiSelectSettingType;
189
197
const options = castedSetting.options;
190
198
191
199
return (
192
200
<FormItem className={Margins.marginTop20} title={displayName}>
193
-
{description && (
194
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
195
-
)}
196
-
<Select
201
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
202
+
<DiscordSelect
197
203
autofocus={false}
198
204
clearable={false}
199
205
closeOnSelect={false}
200
-
options={options.map((o: SelectOption) =>
201
-
typeof o === "string" ? { value: o, label: o } : o
202
-
)}
206
+
options={options.map((o: SelectOption) => (typeof o === "string" ? { value: o, label: o } : o))}
203
207
{...useVariableSelect({
204
208
onSelectInteraction: multiSelect,
205
-
value: new Set(Array.isArray(value) ? value : [value]),
209
+
value: value == null ? new Set() : new Set(Array.isArray(value) ? value : [value]),
206
210
onChange: (value: string) => {
207
211
if (disabled) return;
208
-
MoonbaseSettingsStore.setExtensionConfig(
209
-
ext.uniqueId,
210
-
name,
211
-
Array.from(value)
212
-
);
212
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, Array.from(value));
213
213
}
214
214
})}
215
215
/>
···
217
217
);
218
218
}
219
219
220
-
const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0]
221
-
.exports;
222
-
const CircleXIcon = CommonComponents.CircleXIcon;
223
-
function RemoveEntryButton({
224
-
onClick,
225
-
disabled
226
-
}: {
227
-
onClick: () => void;
228
-
disabled: boolean;
229
-
}) {
230
-
const { Tooltip, Clickable } = CommonComponents;
220
+
function RemoveEntryButton({ onClick, disabled }: { onClick: () => void; disabled: boolean }) {
231
221
return (
232
-
<div className={RemoveButtonClasses.removeButtonContainer}>
222
+
<div className={GuildSettingsRoleEditClasses.removeButtonContainer}>
233
223
<Tooltip text="Remove entry" position="top">
234
224
{(props: any) => (
235
-
<Clickable
236
-
{...props}
237
-
className={RemoveButtonClasses.removeButton}
238
-
onClick={onClick}
239
-
>
225
+
<Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}>
240
226
<CircleXIcon width={16} height={16} />
241
227
</Clickable>
242
228
)}
···
246
232
}
247
233
248
234
function List({ ext, name, setting, disabled }: SettingsProps) {
249
-
const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents;
250
-
const { value, displayName, description } = useConfigEntry<string[]>(
251
-
ext.uniqueId,
252
-
name
253
-
);
235
+
const { value, displayName, description } = useConfigEntry<string[]>(ext.uniqueId, name);
254
236
255
237
const entries = value ?? [];
256
-
const updateConfig = () =>
257
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, entries);
238
+
const updateConfig = () => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, entries);
258
239
259
240
return (
260
241
<FormItem className={Margins.marginTop20} title={displayName}>
261
-
{description && (
262
-
<FormText className={Margins.marginBottom4}>{description}</FormText>
263
-
)}
242
+
{description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>}
264
243
<Flex direction={Flex.Direction.VERTICAL}>
265
244
{entries.map((val, i) => (
266
245
// FIXME: stylesheets
···
312
291
}
313
292
314
293
function Dictionary({ ext, name, setting, disabled }: SettingsProps) {
315
-
const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents;
316
-
const { value, displayName, description } = useConfigEntry<
317
-
Record<string, string>
318
-
>(ext.uniqueId, name);
294
+
const { value, displayName, description } = useConfigEntry<Record<string, string>>(ext.uniqueId, name);
319
295
320
296
const entries = Object.entries(value ?? {});
321
-
const updateConfig = () =>
322
-
MoonbaseSettingsStore.setExtensionConfig(
323
-
ext.uniqueId,
324
-
name,
325
-
Object.fromEntries(entries)
326
-
);
297
+
const updateConfig = () => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, Object.fromEntries(entries));
327
298
328
299
return (
329
300
<FormItem className={Margins.marginTop20} title={displayName}>
330
-
{description && (
331
-
<FormText className={Margins.marginBottom4}>{description}</FormText>
332
-
)}
301
+
{description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>}
333
302
<Flex direction={Flex.Direction.VERTICAL}>
334
303
{entries.map(([key, val], i) => (
335
304
// FIXME: stylesheets
···
389
358
);
390
359
}
391
360
361
+
function Custom({ ext, name, setting, disabled }: SettingsProps) {
362
+
const { value, displayName } = useConfigEntry<any>(ext.uniqueId, name);
363
+
364
+
const { component: Component } = useStateFromStores(
365
+
[MoonbaseSettingsStore],
366
+
() => {
367
+
return {
368
+
component: MoonbaseSettingsStore.getExtensionConfigComponent(ext.id, name)
369
+
};
370
+
},
371
+
[ext.uniqueId, name]
372
+
);
373
+
374
+
if (Component == null) {
375
+
return (
376
+
<Text variant="text-md/normal">{`Custom setting "${displayName}" is missing a component. Perhaps the extension is not installed?`}</Text>
377
+
);
378
+
}
379
+
380
+
return (
381
+
<ErrorBoundary>
382
+
<Component value={value} setValue={(value) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)} />
383
+
</ErrorBoundary>
384
+
);
385
+
}
386
+
392
387
function Setting({ ext, name, setting, disabled }: SettingsProps) {
393
388
const elements: Partial<Record<ExtensionSettingType, SettingsComponent>> = {
394
389
[ExtensionSettingType.Boolean]: Boolean,
···
398
393
[ExtensionSettingType.Select]: Select,
399
394
[ExtensionSettingType.MultiSelect]: MultiSelect,
400
395
[ExtensionSettingType.List]: List,
401
-
[ExtensionSettingType.Dictionary]: Dictionary
396
+
[ExtensionSettingType.Dictionary]: Dictionary,
397
+
[ExtensionSettingType.Custom]: Custom
402
398
};
403
399
const element = elements[setting.type];
404
400
if (element == null) return <></>;
···
406
402
}
407
403
408
404
export default function Settings({ ext }: { ext: MoonbaseExtension }) {
409
-
const { Flex } = CommonComponents;
410
405
return (
411
406
<Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}>
412
-
{Object.entries(ext.manifest.settings!).map(([name, setting]) => (
407
+
{Object.entries(ext.settingsOverride ?? ext.manifest.settings!).map(([name, setting]) => (
413
408
<Setting
414
409
ext={ext}
415
410
key={name}
+28
-29
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
+28
-29
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
···
1
-
import React from "@moonlight-mod/wp/common_react";
2
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
3
-
import { Text, TabBar } from "@moonlight-mod/wp/common_components";
4
-
import * as Flux from "@moonlight-mod/wp/common_flux";
1
+
import React from "@moonlight-mod/wp/react";
2
+
import { Text, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
3
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
5
4
import { UserSettingsModalStore } from "@moonlight-mod/wp/common_stores";
6
5
7
6
import ExtensionsPage from "./extensions";
8
7
import ConfigPage from "./config";
9
-
10
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
11
-
12
-
const { Divider } = spacepack.findByCode(".forumOrHome]:")[0].exports.Z;
13
-
const TitleBarClasses = spacepack.findByCode("iconWrapper:", "children:")[0]
14
-
.exports;
15
-
const TabBarClasses = spacepack.findByCode("nowPlayingColumn:")[0].exports;
16
-
const { setSection, clearSubsection } = spacepack.findByExports(
17
-
"setSection",
18
-
"clearSubsection"
19
-
)[0].exports.Z;
8
+
import AboutPage from "./about";
9
+
import Update from "./update";
10
+
import RestartAdviceMessage from "./RestartAdvice";
11
+
import { Divider } from "@moonlight-mod/wp/discord/components/common/BaseHeaderBar";
12
+
import HeaderBarClasses from "@moonlight-mod/wp/discord/components/common/HeaderBar.css";
13
+
import PeoplePageClasses from "@moonlight-mod/wp/discord/modules/people/web/PeoplePage.css";
14
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
15
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
20
16
21
17
export const pages: {
22
18
id: string;
···
32
28
id: "config",
33
29
name: "Config",
34
30
element: ConfigPage
31
+
},
32
+
{
33
+
id: "about",
34
+
name: "About",
35
+
element: AboutPage
35
36
}
36
37
];
37
38
38
39
export function Moonbase(props: { initialTab?: number } = {}) {
39
-
const subsection = Flux.useStateFromStores(
40
-
[UserSettingsModalStore],
41
-
() => UserSettingsModalStore.getSubsection() ?? 0
42
-
);
40
+
const subsection = useStateFromStores([UserSettingsModalStore], () => UserSettingsModalStore.getSubsection() ?? 0);
43
41
const setSubsection = React.useCallback(
44
42
(to: string) => {
45
-
if (subsection !== to) setSection("moonbase", to);
43
+
if (subsection !== to) UserSettingsModalActionCreators.setSection("moonbase", to);
46
44
},
47
45
[subsection]
48
46
);
···
50
48
React.useEffect(
51
49
() => () => {
52
50
// Normally there's an onSettingsClose prop you can set but we don't expose it and I don't care enough to add support for it right now
53
-
clearSubsection("moonbase");
51
+
UserSettingsModalActionCreators.clearSubsection("moonbase");
54
52
},
55
53
[]
56
54
);
57
55
58
56
return (
59
57
<>
60
-
<div className={`${TitleBarClasses.children} ${Margins.marginBottom20}`}>
61
-
<Text
62
-
className={TitleBarClasses.titleWrapper}
63
-
variant="heading-lg/semibold"
64
-
tag="h2"
65
-
>
58
+
<div className={`${HeaderBarClasses.children} ${Margins.marginBottom20}`}>
59
+
<Text className={HeaderBarClasses.titleWrapper} variant="heading-lg/semibold" tag="h2">
66
60
Moonbase
67
61
</Text>
68
62
<Divider />
···
70
64
selectedItem={subsection}
71
65
onItemSelect={setSubsection}
72
66
type="top-pill"
73
-
className={TabBarClasses.tabBar}
67
+
className={PeoplePageClasses.tabBar}
74
68
>
75
69
{pages.map((page, i) => (
76
-
<TabBar.Item key={page.id} id={i} className={TabBarClasses.item}>
70
+
<TabBar.Item key={page.id} id={i} className={PeoplePageClasses.item}>
77
71
{page.name}
78
72
</TabBar.Item>
79
73
))}
80
74
</TabBar>
81
75
</div>
82
76
77
+
<RestartAdviceMessage />
78
+
<Update />
79
+
83
80
{React.createElement(pages[subsection].element)}
84
81
</>
85
82
);
86
83
}
84
+
85
+
export { RestartAdviceMessage, Update };
+124
packages/core-extensions/src/moonbase/webpackModules/ui/update.tsx
+124
packages/core-extensions/src/moonbase/webpackModules/ui/update.tsx
···
1
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
2
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
3
+
import React from "@moonlight-mod/wp/react";
4
+
import { UpdateState } from "../../types";
5
+
import HelpMessage from "./HelpMessage";
6
+
import { MoonlightBranch } from "@moonlight-mod/types";
7
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
8
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
9
+
import {
10
+
Button,
11
+
Text,
12
+
ModalRoot,
13
+
ModalSize,
14
+
ModalContent,
15
+
ModalHeader,
16
+
Heading,
17
+
ModalCloseButton,
18
+
openModal
19
+
} from "@moonlight-mod/wp/discord/components/common/index";
20
+
import MarkupClasses from "@moonlight-mod/wp/discord/modules/messages/web/Markup.css";
21
+
import ThemeDarkIcon from "@moonlight-mod/wp/moonbase_ThemeDarkIcon";
22
+
23
+
const strings: Record<UpdateState, string> = {
24
+
[UpdateState.Ready]: "A new version of moonlight is available.",
25
+
[UpdateState.Working]: "Updating moonlight...",
26
+
[UpdateState.Installed]: "Updated. Restart Discord to apply changes.",
27
+
[UpdateState.Failed]: "Failed to update moonlight. Please use the installer instead."
28
+
};
29
+
30
+
function MoonlightChangelog({
31
+
changelog,
32
+
version,
33
+
transitionState,
34
+
onClose
35
+
}: {
36
+
changelog: string;
37
+
version: string;
38
+
transitionState: number | null;
39
+
onClose: () => void;
40
+
}) {
41
+
return (
42
+
<ModalRoot transitionState={transitionState} size={ModalSize.DYNAMIC}>
43
+
<ModalHeader>
44
+
<Flex.Child grow={1} shrink={1}>
45
+
<Heading variant="heading-lg/semibold">moonlight</Heading>
46
+
<Text variant="text-xs/normal">{version}</Text>
47
+
</Flex.Child>
48
+
49
+
<Flex.Child grow={0}>
50
+
<ModalCloseButton onClick={onClose} />
51
+
</Flex.Child>
52
+
</ModalHeader>
53
+
54
+
<ModalContent>
55
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ padding: "1rem" }}>
56
+
{MarkupUtils.parse(changelog, true, {
57
+
allowHeading: true,
58
+
allowList: true,
59
+
allowLinks: true
60
+
})}
61
+
</Text>
62
+
</ModalContent>
63
+
</ModalRoot>
64
+
);
65
+
}
66
+
67
+
export default function Update() {
68
+
const [newVersion, state] = useStateFromStores([MoonbaseSettingsStore], () => [
69
+
MoonbaseSettingsStore.newVersion,
70
+
MoonbaseSettingsStore.updateState
71
+
]);
72
+
73
+
if (newVersion == null) return null;
74
+
75
+
return (
76
+
<HelpMessage text={strings[state]} className="moonbase-update-section" icon={ThemeDarkIcon}>
77
+
<div className="moonbase-help-message-buttons">
78
+
{moonlight.branch === MoonlightBranch.STABLE && (
79
+
<Button
80
+
look={Button.Looks.OUTLINED}
81
+
color={Button.Colors.CUSTOM}
82
+
size={Button.Sizes.TINY}
83
+
onClick={() => {
84
+
fetch(`https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/tags/${newVersion}/CHANGELOG.md`)
85
+
.then((r) => r.text())
86
+
.then((changelog) =>
87
+
openModal((modalProps) => {
88
+
return <MoonlightChangelog {...modalProps} changelog={changelog} version={newVersion} />;
89
+
})
90
+
);
91
+
}}
92
+
>
93
+
View changelog
94
+
</Button>
95
+
)}
96
+
97
+
{state === UpdateState.Installed && (
98
+
<Button
99
+
look={Button.Looks.OUTLINED}
100
+
color={Button.Colors.CUSTOM}
101
+
size={Button.Sizes.TINY}
102
+
onClick={() => {
103
+
MoonbaseSettingsStore.restartDiscord();
104
+
}}
105
+
>
106
+
Restart Discord
107
+
</Button>
108
+
)}
109
+
110
+
<Button
111
+
look={Button.Looks.OUTLINED}
112
+
color={Button.Colors.CUSTOM}
113
+
size={Button.Sizes.TINY}
114
+
disabled={state !== UpdateState.Ready}
115
+
onClick={() => {
116
+
MoonbaseSettingsStore.updateMoonlight();
117
+
}}
118
+
>
119
+
Update
120
+
</Button>
121
+
</div>
122
+
</HelpMessage>
123
+
);
124
+
}
+74
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
+74
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
···
1
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
3
+
import Notices from "@moonlight-mod/wp/notices_notices";
4
+
import { MoonlightBranch } from "@moonlight-mod/types";
5
+
import React from "@moonlight-mod/wp/react";
6
+
import ThemeDarkIcon from "@moonlight-mod/wp/moonbase_ThemeDarkIcon";
7
+
8
+
function plural(str: string, num: number) {
9
+
return `${str}${num > 1 ? "s" : ""}`;
10
+
}
11
+
12
+
function listener() {
13
+
if (
14
+
MoonbaseSettingsStore.shouldShowNotice &&
15
+
MoonbaseSettingsStore.getExtensionConfigRaw("moonbase", "updateBanner", true)
16
+
) {
17
+
MoonbaseSettingsStore.removeChangeListener(listener);
18
+
19
+
const version = MoonbaseSettingsStore.newVersion;
20
+
const extensionUpdateCount = Object.keys(MoonbaseSettingsStore.updates).length;
21
+
const hasExtensionUpdates = extensionUpdateCount > 0;
22
+
23
+
let message;
24
+
25
+
if (version != null) {
26
+
message =
27
+
moonlightNode.branch === MoonlightBranch.NIGHTLY
28
+
? `A new version of moonlight is available`
29
+
: `moonlight ${version} is available`;
30
+
}
31
+
32
+
if (hasExtensionUpdates) {
33
+
let concat = false;
34
+
if (message == null) {
35
+
message = "";
36
+
} else {
37
+
concat = true;
38
+
message += ", and ";
39
+
}
40
+
message += `${extensionUpdateCount} ${concat ? "" : "moonlight "}${plural(
41
+
"extension",
42
+
extensionUpdateCount
43
+
)} can be updated`;
44
+
}
45
+
46
+
if (message != null) message += ".";
47
+
48
+
Notices.addNotice({
49
+
element: (
50
+
<div className="moonbase-updates-notice_text-wrapper">
51
+
<ThemeDarkIcon size="sm" color="currentColor" />
52
+
{message}
53
+
</div>
54
+
),
55
+
color: "moonbase-updates-notice",
56
+
buttons: [
57
+
{
58
+
name: "Open Moonbase",
59
+
onClick: () => {
60
+
const { open } = spacepack.require("discord/actions/UserSettingsModalActionCreators").default;
61
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
62
+
open("moonbase-extensions");
63
+
} else {
64
+
open("moonbase", "0");
65
+
}
66
+
return true;
67
+
}
68
+
}
69
+
]
70
+
});
71
+
}
72
+
}
73
+
74
+
MoonbaseSettingsStore.addChangeListener(listener);
+5
packages/core-extensions/src/moonbase/wp.d.ts
+5
packages/core-extensions/src/moonbase/wp.d.ts
···
5
5
declare module "@moonlight-mod/wp/moonbase_stores" {
6
6
export * from "core-extensions/src/moonbase/webpackModules/stores";
7
7
}
8
+
9
+
declare module "@moonlight-mod/wp/moonbase_ThemeDarkIcon" {
10
+
import ThemeDarkIcon from "core-extensions/src/moonbase/webpackModules/ThemeDarkIcon";
11
+
export = ThemeDarkIcon;
12
+
}
+186
packages/core-extensions/src/nativeFixes/host.ts
+186
packages/core-extensions/src/nativeFixes/host.ts
···
1
+
import { app, nativeTheme } from "electron";
2
+
import * as path from "node:path";
3
+
import * as fs from "node:fs/promises";
4
+
import * as fsSync from "node:fs";
5
+
import { parseTarGzip } from "nanotar";
6
+
7
+
const logger = moonlightHost.getLogger("nativeFixes/host");
8
+
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
9
+
10
+
moonlightHost.events.on("window-created", function (browserWindow) {
11
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "devtoolsThemeFix") ?? true) {
12
+
browserWindow.webContents.on("devtools-opened", () => {
13
+
if (!nativeTheme.shouldUseDarkColors) return;
14
+
nativeTheme.themeSource = "light";
15
+
setTimeout(() => {
16
+
nativeTheme.themeSource = "dark";
17
+
}, 100);
18
+
});
19
+
}
20
+
});
21
+
22
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "disableRendererBackgrounding") ?? true) {
23
+
// Discord already disables UseEcoQoSForBackgroundProcess and some other
24
+
// related features
25
+
app.commandLine.appendSwitch("disable-renderer-backgrounding");
26
+
app.commandLine.appendSwitch("disable-backgrounding-occluded-windows");
27
+
28
+
// already added on Windows, but not on other operating systems
29
+
app.commandLine.appendSwitch("disable-background-timer-throttling");
30
+
}
31
+
32
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vulkan") ?? false) {
33
+
enabledFeatures.push("Vulkan", "DefaultANGLEVulkan", "VulkanFromANGLE");
34
+
}
35
+
36
+
if (process.platform === "linux") {
37
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxAutoscroll") ?? false) {
38
+
app.commandLine.appendSwitch("enable-blink-features", "MiddleClickAutoscroll");
39
+
}
40
+
41
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxSpeechDispatcher") ?? true) {
42
+
app.commandLine.appendSwitch("enable-speech-dispatcher");
43
+
}
44
+
45
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxHevcSupport") ?? true) {
46
+
enabledFeatures.push("PlatformHEVCDecoderSupport");
47
+
}
48
+
}
49
+
50
+
// NOTE: Only tested if this appears on Windows, it should appear on all when
51
+
// hardware acceleration is disabled
52
+
const noAccel = app.commandLine.hasSwitch("disable-gpu-compositing");
53
+
if ((moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) && !noAccel) {
54
+
if (process.platform === "linux") {
55
+
// These will eventually be renamed https://source.chromium.org/chromium/chromium/src/+/5482210941a94d70406b8da962426e4faca7fce4
56
+
enabledFeatures.push("VaapiVideoEncoder", "VaapiVideoDecoder", "VaapiVideoDecodeLinuxGL");
57
+
58
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapiIgnoreDriverChecks") ?? false)
59
+
enabledFeatures.push("VaapiIgnoreDriverChecks");
60
+
}
61
+
}
62
+
63
+
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].join(","));
64
+
65
+
if (process.platform === "linux" && moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxUpdater")) {
66
+
const exePath = app.getPath("exe");
67
+
const appName = path.basename(exePath);
68
+
const targetDir = path.dirname(exePath);
69
+
const { releaseChannel }: { releaseChannel: string } = JSON.parse(
70
+
fsSync.readFileSync(path.join(targetDir, "resources", "build_info.json"), "utf8")
71
+
);
72
+
73
+
const updaterModule = require(path.join(moonlightHost.asarPath, "app_bootstrap", "hostUpdater.js"));
74
+
const updater = updaterModule.constructor;
75
+
76
+
async function doUpdate(cb: (percent: number) => void) {
77
+
logger.debug("Extracting to", targetDir);
78
+
79
+
const exists = (path: string) =>
80
+
fs
81
+
.stat(path)
82
+
.then(() => true)
83
+
.catch(() => false);
84
+
85
+
const url = `https://discord.com/api/download/${releaseChannel}?platform=linux&format=tar.gz`;
86
+
const resp = await fetch(url, {
87
+
cache: "no-store"
88
+
});
89
+
90
+
const reader = resp.body!.getReader();
91
+
const contentLength = parseInt(resp.headers.get("Content-Length") ?? "0");
92
+
logger.info(`Expecting ${contentLength} bytes for the update`);
93
+
const bytes = new Uint8Array(contentLength);
94
+
let pos = 0;
95
+
let lastPercent = 0;
96
+
97
+
while (true) {
98
+
const { done, value } = await reader.read();
99
+
if (done) {
100
+
break;
101
+
} else {
102
+
bytes.set(value, pos);
103
+
pos += value.length;
104
+
105
+
const newPercent = Math.floor((pos / contentLength) * 100);
106
+
if (lastPercent !== newPercent) {
107
+
lastPercent = newPercent;
108
+
cb(newPercent);
109
+
}
110
+
}
111
+
}
112
+
113
+
const files = await parseTarGzip(bytes);
114
+
115
+
for (const file of files) {
116
+
if (!file.data) continue;
117
+
// @ts-expect-error What do you mean their own types are wrong
118
+
if (file.type !== "file") continue;
119
+
120
+
// Discord update files are inside of a main "Discord(PTB|Canary)" folder
121
+
const filePath = file.name.replace(`${appName}/`, "");
122
+
logger.info("Extracting", filePath);
123
+
124
+
let targetFilePath = path.join(targetDir, filePath);
125
+
if (filePath === "resources/app.asar") {
126
+
// You tried
127
+
targetFilePath = path.join(targetDir, "resources", "_app.asar");
128
+
} else if (filePath === appName || filePath === "chrome_crashpad_handler") {
129
+
// Can't write over the executable? Just move it! 4head
130
+
if (await exists(targetFilePath)) {
131
+
await fs.rename(targetFilePath, targetFilePath + ".bak");
132
+
await fs.unlink(targetFilePath + ".bak");
133
+
}
134
+
}
135
+
const targetFileDir = path.dirname(targetFilePath);
136
+
137
+
if (!(await exists(targetFileDir))) await fs.mkdir(targetFileDir, { recursive: true });
138
+
await fs.writeFile(targetFilePath, file.data);
139
+
140
+
const mode = file.attrs?.mode;
141
+
if (mode != null) {
142
+
// Not sure why this slice is needed
143
+
await fs.chmod(targetFilePath, mode.slice(-3));
144
+
}
145
+
}
146
+
147
+
logger.debug("Done updating");
148
+
}
149
+
150
+
const realEmit = updater.prototype.emit;
151
+
updater.prototype.emit = function (event: string, ...args: any[]) {
152
+
// Arrow functions don't bind `this` :D
153
+
const call = (event: string, ...args: any[]) => realEmit.call(this, event, ...args);
154
+
155
+
if (event === "update-manually") {
156
+
const latestVerStr: string = args[0];
157
+
logger.debug("update-manually called, intercepting", latestVerStr);
158
+
call("update-available");
159
+
160
+
(async () => {
161
+
try {
162
+
await doUpdate((progress) => {
163
+
call("update-progress", progress);
164
+
});
165
+
// Copied from the win32 updater
166
+
this.updateVersion = latestVerStr;
167
+
call(
168
+
"update-downloaded",
169
+
{},
170
+
releaseChannel,
171
+
latestVerStr,
172
+
new Date(),
173
+
this.updateUrl,
174
+
this.quitAndInstall.bind(this)
175
+
);
176
+
} catch (e) {
177
+
logger.error("Error updating", e);
178
+
}
179
+
})();
180
+
181
+
return this;
182
+
} else {
183
+
return realEmit.call(this, event, ...args);
184
+
}
185
+
};
186
+
}
+77
packages/core-extensions/src/nativeFixes/manifest.json
+77
packages/core-extensions/src/nativeFixes/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "nativeFixes",
4
+
"meta": {
5
+
"name": "Native Fixes",
6
+
"tagline": "Various configurable fixes for Discord and Electron",
7
+
"authors": ["Cynosphere", "adryd", "NotNite"],
8
+
"tags": ["fixes"]
9
+
},
10
+
"environment": "desktop",
11
+
"settings": {
12
+
"devtoolsThemeFix": {
13
+
"advice": "restart",
14
+
"displayName": "Devtools Theme Fix",
15
+
"description": "Temporary workaround for devtools defaulting to light theme on Electron 32",
16
+
"type": "boolean",
17
+
"default": true
18
+
},
19
+
"disableRendererBackgrounding": {
20
+
"advice": "restart",
21
+
"displayName": "Disable Renderer Backgrounding",
22
+
"description": "This is enabled by default as a power saving measure, but it breaks screensharing and websocket connections fairly often",
23
+
"type": "boolean",
24
+
"default": true
25
+
},
26
+
"vulkan": {
27
+
"advice": "restart",
28
+
"displayName": "Enable Vulkan renderer",
29
+
"description": "Uses the Vulkan backend for rendering",
30
+
"type": "boolean",
31
+
"default": false
32
+
},
33
+
"linuxAutoscroll": {
34
+
"advice": "restart",
35
+
"displayName": "Enable middle click autoscroll on Linux",
36
+
"description": "Requires manual configuration of your system to disable middle click paste, has no effect on other operating systems",
37
+
"type": "boolean",
38
+
"default": false
39
+
},
40
+
"linuxSpeechDispatcher": {
41
+
"advice": "restart",
42
+
"displayName": "Enable speech-dispatcher for TTS on Linux",
43
+
"description": "Fixes text-to-speech. Has no effect on other operating systems",
44
+
"type": "boolean",
45
+
"default": true
46
+
},
47
+
"vaapi": {
48
+
"advice": "restart",
49
+
"displayName": "Enable VAAPI features on Linux",
50
+
"description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems",
51
+
"type": "boolean",
52
+
"default": true
53
+
},
54
+
"vaapiIgnoreDriverChecks": {
55
+
"advice": "restart",
56
+
"displayName": "Ignore VAAPI driver checks on Linux",
57
+
"description": "Forces hardware video acceleration on some graphics drivers at the cost of stability. Has no effect on other operating systems",
58
+
"type": "boolean",
59
+
"default": false
60
+
},
61
+
"linuxUpdater": {
62
+
"advice": "restart",
63
+
"displayName": "Linux Updater",
64
+
"description": "Actually implements updating Discord on Linux. Has no effect on other operating systems",
65
+
"type": "boolean",
66
+
"default": false
67
+
},
68
+
"linuxHevcSupport": {
69
+
"advice": "restart",
70
+
"displayName": "HEVC support on Linux",
71
+
"description": "You might also need to enable Vulkan renderer. Has no effect on other operating systems",
72
+
"type": "boolean",
73
+
"default": true
74
+
}
75
+
},
76
+
"apiLevel": 2
77
+
}
+4
-4
packages/core-extensions/src/noHideToken/index.ts
+4
-4
packages/core-extensions/src/noHideToken/index.ts
···
1
-
import { Patch } from "types/src";
1
+
import { Patch } from "@moonlight-mod/types";
2
2
3
3
export const patches: Patch[] = [
4
4
{
5
-
find: "hideToken:function",
5
+
find: "hideToken:()=>",
6
6
replace: {
7
-
match: /(?<=hideToken:function\(\){)/,
8
-
replacement: `return()=>{};`
7
+
match: /hideToken:\(\)=>.+?,/,
8
+
replacement: `hideToken:()=>{},`
9
9
}
10
10
}
11
11
];
+4
-1
packages/core-extensions/src/noHideToken/manifest.json
+4
-1
packages/core-extensions/src/noHideToken/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "noHideToken",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "No Hide Token",
5
-
"tagline": "Disables removal of token from localStorage when opening dev tools",
7
+
"tagline": "Prevents you from being logged-out on hard-crash",
8
+
"description": "Prevents you from being logged-out on hard-crash by disabling removal of token from localStorage when opening dev tools",
6
9
"authors": ["adryd"],
7
10
"tags": ["dangerZone", "development"]
8
11
}
-15
packages/core-extensions/src/noTrack/host.ts
-15
packages/core-extensions/src/noTrack/host.ts
···
1
-
import { BrowserWindow } from "electron";
2
-
3
-
moonlightHost.events.on("window-created", (window: BrowserWindow) => {
4
-
window.webContents.session.webRequest.onBeforeRequest(
5
-
{
6
-
urls: [
7
-
"https://*.discord.com/api/v*/science",
8
-
"https://*.discord.com/api/v*/metrics"
9
-
]
10
-
},
11
-
function (details, callback) {
12
-
callback({ cancel: true });
13
-
}
14
-
);
15
-
});
+3
-3
packages/core-extensions/src/noTrack/index.ts
+3
-3
packages/core-extensions/src/noTrack/index.ts
···
2
2
3
3
export const patches: Patch[] = [
4
4
{
5
-
find: "analyticsTrackingStoreMaker:function",
5
+
find: "analyticsTrackingStoreMaker:()=>",
6
6
replace: {
7
-
match: /analyticsTrackingStoreMaker:function\(\){return .+?}/,
8
-
replacement: "analyticsTrackingStoreMaker:function(){return ()=>{}}"
7
+
match: /analyticsTrackingStoreMaker:\(\)=>.+?,/,
8
+
replacement: "analyticsTrackingStoreMaker:()=>()=>{},"
9
9
}
10
10
},
11
11
{
+9
-1
packages/core-extensions/src/noTrack/manifest.json
+9
-1
packages/core-extensions/src/noTrack/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "noTrack",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "No Track",
5
7
"tagline": "Disables /api/science and analytics",
6
8
"authors": ["Cynosphere", "NotNite"],
7
9
"tags": ["privacy"]
8
-
}
10
+
},
11
+
"blocked": [
12
+
"https://*.discord.com/api/v*/science",
13
+
"https://*.discord.com/api/v*/metrics",
14
+
"https://*.discordapp.com/api/v*/science",
15
+
"https://*.discordapp.com/api/v*/metrics"
16
+
]
9
17
}
+42
packages/core-extensions/src/notices/index.ts
+42
packages/core-extensions/src/notices/index.ts
···
1
+
import type { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
{
5
+
find: ".GUILD_RAID_NOTIFICATION:",
6
+
replace: {
7
+
match: /(?<=return(\(0,.\.jsx\))\(.+?\);)case .{1,2}\..{1,3}\.GUILD_RAID_NOTIFICATION:/,
8
+
replacement: (orig, createElement) =>
9
+
`case "__moonlight_notice":return${createElement}(require("notices_component").default,{});${orig}`
10
+
}
11
+
},
12
+
{
13
+
find: '"NoticeStore"',
14
+
replace: [
15
+
{
16
+
match: /\[.{1,2}\..{1,3}\.CONNECT_SPOTIFY\]:{/,
17
+
replacement: (orig: string) =>
18
+
`__moonlight_notice:{predicate:()=>require("notices_notices").default.shouldShowNotice()},${orig}`
19
+
},
20
+
{
21
+
match: /=\[(.{1,2}\..{1,3}\.QUARANTINED,)/g,
22
+
replacement: (_, orig) => `=["__moonlight_notice",${orig}`
23
+
}
24
+
]
25
+
}
26
+
];
27
+
28
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
29
+
notices: {
30
+
dependencies: [{ id: "discord/packages/flux" }, { id: "discord/Dispatcher" }]
31
+
},
32
+
33
+
component: {
34
+
dependencies: [
35
+
{ id: "react" },
36
+
{ id: "discord/Dispatcher" },
37
+
{ id: "discord/components/common/index" },
38
+
{ id: "discord/packages/flux" },
39
+
{ ext: "notices", id: "notices" }
40
+
]
41
+
}
42
+
};
+11
packages/core-extensions/src/notices/manifest.json
+11
packages/core-extensions/src/notices/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "notices",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "Notices",
7
+
"tagline": "An API for adding notices at the top of the page",
8
+
"authors": ["Cynosphere", "NotNite"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+50
packages/core-extensions/src/notices/webpackModules/component.tsx
+50
packages/core-extensions/src/notices/webpackModules/component.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
3
+
import { Notice, NoticeCloseButton, PrimaryCTANoticeButton } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
5
+
import NoticesStore from "@moonlight-mod/wp/notices_notices";
6
+
import type { Notice as NoticeType } from "@moonlight-mod/types/coreExtensions/notices";
7
+
8
+
function popAndDismiss(notice: NoticeType) {
9
+
NoticesStore.popNotice();
10
+
if (notice?.onDismiss) {
11
+
notice.onDismiss();
12
+
}
13
+
if (!NoticesStore.shouldShowNotice()) {
14
+
Dispatcher.dispatch({
15
+
type: "NOTICE_DISMISS"
16
+
});
17
+
}
18
+
}
19
+
20
+
export default function UpdateNotice() {
21
+
const { notice } = useStateFromStoresObject([NoticesStore], () => ({
22
+
notice: NoticesStore.getCurrentNotice()
23
+
}));
24
+
25
+
if (notice == null) return <></>;
26
+
27
+
return (
28
+
<Notice color={notice.color}>
29
+
{notice.element}
30
+
31
+
{(notice.showClose ?? true) && (
32
+
<NoticeCloseButton onClick={() => popAndDismiss(notice)} noticeType="__moonlight_notice" />
33
+
)}
34
+
35
+
{(notice.buttons ?? []).map((button) => (
36
+
<PrimaryCTANoticeButton
37
+
key={button.name}
38
+
onClick={() => {
39
+
if (button.onClick()) {
40
+
popAndDismiss(notice);
41
+
}
42
+
}}
43
+
noticeType="__moonlight_notice"
44
+
>
45
+
{button.name}
46
+
</PrimaryCTANoticeButton>
47
+
))}
48
+
</Notice>
49
+
);
50
+
}
+55
packages/core-extensions/src/notices/webpackModules/notices.ts
+55
packages/core-extensions/src/notices/webpackModules/notices.ts
···
1
+
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
2
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
3
+
import type { Notice, Notices } from "@moonlight-mod/types/coreExtensions/notices";
4
+
5
+
// very lazy way of doing this, FIXME
6
+
let open = false;
7
+
8
+
class NoticesStore extends Store<any> {
9
+
private notices: Notice[] = [];
10
+
11
+
constructor() {
12
+
super(Dispatcher);
13
+
}
14
+
15
+
addNotice(notice: Notice) {
16
+
this.notices.push(notice);
17
+
if (open && this.notices.length !== 0) {
18
+
Dispatcher.dispatch({
19
+
type: "NOTICE_SHOW",
20
+
notice: { type: "__moonlight_notice" }
21
+
});
22
+
}
23
+
this.emitChange();
24
+
}
25
+
26
+
popNotice() {
27
+
this.notices.shift();
28
+
this.emitChange();
29
+
}
30
+
31
+
getCurrentNotice() {
32
+
return this.notices.length > 0 ? this.notices[0] : null;
33
+
}
34
+
35
+
shouldShowNotice() {
36
+
return this.notices.length > 0;
37
+
}
38
+
}
39
+
40
+
const store: Notices = new NoticesStore();
41
+
42
+
function showNotice() {
43
+
open = true;
44
+
if (store.shouldShowNotice()) {
45
+
Dispatcher.dispatch({
46
+
type: "NOTICE_SHOW",
47
+
notice: { type: "__moonlight_notice" }
48
+
});
49
+
}
50
+
}
51
+
52
+
Dispatcher.subscribe("CONNECTION_OPEN", showNotice);
53
+
Dispatcher.subscribe("CONNECTION_OPEN_SUPPLEMENTAL", showNotice);
54
+
55
+
export default store;
+98
-40
packages/core-extensions/src/quietLoggers/index.ts
+98
-40
packages/core-extensions/src/quietLoggers/index.ts
···
1
1
import { Patch } from "@moonlight-mod/types";
2
2
3
3
const notXssDefensesOnly = () =>
4
-
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ??
5
-
false) === false;
4
+
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ?? false) === false;
5
+
6
+
const silenceDiscordLogger = moonlight.getConfigOption<boolean>("quietLoggers", "silenceDiscordLogger") ?? false;
6
7
7
8
// These patches MUST run before the simple patches, these are to remove loggers
8
9
// that end up causing syntax errors by the normal patch
9
10
const loggerFixes: Patch[] = [
10
11
{
11
-
find: '"./ggsans-800-extrabolditalic.woff2":',
12
+
find: '"./gg-sans/ggsans-800-extrabolditalic.woff2":',
12
13
replace: {
13
-
match: /throw .+?,./,
14
-
replacement: "return{}"
14
+
match: /var .=Error.+?;throw .+?,./,
15
+
replacement: ""
15
16
}
16
17
},
17
18
{
18
19
find: '("GatewaySocket")',
19
20
replace: {
20
-
match: /.\.(info|log)(\(.+?\))(;|,)/g,
21
-
replacement: (_, type, body, trail) => `(()=>{})${body}${trail}`
21
+
match: /\i\.(log|info)\(/g,
22
+
replacement: "(()=>{})("
23
+
}
24
+
},
25
+
{
26
+
find: '"_connect called with already existing websocket"',
27
+
replace: {
28
+
match: /\i\.(log|info|verbose)\(/g,
29
+
replacement: "(()=>{})("
22
30
}
23
31
}
24
32
];
···
29
37
// Patches to simply remove a logger call
30
38
const stubPatches = [
31
39
// "sh" is not a valid locale.
40
+
["is not a valid locale", /void \i\.error\(""\.concat\(\i," is not a valid locale\."\)\)/g],
41
+
['"[BUILD INFO] Release Channel: "', /new \i\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?\)\),/],
42
+
['.APP_NATIVE_CRASH,"Storage"', /console\.log\("AppCrashedFatalReport lastCrash:",\i,\i\);/],
43
+
['.APP_NATIVE_CRASH,"Storage"', 'void console.log("AppCrashedFatalReport: getLastCrash not supported.")'],
44
+
['"[NATIVE INFO] ', /new \i\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
45
+
['"Spellchecker"', /\i\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
46
+
['throw Error("Messages are still loading.");', /console\.warn\("Unsupported Locale",\i\),/],
47
+
["}_dispatchWithDevtools(", /\i\.totalTime>\i&&\i\.verbose\(.+?\);/],
48
+
['"NativeDispatchUtils"', /null==\i&&\i\.warn\("Tried getting Dispatch instance before instantiated"\),/],
32
49
[
33
-
"is not a valid locale",
34
-
/(.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g
50
+
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
51
+
/\i\.has\(\i\.type\)&&\i\.log\(.+?\.type\)\),/
35
52
],
36
-
['="RunningGameStore"', /.\.info\("games",{.+?}\),/],
53
+
['console.warn("Window state not initialized"', /console\.warn\("Window state not initialized",\i\),/],
54
+
['.name="MaxListenersExceededWarning",', /(?<=\.length),\i\(\i\)/],
37
55
[
38
-
'"[BUILD INFO] Release Channel: "',
39
-
/new .{1,2}\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?"\)\),/
56
+
'"The answer for life the universe and everything is:"',
57
+
/\i\.info\("The answer for life the universe and everything is:",\i\),/
40
58
],
41
59
[
42
-
'.APP_NATIVE_CRASH,"Storage"',
43
-
/console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/
60
+
'"isLibdiscoreBlockedDomainsEnabled called but libdiscore is not loaded"',
61
+
/,\i\.verbose\("isLibdiscoreBlockedDomainsEnabledThisSession: ".concat\(\i\)\)/
44
62
],
45
63
[
46
-
'.APP_NATIVE_CRASH,"Storage"',
47
-
'console.log("AppCrashedFatalReport: getLastCrash not supported.");'
48
-
],
49
-
['"[NATIVE INFO] ', /new .{1,2}\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
50
-
['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
51
-
[
52
-
'throw Error("Messages are still loading.");',
53
-
/console\.warn\("Unsupported Locale",.\),/
64
+
'"Unable to determine render window for element"',
65
+
/console\.warn\("Unable to determine render window for element",\i\),/
54
66
],
55
-
["}_dispatchWithDevtools(", /.\.totalTime>100&&.\.verbose\(.+?\);/],
56
67
[
57
-
'"NativeDispatchUtils"',
58
-
/null==.&&.\.warn\("Tried getting Dispatch instance before instantiated"\),/
68
+
'"Unable to determine render window for element"',
69
+
/console\.warn\('Unable to find element constructor "'\.concat\(\i,'" in'\),\i\),/
59
70
],
60
-
['("DatabaseManager")', /.\.log\("removing database \(user: ".+?\)\),/],
61
71
[
62
-
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
63
-
/.\.has\(.\.type\)&&.\.log\(.+?\.type\)\),/
72
+
'"[PostMessageTransport] Protocol error: event data should be an Array!"',
73
+
/void console\.warn\("\[PostMessageTransport] Protocol error: event data should be an Array!"\)/
64
74
],
65
75
[
66
-
'console.warn("Window state not initialized"',
67
-
/console\.warn\("Window state not initialized",.\),/
76
+
'("ComponentDispatchUtils")',
77
+
/new \i\.Z\("ComponentDispatchUtils"\)\.warn\("ComponentDispatch\.resubscribe: Resubscribe without existing subscription",\i\),/
68
78
]
69
79
];
70
80
81
+
const stripLoggers = [
82
+
'("OverlayRenderStore")',
83
+
'("FetchBlockedDomain")',
84
+
'="UserSettingsProtoLastWriteTimes",',
85
+
'("MessageActionCreators")',
86
+
'("Routing/Utils")',
87
+
'("DatabaseManager")',
88
+
'("KeyboardLayoutMapUtils")',
89
+
'("ChannelMessages")',
90
+
'("MessageQueue")',
91
+
'("RTCLatencyTestManager")',
92
+
'("OverlayStoreV3")',
93
+
'("OverlayBridgeStore")',
94
+
'("AuthenticationStore")',
95
+
'("ConnectionStore")',
96
+
'"Dispatched INITIAL_GUILD "',
97
+
'"handleIdentify called"',
98
+
'("Spotify")'
99
+
];
100
+
71
101
const simplePatches = [
72
102
// Moment.js deprecation warnings
73
-
["suppressDeprecationWarnings=!1", "suppressDeprecationWarnings=!0"],
74
-
75
-
// Zustand related
76
-
[
77
-
/console\.warn\("\[DEPRECATED\] Please use `subscribeWithSelector` middleware"\)/g,
78
-
"/*$&*/"
79
-
],
80
-
["this.getDebugLogging()", "false"]
103
+
["suppressDeprecationWarnings=!1", "suppressDeprecationWarnings=!0"]
81
104
] as { [0]: string | RegExp; [1]: string }[];
82
105
83
106
export const patches: Patch[] = [
84
107
{
85
-
find: ".Messages.XSSDefenses",
108
+
find: ".Messages.SELF_XSS_HEADER",
86
109
replace: {
87
-
match: /\(null!=.{1,2}&&"0\.0\.0"===.{1,2}\.remoteApp\.getVersion\(\)\)/,
110
+
match: /\(null!=\i&&"0\.0\.0"===\i\.remoteApp\.getVersion\(\)\)/,
88
111
replacement: "(true)"
89
112
}
90
113
},
114
+
{
115
+
find: '("ComponentDispatchUtils")',
116
+
replace: {
117
+
match:
118
+
/new \i\.Z\("ComponentDispatchUtils"\)\.warn\("ComponentDispatch\.subscribe: Attempting to add a duplicate listener",\i\)/,
119
+
replacement: "void 0"
120
+
},
121
+
prerequisite: notXssDefensesOnly
122
+
},
123
+
// Highlight.js deprecation warnings
124
+
{
125
+
find: "Deprecated as of",
126
+
replace: {
127
+
match: /console\./g,
128
+
replacement: "false&&console."
129
+
},
130
+
prerequisite: notXssDefensesOnly
131
+
},
132
+
// Discord's logger
133
+
{
134
+
find: "ฮฃ:",
135
+
replace: {
136
+
match: "for",
137
+
replacement: "return;for"
138
+
},
139
+
prerequisite: () => silenceDiscordLogger && notXssDefensesOnly()
140
+
},
91
141
...loggerFixes,
92
142
...stubPatches.map((patch) => ({
93
143
find: patch[0],
···
102
152
replace: {
103
153
match: patch[0],
104
154
replacement: patch[1]
155
+
},
156
+
prerequisite: notXssDefensesOnly
157
+
})),
158
+
...stripLoggers.map((find) => ({
159
+
find,
160
+
replace: {
161
+
match: /(\i|this\.logger)\.(log|warn|error|info|verbose)\(/g,
162
+
replacement: "(()=>{})("
105
163
},
106
164
prerequisite: notXssDefensesOnly
107
165
}))
+10
packages/core-extensions/src/quietLoggers/manifest.json
+10
packages/core-extensions/src/quietLoggers/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "quietLoggers",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Quiet Loggers",
5
7
"tagline": "Quiet errors on startup, and disable unnecesary loggers",
···
8
10
},
9
11
"settings": {
10
12
"xssDefensesOnly": {
13
+
"advice": "reload",
11
14
"displayName": "Only hide self-XSS",
12
15
"description": "Only disable self XSS prevention log",
16
+
"type": "boolean",
17
+
"default": false
18
+
},
19
+
"silenceDiscordLogger": {
20
+
"advice": "reload",
21
+
"displayName": "Silence Discord logger",
22
+
"description": "Hides all messages from Discord's logger (the logs that start with purple text in brackets)",
13
23
"type": "boolean",
14
24
"default": false
15
25
}
+75
packages/core-extensions/src/rocketship/host/permissions.ts
+75
packages/core-extensions/src/rocketship/host/permissions.ts
···
1
+
import type { BrowserWindow } from "electron";
2
+
3
+
type PermissionRequestHandler = (
4
+
webContents: Electron.WebContents,
5
+
permission: string,
6
+
callback: (permissionGranted: boolean) => void,
7
+
details: Electron.PermissionRequestHandlerHandlerDetails
8
+
) => void;
9
+
10
+
type PermissionCheckHandler = (
11
+
webContents: Electron.WebContents | null,
12
+
permission: string,
13
+
requestingOrigin: string,
14
+
details: Electron.PermissionCheckHandlerHandlerDetails
15
+
) => boolean;
16
+
17
+
moonlightHost.events.on("window-created", (window: BrowserWindow, isMainWindow: boolean) => {
18
+
if (!isMainWindow) return;
19
+
const windowSession = window.webContents.session;
20
+
21
+
// setPermissionRequestHandler
22
+
windowSession.setPermissionRequestHandler((webcontents, permission, callback, details) => {
23
+
let cbResult = false;
24
+
function fakeCallback(result: boolean) {
25
+
cbResult = result;
26
+
}
27
+
28
+
if (caughtPermissionRequestHandler) {
29
+
caughtPermissionRequestHandler(webcontents, permission, fakeCallback, details);
30
+
}
31
+
32
+
if (permission === "media" || permission === "display-capture") {
33
+
cbResult = true;
34
+
}
35
+
36
+
callback(cbResult);
37
+
});
38
+
39
+
let caughtPermissionRequestHandler: PermissionRequestHandler | undefined;
40
+
41
+
windowSession.setPermissionRequestHandler = function catchSetPermissionRequestHandler(
42
+
handler: (
43
+
webcontents: Electron.WebContents,
44
+
permission: string,
45
+
callback: (permissionGranted: boolean) => void
46
+
) => void
47
+
) {
48
+
caughtPermissionRequestHandler = handler;
49
+
};
50
+
51
+
// setPermissionCheckHandler
52
+
windowSession.setPermissionCheckHandler((webcontents, permission, requestingOrigin, details) => {
53
+
return false;
54
+
});
55
+
56
+
let caughtPermissionCheckHandler: PermissionCheckHandler | undefined;
57
+
58
+
windowSession.setPermissionCheckHandler((webcontents, permission, requestingOrigin, details) => {
59
+
let result = false;
60
+
61
+
if (caughtPermissionCheckHandler) {
62
+
result = caughtPermissionCheckHandler(webcontents, permission, requestingOrigin, details);
63
+
}
64
+
65
+
if (permission === "media" || permission === "display-capture") {
66
+
result = true;
67
+
}
68
+
69
+
return result;
70
+
});
71
+
72
+
windowSession.setPermissionCheckHandler = function catchSetPermissionCheckHandler(handler: PermissionCheckHandler) {
73
+
caughtPermissionCheckHandler = handler;
74
+
};
75
+
});
+27
packages/core-extensions/src/rocketship/host/types.ts
+27
packages/core-extensions/src/rocketship/host/types.ts
···
1
+
// https://github.com/Vencord/venmic/blob/d737ef33eaae7a73d03ec02673e008cf0243434d/lib/module.d.ts
2
+
type DefaultProps = "node.name" | "application.name";
3
+
4
+
type LiteralUnion<LiteralType, BaseType extends string> = LiteralType | (BaseType & Record<never, never>);
5
+
6
+
type Optional<Type, Key extends keyof Type> = Partial<Pick<Type, Key>> & Omit<Type, Key>;
7
+
8
+
export type Node<T extends string = never> = Record<LiteralUnion<T, string>, string>;
9
+
10
+
export interface LinkData {
11
+
include: Node[];
12
+
exclude: Node[];
13
+
14
+
ignore_devices?: boolean;
15
+
16
+
only_speakers?: boolean;
17
+
only_default_speakers?: boolean;
18
+
19
+
workaround?: Node[];
20
+
}
21
+
22
+
export interface PatchBay {
23
+
unlink(): void;
24
+
25
+
list<T extends string = DefaultProps>(props?: T[]): Node<T>[];
26
+
link(data: Optional<LinkData, "exclude"> | Optional<LinkData, "include">): boolean;
27
+
}
+69
packages/core-extensions/src/rocketship/host/venmic.ts
+69
packages/core-extensions/src/rocketship/host/venmic.ts
···
1
+
import type { BrowserWindow } from "electron";
2
+
import { app, desktopCapturer } from "electron";
3
+
import path from "node:path";
4
+
import { type PatchBay } from "./types";
5
+
6
+
const logger = moonlightHost.getLogger("rocketship");
7
+
8
+
function getPatchbay() {
9
+
try {
10
+
const venmic = require(path.join(path.dirname(moonlightHost.asarPath), "..", "venmic.node")) as {
11
+
PatchBay: new () => PatchBay;
12
+
};
13
+
const patchbay = new venmic.PatchBay();
14
+
return patchbay;
15
+
} catch (error) {
16
+
logger.error("Failed to load venmic.node:", error);
17
+
return null;
18
+
}
19
+
}
20
+
21
+
const patchbay = getPatchbay();
22
+
23
+
// TODO: figure out how to map source to window with venmic
24
+
function linkVenmic() {
25
+
if (patchbay == null) return false;
26
+
27
+
try {
28
+
const pid =
29
+
app
30
+
.getAppMetrics()
31
+
.find((proc) => proc.name === "Audio Service")
32
+
?.pid?.toString() ?? "";
33
+
34
+
logger.info("Audio Service PID:", pid);
35
+
36
+
patchbay.unlink();
37
+
return patchbay.link({
38
+
exclude: [{ "application.process.id": pid }, { "media.class": "Stream/Input/Audio" }],
39
+
ignore_devices: true,
40
+
only_speakers: true,
41
+
only_default_speakers: true
42
+
});
43
+
} catch (error) {
44
+
logger.error("Failed to link venmic:", error);
45
+
return false;
46
+
}
47
+
}
48
+
49
+
moonlightHost.events.on("window-created", (window: BrowserWindow, isMainWindow: boolean) => {
50
+
if (!isMainWindow) return;
51
+
const windowSession = window.webContents.session;
52
+
53
+
// @ts-expect-error these types ancient
54
+
windowSession.setDisplayMediaRequestHandler(
55
+
(request: any, callback: any) => {
56
+
const linked = linkVenmic();
57
+
desktopCapturer.getSources({ types: ["screen", "window"] }).then((sources) => {
58
+
//logger.debug("desktopCapturer.getSources", sources);
59
+
logger.debug("Linked to venmic:", linked);
60
+
61
+
callback({
62
+
video: sources[0],
63
+
audio: "loopback"
64
+
});
65
+
});
66
+
},
67
+
{ useSystemPicker: true }
68
+
);
69
+
});
+2
packages/core-extensions/src/rocketship/host.ts
+2
packages/core-extensions/src/rocketship/host.ts
+124
packages/core-extensions/src/rocketship/index.ts
+124
packages/core-extensions/src/rocketship/index.ts
···
1
+
import { Patch } from "@moonlight-mod/types";
2
+
3
+
const logger = moonlight.getLogger("rocketship");
4
+
const getDisplayMediaOrig = navigator.mediaDevices.getDisplayMedia;
5
+
6
+
async function getVenmicStream() {
7
+
try {
8
+
const devices = await navigator.mediaDevices.enumerateDevices();
9
+
logger.debug("Devices:", devices);
10
+
11
+
// This isn't vencord :(
12
+
const id = devices.find((device) => device.label === "vencord-screen-share")?.deviceId;
13
+
if (!id) return null;
14
+
logger.debug("Got venmic device ID:", id);
15
+
16
+
const stream = await navigator.mediaDevices.getUserMedia({
17
+
audio: {
18
+
deviceId: {
19
+
exact: id
20
+
},
21
+
autoGainControl: false,
22
+
echoCancellation: false,
23
+
noiseSuppression: false
24
+
}
25
+
});
26
+
27
+
return stream.getAudioTracks();
28
+
} catch (error) {
29
+
logger.warn("Failed to get venmic stream:", error);
30
+
return null;
31
+
}
32
+
}
33
+
34
+
navigator.mediaDevices.getDisplayMedia = async function getDisplayMediaRedirect(options) {
35
+
const orig = await getDisplayMediaOrig.call(this, options);
36
+
37
+
const venmic = await getVenmicStream();
38
+
logger.debug("venmic", venmic);
39
+
if (venmic != null) {
40
+
// venmic will be proxying all audio, so we need to remove the original
41
+
// tracks to not cause overlap
42
+
for (const track of orig.getAudioTracks()) {
43
+
orig.removeTrack(track);
44
+
}
45
+
46
+
for (const track of venmic) {
47
+
orig.addTrack(track);
48
+
}
49
+
}
50
+
51
+
return orig;
52
+
};
53
+
54
+
export const patches: Patch[] = [
55
+
// "Ensure discord_voice is happy"
56
+
{
57
+
find: "RustAudioDeviceModule",
58
+
replace: [
59
+
{
60
+
match: /static supported\(\)\{.+?\}/,
61
+
replacement: "static supported(){return true}"
62
+
},
63
+
{
64
+
match: "supported(){return!0}",
65
+
replacement: "supported(){return true}"
66
+
}
67
+
]
68
+
},
69
+
// Remove Native media engine from list of choices
70
+
{
71
+
find: '.CAMERA_BACKGROUND_LIVE="cameraBackgroundLive"',
72
+
replace: {
73
+
match: /.\..{1,2}\.NATIVE,/,
74
+
replacement: ""
75
+
}
76
+
},
77
+
// Stub out browser checks to allow us to use WebRTC voice on Embedded
78
+
{
79
+
find: "Using Unified Plan (",
80
+
replace: {
81
+
match: /return .\..{1,2}\?\((.)\.info/,
82
+
replacement: (_, logger) => `return true?(${logger}.info`
83
+
}
84
+
},
85
+
{
86
+
find: '"UnifiedConnection("',
87
+
replace: {
88
+
match: /this\.videoSupported=.\..{1,2};/,
89
+
replacement: "this.videoSupported=true;"
90
+
}
91
+
},
92
+
{
93
+
find: "OculusBrowser",
94
+
replace: [
95
+
{
96
+
match: /"Firefox"===(.)\(\)\.name/g,
97
+
replacement: (orig, info) => `true||${orig}`
98
+
}
99
+
]
100
+
},
101
+
{
102
+
find: ".getMediaEngine().getDesktopSource",
103
+
replace: {
104
+
match: /.\.isPlatformEmbedded/,
105
+
replacement: "false"
106
+
}
107
+
},
108
+
{
109
+
// Matching MediaEngineStore
110
+
find: '"displayName","MediaEngineStore")',
111
+
replace: [
112
+
// Prevent loading of krisp native module by stubbing out desktop checks
113
+
{
114
+
match: /\(\(0,.\.isWindows\)\(\)\|\|\(0,.\.isLinux\)\(\)\|\|.+?&&!__OVERLAY__/,
115
+
replacement: (orig, macosPlatformCheck) => `false&&!__OVERLAY__`
116
+
},
117
+
// Enable loading of web krisp equivelant by replacing isWeb with true
118
+
{
119
+
match: /\(0,.\.isWeb\)\(\)&&(.{1,2}\.supports\(.{1,2}\..{1,2}.NOISE_CANCELLATION)/,
120
+
replacement: (orig, supportsNoiseCancellation) => `true&&${supportsNoiseCancellation}`
121
+
}
122
+
]
123
+
}
124
+
];
+13
packages/core-extensions/src/rocketship/manifest.json
+13
packages/core-extensions/src/rocketship/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "rocketship",
4
+
"apiLevel": 2,
5
+
"environment": "desktop",
6
+
"meta": {
7
+
"name": "Rocketship",
8
+
"tagline": "Adds new features when using rocketship",
9
+
"description": "**This extension only works on Linux when using rocketship:**\nhttps://github.com/moonlight-mod/rocketship\n\nAdds new features to the Discord Linux client with rocketship, such as a better screensharing experience.",
10
+
"authors": ["NotNite", "Cynosphere", "adryd"],
11
+
"deprecated": true
12
+
}
13
+
}
+4
-6
packages/core-extensions/src/settings/index.ts
+4
-6
packages/core-extensions/src/settings/index.ts
···
5
5
{
6
6
find: '"useGenerateUserSettingsSections"',
7
7
replace: {
8
-
match: /(?<=\.push\(.+?\)}\)\)}\),)./,
9
-
replacement: (sections: string) =>
10
-
`require("settings_settings").Settings._mutateSections(${sections})`
8
+
match: /(?<=\.push\(.+?\)}\)\)}\),)(.+?)}/,
9
+
replacement: (_, sections: string) => `require("settings_settings").Settings._mutateSections(${sections})}`
11
10
}
12
11
},
13
12
{
14
13
find: 'navId:"user-settings-cog",',
15
14
replace: {
16
-
match: /children:\[(.)\.map\(.+?\),children:.\((.)\)/,
15
+
match: /children:\[(\i)\.map\(.+?\),.*?children:\i\((\i)\)/,
17
16
replacement: (orig, sections, section) =>
18
17
`${orig.replace(
19
18
/Object\.values\(.\..+?\)/,
20
-
(orig) =>
21
-
`[...require("settings_settings").Settings.sectionNames,...${orig}]`
19
+
(orig) => `[...require("settings_settings").Settings.sectionNames,...${orig}]`
22
20
)}??${sections}.find(x=>x.section==${section})?._moonlight_submenu?.()`
23
21
}
24
22
}
+2
packages/core-extensions/src/settings/manifest.json
+2
packages/core-extensions/src/settings/manifest.json
+11
-13
packages/core-extensions/src/settings/webpackModules/settings.ts
+11
-13
packages/core-extensions/src/settings/webpackModules/settings.ts
···
1
-
import {
2
-
SettingsSection,
3
-
Settings as SettingsType
4
-
} from "@moonlight-mod/types/coreExtensions";
1
+
import { SettingsSection, Settings as SettingsType } from "@moonlight-mod/types/coreExtensions/settings";
2
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
5
3
6
4
export const Settings: SettingsType = {
7
5
ourSections: [],
8
6
sectionNames: [],
9
7
sectionMenuItems: {},
10
8
11
-
addSection: (section, label, element, color = null, pos, notice) => {
9
+
addSection: (section, label, element, color = null, pos, notice, onClick) => {
12
10
const data: SettingsSection = {
13
11
section,
14
12
label,
15
13
color,
16
14
element,
17
15
pos: pos ?? -4,
18
-
notice: notice
16
+
notice: notice,
17
+
onClick: onClick ?? (() => UserSettingsModalActionCreators.open(section))
19
18
};
20
19
21
20
Settings.ourSections.push(data);
···
24
23
},
25
24
addSectionMenuItems(section, ...newItems) {
26
25
const data = Settings.ourSections.find((x) => x.section === section);
27
-
if (!data || !("element" in data))
28
-
throw new Error(`Could not find section "${section}"`);
26
+
if (!data || !("element" in data)) throw new Error(`Could not find section "${section}"`);
29
27
(Settings.sectionMenuItems[section] ??= []).push(...newItems);
30
28
data._moonlight_submenu ??= () => Settings.sectionMenuItems[section];
31
29
},
···
47
45
48
46
_mutateSections: (sections) => {
49
47
for (const section of Settings.ourSections) {
50
-
sections.splice(
51
-
section.pos < 0 ? sections.length + section.pos : section.pos,
52
-
0,
53
-
section
54
-
);
48
+
// Discord's `pos` only supports numbers, so lets call the function to get the position.
49
+
if (typeof section.pos === "function") {
50
+
section.pos = section.pos(sections);
51
+
}
52
+
sections.splice(section.pos < 0 ? sections.length + section.pos : section.pos, 0, section);
55
53
}
56
54
57
55
return sections;
+1
-1
packages/core-extensions/src/spacepack/index.ts
+1
-1
packages/core-extensions/src/spacepack/index.ts
+3
packages/core-extensions/src/spacepack/manifest.json
+3
packages/core-extensions/src/spacepack/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "spacepack",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Spacepack",
5
7
"tagline": "Search utilities across all Webpack modules",
···
8
10
},
9
11
"settings": {
10
12
"addToGlobalScope": {
13
+
"advice": "reload",
11
14
"displayName": "Add to global scope",
12
15
"description": "Populates window.spacepack for easier usage in DevTools",
13
16
"type": "boolean",
+89
-68
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
+89
-68
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
···
1
-
import {
2
-
WebpackModule,
3
-
WebpackModuleFunc,
4
-
WebpackRequireType
5
-
} from "@moonlight-mod/types";
6
-
import { Spacepack } from "@moonlight-mod/types/coreExtensions";
1
+
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "@moonlight-mod/types";
2
+
import { Spacepack } from "@moonlight-mod/types/coreExtensions/spacepack";
3
+
import { processFind, testFind } from "@moonlight-mod/core/util/patch";
7
4
8
5
const webpackRequire = require as unknown as WebpackRequireType;
9
6
const cache = webpackRequire.c;
···
21
18
module = module.toString();
22
19
}
23
20
21
+
if (module in moonlight.moonmap.modules) {
22
+
module = moonlight.moonmap.modules[module];
23
+
}
24
+
24
25
if (!(module in modules)) {
25
26
return null;
26
27
}
···
36
37
"module",
37
38
"exports",
38
39
"require",
39
-
`(${funcStr}).apply(this, arguments)\n` +
40
-
`//# sourceURL=Webpack-Module-${module}`
40
+
`(${funcStr}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module/${module.slice(0, 3)}/${module}`
41
41
) as WebpackModuleFunc;
42
42
},
43
43
44
44
findByCode: (...args: (string | RegExp)[]) => {
45
-
return Object.entries(modules)
46
-
.filter(
47
-
([id, mod]) =>
48
-
!args.some(
49
-
(item) =>
50
-
!(item instanceof RegExp
51
-
? item.test(mod.toString())
52
-
: mod.toString().indexOf(item) !== -1)
53
-
)
54
-
)
45
+
const ret = Object.entries(modules)
46
+
.filter(([id, mod]) => !args.some((item) => !testFind(mod.toString(), processFind(item))))
55
47
.map(([id]) => {
56
48
//if (!(id in cache)) require(id);
57
49
//return cache[id];
···
60
52
try {
61
53
exports = require(id);
62
54
} catch (e) {
63
-
logger.error(`Error requiring module "${id}": `, e);
55
+
logger.error(`findByCode: Error requiring module "${id}": `, args, e);
64
56
}
65
57
66
58
return {
···
69
61
};
70
62
})
71
63
.filter((item) => item !== null);
64
+
65
+
if (ret.length === 0) {
66
+
logger.warn("findByCode: Got zero results for", args, new Error().stack!.substring(5));
67
+
}
68
+
69
+
return ret;
72
70
},
73
71
74
72
findByExports: (...args: string[]) => {
···
80
78
!(
81
79
exports !== undefined &&
82
80
exports !== window &&
83
-
(exports?.[item] ||
84
-
exports?.default?.[item] ||
85
-
exports?.Z?.[item] ||
86
-
exports?.ZP?.[item])
81
+
(exports?.[item] || exports?.default?.[item] || exports?.Z?.[item] || exports?.ZP?.[item])
87
82
)
88
83
)
89
84
)
···
95
90
},
96
91
97
92
findObjectFromKey: (exports: Record<string, any>, key: string) => {
93
+
let ret = null;
98
94
let subKey;
99
95
if (key.indexOf(".") > -1) {
100
96
const splitKey = key.split(".");
···
105
101
const obj = exports[exportKey];
106
102
if (obj && obj[key] !== undefined) {
107
103
if (subKey) {
108
-
if (obj[key][subKey]) return obj;
104
+
if (obj[key][subKey]) {
105
+
ret = obj;
106
+
break;
107
+
}
109
108
} else {
110
-
return obj;
109
+
ret = obj;
110
+
break;
111
111
}
112
112
}
113
113
}
114
-
return null;
114
+
115
+
if (ret == null) {
116
+
logger.warn("Failed to find object by key", key, "in", exports, new Error().stack!.substring(5));
117
+
}
118
+
119
+
return ret;
115
120
},
116
121
117
122
findObjectFromValue: (exports: Record<string, any>, value: any) => {
123
+
let ret = null;
118
124
for (const exportKey in exports) {
119
125
const obj = exports[exportKey];
120
126
// eslint-disable-next-line eqeqeq
121
-
if (obj == value) return obj;
127
+
if (obj == value) {
128
+
ret = obj;
129
+
break;
130
+
}
122
131
for (const subKey in obj) {
123
132
// eslint-disable-next-line eqeqeq
124
133
if (obj && obj[subKey] == value) {
125
-
return obj;
134
+
ret = obj;
135
+
break;
126
136
}
127
137
}
128
138
}
129
-
return null;
139
+
140
+
if (ret == null) {
141
+
logger.warn("Failed to find object by value", value, "in", exports, new Error().stack!.substring(5));
142
+
}
143
+
144
+
return ret;
130
145
},
131
146
132
-
findObjectFromKeyValuePair: (
133
-
exports: Record<string, any>,
134
-
key: string,
135
-
value: any
136
-
) => {
147
+
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => {
148
+
let ret = null;
137
149
for (const exportKey in exports) {
138
150
const obj = exports[exportKey];
139
151
// eslint-disable-next-line eqeqeq
140
152
if (obj && obj[key] == value) {
141
-
return obj;
153
+
ret = obj;
154
+
break;
142
155
}
143
156
}
157
+
158
+
if (ret == null) {
159
+
logger.warn(
160
+
"Failed to find object by key value pair",
161
+
key,
162
+
value,
163
+
"in",
164
+
exports,
165
+
new Error().stack!.substring(5)
166
+
);
167
+
}
168
+
144
169
return null;
145
170
},
146
171
147
-
findFunctionByStrings: (
148
-
exports: Record<string, any>,
149
-
...strings: (string | RegExp)[]
150
-
) => {
151
-
return (
172
+
findFunctionByStrings: (exports: Record<string, any>, ...strings: (string | RegExp)[]) => {
173
+
const ret =
152
174
Object.entries(exports).filter(
153
175
([index, func]) =>
154
-
typeof func === "function" &&
155
-
!strings.some(
156
-
(query) =>
157
-
!(query instanceof RegExp
158
-
? func.toString().match(query)
159
-
: func.toString().includes(query))
160
-
)
161
-
)?.[0]?.[1] ?? null
162
-
);
176
+
typeof func === "function" && !strings.some((query) => !testFind(func.toString(), processFind(query)))
177
+
)?.[0]?.[1] ?? null;
178
+
179
+
if (ret == null) {
180
+
logger.warn("Failed to find function by strings", strings, "in", exports, new Error().stack!.substring(5));
181
+
}
182
+
183
+
return ret;
163
184
},
164
185
165
-
lazyLoad: (
166
-
find: string | RegExp | (string | RegExp)[],
167
-
chunk: RegExp,
168
-
module: RegExp
169
-
) => {
170
-
const mod = Array.isArray(find)
171
-
? spacepack.findByCode(...find)
172
-
: spacepack.findByCode(find);
173
-
if (mod.length < 1) return Promise.reject("Module find failed");
186
+
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => {
187
+
chunk = processFind(chunk);
188
+
module = processFind(module);
189
+
190
+
const mod = Array.isArray(find) ? spacepack.findByCode(...find) : spacepack.findByCode(find);
191
+
if (mod.length < 1) {
192
+
logger.warn("lazyLoad: Module find failed", find, chunk, module, new Error().stack!.substring(5));
193
+
return Promise.reject("Module find failed");
194
+
}
174
195
175
196
const findId = mod[0].id;
176
197
const findCode = webpackRequire.m[findId].toString().replace(/\n/g, "");
···
180
201
chunkIds = [...findCode.matchAll(chunk)].map(([, id]) => id);
181
202
} else {
182
203
const match = findCode.match(chunk);
183
-
if (match)
184
-
chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id);
204
+
if (match) chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id);
185
205
}
186
206
187
-
if (!chunkIds || chunkIds.length === 0)
207
+
if (!chunkIds || chunkIds.length === 0) {
208
+
logger.warn("lazyLoad: Chunk ID match failed", find, chunk, module, new Error().stack!.substring(5));
188
209
return Promise.reject("Chunk ID match failed");
210
+
}
189
211
190
212
const moduleId = findCode.match(module)?.[1];
191
-
if (!moduleId) return Promise.reject("Module ID match failed");
213
+
if (!moduleId) {
214
+
logger.warn("lazyLoad: Module ID match failed", find, chunk, module, new Error().stack!.substring(5));
215
+
return Promise.reject("Module ID match failed");
216
+
}
192
217
193
-
return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() =>
194
-
webpackRequire(moduleId)
195
-
);
218
+
return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() => webpackRequire(moduleId));
196
219
},
197
220
198
221
filterReal: (modules: WebpackModule[]) => {
···
200
223
}
201
224
};
202
225
203
-
if (
204
-
moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true
205
-
) {
226
+
if (moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true) {
206
227
window.spacepack = spacepack;
207
228
}
208
229
+4
-1
packages/core-extensions/tsconfig.json
+4
-1
packages/core-extensions/tsconfig.json
+10
-3
packages/injector/package.json
+10
-3
packages/injector/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/injector",
3
3
"private": true,
4
+
"engines": {
5
+
"node": ">=22",
6
+
"pnpm": ">=10",
7
+
"npm": "pnpm",
8
+
"yarn": "pnpm"
9
+
},
4
10
"dependencies": {
5
-
"@moonlight-mod/types": "workspace:*",
6
-
"@moonlight-mod/core": "workspace:*"
7
-
}
11
+
"@moonlight-mod/core": "workspace:*",
12
+
"@moonlight-mod/types": "workspace:*"
13
+
},
14
+
"engineStrict": true
8
15
}
+173
-113
packages/injector/src/index.ts
+173
-113
packages/injector/src/index.ts
···
5
5
app
6
6
} from "electron";
7
7
import Module from "node:module";
8
-
import { constants } from "@moonlight-mod/types";
9
-
import { readConfig } from "@moonlight-mod/core/config";
8
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
9
+
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
10
10
import { getExtensions } from "@moonlight-mod/core/extension";
11
-
import Logger from "@moonlight-mod/core/util/logger";
12
-
import {
13
-
loadExtensions,
14
-
loadProcessedExtensions
15
-
} from "@moonlight-mod/core/extension/loader";
11
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
12
+
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
16
13
import EventEmitter from "node:events";
17
-
import { join, resolve } from "node:path";
14
+
import path from "node:path";
15
+
import persist from "@moonlight-mod/core/persist";
16
+
import createFS from "@moonlight-mod/core/fs";
17
+
import { getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
18
+
import { getConfigPath, getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data";
18
19
19
20
const logger = new Logger("injector");
20
21
21
22
let oldPreloadPath: string | undefined;
22
23
let corsAllow: string[] = [];
23
-
let isMoonlightDesktop = false;
24
-
let hasOpenAsar = false;
25
-
let openAsarConfigPreload: string | undefined;
24
+
let blockedUrls: RegExp[] = [];
25
+
let injectorConfig: InjectorConfig | undefined;
26
+
27
+
const scriptUrls = ["web.", "sentry."];
28
+
const blockedScripts = new Set<string>();
26
29
27
30
ipcMain.on(constants.ipcGetOldPreloadPath, (e) => {
28
31
e.returnValue = oldPreloadPath;
29
32
});
33
+
30
34
ipcMain.on(constants.ipcGetAppData, (e) => {
31
35
e.returnValue = app.getPath("appData");
32
36
});
33
-
ipcMain.on(constants.ipcGetIsMoonlightDesktop, (e) => {
34
-
e.returnValue = isMoonlightDesktop;
37
+
ipcMain.on(constants.ipcGetInjectorConfig, (e) => {
38
+
e.returnValue = injectorConfig;
35
39
});
36
40
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
37
41
electron.dialog.showMessageBoxSync(opts);
···
40
44
corsAllow = list;
41
45
});
42
46
43
-
function patchCsp(headers: Record<string, string[]>) {
44
-
const directives = [
45
-
"style-src",
46
-
"connect-src",
47
-
"img-src",
48
-
"font-src",
49
-
"media-src",
50
-
"worker-src",
51
-
"prefetch-src"
52
-
];
53
-
const values = ["*", "blob:", "data:", "'unsafe-inline'", "disclip:"];
47
+
const reEscapeRegExp = /[\\^$.*+?()[\]{}|]/g;
48
+
const reMatchPattern = /^(?<scheme>\*|[a-z][a-z0-9+.-]*):\/\/(?<host>.+?)\/(?<path>.+)?$/;
49
+
50
+
const escapeRegExp = (s: string) => s.replace(reEscapeRegExp, "\\$&");
51
+
ipcMain.handle(constants.ipcSetBlockedList, (_, list: string[]) => {
52
+
// We compile the patterns into a RegExp based on a janky match pattern-like syntax
53
+
const compiled = list
54
+
.map((pattern) => {
55
+
const match = pattern.match(reMatchPattern);
56
+
if (!match?.groups) return;
57
+
58
+
let regex = "";
59
+
if (match.groups.scheme === "*") regex += ".+?";
60
+
else regex += escapeRegExp(match.groups.scheme);
61
+
regex += ":\\/\\/";
62
+
63
+
const parts = match.groups.host.split(".");
64
+
if (parts[0] === "*") {
65
+
parts.shift();
66
+
regex += "(?:.+?\\.)?";
67
+
}
68
+
regex += escapeRegExp(parts.join("."));
69
+
70
+
regex += "\\/" + escapeRegExp(match.groups.path).replace("\\*", ".*?");
71
+
72
+
return new RegExp("^" + regex + "$");
73
+
})
74
+
.filter(Boolean) as RegExp[];
75
+
76
+
blockedUrls = compiled;
77
+
});
78
+
79
+
function patchCsp(headers: Record<string, string[]>, extensionCspOverrides: Record<string, string[]>) {
80
+
const directives = ["script-src", "style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"];
81
+
const values = ["*", "blob:", "data:", "'unsafe-inline'", "'unsafe-eval'", "disclip:"];
54
82
55
83
const csp = "content-security-policy";
56
84
if (headers[csp] == null) return;
···
67
95
68
96
for (const directive of directives) {
69
97
parts[directive] = values;
98
+
}
99
+
100
+
for (const [directive, urls] of Object.entries(extensionCspOverrides)) {
101
+
parts[directive] ??= [];
102
+
parts[directive].push(...urls);
70
103
}
71
104
72
105
const stringified = Object.entries<string[]>(parts)
···
77
110
headers[csp] = [stringified];
78
111
}
79
112
80
-
function removeOpenAsarEventIfPresent(eventHandler: (...args: any[]) => void) {
81
-
const code = eventHandler.toString();
82
-
if (code.indexOf("bw.webContents.on('dom-ready'") > -1) {
83
-
electron.app.off("browser-window-created", eventHandler);
84
-
}
85
-
}
86
-
87
113
class BrowserWindow extends ElectronBrowserWindow {
88
114
constructor(opts: BrowserWindowConstructorOptions) {
89
-
oldPreloadPath = opts.webPreferences!.preload;
115
+
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
90
116
91
-
// Only overwrite preload if its the actual main client window
92
-
if (opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1) {
117
+
if (isMainWindow) {
118
+
if (!oldPreloadPath) oldPreloadPath = opts.webPreferences!.preload;
93
119
opts.webPreferences!.preload = require.resolve("./node-preload.js");
94
120
}
95
121
96
122
// Event for modifying window options
97
-
moonlightHost.events.emit("window-options", opts);
123
+
moonlightHost.events.emit("window-options", opts, isMainWindow);
98
124
99
125
super(opts);
100
126
101
127
// Event for when a window is created
102
-
moonlightHost.events.emit("window-created", this);
128
+
moonlightHost.events.emit("window-created", this, isMainWindow);
129
+
130
+
const extensionCspOverrides: Record<string, string[]> = {};
131
+
132
+
{
133
+
const extCsps = moonlightHost.processedExtensions.extensions.map((x) => x.manifest.csp ?? {});
134
+
for (const csp of extCsps) {
135
+
for (const [directive, urls] of Object.entries(csp)) {
136
+
extensionCspOverrides[directive] ??= [];
137
+
extensionCspOverrides[directive].push(...urls);
138
+
}
139
+
}
140
+
}
103
141
104
142
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
105
143
if (details.responseHeaders != null) {
106
144
// Patch CSP so things can use externally hosted assets
107
145
if (details.resourceType === "mainFrame") {
108
-
patchCsp(details.responseHeaders);
146
+
patchCsp(details.responseHeaders, extensionCspOverrides);
109
147
}
110
148
111
149
// Allow plugins to bypass CORS for specific URLs
112
150
if (corsAllow.some((x) => details.url.startsWith(x))) {
113
-
details.responseHeaders["access-control-allow-origin"] = ["*"];
151
+
if (!details.responseHeaders) details.responseHeaders = {};
152
+
153
+
// Work around HTTP header case sensitivity by reusing the header name if it exists
154
+
// https://github.com/moonlight-mod/moonlight/issues/201
155
+
const fallback = "access-control-allow-origin";
156
+
const key = Object.keys(details.responseHeaders).find((h) => h.toLowerCase() === fallback) ?? fallback;
157
+
details.responseHeaders[key] = ["*"];
114
158
}
159
+
160
+
moonlightHost.events.emit("headers-received", details, isMainWindow);
115
161
116
162
cb({ cancel: false, responseHeaders: details.responseHeaders });
117
163
}
118
164
});
119
165
120
-
if (hasOpenAsar) {
121
-
// Remove DOM injections
122
-
// Settings can still be opened via:
123
-
// `DiscordNative.ipc.send("DISCORD_UPDATED_QUOTES","o")`
124
-
// @ts-expect-error Electron internals
125
-
const events = electron.app._events["browser-window-created"];
126
-
if (Array.isArray(events)) {
127
-
for (const event of events) {
128
-
removeOpenAsarEventIfPresent(event);
166
+
this.webContents.session.webRequest.onBeforeRequest((details, cb) => {
167
+
/*
168
+
In order to get moonlight loading to be truly async, we prevent Discord
169
+
from loading their scripts immediately. We block the requests, keep note
170
+
of their URLs, and then send them off to node-preload when we get all of
171
+
them. node-preload then loads node side, web side, and then recreates
172
+
the script elements to cause them to re-fetch.
173
+
174
+
The browser extension also does this, but in a background script (see
175
+
packages/browser/src/background.js - we should probably get this working
176
+
with esbuild someday).
177
+
*/
178
+
if (details.resourceType === "script" && isMainWindow) {
179
+
const url = new URL(details.url);
180
+
const hasUrl = scriptUrls.some((scriptUrl) => {
181
+
return (
182
+
details.url.includes(scriptUrl) &&
183
+
!url.searchParams.has("inj") &&
184
+
(url.host.endsWith("discord.com") || url.host.endsWith("discordapp.com"))
185
+
);
186
+
});
187
+
if (hasUrl) blockedScripts.add(details.url);
188
+
189
+
if (blockedScripts.size === scriptUrls.length) {
190
+
setTimeout(() => {
191
+
logger.debug("Kicking off node-preload");
192
+
this.webContents.send(constants.ipcNodePreloadKickoff, Array.from(blockedScripts));
193
+
blockedScripts.clear();
194
+
}, 0);
129
195
}
130
-
} else if (events != null) {
131
-
removeOpenAsarEventIfPresent(events);
196
+
197
+
if (hasUrl) return cb({ cancel: true });
132
198
}
133
199
134
-
// Config screen fails to context bridge properly
135
-
// Less than ideal, but better than disabling it everywhere
136
-
if (opts.webPreferences!.preload === openAsarConfigPreload) {
137
-
opts.webPreferences!.sandbox = false;
138
-
}
139
-
}
200
+
// Allow plugins to block some URLs,
201
+
// this is needed because multiple webRequest handlers cannot be registered at once
202
+
cb({ cancel: blockedUrls.some((u) => u.test(details.url)) });
203
+
});
140
204
}
141
205
}
142
206
···
157
221
writable: false
158
222
});
159
223
160
-
export async function inject(asarPath: string) {
161
-
isMoonlightDesktop = asarPath === "moonlightDesktop";
224
+
type InjectorConfig = { disablePersist?: boolean; disableLoad?: boolean };
225
+
export async function inject(asarPath: string, _injectorConfig?: InjectorConfig) {
226
+
injectorConfig = _injectorConfig;
227
+
228
+
global.moonlightNodeSandboxed = {
229
+
fs: createFS(),
230
+
// These aren't supposed to be used from host
231
+
addCors() {},
232
+
addBlocked() {}
233
+
};
234
+
162
235
try {
163
-
const config = readConfig();
164
-
const extensions = getExtensions();
236
+
let config = await readConfig();
237
+
initLogger(config);
238
+
const extensions = await getExtensions();
239
+
const processedExtensions = await loadExtensions(extensions);
240
+
const moonlightDir = await getMoonlightDir();
241
+
const extensionsPath = await getExtensionsPath();
165
242
166
243
// Duplicated in node-preload... oops
167
-
// eslint-disable-next-line no-inner-declarations
168
244
function getConfig(ext: string) {
169
245
const val = config.extensions[ext];
170
246
if (val == null || typeof val === "boolean") return undefined;
171
247
return val.config;
172
248
}
173
-
174
249
global.moonlightHost = {
250
+
get config() {
251
+
return config;
252
+
},
253
+
extensions,
254
+
processedExtensions,
175
255
asarPath,
176
-
config,
177
256
events: new EventEmitter(),
178
-
extensions,
179
-
processedExtensions: {
180
-
extensions: [],
181
-
dependencyGraph: new Map()
182
-
},
257
+
258
+
version: MOONLIGHT_VERSION,
259
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
183
260
184
261
getConfig,
185
-
getConfigOption: <T>(ext: string, name: string) => {
186
-
const config = getConfig(ext);
187
-
if (config == null) return undefined;
188
-
const option = config[name];
189
-
if (option == null) return undefined;
190
-
return option as T;
262
+
getConfigPath,
263
+
getConfigOption(ext, name) {
264
+
const manifest = getManifest(extensions, ext);
265
+
return getConfigOption(ext, name, config, manifest?.settings);
191
266
},
192
-
getLogger: (id: string) => {
267
+
setConfigOption(ext, name, value) {
268
+
setConfigOption(config, ext, name, value);
269
+
this.writeConfig(config);
270
+
},
271
+
async writeConfig(newConfig) {
272
+
await writeConfig(newConfig);
273
+
config = newConfig;
274
+
},
275
+
276
+
getLogger(id) {
193
277
return new Logger(id);
278
+
},
279
+
getMoonlightDir() {
280
+
return moonlightDir;
281
+
},
282
+
getExtensionDir: (ext: string) => {
283
+
return path.join(extensionsPath, ext);
194
284
}
195
285
};
196
286
197
-
// Check if we're running with OpenAsar
198
-
try {
199
-
require.resolve(join(asarPath, "updater", "updater.js"));
200
-
hasOpenAsar = true;
201
-
openAsarConfigPreload = resolve(asarPath, "config", "preload.js");
202
-
// eslint-disable-next-line no-empty
203
-
} catch {}
204
-
205
-
if (hasOpenAsar) {
206
-
// Disable command line switch injection
207
-
// I personally think that the command line switches should be vetted by
208
-
// the user and not just "trust that these are sane defaults that work
209
-
// always". I'm not hating on Ducko or anything, I'm just opinionated.
210
-
// Someone can always make a command line modifier plugin, thats the point
211
-
// of having host modules.
212
-
try {
213
-
const cmdSwitchesPath = require.resolve(
214
-
join(asarPath, "cmdSwitches.js")
215
-
);
216
-
require.cache[cmdSwitchesPath] = new Module(
217
-
cmdSwitchesPath,
218
-
require.cache[require.resolve(asarPath)]
219
-
);
220
-
require.cache[cmdSwitchesPath]!.exports = () => {};
221
-
} catch (error) {
222
-
logger.error("Failed to disable OpenAsar's command line flags:", error);
223
-
}
224
-
}
225
-
226
287
patchElectron();
227
288
228
-
global.moonlightHost.processedExtensions = await loadExtensions(extensions);
229
289
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
230
290
} catch (error) {
231
291
logger.error("Failed to inject:", error);
232
292
}
233
293
234
-
if (isMoonlightDesktop) return;
294
+
if (injectorConfig?.disablePersist !== true) {
295
+
persist(asarPath);
296
+
}
235
297
236
-
// Need to do this instead of require() or it breaks require.main
237
-
// @ts-expect-error Module internals
238
-
Module._load(asarPath, Module, true);
298
+
if (injectorConfig?.disableLoad !== true) {
299
+
// Need to do this instead of require() or it breaks require.main
300
+
// @ts-expect-error Module internals
301
+
Module._load(asarPath, Module, true);
302
+
}
239
303
}
240
304
241
305
function patchElectron() {
···
249
313
configurable: false
250
314
});
251
315
} else {
252
-
Object.defineProperty(
253
-
electronClone,
254
-
property,
255
-
Object.getOwnPropertyDescriptor(electron, property)!
256
-
);
316
+
Object.defineProperty(electronClone, property, Object.getOwnPropertyDescriptor(electron, property)!);
257
317
}
258
318
}
259
319
+8
-1
packages/node-preload/package.json
+8
-1
packages/node-preload/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/node-preload",
3
3
"private": true,
4
+
"engines": {
5
+
"node": ">=22",
6
+
"pnpm": ">=10",
7
+
"npm": "pnpm",
8
+
"yarn": "pnpm"
9
+
},
4
10
"dependencies": {
5
11
"@moonlight-mod/core": "workspace:*",
6
12
"@moonlight-mod/types": "workspace:*"
7
-
}
13
+
},
14
+
"engineStrict": true
8
15
}
+144
-46
packages/node-preload/src/index.ts
+144
-46
packages/node-preload/src/index.ts
···
1
1
import { webFrame, ipcRenderer, contextBridge } from "electron";
2
-
import fs from "fs";
3
-
import path from "path";
2
+
import fs from "node:fs";
3
+
import path from "node:path";
4
4
5
5
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
6
-
import { constants } from "@moonlight-mod/types";
6
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
7
7
import { getExtensions } from "@moonlight-mod/core/extension";
8
-
import { getExtensionsPath } from "@moonlight-mod/core/util/data";
9
-
import Logger from "@moonlight-mod/core/util/logger";
10
-
import {
11
-
loadExtensions,
12
-
loadProcessedExtensions
13
-
} from "@moonlight-mod/core/extension/loader";
8
+
import { getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data";
9
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
10
+
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
11
+
import createFS from "@moonlight-mod/core/fs";
12
+
import { registerCors, registerBlocked, getDynamicCors } from "@moonlight-mod/core/cors";
13
+
import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
14
+
import { NodeEventPayloads, NodeEventType } from "@moonlight-mod/types/core/event";
15
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
16
+
17
+
let initialized = false;
18
+
let logger: Logger;
19
+
20
+
function setCors() {
21
+
const data = getDynamicCors();
22
+
ipcRenderer.invoke(constants.ipcSetCorsList, data.cors);
23
+
ipcRenderer.invoke(constants.ipcSetBlockedList, data.blocked);
24
+
}
14
25
15
26
async function injectGlobals() {
16
-
const config = readConfig();
17
-
const extensions = getExtensions();
18
-
const processed = await loadExtensions(extensions);
27
+
global.moonlightNodeSandboxed = {
28
+
fs: createFS(),
29
+
addCors(url) {
30
+
registerCors(url);
31
+
if (initialized) setCors();
32
+
},
33
+
addBlocked(url) {
34
+
registerBlocked(url);
35
+
if (initialized) setCors();
36
+
}
37
+
};
19
38
20
-
function getConfig(ext: string) {
21
-
const val = config.extensions[ext];
22
-
if (val == null || typeof val === "boolean") return undefined;
23
-
return val.config;
24
-
}
39
+
let config = await readConfig();
40
+
initLogger(config);
41
+
logger = new Logger("node-preload");
42
+
43
+
const extensions = await getExtensions();
44
+
const processedExtensions = await loadExtensions(extensions);
45
+
const moonlightDir = await getMoonlightDir();
46
+
const extensionsPath = await getExtensionsPath();
25
47
26
48
global.moonlightNode = {
27
-
config,
28
-
extensions: getExtensions(),
29
-
processedExtensions: processed,
49
+
get config() {
50
+
return config;
51
+
},
52
+
extensions,
53
+
processedExtensions,
30
54
nativesCache: {},
31
-
getConfig,
32
-
getConfigOption: <T>(ext: string, name: string) => {
33
-
const config = getConfig(ext);
34
-
if (config == null) return undefined;
35
-
const option = config[name];
36
-
if (option == null) return undefined;
37
-
return option as T;
55
+
isBrowser: false,
56
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
57
+
58
+
version: MOONLIGHT_VERSION,
59
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
60
+
61
+
getConfig(ext) {
62
+
return getConfig(ext, config);
63
+
},
64
+
getConfigOption(ext, name) {
65
+
const manifest = getManifest(extensions, ext);
66
+
return getConfigOption(ext, name, config, manifest?.settings);
67
+
},
68
+
async setConfigOption(ext, name, value) {
69
+
setConfigOption(config, ext, name, value);
70
+
await this.writeConfig(config);
71
+
},
72
+
async writeConfig(newConfig) {
73
+
await writeConfig(newConfig);
74
+
config = newConfig;
75
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
38
76
},
77
+
39
78
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
40
79
getLogger: (id: string) => {
41
80
return new Logger(id);
42
81
},
43
-
44
-
getExtensionDir: (ext: string) => {
45
-
const extPath = getExtensionsPath();
46
-
return path.join(extPath, ext);
82
+
getMoonlightDir() {
83
+
return moonlightDir;
47
84
},
48
-
writeConfig
85
+
getExtensionDir: (ext: string) => {
86
+
return path.join(extensionsPath, ext);
87
+
}
49
88
};
50
89
51
-
await loadProcessedExtensions(processed);
90
+
await loadProcessedExtensions(processedExtensions);
52
91
contextBridge.exposeInMainWorld("moonlightNode", moonlightNode);
53
92
54
-
const extCors = moonlightNode.processedExtensions.extensions
55
-
.map((x) => x.manifest.cors ?? [])
56
-
.flat();
93
+
const extCors = moonlightNode.processedExtensions.extensions.flatMap((x) => x.manifest.cors ?? []);
94
+
for (const cors of extCors) {
95
+
registerCors(cors);
96
+
}
57
97
58
98
for (const repo of moonlightNode.config.repositories) {
59
99
const url = new URL(repo);
60
100
url.pathname = "/";
61
-
extCors.push(url.toString());
101
+
registerCors(url.toString());
62
102
}
63
103
64
-
ipcRenderer.invoke(constants.ipcSetCorsList, extCors);
104
+
const extBlocked = moonlightNode.processedExtensions.extensions.flatMap((e) => e.manifest.blocked ?? []);
105
+
for (const blocked of extBlocked) {
106
+
registerBlocked(blocked);
107
+
}
108
+
109
+
setCors();
110
+
111
+
initialized = true;
65
112
}
66
113
67
114
async function loadPreload() {
68
115
const webPreloadPath = path.join(__dirname, "web-preload.js");
69
116
const webPreload = fs.readFileSync(webPreloadPath, "utf8");
70
117
await webFrame.executeJavaScript(webPreload);
118
+
119
+
const func = await webFrame.executeJavaScript("async () => { await window._moonlightWebLoad(); }");
120
+
await func();
71
121
}
72
122
73
-
async function init(oldPreloadPath: string) {
123
+
async function init() {
74
124
try {
75
125
await injectGlobals();
76
126
await loadPreload();
···
81
131
message: message
82
132
});
83
133
}
134
+
}
84
135
85
-
// Let Discord start even if we fail
86
-
if (oldPreloadPath) require(oldPreloadPath);
136
+
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
137
+
const isOverlay = window.location.href.indexOf("discord_overlay") > -1;
138
+
139
+
if (isOverlay) {
140
+
// The overlay has an inline script tag to call to DiscordNative, so we'll
141
+
// just load it immediately. Somehow moonlight still loads in this env, I
142
+
// have no idea why - so I suspect it's just forwarding render calls or
143
+
// something from the original process
144
+
require(oldPreloadPath);
145
+
} else {
146
+
ipcRenderer.on(constants.ipcNodePreloadKickoff, (_, blockedScripts: string[]) => {
147
+
(async () => {
148
+
try {
149
+
await init();
150
+
logger.debug("Blocked scripts:", blockedScripts);
151
+
152
+
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
153
+
logger.debug("Old preload path:", oldPreloadPath);
154
+
if (oldPreloadPath) require(oldPreloadPath);
155
+
156
+
// Do this to get global.DiscordNative assigned
157
+
// @ts-expect-error Lying to discord_desktop_core
158
+
process.emit("loaded");
159
+
160
+
function replayScripts() {
161
+
const scripts = [...document.querySelectorAll("script")].filter(
162
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
163
+
);
164
+
165
+
blockedScripts.reverse();
166
+
for (const url of blockedScripts) {
167
+
if (url.includes("/sentry.")) continue;
168
+
169
+
const script = scripts.find((script) => url.includes(script.src))!;
170
+
const newScript = document.createElement("script");
171
+
for (const attr of script.attributes) {
172
+
if (attr.name === "src") attr.value += "?inj";
173
+
newScript.setAttribute(attr.name, attr.value);
174
+
}
175
+
script.remove();
176
+
document.documentElement.appendChild(newScript);
177
+
}
178
+
}
179
+
180
+
if (document.readyState === "complete") {
181
+
replayScripts();
182
+
} else {
183
+
window.addEventListener("load", replayScripts);
184
+
}
185
+
} catch (e) {
186
+
logger.error("Error restoring original scripts:", e);
187
+
}
188
+
})();
189
+
});
87
190
}
88
-
89
-
const oldPreloadPath: string = ipcRenderer.sendSync(
90
-
constants.ipcGetOldPreloadPath
91
-
);
92
-
init(oldPreloadPath);
+4
-1
packages/node-preload/tsconfig.json
+4
-1
packages/node-preload/tsconfig.json
+15
-6
packages/types/package.json
+15
-6
packages/types/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/types",
3
-
"version": "1.1.8",
4
-
"main": "./src/index.ts",
5
-
"types": "./src/index.ts",
3
+
"version": "1.3.17",
6
4
"exports": {
7
5
".": "./src/index.ts",
8
6
"./import": "./src/import.d.ts",
9
7
"./*": "./src/*.ts"
10
8
},
9
+
"main": "./src/index.ts",
10
+
"types": "./src/index.ts",
11
+
"engineStrict": false,
12
+
"engines": {
13
+
"node": ">=22",
14
+
"pnpm": ">=10",
15
+
"npm": "pnpm",
16
+
"yarn": "pnpm"
17
+
},
11
18
"dependencies": {
12
-
"@types/flux": "^3.1.12",
13
-
"@types/react": "^18.2.22",
14
-
"csstype": "^3.1.2",
19
+
"@moonlight-mod/lunast": "^1.0.1",
20
+
"@moonlight-mod/mappings": "^1.1.25",
21
+
"@moonlight-mod/moonmap": "^1.0.5",
22
+
"@types/react": "^18.3.10",
23
+
"csstype": "^3.1.3",
15
24
"standalone-electron-types": "^1.0.0"
16
25
}
17
26
}
+49
-3
packages/types/src/config.ts
+49
-3
packages/types/src/config.ts
···
6
6
patchAll?: boolean;
7
7
};
8
8
9
-
export type ConfigExtensions =
10
-
| { [key: string]: boolean }
11
-
| { [key: string]: ConfigExtension };
9
+
export type ConfigExtensions = { [key: string]: boolean } | { [key: string]: ConfigExtension };
12
10
13
11
export type ConfigExtension = {
14
12
enabled: boolean;
···
35
33
};
36
34
37
35
export type BooleanSettingType = {
36
+
/**
37
+
* Displays as a simple switch.
38
+
*/
38
39
type: ExtensionSettingType.Boolean;
39
40
default?: boolean;
40
41
};
41
42
42
43
export type NumberSettingType = {
44
+
/**
45
+
* Displays as a simple slider.
46
+
*/
43
47
type: ExtensionSettingType.Number;
44
48
default?: number;
45
49
min?: number;
···
47
51
};
48
52
49
53
export type StringSettingType = {
54
+
/**
55
+
* Displays as a single line string input.
56
+
*/
50
57
type: ExtensionSettingType.String;
51
58
default?: string;
52
59
};
53
60
54
61
export type MultilineTextInputSettingType = {
62
+
/**
63
+
* Displays as a multiple line string input.
64
+
*/
55
65
type: ExtensionSettingType.MultilineString;
56
66
default?: string;
57
67
};
58
68
59
69
export type SelectSettingType = {
70
+
/**
71
+
* A dropdown to pick between one of many values.
72
+
*/
60
73
type: ExtensionSettingType.Select;
61
74
options: SelectOption[];
62
75
default?: string;
63
76
};
64
77
65
78
export type MultiSelectSettingType = {
79
+
/**
80
+
* A dropdown to pick multiple values.
81
+
*/
66
82
type: ExtensionSettingType.MultiSelect;
67
83
options: string[];
68
84
default?: string[];
69
85
};
70
86
71
87
export type ListSettingType = {
88
+
/**
89
+
* A list of strings that the user can add or remove from.
90
+
*/
72
91
type: ExtensionSettingType.List;
73
92
default?: string[];
74
93
};
75
94
76
95
export type DictionarySettingType = {
96
+
/**
97
+
* A dictionary (key-value pair) that the user can add or remove from.
98
+
*/
77
99
type: ExtensionSettingType.Dictionary;
78
100
default?: Record<string, string>;
79
101
};
80
102
81
103
export type CustomSettingType = {
104
+
/**
105
+
* A custom component.
106
+
* You can use the registerConfigComponent function in the Moonbase API to register a React component to render here.
107
+
*/
82
108
type: ExtensionSettingType.Custom;
83
109
default?: any;
84
110
};
85
111
112
+
export enum ExtensionSettingsAdvice {
113
+
None = "none",
114
+
Reload = "reload",
115
+
Restart = "restart"
116
+
}
117
+
86
118
export type ExtensionSettingsManifest = {
119
+
/**
120
+
* A human friendly name for the setting.
121
+
*/
87
122
displayName?: string;
123
+
124
+
/**
125
+
* A longer description for the setting.
126
+
* Markdown is not supported.
127
+
*/
88
128
description?: string;
129
+
130
+
/**
131
+
* The "advice" to give upon changing this setting.
132
+
* Can be configured to reload the client, restart the client, or do nothing.
133
+
*/
134
+
advice?: ExtensionSettingsAdvice;
89
135
} & (
90
136
| BooleanSettingType
91
137
| NumberSettingType
+11
-1
packages/types/src/constants.ts
+11
-1
packages/types/src/constants.ts
···
2
2
export const distDir = "dist";
3
3
export const coreExtensionsDir = "core-extensions";
4
4
export const repoUrlFile = ".moonlight-repo-url";
5
+
export const installedVersionFile = ".moonlight-installed-version";
5
6
7
+
export const ipcNodePreloadKickoff = "_moonlight_nodePreloadKickoff";
6
8
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
9
+
7
10
export const ipcGetAppData = "_moonlight_getAppData";
8
-
export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop";
11
+
export const ipcGetInjectorConfig = "_moonlight_getInjectorConfig";
9
12
export const ipcMessageBox = "_moonlight_messageBox";
10
13
export const ipcSetCorsList = "_moonlight_setCorsList";
14
+
export const ipcSetBlockedList = "_moonlight_setBlockedList";
15
+
16
+
export const apiLevel = 2;
17
+
18
+
export const mainRepo = "https://moonlight-mod.github.io/extensions-dist/repo.json";
19
+
// If you're updating this, update `defaultConfig` in core as well
20
+
export const builtinExtensions = ["moonbase", "disableSentry", "noTrack", "noHideToken"];
+30
packages/types/src/core/event.ts
+30
packages/types/src/core/event.ts
···
1
+
import { Config } from "../config";
2
+
import { WebpackModuleFunc, WebpackRequireType } from "../discord";
3
+
4
+
export interface MoonlightEventEmitter<EventId extends string = string, EventData = Record<EventId, any>> {
5
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => void;
6
+
addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => void;
7
+
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => void;
8
+
}
9
+
10
+
export enum WebEventType {
11
+
ChunkLoad = "chunkLoad",
12
+
ExtensionLoad = "extensionLoad"
13
+
}
14
+
15
+
export type WebEventPayloads = {
16
+
[WebEventType.ChunkLoad]: {
17
+
chunkId?: number[];
18
+
modules: { [id: string]: WebpackModuleFunc };
19
+
require?: (require: WebpackRequireType) => any;
20
+
};
21
+
[WebEventType.ExtensionLoad]: string;
22
+
};
23
+
24
+
export enum NodeEventType {
25
+
ConfigSaved = "configSaved"
26
+
}
27
+
28
+
export type NodeEventPayloads = {
29
+
[NodeEventType.ConfigSaved]: Config;
30
+
};
+13
packages/types/src/coreExtensions/appPanels.ts
+13
packages/types/src/coreExtensions/appPanels.ts
···
1
+
export type AppPanels = {
2
+
/**
3
+
* Registers a new panel to be displayed around the user/voice controls.
4
+
* @param section A unique name for your section
5
+
* @param element A React component
6
+
*/
7
+
addPanel: (section: string, element: React.FC<any>) => void;
8
+
9
+
/**
10
+
* @private
11
+
*/
12
+
getPanels: (el: React.FC<any>) => React.ReactNode;
13
+
};
+204
packages/types/src/coreExtensions/commands.ts
+204
packages/types/src/coreExtensions/commands.ts
···
1
+
export const APPLICATION_ID = "-3";
2
+
3
+
export enum CommandType {
4
+
CHAT = 1,
5
+
MESSAGE = 3,
6
+
PRIMARY_ENTRY_POINT = 4,
7
+
USER = 2
8
+
}
9
+
10
+
export enum InputType {
11
+
BOT = 3,
12
+
BUILT_IN = 0,
13
+
BUILT_IN_INTEGRATION = 2,
14
+
BUILT_IN_TEXT = 1,
15
+
PLACEHOLDER = 4
16
+
}
17
+
18
+
export enum OptionType {
19
+
SUB_COMMAND = 1,
20
+
SUB_COMMAND_GROUP = 2,
21
+
STRING = 3,
22
+
INTEGER = 4,
23
+
BOOLEAN = 5,
24
+
USER = 6,
25
+
CHANNEL = 7,
26
+
ROLE = 8,
27
+
MENTIONABLE = 9,
28
+
NUMBER = 10,
29
+
ATTACHMENT = 11
30
+
}
31
+
32
+
export enum ChannelType {
33
+
GUILD_TEXT = 0,
34
+
DM = 1,
35
+
GUILD_VOICE = 2,
36
+
GROUP_DM = 3,
37
+
GUILD_CATEGORY = 4,
38
+
GUILD_ANNOUNCEMENT = 5,
39
+
GUILD_STORE = 6,
40
+
ANNOUNCEMENT_THREAD = 10,
41
+
PUBLIC_THREAD = 11,
42
+
PRIVATE_THREAD = 12,
43
+
GUILD_STAGE_VOICE = 13,
44
+
GUILD_DIRECTORY = 14,
45
+
GUILD_FORUM = 15,
46
+
GUILD_MEDIA = 16,
47
+
LOBBY = 17,
48
+
DM_SDK = 18
49
+
}
50
+
51
+
export type RegisteredCommandOption = MoonlightCommandOption & {
52
+
displayName: string;
53
+
displayDescription: string;
54
+
};
55
+
56
+
export type CommandOptionChoice<T> = {
57
+
name: string;
58
+
value: T;
59
+
};
60
+
61
+
type CommandOptionBase<T> = {
62
+
type: T;
63
+
name: string;
64
+
description: string;
65
+
required?: T extends OptionType.SUB_COMMAND
66
+
? never
67
+
: T extends OptionType.SUB_COMMAND_GROUP
68
+
? never
69
+
: boolean | undefined;
70
+
choices?: T extends OptionType.STRING
71
+
? CommandOptionChoice<string>[]
72
+
: T extends OptionType.INTEGER
73
+
? CommandOptionChoice<number>[]
74
+
: T extends OptionType.NUMBER
75
+
? CommandOptionChoice<number>[]
76
+
: never;
77
+
options?: T extends OptionType.SUB_COMMAND
78
+
? MoonlightCommandOption[]
79
+
: T extends OptionType.SUB_COMMAND_GROUP
80
+
? MoonlightCommandOption[]
81
+
: never;
82
+
channelTypes?: T extends OptionType.CHANNEL ? ChannelType[] : never;
83
+
minValue?: T extends OptionType.INTEGER ? number : T extends OptionType.NUMBER ? number : never;
84
+
maxValue?: T extends OptionType.INTEGER ? number : T extends OptionType.NUMBER ? number : never;
85
+
minLength?: T extends OptionType.STRING ? number : never;
86
+
maxLength?: T extends OptionType.STRING ? number : never;
87
+
};
88
+
89
+
// This is bad lol
90
+
export type MoonlightCommandOption =
91
+
| CommandOptionBase<OptionType.SUB_COMMAND>
92
+
| CommandOptionBase<OptionType.SUB_COMMAND_GROUP>
93
+
| CommandOptionBase<OptionType.STRING>
94
+
| CommandOptionBase<OptionType.INTEGER>
95
+
| CommandOptionBase<OptionType.BOOLEAN>
96
+
| CommandOptionBase<OptionType.USER>
97
+
| CommandOptionBase<OptionType.CHANNEL>
98
+
| CommandOptionBase<OptionType.ROLE>
99
+
| CommandOptionBase<OptionType.MENTIONABLE>
100
+
| CommandOptionBase<OptionType.NUMBER>
101
+
| CommandOptionBase<OptionType.ATTACHMENT>;
102
+
103
+
// TODO: types
104
+
export type CommandPredicateState = {
105
+
channel: any;
106
+
guild: any;
107
+
};
108
+
109
+
export type RegisteredCommand = {
110
+
id: string;
111
+
untranslatedName: string;
112
+
displayName: string;
113
+
type: CommandType;
114
+
inputType: InputType;
115
+
applicationId: string; // set to -3!
116
+
untranslatedDescription: string;
117
+
displayDescription: string;
118
+
options?: RegisteredCommandOption[];
119
+
predicate?: (state: CommandPredicateState) => boolean;
120
+
execute: (options: CommandOption[]) => void;
121
+
};
122
+
123
+
export type MoonlightCommand = {
124
+
id: string;
125
+
description: string;
126
+
127
+
/**
128
+
* You likely want CHAT
129
+
*/
130
+
type: CommandType;
131
+
132
+
/**
133
+
* You likely want BUILT_IN (or BUILT_IN_TEXT if usable with replies)
134
+
*/
135
+
inputType: InputType;
136
+
options?: MoonlightCommandOption[];
137
+
predicate?: (state: CommandPredicateState) => boolean;
138
+
execute: (options: CommandOption[]) => void;
139
+
};
140
+
141
+
export type CommandOption = {
142
+
name: string;
143
+
} & ( // TODO: more of these
144
+
| {
145
+
type: Exclude<OptionType, OptionType.STRING>;
146
+
value: any;
147
+
}
148
+
| {
149
+
type: OptionType.STRING;
150
+
value: string;
151
+
}
152
+
| {
153
+
type: OptionType.NUMBER | OptionType.INTEGER;
154
+
value: number;
155
+
}
156
+
| {
157
+
type: OptionType.BOOLEAN;
158
+
value: boolean;
159
+
}
160
+
| {
161
+
type: OptionType.SUB_COMMAND | OptionType.SUB_COMMAND_GROUP;
162
+
options: CommandOption[];
163
+
}
164
+
);
165
+
166
+
export type AnyScopeRegex = RegExp["exec"] & {
167
+
regex: RegExp;
168
+
};
169
+
170
+
export type Commands = {
171
+
/**
172
+
* Register a command in the internal slash command system
173
+
*/
174
+
registerCommand: (command: MoonlightCommand) => void;
175
+
176
+
/**
177
+
* Register a legacy command that works via regex
178
+
*/
179
+
registerLegacyCommand: (id: string, command: LegacyCommand) => void;
180
+
181
+
/**
182
+
* Creates a regular expression that legacy commands can understand
183
+
*/
184
+
anyScopeRegex: (regex: RegExp) => AnyScopeRegex;
185
+
186
+
/**
187
+
* @private
188
+
*/
189
+
_getCommands: () => RegisteredCommand[];
190
+
};
191
+
192
+
export type LegacyContext = {
193
+
channel: any;
194
+
isEdit: boolean;
195
+
};
196
+
197
+
export type LegacyReturn = {
198
+
content: string;
199
+
};
200
+
201
+
export type LegacyCommand = {
202
+
match?: RegExp | { regex: RegExp } | AnyScopeRegex;
203
+
action: (content: string, context: LegacyContext) => LegacyReturn;
204
+
};
+33
packages/types/src/coreExtensions/common.ts
+33
packages/types/src/coreExtensions/common.ts
···
1
+
import type { IconProps, IconSize } from "@moonlight-mod/mappings/discord/components/common/index";
2
+
3
+
export type ErrorBoundaryProps = React.PropsWithChildren<{
4
+
noop?: boolean;
5
+
fallback?: React.FC<any>;
6
+
message?: string;
7
+
}>;
8
+
9
+
export type ErrorBoundaryState = {
10
+
errored: boolean;
11
+
error?: Error;
12
+
componentStack?: string;
13
+
};
14
+
15
+
export type ErrorBoundary = React.ComponentClass<ErrorBoundaryProps, ErrorBoundaryState>;
16
+
17
+
export type ParsedIconProps = {
18
+
width: number;
19
+
height: number;
20
+
fill: string;
21
+
className: string;
22
+
};
23
+
24
+
export interface Icons {
25
+
/**
26
+
* Parse icon props into their actual width/height.
27
+
* @param props The icon props
28
+
*/
29
+
parseProps(props?: IconProps): ParsedIconProps;
30
+
}
31
+
32
+
// Re-export so extension developers don't need to depend on mappings
33
+
export type { IconProps, IconSize };
+162
packages/types/src/coreExtensions/componentEditor.ts
+162
packages/types/src/coreExtensions/componentEditor.ts
···
1
+
type Patcher<T> = (elements: React.ReactNode[], props: T) => React.ReactNode[];
2
+
3
+
//#region DM List
4
+
export type DMListAnchors =
5
+
| "content"
6
+
| "favorite-server-indicator"
7
+
| "ignored-indicator"
8
+
| "blocked-indicator"
9
+
| "close-button"
10
+
| undefined;
11
+
export type DMListDecoratorAnchors = "system-tag" | undefined;
12
+
13
+
export enum DMListAnchorIndicies {
14
+
content = 0,
15
+
"favorite-server-indicator",
16
+
"ignored-indicator",
17
+
"blocked-indicator",
18
+
"close-button"
19
+
}
20
+
export enum DMListDecoratorAnchorIndicies {
21
+
"system-tag" = 0
22
+
}
23
+
24
+
export type DMListItem = {
25
+
component: React.FC<any>;
26
+
anchor: DMListAnchors;
27
+
before: boolean;
28
+
};
29
+
export type DMListDecorator = {
30
+
component: React.FC<any>;
31
+
anchor: DMListDecoratorAnchors;
32
+
before: boolean;
33
+
};
34
+
35
+
export type DMList = {
36
+
addItem: (id: string, component: React.FC<any>, anchor?: DMListAnchors, before?: boolean) => void;
37
+
addDecorator: (id: string, component: React.FC<any>, anchor?: DMListDecoratorAnchors, before?: boolean) => void;
38
+
//TODO: fix props type
39
+
/**
40
+
* @private
41
+
*/
42
+
_patchItems: Patcher<any>;
43
+
/**
44
+
* @private
45
+
*/
46
+
_patchDecorators: Patcher<any>;
47
+
};
48
+
//#endregion
49
+
50
+
//#region Member List
51
+
export type MemberListDecoratorAnchors = "bot-tag" | "owner-crown" | "boost-icon" | undefined;
52
+
53
+
export enum MemberListDecoratorAnchorIndicies {
54
+
"bot-tag" = 0,
55
+
"owner-crown",
56
+
"boost-icon"
57
+
}
58
+
59
+
export type MemberListDecorator = {
60
+
component: React.FC<any>;
61
+
anchor: MemberListDecoratorAnchors;
62
+
before: boolean;
63
+
};
64
+
65
+
export type MemberList = {
66
+
addItem: (id: string, component: React.FC<any>) => void;
67
+
addDecorator: (id: string, component: React.FC<any>, anchor?: MemberListDecoratorAnchors, before?: boolean) => void;
68
+
//TODO: fix props type
69
+
/**
70
+
* @private
71
+
*/
72
+
_patchItems: Patcher<any>;
73
+
/**
74
+
* @private
75
+
*/
76
+
_patchDecorators: Patcher<any>;
77
+
};
78
+
//#endregion
79
+
80
+
//#region Messages
81
+
export type MessageUsernameAnchors = "communication-disabled" | "username" | undefined;
82
+
export type MessageUsernameBadgeAnchors =
83
+
| "nitro-author"
84
+
| "role-icon"
85
+
| "new-member"
86
+
| "leaderboard-champion"
87
+
| "connections"
88
+
| undefined;
89
+
export type MessageBadgeAnchors = "silent" | "potion" | undefined;
90
+
91
+
export type MessageUsername = {
92
+
component: React.FC<any>;
93
+
anchor: MessageUsernameAnchors;
94
+
before: boolean;
95
+
};
96
+
export type MessageUsernameBadge = {
97
+
component: React.FC<any>;
98
+
anchor: MessageUsernameBadgeAnchors;
99
+
before: boolean;
100
+
};
101
+
export type MessageBadge = {
102
+
component: React.FC<any>;
103
+
anchor: MessageBadgeAnchors;
104
+
before: boolean;
105
+
};
106
+
107
+
export enum MessageUsernameIndicies {
108
+
"communication-disabled" = 0,
109
+
username
110
+
}
111
+
export enum MessageUsernameBadgeIndicies {
112
+
"nitro-author" = 0,
113
+
"role-icon",
114
+
"new-member",
115
+
"leaderboard-champion",
116
+
connections
117
+
}
118
+
export enum MessageBadgeIndicies {
119
+
silent = 0,
120
+
potion
121
+
}
122
+
123
+
export type Messages = {
124
+
/**
125
+
* Adds a component to the username of a message
126
+
*/
127
+
addToUsername: (id: string, component: React.FC<any>, anchor?: MessageUsernameAnchors, before?: boolean) => void;
128
+
/**
129
+
* Adds a component to the username badge area of a message (e.g. where role icons/new member badge is)
130
+
*/
131
+
addUsernameBadge: (
132
+
id: string,
133
+
component: React.FC<any>,
134
+
anchor?: MessageUsernameBadgeAnchors,
135
+
before?: boolean
136
+
) => void;
137
+
/**
138
+
* Adds a component to the end of a message header (e.g. silent indicator)
139
+
*/
140
+
addBadge: (id: string, component: React.FC<any>, anchor?: MessageBadgeAnchors, before?: boolean) => void;
141
+
/**
142
+
* Adds a component to message accessories (e.g. embeds)
143
+
*/
144
+
addAccessory: (id: string, component: React.FC<any>) => void;
145
+
/**
146
+
* @private
147
+
*/
148
+
_patchUsername: Patcher<any>;
149
+
/**
150
+
* @private
151
+
*/
152
+
_patchUsernameBadges: Patcher<any>;
153
+
/**
154
+
* @private
155
+
*/
156
+
_patchBadges: Patcher<any>;
157
+
/**
158
+
* @private
159
+
*/
160
+
_patchAccessories: Patcher<any>;
161
+
};
162
+
//#endregion
-409
packages/types/src/coreExtensions/components.ts
-409
packages/types/src/coreExtensions/components.ts
···
1
-
import type {
2
-
Component,
3
-
Ref,
4
-
PropsWithChildren,
5
-
PropsWithoutRef,
6
-
CSSProperties,
7
-
ReactNode,
8
-
ReactElement,
9
-
ComponentClass,
10
-
ComponentType,
11
-
MouseEventHandler,
12
-
KeyboardEventHandler
13
-
} from "react";
14
-
import * as CSS from "csstype";
15
-
16
-
export enum TextInputSizes {
17
-
DEFAULT = "inputDefault",
18
-
MINI = "inputMini"
19
-
}
20
-
21
-
interface TextInput
22
-
extends ComponentClass<
23
-
PropsWithoutRef<{
24
-
value?: string;
25
-
name?: string;
26
-
className?: string;
27
-
inputClassName?: string;
28
-
inputPrefix?: string;
29
-
disabled?: boolean;
30
-
size?: TextInputSizes;
31
-
editable?: boolean;
32
-
inputRef?: Ref<any>;
33
-
prefixElement?: Component;
34
-
focusProps?: PropsWithoutRef<any>;
35
-
error?: string;
36
-
minLength?: number;
37
-
maxLength?: number;
38
-
onChange?: (value: string, name: string) => void;
39
-
onFocus?: (event: any, name: string) => void;
40
-
onBlur?: (event: any, name: string) => void;
41
-
}>
42
-
> {
43
-
Sizes: typeof TextInputSizes;
44
-
}
45
-
46
-
export enum TextAreaAutoComplete {
47
-
ON = "on",
48
-
OFF = "off"
49
-
}
50
-
51
-
export enum TextAreaWrap {
52
-
HARD = "hard",
53
-
SOFT = "soft",
54
-
OFF = "off"
55
-
}
56
-
57
-
interface TextArea
58
-
extends ComponentClass<
59
-
PropsWithoutRef<{
60
-
value?: string;
61
-
defaultValue?: string;
62
-
autoComplete?: TextAreaAutoComplete;
63
-
autoFocus?: boolean;
64
-
cols?: number;
65
-
disabled?: boolean;
66
-
form?: string;
67
-
maxLength?: number;
68
-
minLength?: number;
69
-
name?: string;
70
-
onChange?: (value: string, name: string) => void;
71
-
onChangeCapture?: (value: string, name: string) => void;
72
-
onInput?: (value: string, name: string) => void;
73
-
onInputCapture?: (value: string, name: string) => void;
74
-
onInvalid?: (value: string, name: string) => void;
75
-
onInvalidCapture?: (value: string, name: string) => void;
76
-
onSelect?: (value: string, name: string) => void;
77
-
onSelectCapture?: (value: string, name: string) => void;
78
-
placeholder?: string;
79
-
readOnly?: boolean;
80
-
required?: boolean;
81
-
rows?: number;
82
-
wrap?: TextAreaWrap;
83
-
className?: string;
84
-
}>
85
-
> {
86
-
AutoCompletes: typeof TextAreaAutoComplete;
87
-
Wraps: typeof TextAreaWrap;
88
-
}
89
-
90
-
export enum FormTextTypes {
91
-
DEFAULT = "default",
92
-
DESCRIPTION = "description",
93
-
ERROR = "error",
94
-
INPUT_PLACEHOLDER = "placeholder",
95
-
LABEL_BOLD = "labelBold",
96
-
LABEL_DESCRIPTOR = "labelDescriptor",
97
-
LABEL_SELECTED = "labelSelected",
98
-
SUCCESS = "success"
99
-
}
100
-
101
-
interface FormText
102
-
extends ComponentClass<
103
-
PropsWithChildren<{
104
-
type?: FormTextTypes;
105
-
className?: string;
106
-
disabled?: boolean;
107
-
selectable?: boolean;
108
-
style?: CSSProperties;
109
-
}>
110
-
> {
111
-
Types: FormTextTypes;
112
-
}
113
-
114
-
declare enum SliderMarkerPosition {
115
-
ABOVE,
116
-
BELOW
117
-
}
118
-
119
-
declare enum ButtonLooks {
120
-
FILLED = "lookFilled",
121
-
INVERTED = "lookInverted",
122
-
OUTLINED = "lookOutlined",
123
-
LINK = "lookLink",
124
-
BLANK = "lookBlank"
125
-
}
126
-
declare enum ButtonColors {
127
-
BRAND = "colorBrand",
128
-
RED = "colorRed",
129
-
GREEN = "colorGreen",
130
-
YELLOW = "colorYellow",
131
-
PRIMARY = "colorPrimary",
132
-
LINK = "colorLink",
133
-
WHITE = "colorWhite",
134
-
BLACK = "colorBlack",
135
-
TRANSPARENT = "colorTransparent",
136
-
BRAND_NEW = "colorBrandNew",
137
-
CUSTOM = ""
138
-
}
139
-
declare enum ButtonBorderColors {
140
-
BRAND = "borderBrand",
141
-
RED = "borderRed",
142
-
GREEN = "borderGreen",
143
-
YELLOW = "borderYellow",
144
-
PRIMARY = "borderPrimary",
145
-
LINK = "borderLink",
146
-
WHITE = "borderWhite",
147
-
BLACK = "borderBlack",
148
-
TRANSPARENT = "borderTransparent",
149
-
BRAND_NEW = "borderBrandNew"
150
-
}
151
-
declare enum ButtonHovers {
152
-
DEFAULT = "",
153
-
BRAND = "hoverBrand",
154
-
RED = "hoverRed",
155
-
GREEN = "hoverGreen",
156
-
YELLOW = "hoverYellow",
157
-
PRIMARY = "hoverPrimary",
158
-
LINK = "hoverLink",
159
-
WHITE = "hoverWhite",
160
-
BLACK = "hoverBlack",
161
-
TRANSPARENT = "hoverTransparent"
162
-
}
163
-
declare enum ButtonSizes {
164
-
NONE = "",
165
-
TINY = "sizeTiny",
166
-
SMALL = "sizeSmall",
167
-
MEDIUM = "sizeMedium",
168
-
LARGE = "sizeLarge",
169
-
XLARGE = "sizeXlarge",
170
-
MIN = "sizeMin",
171
-
MAX = "sizeMax",
172
-
ICON = "sizeIcon"
173
-
}
174
-
175
-
type Button = ComponentType<
176
-
PropsWithChildren<{
177
-
look?: ButtonLooks;
178
-
color?: ButtonColors;
179
-
borderColor?: ButtonBorderColors;
180
-
hover?: ButtonHovers;
181
-
size?: ButtonSizes;
182
-
fullWidth?: boolean;
183
-
grow?: boolean;
184
-
disabled?: boolean;
185
-
submitting?: boolean;
186
-
type?: string;
187
-
style?: CSSProperties;
188
-
wrapperClassName?: string;
189
-
className?: string;
190
-
innerClassName?: string;
191
-
onClick?: MouseEventHandler;
192
-
onDoubleClick?: MouseEventHandler;
193
-
onMouseDown?: MouseEventHandler;
194
-
onMouseUp?: MouseEventHandler;
195
-
onMouseEnter?: MouseEventHandler;
196
-
onMouseLeave?: MouseEventHandler;
197
-
onKeyDown?: KeyboardEventHandler;
198
-
rel?: any;
199
-
buttonRef?: Ref<any>;
200
-
focusProps?: PropsWithChildren<any>;
201
-
"aria-label"?: string;
202
-
submittingStartedLabel?: string;
203
-
submittingFinishedLabel?: string;
204
-
}>
205
-
> & {
206
-
Looks: typeof ButtonLooks;
207
-
Colors: typeof ButtonColors;
208
-
BorderColors: typeof ButtonBorderColors;
209
-
Hovers: typeof ButtonHovers;
210
-
Sizes: typeof ButtonSizes;
211
-
};
212
-
213
-
export enum FlexDirection {
214
-
VERTICAL = "vertical",
215
-
HORIZONTAL = "horizontal",
216
-
HORIZONTAL_REVERSE = "horizontalReverse"
217
-
}
218
-
219
-
declare enum FlexAlign {
220
-
START = "alignStart",
221
-
END = "alignEnd",
222
-
CENTER = "alignCenter",
223
-
STRETCH = "alignStretch",
224
-
BASELINE = "alignBaseline"
225
-
}
226
-
declare enum FlexJustify {
227
-
START = "justifyStart",
228
-
END = "justifyEnd",
229
-
CENTER = "justifyCenter",
230
-
BETWEEN = "justifyBetween",
231
-
AROUND = "justifyAround"
232
-
}
233
-
declare enum FlexWrap {
234
-
NO_WRAP = "noWrap",
235
-
WRAP = "wrap",
236
-
WRAP_REVERSE = "wrapReverse"
237
-
}
238
-
interface Flex
239
-
extends ComponentClass<
240
-
PropsWithChildren<{
241
-
className?: string;
242
-
direction?: FlexDirection;
243
-
justify?: FlexJustify;
244
-
align?: FlexAlign;
245
-
wrap?: FlexWrap;
246
-
shrink?: CSS.Property.FlexShrink;
247
-
grow?: CSS.Property.FlexGrow;
248
-
basis?: CSS.Property.FlexBasis;
249
-
style?: CSSProperties;
250
-
}>
251
-
> {
252
-
Direction: typeof FlexDirection;
253
-
Align: typeof FlexAlign;
254
-
Justify: typeof FlexJustify;
255
-
Wrap: typeof FlexWrap;
256
-
Child: Component<
257
-
PropsWithChildren<{
258
-
className?: string;
259
-
shrink?: CSS.Property.FlexShrink;
260
-
grow?: CSS.Property.FlexGrow;
261
-
basis?: CSS.Property.FlexBasis;
262
-
style?: CSSProperties;
263
-
wrap?: boolean;
264
-
}>
265
-
>;
266
-
}
267
-
268
-
// TODO: wtaf is up with react types not working in jsx
269
-
export type CommonComponents = {
270
-
[index: string]: any;
271
-
Clickable: ComponentClass<
272
-
PropsWithChildren<{
273
-
onClick?: () => void;
274
-
href?: any;
275
-
onKeyPress?: () => void;
276
-
ignoreKeyPress?: boolean;
277
-
innerRef?: Ref<any>;
278
-
focusProps?: any;
279
-
tag?: string | Component;
280
-
role?: any;
281
-
tabIndex?: any;
282
-
className?: string;
283
-
}>
284
-
>;
285
-
TextInput: TextInput;
286
-
TextArea: TextArea;
287
-
FormDivider: ComponentClass<any>;
288
-
FormSection: ComponentClass<
289
-
PropsWithChildren<{
290
-
className?: string;
291
-
titleClassName?: string;
292
-
title?: ReactNode;
293
-
icon?: ReactNode;
294
-
disabled?: boolean;
295
-
htmlFor?: any;
296
-
tag?: string;
297
-
}>
298
-
>;
299
-
FormText: FormText;
300
-
FormTitle: ComponentClass<
301
-
PropsWithChildren<{
302
-
tag?: string;
303
-
className?: string;
304
-
faded?: boolean;
305
-
disabled?: boolean;
306
-
required?: boolean;
307
-
error?: string;
308
-
}>
309
-
>;
310
-
FormSwitch: ComponentClass<PropsWithChildren<any>>;
311
-
FormItem: ComponentClass<PropsWithChildren<any>>;
312
-
Slider: ComponentClass<
313
-
PropsWithChildren<{
314
-
disabled?: boolean;
315
-
stickToMarkers?: boolean;
316
-
className?: string;
317
-
barStyles?: CSSProperties;
318
-
fillStyles?: CSSProperties;
319
-
mini?: boolean;
320
-
hideBubble?: boolean;
321
-
initialValue?: number;
322
-
orientation?: "horizontal" | "vertical";
323
-
onValueRender?: (value: number) => string;
324
-
renderMarker?: (marker: number) => ReactNode;
325
-
getAriaValueText?: (value: number) => string;
326
-
barClassName?: string;
327
-
grabberClassName?: string;
328
-
grabberStyles?: CSSProperties;
329
-
markerPosition?: SliderMarkerPosition;
330
-
"aria-hidden"?: "true" | "false";
331
-
"aria-label"?: string;
332
-
"aria-labelledby"?: string;
333
-
"aria-describedby"?: string;
334
-
minValue?: number;
335
-
maxValue?: number;
336
-
asValueChanges?: (value: number) => void;
337
-
onValueChange?: (value: number) => void;
338
-
keyboardStep?: number;
339
-
}>
340
-
>;
341
-
Switch: ComponentClass<PropsWithChildren<any>>;
342
-
Button: Button;
343
-
Tooltip: ComponentClass<PropsWithChildren<any>>;
344
-
SmallSlider: Component;
345
-
Avatar: Component;
346
-
Scroller: Component;
347
-
Text: ComponentClass<PropsWithChildren<any>>;
348
-
Heading: ComponentClass<PropsWithChildren<any>>;
349
-
LegacyText: Component;
350
-
Flex: Flex;
351
-
Card: ComponentClass<PropsWithChildren<any>>;
352
-
Popout: ComponentClass<PropsWithChildren<any>>;
353
-
Dialog: ComponentClass<PropsWithChildren<any>>;
354
-
Menu: ComponentClass<PropsWithChildren<any>>;
355
-
MenuItem: ComponentClass<PropsWithChildren<any>>;
356
-
MenuGroup: ComponentClass<PropsWithChildren<any>>;
357
-
MenuCheckboxItem: ComponentClass<PropsWithChildren<any>>;
358
-
CardClasses: {
359
-
card: string;
360
-
cardHeader: string;
361
-
};
362
-
ControlClasses: {
363
-
container: string;
364
-
control: string;
365
-
disabled: string;
366
-
dividerDefault: string;
367
-
labelRow: string;
368
-
note: string;
369
-
title: string;
370
-
titleDefault: string;
371
-
titleMini: string;
372
-
};
373
-
MarkdownParser: {
374
-
parse: (text: string) => ReactElement;
375
-
};
376
-
SettingsNotice: React.ComponentType<{
377
-
submitting: boolean;
378
-
onReset: () => void;
379
-
onSave: () => void;
380
-
}>;
381
-
TabBar: React.ComponentType<any> & {
382
-
Item: React.ComponentType<any>;
383
-
};
384
-
SingleSelect: React.ComponentType<{
385
-
autofocus?: boolean;
386
-
clearable?: boolean;
387
-
value?: string;
388
-
options?: {
389
-
value: string;
390
-
label: string;
391
-
}[];
392
-
onChange?: (value: string) => void;
393
-
}>;
394
-
Select: React.ComponentType<{
395
-
autofocus?: boolean;
396
-
clearable?: boolean;
397
-
value?: string[];
398
-
options?: {
399
-
value: string;
400
-
label: string;
401
-
}[];
402
-
onChange?: (value: string[]) => void;
403
-
}>;
404
-
405
-
// TODO
406
-
useVariableSelect: any;
407
-
multiSelect: any;
408
-
tokens: any;
409
-
};
+21
-121
packages/types/src/coreExtensions/contextMenu.ts
+21
-121
packages/types/src/coreExtensions/contextMenu.ts
···
1
-
// TODO: Deduplicate common props
2
-
3
-
export type Menu = React.FunctionComponent<{
4
-
navId: string;
5
-
variant?: string;
6
-
hideScrollbar?: boolean;
7
-
className?: string;
8
-
children: React.ReactComponentElement<MenuElement>[];
9
-
onClose?: () => void;
10
-
onSelect?: () => void;
11
-
}>;
12
-
export type MenuProps = React.ComponentProps<Menu>;
13
-
14
-
export type MenuElement =
15
-
| MenuSeparator
16
-
| MenuGroup
17
-
| MenuItem
18
-
| MenuCheckboxItem
19
-
| MenuRadioItem
20
-
| MenuControlItem;
21
-
22
-
/* eslint-disable prettier/prettier */
23
-
export type MenuSeparator = React.FunctionComponent;
24
-
export type MenuGroup = React.FunctionComponent<{
25
-
label?: string;
26
-
className?: string;
27
-
color?: string;
28
-
children: React.ReactComponentElement<MenuElement>[];
29
-
}>;
30
-
export type MenuItem = React.FunctionComponent<
31
-
{
32
-
id: any;
33
-
dontCloseOnActionIfHoldingShiftKey?: boolean;
34
-
} & (
35
-
| {
36
-
label: string;
37
-
subtext?: string;
38
-
color?: string;
39
-
hint?: string;
40
-
disabled?: boolean;
41
-
icon?: any;
42
-
showIconFirst?: boolean;
43
-
imageUrl?: string;
44
-
45
-
className?: string;
46
-
focusedClassName?: string;
47
-
subMenuIconClassName?: string;
48
-
49
-
action?: () => void;
50
-
onFocus?: () => void;
51
-
52
-
iconProps?: any;
53
-
sparkle?: any;
54
-
55
-
children?: React.ReactComponentElement<MenuElement>[];
56
-
onChildrenScroll?: any;
57
-
childRowHeight?: any;
58
-
listClassName?: string;
59
-
subMenuClassName?: string;
60
-
}
61
-
| {
62
-
color?: string;
63
-
disabled?: boolean;
64
-
keepItemStyles?: boolean;
65
-
66
-
action?: () => void;
67
-
68
-
render: any;
69
-
navigable?: boolean;
70
-
}
71
-
)
72
-
>;
73
-
export type MenuCheckboxItem = React.FunctionComponent<{
74
-
id: any;
75
-
label: string;
76
-
subtext?: string;
77
-
color?: string;
78
-
className?: string;
79
-
focusedClassName?: string;
80
-
disabled?: boolean;
81
-
checked: boolean;
82
-
action?: () => void;
83
-
}>;
84
-
export type MenuRadioItem = React.FunctionComponent<{
85
-
id: any;
86
-
label: string;
87
-
subtext?: string;
88
-
color?: string;
89
-
disabled?: boolean;
90
-
action?: () => void;
91
-
}>;
92
-
export type MenuControlItem = React.FunctionComponent<
93
-
{
94
-
id: any;
95
-
label: string;
96
-
color?: string;
97
-
disabled?: boolean;
98
-
showDefaultFocus?: boolean;
99
-
} & (
100
-
| {
101
-
control: any;
102
-
}
103
-
| {
104
-
control?: undefined;
105
-
interactive?: boolean;
106
-
children?: React.ReactComponentElement<MenuElement>[];
107
-
}
108
-
)
109
-
>;
110
-
/* eslint-disable prettier/prettier */
1
+
import {
2
+
Menu,
3
+
MenuCheckboxItem,
4
+
MenuControlItem,
5
+
MenuGroup,
6
+
MenuRadioItem,
7
+
MenuSeparator,
8
+
MenuItem,
9
+
MenuElement
10
+
} from "@moonlight-mod/mappings/discord/components/common/index";
111
11
112
12
export type ContextMenu = {
113
-
addItem: (
114
-
navId: string,
115
-
item: (props: any) => React.ReactComponentElement<MenuElement>,
116
-
anchorId: string,
117
-
before?: boolean
118
-
) => void;
13
+
/**
14
+
* Registers a new context menu item for a given context menu type.
15
+
* @param navId The navigation ID for the target context menu (e.g. "user-context", "message")
16
+
* @param item A React component
17
+
* @param anchor An existing item's ID to anchor the new item to
18
+
* @param before Whether to insert the new item before the anchor item
19
+
*/
20
+
addItem: (navId: string, item: React.FC<any>, anchor: string | RegExp, before?: boolean) => void;
119
21
120
22
MenuCheckboxItem: MenuCheckboxItem;
121
23
MenuControlItem: MenuControlItem;
···
157
59
label: string;
158
60
};
159
61
160
-
export type EvilItemParser = (
161
-
el:
162
-
| React.ReactComponentElement<MenuElement>
163
-
| React.ReactComponentElement<MenuElement>[]
164
-
) => InternalItem[];
62
+
export type EvilItemParser = (el: MenuElement | MenuElement[]) => InternalItem[];
63
+
64
+
export type { Menu, MenuElement };
+20
-23
packages/types/src/coreExtensions/markdown.ts
+20
-23
packages/types/src/coreExtensions/markdown.ts
···
11
11
12
12
export type ASTNode = SingleASTNode | Array<SingleASTNode>;
13
13
14
-
export type Parser = (
15
-
source: string,
16
-
state?: State | null | undefined
17
-
) => Array<SingleASTNode>;
14
+
export type Parser = (source: string, state?: State | null | undefined) => Array<SingleASTNode>;
18
15
19
-
export type ParseFunction = (
20
-
capture: Capture,
21
-
nestedParse: Parser,
22
-
state: State
23
-
) => UntypedASTNode | ASTNode;
16
+
export type ParseFunction = (capture: Capture, nestedParse: Parser, state: State) => UntypedASTNode | ASTNode;
24
17
25
18
export type Capture =
26
19
| (Array<string> & {
···
38
31
39
32
export type MatchFunction = {
40
33
regex?: RegExp;
41
-
} & ((
42
-
source: string,
43
-
state: State,
44
-
prevCapture: string
45
-
) => Capture | null | undefined);
34
+
} & ((source: string, state: State, prevCapture: string) => Capture | null | undefined);
46
35
47
-
export type Output<Result> = (
48
-
node: ASTNode,
49
-
state?: State | null | undefined
50
-
) => Result;
36
+
export type Output<Result> = (node: ASTNode, state?: State | null | undefined) => Result;
51
37
52
-
export type SingleNodeOutput<Result> = (
53
-
node: SingleASTNode,
54
-
nestedOutput: Output<Result>,
55
-
state: State
56
-
) => Result;
38
+
export type SingleNodeOutput<Result> = (node: SingleASTNode, nestedOutput: Output<Result>, state: State) => Result;
57
39
58
40
// }}}
59
41
···
100
82
slateDecorators: Record<string, string>;
101
83
ruleBlacklists: Record<Ruleset, Record<string, boolean>>;
102
84
85
+
/**
86
+
* Registers a new Markdown rule with simple-markdown.
87
+
* @param name The name of the rule
88
+
* @param markdown A function that returns simple-markdown rules
89
+
* @param slate A function that returns Slate rules
90
+
* @param decorator A decorator name for Slate
91
+
* @see https://www.npmjs.com/package/simple-markdown#adding-a-simple-extension
92
+
* @see https://docs.slatejs.org/
93
+
*/
103
94
addRule: (
104
95
name: string,
105
96
markdown: (rules: Record<string, MarkdownRule>) => MarkdownRule,
106
97
slate: (rules: Record<string, SlateRule>) => SlateRule,
107
98
decorator?: string | undefined
108
99
) => void;
100
+
101
+
/**
102
+
* Blacklist a rule from a ruleset.
103
+
* @param ruleset The ruleset name
104
+
* @param name The rule name
105
+
*/
109
106
blacklistFromRuleset: (ruleset: Ruleset, name: string) => void;
110
107
};
+17
packages/types/src/coreExtensions/moonbase.ts
+17
packages/types/src/coreExtensions/moonbase.ts
···
1
+
export type CustomComponentProps = {
2
+
value: any;
3
+
setValue: (value: any) => void;
4
+
};
5
+
6
+
export type CustomComponent = React.FC<CustomComponentProps>;
7
+
8
+
export type Moonbase = {
9
+
/**
10
+
* Registers a custom component for an extension setting.
11
+
* The extension setting must be of type "custom".
12
+
* @param ext The extension ID
13
+
* @param option The setting ID
14
+
* @param component A React component
15
+
*/
16
+
registerConfigComponent: (ext: string, option: string, component: CustomComponent) => void;
17
+
};
+36
packages/types/src/coreExtensions/notices.ts
+36
packages/types/src/coreExtensions/notices.ts
···
1
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store";
2
+
3
+
export type NoticeButton = {
4
+
name: string;
5
+
onClick: () => boolean; // return true to dismiss the notice after the button is clicked
6
+
};
7
+
8
+
export type Notice = {
9
+
element: React.ReactNode;
10
+
color?: string;
11
+
showClose?: boolean;
12
+
buttons?: NoticeButton[];
13
+
onDismiss?: () => void;
14
+
};
15
+
16
+
export type Notices = Store<any> & {
17
+
/**
18
+
* Adds a custom notice to the top of the screen.
19
+
*/
20
+
addNotice: (notice: Notice) => void;
21
+
22
+
/**
23
+
* Removes the current notice from the top of the screen.
24
+
*/
25
+
popNotice: () => void;
26
+
27
+
/**
28
+
* @private
29
+
*/
30
+
getCurrentNotice: () => Notice | null;
31
+
32
+
/**
33
+
* @private
34
+
*/
35
+
shouldShowNotice: () => boolean;
36
+
};
+71
packages/types/src/coreExtensions/settings.ts
+71
packages/types/src/coreExtensions/settings.ts
···
1
+
import React, { ReactElement } from "react";
2
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store";
3
+
4
+
export type NoticeProps = {
5
+
stores: Store<any>[];
6
+
element: React.FunctionComponent;
7
+
};
8
+
9
+
export type SettingsSection =
10
+
| { section: "DIVIDER"; pos: number | ((sections: SettingsSection[]) => number) }
11
+
| { section: "HEADER"; label: string; pos: number | ((sections: SettingsSection[]) => number) }
12
+
| {
13
+
section: string;
14
+
label: string;
15
+
color: string | null;
16
+
element: React.FunctionComponent;
17
+
pos: number | ((sections: SettingsSection[]) => number);
18
+
notice?: NoticeProps;
19
+
onClick?: () => void;
20
+
_moonlight_submenu?: () => ReactElement | ReactElement[];
21
+
};
22
+
23
+
export type Settings = {
24
+
ourSections: SettingsSection[];
25
+
sectionNames: string[];
26
+
sectionMenuItems: Record<string, ReactElement[]>;
27
+
28
+
/**
29
+
* Registers a new section in the settings menu.
30
+
* @param section The section ID
31
+
* @param label The label for the section
32
+
* @param element The React component to render
33
+
* @param color A color to use for the section
34
+
* @param pos The position in the settings menu to place the section
35
+
* @param notice A notice to display when in the section
36
+
* @param onClick A custom action to execute when clicked from the context menu
37
+
*/
38
+
addSection: (
39
+
section: string,
40
+
label: string,
41
+
element: React.FunctionComponent,
42
+
color?: string | null,
43
+
pos?: number | ((sections: SettingsSection[]) => number),
44
+
notice?: NoticeProps,
45
+
onClick?: () => void
46
+
) => void;
47
+
48
+
/**
49
+
* Adds new items to a section in the settings menu.
50
+
* @param section The section ID
51
+
* @param items The React components to render
52
+
*/
53
+
addSectionMenuItems: (section: string, ...items: ReactElement[]) => void;
54
+
55
+
/**
56
+
* Places a divider in the settings menu.
57
+
* @param pos The position in the settings menu to place the divider
58
+
*/
59
+
addDivider: (pos: number | ((sections: SettingsSection[]) => number) | null) => void;
60
+
61
+
/**
62
+
* Places a header in the settings menu.
63
+
* @param pos The position in the settings menu to place the header
64
+
*/
65
+
addHeader: (label: string, pos: number | ((sections: SettingsSection[]) => number) | null) => void;
66
+
67
+
/**
68
+
* @private
69
+
*/
70
+
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
71
+
};
+97
packages/types/src/coreExtensions/spacepack.ts
+97
packages/types/src/coreExtensions/spacepack.ts
···
1
+
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "../discord";
2
+
3
+
export type Spacepack = {
4
+
/**
5
+
* Given a Webpack module ID, returns the function for the Webpack module.
6
+
* Can be double clicked to inspect in DevTools.
7
+
* @param module The module ID
8
+
* @returns The Webpack module, if found
9
+
*/
10
+
inspect: (module: number | string) => WebpackModuleFunc | null;
11
+
12
+
/**
13
+
* Find Webpack modules based on matches in code.
14
+
* @param args A list of finds to match against
15
+
* @returns The Webpack modules, if found
16
+
*/
17
+
findByCode: (...args: (string | RegExp)[]) => WebpackModule[];
18
+
19
+
/**
20
+
* Find Webpack modules based on their exports.
21
+
* @deprecated This has race conditions. Consider using findByCode instead.
22
+
* @param args A list of finds to match exports against
23
+
* @returns The Webpack modules, if found
24
+
*/
25
+
findByExports: (...args: string[]) => WebpackModule[];
26
+
27
+
/**
28
+
* The Webpack require function.
29
+
*/
30
+
require: WebpackRequireType;
31
+
32
+
/**
33
+
* The Webpack module list.
34
+
* Re-export of require.m.
35
+
*/
36
+
modules: Record<string, WebpackModuleFunc>;
37
+
38
+
/**
39
+
* The Webpack module cache.
40
+
* Re-export of require.c.
41
+
*/
42
+
cache: Record<string, any>;
43
+
44
+
/**
45
+
* Finds an object from a module's exports using the given key.
46
+
* @param exports Exports from a Webpack module
47
+
* @param key The key to find with
48
+
* @returns The object, if found
49
+
*/
50
+
findObjectFromKey: (exports: Record<string, any>, key: string) => any | null;
51
+
52
+
/**
53
+
* Finds an object from a module's exports using the given value.
54
+
* @param exports Exports from a Webpack module
55
+
* @param value The value to find with
56
+
* @returns The object, if found
57
+
*/
58
+
findObjectFromValue: (exports: Record<string, any>, value: any) => any | null;
59
+
60
+
/**
61
+
* Finds an object from a module's exports using the given key-value pair.
62
+
* @param exports Exports from a Webpack module
63
+
* @param key The key to find with
64
+
* @param value The value to find with
65
+
* @returns The object, if found
66
+
*/
67
+
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => any | null;
68
+
69
+
/**
70
+
* Finds a function from a module's exports using the given source find.
71
+
* This behaves like findByCode but localized to the exported function.
72
+
* @param exports A module's exports
73
+
* @param strings A list of finds to use
74
+
* @returns The function, if found
75
+
*/
76
+
findFunctionByStrings: (
77
+
exports: Record<string, any>,
78
+
...strings: (string | RegExp)[]
79
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
80
+
) => Function | null;
81
+
82
+
/**
83
+
* Lazy load a Webpack module.
84
+
* @param find A list of finds to discover a target module with
85
+
* @param chunk A RegExp to match chunks to load
86
+
* @param module A RegExp to match the target Webpack module
87
+
* @returns The target Webpack module
88
+
*/
89
+
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => Promise<any>;
90
+
91
+
/**
92
+
* Filter a list of Webpack modules to "real" ones from the Discord client.
93
+
* @param modules A list of Webpack modules
94
+
* @returns A filtered list of Webpack modules
95
+
*/
96
+
filterReal: (modules: WebpackModule[]) => WebpackModule[];
97
+
};
+8
-80
packages/types/src/coreExtensions.ts
+8
-80
packages/types/src/coreExtensions.ts
···
1
-
import { FluxDefault, Store } from "./discord/common/Flux";
2
-
import { CommonComponents as CommonComponents_ } from "./coreExtensions/components";
3
-
import { Dispatcher } from "flux";
4
-
import React, { ReactElement } from "react";
5
-
import {
6
-
WebpackModule,
7
-
WebpackModuleFunc,
8
-
WebpackRequireType
9
-
} from "./discord";
10
-
11
-
export type Spacepack = {
12
-
inspect: (module: number | string) => WebpackModuleFunc | null;
13
-
findByCode: (...args: (string | RegExp)[]) => any[];
14
-
findByExports: (...args: string[]) => any[];
15
-
require: WebpackRequireType;
16
-
modules: Record<string, WebpackModuleFunc>;
17
-
cache: Record<string, any>;
18
-
findObjectFromKey: (exports: Record<string, any>, key: string) => any | null;
19
-
findObjectFromValue: (exports: Record<string, any>, value: any) => any | null;
20
-
findObjectFromKeyValuePair: (
21
-
exports: Record<string, any>,
22
-
key: string,
23
-
value: any
24
-
) => any | null;
25
-
findFunctionByStrings: (
26
-
exports: Record<string, any>,
27
-
...strings: (string | RegExp)[]
28
-
// eslint-disable-next-line @typescript-eslint/ban-types
29
-
) => Function | null;
30
-
lazyLoad: (
31
-
find: string | RegExp | (string | RegExp)[],
32
-
chunk: RegExp,
33
-
module: RegExp
34
-
) => Promise<any>;
35
-
filterReal: (modules: WebpackModule[]) => WebpackModule[];
36
-
};
37
-
38
-
export type NoticeProps = {
39
-
stores: Store<any>[];
40
-
element: React.FunctionComponent;
41
-
};
42
-
43
-
export type SettingsSection =
44
-
| { section: "DIVIDER"; pos: number }
45
-
| { section: "HEADER"; label: string; pos: number }
46
-
| {
47
-
section: string;
48
-
label: string;
49
-
color: string | null;
50
-
element: React.FunctionComponent;
51
-
pos: number;
52
-
notice?: NoticeProps;
53
-
_moonlight_submenu?: () => ReactElement | ReactElement[];
54
-
};
55
-
56
-
export type Settings = {
57
-
ourSections: SettingsSection[];
58
-
sectionNames: string[];
59
-
sectionMenuItems: Record<string, ReactElement[]>;
60
-
61
-
addSection: (
62
-
section: string,
63
-
label: string,
64
-
element: React.FunctionComponent,
65
-
color?: string | null,
66
-
pos?: number,
67
-
notice?: NoticeProps
68
-
) => void;
69
-
addSectionMenuItems: (section: string, ...items: ReactElement[]) => void;
70
-
71
-
addDivider: (pos: number | null) => void;
72
-
addHeader: (label: string, pos: number | null) => void;
73
-
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
74
-
};
75
-
76
-
export type CommonReact = typeof import("react");
77
-
export type CommonFlux = FluxDefault;
78
-
export type CommonComponents = CommonComponents_; // lol
79
-
export type CommonFluxDispatcher = Dispatcher<any>;
80
-
1
+
export * as Spacepack from "./coreExtensions/spacepack";
2
+
export * as Settings from "./coreExtensions/settings";
81
3
export * as Markdown from "./coreExtensions/markdown";
82
4
export * as ContextMenu from "./coreExtensions/contextMenu";
5
+
export * as Notices from "./coreExtensions/notices";
6
+
export * as Moonbase from "./coreExtensions/moonbase";
7
+
export * as AppPanels from "./coreExtensions/appPanels";
8
+
export * as Commands from "./coreExtensions/commands";
9
+
export * as ComponentEditor from "./coreExtensions/componentEditor";
10
+
export * as Common from "./coreExtensions/common";
-57
packages/types/src/discord/common/Flux.ts
-57
packages/types/src/discord/common/Flux.ts
···
1
-
/*
2
-
It seems like Discord maintains their own version of Flux that doesn't match
3
-
the types on NPM. This is a heavy work in progress - if you encounter rough
4
-
edges, please contribute!
5
-
*/
6
-
7
-
import { DependencyList } from "react";
8
-
import { Store as FluxStore } from "flux/utils";
9
-
import { Dispatcher as FluxDispatcher } from "flux";
10
-
import { ComponentConstructor } from "flux/lib/FluxContainer";
11
-
12
-
export declare abstract class Store<T> extends FluxStore<T> {
13
-
static getAll: () => Store<any>[];
14
-
getName: () => string;
15
-
emitChange: () => void;
16
-
}
17
-
18
-
interface ConnectStores {
19
-
<T>(
20
-
stores: Store<any>[],
21
-
callback: T,
22
-
context?: any
23
-
): ComponentConstructor<T>;
24
-
}
25
-
26
-
export type FluxDefault = {
27
-
DeviceSettingsStore: any; // TODO
28
-
Emitter: any; // @types/fbemitter
29
-
OfflineCacheStore: any; // TODO
30
-
PersistedStore: any; // TODO
31
-
Store: typeof Store;
32
-
Dispatcher: typeof FluxDispatcher;
33
-
connectStores: ConnectStores;
34
-
initialize: () => void;
35
-
initialized: Promise<boolean>;
36
-
destroy: () => void;
37
-
useStateFromStores: UseStateFromStores;
38
-
useStateFromStoresArray: UseStateFromStoresArray;
39
-
useStateFromStoresObject: UseStateFromStoresObject;
40
-
};
41
-
42
-
interface UseStateFromStores {
43
-
<T>(
44
-
stores: Store<any>[],
45
-
callback: () => T,
46
-
deps?: DependencyList,
47
-
shouldUpdate?: (oldState: T, newState: T) => boolean
48
-
): T;
49
-
}
50
-
51
-
interface UseStateFromStoresArray {
52
-
<T>(stores: Store<any>[], callback: () => T, deps?: DependencyList): T;
53
-
}
54
-
55
-
interface UseStateFromStoresObject {
56
-
<T>(stores: Store<any>[], callback: () => T, deps?: DependencyList): T;
57
-
}
+31
-22
packages/types/src/discord/require.ts
+31
-22
packages/types/src/discord/require.ts
···
1
-
import {
2
-
Spacepack,
3
-
CommonReact,
4
-
CommonFlux,
5
-
Settings,
6
-
CommonComponents,
7
-
CommonFluxDispatcher
8
-
} from "../coreExtensions";
1
+
import { AppPanels } from "../coreExtensions/appPanels";
2
+
import { Commands } from "../coreExtensions/commands";
3
+
import { ErrorBoundary, Icons } from "../coreExtensions/common";
4
+
import { DMList, MemberList, Messages } from "../coreExtensions/componentEditor";
9
5
import { ContextMenu, EvilItemParser } from "../coreExtensions/contextMenu";
10
6
import { Markdown } from "../coreExtensions/markdown";
7
+
import { Moonbase } from "../coreExtensions/moonbase";
8
+
import { Notices } from "../coreExtensions/notices";
9
+
import { Settings } from "../coreExtensions/settings";
10
+
import { Spacepack } from "../coreExtensions/spacepack";
11
11
12
12
declare function WebpackRequire(id: string): any;
13
-
declare function WebpackRequire(id: "spacepack_spacepack"): {
14
-
default: Spacepack;
15
-
spacepack: Spacepack;
16
-
};
13
+
14
+
declare function WebpackRequire(id: "appPanels_appPanels"): AppPanels;
15
+
16
+
declare function WebpackRequire(id: "commands_commands"): Commands;
17
+
18
+
declare function WebpackRequire(id: "common_ErrorBoundary"): ErrorBoundary;
19
+
declare function WebpackRequire(id: "common_icons"): Icons;
20
+
21
+
declare function WebpackRequire(id: "componentEditor_dmList"): DMList;
22
+
declare function WebpackRequire(id: "componentEditor_memberList"): MemberList;
23
+
declare function WebpackRequire(id: "componentEditor_messages"): Messages;
17
24
18
-
declare function WebpackRequire(id: "common_components"): CommonComponents;
19
-
declare function WebpackRequire(id: "common_flux"): CommonFlux;
20
-
declare function WebpackRequire(
21
-
id: "common_fluxDispatcher"
22
-
): CommonFluxDispatcher;
23
-
declare function WebpackRequire(id: "common_react"): CommonReact;
25
+
declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser;
26
+
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
27
+
28
+
declare function WebpackRequire(id: "markdown_markdown"): Markdown;
29
+
30
+
declare function WebpackRequire(id: "moonbase_moonbase"): Moonbase;
31
+
32
+
declare function WebpackRequire(id: "notices_notices"): Notices;
24
33
25
34
declare function WebpackRequire(id: "settings_settings"): {
26
35
Settings: Settings;
27
36
default: Settings;
28
37
};
29
38
30
-
declare function WebpackRequire(id: "markdown_markdown"): Markdown;
31
-
32
-
declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser;
33
-
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
39
+
declare function WebpackRequire(id: "spacepack_spacepack"): {
40
+
default: Spacepack;
41
+
spacepack: Spacepack;
42
+
};
34
43
35
44
export default WebpackRequire;
+10
-16
packages/types/src/discord/webpack.ts
+10
-16
packages/types/src/discord/webpack.ts
···
1
1
import WebpackRequire from "./require";
2
+
import { WebpackRequire as MappingsWebpackRequire } from "@moonlight-mod/mappings";
2
3
3
-
export type WebpackRequireType = typeof WebpackRequire & {
4
-
c: Record<string, WebpackModule>;
5
-
m: Record<string, WebpackModuleFunc>;
6
-
e: (module: number | string) => Promise<void>;
7
-
};
4
+
export type WebpackRequireType = typeof MappingsWebpackRequire &
5
+
typeof WebpackRequire & {
6
+
c: Record<string, WebpackModule>;
7
+
m: Record<string, WebpackModuleFunc>;
8
+
e: (module: number | string) => Promise<void>;
9
+
};
8
10
9
11
export type WebpackModule = {
10
12
id: string | number;
11
-
loaded: boolean;
13
+
loaded?: boolean;
12
14
exports: any;
13
15
};
14
16
15
-
export type WebpackModuleFunc = ((
16
-
module: any,
17
-
exports: any,
18
-
require: WebpackRequireType
19
-
) => void) & {
17
+
export type WebpackModuleFunc = ((module: any, exports: any, require: WebpackRequireType) => void) & {
20
18
__moonlight?: boolean;
21
19
};
22
20
23
-
export type WebpackJsonpEntry = [
24
-
number[],
25
-
{ [id: string]: WebpackModuleFunc },
26
-
(require: WebpackRequireType) => any
27
-
];
21
+
export type WebpackJsonpEntry = [number[], { [id: string]: WebpackModuleFunc }, (require: WebpackRequireType) => any];
28
22
29
23
export type WebpackJsonp = WebpackJsonpEntry[] & {
30
24
push: {
+116
-4
packages/types/src/extension.ts
+116
-4
packages/types/src/extension.ts
···
28
28
};
29
29
30
30
export type ExtensionManifest = {
31
+
$schema?: string;
32
+
33
+
/**
34
+
* A unique identifier for your extension.
35
+
*/
31
36
id: string;
37
+
38
+
/**
39
+
* A version string for your extension - doesn't need to follow a specific format. Required for publishing.
40
+
*/
32
41
version?: string;
33
42
43
+
/**
44
+
* The API level this extension targets. If it does not match the current version, the extension will not be loaded.
45
+
*/
46
+
apiLevel?: number;
47
+
48
+
/**
49
+
* Which environment this extension is capable of running in.
50
+
*/
51
+
environment?: ExtensionEnvironment;
52
+
53
+
/**
54
+
* Metadata about your extension for use in Moonbase.
55
+
*/
34
56
meta?: {
57
+
/**
58
+
* A human friendly name for your extension as a proper noun.
59
+
*/
35
60
name?: string;
61
+
62
+
/**
63
+
* A short tagline that appears below the name.
64
+
*/
36
65
tagline?: string;
66
+
67
+
/**
68
+
* A longer description that can use Markdown.
69
+
*/
37
70
description?: string;
71
+
72
+
/**
73
+
* List of authors that worked on this extension - accepts string or object with ID.
74
+
*/
38
75
authors?: ExtensionAuthor[];
39
-
deprecated?: boolean;
76
+
77
+
/**
78
+
* A list of tags that are relevant to the extension.
79
+
*/
40
80
tags?: ExtensionTag[];
81
+
82
+
/**
83
+
* The URL to the source repository.
84
+
*/
41
85
source?: string;
86
+
87
+
/**
88
+
* A donation link (or other method of support). If you don't want financial contributions, consider putting your favorite charity here!
89
+
*/
90
+
donate?: string;
91
+
92
+
/**
93
+
* A changelog to show in Moonbase.
94
+
* Moonbase will show the changelog for the latest version, even if it is not installed.
95
+
*/
96
+
changelog?: string;
97
+
98
+
/**
99
+
* Whether the extension is deprecated and no longer receiving updates.
100
+
*/
101
+
deprecated?: boolean;
42
102
};
43
103
104
+
/**
105
+
* A list of extension IDs that are required for the extension to load.
106
+
*/
44
107
dependencies?: string[];
108
+
109
+
/**
110
+
* A list of extension IDs that the user may want to install.
111
+
*/
45
112
suggested?: string[];
113
+
114
+
/**
115
+
* A list of extension IDs that the extension is incompatible with.
116
+
* If two incompatible extensions are enabled, one of them will not load.
117
+
*/
46
118
incompatible?: string[];
47
119
120
+
/**
121
+
* A list of settings for your extension, where the key is the settings ID.
122
+
*/
48
123
settings?: Record<string, ExtensionSettingsManifest>;
124
+
125
+
/**
126
+
* A list of URLs to bypass CORS for.
127
+
* This is implemented by checking if the start of the URL matches.
128
+
* @example https://moonlight-mod.github.io/
129
+
*/
49
130
cors?: string[];
131
+
132
+
/**
133
+
* A list of URLs to block all requests to.
134
+
* This is implemented by checking if the start of the URL matches.
135
+
* @example https://moonlight-mod.github.io/
136
+
*/
137
+
blocked?: string[];
138
+
139
+
/**
140
+
* A mapping from CSP directives to URLs to allow.
141
+
* @example { "script-src": ["https://example.com"] }
142
+
*/
143
+
csp?: Record<string, string[]>;
50
144
};
51
145
146
+
export enum ExtensionEnvironment {
147
+
/**
148
+
* The extension will run on both platforms, the host/native modules MAY be loaded
149
+
*/
150
+
Both = "both",
151
+
152
+
/**
153
+
* Extension will run on desktop only, the host/native modules are guaranteed to load
154
+
*/
155
+
Desktop = "desktop",
156
+
157
+
/**
158
+
* Currently equivalent to Both
159
+
*/
160
+
Web = "web"
161
+
}
162
+
52
163
export enum ExtensionLoadSource {
53
164
Developer,
54
165
Core,
···
65
176
webpackModules?: Record<string, string>;
66
177
nodePath?: string;
67
178
hostPath?: string;
179
+
style?: string;
68
180
};
69
181
};
70
182
···
96
208
export type Patch = {
97
209
find: PatchMatch;
98
210
replace: PatchReplace | PatchReplace[];
211
+
hardFail?: boolean; // if any patches fail, all fail
99
212
prerequisite?: () => boolean;
100
213
};
101
214
102
215
export type ExplicitExtensionDependency = {
103
-
ext: string;
216
+
ext?: string;
104
217
id: string;
105
218
};
106
219
···
123
236
id: number;
124
237
};
125
238
126
-
export type IdentifiedWebpackModule = ExtensionWebpackModule &
127
-
ExplicitExtensionDependency;
239
+
export type IdentifiedWebpackModule = ExtensionWebpackModule & ExplicitExtensionDependency;
+19
packages/types/src/fs.ts
+19
packages/types/src/fs.ts
···
1
+
export type MoonlightFS = {
2
+
readFile: (path: string) => Promise<Uint8Array>;
3
+
readFileString: (path: string) => Promise<string>;
4
+
writeFile: (path: string, data: Uint8Array) => Promise<void>;
5
+
writeFileString: (path: string, data: string) => Promise<void>;
6
+
unlink: (path: string) => Promise<void>;
7
+
8
+
readdir: (path: string) => Promise<string[]>;
9
+
mkdir: (path: string) => Promise<void>;
10
+
rmdir: (path: string) => Promise<void>;
11
+
12
+
exists: (path: string) => Promise<boolean>;
13
+
isFile: (path: string) => Promise<boolean>;
14
+
isDir: (path: string) => Promise<boolean>;
15
+
16
+
join: (...parts: string[]) => string;
17
+
dirname: (path: string) => string;
18
+
basename: (path: string) => string;
19
+
};
+67
-15
packages/types/src/globals.ts
+67
-15
packages/types/src/globals.ts
···
1
-
import { Logger } from "./logger";
2
-
import { Config, ConfigExtension } from "./config";
3
-
import {
4
-
DetectedExtension,
5
-
IdentifiedPatch,
6
-
IdentifiedWebpackModule,
7
-
ProcessedExtensions
8
-
} from "./extension";
9
-
import EventEmitter from "events";
1
+
import type { Logger } from "./logger";
2
+
import type { Config, ConfigExtension } from "./config";
3
+
import type { DetectedExtension, IdentifiedPatch, IdentifiedWebpackModule, ProcessedExtensions } from "./extension";
4
+
import type EventEmitter from "events";
5
+
import type LunAST from "@moonlight-mod/lunast";
6
+
import type Moonmap from "@moonlight-mod/moonmap";
7
+
import type {
8
+
WebEventPayloads,
9
+
WebEventType,
10
+
MoonlightEventEmitter,
11
+
NodeEventType,
12
+
NodeEventPayloads
13
+
} from "./core/event";
14
+
import type { MoonlightFS } from "./fs";
10
15
11
16
export type MoonlightHost = {
12
-
asarPath: string;
13
17
config: Config;
14
-
events: EventEmitter;
15
18
extensions: DetectedExtension[];
16
19
processedExtensions: ProcessedExtensions;
20
+
asarPath: string;
21
+
events: EventEmitter;
22
+
23
+
version: string;
24
+
branch: MoonlightBranch;
17
25
18
26
getConfig: (ext: string) => ConfigExtension["config"];
27
+
getConfigPath: () => Promise<string>;
19
28
getConfigOption: <T>(ext: string, name: string) => T | undefined;
29
+
setConfigOption: <T>(ext: string, name: string, value: T) => void;
30
+
writeConfig: (config: Config) => Promise<void>;
31
+
20
32
getLogger: (id: string) => Logger;
33
+
getMoonlightDir: () => string;
34
+
getExtensionDir: (ext: string) => string;
21
35
};
22
36
23
37
export type MoonlightNode = {
···
25
39
extensions: DetectedExtension[];
26
40
processedExtensions: ProcessedExtensions;
27
41
nativesCache: Record<string, any>;
42
+
isBrowser: boolean;
43
+
events: MoonlightEventEmitter<NodeEventType, NodeEventPayloads>;
44
+
45
+
version: string;
46
+
branch: MoonlightBranch;
28
47
29
48
getConfig: (ext: string) => ConfigExtension["config"];
30
49
getConfigOption: <T>(ext: string, name: string) => T | undefined;
50
+
setConfigOption: <T>(ext: string, name: string, value: T) => Promise<void>;
51
+
writeConfig: (config: Config) => Promise<void>;
52
+
31
53
getNatives: (ext: string) => any | undefined;
32
54
getLogger: (id: string) => Logger;
55
+
getMoonlightDir: () => string;
56
+
getExtensionDir: (ext: string) => string;
57
+
};
33
58
34
-
getExtensionDir: (ext: string) => string;
35
-
writeConfig: (config: Config) => void;
59
+
export type MoonlightNodeSandboxed = {
60
+
fs: MoonlightFS;
61
+
addCors: (url: string) => void;
62
+
addBlocked: (url: string) => void;
36
63
};
37
64
38
65
export type MoonlightWeb = {
66
+
patched: Map<string, Set<string>>;
39
67
unpatched: Set<IdentifiedPatch>;
40
68
pendingModules: Set<IdentifiedWebpackModule>;
41
69
enabledExtensions: Set<string>;
70
+
events: MoonlightEventEmitter<WebEventType, WebEventPayloads>;
71
+
patchingInternals: {
72
+
onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void;
73
+
registerPatch: (patch: IdentifiedPatch) => void;
74
+
registerWebpackModule: (module: IdentifiedWebpackModule) => void;
75
+
};
76
+
localStorage: Storage;
42
77
43
-
getConfig: (ext: string) => ConfigExtension["config"];
44
-
getConfigOption: <T>(ext: string, name: string) => T | undefined;
78
+
version: string;
79
+
branch: MoonlightBranch;
80
+
apiLevel: number;
81
+
82
+
// Re-exports for ease of use
83
+
getConfig: MoonlightNode["getConfig"];
84
+
getConfigOption: MoonlightNode["getConfigOption"];
85
+
setConfigOption: MoonlightNode["setConfigOption"];
86
+
writeConfig: MoonlightNode["writeConfig"];
87
+
45
88
getNatives: (ext: string) => any | undefined;
46
89
getLogger: (id: string) => Logger;
90
+
91
+
lunast: LunAST;
92
+
moonmap: Moonmap;
47
93
};
48
94
49
95
export enum MoonlightEnv {
···
51
97
NodePreload = "node-preload",
52
98
WebPreload = "web-preload"
53
99
}
100
+
101
+
export enum MoonlightBranch {
102
+
STABLE = "stable",
103
+
NIGHTLY = "nightly",
104
+
DEV = "dev"
105
+
}
+52
-27
packages/types/src/import.d.ts
+52
-27
packages/types/src/import.d.ts
···
1
-
declare module "@moonlight-mod/wp/spacepack_spacepack" {
1
+
declare module "@moonlight-mod/wp/appPanels_appPanels" {
2
2
import { CoreExtensions } from "@moonlight-mod/types";
3
-
export const spacepack: CoreExtensions.Spacepack;
4
-
export default spacepack;
3
+
const AppPanels: CoreExtensions.AppPanels.AppPanels;
4
+
export = AppPanels;
5
5
}
6
6
7
-
declare module "@moonlight-mod/wp/common_components" {
7
+
declare module "@moonlight-mod/wp/commands_commands" {
8
8
import { CoreExtensions } from "@moonlight-mod/types";
9
-
const CommonComponent: CoreExtensions.CommonComponents;
10
-
export = CommonComponent;
9
+
export const commands: CoreExtensions.Commands.Commands;
10
+
export default commands;
11
11
}
12
12
13
-
declare module "@moonlight-mod/wp/common_flux" {
13
+
declare module "@moonlight-mod/wp/common_ErrorBoundary" {
14
14
import { CoreExtensions } from "@moonlight-mod/types";
15
-
const Flux: CoreExtensions.CommonFlux;
16
-
// FIXME: This is wrong, the default export differs from the named exports.
17
-
export = Flux;
15
+
const ErrorBoundary: CoreExtensions.Common.ErrorBoundary;
16
+
export = ErrorBoundary;
18
17
}
19
-
20
-
declare module "@moonlight-mod/wp/common_fluxDispatcher" {
18
+
declare module "@moonlight-mod/wp/common_icons" {
21
19
import { CoreExtensions } from "@moonlight-mod/types";
22
-
const Dispatcher: CoreExtensions.CommonFluxDispatcher;
23
-
export default Dispatcher;
20
+
export const icons: CoreExtensions.Common.Icons;
21
+
export default icons;
24
22
}
25
-
26
23
declare module "@moonlight-mod/wp/common_stores";
27
24
28
-
declare module "@moonlight-mod/wp/common_react" {
29
-
import React from "react";
30
-
export = React;
25
+
declare module "@moonlight-mod/wp/componentEditor_dmList" {
26
+
import { CoreExtensions } from "@moonlight-mod/types";
27
+
export const dmList: CoreExtensions.ComponentEditor.DMList;
28
+
export default dmList;
29
+
}
30
+
declare module "@moonlight-mod/wp/componentEditor_memberList" {
31
+
import { CoreExtensions } from "@moonlight-mod/types";
32
+
export const memberList: CoreExtensions.ComponentEditor.MemberList;
33
+
export default memberList;
34
+
}
35
+
declare module "@moonlight-mod/wp/componentEditor_messages" {
36
+
import { CoreExtensions } from "@moonlight-mod/types";
37
+
export const message: CoreExtensions.ComponentEditor.Messages;
38
+
export default message;
31
39
}
32
40
33
-
declare module "@moonlight-mod/wp/settings_settings" {
41
+
declare module "@moonlight-mod/wp/contextMenu_evilMenu" {
34
42
import { CoreExtensions } from "@moonlight-mod/types";
35
-
export const Settings: CoreExtensions.Settings;
36
-
export default Settings;
43
+
const EvilParser: CoreExtensions.ContextMenu.EvilItemParser;
44
+
export = EvilParser;
45
+
}
46
+
declare module "@moonlight-mod/wp/contextMenu_contextMenu" {
47
+
import { CoreExtensions } from "@moonlight-mod/types";
48
+
const ContextMenu: CoreExtensions.ContextMenu.ContextMenu;
49
+
export = ContextMenu;
37
50
}
38
51
39
52
declare module "@moonlight-mod/wp/markdown_markdown" {
···
42
55
export = Markdown;
43
56
}
44
57
45
-
declare module "@moonlight-mod/wp/contextMenu_evilMenu" {
58
+
declare module "@moonlight-mod/wp/moonbase_moonbase" {
59
+
import { CoreExtensions } from "@moonlight-mod/types";
60
+
const Moonbase: CoreExtensions.Moonbase.Moonbase;
61
+
export = Moonbase;
62
+
}
63
+
64
+
declare module "@moonlight-mod/wp/notices_notices" {
46
65
import { CoreExtensions } from "@moonlight-mod/types";
47
-
const EvilParser: CoreExtensions.ContextMenu.EvilItemParser;
48
-
export = EvilParser;
66
+
const Notices: CoreExtensions.Notices.Notices;
67
+
export = Notices;
68
+
}
69
+
70
+
declare module "@moonlight-mod/wp/settings_settings" {
71
+
import { CoreExtensions } from "@moonlight-mod/types";
72
+
export const Settings: CoreExtensions.Settings.Settings;
73
+
export default Settings;
49
74
}
50
75
51
-
declare module "@moonlight-mod/wp/contextMenu_contextMenu" {
76
+
declare module "@moonlight-mod/wp/spacepack_spacepack" {
52
77
import { CoreExtensions } from "@moonlight-mod/types";
53
-
const ContextMenu: CoreExtensions.ContextMenu.ContextMenu;
54
-
export = ContextMenu;
78
+
export const spacepack: CoreExtensions.Spacepack.Spacepack;
79
+
export default spacepack;
55
80
}
+14
-7
packages/types/src/index.ts
+14
-7
packages/types/src/index.ts
···
1
1
/// <reference types="standalone-electron-types" />
2
2
/// <reference types="react" />
3
-
/// <reference types="flux" />
4
3
/// <reference types="./import" />
4
+
/// <reference types="./mappings" />
5
5
/* eslint-disable no-var */
6
6
7
-
import {
8
-
MoonlightEnv,
9
-
MoonlightHost,
10
-
MoonlightNode,
11
-
MoonlightWeb
12
-
} from "./globals";
7
+
import { MoonlightEnv, MoonlightHost, MoonlightNode, MoonlightNodeSandboxed, MoonlightWeb } from "./globals";
13
8
14
9
export * from "./discord";
15
10
export * from "./config";
···
18
13
export * from "./globals";
19
14
export * from "./logger";
20
15
export * as constants from "./constants";
16
+
export * from "./fs";
17
+
18
+
export type { AST } from "@moonlight-mod/lunast";
19
+
export { ModuleExport, ModuleExportType } from "@moonlight-mod/moonmap";
21
20
22
21
declare global {
23
22
const MOONLIGHT_ENV: MoonlightEnv;
···
25
24
const MOONLIGHT_INJECTOR: boolean;
26
25
const MOONLIGHT_NODE_PRELOAD: boolean;
27
26
const MOONLIGHT_WEB_PRELOAD: boolean;
27
+
const MOONLIGHT_BROWSER: boolean;
28
+
const MOONLIGHT_BRANCH: string;
29
+
const MOONLIGHT_VERSION: string;
28
30
29
31
var moonlightHost: MoonlightHost;
30
32
var moonlightNode: MoonlightNode;
33
+
var moonlightNodeSandboxed: MoonlightNodeSandboxed;
31
34
var moonlight: MoonlightWeb;
35
+
var _moonlight_coreExtensionsStr: string;
36
+
37
+
var _moonlightBrowserInit: undefined | (() => Promise<void>);
38
+
var _moonlightWebLoad: undefined | (() => Promise<void>);
32
39
}
+888
packages/types/src/mappings.d.ts
+888
packages/types/src/mappings.d.ts
···
1
+
// auto-generated
2
+
declare module "@moonlight-mod/wp/chroma-js" {}
3
+
4
+
declare module "@moonlight-mod/wp/classnames" {
5
+
import { MappedModules } from "@moonlight-mod/mappings";
6
+
const _default: MappedModules["classnames"]["default"];
7
+
export default _default;
8
+
}
9
+
10
+
declare module "@moonlight-mod/wp/dependency-graph" {
11
+
import { MappedModules } from "@moonlight-mod/mappings";
12
+
export const DepGraph: MappedModules["dependency-graph"]["DepGraph"];
13
+
}
14
+
15
+
declare module "@moonlight-mod/wp/discord/Constants" {
16
+
import { MappedModules } from "@moonlight-mod/mappings";
17
+
export const ActivityFlags: MappedModules["discord/Constants"]["ActivityFlags"];
18
+
export const ActivityTypes: MappedModules["discord/Constants"]["ActivityTypes"];
19
+
export const AnalyticsLocations: MappedModules["discord/Constants"]["AnalyticsLocations"];
20
+
export const ChannelLayouts: MappedModules["discord/Constants"]["ChannelLayouts"];
21
+
export const ChannelModes: MappedModules["discord/Constants"]["ChannelModes"];
22
+
export const ChannelTypes: MappedModules["discord/Constants"]["ChannelTypes"];
23
+
export const ChannelStreamTypes: MappedModules["discord/Constants"]["ChannelStreamTypes"];
24
+
export const ComponentActions: MappedModules["discord/Constants"]["ComponentActions"];
25
+
export const DEFAULT_ROLE_COLOR: MappedModules["discord/Constants"]["DEFAULT_ROLE_COLOR"];
26
+
export const Endpoints: MappedModules["discord/Constants"]["Endpoints"];
27
+
export const MessageFlags: MappedModules["discord/Constants"]["MessageFlags"];
28
+
export const MessageTypes: MappedModules["discord/Constants"]["MessageTypes"];
29
+
export const Permissions: MappedModules["discord/Constants"]["Permissions"];
30
+
export const PlatformTypes: MappedModules["discord/Constants"]["PlatformTypes"];
31
+
export const RelationshipTypes: MappedModules["discord/Constants"]["RelationshipTypes"];
32
+
export const Routes: MappedModules["discord/Constants"]["Routes"];
33
+
export const StatusTypes: MappedModules["discord/Constants"]["StatusTypes"];
34
+
export const Themes: MappedModules["discord/Constants"]["Themes"];
35
+
export const UserSettingsSections: MappedModules["discord/Constants"]["UserSettingsSections"];
36
+
export const UserFlags: MappedModules["discord/Constants"]["UserFlags"];
37
+
}
38
+
39
+
declare module "@moonlight-mod/wp/discord/Dispatcher" {
40
+
import { MappedModules } from "@moonlight-mod/mappings";
41
+
const _default: MappedModules["discord/Dispatcher"]["default"];
42
+
export default _default;
43
+
}
44
+
45
+
declare module "@moonlight-mod/wp/discord/actions/ContextMenuActionCreators" {
46
+
import { MappedModules } from "@moonlight-mod/mappings";
47
+
export const closeContextMenu: MappedModules["discord/actions/ContextMenuActionCreators"]["closeContextMenu"];
48
+
export const openContextMenu: MappedModules["discord/actions/ContextMenuActionCreators"]["openContextMenu"];
49
+
export const openContextMenuLazy: MappedModules["discord/actions/ContextMenuActionCreators"]["openContextMenuLazy"];
50
+
}
51
+
52
+
declare module "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators" {
53
+
import { MappedModules } from "@moonlight-mod/mappings";
54
+
const _default: MappedModules["discord/actions/UserSettingsModalActionCreators"]["default"];
55
+
export default _default;
56
+
}
57
+
58
+
declare module "@moonlight-mod/wp/discord/common/AppStartPerformance" {
59
+
import { MappedModules } from "@moonlight-mod/mappings";
60
+
const _default: MappedModules["discord/common/AppStartPerformance"]["default"];
61
+
export default _default;
62
+
}
63
+
64
+
declare module "@moonlight-mod/wp/discord/components/common/Alerts" {
65
+
import { MappedModules } from "@moonlight-mod/mappings";
66
+
const _default: MappedModules["discord/components/common/Alerts"]["default"];
67
+
export default _default;
68
+
}
69
+
70
+
declare module "@moonlight-mod/wp/discord/components/common/BaseHeaderBar" {
71
+
import { MappedModules } from "@moonlight-mod/mappings";
72
+
export const Icon: MappedModules["discord/components/common/BaseHeaderBar"]["Icon"];
73
+
export const Divider: MappedModules["discord/components/common/BaseHeaderBar"]["Divider"];
74
+
const _default: MappedModules["discord/components/common/BaseHeaderBar"]["default"];
75
+
export default _default;
76
+
}
77
+
78
+
declare module "@moonlight-mod/wp/discord/components/common/Card" {
79
+
import { MappedModules } from "@moonlight-mod/mappings";
80
+
const _default: MappedModules["discord/components/common/Card"]["default"];
81
+
export default _default;
82
+
export const Types: MappedModules["discord/components/common/Card"]["Types"];
83
+
}
84
+
85
+
declare module "@moonlight-mod/wp/discord/components/common/FileUpload" {
86
+
import { MappedModules } from "@moonlight-mod/mappings";
87
+
const _default: MappedModules["discord/components/common/FileUpload"]["default"];
88
+
export default _default;
89
+
}
90
+
91
+
declare module "@moonlight-mod/wp/discord/components/common/FormSwitch.css" {
92
+
import { MappedModules } from "@moonlight-mod/mappings";
93
+
export const container: MappedModules["discord/components/common/FormSwitch.css"]["container"];
94
+
export const labelRow: MappedModules["discord/components/common/FormSwitch.css"]["labelRow"];
95
+
export const control: MappedModules["discord/components/common/FormSwitch.css"]["control"];
96
+
export const disabled: MappedModules["discord/components/common/FormSwitch.css"]["disabled"];
97
+
export const title: MappedModules["discord/components/common/FormSwitch.css"]["title"];
98
+
export const note: MappedModules["discord/components/common/FormSwitch.css"]["note"];
99
+
export const disabledText: MappedModules["discord/components/common/FormSwitch.css"]["disabledText"];
100
+
export const dividerDefault: MappedModules["discord/components/common/FormSwitch.css"]["dividerDefault"];
101
+
}
102
+
103
+
declare module "@moonlight-mod/wp/discord/components/common/HeaderBar.css" {
104
+
import { MappedModules } from "@moonlight-mod/mappings";
105
+
export const caret: MappedModules["discord/components/common/HeaderBar.css"]["caret"];
106
+
export const children: MappedModules["discord/components/common/HeaderBar.css"]["children"];
107
+
export const clickable: MappedModules["discord/components/common/HeaderBar.css"]["clickable"];
108
+
export const container: MappedModules["discord/components/common/HeaderBar.css"]["container"];
109
+
export const divider: MappedModules["discord/components/common/HeaderBar.css"]["divider"];
110
+
export const dot: MappedModules["discord/components/common/HeaderBar.css"]["dot"];
111
+
export const hamburger: MappedModules["discord/components/common/HeaderBar.css"]["hamburger"];
112
+
export const icon: MappedModules["discord/components/common/HeaderBar.css"]["icon"];
113
+
export const iconBadge: MappedModules["discord/components/common/HeaderBar.css"]["iconBadge"];
114
+
export const iconBadgeBottom: MappedModules["discord/components/common/HeaderBar.css"]["iconBadgeBottom"];
115
+
export const iconBadgeTop: MappedModules["discord/components/common/HeaderBar.css"]["iconBadgeTop"];
116
+
export const iconWrapper: MappedModules["discord/components/common/HeaderBar.css"]["iconWrapper"];
117
+
export const scrollable: MappedModules["discord/components/common/HeaderBar.css"]["scrollable"];
118
+
export const selected: MappedModules["discord/components/common/HeaderBar.css"]["selected"];
119
+
export const themed: MappedModules["discord/components/common/HeaderBar.css"]["themed"];
120
+
export const themedMobile: MappedModules["discord/components/common/HeaderBar.css"]["themedMobile"];
121
+
export const title: MappedModules["discord/components/common/HeaderBar.css"]["title"];
122
+
export const titleWrapper: MappedModules["discord/components/common/HeaderBar.css"]["titleWrapper"];
123
+
export const toolbar: MappedModules["discord/components/common/HeaderBar.css"]["toolbar"];
124
+
export const transparent: MappedModules["discord/components/common/HeaderBar.css"]["transparent"];
125
+
export const upperContainer: MappedModules["discord/components/common/HeaderBar.css"]["upperContainer"];
126
+
}
127
+
128
+
declare module "@moonlight-mod/wp/discord/components/common/HelpMessage.css" {
129
+
import { MappedModules } from "@moonlight-mod/mappings";
130
+
export const container: MappedModules["discord/components/common/HelpMessage.css"]["container"];
131
+
export const icon: MappedModules["discord/components/common/HelpMessage.css"]["icon"];
132
+
export const iconDiv: MappedModules["discord/components/common/HelpMessage.css"]["iconDiv"];
133
+
export const text: MappedModules["discord/components/common/HelpMessage.css"]["text"];
134
+
export const positive: MappedModules["discord/components/common/HelpMessage.css"]["positive"];
135
+
export const warning: MappedModules["discord/components/common/HelpMessage.css"]["warning"];
136
+
export const info: MappedModules["discord/components/common/HelpMessage.css"]["info"];
137
+
export const error: MappedModules["discord/components/common/HelpMessage.css"]["error"];
138
+
}
139
+
140
+
declare module "@moonlight-mod/wp/discord/components/common/Image" {}
141
+
142
+
declare module "@moonlight-mod/wp/discord/components/common/PanelButton" {
143
+
import { MappedModules } from "@moonlight-mod/mappings";
144
+
const _default: MappedModules["discord/components/common/PanelButton"]["default"];
145
+
export default _default;
146
+
}
147
+
148
+
declare module "@moonlight-mod/wp/discord/components/common/Scroller.css" {
149
+
import { MappedModules } from "@moonlight-mod/mappings";
150
+
export const auto: MappedModules["discord/components/common/Scroller.css"]["auto"];
151
+
export const content: MappedModules["discord/components/common/Scroller.css"]["content"];
152
+
export const customTheme: MappedModules["discord/components/common/Scroller.css"]["customTheme"];
153
+
export const disableScrollAnchor: MappedModules["discord/components/common/Scroller.css"]["disableScrollAnchor"];
154
+
export const fade: MappedModules["discord/components/common/Scroller.css"]["fade"];
155
+
export const managedReactiveScroller: MappedModules["discord/components/common/Scroller.css"]["managedReactiveScroller"];
156
+
export const none: MappedModules["discord/components/common/Scroller.css"]["none"];
157
+
export const pointerCover: MappedModules["discord/components/common/Scroller.css"]["pointerCover"];
158
+
export const scrolling: MappedModules["discord/components/common/Scroller.css"]["scrolling"];
159
+
export const thin: MappedModules["discord/components/common/Scroller.css"]["thin"];
160
+
}
161
+
162
+
declare module "@moonlight-mod/wp/discord/components/common/index" {
163
+
import { MappedModules } from "@moonlight-mod/mappings";
164
+
export const Clickable: MappedModules["discord/components/common/index"]["Clickable"];
165
+
export const TextInput: MappedModules["discord/components/common/index"]["TextInput"];
166
+
export const TextArea: MappedModules["discord/components/common/index"]["TextArea"];
167
+
export const FormDivider: MappedModules["discord/components/common/index"]["FormDivider"];
168
+
export const FormSection: MappedModules["discord/components/common/index"]["FormSection"];
169
+
export const FormText: MappedModules["discord/components/common/index"]["FormText"];
170
+
export const FormTitle: MappedModules["discord/components/common/index"]["FormTitle"];
171
+
export const FormSwitch: MappedModules["discord/components/common/index"]["FormSwitch"];
172
+
export const FormItem: MappedModules["discord/components/common/index"]["FormItem"];
173
+
export const Slider: MappedModules["discord/components/common/index"]["Slider"];
174
+
export const Switch: MappedModules["discord/components/common/index"]["Switch"];
175
+
export const Button: MappedModules["discord/components/common/index"]["Button"];
176
+
export const Tooltip: MappedModules["discord/components/common/index"]["Tooltip"];
177
+
export const Avatar: MappedModules["discord/components/common/index"]["Avatar"];
178
+
export const AvatarSizes: MappedModules["discord/components/common/index"]["AvatarSizes"];
179
+
export const AvatarSizeSpecs: MappedModules["discord/components/common/index"]["AvatarSizeSpecs"];
180
+
export const Scroller: MappedModules["discord/components/common/index"]["Scroller"];
181
+
export const Text: MappedModules["discord/components/common/index"]["Text"];
182
+
export const Heading: MappedModules["discord/components/common/index"]["Heading"];
183
+
export const Card: MappedModules["discord/components/common/index"]["Card"];
184
+
export const Popout: MappedModules["discord/components/common/index"]["Popout"];
185
+
export const Dialog: MappedModules["discord/components/common/index"]["Dialog"];
186
+
export const Menu: MappedModules["discord/components/common/index"]["Menu"];
187
+
export const TabBar: MappedModules["discord/components/common/index"]["TabBar"];
188
+
export const SingleSelect: MappedModules["discord/components/common/index"]["SingleSelect"];
189
+
export const Select: MappedModules["discord/components/common/index"]["Select"];
190
+
export const NoticeColors: MappedModules["discord/components/common/index"]["NoticeColors"];
191
+
export const Notice: MappedModules["discord/components/common/index"]["Notice"];
192
+
export const NoticeCloseButton: MappedModules["discord/components/common/index"]["NoticeCloseButton"];
193
+
export const PrimaryCTANoticeButton: MappedModules["discord/components/common/index"]["PrimaryCTANoticeButton"];
194
+
export const Breadcrumbs: MappedModules["discord/components/common/index"]["Breadcrumbs"];
195
+
export const Image: MappedModules["discord/components/common/index"]["Image"];
196
+
export const tokens: MappedModules["discord/components/common/index"]["tokens"];
197
+
export const useVariableSelect: MappedModules["discord/components/common/index"]["useVariableSelect"];
198
+
export const useMultiSelect: MappedModules["discord/components/common/index"]["useMultiSelect"];
199
+
export const multiSelect: MappedModules["discord/components/common/index"]["multiSelect"];
200
+
export const openModal: MappedModules["discord/components/common/index"]["openModal"];
201
+
export const openModalLazy: MappedModules["discord/components/common/index"]["openModalLazy"];
202
+
export const closeModal: MappedModules["discord/components/common/index"]["closeModal"];
203
+
export const AngleBracketsIcon: MappedModules["discord/components/common/index"]["AngleBracketsIcon"];
204
+
export const ArrowAngleLeftUpIcon: MappedModules["discord/components/common/index"]["ArrowAngleLeftUpIcon"];
205
+
export const ArrowAngleRightUpIcon: MappedModules["discord/components/common/index"]["ArrowAngleRightUpIcon"];
206
+
export const ArrowsUpDownIcon: MappedModules["discord/components/common/index"]["ArrowsUpDownIcon"];
207
+
export const BookCheckIcon: MappedModules["discord/components/common/index"]["BookCheckIcon"];
208
+
export const ChannelListIcon: MappedModules["discord/components/common/index"]["ChannelListIcon"];
209
+
export const ChevronSmallDownIcon: MappedModules["discord/components/common/index"]["ChevronSmallDownIcon"];
210
+
export const ChevronSmallUpIcon: MappedModules["discord/components/common/index"]["ChevronSmallUpIcon"];
211
+
export const CircleInformationIcon: MappedModules["discord/components/common/index"]["CircleInformationIcon"];
212
+
export const CircleWarningIcon: MappedModules["discord/components/common/index"]["CircleWarningIcon"];
213
+
export const CircleXIcon: MappedModules["discord/components/common/index"]["CircleXIcon"];
214
+
export const ClydeIcon: MappedModules["discord/components/common/index"]["ClydeIcon"];
215
+
export const CopyIcon: MappedModules["discord/components/common/index"]["CopyIcon"];
216
+
export const DownloadIcon: MappedModules["discord/components/common/index"]["DownloadIcon"];
217
+
export const FullscreenEnterIcon: MappedModules["discord/components/common/index"]["FullscreenEnterIcon"];
218
+
export const GameControllerIcon: MappedModules["discord/components/common/index"]["GameControllerIcon"];
219
+
export const GlobeEarthIcon: MappedModules["discord/components/common/index"]["GlobeEarthIcon"];
220
+
export const HeartIcon: MappedModules["discord/components/common/index"]["HeartIcon"];
221
+
export const LinkIcon: MappedModules["discord/components/common/index"]["LinkIcon"];
222
+
export const MaximizeIcon: MappedModules["discord/components/common/index"]["MaximizeIcon"];
223
+
export const MinusIcon: MappedModules["discord/components/common/index"]["MinusIcon"];
224
+
export const MobilePhoneIcon: MappedModules["discord/components/common/index"]["MobilePhoneIcon"];
225
+
export const PauseIcon: MappedModules["discord/components/common/index"]["PauseIcon"];
226
+
export const PlayIcon: MappedModules["discord/components/common/index"]["PlayIcon"];
227
+
export const PlusLargeIcon: MappedModules["discord/components/common/index"]["PlusLargeIcon"];
228
+
export const RetryIcon: MappedModules["discord/components/common/index"]["RetryIcon"];
229
+
export const ScienceIcon: MappedModules["discord/components/common/index"]["ScienceIcon"];
230
+
export const ScreenIcon: MappedModules["discord/components/common/index"]["ScreenIcon"];
231
+
export const StarIcon: MappedModules["discord/components/common/index"]["StarIcon"];
232
+
export const TrashIcon: MappedModules["discord/components/common/index"]["TrashIcon"];
233
+
export const WarningIcon: MappedModules["discord/components/common/index"]["WarningIcon"];
234
+
export const WindowLaunchIcon: MappedModules["discord/components/common/index"]["WindowLaunchIcon"];
235
+
export const WindowTopOutlineIcon: MappedModules["discord/components/common/index"]["WindowTopOutlineIcon"];
236
+
export const XLargeIcon: MappedModules["discord/components/common/index"]["XLargeIcon"];
237
+
export const XSmallIcon: MappedModules["discord/components/common/index"]["XSmallIcon"];
238
+
export const ConfirmModal: MappedModules["discord/components/common/index"]["ConfirmModal"];
239
+
export const H: MappedModules["discord/components/common/index"]["H"];
240
+
export const HelpMessage: MappedModules["discord/components/common/index"]["HelpMessage"];
241
+
export const ModalCloseButton: MappedModules["discord/components/common/index"]["ModalCloseButton"];
242
+
export const ModalContent: MappedModules["discord/components/common/index"]["ModalContent"];
243
+
export const ModalFooter: MappedModules["discord/components/common/index"]["ModalFooter"];
244
+
export const ModalHeader: MappedModules["discord/components/common/index"]["ModalHeader"];
245
+
export const ModalRoot: MappedModules["discord/components/common/index"]["ModalRoot"];
246
+
export const NumberInputStepper: MappedModules["discord/components/common/index"]["NumberInputStepper"];
247
+
export const SearchableSelect: MappedModules["discord/components/common/index"]["SearchableSelect"];
248
+
export const createToast: MappedModules["discord/components/common/index"]["createToast"];
249
+
export const popToast: MappedModules["discord/components/common/index"]["popToast"];
250
+
export const showToast: MappedModules["discord/components/common/index"]["showToast"];
251
+
export const useThemeContext: MappedModules["discord/components/common/index"]["useThemeContext"];
252
+
export const AccessibilityAnnouncer: MappedModules["discord/components/common/index"]["AccessibilityAnnouncer"];
253
+
export const BackdropStyles: MappedModules["discord/components/common/index"]["BackdropStyles"];
254
+
export const BadgeShapes: MappedModules["discord/components/common/index"]["BadgeShapes"];
255
+
export const CardTypes: MappedModules["discord/components/common/index"]["CardTypes"];
256
+
export const CircleIconButtonColors: MappedModules["discord/components/common/index"]["CircleIconButtonColors"];
257
+
export const CircleIconButtonSizes: MappedModules["discord/components/common/index"]["CircleIconButtonSizes"];
258
+
export const FormErrorBlockColors: MappedModules["discord/components/common/index"]["FormErrorBlockColors"];
259
+
export const FormNoticeImagePositions: MappedModules["discord/components/common/index"]["FormNoticeImagePositions"];
260
+
export const FormTitleTags: MappedModules["discord/components/common/index"]["FormTitleTags"];
261
+
export const HelpMessageTypes: MappedModules["discord/components/common/index"]["HelpMessageTypes"];
262
+
export const ModalSize: MappedModules["discord/components/common/index"]["ModalSize"];
263
+
export const ModalTransitionState: MappedModules["discord/components/common/index"]["ModalTransitionState"];
264
+
export const PRETTY_KEYS: MappedModules["discord/components/common/index"]["PRETTY_KEYS"];
265
+
export const SelectLooks: MappedModules["discord/components/common/index"]["SelectLooks"];
266
+
export const SpinnerTypes: MappedModules["discord/components/common/index"]["SpinnerTypes"];
267
+
export const StatusTypes: MappedModules["discord/components/common/index"]["StatusTypes"];
268
+
export const ToastPosition: MappedModules["discord/components/common/index"]["ToastPosition"];
269
+
export const ToastType: MappedModules["discord/components/common/index"]["ToastType"];
270
+
export const TransitionStates: MappedModules["discord/components/common/index"]["TransitionStates"];
271
+
export const DEFAULT_MODAL_CONTEXT: MappedModules["discord/components/common/index"]["DEFAULT_MODAL_CONTEXT"];
272
+
export const LOW_SATURATION_THRESHOLD: MappedModules["discord/components/common/index"]["LOW_SATURATION_THRESHOLD"];
273
+
export const LayerClassName: MappedModules["discord/components/common/index"]["LayerClassName"];
274
+
export const POPOUT_MODAL_CONTEXT: MappedModules["discord/components/common/index"]["POPOUT_MODAL_CONTEXT"];
275
+
}
276
+
277
+
declare module "@moonlight-mod/wp/discord/components/modals/ConfirmModal" {
278
+
import { MappedModules } from "@moonlight-mod/mappings";
279
+
const _default: MappedModules["discord/components/modals/ConfirmModal"]["default"];
280
+
export default _default;
281
+
}
282
+
283
+
declare module "@moonlight-mod/wp/discord/lib/BaseRecord" {
284
+
import { MappedModules } from "@moonlight-mod/mappings";
285
+
const _default: MappedModules["discord/lib/BaseRecord"]["default"];
286
+
export default _default;
287
+
}
288
+
289
+
declare module "@moonlight-mod/wp/discord/lib/web/Storage" {
290
+
import { MappedModules } from "@moonlight-mod/mappings";
291
+
export const ObjectStorage: MappedModules["discord/lib/web/Storage"]["ObjectStorage"];
292
+
export const impl: MappedModules["discord/lib/web/Storage"]["impl"];
293
+
}
294
+
295
+
declare module "@moonlight-mod/wp/discord/modules/build_overrides/web/BuildOverride.css" {
296
+
import { MappedModules } from "@moonlight-mod/mappings";
297
+
export const wrapper: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["wrapper"];
298
+
export const titleRegion: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["titleRegion"];
299
+
export const title: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["title"];
300
+
export const infoIcon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["infoIcon"];
301
+
export const copyLink: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copyLink"];
302
+
export const copied: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copied"];
303
+
export const copyLinkIcon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copyLinkIcon"];
304
+
export const content: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["content"];
305
+
export const infoLink: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["infoLink"];
306
+
export const buildInfo: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buildInfo"];
307
+
export const button: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["button"];
308
+
export const buttonSize: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buttonSize"];
309
+
export const subHead: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["subHead"];
310
+
export const icon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["icon"];
311
+
export const buildDetails: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buildDetails"];
312
+
export const barLoader: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["barLoader"];
313
+
export const barTitle: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["barTitle"];
314
+
export const buttonLoader: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buttonLoader"];
315
+
export const disabledButtonOverride: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["disabledButtonOverride"];
316
+
}
317
+
318
+
declare module "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css" {
319
+
import { MappedModules } from "@moonlight-mod/mappings";
320
+
export const header: MappedModules["discord/modules/discovery/web/Discovery.css"]["header"];
321
+
export const headerImage: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImage"];
322
+
export const headerImageSimple: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImageSimple"];
323
+
export const headerImageBG: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImageBG"];
324
+
export const searchTitle: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchTitle"];
325
+
export const searchSubtitle: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchSubtitle"];
326
+
export const headerContentWrapper: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContentWrapper"];
327
+
export const headerContent: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContent"];
328
+
export const headerContentSmall: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContentSmall"];
329
+
export const searchBox: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchBox"];
330
+
export const searchBoxInput: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchBoxInput"];
331
+
export const closeIcon: MappedModules["discord/modules/discovery/web/Discovery.css"]["closeIcon"];
332
+
export const searchIcon: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchIcon"];
333
+
export const tabBar: MappedModules["discord/modules/discovery/web/Discovery.css"]["tabBar"];
334
+
export const tabBarItem: MappedModules["discord/modules/discovery/web/Discovery.css"]["tabBarItem"];
335
+
export const sectionHeader: MappedModules["discord/modules/discovery/web/Discovery.css"]["sectionHeader"];
336
+
}
337
+
338
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Forums.css" {
339
+
import { MappedModules } from "@moonlight-mod/mappings";
340
+
export const container: MappedModules["discord/modules/forums/web/Forums.css"]["container"];
341
+
export const uploadArea: MappedModules["discord/modules/forums/web/Forums.css"]["uploadArea"];
342
+
export const label: MappedModules["discord/modules/forums/web/Forums.css"]["label"];
343
+
export const content: MappedModules["discord/modules/forums/web/Forums.css"]["content"];
344
+
export const noListContainer: MappedModules["discord/modules/forums/web/Forums.css"]["noListContainer"];
345
+
export const list: MappedModules["discord/modules/forums/web/Forums.css"]["list"];
346
+
export const grid: MappedModules["discord/modules/forums/web/Forums.css"]["grid"];
347
+
export const headerRow: MappedModules["discord/modules/forums/web/Forums.css"]["headerRow"];
348
+
export const card: MappedModules["discord/modules/forums/web/Forums.css"]["card"];
349
+
export const columnsSpan: MappedModules["discord/modules/forums/web/Forums.css"]["columnsSpan"];
350
+
export const emptyStateRow: MappedModules["discord/modules/forums/web/Forums.css"]["emptyStateRow"];
351
+
export const newMemberBanner: MappedModules["discord/modules/forums/web/Forums.css"]["newMemberBanner"];
352
+
export const gridViewBanner: MappedModules["discord/modules/forums/web/Forums.css"]["gridViewBanner"];
353
+
export const placeholder: MappedModules["discord/modules/forums/web/Forums.css"]["placeholder"];
354
+
export const mainCard: MappedModules["discord/modules/forums/web/Forums.css"]["mainCard"];
355
+
export const emptyMainCard: MappedModules["discord/modules/forums/web/Forums.css"]["emptyMainCard"];
356
+
export const outOfDate: MappedModules["discord/modules/forums/web/Forums.css"]["outOfDate"];
357
+
export const header: MappedModules["discord/modules/forums/web/Forums.css"]["header"];
358
+
export const matchingPostsRow: MappedModules["discord/modules/forums/web/Forums.css"]["matchingPostsRow"];
359
+
export const headerWithMatchingPosts: MappedModules["discord/modules/forums/web/Forums.css"]["headerWithMatchingPosts"];
360
+
export const noForm: MappedModules["discord/modules/forums/web/Forums.css"]["noForm"];
361
+
export const sortContainer: MappedModules["discord/modules/forums/web/Forums.css"]["sortContainer"];
362
+
export const sort: MappedModules["discord/modules/forums/web/Forums.css"]["sort"];
363
+
export const sortPopout: MappedModules["discord/modules/forums/web/Forums.css"]["sortPopout"];
364
+
export const archivedDividerRow: MappedModules["discord/modules/forums/web/Forums.css"]["archivedDividerRow"];
365
+
export const archivedDivider: MappedModules["discord/modules/forums/web/Forums.css"]["archivedDivider"];
366
+
export const newPostsButton: MappedModules["discord/modules/forums/web/Forums.css"]["newPostsButton"];
367
+
export const loadingCard: MappedModules["discord/modules/forums/web/Forums.css"]["loadingCard"];
368
+
export const enterIcon: MappedModules["discord/modules/forums/web/Forums.css"]["enterIcon"];
369
+
export const warnIcon: MappedModules["discord/modules/forums/web/Forums.css"]["warnIcon"];
370
+
export const searchIcon: MappedModules["discord/modules/forums/web/Forums.css"]["searchIcon"];
371
+
export const missingReadHistoryPermission: MappedModules["discord/modules/forums/web/Forums.css"]["missingReadHistoryPermission"];
372
+
export const divider: MappedModules["discord/modules/forums/web/Forums.css"]["divider"];
373
+
export const tagsContainer: MappedModules["discord/modules/forums/web/Forums.css"]["tagsContainer"];
374
+
export const filterIcon: MappedModules["discord/modules/forums/web/Forums.css"]["filterIcon"];
375
+
export const tagList: MappedModules["discord/modules/forums/web/Forums.css"]["tagList"];
376
+
export const tagListInner: MappedModules["discord/modules/forums/web/Forums.css"]["tagListInner"];
377
+
export const tag: MappedModules["discord/modules/forums/web/Forums.css"]["tag"];
378
+
export const tagsButton: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButton"];
379
+
export const tagsButtonInner: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonInner"];
380
+
export const tagsButtonPlaceholder: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonPlaceholder"];
381
+
export const tagsButtonWithCount: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonWithCount"];
382
+
export const sortDropdown: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdown"];
383
+
export const sortDropdownInner: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdownInner"];
384
+
export const sortDropdownText: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdownText"];
385
+
export const clear: MappedModules["discord/modules/forums/web/Forums.css"]["clear"];
386
+
export const matchingPosts: MappedModules["discord/modules/forums/web/Forums.css"]["matchingPosts"];
387
+
export const startPostHelp: MappedModules["discord/modules/forums/web/Forums.css"]["startPostHelp"];
388
+
export const tagsSpacer: MappedModules["discord/modules/forums/web/Forums.css"]["tagsSpacer"];
389
+
export const keyboardShortcut: MappedModules["discord/modules/forums/web/Forums.css"]["keyboardShortcut"];
390
+
export const key: MappedModules["discord/modules/forums/web/Forums.css"]["key"];
391
+
export const countContainer: MappedModules["discord/modules/forums/web/Forums.css"]["countContainer"];
392
+
export const countText: MappedModules["discord/modules/forums/web/Forums.css"]["countText"];
393
+
export const optInNotice: MappedModules["discord/modules/forums/web/Forums.css"]["optInNotice"];
394
+
}
395
+
396
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Header.css" {
397
+
import { MappedModules } from "@moonlight-mod/mappings";
398
+
export const container: MappedModules["discord/modules/forums/web/Header.css"]["container"];
399
+
export const header: MappedModules["discord/modules/forums/web/Header.css"]["header"];
400
+
export const headerLeft: MappedModules["discord/modules/forums/web/Header.css"]["headerLeft"];
401
+
export const headerText: MappedModules["discord/modules/forums/web/Header.css"]["headerText"];
402
+
export const countContainer: MappedModules["discord/modules/forums/web/Header.css"]["countContainer"];
403
+
export const countText: MappedModules["discord/modules/forums/web/Header.css"]["countText"];
404
+
export const tagContainer: MappedModules["discord/modules/forums/web/Header.css"]["tagContainer"];
405
+
export const tag: MappedModules["discord/modules/forums/web/Header.css"]["tag"];
406
+
export const clear: MappedModules["discord/modules/forums/web/Header.css"]["clear"];
407
+
export const row: MappedModules["discord/modules/forums/web/Header.css"]["row"];
408
+
export const separator: MappedModules["discord/modules/forums/web/Header.css"]["separator"];
409
+
}
410
+
411
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/SortMenu.css" {
412
+
import { MappedModules } from "@moonlight-mod/mappings";
413
+
export const container: MappedModules["discord/modules/forums/web/SortMenu.css"]["container"];
414
+
export const clearText: MappedModules["discord/modules/forums/web/SortMenu.css"]["clearText"];
415
+
}
416
+
417
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Tag" {
418
+
import { MappedModules } from "@moonlight-mod/mappings";
419
+
const _default: MappedModules["discord/modules/forums/web/Tag"]["default"];
420
+
export default _default;
421
+
export const TagBar: MappedModules["discord/modules/forums/web/Tag"]["TagBar"];
422
+
}
423
+
424
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css" {
425
+
import { MappedModules } from "@moonlight-mod/mappings";
426
+
export const addButton: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["addButton"];
427
+
export const container: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["container"];
428
+
export const emptyRowContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["emptyRowContainer"];
429
+
export const emptyRowText: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["emptyRowText"];
430
+
export const headerContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["headerContainer"];
431
+
export const list: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["list"];
432
+
export const memberDetails: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["memberDetails"];
433
+
export const memberRow: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["memberRow"];
434
+
export const removeButton: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButton"];
435
+
export const removeButtonContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButtonContainer"];
436
+
export const removeButtonDisabled: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButtonDisabled"];
437
+
export const removeTip: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeTip"];
438
+
export const searchContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["searchContainer"];
439
+
export const searchWarning: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["searchWarning"];
440
+
}
441
+
442
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCard.css" {
443
+
import { MappedModules } from "@moonlight-mod/mappings";
444
+
export const card: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["card"];
445
+
export const inModal: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["inModal"];
446
+
export const cardHeader: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["cardHeader"];
447
+
export const title: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["title"];
448
+
}
449
+
450
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCardItem.css" {
451
+
import { MappedModules } from "@moonlight-mod/mappings";
452
+
export const icon: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["icon"];
453
+
export const identifier: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["identifier"];
454
+
export const item: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["item"];
455
+
export const statusContainer: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusContainer"];
456
+
export const statusLine: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusLine"];
457
+
export const statusIcon: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusIcon"];
458
+
}
459
+
460
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/SearchSection.css" {
461
+
import { MappedModules } from "@moonlight-mod/mappings";
462
+
export const container: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["container"];
463
+
export const headerContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["headerContainer"];
464
+
export const searchContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["searchContainer"];
465
+
export const searchWarning: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["searchWarning"];
466
+
export const addButton: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["addButton"];
467
+
export const memberRow: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["memberRow"];
468
+
export const emptyRowContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["emptyRowContainer"];
469
+
export const emptyRowText: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["emptyRowText"];
470
+
export const memberDetails: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["memberDetails"];
471
+
export const list: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["list"];
472
+
export const removeButtonContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButtonContainer"];
473
+
export const removeButton: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButton"];
474
+
export const removeButtonDisabled: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButtonDisabled"];
475
+
export const removeTip: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeTip"];
476
+
}
477
+
478
+
declare module "@moonlight-mod/wp/discord/modules/guild_sidebar/web/CategoryChannel.css" {
479
+
import { MappedModules } from "@moonlight-mod/mappings";
480
+
export const containerDefault: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDefault"];
481
+
export const containerDragBefore: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDragBefore"];
482
+
export const containerDragAfter: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDragAfter"];
483
+
export const addButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["addButton"];
484
+
export const forceVisible: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["forceVisible"];
485
+
export const iconVisibility: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["iconVisibility"];
486
+
export const addButtonIcon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["addButtonIcon"];
487
+
export const wrapper: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["wrapper"];
488
+
export const wrapperStatic: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["wrapperStatic"];
489
+
export const clickable: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["clickable"];
490
+
export const children: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["children"];
491
+
export const mainContent: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["mainContent"];
492
+
export const icon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["icon"];
493
+
export const collapsed: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["collapsed"];
494
+
export const muted: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["muted"];
495
+
export const name: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["name"];
496
+
export const dismissWrapper: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismissWrapper"];
497
+
export const dismissButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismissButton"];
498
+
export const dismiss: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismiss"];
499
+
export const voiceChannelsButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["voiceChannelsButton"];
500
+
export const voiceChannelsToggleIcon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["voiceChannelsToggleIcon"];
501
+
export const refreshVoiceChannelsButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["refreshVoiceChannelsButton"];
502
+
export const refreshVoiceChannelsButtonInner: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["refreshVoiceChannelsButtonInner"];
503
+
}
504
+
505
+
declare module "@moonlight-mod/wp/discord/modules/markup/MarkupUtils" {
506
+
import { MappedModules } from "@moonlight-mod/mappings";
507
+
const _default: MappedModules["discord/modules/markup/MarkupUtils"]["default"];
508
+
export default _default;
509
+
}
510
+
511
+
declare module "@moonlight-mod/wp/discord/modules/menus/web/Menu" {
512
+
import { MappedModules } from "@moonlight-mod/mappings";
513
+
export const MenuSpinner: MappedModules["discord/modules/menus/web/Menu"]["MenuSpinner"];
514
+
export const Menu: MappedModules["discord/modules/menus/web/Menu"]["Menu"];
515
+
}
516
+
517
+
declare module "@moonlight-mod/wp/discord/modules/messages/web/Markup.css" {
518
+
import { MappedModules } from "@moonlight-mod/mappings";
519
+
export const markup: MappedModules["discord/modules/messages/web/Markup.css"]["markup"];
520
+
export const inlineFormat: MappedModules["discord/modules/messages/web/Markup.css"]["inlineFormat"];
521
+
export const codeContainer: MappedModules["discord/modules/messages/web/Markup.css"]["codeContainer"];
522
+
export const codeActions: MappedModules["discord/modules/messages/web/Markup.css"]["codeActions"];
523
+
export const blockquoteContainer: MappedModules["discord/modules/messages/web/Markup.css"]["blockquoteContainer"];
524
+
export const blockquoteDivider: MappedModules["discord/modules/messages/web/Markup.css"]["blockquoteDivider"];
525
+
export const slateBlockquoteContainer: MappedModules["discord/modules/messages/web/Markup.css"]["slateBlockquoteContainer"];
526
+
export const roleMention: MappedModules["discord/modules/messages/web/Markup.css"]["roleMention"];
527
+
export const rolePopout: MappedModules["discord/modules/messages/web/Markup.css"]["rolePopout"];
528
+
export const roleHeader: MappedModules["discord/modules/messages/web/Markup.css"]["roleHeader"];
529
+
export const roleScroller: MappedModules["discord/modules/messages/web/Markup.css"]["roleScroller"];
530
+
export const timestamp: MappedModules["discord/modules/messages/web/Markup.css"]["timestamp"];
531
+
export const timestampTooltip: MappedModules["discord/modules/messages/web/Markup.css"]["timestampTooltip"];
532
+
}
533
+
534
+
declare module "@moonlight-mod/wp/discord/modules/messages/web/Message.css" {
535
+
import { MappedModules } from "@moonlight-mod/mappings";
536
+
export const wrapper: MappedModules["discord/modules/messages/web/Message.css"]["wrapper"];
537
+
export const compact: MappedModules["discord/modules/messages/web/Message.css"]["compact"];
538
+
export const cozy: MappedModules["discord/modules/messages/web/Message.css"]["cozy"];
539
+
export const contentOnly: MappedModules["discord/modules/messages/web/Message.css"]["contentOnly"];
540
+
export const repliedMessage: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessage"];
541
+
export const threadMessageAccessory: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessory"];
542
+
export const executedCommand: MappedModules["discord/modules/messages/web/Message.css"]["executedCommand"];
543
+
export const latin12CompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["latin12CompactTimeStamp"];
544
+
export const latin24CompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["latin24CompactTimeStamp"];
545
+
export const asianCompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["asianCompactTimeStamp"];
546
+
export const contextCommandMessage: MappedModules["discord/modules/messages/web/Message.css"]["contextCommandMessage"];
547
+
export const messageSpine: MappedModules["discord/modules/messages/web/Message.css"]["messageSpine"];
548
+
export const repliedMessageClickableSpine: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageClickableSpine"];
549
+
export const repliedMessageContentHovered: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageContentHovered"];
550
+
export const threadMessageAccessoryAvatar: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryAvatar"];
551
+
export const replyAvatar: MappedModules["discord/modules/messages/web/Message.css"]["replyAvatar"];
552
+
export const replyBadge: MappedModules["discord/modules/messages/web/Message.css"]["replyBadge"];
553
+
export const executedCommandAvatar: MappedModules["discord/modules/messages/web/Message.css"]["executedCommandAvatar"];
554
+
export const replyChatIconContainer: MappedModules["discord/modules/messages/web/Message.css"]["replyChatIconContainer"];
555
+
export const replyIcon: MappedModules["discord/modules/messages/web/Message.css"]["replyIcon"];
556
+
export const clanTagChiplet: MappedModules["discord/modules/messages/web/Message.css"]["clanTagChiplet"];
557
+
export const userJoinSystemMessageIcon: MappedModules["discord/modules/messages/web/Message.css"]["userJoinSystemMessageIcon"];
558
+
export const ticketIcon: MappedModules["discord/modules/messages/web/Message.css"]["ticketIcon"];
559
+
export const commandIcon: MappedModules["discord/modules/messages/web/Message.css"]["commandIcon"];
560
+
export const username: MappedModules["discord/modules/messages/web/Message.css"]["username"];
561
+
export const roleDot: MappedModules["discord/modules/messages/web/Message.css"]["roleDot"];
562
+
export const commandName: MappedModules["discord/modules/messages/web/Message.css"]["commandName"];
563
+
export const appsIcon: MappedModules["discord/modules/messages/web/Message.css"]["appsIcon"];
564
+
export const appLauncherOnboardingCommandName: MappedModules["discord/modules/messages/web/Message.css"]["appLauncherOnboardingCommandName"];
565
+
export const targetUsername: MappedModules["discord/modules/messages/web/Message.css"]["targetUsername"];
566
+
export const executedCommandSeparator: MappedModules["discord/modules/messages/web/Message.css"]["executedCommandSeparator"];
567
+
export const repliedTextPreview: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextPreview"];
568
+
export const threadMessageAccessoryPreview: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryPreview"];
569
+
export const repliedTextContent: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContent"];
570
+
export const clickable: MappedModules["discord/modules/messages/web/Message.css"]["clickable"];
571
+
export const repliedMessageClickableSpineHovered: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageClickableSpineHovered"];
572
+
export const threadMessageAccessoryContent: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContent"];
573
+
export const repliedTextPlaceholder: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextPlaceholder"];
574
+
export const threadMessageAccessoryPlaceholder: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryPlaceholder"];
575
+
export const repliedTextContentTrailingIcon: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContentTrailingIcon"];
576
+
export const threadMessageAccessoryContentTrailingIcon: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContentTrailingIcon"];
577
+
export const repliedTextContentLeadingIcon: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContentLeadingIcon"];
578
+
export const threadMessageAccessoryContentLeadingIcon: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContentLeadingIcon"];
579
+
export const contents: MappedModules["discord/modules/messages/web/Message.css"]["contents"];
580
+
export const zalgo: MappedModules["discord/modules/messages/web/Message.css"]["zalgo"];
581
+
export const messageContent: MappedModules["discord/modules/messages/web/Message.css"]["messageContent"];
582
+
export const header: MappedModules["discord/modules/messages/web/Message.css"]["header"];
583
+
export const buttonContainer: MappedModules["discord/modules/messages/web/Message.css"]["buttonContainer"];
584
+
export const avatar: MappedModules["discord/modules/messages/web/Message.css"]["avatar"];
585
+
export const avatarDecoration: MappedModules["discord/modules/messages/web/Message.css"]["avatarDecoration"];
586
+
export const roleIcon: MappedModules["discord/modules/messages/web/Message.css"]["roleIcon"];
587
+
export const timestamp: MappedModules["discord/modules/messages/web/Message.css"]["timestamp"];
588
+
export const timestampInline: MappedModules["discord/modules/messages/web/Message.css"]["timestampInline"];
589
+
export const alt: MappedModules["discord/modules/messages/web/Message.css"]["alt"];
590
+
export const timestampTooltip: MappedModules["discord/modules/messages/web/Message.css"]["timestampTooltip"];
591
+
export const timestampVisibleOnHover: MappedModules["discord/modules/messages/web/Message.css"]["timestampVisibleOnHover"];
592
+
export const nitroAuthorBadgeTootip: MappedModules["discord/modules/messages/web/Message.css"]["nitroAuthorBadgeTootip"];
593
+
export const headerText: MappedModules["discord/modules/messages/web/Message.css"]["headerText"];
594
+
export const hasRoleIcon: MappedModules["discord/modules/messages/web/Message.css"]["hasRoleIcon"];
595
+
export const hasBadges: MappedModules["discord/modules/messages/web/Message.css"]["hasBadges"];
596
+
export const botTagCompact: MappedModules["discord/modules/messages/web/Message.css"]["botTagCompact"];
597
+
export const botTagCozy: MappedModules["discord/modules/messages/web/Message.css"]["botTagCozy"];
598
+
export const nitroBadgeSvg: MappedModules["discord/modules/messages/web/Message.css"]["nitroBadgeSvg"];
599
+
export const nitroAuthorBadgeContainer: MappedModules["discord/modules/messages/web/Message.css"]["nitroAuthorBadgeContainer"];
600
+
export const separator: MappedModules["discord/modules/messages/web/Message.css"]["separator"];
601
+
export const hasThread: MappedModules["discord/modules/messages/web/Message.css"]["hasThread"];
602
+
export const isSystemMessage: MappedModules["discord/modules/messages/web/Message.css"]["isSystemMessage"];
603
+
export const hasReply: MappedModules["discord/modules/messages/web/Message.css"]["hasReply"];
604
+
export const markupRtl: MappedModules["discord/modules/messages/web/Message.css"]["markupRtl"];
605
+
export const isSending: MappedModules["discord/modules/messages/web/Message.css"]["isSending"];
606
+
export const isFailed: MappedModules["discord/modules/messages/web/Message.css"]["isFailed"];
607
+
export const isUnsupported: MappedModules["discord/modules/messages/web/Message.css"]["isUnsupported"];
608
+
export const edited: MappedModules["discord/modules/messages/web/Message.css"]["edited"];
609
+
export const communicationDisabled: MappedModules["discord/modules/messages/web/Message.css"]["communicationDisabled"];
610
+
export const compactCommunicationDisabled: MappedModules["discord/modules/messages/web/Message.css"]["compactCommunicationDisabled"];
611
+
export const communicationDisabledOpacity: MappedModules["discord/modules/messages/web/Message.css"]["communicationDisabledOpacity"];
612
+
export const badgesContainer: MappedModules["discord/modules/messages/web/Message.css"]["badgesContainer"];
613
+
}
614
+
615
+
declare module "@moonlight-mod/wp/discord/modules/modals/Modals" {
616
+
import { MappedModules } from "@moonlight-mod/mappings";
617
+
export const closeAllModals: MappedModules["discord/modules/modals/Modals"]["closeAllModals"];
618
+
export const closeAllModalsForContext: MappedModules["discord/modules/modals/Modals"]["closeAllModalsForContext"];
619
+
export const closeModal: MappedModules["discord/modules/modals/Modals"]["closeModal"];
620
+
export const getInteractingModalContext: MappedModules["discord/modules/modals/Modals"]["getInteractingModalContext"];
621
+
export const hasAnyModalOpen: MappedModules["discord/modules/modals/Modals"]["hasAnyModalOpen"];
622
+
export const hasAnyModalOpenSelector: MappedModules["discord/modules/modals/Modals"]["hasAnyModalOpenSelector"];
623
+
export const hasModalOpen: MappedModules["discord/modules/modals/Modals"]["hasModalOpen"];
624
+
export const hasModalOpenSelector: MappedModules["discord/modules/modals/Modals"]["hasModalOpenSelector"];
625
+
export const openModal: MappedModules["discord/modules/modals/Modals"]["openModal"];
626
+
export const openModalLazy: MappedModules["discord/modules/modals/Modals"]["openModalLazy"];
627
+
export const updateModal: MappedModules["discord/modules/modals/Modals"]["updateModal"];
628
+
export const useHasAnyModalOpen: MappedModules["discord/modules/modals/Modals"]["useHasAnyModalOpen"];
629
+
export const useIsModalAtTop: MappedModules["discord/modules/modals/Modals"]["useIsModalAtTop"];
630
+
export const useModalsStore: MappedModules["discord/modules/modals/Modals"]["useModalsStore"];
631
+
}
632
+
633
+
declare module "@moonlight-mod/wp/discord/modules/oauth2/index" {
634
+
import { MappedModules } from "@moonlight-mod/mappings";
635
+
export const OAuth2AuthorizeModal: MappedModules["discord/modules/oauth2/index"]["OAuth2AuthorizeModal"];
636
+
export const OAuth2AuthorizePage: MappedModules["discord/modules/oauth2/index"]["OAuth2AuthorizePage"];
637
+
export const getOAuth2AuthorizeProps: MappedModules["discord/modules/oauth2/index"]["getOAuth2AuthorizeProps"];
638
+
export const openOAuth2Modal: MappedModules["discord/modules/oauth2/index"]["openOAuth2Modal"];
639
+
export const openOAuth2ModalWithCreateGuildModal: MappedModules["discord/modules/oauth2/index"]["openOAuth2ModalWithCreateGuildModal"];
640
+
export const useOAuth2AuthorizeForm: MappedModules["discord/modules/oauth2/index"]["useOAuth2AuthorizeForm"];
641
+
}
642
+
643
+
declare module "@moonlight-mod/wp/discord/modules/people/web/PeoplePage.css" {
644
+
import { MappedModules } from "@moonlight-mod/mappings";
645
+
export const addFriend: MappedModules["discord/modules/people/web/PeoplePage.css"]["addFriend"];
646
+
export const badge: MappedModules["discord/modules/people/web/PeoplePage.css"]["badge"];
647
+
export const container: MappedModules["discord/modules/people/web/PeoplePage.css"]["container"];
648
+
export const inviteToolbar: MappedModules["discord/modules/people/web/PeoplePage.css"]["inviteToolbar"];
649
+
export const item: MappedModules["discord/modules/people/web/PeoplePage.css"]["item"];
650
+
export const nowPlayingColumn: MappedModules["discord/modules/people/web/PeoplePage.css"]["nowPlayingColumn"];
651
+
export const peopleColumn: MappedModules["discord/modules/people/web/PeoplePage.css"]["peopleColumn"];
652
+
export const tabBar: MappedModules["discord/modules/people/web/PeoplePage.css"]["tabBar"];
653
+
export const tabBody: MappedModules["discord/modules/people/web/PeoplePage.css"]["tabBody"];
654
+
}
655
+
656
+
declare module "@moonlight-mod/wp/discord/modules/user_profile/web/BiteSizeActivity.css" {
657
+
import { MappedModules } from "@moonlight-mod/mappings";
658
+
export const header: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["header"];
659
+
export const headerTag: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["headerTag"];
660
+
export const body: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["body"];
661
+
export const footer: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["footer"];
662
+
export const backdrop: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["backdrop"];
663
+
export const toast: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["toast"];
664
+
export const activity: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["activity"];
665
+
export const upsell: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["upsell"];
666
+
}
667
+
668
+
declare module "@moonlight-mod/wp/discord/packages/flux" {
669
+
import { MappedModules } from "@moonlight-mod/mappings";
670
+
export const BatchedStoreListener: MappedModules["discord/packages/flux"]["BatchedStoreListener"];
671
+
export const Dispatcher: MappedModules["discord/packages/flux"]["Dispatcher"];
672
+
export const Store: MappedModules["discord/packages/flux"]["Store"];
673
+
const _default: MappedModules["discord/packages/flux"]["default"];
674
+
export default _default;
675
+
export const statesWillNeverBeEqual: MappedModules["discord/packages/flux"]["statesWillNeverBeEqual"];
676
+
export const useStateFromStores: MappedModules["discord/packages/flux"]["useStateFromStores"];
677
+
export const useStateFromStoresArray: MappedModules["discord/packages/flux"]["useStateFromStoresArray"];
678
+
export const useStateFromStoresObject: MappedModules["discord/packages/flux"]["useStateFromStoresObject"];
679
+
}
680
+
681
+
declare module "@moonlight-mod/wp/discord/packages/flux/BatchedStoreListener" {
682
+
import { MappedModules } from "@moonlight-mod/mappings";
683
+
const _default: MappedModules["discord/packages/flux/BatchedStoreListener"]["default"];
684
+
export default _default;
685
+
}
686
+
687
+
declare module "@moonlight-mod/wp/discord/packages/flux/ChangeListeners" {
688
+
import { MappedModules } from "@moonlight-mod/mappings";
689
+
const _default: MappedModules["discord/packages/flux/ChangeListeners"]["default"];
690
+
export default _default;
691
+
}
692
+
693
+
declare module "@moonlight-mod/wp/discord/packages/flux/Dispatcher" {
694
+
import { MappedModules } from "@moonlight-mod/mappings";
695
+
export const Dispatcher: MappedModules["discord/packages/flux/Dispatcher"]["Dispatcher"];
696
+
}
697
+
698
+
declare module "@moonlight-mod/wp/discord/packages/flux/Emitter" {
699
+
import { MappedModules } from "@moonlight-mod/mappings";
700
+
const _default: MappedModules["discord/packages/flux/Emitter"]["default"];
701
+
export default _default;
702
+
}
703
+
704
+
declare module "@moonlight-mod/wp/discord/packages/flux/LoggingUtils" {
705
+
import { MappedModules } from "@moonlight-mod/mappings";
706
+
const _default: MappedModules["discord/packages/flux/LoggingUtils"]["default"];
707
+
export default _default;
708
+
}
709
+
710
+
declare module "@moonlight-mod/wp/discord/packages/flux/PersistedStore" {
711
+
import { MappedModules } from "@moonlight-mod/mappings";
712
+
export const PersistedStore: MappedModules["discord/packages/flux/PersistedStore"]["PersistedStore"];
713
+
}
714
+
715
+
declare module "@moonlight-mod/wp/discord/packages/flux/Store" {
716
+
import { MappedModules } from "@moonlight-mod/mappings";
717
+
export const Store: MappedModules["discord/packages/flux/Store"]["Store"];
718
+
}
719
+
720
+
declare module "@moonlight-mod/wp/discord/packages/flux/connectStores" {
721
+
import { MappedModules } from "@moonlight-mod/mappings";
722
+
const _default: MappedModules["discord/packages/flux/connectStores"]["default"];
723
+
export default _default;
724
+
}
725
+
726
+
declare module "@moonlight-mod/wp/discord/records/UserRecord" {
727
+
import { MappedModules } from "@moonlight-mod/mappings";
728
+
const _default: MappedModules["discord/records/UserRecord"]["default"];
729
+
export default _default;
730
+
}
731
+
732
+
declare module "@moonlight-mod/wp/discord/styles/shared/Margins.css" {
733
+
import { MappedModules } from "@moonlight-mod/mappings";
734
+
export const marginReset: MappedModules["discord/styles/shared/Margins.css"]["marginReset"];
735
+
export const marginTop4: MappedModules["discord/styles/shared/Margins.css"]["marginTop4"];
736
+
export const marginBottom4: MappedModules["discord/styles/shared/Margins.css"]["marginBottom4"];
737
+
export const marginTop8: MappedModules["discord/styles/shared/Margins.css"]["marginTop8"];
738
+
export const marginBottom8: MappedModules["discord/styles/shared/Margins.css"]["marginBottom8"];
739
+
export const marginTop20: MappedModules["discord/styles/shared/Margins.css"]["marginTop20"];
740
+
export const marginBottom20: MappedModules["discord/styles/shared/Margins.css"]["marginBottom20"];
741
+
export const marginTop40: MappedModules["discord/styles/shared/Margins.css"]["marginTop40"];
742
+
export const marginBottom40: MappedModules["discord/styles/shared/Margins.css"]["marginBottom40"];
743
+
export const marginTop60: MappedModules["discord/styles/shared/Margins.css"]["marginTop60"];
744
+
export const marginBottom60: MappedModules["discord/styles/shared/Margins.css"]["marginBottom60"];
745
+
export const marginCenterHorz: MappedModules["discord/styles/shared/Margins.css"]["marginCenterHorz"];
746
+
export const marginLeft8: MappedModules["discord/styles/shared/Margins.css"]["marginLeft8"];
747
+
}
748
+
749
+
declare module "@moonlight-mod/wp/discord/uikit/Flex" {
750
+
import { MappedModules } from "@moonlight-mod/mappings";
751
+
const _default: MappedModules["discord/uikit/Flex"]["default"];
752
+
export default _default;
753
+
}
754
+
755
+
declare module "@moonlight-mod/wp/discord/utils/ClipboardUtils" {
756
+
import { MappedModules } from "@moonlight-mod/mappings";
757
+
export const SUPPORTS_COPY: MappedModules["discord/utils/ClipboardUtils"]["SUPPORTS_COPY"];
758
+
export const copy: MappedModules["discord/utils/ClipboardUtils"]["copy"];
759
+
}
760
+
761
+
declare module "@moonlight-mod/wp/discord/utils/ComponentDispatchUtils" {
762
+
import { MappedModules } from "@moonlight-mod/mappings";
763
+
export const ComponentDispatcher: MappedModules["discord/utils/ComponentDispatchUtils"]["ComponentDispatcher"];
764
+
export const ComponentDispatch: MappedModules["discord/utils/ComponentDispatchUtils"]["ComponentDispatch"];
765
+
}
766
+
767
+
declare module "@moonlight-mod/wp/discord/utils/HTTPUtils" {
768
+
import { MappedModules } from "@moonlight-mod/mappings";
769
+
export const HTTP: MappedModules["discord/utils/HTTPUtils"]["HTTP"];
770
+
}
771
+
772
+
declare module "@moonlight-mod/wp/discord/utils/MaskedLinkUtils" {
773
+
import { MappedModules } from "@moonlight-mod/mappings";
774
+
export const isLinkTrusted: MappedModules["discord/utils/MaskedLinkUtils"]["isLinkTrusted"];
775
+
export const handleClick: MappedModules["discord/utils/MaskedLinkUtils"]["handleClick"];
776
+
}
777
+
778
+
declare module "@moonlight-mod/wp/discord/utils/NativeUtils" {
779
+
import { MappedModules } from "@moonlight-mod/mappings";
780
+
const _default: MappedModules["discord/utils/NativeUtils"]["default"];
781
+
export default _default;
782
+
}
783
+
784
+
declare module "@moonlight-mod/wp/highlight.js" {
785
+
import { MappedModules } from "@moonlight-mod/mappings";
786
+
export const highlight: MappedModules["highlight.js"]["highlight"];
787
+
export const highlightAuto: MappedModules["highlight.js"]["highlightAuto"];
788
+
export const fixMarkup: MappedModules["highlight.js"]["fixMarkup"];
789
+
export const highlightBlock: MappedModules["highlight.js"]["highlightBlock"];
790
+
export const configure: MappedModules["highlight.js"]["configure"];
791
+
export const initHighlighting: MappedModules["highlight.js"]["initHighlighting"];
792
+
export const initHighlightingOnLoad: MappedModules["highlight.js"]["initHighlightingOnLoad"];
793
+
export const registerLanguage: MappedModules["highlight.js"]["registerLanguage"];
794
+
export const listLanguages: MappedModules["highlight.js"]["listLanguages"];
795
+
export const getLanguage: MappedModules["highlight.js"]["getLanguage"];
796
+
export const inherit: MappedModules["highlight.js"]["inherit"];
797
+
export const COMMENT: MappedModules["highlight.js"]["COMMENT"];
798
+
export const IDENT_RE: MappedModules["highlight.js"]["IDENT_RE"];
799
+
export const UNDERSCORE_IDENT_RE: MappedModules["highlight.js"]["UNDERSCORE_IDENT_RE"];
800
+
export const NUMBER_RE: MappedModules["highlight.js"]["NUMBER_RE"];
801
+
export const C_NUMBER_RE: MappedModules["highlight.js"]["C_NUMBER_RE"];
802
+
export const BINARY_NUMBER_RE: MappedModules["highlight.js"]["BINARY_NUMBER_RE"];
803
+
export const RE_STARTERS_RE: MappedModules["highlight.js"]["RE_STARTERS_RE"];
804
+
export const BACKSLASH_ESCAPE: MappedModules["highlight.js"]["BACKSLASH_ESCAPE"];
805
+
export const APOS_STRING_MODE: MappedModules["highlight.js"]["APOS_STRING_MODE"];
806
+
export const QUOTE_STRING_MODE: MappedModules["highlight.js"]["QUOTE_STRING_MODE"];
807
+
export const PHRASAL_WORDS_MODE: MappedModules["highlight.js"]["PHRASAL_WORDS_MODE"];
808
+
export const C_LINE_COMMENT_MODE: MappedModules["highlight.js"]["C_LINE_COMMENT_MODE"];
809
+
export const C_BLOCK_COMMENT_MODE: MappedModules["highlight.js"]["C_BLOCK_COMMENT_MODE"];
810
+
export const HASH_COMMENT_MODE: MappedModules["highlight.js"]["HASH_COMMENT_MODE"];
811
+
export const NUMBER_MODE: MappedModules["highlight.js"]["NUMBER_MODE"];
812
+
export const C_NUMBER_MODE: MappedModules["highlight.js"]["C_NUMBER_MODE"];
813
+
export const BINARY_NUMBER_MODE: MappedModules["highlight.js"]["BINARY_NUMBER_MODE"];
814
+
export const CSS_NUMBER_MODE: MappedModules["highlight.js"]["CSS_NUMBER_MODE"];
815
+
export const REGEX_MODE: MappedModules["highlight.js"]["REGEX_MODE"];
816
+
export const TITLE_MODE: MappedModules["highlight.js"]["TITLE_MODE"];
817
+
export const UNDERSCORE_TITLE_MODE: MappedModules["highlight.js"]["UNDERSCORE_TITLE_MODE"];
818
+
const _default: MappedModules["highlight.js"]["default"];
819
+
export default _default;
820
+
export const HighlightJS: MappedModules["highlight.js"]["HighlightJS"];
821
+
}
822
+
823
+
declare module "@moonlight-mod/wp/highlight.js/lib/core" {
824
+
import { MappedModules } from "@moonlight-mod/mappings";
825
+
export const highlight: MappedModules["highlight.js/lib/core"]["highlight"];
826
+
export const highlightAuto: MappedModules["highlight.js/lib/core"]["highlightAuto"];
827
+
export const fixMarkup: MappedModules["highlight.js/lib/core"]["fixMarkup"];
828
+
export const highlightBlock: MappedModules["highlight.js/lib/core"]["highlightBlock"];
829
+
export const configure: MappedModules["highlight.js/lib/core"]["configure"];
830
+
export const initHighlighting: MappedModules["highlight.js/lib/core"]["initHighlighting"];
831
+
export const initHighlightingOnLoad: MappedModules["highlight.js/lib/core"]["initHighlightingOnLoad"];
832
+
export const registerLanguage: MappedModules["highlight.js/lib/core"]["registerLanguage"];
833
+
export const listLanguages: MappedModules["highlight.js/lib/core"]["listLanguages"];
834
+
export const getLanguage: MappedModules["highlight.js/lib/core"]["getLanguage"];
835
+
export const inherit: MappedModules["highlight.js/lib/core"]["inherit"];
836
+
export const COMMENT: MappedModules["highlight.js/lib/core"]["COMMENT"];
837
+
export const IDENT_RE: MappedModules["highlight.js/lib/core"]["IDENT_RE"];
838
+
export const UNDERSCORE_IDENT_RE: MappedModules["highlight.js/lib/core"]["UNDERSCORE_IDENT_RE"];
839
+
export const NUMBER_RE: MappedModules["highlight.js/lib/core"]["NUMBER_RE"];
840
+
export const C_NUMBER_RE: MappedModules["highlight.js/lib/core"]["C_NUMBER_RE"];
841
+
export const BINARY_NUMBER_RE: MappedModules["highlight.js/lib/core"]["BINARY_NUMBER_RE"];
842
+
export const RE_STARTERS_RE: MappedModules["highlight.js/lib/core"]["RE_STARTERS_RE"];
843
+
export const BACKSLASH_ESCAPE: MappedModules["highlight.js/lib/core"]["BACKSLASH_ESCAPE"];
844
+
export const APOS_STRING_MODE: MappedModules["highlight.js/lib/core"]["APOS_STRING_MODE"];
845
+
export const QUOTE_STRING_MODE: MappedModules["highlight.js/lib/core"]["QUOTE_STRING_MODE"];
846
+
export const PHRASAL_WORDS_MODE: MappedModules["highlight.js/lib/core"]["PHRASAL_WORDS_MODE"];
847
+
export const C_LINE_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["C_LINE_COMMENT_MODE"];
848
+
export const C_BLOCK_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["C_BLOCK_COMMENT_MODE"];
849
+
export const HASH_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["HASH_COMMENT_MODE"];
850
+
export const NUMBER_MODE: MappedModules["highlight.js/lib/core"]["NUMBER_MODE"];
851
+
export const C_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["C_NUMBER_MODE"];
852
+
export const BINARY_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["BINARY_NUMBER_MODE"];
853
+
export const CSS_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["CSS_NUMBER_MODE"];
854
+
export const REGEX_MODE: MappedModules["highlight.js/lib/core"]["REGEX_MODE"];
855
+
export const TITLE_MODE: MappedModules["highlight.js/lib/core"]["TITLE_MODE"];
856
+
export const UNDERSCORE_TITLE_MODE: MappedModules["highlight.js/lib/core"]["UNDERSCORE_TITLE_MODE"];
857
+
}
858
+
859
+
declare module "@moonlight-mod/wp/lodash" {}
860
+
861
+
declare module "@moonlight-mod/wp/murmurhash" {
862
+
import { MappedModules } from "@moonlight-mod/mappings";
863
+
export const v2: MappedModules["murmurhash"]["v2"];
864
+
export const v3: MappedModules["murmurhash"]["v3"];
865
+
}
866
+
867
+
declare module "@moonlight-mod/wp/platform.js" {
868
+
import { MappedModules } from "@moonlight-mod/mappings";
869
+
export const description: MappedModules["platform.js"]["description"];
870
+
export const layout: MappedModules["platform.js"]["layout"];
871
+
export const manufacturer: MappedModules["platform.js"]["manufacturer"];
872
+
export const name: MappedModules["platform.js"]["name"];
873
+
export const prerelease: MappedModules["platform.js"]["prerelease"];
874
+
export const product: MappedModules["platform.js"]["product"];
875
+
export const ua: MappedModules["platform.js"]["ua"];
876
+
export const version: MappedModules["platform.js"]["version"];
877
+
export const os: MappedModules["platform.js"]["os"];
878
+
export const parse: MappedModules["platform.js"]["parse"];
879
+
export const toString: MappedModules["platform.js"]["toString"];
880
+
}
881
+
882
+
declare module "@moonlight-mod/wp/react" {
883
+
import { MappedModules } from "@moonlight-mod/mappings";
884
+
const _: Omit<MappedModules["react"], "__mappings_exportEquals">;
885
+
export = _;
886
+
}
887
+
888
+
declare module "@moonlight-mod/wp/uuid/v4" {}
+7
-7
packages/types/tsconfig.json
+7
-7
packages/types/tsconfig.json
···
1
1
{
2
2
"compilerOptions": {
3
-
"target": "es2016",
4
-
"module": "es6",
5
-
"esModuleInterop": true,
6
-
"forceConsistentCasingInFileNames": true,
7
-
"strict": true,
8
-
"moduleResolution": "bundler",
3
+
"target": "ES2016",
9
4
"jsx": "react",
10
-
"declaration": true
5
+
"module": "ES6",
6
+
"moduleResolution": "bundler",
7
+
"strict": true,
8
+
"declaration": true,
9
+
"esModuleInterop": true,
10
+
"forceConsistentCasingInFileNames": true
11
11
},
12
12
"include": ["./src/**/*", "src/index.ts", "./src/import.d.ts"]
13
13
}
+13
-1
packages/web-preload/package.json
+13
-1
packages/web-preload/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/web-preload",
3
3
"private": true,
4
+
"main": "src/index.ts",
5
+
"engineStrict": true,
6
+
"engines": {
7
+
"node": ">=22",
8
+
"pnpm": ">=10",
9
+
"npm": "pnpm",
10
+
"yarn": "pnpm"
11
+
},
4
12
"dependencies": {
5
-
"@moonlight-mod/core": "workspace:*"
13
+
"@moonlight-mod/core": "workspace:*",
14
+
"@moonlight-mod/lunast": "catalog:prod",
15
+
"@moonlight-mod/mappings": "catalog:prod",
16
+
"@moonlight-mod/moonmap": "catalog:prod",
17
+
"@moonlight-mod/types": "workspace:*"
6
18
}
7
19
}
+40
-8
packages/web-preload/src/index.ts
+40
-8
packages/web-preload/src/index.ts
···
1
1
import { loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
2
-
import { installWebpackPatcher } from "@moonlight-mod/core/patch";
2
+
import { installWebpackPatcher, onModuleLoad, registerPatch, registerWebpackModule } from "@moonlight-mod/core/patch";
3
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
3
4
import { installStyles } from "@moonlight-mod/core/styles";
4
-
import Logger from "@moonlight-mod/core/util/logger";
5
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
6
+
import LunAST from "@moonlight-mod/lunast";
7
+
import Moonmap from "@moonlight-mod/moonmap";
8
+
import loadMappings from "@moonlight-mod/mappings";
9
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
10
+
import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event";
5
11
6
-
(async () => {
12
+
async function load() {
13
+
delete window._moonlightWebLoad;
14
+
initLogger(moonlightNode.config);
7
15
const logger = new Logger("web-preload");
8
16
9
17
window.moonlight = {
18
+
patched: new Map(),
10
19
unpatched: new Set(),
11
20
pendingModules: new Set(),
12
21
enabledExtensions: new Set(),
13
22
23
+
events: createEventEmitter<WebEventType, WebEventPayloads>(),
24
+
patchingInternals: {
25
+
onModuleLoad,
26
+
registerPatch,
27
+
registerWebpackModule
28
+
},
29
+
localStorage: window.localStorage,
30
+
31
+
version: MOONLIGHT_VERSION,
32
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
33
+
apiLevel: constants.apiLevel,
34
+
14
35
getConfig: moonlightNode.getConfig.bind(moonlightNode),
15
36
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
37
+
setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode),
38
+
writeConfig: moonlightNode.writeConfig.bind(moonlightNode),
39
+
16
40
getNatives: moonlightNode.getNatives.bind(moonlightNode),
17
-
getLogger: (id: string) => {
41
+
getLogger(id) {
18
42
return new Logger(id);
19
-
}
43
+
},
44
+
45
+
lunast: new LunAST(),
46
+
moonmap: new Moonmap()
20
47
};
21
48
22
49
try {
50
+
loadMappings(window.moonlight.moonmap, window.moonlight.lunast);
23
51
await loadProcessedExtensions(moonlightNode.processedExtensions);
24
52
await installWebpackPatcher();
25
53
} catch (e) {
26
54
logger.error("Error setting up web-preload", e);
27
55
}
28
56
29
-
window.addEventListener("DOMContentLoaded", () => {
57
+
if (document.readyState === "complete") {
30
58
installStyles();
31
-
});
32
-
})();
59
+
} else {
60
+
window.addEventListener("load", installStyles);
61
+
}
62
+
}
63
+
64
+
window._moonlightWebLoad = load;
+4
-1
packages/web-preload/tsconfig.json
+4
-1
packages/web-preload/tsconfig.json
+1683
-1139
pnpm-lock.yaml
+1683
-1139
pnpm-lock.yaml
···
4
4
autoInstallPeers: true
5
5
excludeLinksFromLockfile: false
6
6
7
+
catalogs:
8
+
dev:
9
+
'@moonlight-mod/eslint-config':
10
+
specifier: github:moonlight-mod/eslint-config
11
+
version: 1.0.1
12
+
'@types/chrome':
13
+
specifier: ^0.0.313
14
+
version: 0.0.313
15
+
'@types/node':
16
+
specifier: ^22.14.0
17
+
version: 22.14.0
18
+
esbuild:
19
+
specifier: ^0.19.3
20
+
version: 0.19.3
21
+
esbuild-copy-static-files:
22
+
specifier: ^0.1.0
23
+
version: 0.1.0
24
+
eslint:
25
+
specifier: ^9.12.0
26
+
version: 9.23.0
27
+
husky:
28
+
specifier: ^8.0.3
29
+
version: 8.0.3
30
+
prettier:
31
+
specifier: ^3.1.0
32
+
version: 3.1.0
33
+
taze:
34
+
specifier: ^19.0.4
35
+
version: 19.0.4
36
+
typescript:
37
+
specifier: ^5.3.3
38
+
version: 5.8.2
39
+
prod:
40
+
'@moonlight-mod/lunast':
41
+
specifier: ^1.0.1
42
+
version: 1.0.1
43
+
'@moonlight-mod/mappings':
44
+
specifier: ^1.1.25
45
+
version: 1.1.25
46
+
'@moonlight-mod/moonmap':
47
+
specifier: ^1.0.5
48
+
version: 1.0.5
49
+
'@zenfs/core':
50
+
specifier: ^2.0.0
51
+
version: 2.0.0
52
+
'@zenfs/dom':
53
+
specifier: ^1.1.3
54
+
version: 1.1.6
55
+
microdiff:
56
+
specifier: ^1.5.0
57
+
version: 1.5.0
58
+
nanotar:
59
+
specifier: ^0.1.1
60
+
version: 0.1.1
61
+
7
62
importers:
8
63
9
64
.:
10
65
devDependencies:
11
-
'@typescript-eslint/eslint-plugin':
12
-
specifier: ^6.13.2
13
-
version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.2)
14
-
'@typescript-eslint/parser':
15
-
specifier: ^6.13.2
16
-
version: 6.13.2(eslint@8.55.0)(typescript@5.3.2)
66
+
'@moonlight-mod/eslint-config':
67
+
specifier: catalog:dev
68
+
version: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9(@types/eslint@9.6.1)(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0)(typescript@5.8.2)
69
+
'@types/node':
70
+
specifier: catalog:dev
71
+
version: 22.14.0
17
72
esbuild:
18
-
specifier: ^0.19.3
73
+
specifier: catalog:dev
19
74
version: 0.19.3
20
75
esbuild-copy-static-files:
21
-
specifier: ^0.1.0
76
+
specifier: catalog:dev
22
77
version: 0.1.0
23
78
eslint:
24
-
specifier: ^8.55.0
25
-
version: 8.55.0
26
-
eslint-config-prettier:
27
-
specifier: ^9.1.0
28
-
version: 9.1.0(eslint@8.55.0)
29
-
eslint-plugin-prettier:
30
-
specifier: ^5.0.1
31
-
version: 5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.0)
32
-
eslint-plugin-react:
33
-
specifier: ^7.33.2
34
-
version: 7.33.2(eslint@8.55.0)
79
+
specifier: catalog:dev
80
+
version: 9.23.0(jiti@2.4.2)
35
81
husky:
36
-
specifier: ^8.0.3
82
+
specifier: catalog:dev
37
83
version: 8.0.3
38
84
prettier:
39
-
specifier: ^3.1.0
85
+
specifier: catalog:dev
40
86
version: 3.1.0
87
+
taze:
88
+
specifier: catalog:dev
89
+
version: 19.0.4
41
90
typescript:
42
-
specifier: ^5.3.2
43
-
version: 5.3.2
91
+
specifier: catalog:dev
92
+
version: 5.8.2
93
+
94
+
packages/browser:
95
+
dependencies:
96
+
'@moonlight-mod/core':
97
+
specifier: workspace:*
98
+
version: link:../core
99
+
'@moonlight-mod/types':
100
+
specifier: workspace:*
101
+
version: link:../types
102
+
'@moonlight-mod/web-preload':
103
+
specifier: workspace:*
104
+
version: link:../web-preload
105
+
'@zenfs/core':
106
+
specifier: catalog:prod
107
+
version: 2.0.0
108
+
'@zenfs/dom':
109
+
specifier: catalog:prod
110
+
version: 1.1.6(@zenfs/core@2.0.0)(utilium@1.10.1)
111
+
devDependencies:
112
+
'@types/chrome':
113
+
specifier: catalog:dev
114
+
version: 0.0.313
44
115
45
116
packages/core:
46
117
dependencies:
···
50
121
51
122
packages/core-extensions:
52
123
dependencies:
53
-
'@electron/asar':
54
-
specifier: ^3.2.5
55
-
version: 3.2.5
124
+
'@moonlight-mod/core':
125
+
specifier: workspace:*
126
+
version: link:../core
56
127
'@moonlight-mod/types':
57
128
specifier: workspace:*
58
129
version: link:../types
130
+
microdiff:
131
+
specifier: catalog:prod
132
+
version: 1.5.0
133
+
nanotar:
134
+
specifier: catalog:prod
135
+
version: 0.1.1
59
136
60
137
packages/injector:
61
138
dependencies:
···
77
154
78
155
packages/types:
79
156
dependencies:
80
-
'@types/flux':
81
-
specifier: ^3.1.12
82
-
version: 3.1.12
157
+
'@moonlight-mod/lunast':
158
+
specifier: ^1.0.1
159
+
version: 1.0.1
160
+
'@moonlight-mod/mappings':
161
+
specifier: ^1.1.25
162
+
version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)
163
+
'@moonlight-mod/moonmap':
164
+
specifier: ^1.0.5
165
+
version: 1.0.5
83
166
'@types/react':
84
-
specifier: ^18.2.22
85
-
version: 18.2.22
167
+
specifier: ^18.3.10
168
+
version: 18.3.20
86
169
csstype:
87
-
specifier: ^3.1.2
88
-
version: 3.1.2
170
+
specifier: ^3.1.3
171
+
version: 3.1.3
89
172
standalone-electron-types:
90
173
specifier: ^1.0.0
91
174
version: 1.0.0
···
95
178
'@moonlight-mod/core':
96
179
specifier: workspace:*
97
180
version: link:../core
181
+
'@moonlight-mod/lunast':
182
+
specifier: catalog:prod
183
+
version: 1.0.1
184
+
'@moonlight-mod/mappings':
185
+
specifier: catalog:prod
186
+
version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)
187
+
'@moonlight-mod/moonmap':
188
+
specifier: catalog:prod
189
+
version: 1.0.5
190
+
'@moonlight-mod/types':
191
+
specifier: workspace:*
192
+
version: link:../types
98
193
99
194
packages:
100
195
···
102
197
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
103
198
engines: {node: '>=0.10.0'}
104
199
105
-
'@electron/asar@3.2.5':
106
-
resolution: {integrity: sha512-Ypahc2ElTj9YOrFvUHuoXv5Z/V1nPA5enlhmQapc578m/HZBHKTbqhoL5JZQjje2+/6Ti5AHh7Gj1/haeJa63Q==}
107
-
engines: {node: '>=10.12.0'}
200
+
'@antfu/ni@24.3.0':
201
+
resolution: {integrity: sha512-wBSav4mBxvHEW9RbdSo1SWLQ6MAlT0Dc423weC58yOWqW4OcMvtnNDdDrxOZeJ88fEIyPK93gDUWIelBxzSf8g==}
108
202
hasBin: true
109
203
110
204
'@esbuild/android-arm64@0.19.3':
···
239
333
cpu: [x64]
240
334
os: [win32]
241
335
242
-
'@eslint-community/eslint-utils@4.4.0':
243
-
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
336
+
'@eslint-community/eslint-utils@4.5.1':
337
+
resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==}
244
338
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
245
339
peerDependencies:
246
340
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
247
341
248
-
'@eslint-community/regexpp@4.10.0':
249
-
resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
342
+
'@eslint-community/regexpp@4.12.1':
343
+
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
250
344
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
251
345
252
-
'@eslint/eslintrc@2.1.4':
253
-
resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
254
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
346
+
'@eslint/config-array@0.19.2':
347
+
resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
348
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
255
349
256
-
'@eslint/js@8.55.0':
257
-
resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==}
258
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
350
+
'@eslint/config-helpers@0.2.1':
351
+
resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
352
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
259
353
260
-
'@humanwhocodes/config-array@0.11.13':
261
-
resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
262
-
engines: {node: '>=10.10.0'}
354
+
'@eslint/core@0.12.0':
355
+
resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
356
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
357
+
358
+
'@eslint/core@0.13.0':
359
+
resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
360
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
361
+
362
+
'@eslint/eslintrc@3.3.1':
363
+
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
364
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
365
+
366
+
'@eslint/js@9.23.0':
367
+
resolution: {integrity: sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==}
368
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
369
+
370
+
'@eslint/object-schema@2.1.6':
371
+
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
372
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
373
+
374
+
'@eslint/plugin-kit@0.2.8':
375
+
resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
376
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
377
+
378
+
'@humanfs/core@0.19.1':
379
+
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
380
+
engines: {node: '>=18.18.0'}
381
+
382
+
'@humanfs/node@0.16.6':
383
+
resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
384
+
engines: {node: '>=18.18.0'}
263
385
264
386
'@humanwhocodes/module-importer@1.0.1':
265
387
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
266
388
engines: {node: '>=12.22'}
267
389
268
-
'@humanwhocodes/object-schema@2.0.1':
269
-
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
390
+
'@humanwhocodes/retry@0.3.1':
391
+
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
392
+
engines: {node: '>=18.18'}
393
+
394
+
'@humanwhocodes/retry@0.4.2':
395
+
resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
396
+
engines: {node: '>=18.18'}
397
+
398
+
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9':
399
+
resolution: {tarball: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9}
400
+
version: 1.0.1
401
+
peerDependencies:
402
+
eslint: '>= 9'
403
+
typescript: '>= 5.3'
404
+
405
+
'@moonlight-mod/lunast@1.0.1':
406
+
resolution: {integrity: sha512-K3vxzDlfFuYKjciIW2FMlcZ1qrrkAGDGpSBlNqYGtJ0sMt9bRCd2lpSpg6AX/giSljDtmAUXa/5mOfUoDQxjBA==}
407
+
408
+
'@moonlight-mod/mappings@1.1.25':
409
+
resolution: {integrity: sha512-bgnSN9H/IBdMGxGev6RQKXuzhQxwo1090NhIDHnflguZnjiu2pg/usPfh76bqyhxRuX4SS7tiZSNTwBoSflCLg==}
410
+
engines: {node: '>=22', npm: pnpm, pnpm: '>=10', yarn: pnpm}
411
+
peerDependencies:
412
+
'@moonlight-mod/lunast': ^1.0.1
413
+
'@moonlight-mod/moonmap': ^1.0.5
414
+
415
+
'@moonlight-mod/moonmap@1.0.5':
416
+
resolution: {integrity: sha512-Fdpxj8ghdulKB6TlTnchlCPey2YUKgEf1chuO1ofOIcvlqnVPBcQwSf2S80naOUQpXCDo4dQ+LWSE2fmhdDiiw==}
270
417
271
418
'@nodelib/fs.scandir@2.1.5':
272
419
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
···
280
427
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
281
428
engines: {node: '>= 8'}
282
429
283
-
'@pkgr/utils@2.4.2':
284
-
resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==}
430
+
'@pkgr/core@0.2.0':
431
+
resolution: {integrity: sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==}
285
432
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
286
433
287
-
'@types/fbemitter@2.0.33':
288
-
resolution: {integrity: sha512-KcSilwdl0D8YgXGL6l9d+rTBm2W7pDyTZrDEw0+IzqQ724676KJtMeO+xHodJewKFWZT+GFWaJubA5mpMxSkcg==}
434
+
'@quansync/fs@0.1.2':
435
+
resolution: {integrity: sha512-ezIadUb1aFhwJLd++WVqVpi9rnlX8vnd4ju7saPhwLHJN1mJgOv0puePTGV+FbtSnWtwoHDT8lAm4kagDZmpCg==}
436
+
engines: {node: '>=20.0.0'}
437
+
438
+
'@types/chroma-js@3.1.0':
439
+
resolution: {integrity: sha512-Uwl3SOtUkbQ6Ye6ZYu4q4xdLGBzmY839sEHYtOT7i691neeyd+7fXWT5VIkcUSfNwIFrIjQutNYQn9h4q5HFvg==}
289
440
290
-
'@types/flux@3.1.12':
291
-
resolution: {integrity: sha512-HZ8o/DTVNgcgnXoDyn0ZnjqEZMT4Chr4w5ktMQSbQAnqVDklasmRqNGd2agZDsk5i0jYHQLgQQuM782bWG7fUA==}
441
+
'@types/chrome@0.0.313':
442
+
resolution: {integrity: sha512-9R5T7gTaYZhkxlu+Ho4wk9FL+y/werWQY2yjGWSqCuiTsqS7nL/BE5UMTP6rU7J+oIG2FRKqrEycHhJATeltVA==}
443
+
444
+
'@types/eslint@9.6.1':
445
+
resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
446
+
447
+
'@types/estree-jsx@1.0.5':
448
+
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
449
+
450
+
'@types/estree@1.0.6':
451
+
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
452
+
453
+
'@types/estree@1.0.7':
454
+
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
455
+
456
+
'@types/fbemitter@2.0.35':
457
+
resolution: {integrity: sha512-Xem6d7qUfmouCHntCrRYgDBwbf+WWRd6G+7WEFlEZFZ67LZXiYRvT2LV8wcZa6mIaAil95+ABQdKgB6hPIsnng==}
458
+
459
+
'@types/filesystem@0.0.36':
460
+
resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
461
+
462
+
'@types/filewriter@0.0.33':
463
+
resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
464
+
465
+
'@types/flux@3.1.14':
466
+
resolution: {integrity: sha512-WRXN0kQPCnqxN0/PgNgc7WBF6c8rbSHsEep3/qBLpsQ824RONdOmTs0TV7XhIW2GDNRAHO2CqCgAFLR5PChosw==}
467
+
468
+
'@types/har-format@1.2.16':
469
+
resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
470
+
471
+
'@types/highlightjs@9.12.6':
472
+
resolution: {integrity: sha512-Qfd1DUrwE851Hc3tExADJY4qY8yeZMt06Xw9AJm/UtpneepJS3MZY29c33BY0wP899veaaHD4gZzYiSuQm84Fg==}
292
473
293
474
'@types/json-schema@7.0.15':
294
475
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
476
+
477
+
'@types/lodash@4.17.14':
478
+
resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==}
295
479
296
480
'@types/node@18.17.17':
297
481
resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==}
298
482
299
-
'@types/prop-types@15.7.6':
300
-
resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==}
483
+
'@types/node@22.13.6':
484
+
resolution: {integrity: sha512-GYmF65GI7417CpZXsEXMjT8goQQDnpRnJnDw6jIYa+le3V/lMazPZ4vZmK1B/9R17fh2VLr2zuy9d/h5xgrLAg==}
301
485
302
-
'@types/react@18.2.22':
303
-
resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==}
486
+
'@types/node@22.14.0':
487
+
resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==}
304
488
305
-
'@types/scheduler@0.16.3':
306
-
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
489
+
'@types/platform@1.3.6':
490
+
resolution: {integrity: sha512-ZmSaqHuvzv+jC232cFoz2QqPUkaj6EvMmCrWcx3WRr7xTPVFCMUOTcOq8m2d+Zw1iKRc1kDiaA+jtNrV0hkVew==}
307
491
308
-
'@types/semver@7.5.6':
309
-
resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==}
492
+
'@types/prop-types@15.7.13':
493
+
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
494
+
495
+
'@types/react@18.3.20':
496
+
resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==}
310
497
311
-
'@typescript-eslint/eslint-plugin@6.13.2':
312
-
resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==}
313
-
engines: {node: ^16.0.0 || >=18.0.0}
498
+
'@typescript-eslint/eslint-plugin@8.29.0':
499
+
resolution: {integrity: sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==}
500
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
314
501
peerDependencies:
315
-
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
316
-
eslint: ^7.0.0 || ^8.0.0
317
-
typescript: '*'
318
-
peerDependenciesMeta:
319
-
typescript:
320
-
optional: true
502
+
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
503
+
eslint: ^8.57.0 || ^9.0.0
504
+
typescript: '>=4.8.4 <5.9.0'
321
505
322
-
'@typescript-eslint/parser@6.13.2':
323
-
resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==}
324
-
engines: {node: ^16.0.0 || >=18.0.0}
506
+
'@typescript-eslint/parser@8.29.0':
507
+
resolution: {integrity: sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==}
508
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
325
509
peerDependencies:
326
-
eslint: ^7.0.0 || ^8.0.0
327
-
typescript: '*'
328
-
peerDependenciesMeta:
329
-
typescript:
330
-
optional: true
510
+
eslint: ^8.57.0 || ^9.0.0
511
+
typescript: '>=4.8.4 <5.9.0'
331
512
332
-
'@typescript-eslint/scope-manager@6.13.2':
333
-
resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==}
334
-
engines: {node: ^16.0.0 || >=18.0.0}
513
+
'@typescript-eslint/scope-manager@8.29.0':
514
+
resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==}
515
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
335
516
336
-
'@typescript-eslint/type-utils@6.13.2':
337
-
resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==}
338
-
engines: {node: ^16.0.0 || >=18.0.0}
517
+
'@typescript-eslint/type-utils@8.29.0':
518
+
resolution: {integrity: sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==}
519
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
339
520
peerDependencies:
340
-
eslint: ^7.0.0 || ^8.0.0
341
-
typescript: '*'
342
-
peerDependenciesMeta:
343
-
typescript:
344
-
optional: true
521
+
eslint: ^8.57.0 || ^9.0.0
522
+
typescript: '>=4.8.4 <5.9.0'
345
523
346
-
'@typescript-eslint/types@6.13.2':
347
-
resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==}
348
-
engines: {node: ^16.0.0 || >=18.0.0}
524
+
'@typescript-eslint/types@8.29.0':
525
+
resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==}
526
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
349
527
350
-
'@typescript-eslint/typescript-estree@6.13.2':
351
-
resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==}
352
-
engines: {node: ^16.0.0 || >=18.0.0}
528
+
'@typescript-eslint/typescript-estree@8.29.0':
529
+
resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==}
530
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
353
531
peerDependencies:
354
-
typescript: '*'
355
-
peerDependenciesMeta:
356
-
typescript:
357
-
optional: true
532
+
typescript: '>=4.8.4 <5.9.0'
358
533
359
-
'@typescript-eslint/utils@6.13.2':
360
-
resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==}
361
-
engines: {node: ^16.0.0 || >=18.0.0}
534
+
'@typescript-eslint/utils@8.29.0':
535
+
resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==}
536
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
362
537
peerDependencies:
363
-
eslint: ^7.0.0 || ^8.0.0
538
+
eslint: ^8.57.0 || ^9.0.0
539
+
typescript: '>=4.8.4 <5.9.0'
540
+
541
+
'@typescript-eslint/visitor-keys@8.29.0':
542
+
resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==}
543
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
544
+
545
+
'@xterm/xterm@5.5.0':
546
+
resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
547
+
548
+
'@zenfs/core@2.0.0':
549
+
resolution: {integrity: sha512-wOKNFTY1DJ1vdLqKdU7M8cRh0nVYZcDVu7WHuk/3u49hrSwTZVm4PzGxJUjFd8O9Wi3U5nYTbZoN7RX5mS2ldA==}
550
+
engines: {node: '>= 18'}
551
+
hasBin: true
364
552
365
-
'@typescript-eslint/visitor-keys@6.13.2':
366
-
resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==}
367
-
engines: {node: ^16.0.0 || >=18.0.0}
553
+
'@zenfs/dom@1.1.6':
554
+
resolution: {integrity: sha512-7SBTWgA0esuEv/TE+N/xk6W/XJf8uBF+LhlPNHQdXds0H7aOy/UYsWv/8glvARe+meDMMidoeWFLzUWoMXfjlA==}
555
+
engines: {node: '>= 18'}
556
+
peerDependencies:
557
+
'@zenfs/core': ^2.0.0
558
+
utilium: ^1.9.0
368
559
369
-
'@ungap/structured-clone@1.2.0':
370
-
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
560
+
abort-controller@3.0.0:
561
+
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
562
+
engines: {node: '>=6.5'}
371
563
372
564
acorn-jsx@5.3.2:
373
565
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
374
566
peerDependencies:
375
567
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
376
568
377
-
acorn@8.11.2:
378
-
resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
569
+
acorn@8.14.1:
570
+
resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
379
571
engines: {node: '>=0.4.0'}
380
572
hasBin: true
381
573
382
574
ajv@6.12.6:
383
575
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
384
576
385
-
ansi-regex@5.0.1:
386
-
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
387
-
engines: {node: '>=8'}
388
-
389
577
ansi-styles@4.3.0:
390
578
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
391
579
engines: {node: '>=8'}
392
580
581
+
ansis@3.17.0:
582
+
resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==}
583
+
engines: {node: '>=14'}
584
+
393
585
argparse@2.0.1:
394
586
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
395
587
396
-
array-buffer-byte-length@1.0.0:
397
-
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
588
+
array-buffer-byte-length@1.0.2:
589
+
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
590
+
engines: {node: '>= 0.4'}
398
591
399
-
array-includes@3.1.7:
400
-
resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
592
+
array-includes@3.1.8:
593
+
resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
401
594
engines: {node: '>= 0.4'}
402
595
403
-
array-union@2.1.0:
404
-
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
405
-
engines: {node: '>=8'}
596
+
array.prototype.findlast@1.2.5:
597
+
resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
598
+
engines: {node: '>= 0.4'}
406
599
407
-
array.prototype.flat@1.3.2:
408
-
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
600
+
array.prototype.flat@1.3.3:
601
+
resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
409
602
engines: {node: '>= 0.4'}
410
603
411
-
array.prototype.flatmap@1.3.2:
412
-
resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
604
+
array.prototype.flatmap@1.3.3:
605
+
resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
413
606
engines: {node: '>= 0.4'}
414
607
415
-
array.prototype.tosorted@1.1.2:
416
-
resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==}
608
+
array.prototype.tosorted@1.1.4:
609
+
resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
610
+
engines: {node: '>= 0.4'}
417
611
418
-
arraybuffer.prototype.slice@1.0.2:
419
-
resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==}
612
+
arraybuffer.prototype.slice@1.0.4:
613
+
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
420
614
engines: {node: '>= 0.4'}
421
615
422
-
asynciterator.prototype@1.0.0:
423
-
resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==}
616
+
astring@1.9.0:
617
+
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
618
+
hasBin: true
424
619
425
-
available-typed-arrays@1.0.5:
426
-
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
620
+
async-function@1.0.0:
621
+
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
622
+
engines: {node: '>= 0.4'}
623
+
624
+
available-typed-arrays@1.0.7:
625
+
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
427
626
engines: {node: '>= 0.4'}
428
627
429
628
balanced-match@1.0.2:
430
629
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
431
630
432
-
big-integer@1.6.52:
433
-
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
434
-
engines: {node: '>=0.6'}
435
-
436
-
bplist-parser@0.2.0:
437
-
resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
438
-
engines: {node: '>= 5.10.0'}
631
+
base64-js@1.5.1:
632
+
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
439
633
440
634
brace-expansion@1.1.11:
441
635
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
442
636
443
-
braces@3.0.2:
444
-
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
637
+
brace-expansion@2.0.1:
638
+
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
639
+
640
+
braces@3.0.3:
641
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
445
642
engines: {node: '>=8'}
446
643
447
-
bundle-name@3.0.0:
448
-
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
449
-
engines: {node: '>=12'}
644
+
buffer@6.0.3:
645
+
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
450
646
451
-
call-bind@1.0.5:
452
-
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
647
+
cac@6.7.14:
648
+
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
649
+
engines: {node: '>=8'}
650
+
651
+
call-bind-apply-helpers@1.0.2:
652
+
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
653
+
engines: {node: '>= 0.4'}
654
+
655
+
call-bind@1.0.8:
656
+
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
657
+
engines: {node: '>= 0.4'}
658
+
659
+
call-bound@1.0.4:
660
+
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
661
+
engines: {node: '>= 0.4'}
453
662
454
663
callsites@3.1.0:
455
664
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
···
466
675
color-name@1.1.4:
467
676
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
468
677
469
-
commander@5.1.0:
470
-
resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
471
-
engines: {node: '>= 6'}
472
-
473
678
concat-map@0.0.1:
474
-
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
679
+
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
475
680
476
-
cross-spawn@7.0.3:
477
-
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
681
+
cross-spawn@7.0.6:
682
+
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
478
683
engines: {node: '>= 8'}
479
684
480
-
csstype@3.1.2:
481
-
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
685
+
csstype@3.1.3:
686
+
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
687
+
688
+
data-view-buffer@1.0.2:
689
+
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
690
+
engines: {node: '>= 0.4'}
691
+
692
+
data-view-byte-length@1.0.2:
693
+
resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
694
+
engines: {node: '>= 0.4'}
695
+
696
+
data-view-byte-offset@1.0.1:
697
+
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
698
+
engines: {node: '>= 0.4'}
482
699
483
-
debug@4.3.4:
484
-
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
700
+
debug@4.4.0:
701
+
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
485
702
engines: {node: '>=6.0'}
486
703
peerDependencies:
487
704
supports-color: '*'
···
492
709
deep-is@0.1.4:
493
710
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
494
711
495
-
default-browser-id@3.0.0:
496
-
resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
497
-
engines: {node: '>=12'}
498
-
499
-
default-browser@4.0.0:
500
-
resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==}
501
-
engines: {node: '>=14.16'}
502
-
503
-
define-data-property@1.1.1:
504
-
resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
712
+
define-data-property@1.1.4:
713
+
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
505
714
engines: {node: '>= 0.4'}
506
-
507
-
define-lazy-prop@3.0.0:
508
-
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
509
-
engines: {node: '>=12'}
510
715
511
716
define-properties@1.2.1:
512
717
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
513
718
engines: {node: '>= 0.4'}
514
719
515
-
dir-glob@3.0.1:
516
-
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
517
-
engines: {node: '>=8'}
720
+
defu@6.1.4:
721
+
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
722
+
723
+
destr@2.0.4:
724
+
resolution: {integrity: sha512-FCAorltMy7QwX0QU38jOkhrv20LBpsHA8ogzvMhhPHCCKVCaN6GxrB0GGaWEWBUYI4eEjjfJ95RdP6dk9IdMQA==}
518
725
519
726
doctrine@2.1.0:
520
727
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
521
728
engines: {node: '>=0.10.0'}
522
729
523
-
doctrine@3.0.0:
524
-
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
525
-
engines: {node: '>=6.0.0'}
730
+
dunder-proto@1.0.1:
731
+
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
732
+
engines: {node: '>= 0.4'}
526
733
527
-
es-abstract@1.22.3:
528
-
resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==}
734
+
es-abstract@1.23.9:
735
+
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
529
736
engines: {node: '>= 0.4'}
530
737
531
-
es-iterator-helpers@1.0.15:
532
-
resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==}
738
+
es-define-property@1.0.1:
739
+
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
740
+
engines: {node: '>= 0.4'}
533
741
534
-
es-set-tostringtag@2.0.2:
535
-
resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==}
742
+
es-errors@1.3.0:
743
+
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
536
744
engines: {node: '>= 0.4'}
537
745
538
-
es-shim-unscopables@1.0.2:
539
-
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
746
+
es-iterator-helpers@1.2.1:
747
+
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
748
+
engines: {node: '>= 0.4'}
540
749
541
-
es-to-primitive@1.2.1:
542
-
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
750
+
es-object-atoms@1.1.1:
751
+
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
752
+
engines: {node: '>= 0.4'}
753
+
754
+
es-set-tostringtag@2.1.0:
755
+
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
756
+
engines: {node: '>= 0.4'}
757
+
758
+
es-shim-unscopables@1.1.0:
759
+
resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
760
+
engines: {node: '>= 0.4'}
761
+
762
+
es-to-primitive@1.3.0:
763
+
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
543
764
engines: {node: '>= 0.4'}
544
765
545
766
esbuild-copy-static-files@0.1.0:
···
560
781
peerDependencies:
561
782
eslint: '>=7.0.0'
562
783
563
-
eslint-plugin-prettier@5.0.1:
564
-
resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==}
784
+
eslint-plugin-prettier@5.2.6:
785
+
resolution: {integrity: sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==}
565
786
engines: {node: ^14.18.0 || >=16.0.0}
566
787
peerDependencies:
567
788
'@types/eslint': '>=8.0.0'
568
789
eslint: '>=8.0.0'
569
-
eslint-config-prettier: '*'
790
+
eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
570
791
prettier: '>=3.0.0'
571
792
peerDependenciesMeta:
572
793
'@types/eslint':
···
574
795
eslint-config-prettier:
575
796
optional: true
576
797
577
-
eslint-plugin-react@7.33.2:
578
-
resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
798
+
eslint-plugin-react@7.37.5:
799
+
resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
579
800
engines: {node: '>=4'}
580
801
peerDependencies:
581
-
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
802
+
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
582
803
583
-
eslint-scope@7.2.2:
584
-
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
585
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
804
+
eslint-scope@8.3.0:
805
+
resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
806
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
586
807
587
808
eslint-visitor-keys@3.4.3:
588
809
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
589
810
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
590
811
591
-
eslint@8.55.0:
592
-
resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==}
593
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
812
+
eslint-visitor-keys@4.2.0:
813
+
resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
814
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
815
+
816
+
eslint@9.23.0:
817
+
resolution: {integrity: sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==}
818
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
594
819
hasBin: true
820
+
peerDependencies:
821
+
jiti: '*'
822
+
peerDependenciesMeta:
823
+
jiti:
824
+
optional: true
595
825
596
-
espree@9.6.1:
597
-
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
598
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
826
+
espree@10.3.0:
827
+
resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
828
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
599
829
600
-
esquery@1.5.0:
601
-
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
830
+
esquery@1.6.0:
831
+
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
602
832
engines: {node: '>=0.10'}
603
833
604
834
esrecurse@4.3.0:
···
608
838
estraverse@5.3.0:
609
839
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
610
840
engines: {node: '>=4.0'}
841
+
842
+
estree-toolkit@1.7.8:
843
+
resolution: {integrity: sha512-v0Q0L+0agSDFe3x9Sj7aAzrI9afvsfr5r7AM2SNk/8bKYRQ3tUf4PQEUWe99LkWysmT1PsuSpW+W1w/xZmCKeg==}
611
844
612
845
esutils@2.0.3:
613
846
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
614
847
engines: {node: '>=0.10.0'}
615
848
616
-
execa@5.1.1:
617
-
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
618
-
engines: {node: '>=10'}
849
+
event-target-shim@5.0.1:
850
+
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
851
+
engines: {node: '>=6'}
619
852
620
-
execa@7.2.0:
621
-
resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==}
622
-
engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0}
853
+
eventemitter3@5.0.1:
854
+
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
855
+
856
+
events@3.3.0:
857
+
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
858
+
engines: {node: '>=0.8.x'}
623
859
624
860
fast-deep-equal@3.1.3:
625
861
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
···
637
873
fast-levenshtein@2.0.6:
638
874
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
639
875
640
-
fastq@1.15.0:
641
-
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
876
+
fastq@1.17.1:
877
+
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
642
878
643
-
file-entry-cache@6.0.1:
644
-
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
645
-
engines: {node: ^10.12.0 || >=12.0.0}
879
+
fdir@6.4.3:
880
+
resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
881
+
peerDependencies:
882
+
picomatch: ^3 || ^4
883
+
peerDependenciesMeta:
884
+
picomatch:
885
+
optional: true
646
886
647
-
fill-range@7.0.1:
648
-
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
887
+
file-entry-cache@8.0.0:
888
+
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
889
+
engines: {node: '>=16.0.0'}
890
+
891
+
fill-range@7.1.1:
892
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
649
893
engines: {node: '>=8'}
650
894
895
+
find-up-simple@1.0.1:
896
+
resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
897
+
engines: {node: '>=18'}
898
+
651
899
find-up@5.0.0:
652
900
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
653
901
engines: {node: '>=10'}
654
902
655
-
flat-cache@3.2.0:
656
-
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
657
-
engines: {node: ^10.12.0 || >=12.0.0}
903
+
flat-cache@4.0.1:
904
+
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
905
+
engines: {node: '>=16'}
658
906
659
907
flatted@3.2.9:
660
908
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
661
909
662
-
for-each@0.3.3:
663
-
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
664
-
665
-
fs.realpath@1.0.0:
666
-
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
910
+
for-each@0.3.5:
911
+
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
912
+
engines: {node: '>= 0.4'}
667
913
668
914
function-bind@1.1.2:
669
915
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
670
916
671
-
function.prototype.name@1.1.6:
672
-
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
917
+
function.prototype.name@1.1.8:
918
+
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
673
919
engines: {node: '>= 0.4'}
674
920
675
921
functions-have-names@1.2.3:
676
922
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
677
923
678
-
get-intrinsic@1.2.2:
679
-
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
924
+
fzf@0.5.2:
925
+
resolution: {integrity: sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==}
926
+
927
+
get-intrinsic@1.3.0:
928
+
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
929
+
engines: {node: '>= 0.4'}
680
930
681
-
get-stream@6.0.1:
682
-
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
683
-
engines: {node: '>=10'}
931
+
get-proto@1.0.1:
932
+
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
933
+
engines: {node: '>= 0.4'}
684
934
685
-
get-symbol-description@1.0.0:
686
-
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
935
+
get-symbol-description@1.1.0:
936
+
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
687
937
engines: {node: '>= 0.4'}
688
938
689
939
glob-parent@5.1.2:
···
694
944
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
695
945
engines: {node: '>=10.13.0'}
696
946
697
-
glob@7.2.3:
698
-
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
947
+
globals@14.0.0:
948
+
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
949
+
engines: {node: '>=18'}
699
950
700
-
globals@13.23.0:
701
-
resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==}
702
-
engines: {node: '>=8'}
703
-
704
-
globalthis@1.0.3:
705
-
resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
951
+
globalthis@1.0.4:
952
+
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
706
953
engines: {node: '>= 0.4'}
707
954
708
-
globby@11.1.0:
709
-
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
710
-
engines: {node: '>=10'}
711
-
712
-
gopd@1.0.1:
713
-
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
955
+
gopd@1.2.0:
956
+
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
957
+
engines: {node: '>= 0.4'}
714
958
715
959
graphemer@1.4.0:
716
960
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
717
961
718
-
has-bigints@1.0.2:
719
-
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
962
+
has-bigints@1.1.0:
963
+
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
964
+
engines: {node: '>= 0.4'}
720
965
721
966
has-flag@4.0.0:
722
967
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
723
968
engines: {node: '>=8'}
724
969
725
-
has-property-descriptors@1.0.1:
726
-
resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
970
+
has-property-descriptors@1.0.2:
971
+
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
727
972
728
-
has-proto@1.0.1:
729
-
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
973
+
has-proto@1.2.0:
974
+
resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
730
975
engines: {node: '>= 0.4'}
731
976
732
-
has-symbols@1.0.3:
733
-
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
977
+
has-symbols@1.1.0:
978
+
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
734
979
engines: {node: '>= 0.4'}
735
980
736
-
has-tostringtag@1.0.0:
737
-
resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
981
+
has-tostringtag@1.0.2:
982
+
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
738
983
engines: {node: '>= 0.4'}
739
984
740
-
hasown@2.0.0:
741
-
resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
985
+
hasown@2.0.2:
986
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
742
987
engines: {node: '>= 0.4'}
743
988
744
-
human-signals@2.1.0:
745
-
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
746
-
engines: {node: '>=10.17.0'}
747
-
748
-
human-signals@4.3.1:
749
-
resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==}
750
-
engines: {node: '>=14.18.0'}
751
-
752
989
husky@8.0.3:
753
990
resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==}
754
991
engines: {node: '>=14'}
755
992
hasBin: true
756
993
757
-
ignore@5.3.0:
758
-
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
994
+
ieee754@1.2.1:
995
+
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
996
+
997
+
ignore@5.3.2:
998
+
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
759
999
engines: {node: '>= 4'}
760
1000
761
1001
import-fresh@3.3.0:
···
766
1006
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
767
1007
engines: {node: '>=0.8.19'}
768
1008
769
-
inflight@1.0.6:
770
-
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
1009
+
internal-slot@1.1.0:
1010
+
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
1011
+
engines: {node: '>= 0.4'}
771
1012
772
-
inherits@2.0.4:
773
-
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
774
-
775
-
internal-slot@1.0.6:
776
-
resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==}
1013
+
is-array-buffer@3.0.5:
1014
+
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
777
1015
engines: {node: '>= 0.4'}
778
1016
779
-
is-array-buffer@3.0.2:
780
-
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
781
-
782
-
is-async-function@2.0.0:
783
-
resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
1017
+
is-async-function@2.1.1:
1018
+
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
784
1019
engines: {node: '>= 0.4'}
785
1020
786
-
is-bigint@1.0.4:
787
-
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
1021
+
is-bigint@1.1.0:
1022
+
resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
1023
+
engines: {node: '>= 0.4'}
788
1024
789
-
is-boolean-object@1.1.2:
790
-
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
1025
+
is-boolean-object@1.2.2:
1026
+
resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
791
1027
engines: {node: '>= 0.4'}
792
1028
793
1029
is-callable@1.2.7:
794
1030
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
795
1031
engines: {node: '>= 0.4'}
796
1032
797
-
is-core-module@2.13.1:
798
-
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
799
-
800
-
is-date-object@1.0.5:
801
-
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
1033
+
is-core-module@2.16.1:
1034
+
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
802
1035
engines: {node: '>= 0.4'}
803
1036
804
-
is-docker@2.2.1:
805
-
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
806
-
engines: {node: '>=8'}
807
-
hasBin: true
1037
+
is-data-view@1.0.2:
1038
+
resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
1039
+
engines: {node: '>= 0.4'}
808
1040
809
-
is-docker@3.0.0:
810
-
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
811
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
812
-
hasBin: true
1041
+
is-date-object@1.1.0:
1042
+
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
1043
+
engines: {node: '>= 0.4'}
813
1044
814
1045
is-extglob@2.1.1:
815
1046
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
816
1047
engines: {node: '>=0.10.0'}
817
1048
818
-
is-finalizationregistry@1.0.2:
819
-
resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
1049
+
is-finalizationregistry@1.1.1:
1050
+
resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
1051
+
engines: {node: '>= 0.4'}
820
1052
821
-
is-generator-function@1.0.10:
822
-
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
1053
+
is-generator-function@1.1.0:
1054
+
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
823
1055
engines: {node: '>= 0.4'}
824
1056
825
1057
is-glob@4.0.3:
826
1058
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
827
1059
engines: {node: '>=0.10.0'}
828
1060
829
-
is-inside-container@1.0.0:
830
-
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
831
-
engines: {node: '>=14.16'}
832
-
hasBin: true
833
-
834
-
is-map@2.0.2:
835
-
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
836
-
837
-
is-negative-zero@2.0.2:
838
-
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
1061
+
is-map@2.0.3:
1062
+
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
839
1063
engines: {node: '>= 0.4'}
840
1064
841
-
is-number-object@1.0.7:
842
-
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
1065
+
is-number-object@1.1.1:
1066
+
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
843
1067
engines: {node: '>= 0.4'}
844
1068
845
1069
is-number@7.0.0:
846
1070
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
847
1071
engines: {node: '>=0.12.0'}
848
1072
849
-
is-path-inside@3.0.3:
850
-
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
851
-
engines: {node: '>=8'}
1073
+
is-regex@1.2.1:
1074
+
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
1075
+
engines: {node: '>= 0.4'}
852
1076
853
-
is-regex@1.1.4:
854
-
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
1077
+
is-set@2.0.3:
1078
+
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
855
1079
engines: {node: '>= 0.4'}
856
1080
857
-
is-set@2.0.2:
858
-
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
1081
+
is-shared-array-buffer@1.0.4:
1082
+
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
1083
+
engines: {node: '>= 0.4'}
859
1084
860
-
is-shared-array-buffer@1.0.2:
861
-
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
1085
+
is-string@1.1.1:
1086
+
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
1087
+
engines: {node: '>= 0.4'}
862
1088
863
-
is-stream@2.0.1:
864
-
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
865
-
engines: {node: '>=8'}
866
-
867
-
is-stream@3.0.0:
868
-
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
869
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
870
-
871
-
is-string@1.0.7:
872
-
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
1089
+
is-symbol@1.1.1:
1090
+
resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
873
1091
engines: {node: '>= 0.4'}
874
1092
875
-
is-symbol@1.0.4:
876
-
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
1093
+
is-typed-array@1.1.15:
1094
+
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
877
1095
engines: {node: '>= 0.4'}
878
1096
879
-
is-typed-array@1.1.12:
880
-
resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==}
1097
+
is-weakmap@2.0.2:
1098
+
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
881
1099
engines: {node: '>= 0.4'}
882
1100
883
-
is-weakmap@2.0.1:
884
-
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
1101
+
is-weakref@1.1.1:
1102
+
resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
1103
+
engines: {node: '>= 0.4'}
885
1104
886
-
is-weakref@1.0.2:
887
-
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
888
-
889
-
is-weakset@2.0.2:
890
-
resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
891
-
892
-
is-wsl@2.2.0:
893
-
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
894
-
engines: {node: '>=8'}
1105
+
is-weakset@2.0.4:
1106
+
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
1107
+
engines: {node: '>= 0.4'}
895
1108
896
1109
isarray@2.0.5:
897
1110
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
···
899
1112
isexe@2.0.0:
900
1113
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
901
1114
902
-
iterator.prototype@1.1.2:
903
-
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
1115
+
iterator.prototype@1.1.5:
1116
+
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
1117
+
engines: {node: '>= 0.4'}
1118
+
1119
+
jiti@2.4.2:
1120
+
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
1121
+
hasBin: true
904
1122
905
1123
js-tokens@4.0.0:
906
1124
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
···
940
1158
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
941
1159
hasBin: true
942
1160
943
-
lru-cache@6.0.0:
944
-
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
945
-
engines: {node: '>=10'}
946
-
947
-
merge-stream@2.0.0:
948
-
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
1161
+
math-intrinsics@1.1.0:
1162
+
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
1163
+
engines: {node: '>= 0.4'}
949
1164
950
1165
merge2@1.4.1:
951
1166
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
952
1167
engines: {node: '>= 8'}
953
1168
954
-
micromatch@4.0.5:
955
-
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
1169
+
meriyah@6.0.1:
1170
+
resolution: {integrity: sha512-OyvYIOgpzXREySYJ1cqEb2pOKdeQMTfF9M8dRU6nC4hi/GXMmNpe9ssZCrSoTHazu05BSAoRBN/uYeco+ymfOg==}
1171
+
engines: {node: '>=18.0.0'}
1172
+
1173
+
microdiff@1.5.0:
1174
+
resolution: {integrity: sha512-Drq+/THMvDdzRYrK0oxJmOKiC24ayUV8ahrt8l3oRK51PWt6gdtrIGrlIH3pT/lFh1z93FbAcidtsHcWbnRz8Q==}
1175
+
1176
+
micromatch@4.0.8:
1177
+
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
956
1178
engines: {node: '>=8.6'}
957
1179
958
-
mimic-fn@2.1.0:
959
-
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
960
-
engines: {node: '>=6'}
961
-
962
-
mimic-fn@4.0.0:
963
-
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
964
-
engines: {node: '>=12'}
1180
+
mimic-function@5.0.1:
1181
+
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
1182
+
engines: {node: '>=18'}
965
1183
966
1184
minimatch@3.1.2:
967
1185
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
968
1186
969
-
ms@2.1.2:
970
-
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
1187
+
minimatch@9.0.5:
1188
+
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1189
+
engines: {node: '>=16 || 14 >=14.17'}
1190
+
1191
+
ms@2.1.3:
1192
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1193
+
1194
+
nanotar@0.1.1:
1195
+
resolution: {integrity: sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==}
971
1196
972
1197
natural-compare@1.4.0:
973
1198
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
974
1199
975
-
npm-run-path@4.0.1:
976
-
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
977
-
engines: {node: '>=8'}
978
-
979
-
npm-run-path@5.1.0:
980
-
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
981
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1200
+
node-fetch-native@1.6.6:
1201
+
resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
982
1202
983
1203
object-assign@4.1.1:
984
1204
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
985
1205
engines: {node: '>=0.10.0'}
986
1206
987
-
object-inspect@1.13.1:
988
-
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
1207
+
object-inspect@1.13.4:
1208
+
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
1209
+
engines: {node: '>= 0.4'}
989
1210
990
1211
object-keys@1.1.1:
991
1212
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
992
1213
engines: {node: '>= 0.4'}
993
1214
994
-
object.assign@4.1.5:
995
-
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
1215
+
object.assign@4.1.7:
1216
+
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
996
1217
engines: {node: '>= 0.4'}
997
1218
998
-
object.entries@1.1.7:
999
-
resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==}
1219
+
object.entries@1.1.9:
1220
+
resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
1000
1221
engines: {node: '>= 0.4'}
1001
1222
1002
-
object.fromentries@2.0.7:
1003
-
resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
1223
+
object.fromentries@2.0.8:
1224
+
resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
1004
1225
engines: {node: '>= 0.4'}
1005
1226
1006
-
object.hasown@1.1.3:
1007
-
resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==}
1008
-
1009
-
object.values@1.1.7:
1010
-
resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
1227
+
object.values@1.2.1:
1228
+
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
1011
1229
engines: {node: '>= 0.4'}
1012
1230
1013
-
once@1.4.0:
1014
-
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
1015
-
1016
-
onetime@5.1.2:
1017
-
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
1018
-
engines: {node: '>=6'}
1019
-
1020
-
onetime@6.0.0:
1021
-
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
1022
-
engines: {node: '>=12'}
1231
+
ofetch@1.4.1:
1232
+
resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
1023
1233
1024
-
open@9.1.0:
1025
-
resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==}
1026
-
engines: {node: '>=14.16'}
1234
+
onetime@7.0.0:
1235
+
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
1236
+
engines: {node: '>=18'}
1027
1237
1028
1238
optionator@0.9.3:
1029
1239
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
1030
1240
engines: {node: '>= 0.8.0'}
1241
+
1242
+
own-keys@1.0.1:
1243
+
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
1244
+
engines: {node: '>= 0.4'}
1031
1245
1032
1246
p-limit@3.1.0:
1033
1247
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
···
1037
1251
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
1038
1252
engines: {node: '>=10'}
1039
1253
1254
+
package-manager-detector@1.1.0:
1255
+
resolution: {integrity: sha512-Y8f9qUlBzW8qauJjd/eu6jlpJZsuPJm2ZAV0cDVd420o4EdpH5RPdoCv+60/TdJflGatr4sDfpAL6ArWZbM5tA==}
1256
+
1040
1257
parent-module@1.0.1:
1041
1258
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
1042
1259
engines: {node: '>=6'}
···
1045
1262
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
1046
1263
engines: {node: '>=8'}
1047
1264
1048
-
path-is-absolute@1.0.1:
1049
-
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
1050
-
engines: {node: '>=0.10.0'}
1051
-
1052
1265
path-key@3.1.1:
1053
1266
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1054
1267
engines: {node: '>=8'}
1055
-
1056
-
path-key@4.0.0:
1057
-
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
1058
-
engines: {node: '>=12'}
1059
1268
1060
1269
path-parse@1.0.7:
1061
1270
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1062
1271
1063
-
path-type@4.0.0:
1064
-
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
1065
-
engines: {node: '>=8'}
1066
-
1067
-
picocolors@1.0.0:
1068
-
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
1272
+
pathe@2.0.3:
1273
+
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
1069
1274
1070
1275
picomatch@2.3.1:
1071
1276
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1072
1277
engines: {node: '>=8.6'}
1073
1278
1279
+
picomatch@4.0.2:
1280
+
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
1281
+
engines: {node: '>=12'}
1282
+
1283
+
pnpm-workspace-yaml@0.3.1:
1284
+
resolution: {integrity: sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA==}
1285
+
1286
+
possible-typed-array-names@1.1.0:
1287
+
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
1288
+
engines: {node: '>= 0.4'}
1289
+
1074
1290
prelude-ls@1.2.1:
1075
1291
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
1076
1292
engines: {node: '>= 0.8.0'}
···
1084
1300
engines: {node: '>=14'}
1085
1301
hasBin: true
1086
1302
1303
+
process@0.11.10:
1304
+
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
1305
+
engines: {node: '>= 0.6.0'}
1306
+
1087
1307
prop-types@15.8.1:
1088
1308
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
1089
1309
···
1091
1311
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1092
1312
engines: {node: '>=6'}
1093
1313
1314
+
quansync@0.2.10:
1315
+
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
1316
+
1094
1317
queue-microtask@1.2.3:
1095
1318
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1096
1319
1097
1320
react-is@16.13.1:
1098
1321
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
1099
1322
1100
-
reflect.getprototypeof@1.0.4:
1101
-
resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==}
1323
+
readable-stream@4.5.2:
1324
+
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
1325
+
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
1326
+
1327
+
reflect.getprototypeof@1.0.10:
1328
+
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
1102
1329
engines: {node: '>= 0.4'}
1103
1330
1104
-
regexp.prototype.flags@1.5.1:
1105
-
resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
1331
+
regexp.prototype.flags@1.5.4:
1332
+
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
1106
1333
engines: {node: '>= 0.4'}
1107
1334
1108
1335
resolve-from@4.0.0:
···
1113
1340
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
1114
1341
hasBin: true
1115
1342
1343
+
restore-cursor@5.1.0:
1344
+
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
1345
+
engines: {node: '>=18'}
1346
+
1116
1347
reusify@1.0.4:
1117
1348
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
1118
1349
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
1119
1350
1120
-
rimraf@3.0.2:
1121
-
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
1122
-
hasBin: true
1123
-
1124
-
run-applescript@5.0.0:
1125
-
resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==}
1126
-
engines: {node: '>=12'}
1127
-
1128
1351
run-parallel@1.2.0:
1129
1352
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1130
1353
1131
-
safe-array-concat@1.0.1:
1132
-
resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==}
1354
+
safe-array-concat@1.1.3:
1355
+
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
1133
1356
engines: {node: '>=0.4'}
1134
1357
1135
-
safe-regex-test@1.0.0:
1136
-
resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
1358
+
safe-buffer@5.2.1:
1359
+
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
1360
+
1361
+
safe-push-apply@1.0.0:
1362
+
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
1363
+
engines: {node: '>= 0.4'}
1364
+
1365
+
safe-regex-test@1.1.0:
1366
+
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
1367
+
engines: {node: '>= 0.4'}
1137
1368
1138
1369
semver@6.3.1:
1139
1370
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1140
1371
hasBin: true
1141
1372
1142
-
semver@7.5.4:
1143
-
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
1373
+
semver@7.7.1:
1374
+
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
1144
1375
engines: {node: '>=10'}
1145
1376
hasBin: true
1146
1377
1147
-
set-function-length@1.1.1:
1148
-
resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
1378
+
set-function-length@1.2.2:
1379
+
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
1149
1380
engines: {node: '>= 0.4'}
1150
1381
1151
-
set-function-name@2.0.1:
1152
-
resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
1382
+
set-function-name@2.0.2:
1383
+
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
1384
+
engines: {node: '>= 0.4'}
1385
+
1386
+
set-proto@1.0.0:
1387
+
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
1153
1388
engines: {node: '>= 0.4'}
1154
1389
1155
1390
shebang-command@2.0.0:
···
1160
1395
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1161
1396
engines: {node: '>=8'}
1162
1397
1163
-
side-channel@1.0.4:
1164
-
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
1398
+
side-channel-list@1.0.0:
1399
+
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
1400
+
engines: {node: '>= 0.4'}
1165
1401
1166
-
signal-exit@3.0.7:
1167
-
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
1402
+
side-channel-map@1.0.1:
1403
+
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
1404
+
engines: {node: '>= 0.4'}
1168
1405
1169
-
slash@3.0.0:
1170
-
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
1171
-
engines: {node: '>=8'}
1406
+
side-channel-weakmap@1.0.2:
1407
+
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
1408
+
engines: {node: '>= 0.4'}
1409
+
1410
+
side-channel@1.1.0:
1411
+
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
1412
+
engines: {node: '>= 0.4'}
1413
+
1414
+
signal-exit@4.1.0:
1415
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
1416
+
engines: {node: '>=14'}
1172
1417
1173
1418
standalone-electron-types@1.0.0:
1174
1419
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
1175
1420
1176
-
string.prototype.matchall@4.0.10:
1177
-
resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==}
1178
-
1179
-
string.prototype.trim@1.2.8:
1180
-
resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
1421
+
string.prototype.matchall@4.0.12:
1422
+
resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
1181
1423
engines: {node: '>= 0.4'}
1182
1424
1183
-
string.prototype.trimend@1.0.7:
1184
-
resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
1425
+
string.prototype.repeat@1.0.0:
1426
+
resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
1185
1427
1186
-
string.prototype.trimstart@1.0.7:
1187
-
resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
1428
+
string.prototype.trim@1.2.10:
1429
+
resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
1430
+
engines: {node: '>= 0.4'}
1188
1431
1189
-
strip-ansi@6.0.1:
1190
-
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
1191
-
engines: {node: '>=8'}
1432
+
string.prototype.trimend@1.0.9:
1433
+
resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
1434
+
engines: {node: '>= 0.4'}
1192
1435
1193
-
strip-final-newline@2.0.0:
1194
-
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
1195
-
engines: {node: '>=6'}
1436
+
string.prototype.trimstart@1.0.8:
1437
+
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
1438
+
engines: {node: '>= 0.4'}
1196
1439
1197
-
strip-final-newline@3.0.0:
1198
-
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
1199
-
engines: {node: '>=12'}
1440
+
string_decoder@1.3.0:
1441
+
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
1200
1442
1201
1443
strip-json-comments@3.1.1:
1202
1444
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
···
1210
1452
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1211
1453
engines: {node: '>= 0.4'}
1212
1454
1213
-
synckit@0.8.6:
1214
-
resolution: {integrity: sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==}
1455
+
synckit@0.11.1:
1456
+
resolution: {integrity: sha512-fWZqNBZNNFp/7mTUy1fSsydhKsAKJ+u90Nk7kOK5Gcq9vObaqLBLjWFDBkyVU9Vvc6Y71VbOevMuGhqv02bT+Q==}
1215
1457
engines: {node: ^14.18.0 || >=16.0.0}
1216
1458
1217
-
text-table@0.2.0:
1218
-
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
1459
+
taze@19.0.4:
1460
+
resolution: {integrity: sha512-bviyNotzqcIWpVBCC4QYVb2yupzKyUDGQi2m/8GERdiPaudVMtgAqaE98+x0cDDaByYRMJCyhQWM04ikUL6+kQ==}
1461
+
hasBin: true
1219
1462
1220
-
titleize@3.0.0:
1221
-
resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==}
1222
-
engines: {node: '>=12'}
1463
+
tinyexec@1.0.1:
1464
+
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
1465
+
1466
+
tinyglobby@0.2.12:
1467
+
resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
1468
+
engines: {node: '>=12.0.0'}
1223
1469
1224
1470
to-regex-range@5.0.1:
1225
1471
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1226
1472
engines: {node: '>=8.0'}
1227
1473
1228
-
ts-api-utils@1.0.3:
1229
-
resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==}
1230
-
engines: {node: '>=16.13.0'}
1474
+
ts-api-utils@2.1.0:
1475
+
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
1476
+
engines: {node: '>=18.12'}
1231
1477
peerDependencies:
1232
-
typescript: '>=4.2.0'
1478
+
typescript: '>=4.8.4'
1233
1479
1234
-
tslib@2.6.2:
1235
-
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
1480
+
tslib@2.8.1:
1481
+
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
1236
1482
1237
1483
type-check@0.4.0:
1238
1484
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
1239
1485
engines: {node: '>= 0.8.0'}
1240
1486
1241
-
type-fest@0.20.2:
1242
-
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
1243
-
engines: {node: '>=10'}
1487
+
typed-array-buffer@1.0.3:
1488
+
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
1489
+
engines: {node: '>= 0.4'}
1244
1490
1245
-
typed-array-buffer@1.0.0:
1246
-
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
1491
+
typed-array-byte-length@1.0.3:
1492
+
resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
1247
1493
engines: {node: '>= 0.4'}
1248
1494
1249
-
typed-array-byte-length@1.0.0:
1250
-
resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==}
1495
+
typed-array-byte-offset@1.0.4:
1496
+
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
1251
1497
engines: {node: '>= 0.4'}
1252
1498
1253
-
typed-array-byte-offset@1.0.0:
1254
-
resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==}
1499
+
typed-array-length@1.0.7:
1500
+
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
1255
1501
engines: {node: '>= 0.4'}
1256
1502
1257
-
typed-array-length@1.0.4:
1258
-
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
1503
+
typescript-eslint@8.29.0:
1504
+
resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==}
1505
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1506
+
peerDependencies:
1507
+
eslint: ^8.57.0 || ^9.0.0
1508
+
typescript: '>=4.8.4 <5.9.0'
1259
1509
1260
-
typescript@5.3.2:
1261
-
resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
1510
+
typescript@5.8.2:
1511
+
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
1262
1512
engines: {node: '>=14.17'}
1263
1513
hasBin: true
1264
1514
1265
-
unbox-primitive@1.0.2:
1266
-
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
1515
+
ufo@1.5.4:
1516
+
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
1267
1517
1268
-
untildify@4.0.0:
1269
-
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
1270
-
engines: {node: '>=8'}
1518
+
unbox-primitive@1.1.0:
1519
+
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
1520
+
engines: {node: '>= 0.4'}
1521
+
1522
+
unconfig@7.3.1:
1523
+
resolution: {integrity: sha512-LH5WL+un92tGAzWS87k7LkAfwpMdm7V0IXG2FxEjZz/QxiIW5J5LkcrKQThj0aRz6+h/lFmKI9EUXmK/T0bcrw==}
1524
+
1525
+
undici-types@6.20.0:
1526
+
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
1527
+
1528
+
undici-types@6.21.0:
1529
+
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
1271
1530
1272
1531
uri-js@4.4.1:
1273
1532
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
1274
1533
1275
-
which-boxed-primitive@1.0.2:
1276
-
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
1534
+
utilium@1.10.1:
1535
+
resolution: {integrity: sha512-GQINDTb/ocyz4acQj3GXAe0wipYxws6L+9ouqaq10KlInTk9DGvW9TJd0pYa/Xu3cppNnZuB4T/sBuSXpcN2ng==}
1277
1536
1278
-
which-builtin-type@1.1.3:
1279
-
resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==}
1537
+
which-boxed-primitive@1.1.1:
1538
+
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
1280
1539
engines: {node: '>= 0.4'}
1281
1540
1282
-
which-collection@1.0.1:
1283
-
resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
1541
+
which-builtin-type@1.2.1:
1542
+
resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
1543
+
engines: {node: '>= 0.4'}
1284
1544
1285
-
which-typed-array@1.1.13:
1286
-
resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==}
1545
+
which-collection@1.0.2:
1546
+
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
1547
+
engines: {node: '>= 0.4'}
1548
+
1549
+
which-typed-array@1.1.19:
1550
+
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
1287
1551
engines: {node: '>= 0.4'}
1288
1552
1289
1553
which@2.0.2:
···
1291
1555
engines: {node: '>= 8'}
1292
1556
hasBin: true
1293
1557
1294
-
wrappy@1.0.2:
1295
-
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
1296
-
1297
-
yallist@4.0.0:
1298
-
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
1558
+
yaml@2.7.1:
1559
+
resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
1560
+
engines: {node: '>= 14'}
1561
+
hasBin: true
1299
1562
1300
1563
yocto-queue@0.1.0:
1301
1564
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
1302
1565
engines: {node: '>=10'}
1303
1566
1567
+
zustand@5.0.3:
1568
+
resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==}
1569
+
engines: {node: '>=12.20.0'}
1570
+
peerDependencies:
1571
+
'@types/react': '>=18.0.0'
1572
+
immer: '>=9.0.6'
1573
+
react: '>=18.0.0'
1574
+
use-sync-external-store: '>=1.2.0'
1575
+
peerDependenciesMeta:
1576
+
'@types/react':
1577
+
optional: true
1578
+
immer:
1579
+
optional: true
1580
+
react:
1581
+
optional: true
1582
+
use-sync-external-store:
1583
+
optional: true
1584
+
1304
1585
snapshots:
1305
1586
1306
1587
'@aashutoshrathi/word-wrap@1.2.6': {}
1307
1588
1308
-
'@electron/asar@3.2.5':
1589
+
'@antfu/ni@24.3.0':
1309
1590
dependencies:
1310
-
commander: 5.1.0
1311
-
glob: 7.2.3
1312
-
minimatch: 3.1.2
1591
+
ansis: 3.17.0
1592
+
fzf: 0.5.2
1593
+
package-manager-detector: 1.1.0
1594
+
tinyexec: 1.0.1
1313
1595
1314
1596
'@esbuild/android-arm64@0.19.3':
1315
1597
optional: true
···
1377
1659
'@esbuild/win32-x64@0.19.3':
1378
1660
optional: true
1379
1661
1380
-
'@eslint-community/eslint-utils@4.4.0(eslint@8.55.0)':
1662
+
'@eslint-community/eslint-utils@4.5.1(eslint@9.23.0(jiti@2.4.2))':
1381
1663
dependencies:
1382
-
eslint: 8.55.0
1664
+
eslint: 9.23.0(jiti@2.4.2)
1383
1665
eslint-visitor-keys: 3.4.3
1384
1666
1385
-
'@eslint-community/regexpp@4.10.0': {}
1667
+
'@eslint-community/regexpp@4.12.1': {}
1386
1668
1387
-
'@eslint/eslintrc@2.1.4':
1669
+
'@eslint/config-array@0.19.2':
1670
+
dependencies:
1671
+
'@eslint/object-schema': 2.1.6
1672
+
debug: 4.4.0
1673
+
minimatch: 3.1.2
1674
+
transitivePeerDependencies:
1675
+
- supports-color
1676
+
1677
+
'@eslint/config-helpers@0.2.1': {}
1678
+
1679
+
'@eslint/core@0.12.0':
1680
+
dependencies:
1681
+
'@types/json-schema': 7.0.15
1682
+
1683
+
'@eslint/core@0.13.0':
1684
+
dependencies:
1685
+
'@types/json-schema': 7.0.15
1686
+
1687
+
'@eslint/eslintrc@3.3.1':
1388
1688
dependencies:
1389
1689
ajv: 6.12.6
1390
-
debug: 4.3.4
1391
-
espree: 9.6.1
1392
-
globals: 13.23.0
1393
-
ignore: 5.3.0
1690
+
debug: 4.4.0
1691
+
espree: 10.3.0
1692
+
globals: 14.0.0
1693
+
ignore: 5.3.2
1394
1694
import-fresh: 3.3.0
1395
1695
js-yaml: 4.1.0
1396
1696
minimatch: 3.1.2
···
1398
1698
transitivePeerDependencies:
1399
1699
- supports-color
1400
1700
1401
-
'@eslint/js@8.55.0': {}
1701
+
'@eslint/js@9.23.0': {}
1702
+
1703
+
'@eslint/object-schema@2.1.6': {}
1402
1704
1403
-
'@humanwhocodes/config-array@0.11.13':
1705
+
'@eslint/plugin-kit@0.2.8':
1404
1706
dependencies:
1405
-
'@humanwhocodes/object-schema': 2.0.1
1406
-
debug: 4.3.4
1407
-
minimatch: 3.1.2
1707
+
'@eslint/core': 0.13.0
1708
+
levn: 0.4.1
1709
+
1710
+
'@humanfs/core@0.19.1': {}
1711
+
1712
+
'@humanfs/node@0.16.6':
1713
+
dependencies:
1714
+
'@humanfs/core': 0.19.1
1715
+
'@humanwhocodes/retry': 0.3.1
1716
+
1717
+
'@humanwhocodes/module-importer@1.0.1': {}
1718
+
1719
+
'@humanwhocodes/retry@0.3.1': {}
1720
+
1721
+
'@humanwhocodes/retry@0.4.2': {}
1722
+
1723
+
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9(@types/eslint@9.6.1)(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0)(typescript@5.8.2)':
1724
+
dependencies:
1725
+
'@eslint/js': 9.23.0
1726
+
eslint: 9.23.0(jiti@2.4.2)
1727
+
eslint-config-prettier: 9.1.0(eslint@9.23.0(jiti@2.4.2))
1728
+
eslint-plugin-prettier: 5.2.6(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)))(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0)
1729
+
eslint-plugin-react: 7.37.5(eslint@9.23.0(jiti@2.4.2))
1730
+
typescript: 5.8.2
1731
+
typescript-eslint: 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1408
1732
transitivePeerDependencies:
1733
+
- '@types/eslint'
1734
+
- prettier
1409
1735
- supports-color
1410
1736
1411
-
'@humanwhocodes/module-importer@1.0.1': {}
1737
+
'@moonlight-mod/lunast@1.0.1':
1738
+
dependencies:
1739
+
astring: 1.9.0
1740
+
estree-toolkit: 1.7.8
1741
+
meriyah: 6.0.1
1412
1742
1413
-
'@humanwhocodes/object-schema@2.0.1': {}
1743
+
'@moonlight-mod/mappings@1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)':
1744
+
dependencies:
1745
+
'@moonlight-mod/lunast': 1.0.1
1746
+
'@moonlight-mod/moonmap': 1.0.5
1747
+
'@types/chroma-js': 3.1.0
1748
+
'@types/flux': 3.1.14
1749
+
'@types/highlightjs': 9.12.6
1750
+
'@types/lodash': 4.17.14
1751
+
'@types/platform': 1.3.6
1752
+
'@types/react': 18.3.20
1753
+
csstype: 3.1.3
1754
+
zustand: 5.0.3(@types/react@18.3.20)
1755
+
transitivePeerDependencies:
1756
+
- immer
1757
+
- react
1758
+
- use-sync-external-store
1759
+
1760
+
'@moonlight-mod/moonmap@1.0.5': {}
1414
1761
1415
1762
'@nodelib/fs.scandir@2.1.5':
1416
1763
dependencies:
···
1422
1769
'@nodelib/fs.walk@1.2.8':
1423
1770
dependencies:
1424
1771
'@nodelib/fs.scandir': 2.1.5
1425
-
fastq: 1.15.0
1772
+
fastq: 1.17.1
1426
1773
1427
-
'@pkgr/utils@2.4.2':
1774
+
'@pkgr/core@0.2.0': {}
1775
+
1776
+
'@quansync/fs@0.1.2':
1428
1777
dependencies:
1429
-
cross-spawn: 7.0.3
1430
-
fast-glob: 3.3.2
1431
-
is-glob: 4.0.3
1432
-
open: 9.1.0
1433
-
picocolors: 1.0.0
1434
-
tslib: 2.6.2
1778
+
quansync: 0.2.10
1779
+
1780
+
'@types/chroma-js@3.1.0': {}
1781
+
1782
+
'@types/chrome@0.0.313':
1783
+
dependencies:
1784
+
'@types/filesystem': 0.0.36
1785
+
'@types/har-format': 1.2.16
1786
+
1787
+
'@types/eslint@9.6.1':
1788
+
dependencies:
1789
+
'@types/estree': 1.0.7
1790
+
'@types/json-schema': 7.0.15
1791
+
optional: true
1792
+
1793
+
'@types/estree-jsx@1.0.5':
1794
+
dependencies:
1795
+
'@types/estree': 1.0.6
1796
+
1797
+
'@types/estree@1.0.6': {}
1798
+
1799
+
'@types/estree@1.0.7':
1800
+
optional: true
1435
1801
1436
-
'@types/fbemitter@2.0.33': {}
1802
+
'@types/fbemitter@2.0.35': {}
1437
1803
1438
-
'@types/flux@3.1.12':
1804
+
'@types/filesystem@0.0.36':
1439
1805
dependencies:
1440
-
'@types/fbemitter': 2.0.33
1441
-
'@types/react': 18.2.22
1806
+
'@types/filewriter': 0.0.33
1807
+
1808
+
'@types/filewriter@0.0.33': {}
1809
+
1810
+
'@types/flux@3.1.14':
1811
+
dependencies:
1812
+
'@types/fbemitter': 2.0.35
1813
+
'@types/react': 18.3.20
1814
+
1815
+
'@types/har-format@1.2.16': {}
1816
+
1817
+
'@types/highlightjs@9.12.6': {}
1442
1818
1443
1819
'@types/json-schema@7.0.15': {}
1820
+
1821
+
'@types/lodash@4.17.14': {}
1444
1822
1445
1823
'@types/node@18.17.17': {}
1446
1824
1447
-
'@types/prop-types@15.7.6': {}
1825
+
'@types/node@22.13.6':
1826
+
dependencies:
1827
+
undici-types: 6.20.0
1448
1828
1449
-
'@types/react@18.2.22':
1829
+
'@types/node@22.14.0':
1450
1830
dependencies:
1451
-
'@types/prop-types': 15.7.6
1452
-
'@types/scheduler': 0.16.3
1453
-
csstype: 3.1.2
1831
+
undici-types: 6.21.0
1454
1832
1455
-
'@types/scheduler@0.16.3': {}
1833
+
'@types/platform@1.3.6': {}
1456
1834
1457
-
'@types/semver@7.5.6': {}
1835
+
'@types/prop-types@15.7.13': {}
1458
1836
1459
-
'@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.2)':
1837
+
'@types/react@18.3.20':
1460
1838
dependencies:
1461
-
'@eslint-community/regexpp': 4.10.0
1462
-
'@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1463
-
'@typescript-eslint/scope-manager': 6.13.2
1464
-
'@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1465
-
'@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1466
-
'@typescript-eslint/visitor-keys': 6.13.2
1467
-
debug: 4.3.4
1468
-
eslint: 8.55.0
1839
+
'@types/prop-types': 15.7.13
1840
+
csstype: 3.1.3
1841
+
1842
+
'@typescript-eslint/eslint-plugin@8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
1843
+
dependencies:
1844
+
'@eslint-community/regexpp': 4.12.1
1845
+
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1846
+
'@typescript-eslint/scope-manager': 8.29.0
1847
+
'@typescript-eslint/type-utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1848
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1849
+
'@typescript-eslint/visitor-keys': 8.29.0
1850
+
eslint: 9.23.0(jiti@2.4.2)
1469
1851
graphemer: 1.4.0
1470
-
ignore: 5.3.0
1852
+
ignore: 5.3.2
1471
1853
natural-compare: 1.4.0
1472
-
semver: 7.5.4
1473
-
ts-api-utils: 1.0.3(typescript@5.3.2)
1474
-
typescript: 5.3.2
1854
+
ts-api-utils: 2.1.0(typescript@5.8.2)
1855
+
typescript: 5.8.2
1475
1856
transitivePeerDependencies:
1476
1857
- supports-color
1477
1858
1478
-
'@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.2)':
1859
+
'@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
1479
1860
dependencies:
1480
-
'@typescript-eslint/scope-manager': 6.13.2
1481
-
'@typescript-eslint/types': 6.13.2
1482
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
1483
-
'@typescript-eslint/visitor-keys': 6.13.2
1484
-
debug: 4.3.4
1485
-
eslint: 8.55.0
1486
-
typescript: 5.3.2
1861
+
'@typescript-eslint/scope-manager': 8.29.0
1862
+
'@typescript-eslint/types': 8.29.0
1863
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
1864
+
'@typescript-eslint/visitor-keys': 8.29.0
1865
+
debug: 4.4.0
1866
+
eslint: 9.23.0(jiti@2.4.2)
1867
+
typescript: 5.8.2
1487
1868
transitivePeerDependencies:
1488
1869
- supports-color
1489
1870
1490
-
'@typescript-eslint/scope-manager@6.13.2':
1871
+
'@typescript-eslint/scope-manager@8.29.0':
1491
1872
dependencies:
1492
-
'@typescript-eslint/types': 6.13.2
1493
-
'@typescript-eslint/visitor-keys': 6.13.2
1873
+
'@typescript-eslint/types': 8.29.0
1874
+
'@typescript-eslint/visitor-keys': 8.29.0
1494
1875
1495
-
'@typescript-eslint/type-utils@6.13.2(eslint@8.55.0)(typescript@5.3.2)':
1876
+
'@typescript-eslint/type-utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
1496
1877
dependencies:
1497
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
1498
-
'@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1499
-
debug: 4.3.4
1500
-
eslint: 8.55.0
1501
-
ts-api-utils: 1.0.3(typescript@5.3.2)
1502
-
typescript: 5.3.2
1878
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
1879
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1880
+
debug: 4.4.0
1881
+
eslint: 9.23.0(jiti@2.4.2)
1882
+
ts-api-utils: 2.1.0(typescript@5.8.2)
1883
+
typescript: 5.8.2
1503
1884
transitivePeerDependencies:
1504
1885
- supports-color
1505
1886
1506
-
'@typescript-eslint/types@6.13.2': {}
1887
+
'@typescript-eslint/types@8.29.0': {}
1507
1888
1508
-
'@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.2)':
1889
+
'@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.2)':
1509
1890
dependencies:
1510
-
'@typescript-eslint/types': 6.13.2
1511
-
'@typescript-eslint/visitor-keys': 6.13.2
1512
-
debug: 4.3.4
1513
-
globby: 11.1.0
1891
+
'@typescript-eslint/types': 8.29.0
1892
+
'@typescript-eslint/visitor-keys': 8.29.0
1893
+
debug: 4.4.0
1894
+
fast-glob: 3.3.2
1514
1895
is-glob: 4.0.3
1515
-
semver: 7.5.4
1516
-
ts-api-utils: 1.0.3(typescript@5.3.2)
1517
-
typescript: 5.3.2
1896
+
minimatch: 9.0.5
1897
+
semver: 7.7.1
1898
+
ts-api-utils: 2.1.0(typescript@5.8.2)
1899
+
typescript: 5.8.2
1518
1900
transitivePeerDependencies:
1519
1901
- supports-color
1520
1902
1521
-
'@typescript-eslint/utils@6.13.2(eslint@8.55.0)(typescript@5.3.2)':
1903
+
'@typescript-eslint/utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
1522
1904
dependencies:
1523
-
'@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
1524
-
'@types/json-schema': 7.0.15
1525
-
'@types/semver': 7.5.6
1526
-
'@typescript-eslint/scope-manager': 6.13.2
1527
-
'@typescript-eslint/types': 6.13.2
1528
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
1529
-
eslint: 8.55.0
1530
-
semver: 7.5.4
1905
+
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
1906
+
'@typescript-eslint/scope-manager': 8.29.0
1907
+
'@typescript-eslint/types': 8.29.0
1908
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
1909
+
eslint: 9.23.0(jiti@2.4.2)
1910
+
typescript: 5.8.2
1531
1911
transitivePeerDependencies:
1532
1912
- supports-color
1533
-
- typescript
1913
+
1914
+
'@typescript-eslint/visitor-keys@8.29.0':
1915
+
dependencies:
1916
+
'@typescript-eslint/types': 8.29.0
1917
+
eslint-visitor-keys: 4.2.0
1918
+
1919
+
'@xterm/xterm@5.5.0':
1920
+
optional: true
1921
+
1922
+
'@zenfs/core@2.0.0':
1923
+
dependencies:
1924
+
'@types/node': 22.13.6
1925
+
buffer: 6.0.3
1926
+
eventemitter3: 5.0.1
1927
+
readable-stream: 4.5.2
1928
+
utilium: 1.10.1
1534
1929
1535
-
'@typescript-eslint/visitor-keys@6.13.2':
1930
+
'@zenfs/dom@1.1.6(@zenfs/core@2.0.0)(utilium@1.10.1)':
1536
1931
dependencies:
1537
-
'@typescript-eslint/types': 6.13.2
1538
-
eslint-visitor-keys: 3.4.3
1932
+
'@zenfs/core': 2.0.0
1933
+
utilium: 1.10.1
1539
1934
1540
-
'@ungap/structured-clone@1.2.0': {}
1935
+
abort-controller@3.0.0:
1936
+
dependencies:
1937
+
event-target-shim: 5.0.1
1541
1938
1542
-
acorn-jsx@5.3.2(acorn@8.11.2):
1939
+
acorn-jsx@5.3.2(acorn@8.14.1):
1543
1940
dependencies:
1544
-
acorn: 8.11.2
1941
+
acorn: 8.14.1
1545
1942
1546
-
acorn@8.11.2: {}
1943
+
acorn@8.14.1: {}
1547
1944
1548
1945
ajv@6.12.6:
1549
1946
dependencies:
···
1552
1949
json-schema-traverse: 0.4.1
1553
1950
uri-js: 4.4.1
1554
1951
1555
-
ansi-regex@5.0.1: {}
1556
-
1557
1952
ansi-styles@4.3.0:
1558
1953
dependencies:
1559
1954
color-convert: 2.0.1
1955
+
1956
+
ansis@3.17.0: {}
1560
1957
1561
1958
argparse@2.0.1: {}
1562
1959
1563
-
array-buffer-byte-length@1.0.0:
1960
+
array-buffer-byte-length@1.0.2:
1564
1961
dependencies:
1565
-
call-bind: 1.0.5
1566
-
is-array-buffer: 3.0.2
1962
+
call-bound: 1.0.4
1963
+
is-array-buffer: 3.0.5
1567
1964
1568
-
array-includes@3.1.7:
1965
+
array-includes@3.1.8:
1569
1966
dependencies:
1570
-
call-bind: 1.0.5
1967
+
call-bind: 1.0.8
1571
1968
define-properties: 1.2.1
1572
-
es-abstract: 1.22.3
1573
-
get-intrinsic: 1.2.2
1574
-
is-string: 1.0.7
1575
-
1576
-
array-union@2.1.0: {}
1969
+
es-abstract: 1.23.9
1970
+
es-object-atoms: 1.1.1
1971
+
get-intrinsic: 1.3.0
1972
+
is-string: 1.1.1
1577
1973
1578
-
array.prototype.flat@1.3.2:
1974
+
array.prototype.findlast@1.2.5:
1579
1975
dependencies:
1580
-
call-bind: 1.0.5
1976
+
call-bind: 1.0.8
1581
1977
define-properties: 1.2.1
1582
-
es-abstract: 1.22.3
1583
-
es-shim-unscopables: 1.0.2
1978
+
es-abstract: 1.23.9
1979
+
es-errors: 1.3.0
1980
+
es-object-atoms: 1.1.1
1981
+
es-shim-unscopables: 1.1.0
1584
1982
1585
-
array.prototype.flatmap@1.3.2:
1983
+
array.prototype.flat@1.3.3:
1586
1984
dependencies:
1587
-
call-bind: 1.0.5
1985
+
call-bind: 1.0.8
1588
1986
define-properties: 1.2.1
1589
-
es-abstract: 1.22.3
1590
-
es-shim-unscopables: 1.0.2
1987
+
es-abstract: 1.23.9
1988
+
es-shim-unscopables: 1.1.0
1591
1989
1592
-
array.prototype.tosorted@1.1.2:
1990
+
array.prototype.flatmap@1.3.3:
1593
1991
dependencies:
1594
-
call-bind: 1.0.5
1992
+
call-bind: 1.0.8
1595
1993
define-properties: 1.2.1
1596
-
es-abstract: 1.22.3
1597
-
es-shim-unscopables: 1.0.2
1598
-
get-intrinsic: 1.2.2
1994
+
es-abstract: 1.23.9
1995
+
es-shim-unscopables: 1.1.0
1599
1996
1600
-
arraybuffer.prototype.slice@1.0.2:
1997
+
array.prototype.tosorted@1.1.4:
1601
1998
dependencies:
1602
-
array-buffer-byte-length: 1.0.0
1603
-
call-bind: 1.0.5
1999
+
call-bind: 1.0.8
1604
2000
define-properties: 1.2.1
1605
-
es-abstract: 1.22.3
1606
-
get-intrinsic: 1.2.2
1607
-
is-array-buffer: 3.0.2
1608
-
is-shared-array-buffer: 1.0.2
2001
+
es-abstract: 1.23.9
2002
+
es-errors: 1.3.0
2003
+
es-shim-unscopables: 1.1.0
1609
2004
1610
-
asynciterator.prototype@1.0.0:
2005
+
arraybuffer.prototype.slice@1.0.4:
1611
2006
dependencies:
1612
-
has-symbols: 1.0.3
1613
-
1614
-
available-typed-arrays@1.0.5: {}
2007
+
array-buffer-byte-length: 1.0.2
2008
+
call-bind: 1.0.8
2009
+
define-properties: 1.2.1
2010
+
es-abstract: 1.23.9
2011
+
es-errors: 1.3.0
2012
+
get-intrinsic: 1.3.0
2013
+
is-array-buffer: 3.0.5
1615
2014
1616
-
balanced-match@1.0.2: {}
2015
+
astring@1.9.0: {}
1617
2016
1618
-
big-integer@1.6.52: {}
2017
+
async-function@1.0.0: {}
1619
2018
1620
-
bplist-parser@0.2.0:
2019
+
available-typed-arrays@1.0.7:
1621
2020
dependencies:
1622
-
big-integer: 1.6.52
2021
+
possible-typed-array-names: 1.1.0
2022
+
2023
+
balanced-match@1.0.2: {}
2024
+
2025
+
base64-js@1.5.1: {}
1623
2026
1624
2027
brace-expansion@1.1.11:
1625
2028
dependencies:
1626
2029
balanced-match: 1.0.2
1627
2030
concat-map: 0.0.1
1628
2031
1629
-
braces@3.0.2:
2032
+
brace-expansion@2.0.1:
1630
2033
dependencies:
1631
-
fill-range: 7.0.1
2034
+
balanced-match: 1.0.2
1632
2035
1633
-
bundle-name@3.0.0:
2036
+
braces@3.0.3:
1634
2037
dependencies:
1635
-
run-applescript: 5.0.0
2038
+
fill-range: 7.1.1
1636
2039
1637
-
call-bind@1.0.5:
2040
+
buffer@6.0.3:
1638
2041
dependencies:
2042
+
base64-js: 1.5.1
2043
+
ieee754: 1.2.1
2044
+
2045
+
cac@6.7.14: {}
2046
+
2047
+
call-bind-apply-helpers@1.0.2:
2048
+
dependencies:
2049
+
es-errors: 1.3.0
1639
2050
function-bind: 1.1.2
1640
-
get-intrinsic: 1.2.2
1641
-
set-function-length: 1.1.1
2051
+
2052
+
call-bind@1.0.8:
2053
+
dependencies:
2054
+
call-bind-apply-helpers: 1.0.2
2055
+
es-define-property: 1.0.1
2056
+
get-intrinsic: 1.3.0
2057
+
set-function-length: 1.2.2
2058
+
2059
+
call-bound@1.0.4:
2060
+
dependencies:
2061
+
call-bind-apply-helpers: 1.0.2
2062
+
get-intrinsic: 1.3.0
1642
2063
1643
2064
callsites@3.1.0: {}
1644
2065
···
1653
2074
1654
2075
color-name@1.1.4: {}
1655
2076
1656
-
commander@5.1.0: {}
1657
-
1658
2077
concat-map@0.0.1: {}
1659
2078
1660
-
cross-spawn@7.0.3:
2079
+
cross-spawn@7.0.6:
1661
2080
dependencies:
1662
2081
path-key: 3.1.1
1663
2082
shebang-command: 2.0.0
1664
2083
which: 2.0.2
1665
2084
1666
-
csstype@3.1.2: {}
2085
+
csstype@3.1.3: {}
1667
2086
1668
-
debug@4.3.4:
2087
+
data-view-buffer@1.0.2:
1669
2088
dependencies:
1670
-
ms: 2.1.2
2089
+
call-bound: 1.0.4
2090
+
es-errors: 1.3.0
2091
+
is-data-view: 1.0.2
1671
2092
1672
-
deep-is@0.1.4: {}
1673
-
1674
-
default-browser-id@3.0.0:
2093
+
data-view-byte-length@1.0.2:
1675
2094
dependencies:
1676
-
bplist-parser: 0.2.0
1677
-
untildify: 4.0.0
2095
+
call-bound: 1.0.4
2096
+
es-errors: 1.3.0
2097
+
is-data-view: 1.0.2
1678
2098
1679
-
default-browser@4.0.0:
2099
+
data-view-byte-offset@1.0.1:
1680
2100
dependencies:
1681
-
bundle-name: 3.0.0
1682
-
default-browser-id: 3.0.0
1683
-
execa: 7.2.0
1684
-
titleize: 3.0.0
2101
+
call-bound: 1.0.4
2102
+
es-errors: 1.3.0
2103
+
is-data-view: 1.0.2
1685
2104
1686
-
define-data-property@1.1.1:
2105
+
debug@4.4.0:
1687
2106
dependencies:
1688
-
get-intrinsic: 1.2.2
1689
-
gopd: 1.0.1
1690
-
has-property-descriptors: 1.0.1
2107
+
ms: 2.1.3
1691
2108
1692
-
define-lazy-prop@3.0.0: {}
2109
+
deep-is@0.1.4: {}
2110
+
2111
+
define-data-property@1.1.4:
2112
+
dependencies:
2113
+
es-define-property: 1.0.1
2114
+
es-errors: 1.3.0
2115
+
gopd: 1.2.0
1693
2116
1694
2117
define-properties@1.2.1:
1695
2118
dependencies:
1696
-
define-data-property: 1.1.1
1697
-
has-property-descriptors: 1.0.1
2119
+
define-data-property: 1.1.4
2120
+
has-property-descriptors: 1.0.2
1698
2121
object-keys: 1.1.1
1699
2122
1700
-
dir-glob@3.0.1:
1701
-
dependencies:
1702
-
path-type: 4.0.0
2123
+
defu@6.1.4: {}
2124
+
2125
+
destr@2.0.4: {}
1703
2126
1704
2127
doctrine@2.1.0:
1705
2128
dependencies:
1706
2129
esutils: 2.0.3
1707
2130
1708
-
doctrine@3.0.0:
2131
+
dunder-proto@1.0.1:
1709
2132
dependencies:
1710
-
esutils: 2.0.3
2133
+
call-bind-apply-helpers: 1.0.2
2134
+
es-errors: 1.3.0
2135
+
gopd: 1.2.0
1711
2136
1712
-
es-abstract@1.22.3:
2137
+
es-abstract@1.23.9:
1713
2138
dependencies:
1714
-
array-buffer-byte-length: 1.0.0
1715
-
arraybuffer.prototype.slice: 1.0.2
1716
-
available-typed-arrays: 1.0.5
1717
-
call-bind: 1.0.5
1718
-
es-set-tostringtag: 2.0.2
1719
-
es-to-primitive: 1.2.1
1720
-
function.prototype.name: 1.1.6
1721
-
get-intrinsic: 1.2.2
1722
-
get-symbol-description: 1.0.0
1723
-
globalthis: 1.0.3
1724
-
gopd: 1.0.1
1725
-
has-property-descriptors: 1.0.1
1726
-
has-proto: 1.0.1
1727
-
has-symbols: 1.0.3
1728
-
hasown: 2.0.0
1729
-
internal-slot: 1.0.6
1730
-
is-array-buffer: 3.0.2
2139
+
array-buffer-byte-length: 1.0.2
2140
+
arraybuffer.prototype.slice: 1.0.4
2141
+
available-typed-arrays: 1.0.7
2142
+
call-bind: 1.0.8
2143
+
call-bound: 1.0.4
2144
+
data-view-buffer: 1.0.2
2145
+
data-view-byte-length: 1.0.2
2146
+
data-view-byte-offset: 1.0.1
2147
+
es-define-property: 1.0.1
2148
+
es-errors: 1.3.0
2149
+
es-object-atoms: 1.1.1
2150
+
es-set-tostringtag: 2.1.0
2151
+
es-to-primitive: 1.3.0
2152
+
function.prototype.name: 1.1.8
2153
+
get-intrinsic: 1.3.0
2154
+
get-proto: 1.0.1
2155
+
get-symbol-description: 1.1.0
2156
+
globalthis: 1.0.4
2157
+
gopd: 1.2.0
2158
+
has-property-descriptors: 1.0.2
2159
+
has-proto: 1.2.0
2160
+
has-symbols: 1.1.0
2161
+
hasown: 2.0.2
2162
+
internal-slot: 1.1.0
2163
+
is-array-buffer: 3.0.5
1731
2164
is-callable: 1.2.7
1732
-
is-negative-zero: 2.0.2
1733
-
is-regex: 1.1.4
1734
-
is-shared-array-buffer: 1.0.2
1735
-
is-string: 1.0.7
1736
-
is-typed-array: 1.1.12
1737
-
is-weakref: 1.0.2
1738
-
object-inspect: 1.13.1
2165
+
is-data-view: 1.0.2
2166
+
is-regex: 1.2.1
2167
+
is-shared-array-buffer: 1.0.4
2168
+
is-string: 1.1.1
2169
+
is-typed-array: 1.1.15
2170
+
is-weakref: 1.1.1
2171
+
math-intrinsics: 1.1.0
2172
+
object-inspect: 1.13.4
1739
2173
object-keys: 1.1.1
1740
-
object.assign: 4.1.5
1741
-
regexp.prototype.flags: 1.5.1
1742
-
safe-array-concat: 1.0.1
1743
-
safe-regex-test: 1.0.0
1744
-
string.prototype.trim: 1.2.8
1745
-
string.prototype.trimend: 1.0.7
1746
-
string.prototype.trimstart: 1.0.7
1747
-
typed-array-buffer: 1.0.0
1748
-
typed-array-byte-length: 1.0.0
1749
-
typed-array-byte-offset: 1.0.0
1750
-
typed-array-length: 1.0.4
1751
-
unbox-primitive: 1.0.2
1752
-
which-typed-array: 1.1.13
2174
+
object.assign: 4.1.7
2175
+
own-keys: 1.0.1
2176
+
regexp.prototype.flags: 1.5.4
2177
+
safe-array-concat: 1.1.3
2178
+
safe-push-apply: 1.0.0
2179
+
safe-regex-test: 1.1.0
2180
+
set-proto: 1.0.0
2181
+
string.prototype.trim: 1.2.10
2182
+
string.prototype.trimend: 1.0.9
2183
+
string.prototype.trimstart: 1.0.8
2184
+
typed-array-buffer: 1.0.3
2185
+
typed-array-byte-length: 1.0.3
2186
+
typed-array-byte-offset: 1.0.4
2187
+
typed-array-length: 1.0.7
2188
+
unbox-primitive: 1.1.0
2189
+
which-typed-array: 1.1.19
1753
2190
1754
-
es-iterator-helpers@1.0.15:
2191
+
es-define-property@1.0.1: {}
2192
+
2193
+
es-errors@1.3.0: {}
2194
+
2195
+
es-iterator-helpers@1.2.1:
1755
2196
dependencies:
1756
-
asynciterator.prototype: 1.0.0
1757
-
call-bind: 1.0.5
2197
+
call-bind: 1.0.8
2198
+
call-bound: 1.0.4
1758
2199
define-properties: 1.2.1
1759
-
es-abstract: 1.22.3
1760
-
es-set-tostringtag: 2.0.2
2200
+
es-abstract: 1.23.9
2201
+
es-errors: 1.3.0
2202
+
es-set-tostringtag: 2.1.0
1761
2203
function-bind: 1.1.2
1762
-
get-intrinsic: 1.2.2
1763
-
globalthis: 1.0.3
1764
-
has-property-descriptors: 1.0.1
1765
-
has-proto: 1.0.1
1766
-
has-symbols: 1.0.3
1767
-
internal-slot: 1.0.6
1768
-
iterator.prototype: 1.1.2
1769
-
safe-array-concat: 1.0.1
2204
+
get-intrinsic: 1.3.0
2205
+
globalthis: 1.0.4
2206
+
gopd: 1.2.0
2207
+
has-property-descriptors: 1.0.2
2208
+
has-proto: 1.2.0
2209
+
has-symbols: 1.1.0
2210
+
internal-slot: 1.1.0
2211
+
iterator.prototype: 1.1.5
2212
+
safe-array-concat: 1.1.3
1770
2213
1771
-
es-set-tostringtag@2.0.2:
2214
+
es-object-atoms@1.1.1:
1772
2215
dependencies:
1773
-
get-intrinsic: 1.2.2
1774
-
has-tostringtag: 1.0.0
1775
-
hasown: 2.0.0
2216
+
es-errors: 1.3.0
1776
2217
1777
-
es-shim-unscopables@1.0.2:
2218
+
es-set-tostringtag@2.1.0:
1778
2219
dependencies:
1779
-
hasown: 2.0.0
2220
+
es-errors: 1.3.0
2221
+
get-intrinsic: 1.3.0
2222
+
has-tostringtag: 1.0.2
2223
+
hasown: 2.0.2
1780
2224
1781
-
es-to-primitive@1.2.1:
2225
+
es-shim-unscopables@1.1.0:
2226
+
dependencies:
2227
+
hasown: 2.0.2
2228
+
2229
+
es-to-primitive@1.3.0:
1782
2230
dependencies:
1783
2231
is-callable: 1.2.7
1784
-
is-date-object: 1.0.5
1785
-
is-symbol: 1.0.4
2232
+
is-date-object: 1.1.0
2233
+
is-symbol: 1.1.1
1786
2234
1787
2235
esbuild-copy-static-files@0.1.0: {}
1788
2236
···
1813
2261
1814
2262
escape-string-regexp@4.0.0: {}
1815
2263
1816
-
eslint-config-prettier@9.1.0(eslint@8.55.0):
2264
+
eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)):
1817
2265
dependencies:
1818
-
eslint: 8.55.0
2266
+
eslint: 9.23.0(jiti@2.4.2)
1819
2267
1820
-
eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.0):
2268
+
eslint-plugin-prettier@5.2.6(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)))(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0):
1821
2269
dependencies:
1822
-
eslint: 8.55.0
1823
-
eslint-config-prettier: 9.1.0(eslint@8.55.0)
2270
+
eslint: 9.23.0(jiti@2.4.2)
1824
2271
prettier: 3.1.0
1825
2272
prettier-linter-helpers: 1.0.0
1826
-
synckit: 0.8.6
2273
+
synckit: 0.11.1
2274
+
optionalDependencies:
2275
+
'@types/eslint': 9.6.1
2276
+
eslint-config-prettier: 9.1.0(eslint@9.23.0(jiti@2.4.2))
1827
2277
1828
-
eslint-plugin-react@7.33.2(eslint@8.55.0):
2278
+
eslint-plugin-react@7.37.5(eslint@9.23.0(jiti@2.4.2)):
1829
2279
dependencies:
1830
-
array-includes: 3.1.7
1831
-
array.prototype.flatmap: 1.3.2
1832
-
array.prototype.tosorted: 1.1.2
2280
+
array-includes: 3.1.8
2281
+
array.prototype.findlast: 1.2.5
2282
+
array.prototype.flatmap: 1.3.3
2283
+
array.prototype.tosorted: 1.1.4
1833
2284
doctrine: 2.1.0
1834
-
es-iterator-helpers: 1.0.15
1835
-
eslint: 8.55.0
2285
+
es-iterator-helpers: 1.2.1
2286
+
eslint: 9.23.0(jiti@2.4.2)
1836
2287
estraverse: 5.3.0
2288
+
hasown: 2.0.2
1837
2289
jsx-ast-utils: 3.3.5
1838
2290
minimatch: 3.1.2
1839
-
object.entries: 1.1.7
1840
-
object.fromentries: 2.0.7
1841
-
object.hasown: 1.1.3
1842
-
object.values: 1.1.7
2291
+
object.entries: 1.1.9
2292
+
object.fromentries: 2.0.8
2293
+
object.values: 1.2.1
1843
2294
prop-types: 15.8.1
1844
2295
resolve: 2.0.0-next.5
1845
2296
semver: 6.3.1
1846
-
string.prototype.matchall: 4.0.10
2297
+
string.prototype.matchall: 4.0.12
2298
+
string.prototype.repeat: 1.0.0
1847
2299
1848
-
eslint-scope@7.2.2:
2300
+
eslint-scope@8.3.0:
1849
2301
dependencies:
1850
2302
esrecurse: 4.3.0
1851
2303
estraverse: 5.3.0
1852
2304
1853
2305
eslint-visitor-keys@3.4.3: {}
1854
2306
1855
-
eslint@8.55.0:
2307
+
eslint-visitor-keys@4.2.0: {}
2308
+
2309
+
eslint@9.23.0(jiti@2.4.2):
1856
2310
dependencies:
1857
-
'@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
1858
-
'@eslint-community/regexpp': 4.10.0
1859
-
'@eslint/eslintrc': 2.1.4
1860
-
'@eslint/js': 8.55.0
1861
-
'@humanwhocodes/config-array': 0.11.13
2311
+
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
2312
+
'@eslint-community/regexpp': 4.12.1
2313
+
'@eslint/config-array': 0.19.2
2314
+
'@eslint/config-helpers': 0.2.1
2315
+
'@eslint/core': 0.12.0
2316
+
'@eslint/eslintrc': 3.3.1
2317
+
'@eslint/js': 9.23.0
2318
+
'@eslint/plugin-kit': 0.2.8
2319
+
'@humanfs/node': 0.16.6
1862
2320
'@humanwhocodes/module-importer': 1.0.1
1863
-
'@nodelib/fs.walk': 1.2.8
1864
-
'@ungap/structured-clone': 1.2.0
2321
+
'@humanwhocodes/retry': 0.4.2
2322
+
'@types/estree': 1.0.6
2323
+
'@types/json-schema': 7.0.15
1865
2324
ajv: 6.12.6
1866
2325
chalk: 4.1.2
1867
-
cross-spawn: 7.0.3
1868
-
debug: 4.3.4
1869
-
doctrine: 3.0.0
2326
+
cross-spawn: 7.0.6
2327
+
debug: 4.4.0
1870
2328
escape-string-regexp: 4.0.0
1871
-
eslint-scope: 7.2.2
1872
-
eslint-visitor-keys: 3.4.3
1873
-
espree: 9.6.1
1874
-
esquery: 1.5.0
2329
+
eslint-scope: 8.3.0
2330
+
eslint-visitor-keys: 4.2.0
2331
+
espree: 10.3.0
2332
+
esquery: 1.6.0
1875
2333
esutils: 2.0.3
1876
2334
fast-deep-equal: 3.1.3
1877
-
file-entry-cache: 6.0.1
2335
+
file-entry-cache: 8.0.0
1878
2336
find-up: 5.0.0
1879
2337
glob-parent: 6.0.2
1880
-
globals: 13.23.0
1881
-
graphemer: 1.4.0
1882
-
ignore: 5.3.0
2338
+
ignore: 5.3.2
1883
2339
imurmurhash: 0.1.4
1884
2340
is-glob: 4.0.3
1885
-
is-path-inside: 3.0.3
1886
-
js-yaml: 4.1.0
1887
2341
json-stable-stringify-without-jsonify: 1.0.1
1888
-
levn: 0.4.1
1889
2342
lodash.merge: 4.6.2
1890
2343
minimatch: 3.1.2
1891
2344
natural-compare: 1.4.0
1892
2345
optionator: 0.9.3
1893
-
strip-ansi: 6.0.1
1894
-
text-table: 0.2.0
2346
+
optionalDependencies:
2347
+
jiti: 2.4.2
1895
2348
transitivePeerDependencies:
1896
2349
- supports-color
1897
2350
1898
-
espree@9.6.1:
2351
+
espree@10.3.0:
1899
2352
dependencies:
1900
-
acorn: 8.11.2
1901
-
acorn-jsx: 5.3.2(acorn@8.11.2)
1902
-
eslint-visitor-keys: 3.4.3
2353
+
acorn: 8.14.1
2354
+
acorn-jsx: 5.3.2(acorn@8.14.1)
2355
+
eslint-visitor-keys: 4.2.0
1903
2356
1904
-
esquery@1.5.0:
2357
+
esquery@1.6.0:
1905
2358
dependencies:
1906
2359
estraverse: 5.3.0
1907
2360
···
1911
2364
1912
2365
estraverse@5.3.0: {}
1913
2366
2367
+
estree-toolkit@1.7.8:
2368
+
dependencies:
2369
+
'@types/estree': 1.0.6
2370
+
'@types/estree-jsx': 1.0.5
2371
+
1914
2372
esutils@2.0.3: {}
1915
2373
1916
-
execa@5.1.1:
1917
-
dependencies:
1918
-
cross-spawn: 7.0.3
1919
-
get-stream: 6.0.1
1920
-
human-signals: 2.1.0
1921
-
is-stream: 2.0.1
1922
-
merge-stream: 2.0.0
1923
-
npm-run-path: 4.0.1
1924
-
onetime: 5.1.2
1925
-
signal-exit: 3.0.7
1926
-
strip-final-newline: 2.0.0
2374
+
event-target-shim@5.0.1: {}
1927
2375
1928
-
execa@7.2.0:
1929
-
dependencies:
1930
-
cross-spawn: 7.0.3
1931
-
get-stream: 6.0.1
1932
-
human-signals: 4.3.1
1933
-
is-stream: 3.0.0
1934
-
merge-stream: 2.0.0
1935
-
npm-run-path: 5.1.0
1936
-
onetime: 6.0.0
1937
-
signal-exit: 3.0.7
1938
-
strip-final-newline: 3.0.0
2376
+
eventemitter3@5.0.1: {}
2377
+
2378
+
events@3.3.0: {}
1939
2379
1940
2380
fast-deep-equal@3.1.3: {}
1941
2381
···
1947
2387
'@nodelib/fs.walk': 1.2.8
1948
2388
glob-parent: 5.1.2
1949
2389
merge2: 1.4.1
1950
-
micromatch: 4.0.5
2390
+
micromatch: 4.0.8
1951
2391
1952
2392
fast-json-stable-stringify@2.1.0: {}
1953
2393
1954
2394
fast-levenshtein@2.0.6: {}
1955
2395
1956
-
fastq@1.15.0:
2396
+
fastq@1.17.1:
1957
2397
dependencies:
1958
2398
reusify: 1.0.4
1959
2399
1960
-
file-entry-cache@6.0.1:
2400
+
fdir@6.4.3(picomatch@4.0.2):
2401
+
optionalDependencies:
2402
+
picomatch: 4.0.2
2403
+
2404
+
file-entry-cache@8.0.0:
1961
2405
dependencies:
1962
-
flat-cache: 3.2.0
2406
+
flat-cache: 4.0.1
1963
2407
1964
-
fill-range@7.0.1:
2408
+
fill-range@7.1.1:
1965
2409
dependencies:
1966
2410
to-regex-range: 5.0.1
2411
+
2412
+
find-up-simple@1.0.1: {}
1967
2413
1968
2414
find-up@5.0.0:
1969
2415
dependencies:
1970
2416
locate-path: 6.0.0
1971
2417
path-exists: 4.0.0
1972
2418
1973
-
flat-cache@3.2.0:
2419
+
flat-cache@4.0.1:
1974
2420
dependencies:
1975
2421
flatted: 3.2.9
1976
2422
keyv: 4.5.4
1977
-
rimraf: 3.0.2
1978
2423
1979
2424
flatted@3.2.9: {}
1980
2425
1981
-
for-each@0.3.3:
2426
+
for-each@0.3.5:
1982
2427
dependencies:
1983
2428
is-callable: 1.2.7
1984
2429
1985
-
fs.realpath@1.0.0: {}
1986
-
1987
2430
function-bind@1.1.2: {}
1988
2431
1989
-
function.prototype.name@1.1.6:
2432
+
function.prototype.name@1.1.8:
1990
2433
dependencies:
1991
-
call-bind: 1.0.5
2434
+
call-bind: 1.0.8
2435
+
call-bound: 1.0.4
1992
2436
define-properties: 1.2.1
1993
-
es-abstract: 1.22.3
1994
2437
functions-have-names: 1.2.3
2438
+
hasown: 2.0.2
2439
+
is-callable: 1.2.7
1995
2440
1996
2441
functions-have-names@1.2.3: {}
1997
2442
1998
-
get-intrinsic@1.2.2:
2443
+
fzf@0.5.2: {}
2444
+
2445
+
get-intrinsic@1.3.0:
1999
2446
dependencies:
2447
+
call-bind-apply-helpers: 1.0.2
2448
+
es-define-property: 1.0.1
2449
+
es-errors: 1.3.0
2450
+
es-object-atoms: 1.1.1
2000
2451
function-bind: 1.1.2
2001
-
has-proto: 1.0.1
2002
-
has-symbols: 1.0.3
2003
-
hasown: 2.0.0
2452
+
get-proto: 1.0.1
2453
+
gopd: 1.2.0
2454
+
has-symbols: 1.1.0
2455
+
hasown: 2.0.2
2456
+
math-intrinsics: 1.1.0
2004
2457
2005
-
get-stream@6.0.1: {}
2458
+
get-proto@1.0.1:
2459
+
dependencies:
2460
+
dunder-proto: 1.0.1
2461
+
es-object-atoms: 1.1.1
2006
2462
2007
-
get-symbol-description@1.0.0:
2463
+
get-symbol-description@1.1.0:
2008
2464
dependencies:
2009
-
call-bind: 1.0.5
2010
-
get-intrinsic: 1.2.2
2465
+
call-bound: 1.0.4
2466
+
es-errors: 1.3.0
2467
+
get-intrinsic: 1.3.0
2011
2468
2012
2469
glob-parent@5.1.2:
2013
2470
dependencies:
···
2017
2474
dependencies:
2018
2475
is-glob: 4.0.3
2019
2476
2020
-
glob@7.2.3:
2021
-
dependencies:
2022
-
fs.realpath: 1.0.0
2023
-
inflight: 1.0.6
2024
-
inherits: 2.0.4
2025
-
minimatch: 3.1.2
2026
-
once: 1.4.0
2027
-
path-is-absolute: 1.0.1
2477
+
globals@14.0.0: {}
2028
2478
2029
-
globals@13.23.0:
2030
-
dependencies:
2031
-
type-fest: 0.20.2
2032
-
2033
-
globalthis@1.0.3:
2479
+
globalthis@1.0.4:
2034
2480
dependencies:
2035
2481
define-properties: 1.2.1
2482
+
gopd: 1.2.0
2036
2483
2037
-
globby@11.1.0:
2038
-
dependencies:
2039
-
array-union: 2.1.0
2040
-
dir-glob: 3.0.1
2041
-
fast-glob: 3.3.2
2042
-
ignore: 5.3.0
2043
-
merge2: 1.4.1
2044
-
slash: 3.0.0
2045
-
2046
-
gopd@1.0.1:
2047
-
dependencies:
2048
-
get-intrinsic: 1.2.2
2484
+
gopd@1.2.0: {}
2049
2485
2050
2486
graphemer@1.4.0: {}
2051
2487
2052
-
has-bigints@1.0.2: {}
2488
+
has-bigints@1.1.0: {}
2053
2489
2054
2490
has-flag@4.0.0: {}
2055
2491
2056
-
has-property-descriptors@1.0.1:
2492
+
has-property-descriptors@1.0.2:
2057
2493
dependencies:
2058
-
get-intrinsic: 1.2.2
2494
+
es-define-property: 1.0.1
2059
2495
2060
-
has-proto@1.0.1: {}
2496
+
has-proto@1.2.0:
2497
+
dependencies:
2498
+
dunder-proto: 1.0.1
2061
2499
2062
-
has-symbols@1.0.3: {}
2500
+
has-symbols@1.1.0: {}
2063
2501
2064
-
has-tostringtag@1.0.0:
2502
+
has-tostringtag@1.0.2:
2065
2503
dependencies:
2066
-
has-symbols: 1.0.3
2504
+
has-symbols: 1.1.0
2067
2505
2068
-
hasown@2.0.0:
2506
+
hasown@2.0.2:
2069
2507
dependencies:
2070
2508
function-bind: 1.1.2
2071
2509
2072
-
human-signals@2.1.0: {}
2073
-
2074
-
human-signals@4.3.1: {}
2075
-
2076
2510
husky@8.0.3: {}
2077
2511
2078
-
ignore@5.3.0: {}
2512
+
ieee754@1.2.1: {}
2513
+
2514
+
ignore@5.3.2: {}
2079
2515
2080
2516
import-fresh@3.3.0:
2081
2517
dependencies:
···
2084
2520
2085
2521
imurmurhash@0.1.4: {}
2086
2522
2087
-
inflight@1.0.6:
2523
+
internal-slot@1.1.0:
2088
2524
dependencies:
2089
-
once: 1.4.0
2090
-
wrappy: 1.0.2
2525
+
es-errors: 1.3.0
2526
+
hasown: 2.0.2
2527
+
side-channel: 1.1.0
2091
2528
2092
-
inherits@2.0.4: {}
2093
-
2094
-
internal-slot@1.0.6:
2529
+
is-array-buffer@3.0.5:
2095
2530
dependencies:
2096
-
get-intrinsic: 1.2.2
2097
-
hasown: 2.0.0
2098
-
side-channel: 1.0.4
2531
+
call-bind: 1.0.8
2532
+
call-bound: 1.0.4
2533
+
get-intrinsic: 1.3.0
2099
2534
2100
-
is-array-buffer@3.0.2:
2535
+
is-async-function@2.1.1:
2101
2536
dependencies:
2102
-
call-bind: 1.0.5
2103
-
get-intrinsic: 1.2.2
2104
-
is-typed-array: 1.1.12
2537
+
async-function: 1.0.0
2538
+
call-bound: 1.0.4
2539
+
get-proto: 1.0.1
2540
+
has-tostringtag: 1.0.2
2541
+
safe-regex-test: 1.1.0
2105
2542
2106
-
is-async-function@2.0.0:
2543
+
is-bigint@1.1.0:
2107
2544
dependencies:
2108
-
has-tostringtag: 1.0.0
2545
+
has-bigints: 1.1.0
2109
2546
2110
-
is-bigint@1.0.4:
2547
+
is-boolean-object@1.2.2:
2111
2548
dependencies:
2112
-
has-bigints: 1.0.2
2113
-
2114
-
is-boolean-object@1.1.2:
2115
-
dependencies:
2116
-
call-bind: 1.0.5
2117
-
has-tostringtag: 1.0.0
2549
+
call-bound: 1.0.4
2550
+
has-tostringtag: 1.0.2
2118
2551
2119
2552
is-callable@1.2.7: {}
2120
2553
2121
-
is-core-module@2.13.1:
2554
+
is-core-module@2.16.1:
2122
2555
dependencies:
2123
-
hasown: 2.0.0
2556
+
hasown: 2.0.2
2124
2557
2125
-
is-date-object@1.0.5:
2558
+
is-data-view@1.0.2:
2126
2559
dependencies:
2127
-
has-tostringtag: 1.0.0
2560
+
call-bound: 1.0.4
2561
+
get-intrinsic: 1.3.0
2562
+
is-typed-array: 1.1.15
2128
2563
2129
-
is-docker@2.2.1: {}
2130
-
2131
-
is-docker@3.0.0: {}
2564
+
is-date-object@1.1.0:
2565
+
dependencies:
2566
+
call-bound: 1.0.4
2567
+
has-tostringtag: 1.0.2
2132
2568
2133
2569
is-extglob@2.1.1: {}
2134
2570
2135
-
is-finalizationregistry@1.0.2:
2571
+
is-finalizationregistry@1.1.1:
2136
2572
dependencies:
2137
-
call-bind: 1.0.5
2573
+
call-bound: 1.0.4
2138
2574
2139
-
is-generator-function@1.0.10:
2575
+
is-generator-function@1.1.0:
2140
2576
dependencies:
2141
-
has-tostringtag: 1.0.0
2577
+
call-bound: 1.0.4
2578
+
get-proto: 1.0.1
2579
+
has-tostringtag: 1.0.2
2580
+
safe-regex-test: 1.1.0
2142
2581
2143
2582
is-glob@4.0.3:
2144
2583
dependencies:
2145
2584
is-extglob: 2.1.1
2146
2585
2147
-
is-inside-container@1.0.0:
2148
-
dependencies:
2149
-
is-docker: 3.0.0
2150
-
2151
-
is-map@2.0.2: {}
2152
-
2153
-
is-negative-zero@2.0.2: {}
2586
+
is-map@2.0.3: {}
2154
2587
2155
-
is-number-object@1.0.7:
2588
+
is-number-object@1.1.1:
2156
2589
dependencies:
2157
-
has-tostringtag: 1.0.0
2590
+
call-bound: 1.0.4
2591
+
has-tostringtag: 1.0.2
2158
2592
2159
2593
is-number@7.0.0: {}
2160
2594
2161
-
is-path-inside@3.0.3: {}
2162
-
2163
-
is-regex@1.1.4:
2595
+
is-regex@1.2.1:
2164
2596
dependencies:
2165
-
call-bind: 1.0.5
2166
-
has-tostringtag: 1.0.0
2597
+
call-bound: 1.0.4
2598
+
gopd: 1.2.0
2599
+
has-tostringtag: 1.0.2
2600
+
hasown: 2.0.2
2167
2601
2168
-
is-set@2.0.2: {}
2602
+
is-set@2.0.3: {}
2169
2603
2170
-
is-shared-array-buffer@1.0.2:
2604
+
is-shared-array-buffer@1.0.4:
2171
2605
dependencies:
2172
-
call-bind: 1.0.5
2173
-
2174
-
is-stream@2.0.1: {}
2175
-
2176
-
is-stream@3.0.0: {}
2606
+
call-bound: 1.0.4
2177
2607
2178
-
is-string@1.0.7:
2608
+
is-string@1.1.1:
2179
2609
dependencies:
2180
-
has-tostringtag: 1.0.0
2610
+
call-bound: 1.0.4
2611
+
has-tostringtag: 1.0.2
2181
2612
2182
-
is-symbol@1.0.4:
2613
+
is-symbol@1.1.1:
2183
2614
dependencies:
2184
-
has-symbols: 1.0.3
2615
+
call-bound: 1.0.4
2616
+
has-symbols: 1.1.0
2617
+
safe-regex-test: 1.1.0
2185
2618
2186
-
is-typed-array@1.1.12:
2619
+
is-typed-array@1.1.15:
2187
2620
dependencies:
2188
-
which-typed-array: 1.1.13
2621
+
which-typed-array: 1.1.19
2189
2622
2190
-
is-weakmap@2.0.1: {}
2623
+
is-weakmap@2.0.2: {}
2191
2624
2192
-
is-weakref@1.0.2:
2625
+
is-weakref@1.1.1:
2193
2626
dependencies:
2194
-
call-bind: 1.0.5
2627
+
call-bound: 1.0.4
2195
2628
2196
-
is-weakset@2.0.2:
2629
+
is-weakset@2.0.4:
2197
2630
dependencies:
2198
-
call-bind: 1.0.5
2199
-
get-intrinsic: 1.2.2
2200
-
2201
-
is-wsl@2.2.0:
2202
-
dependencies:
2203
-
is-docker: 2.2.1
2631
+
call-bound: 1.0.4
2632
+
get-intrinsic: 1.3.0
2204
2633
2205
2634
isarray@2.0.5: {}
2206
2635
2207
2636
isexe@2.0.0: {}
2208
2637
2209
-
iterator.prototype@1.1.2:
2638
+
iterator.prototype@1.1.5:
2210
2639
dependencies:
2211
-
define-properties: 1.2.1
2212
-
get-intrinsic: 1.2.2
2213
-
has-symbols: 1.0.3
2214
-
reflect.getprototypeof: 1.0.4
2215
-
set-function-name: 2.0.1
2640
+
define-data-property: 1.1.4
2641
+
es-object-atoms: 1.1.1
2642
+
get-intrinsic: 1.3.0
2643
+
get-proto: 1.0.1
2644
+
has-symbols: 1.1.0
2645
+
set-function-name: 2.0.2
2646
+
2647
+
jiti@2.4.2: {}
2216
2648
2217
2649
js-tokens@4.0.0: {}
2218
2650
···
2228
2660
2229
2661
jsx-ast-utils@3.3.5:
2230
2662
dependencies:
2231
-
array-includes: 3.1.7
2232
-
array.prototype.flat: 1.3.2
2233
-
object.assign: 4.1.5
2234
-
object.values: 1.1.7
2663
+
array-includes: 3.1.8
2664
+
array.prototype.flat: 1.3.3
2665
+
object.assign: 4.1.7
2666
+
object.values: 1.2.1
2235
2667
2236
2668
keyv@4.5.4:
2237
2669
dependencies:
···
2252
2684
dependencies:
2253
2685
js-tokens: 4.0.0
2254
2686
2255
-
lru-cache@6.0.0:
2256
-
dependencies:
2257
-
yallist: 4.0.0
2258
-
2259
-
merge-stream@2.0.0: {}
2687
+
math-intrinsics@1.1.0: {}
2260
2688
2261
2689
merge2@1.4.1: {}
2262
2690
2263
-
micromatch@4.0.5:
2691
+
meriyah@6.0.1: {}
2692
+
2693
+
microdiff@1.5.0: {}
2694
+
2695
+
micromatch@4.0.8:
2264
2696
dependencies:
2265
-
braces: 3.0.2
2697
+
braces: 3.0.3
2266
2698
picomatch: 2.3.1
2267
2699
2268
-
mimic-fn@2.1.0: {}
2269
-
2270
-
mimic-fn@4.0.0: {}
2700
+
mimic-function@5.0.1: {}
2271
2701
2272
2702
minimatch@3.1.2:
2273
2703
dependencies:
2274
2704
brace-expansion: 1.1.11
2275
2705
2276
-
ms@2.1.2: {}
2706
+
minimatch@9.0.5:
2707
+
dependencies:
2708
+
brace-expansion: 2.0.1
2277
2709
2278
-
natural-compare@1.4.0: {}
2710
+
ms@2.1.3: {}
2279
2711
2280
-
npm-run-path@4.0.1:
2281
-
dependencies:
2282
-
path-key: 3.1.1
2712
+
nanotar@0.1.1: {}
2283
2713
2284
-
npm-run-path@5.1.0:
2285
-
dependencies:
2286
-
path-key: 4.0.0
2714
+
natural-compare@1.4.0: {}
2715
+
2716
+
node-fetch-native@1.6.6: {}
2287
2717
2288
2718
object-assign@4.1.1: {}
2289
2719
2290
-
object-inspect@1.13.1: {}
2720
+
object-inspect@1.13.4: {}
2291
2721
2292
2722
object-keys@1.1.1: {}
2293
2723
2294
-
object.assign@4.1.5:
2724
+
object.assign@4.1.7:
2295
2725
dependencies:
2296
-
call-bind: 1.0.5
2726
+
call-bind: 1.0.8
2727
+
call-bound: 1.0.4
2297
2728
define-properties: 1.2.1
2298
-
has-symbols: 1.0.3
2729
+
es-object-atoms: 1.1.1
2730
+
has-symbols: 1.1.0
2299
2731
object-keys: 1.1.1
2300
2732
2301
-
object.entries@1.1.7:
2302
-
dependencies:
2303
-
call-bind: 1.0.5
2304
-
define-properties: 1.2.1
2305
-
es-abstract: 1.22.3
2306
-
2307
-
object.fromentries@2.0.7:
2733
+
object.entries@1.1.9:
2308
2734
dependencies:
2309
-
call-bind: 1.0.5
2735
+
call-bind: 1.0.8
2736
+
call-bound: 1.0.4
2310
2737
define-properties: 1.2.1
2311
-
es-abstract: 1.22.3
2738
+
es-object-atoms: 1.1.1
2312
2739
2313
-
object.hasown@1.1.3:
2740
+
object.fromentries@2.0.8:
2314
2741
dependencies:
2742
+
call-bind: 1.0.8
2315
2743
define-properties: 1.2.1
2316
-
es-abstract: 1.22.3
2744
+
es-abstract: 1.23.9
2745
+
es-object-atoms: 1.1.1
2317
2746
2318
-
object.values@1.1.7:
2747
+
object.values@1.2.1:
2319
2748
dependencies:
2320
-
call-bind: 1.0.5
2749
+
call-bind: 1.0.8
2750
+
call-bound: 1.0.4
2321
2751
define-properties: 1.2.1
2322
-
es-abstract: 1.22.3
2323
-
2324
-
once@1.4.0:
2325
-
dependencies:
2326
-
wrappy: 1.0.2
2327
-
2328
-
onetime@5.1.2:
2329
-
dependencies:
2330
-
mimic-fn: 2.1.0
2752
+
es-object-atoms: 1.1.1
2331
2753
2332
-
onetime@6.0.0:
2754
+
ofetch@1.4.1:
2333
2755
dependencies:
2334
-
mimic-fn: 4.0.0
2756
+
destr: 2.0.4
2757
+
node-fetch-native: 1.6.6
2758
+
ufo: 1.5.4
2335
2759
2336
-
open@9.1.0:
2760
+
onetime@7.0.0:
2337
2761
dependencies:
2338
-
default-browser: 4.0.0
2339
-
define-lazy-prop: 3.0.0
2340
-
is-inside-container: 1.0.0
2341
-
is-wsl: 2.2.0
2762
+
mimic-function: 5.0.1
2342
2763
2343
2764
optionator@0.9.3:
2344
2765
dependencies:
···
2349
2770
prelude-ls: 1.2.1
2350
2771
type-check: 0.4.0
2351
2772
2773
+
own-keys@1.0.1:
2774
+
dependencies:
2775
+
get-intrinsic: 1.3.0
2776
+
object-keys: 1.1.1
2777
+
safe-push-apply: 1.0.0
2778
+
2352
2779
p-limit@3.1.0:
2353
2780
dependencies:
2354
2781
yocto-queue: 0.1.0
···
2356
2783
p-locate@5.0.0:
2357
2784
dependencies:
2358
2785
p-limit: 3.1.0
2786
+
2787
+
package-manager-detector@1.1.0: {}
2359
2788
2360
2789
parent-module@1.0.1:
2361
2790
dependencies:
···
2363
2792
2364
2793
path-exists@4.0.0: {}
2365
2794
2366
-
path-is-absolute@1.0.1: {}
2367
-
2368
2795
path-key@3.1.1: {}
2369
2796
2370
-
path-key@4.0.0: {}
2371
-
2372
2797
path-parse@1.0.7: {}
2373
2798
2374
-
path-type@4.0.0: {}
2375
-
2376
-
picocolors@1.0.0: {}
2799
+
pathe@2.0.3: {}
2377
2800
2378
2801
picomatch@2.3.1: {}
2379
2802
2803
+
picomatch@4.0.2: {}
2804
+
2805
+
pnpm-workspace-yaml@0.3.1:
2806
+
dependencies:
2807
+
yaml: 2.7.1
2808
+
2809
+
possible-typed-array-names@1.1.0: {}
2810
+
2380
2811
prelude-ls@1.2.1: {}
2381
2812
2382
2813
prettier-linter-helpers@1.0.0:
···
2385
2816
2386
2817
prettier@3.1.0: {}
2387
2818
2819
+
process@0.11.10: {}
2820
+
2388
2821
prop-types@15.8.1:
2389
2822
dependencies:
2390
2823
loose-envify: 1.4.0
···
2393
2826
2394
2827
punycode@2.3.1: {}
2395
2828
2829
+
quansync@0.2.10: {}
2830
+
2396
2831
queue-microtask@1.2.3: {}
2397
2832
2398
2833
react-is@16.13.1: {}
2399
2834
2400
-
reflect.getprototypeof@1.0.4:
2835
+
readable-stream@4.5.2:
2401
2836
dependencies:
2402
-
call-bind: 1.0.5
2837
+
abort-controller: 3.0.0
2838
+
buffer: 6.0.3
2839
+
events: 3.3.0
2840
+
process: 0.11.10
2841
+
string_decoder: 1.3.0
2842
+
2843
+
reflect.getprototypeof@1.0.10:
2844
+
dependencies:
2845
+
call-bind: 1.0.8
2403
2846
define-properties: 1.2.1
2404
-
es-abstract: 1.22.3
2405
-
get-intrinsic: 1.2.2
2406
-
globalthis: 1.0.3
2407
-
which-builtin-type: 1.1.3
2847
+
es-abstract: 1.23.9
2848
+
es-errors: 1.3.0
2849
+
es-object-atoms: 1.1.1
2850
+
get-intrinsic: 1.3.0
2851
+
get-proto: 1.0.1
2852
+
which-builtin-type: 1.2.1
2408
2853
2409
-
regexp.prototype.flags@1.5.1:
2854
+
regexp.prototype.flags@1.5.4:
2410
2855
dependencies:
2411
-
call-bind: 1.0.5
2856
+
call-bind: 1.0.8
2412
2857
define-properties: 1.2.1
2413
-
set-function-name: 2.0.1
2858
+
es-errors: 1.3.0
2859
+
get-proto: 1.0.1
2860
+
gopd: 1.2.0
2861
+
set-function-name: 2.0.2
2414
2862
2415
2863
resolve-from@4.0.0: {}
2416
2864
2417
2865
resolve@2.0.0-next.5:
2418
2866
dependencies:
2419
-
is-core-module: 2.13.1
2867
+
is-core-module: 2.16.1
2420
2868
path-parse: 1.0.7
2421
2869
supports-preserve-symlinks-flag: 1.0.0
2422
2870
2423
-
reusify@1.0.4: {}
2424
-
2425
-
rimraf@3.0.2:
2871
+
restore-cursor@5.1.0:
2426
2872
dependencies:
2427
-
glob: 7.2.3
2873
+
onetime: 7.0.0
2874
+
signal-exit: 4.1.0
2428
2875
2429
-
run-applescript@5.0.0:
2430
-
dependencies:
2431
-
execa: 5.1.1
2876
+
reusify@1.0.4: {}
2432
2877
2433
2878
run-parallel@1.2.0:
2434
2879
dependencies:
2435
2880
queue-microtask: 1.2.3
2436
2881
2437
-
safe-array-concat@1.0.1:
2882
+
safe-array-concat@1.1.3:
2438
2883
dependencies:
2439
-
call-bind: 1.0.5
2440
-
get-intrinsic: 1.2.2
2441
-
has-symbols: 1.0.3
2884
+
call-bind: 1.0.8
2885
+
call-bound: 1.0.4
2886
+
get-intrinsic: 1.3.0
2887
+
has-symbols: 1.1.0
2442
2888
isarray: 2.0.5
2443
2889
2444
-
safe-regex-test@1.0.0:
2890
+
safe-buffer@5.2.1: {}
2891
+
2892
+
safe-push-apply@1.0.0:
2445
2893
dependencies:
2446
-
call-bind: 1.0.5
2447
-
get-intrinsic: 1.2.2
2448
-
is-regex: 1.1.4
2894
+
es-errors: 1.3.0
2895
+
isarray: 2.0.5
2896
+
2897
+
safe-regex-test@1.1.0:
2898
+
dependencies:
2899
+
call-bound: 1.0.4
2900
+
es-errors: 1.3.0
2901
+
is-regex: 1.2.1
2449
2902
2450
2903
semver@6.3.1: {}
2451
2904
2452
-
semver@7.5.4:
2453
-
dependencies:
2454
-
lru-cache: 6.0.0
2905
+
semver@7.7.1: {}
2455
2906
2456
-
set-function-length@1.1.1:
2907
+
set-function-length@1.2.2:
2457
2908
dependencies:
2458
-
define-data-property: 1.1.1
2459
-
get-intrinsic: 1.2.2
2460
-
gopd: 1.0.1
2461
-
has-property-descriptors: 1.0.1
2909
+
define-data-property: 1.1.4
2910
+
es-errors: 1.3.0
2911
+
function-bind: 1.1.2
2912
+
get-intrinsic: 1.3.0
2913
+
gopd: 1.2.0
2914
+
has-property-descriptors: 1.0.2
2462
2915
2463
-
set-function-name@2.0.1:
2916
+
set-function-name@2.0.2:
2464
2917
dependencies:
2465
-
define-data-property: 1.1.1
2918
+
define-data-property: 1.1.4
2919
+
es-errors: 1.3.0
2466
2920
functions-have-names: 1.2.3
2467
-
has-property-descriptors: 1.0.1
2921
+
has-property-descriptors: 1.0.2
2922
+
2923
+
set-proto@1.0.0:
2924
+
dependencies:
2925
+
dunder-proto: 1.0.1
2926
+
es-errors: 1.3.0
2927
+
es-object-atoms: 1.1.1
2468
2928
2469
2929
shebang-command@2.0.0:
2470
2930
dependencies:
···
2472
2932
2473
2933
shebang-regex@3.0.0: {}
2474
2934
2475
-
side-channel@1.0.4:
2935
+
side-channel-list@1.0.0:
2476
2936
dependencies:
2477
-
call-bind: 1.0.5
2478
-
get-intrinsic: 1.2.2
2479
-
object-inspect: 1.13.1
2937
+
es-errors: 1.3.0
2938
+
object-inspect: 1.13.4
2480
2939
2481
-
signal-exit@3.0.7: {}
2940
+
side-channel-map@1.0.1:
2941
+
dependencies:
2942
+
call-bound: 1.0.4
2943
+
es-errors: 1.3.0
2944
+
get-intrinsic: 1.3.0
2945
+
object-inspect: 1.13.4
2482
2946
2483
-
slash@3.0.0: {}
2947
+
side-channel-weakmap@1.0.2:
2948
+
dependencies:
2949
+
call-bound: 1.0.4
2950
+
es-errors: 1.3.0
2951
+
get-intrinsic: 1.3.0
2952
+
object-inspect: 1.13.4
2953
+
side-channel-map: 1.0.1
2954
+
2955
+
side-channel@1.1.0:
2956
+
dependencies:
2957
+
es-errors: 1.3.0
2958
+
object-inspect: 1.13.4
2959
+
side-channel-list: 1.0.0
2960
+
side-channel-map: 1.0.1
2961
+
side-channel-weakmap: 1.0.2
2962
+
2963
+
signal-exit@4.1.0: {}
2484
2964
2485
2965
standalone-electron-types@1.0.0:
2486
2966
dependencies:
2487
2967
'@types/node': 18.17.17
2488
2968
2489
-
string.prototype.matchall@4.0.10:
2969
+
string.prototype.matchall@4.0.12:
2490
2970
dependencies:
2491
-
call-bind: 1.0.5
2971
+
call-bind: 1.0.8
2972
+
call-bound: 1.0.4
2492
2973
define-properties: 1.2.1
2493
-
es-abstract: 1.22.3
2494
-
get-intrinsic: 1.2.2
2495
-
has-symbols: 1.0.3
2496
-
internal-slot: 1.0.6
2497
-
regexp.prototype.flags: 1.5.1
2498
-
set-function-name: 2.0.1
2499
-
side-channel: 1.0.4
2974
+
es-abstract: 1.23.9
2975
+
es-errors: 1.3.0
2976
+
es-object-atoms: 1.1.1
2977
+
get-intrinsic: 1.3.0
2978
+
gopd: 1.2.0
2979
+
has-symbols: 1.1.0
2980
+
internal-slot: 1.1.0
2981
+
regexp.prototype.flags: 1.5.4
2982
+
set-function-name: 2.0.2
2983
+
side-channel: 1.1.0
2500
2984
2501
-
string.prototype.trim@1.2.8:
2985
+
string.prototype.repeat@1.0.0:
2502
2986
dependencies:
2503
-
call-bind: 1.0.5
2504
2987
define-properties: 1.2.1
2505
-
es-abstract: 1.22.3
2988
+
es-abstract: 1.23.9
2506
2989
2507
-
string.prototype.trimend@1.0.7:
2990
+
string.prototype.trim@1.2.10:
2508
2991
dependencies:
2509
-
call-bind: 1.0.5
2992
+
call-bind: 1.0.8
2993
+
call-bound: 1.0.4
2994
+
define-data-property: 1.1.4
2510
2995
define-properties: 1.2.1
2511
-
es-abstract: 1.22.3
2996
+
es-abstract: 1.23.9
2997
+
es-object-atoms: 1.1.1
2998
+
has-property-descriptors: 1.0.2
2512
2999
2513
-
string.prototype.trimstart@1.0.7:
3000
+
string.prototype.trimend@1.0.9:
2514
3001
dependencies:
2515
-
call-bind: 1.0.5
3002
+
call-bind: 1.0.8
3003
+
call-bound: 1.0.4
2516
3004
define-properties: 1.2.1
2517
-
es-abstract: 1.22.3
3005
+
es-object-atoms: 1.1.1
2518
3006
2519
-
strip-ansi@6.0.1:
3007
+
string.prototype.trimstart@1.0.8:
2520
3008
dependencies:
2521
-
ansi-regex: 5.0.1
2522
-
2523
-
strip-final-newline@2.0.0: {}
3009
+
call-bind: 1.0.8
3010
+
define-properties: 1.2.1
3011
+
es-object-atoms: 1.1.1
2524
3012
2525
-
strip-final-newline@3.0.0: {}
3013
+
string_decoder@1.3.0:
3014
+
dependencies:
3015
+
safe-buffer: 5.2.1
2526
3016
2527
3017
strip-json-comments@3.1.1: {}
2528
3018
···
2532
3022
2533
3023
supports-preserve-symlinks-flag@1.0.0: {}
2534
3024
2535
-
synckit@0.8.6:
3025
+
synckit@0.11.1:
2536
3026
dependencies:
2537
-
'@pkgr/utils': 2.4.2
2538
-
tslib: 2.6.2
3027
+
'@pkgr/core': 0.2.0
3028
+
tslib: 2.8.1
2539
3029
2540
-
text-table@0.2.0: {}
3030
+
taze@19.0.4:
3031
+
dependencies:
3032
+
'@antfu/ni': 24.3.0
3033
+
cac: 6.7.14
3034
+
find-up-simple: 1.0.1
3035
+
ofetch: 1.4.1
3036
+
package-manager-detector: 1.1.0
3037
+
pathe: 2.0.3
3038
+
pnpm-workspace-yaml: 0.3.1
3039
+
restore-cursor: 5.1.0
3040
+
tinyexec: 1.0.1
3041
+
tinyglobby: 0.2.12
3042
+
unconfig: 7.3.1
3043
+
yaml: 2.7.1
2541
3044
2542
-
titleize@3.0.0: {}
3045
+
tinyexec@1.0.1: {}
3046
+
3047
+
tinyglobby@0.2.12:
3048
+
dependencies:
3049
+
fdir: 6.4.3(picomatch@4.0.2)
3050
+
picomatch: 4.0.2
2543
3051
2544
3052
to-regex-range@5.0.1:
2545
3053
dependencies:
2546
3054
is-number: 7.0.0
2547
3055
2548
-
ts-api-utils@1.0.3(typescript@5.3.2):
3056
+
ts-api-utils@2.1.0(typescript@5.8.2):
2549
3057
dependencies:
2550
-
typescript: 5.3.2
3058
+
typescript: 5.8.2
2551
3059
2552
-
tslib@2.6.2: {}
3060
+
tslib@2.8.1: {}
2553
3061
2554
3062
type-check@0.4.0:
2555
3063
dependencies:
2556
3064
prelude-ls: 1.2.1
2557
3065
2558
-
type-fest@0.20.2: {}
3066
+
typed-array-buffer@1.0.3:
3067
+
dependencies:
3068
+
call-bound: 1.0.4
3069
+
es-errors: 1.3.0
3070
+
is-typed-array: 1.1.15
2559
3071
2560
-
typed-array-buffer@1.0.0:
3072
+
typed-array-byte-length@1.0.3:
2561
3073
dependencies:
2562
-
call-bind: 1.0.5
2563
-
get-intrinsic: 1.2.2
2564
-
is-typed-array: 1.1.12
3074
+
call-bind: 1.0.8
3075
+
for-each: 0.3.5
3076
+
gopd: 1.2.0
3077
+
has-proto: 1.2.0
3078
+
is-typed-array: 1.1.15
2565
3079
2566
-
typed-array-byte-length@1.0.0:
3080
+
typed-array-byte-offset@1.0.4:
2567
3081
dependencies:
2568
-
call-bind: 1.0.5
2569
-
for-each: 0.3.3
2570
-
has-proto: 1.0.1
2571
-
is-typed-array: 1.1.12
3082
+
available-typed-arrays: 1.0.7
3083
+
call-bind: 1.0.8
3084
+
for-each: 0.3.5
3085
+
gopd: 1.2.0
3086
+
has-proto: 1.2.0
3087
+
is-typed-array: 1.1.15
3088
+
reflect.getprototypeof: 1.0.10
2572
3089
2573
-
typed-array-byte-offset@1.0.0:
3090
+
typed-array-length@1.0.7:
2574
3091
dependencies:
2575
-
available-typed-arrays: 1.0.5
2576
-
call-bind: 1.0.5
2577
-
for-each: 0.3.3
2578
-
has-proto: 1.0.1
2579
-
is-typed-array: 1.1.12
3092
+
call-bind: 1.0.8
3093
+
for-each: 0.3.5
3094
+
gopd: 1.2.0
3095
+
is-typed-array: 1.1.15
3096
+
possible-typed-array-names: 1.1.0
3097
+
reflect.getprototypeof: 1.0.10
2580
3098
2581
-
typed-array-length@1.0.4:
3099
+
typescript-eslint@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2):
2582
3100
dependencies:
2583
-
call-bind: 1.0.5
2584
-
for-each: 0.3.3
2585
-
is-typed-array: 1.1.12
3101
+
'@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
3102
+
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
3103
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
3104
+
eslint: 9.23.0(jiti@2.4.2)
3105
+
typescript: 5.8.2
3106
+
transitivePeerDependencies:
3107
+
- supports-color
2586
3108
2587
-
typescript@5.3.2: {}
3109
+
typescript@5.8.2: {}
2588
3110
2589
-
unbox-primitive@1.0.2:
3111
+
ufo@1.5.4: {}
3112
+
3113
+
unbox-primitive@1.1.0:
2590
3114
dependencies:
2591
-
call-bind: 1.0.5
2592
-
has-bigints: 1.0.2
2593
-
has-symbols: 1.0.3
2594
-
which-boxed-primitive: 1.0.2
3115
+
call-bound: 1.0.4
3116
+
has-bigints: 1.1.0
3117
+
has-symbols: 1.1.0
3118
+
which-boxed-primitive: 1.1.1
2595
3119
2596
-
untildify@4.0.0: {}
3120
+
unconfig@7.3.1:
3121
+
dependencies:
3122
+
'@quansync/fs': 0.1.2
3123
+
defu: 6.1.4
3124
+
jiti: 2.4.2
3125
+
quansync: 0.2.10
3126
+
3127
+
undici-types@6.20.0: {}
3128
+
3129
+
undici-types@6.21.0: {}
2597
3130
2598
3131
uri-js@4.4.1:
2599
3132
dependencies:
2600
3133
punycode: 2.3.1
2601
3134
2602
-
which-boxed-primitive@1.0.2:
3135
+
utilium@1.10.1:
2603
3136
dependencies:
2604
-
is-bigint: 1.0.4
2605
-
is-boolean-object: 1.1.2
2606
-
is-number-object: 1.0.7
2607
-
is-string: 1.0.7
2608
-
is-symbol: 1.0.4
3137
+
eventemitter3: 5.0.1
3138
+
optionalDependencies:
3139
+
'@xterm/xterm': 5.5.0
2609
3140
2610
-
which-builtin-type@1.1.3:
3141
+
which-boxed-primitive@1.1.1:
2611
3142
dependencies:
2612
-
function.prototype.name: 1.1.6
2613
-
has-tostringtag: 1.0.0
2614
-
is-async-function: 2.0.0
2615
-
is-date-object: 1.0.5
2616
-
is-finalizationregistry: 1.0.2
2617
-
is-generator-function: 1.0.10
2618
-
is-regex: 1.1.4
2619
-
is-weakref: 1.0.2
3143
+
is-bigint: 1.1.0
3144
+
is-boolean-object: 1.2.2
3145
+
is-number-object: 1.1.1
3146
+
is-string: 1.1.1
3147
+
is-symbol: 1.1.1
3148
+
3149
+
which-builtin-type@1.2.1:
3150
+
dependencies:
3151
+
call-bound: 1.0.4
3152
+
function.prototype.name: 1.1.8
3153
+
has-tostringtag: 1.0.2
3154
+
is-async-function: 2.1.1
3155
+
is-date-object: 1.1.0
3156
+
is-finalizationregistry: 1.1.1
3157
+
is-generator-function: 1.1.0
3158
+
is-regex: 1.2.1
3159
+
is-weakref: 1.1.1
2620
3160
isarray: 2.0.5
2621
-
which-boxed-primitive: 1.0.2
2622
-
which-collection: 1.0.1
2623
-
which-typed-array: 1.1.13
3161
+
which-boxed-primitive: 1.1.1
3162
+
which-collection: 1.0.2
3163
+
which-typed-array: 1.1.19
2624
3164
2625
-
which-collection@1.0.1:
3165
+
which-collection@1.0.2:
2626
3166
dependencies:
2627
-
is-map: 2.0.2
2628
-
is-set: 2.0.2
2629
-
is-weakmap: 2.0.1
2630
-
is-weakset: 2.0.2
3167
+
is-map: 2.0.3
3168
+
is-set: 2.0.3
3169
+
is-weakmap: 2.0.2
3170
+
is-weakset: 2.0.4
2631
3171
2632
-
which-typed-array@1.1.13:
3172
+
which-typed-array@1.1.19:
2633
3173
dependencies:
2634
-
available-typed-arrays: 1.0.5
2635
-
call-bind: 1.0.5
2636
-
for-each: 0.3.3
2637
-
gopd: 1.0.1
2638
-
has-tostringtag: 1.0.0
3174
+
available-typed-arrays: 1.0.7
3175
+
call-bind: 1.0.8
3176
+
call-bound: 1.0.4
3177
+
for-each: 0.3.5
3178
+
get-proto: 1.0.1
3179
+
gopd: 1.2.0
3180
+
has-tostringtag: 1.0.2
2639
3181
2640
3182
which@2.0.2:
2641
3183
dependencies:
2642
3184
isexe: 2.0.0
2643
3185
2644
-
wrappy@1.0.2: {}
3186
+
yaml@2.7.1: {}
2645
3187
2646
-
yallist@4.0.0: {}
3188
+
yocto-queue@0.1.0: {}
2647
3189
2648
-
yocto-queue@0.1.0: {}
3190
+
zustand@5.0.3(@types/react@18.3.20):
3191
+
optionalDependencies:
3192
+
'@types/react': 18.3.20
+31
-1
pnpm-workspace.yaml
+31
-1
pnpm-workspace.yaml
···
1
1
packages:
2
-
- "packages/*"
2
+
- packages/*
3
+
4
+
catalogs:
5
+
dev:
6
+
esbuild: ^0.19.3
7
+
esbuild-copy-static-files: ^0.1.0
8
+
"@types/node": ^22.14.0
9
+
"@moonlight-mod/eslint-config": "github:moonlight-mod/eslint-config"
10
+
eslint: ^9.12.0
11
+
"@types/chrome": ^0.0.313
12
+
husky: ^8.0.3
13
+
prettier: ^3.1.0
14
+
typescript: ^5.3.3
15
+
taze: ^19.0.4
16
+
prod:
17
+
"@moonlight-mod/lunast": ^1.0.1
18
+
"@moonlight-mod/mappings": ^1.1.25
19
+
"@moonlight-mod/moonmap": ^1.0.5
20
+
microdiff: ^1.5.0
21
+
nanotar: ^0.1.1
22
+
"@zenfs/core": ^2.0.0
23
+
"@zenfs/dom": ^1.1.3
24
+
25
+
onlyBuiltDependencies:
26
+
- esbuild
27
+
28
+
engineStrict: true
29
+
strictSsl: true
30
+
strictDepBuilds: true
31
+
packageManagerStrict: true
32
+
registry: https://registry.npmjs.org/
+78
scripts/link.mjs
+78
scripts/link.mjs
···
1
+
// Janky script to get around pnpm link issues
2
+
// Probably don't use this. Probably
3
+
/* eslint-disable no-console */
4
+
const fs = require("fs");
5
+
const path = require("path");
6
+
const child_process = require("child_process");
7
+
8
+
const cwd = process.cwd();
9
+
const onDisk = {
10
+
//"@moonlight-mod/lunast": "../lunast",
11
+
//"@moonlight-mod/moonmap": "../moonmap",
12
+
"@moonlight-mod/mappings": "../mappings"
13
+
};
14
+
15
+
function exec(cmd, dir) {
16
+
child_process.execSync(cmd, { cwd: dir, stdio: "inherit" });
17
+
}
18
+
19
+
function getDeps(packageJSON) {
20
+
const ret = {};
21
+
Object.assign(ret, packageJSON.dependencies || {});
22
+
Object.assign(ret, packageJSON.devDependencies || {});
23
+
Object.assign(ret, packageJSON.peerDependencies || {});
24
+
return ret;
25
+
}
26
+
27
+
function link(dir) {
28
+
const packageJSONPath = path.join(dir, "package.json");
29
+
if (!fs.existsSync(packageJSONPath)) return;
30
+
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"));
31
+
const deps = getDeps(packageJSON);
32
+
33
+
for (const [dep, relativePath] of Object.entries(onDisk)) {
34
+
const fullPath = path.join(cwd, relativePath);
35
+
if (deps[dep]) {
36
+
exec(`pnpm link ${fullPath}`, dir);
37
+
}
38
+
}
39
+
}
40
+
41
+
function undo(dir) {
42
+
exec("pnpm unlink", dir);
43
+
try {
44
+
if (fs.existsSync(path.join(dir, "pnpm-lock.yaml"))) {
45
+
exec("git restore pnpm-lock.yaml", dir);
46
+
}
47
+
} catch {
48
+
// ignored
49
+
}
50
+
}
51
+
52
+
const shouldUndo = process.argv.includes("--undo");
53
+
const packages = fs.readdirSync("./packages");
54
+
55
+
for (const path of Object.values(onDisk)) {
56
+
console.log(path);
57
+
if (shouldUndo) {
58
+
undo(path);
59
+
} else {
60
+
link(path);
61
+
}
62
+
}
63
+
64
+
if (shouldUndo) {
65
+
console.log(cwd);
66
+
undo(cwd);
67
+
for (const pkg of packages) {
68
+
const dir = path.join(cwd, "packages", pkg);
69
+
console.log(dir);
70
+
undo(dir);
71
+
}
72
+
} else {
73
+
for (const pkg of packages) {
74
+
const dir = path.join(cwd, "packages", pkg);
75
+
console.log(dir);
76
+
link(dir);
77
+
}
78
+
}
+35
tsconfig.base.json
+35
tsconfig.base.json
···
1
+
{
2
+
"$schema": "https://json.schemastore.org/tsconfig.json",
3
+
"display": "Base",
4
+
"_version": "1.0.0",
5
+
"compilerOptions": {
6
+
"incremental": true,
7
+
"target": "ES2022",
8
+
"jsx": "react",
9
+
"lib": ["ESNext", "ESNext.Disposable", "DOM", "DOM.Iterable"],
10
+
"module": "ES2020",
11
+
"moduleResolution": "Bundler",
12
+
"resolveJsonModule": true,
13
+
"allowArbitraryExtensions": false,
14
+
"allowImportingTsExtensions": true,
15
+
"allowJs": true,
16
+
"strict": true,
17
+
"strictNullChecks": true,
18
+
19
+
// disable unreachable code detection because it breaks with esbuild labels
20
+
"allowUnreachableCode": true,
21
+
"noFallthroughCasesInSwitch": true,
22
+
"noImplicitReturns": true,
23
+
"declaration": true,
24
+
"declarationMap": true,
25
+
"outDir": "dist",
26
+
"sourceMap": true,
27
+
"stripInternal": true,
28
+
"esModuleInterop": true,
29
+
"forceConsistentCasingInFileNames": true,
30
+
"noErrorTruncation": true,
31
+
"verbatimModuleSyntax": false,
32
+
// meriyah has a broken import lol
33
+
"skipLibCheck": true
34
+
}
35
+
}
+7
-13
tsconfig.json
+7
-13
tsconfig.json
···
1
1
{
2
+
"extends": ["./tsconfig.base.json"],
2
3
"compilerOptions": {
3
-
"target": "es2016",
4
-
"module": "es6",
5
-
"esModuleInterop": true,
6
-
"forceConsistentCasingInFileNames": true,
7
-
"strict": true,
8
-
"moduleResolution": "bundler",
9
4
"baseUrl": "./packages/",
10
-
"jsx": "react",
11
-
"noEmit": true,
12
-
13
-
// disable unreachable code detection because it breaks with esbuild labels
14
-
"allowUnreachableCode": true
5
+
"noEmit": true
15
6
},
16
-
"include": ["./packages/**/*", "./env.d.ts"],
17
-
"exclude": ["node_modules"]
7
+
"exclude": [
8
+
"**/node_modules/**",
9
+
"**/dist/**",
10
+
"**/build/**"
11
+
]
18
12
}